)
+
+ const headers = new Headers(request.headers)
+ if (cookies.length > 0) {
+ headers.set('Cookie', cookie.stringifyCookie(cookieHeader))
+ }
+
+ const response = await fetch(request.url, {
+ method: request.method,
+ headers,
+ body: ['GET', 'HEAD'].includes(request.method) ? undefined : request.clone().body,
+ redirect: 'manual', // Handle redirects manually to preserve cookies
+ // @ts-expect-error Who needs types when you have ts-expect-error?
+ duplex: 'half'
+ })
+
+ // Manually apply Set-Cookie headers to Chrome's cookie jar
+ const setCookieHeaders = response.headers.getSetCookie()
+ for (const cookieValue of setCookieHeaders) {
+ const c = cookie.parseSetCookie(cookieValue)
+ // https://stackoverflow.com/a/39136448
+ // Cookies require an expirationDate to be persistent, for some reason
+ const expires = c.maxAge
+ ? Date.now() + (c.maxAge * 1000)
+ : c.expires?.getTime()
+
+ await session.defaultSession.cookies.set({
+ url: request.url,
+ domain: c.domain,
+ expirationDate: expires,
+ httpOnly: c.httpOnly,
+ name: c.name,
+ path: c.path,
+ sameSite: c.sameSite === true
+ ? 'strict'
+ : c.sameSite === 'none' || c.sameSite === false
+ ? 'no_restriction'
+ : c.sameSite,
+ secure: c.secure,
+ value: c.value
+ })
+ }
+
+ return response
+ })
+
+ createWindow()
+
+ app.on('activate', () => {
+ if (BrowserWindow.getAllWindows().length === 0) {
+ createWindow()
+ }
+ })
+})
+
+app.on('window-all-closed', () => {
+ if (process.platform !== 'darwin') {
+ app.quit()
+ }
+})
+
+initializeSteam()
+enableSteamOverlay()
diff --git a/electron/preload.js b/electron/preload.js
new file mode 100644
index 000000000..9a8b1f27c
--- /dev/null
+++ b/electron/preload.js
@@ -0,0 +1,43 @@
+// @ts-check
+// Yes, it has to be CommonJS
+
+const { contextBridge, ipcRenderer } = require('electron')
+
+contextBridge.exposeInMainWorld('steam', {
+ getSteamId: () => ipcRenderer.invoke('steam:getSteamId'),
+ getUsername: () => ipcRenderer.invoke('steam:getUsername'),
+ getCurrentGameLanguage: () => ipcRenderer.invoke('steam:getCurrentGameLanguage'),
+ /**
+ * @param {string} key
+ * @param {string} [value]
+ */
+ setRichPresence: (key, value) => ipcRenderer.invoke('steam:setRichPresence', key, value),
+ getSessionTicket: () => ipcRenderer.invoke('steam:getSessionTicket'),
+ /**
+ * @param {(response: import('steamworks.js/callbacks').CallbackReturns[
+ * import('steamworks.js/client').callback.SteamCallback.MicroTxnAuthorizationResponse
+ * ]) => void} callback
+ */
+ onMicroTxnAuthorizationResponse: (callback) => {
+ ipcRenderer.once('steam:microTxnAuthorizationResponse', (_, response) => callback(response))
+ },
+ // Steam Cloud Storage
+ /** @param {string} name */
+ cloudFileExists: (name) => ipcRenderer.invoke('steam:cloudFileExists', name),
+ /** @param {string} name */
+ cloudReadFile: (name) => ipcRenderer.invoke('steam:cloudReadFile', name),
+ /**
+ * @param {string} name
+ * @param {string} content
+ */
+ cloudWriteFile: (name, content) => ipcRenderer.invoke('steam:cloudWriteFile', name, content),
+ /** @param {string} name */
+ cloudDeleteFile: (name) => ipcRenderer.invoke('steam:cloudDeleteFile', name)
+})
+
+contextBridge.exposeInMainWorld('discord', {
+ /**
+ * @param {import('./lib/discord').PresenceOptions} options
+ */
+ setRichPresence: (options) => ipcRenderer.invoke('discord:setRichPresence', options)
+})
diff --git a/index.html b/index.html
index a3f1a76eb..dd8a190e6 100644
--- a/index.html
+++ b/index.html
@@ -1,7 +1,7 @@
-
+
@@ -3233,7 +3233,7 @@ Artists
-