diff --git a/.github/workflows/flatpak-node.yml b/.github/workflows/flatpak-node.yml deleted file mode 100644 index 082ee4a..0000000 --- a/.github/workflows/flatpak-node.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Upload generated-sources.json to release for Flatpak building - -on: - release: - types: - - published - workflow_dispatch: - -permissions: - contents: write - -jobs: - upload: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install flatpak-node-generator - run: pipx install git+https://github.com/flatpak/flatpak-builder-tools.git#subdirectory=node - - - name: Create generated-sources.json - run: /root/.local/bin/flatpak-node-generator pnpm pnpm-lock.yaml --node-sdk-extension org.freedesktop.Sdk.Extension.node26 --electron-node-headers - - - name: Upload generated-sources.json to release - run: | - gh release upload ${{ github.event.release.tag_name }} generated-sources.json - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/examples/clipboard-fallback-plugin/README.md b/docs/examples/clipboard-fallback-plugin/README.md deleted file mode 100644 index 55d85b3..0000000 --- a/docs/examples/clipboard-fallback-plugin/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# Legcord Clipboard Fallback Plugin - -Fixes Discord in-page copy actions in Legcord, including: - -- Copy User ID -- Copy Message ID -- Copy Message Link -- Other Discord menu actions that call `navigator.clipboard.writeText(...)` - -## Why this exists - -In affected Legcord/Electron environments, Discord's web UI calls: - -```js -navigator.clipboard.writeText(text) -``` - -but Chromium rejects it, commonly with errors like: - -```text -NotAllowedError: Failed to execute 'writeText' on 'Clipboard': Document is not focused. -``` - -or Legcord logs: - -```text -Unable to determine render window for element [object HTMLDocument] -``` - -This plugin patches `navigator.clipboard.writeText` in the Discord page and falls back to a selection-based `document.execCommand("copy")` copy path. - -## Known limitation - -Legcord's native **Copy Image** context-menu action does not go through `navigator.clipboard.writeText` or `navigator.clipboard.write` in the page. It is handled by Electron's main-process context menu (`webContents.copyImageAt(...)`), so a renderer/custom-bundle plugin cannot reliably fix image copying. That needs a Legcord main-process fix or a filesystem plugin with main/preload access on newer Legcord versions. - -## Install on Legcord versions with filesystem plugins - -1. Open the Legcord plugins folder: - - ```text - ~/Library/Application Support/legcord/plugins - ``` - -2. Create this folder: - - ```text - clipboard-fallback - ``` - -3. Copy these files into it: - - ```text - manifest.json - renderer.js - ``` - -4. Restart Legcord. -5. Enable **Clipboard Fallback** in Legcord's plugin settings. - -## Older Legcord workaround: custom bundle - -If your Legcord version does not have filesystem plugins yet, copy `custom-bundle.js` into: - -```text -~/Library/Application Support/legcord/custom.js -``` - -Do **not** use `renderer.js` as `custom.js`; `renderer.js` is the filesystem-plugin entry and expects Legcord's plugin loader to provide `module.exports`. - -and add `"custom"` to the `mods` array in: - -```text -~/Library/Application Support/legcord/storage/settings.json -``` - -Example: - -```json -"mods": ["equicord", "custom"] -``` - -Then restart Legcord. diff --git a/docs/examples/clipboard-fallback-plugin/custom-bundle.js b/docs/examples/clipboard-fallback-plugin/custom-bundle.js deleted file mode 100644 index 83cb93f..0000000 --- a/docs/examples/clipboard-fallback-plugin/custom-bundle.js +++ /dev/null @@ -1,216 +0,0 @@ -(() => { - const module = { exports: {} }; - const api = { - logger: { - log: (...args) => console.log("[ClipboardFallback]", ...args), - warn: (...args) => console.warn("[ClipboardFallback]", ...args), - error: (...args) => console.error("[ClipboardFallback]", ...args), - }, - }; - - /** - * Legcord Clipboard Fallback - * - * Discord's web UI uses navigator.clipboard.writeText for actions such as - * "Copy User ID" and "Copy Message Link", and navigator.clipboard.write for - * richer clipboard payloads such as images. In some Legcord/Electron/macOS - * combinations Chromium rejects those calls because the document is not focused - * or the clipboard permission is not granted, leaving the clipboard unchanged. - * - * This renderer plugin replaces those APIs with selection-based copy fallbacks - * that run inside the original click gesture. - */ - module.exports.activate = (api) => { - const PATCH_KEY = Symbol.for("legcord.clipboardFallback.installed"); - - function install() { - try { - if (!navigator.clipboard) { - api.logger.warn("navigator.clipboard is unavailable"); - return; - } - - if (navigator.clipboard[PATCH_KEY]) return; - - const originalWriteText = navigator.clipboard.writeText?.bind(navigator.clipboard); - const originalWrite = navigator.clipboard.write?.bind(navigator.clipboard); - - async function blobToDataUrl(blob) { - if (typeof FileReader !== "undefined") { - return await new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(String(reader.result)); - reader.onerror = () => reject(reader.error ?? new Error("Failed to read clipboard image")); - reader.readAsDataURL(blob); - }); - } - - // Test/runtime fallback for environments with Blob but no FileReader. - const bytes = new Uint8Array(await blob.arrayBuffer()); - let binary = ""; - for (const byte of bytes) binary += String.fromCharCode(byte); - const base64 = typeof btoa === "function" ? btoa(binary) : Buffer.from(bytes).toString("base64"); - return `data:${blob.type || "application/octet-stream"};base64,${base64}`; - } - - function selectElementForCopy(element) { - const previousActiveElement = document.activeElement; - const selection = window.getSelection?.() ?? globalThis.getSelection?.(); - const range = document.createRange(); - - element.focus?.(); - range.selectNodeContents(element); - selection?.removeAllRanges(); - selection?.addRange(range); - - const copied = document.execCommand("copy"); - - selection?.removeAllRanges(); - if (previousActiveElement && typeof previousActiveElement.focus === "function") { - try { - previousActiveElement.focus(); - } catch {} - } - - if (!copied) throw new Error("document.execCommand('copy') returned false"); - } - - async function fallbackCopyText(text) { - const value = String(text); - const parent = document.body || document.documentElement; - if (!parent) throw new Error("No document body available for clipboard fallback"); - - const textarea = document.createElement("textarea"); - textarea.value = value; - textarea.setAttribute("readonly", ""); - textarea.setAttribute("aria-hidden", "true"); - textarea.style.position = "fixed"; - textarea.style.left = "-9999px"; - textarea.style.top = "0"; - textarea.style.width = "1px"; - textarea.style.height = "1px"; - textarea.style.opacity = "0"; - textarea.style.pointerEvents = "none"; - - parent.appendChild(textarea); - - const previousActiveElement = document.activeElement; - textarea.focus(); - textarea.select(); - textarea.setSelectionRange(0, value.length); - - const copied = document.execCommand("copy"); - textarea.remove(); - - if (previousActiveElement && typeof previousActiveElement.focus === "function") { - try { - previousActiveElement.focus(); - } catch {} - } - - if (!copied) throw new Error("document.execCommand('copy') returned false"); - } - - async function getClipboardItemType(item, type) { - if (!item?.types?.includes(type) || typeof item.getType !== "function") return null; - return await item.getType(type); - } - - async function fallbackCopyItems(items) { - const parent = document.body || document.documentElement; - if (!parent) throw new Error("No document body available for clipboard fallback"); - - const container = document.createElement("div"); - container.contentEditable = "true"; - container.setAttribute("aria-hidden", "true"); - container.style.position = "fixed"; - container.style.left = "-9999px"; - container.style.top = "0"; - container.style.width = "1px"; - container.style.height = "1px"; - container.style.overflow = "hidden"; - - for (const item of items) { - const htmlBlob = await getClipboardItemType(item, "text/html"); - if (htmlBlob) { - container.innerHTML += await htmlBlob.text(); - continue; - } - - const textBlob = await getClipboardItemType(item, "text/plain"); - if (textBlob) { - const span = document.createElement("span"); - span.textContent = await textBlob.text(); - container.appendChild(span); - continue; - } - - const imageType = item?.types?.find((type) => type.startsWith("image/")); - if (imageType && typeof item.getType === "function") { - const imageBlob = await item.getType(imageType); - const image = document.createElement("img"); - image.src = await blobToDataUrl(imageBlob); - image.alt = ""; - container.appendChild(image); - } - } - - if (!container.innerHTML && !container.textContent && !container.children?.length) { - throw new Error("No supported clipboard item types found"); - } - - parent.appendChild(container); - selectElementForCopy(container); - container.remove(); - } - - if (originalWriteText) { - Object.defineProperty(navigator.clipboard, "writeText", { - configurable: true, - value: async (text) => { - try { - await fallbackCopyText(text); - api.logger.log("copied text via fallback", text); - } catch (fallbackError) { - api.logger.warn("text fallback failed; trying original writeText", fallbackError); - return originalWriteText(text); - } - }, - }); - } - - if (originalWrite) { - Object.defineProperty(navigator.clipboard, "write", { - configurable: true, - value: async (items) => { - try { - await fallbackCopyItems(items); - api.logger.log("copied rich clipboard payload via fallback"); - } catch (fallbackError) { - api.logger.warn("rich clipboard fallback failed; trying original write", fallbackError); - return originalWrite(items); - } - }, - }); - } - - Object.defineProperty(navigator.clipboard, PATCH_KEY, { - configurable: false, - enumerable: false, - value: true, - }); - - api.logger.log("installed"); - } catch (error) { - api.logger.error("install failed", error); - } - } - - install(); - window.addEventListener("DOMContentLoaded", install, { once: true }); - }; - - if (typeof module.exports.activate === "function") { - module.exports.activate(api); - } -})(); diff --git a/docs/examples/clipboard-fallback-plugin/manifest.json b/docs/examples/clipboard-fallback-plugin/manifest.json deleted file mode 100644 index 7a509dd..0000000 --- a/docs/examples/clipboard-fallback-plugin/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "id": "clipboard-fallback", - "name": "Clipboard Fallback", - "version": "1.1.0", - "description": "Fixes Discord in-page text copy actions in Legcord by falling back to document.execCommand('copy') when navigator.clipboard.writeText is blocked.", - "author": "Nigel Thornberry", - "compatibleVersions": ["*"], - "renderer": "renderer.js" -} diff --git a/docs/examples/clipboard-fallback-plugin/renderer.js b/docs/examples/clipboard-fallback-plugin/renderer.js deleted file mode 100644 index 33cdb01..0000000 --- a/docs/examples/clipboard-fallback-plugin/renderer.js +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Legcord Clipboard Fallback - * - * Discord's web UI uses navigator.clipboard.writeText for actions such as - * "Copy User ID" and "Copy Message Link", and navigator.clipboard.write for - * richer clipboard payloads such as images. In some Legcord/Electron/macOS - * combinations Chromium rejects those calls because the document is not focused - * or the clipboard permission is not granted, leaving the clipboard unchanged. - * - * This renderer plugin replaces those APIs with selection-based copy fallbacks - * that run inside the original click gesture. - */ -module.exports.activate = (api) => { - const PATCH_KEY = Symbol.for("legcord.clipboardFallback.installed"); - - function install() { - try { - if (!navigator.clipboard) { - api.logger.warn("navigator.clipboard is unavailable"); - return; - } - - if (navigator.clipboard[PATCH_KEY]) return; - - const originalWriteText = navigator.clipboard.writeText?.bind(navigator.clipboard); - const originalWrite = navigator.clipboard.write?.bind(navigator.clipboard); - - async function blobToDataUrl(blob) { - if (typeof FileReader !== "undefined") { - return await new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(String(reader.result)); - reader.onerror = () => reject(reader.error ?? new Error("Failed to read clipboard image")); - reader.readAsDataURL(blob); - }); - } - - // Test/runtime fallback for environments with Blob but no FileReader. - const bytes = new Uint8Array(await blob.arrayBuffer()); - let binary = ""; - for (const byte of bytes) binary += String.fromCharCode(byte); - const base64 = typeof btoa === "function" ? btoa(binary) : Buffer.from(bytes).toString("base64"); - return `data:${blob.type || "application/octet-stream"};base64,${base64}`; - } - - function selectElementForCopy(element) { - const previousActiveElement = document.activeElement; - const selection = window.getSelection?.() ?? globalThis.getSelection?.(); - const range = document.createRange(); - - element.focus?.(); - range.selectNodeContents(element); - selection?.removeAllRanges(); - selection?.addRange(range); - - const copied = document.execCommand("copy"); - - selection?.removeAllRanges(); - if (previousActiveElement && typeof previousActiveElement.focus === "function") { - try { - previousActiveElement.focus(); - } catch {} - } - - if (!copied) throw new Error("document.execCommand('copy') returned false"); - } - - async function fallbackCopyText(text) { - const value = String(text); - const parent = document.body || document.documentElement; - if (!parent) throw new Error("No document body available for clipboard fallback"); - - const textarea = document.createElement("textarea"); - textarea.value = value; - textarea.setAttribute("readonly", ""); - textarea.setAttribute("aria-hidden", "true"); - textarea.style.position = "fixed"; - textarea.style.left = "-9999px"; - textarea.style.top = "0"; - textarea.style.width = "1px"; - textarea.style.height = "1px"; - textarea.style.opacity = "0"; - textarea.style.pointerEvents = "none"; - - parent.appendChild(textarea); - - const previousActiveElement = document.activeElement; - textarea.focus(); - textarea.select(); - textarea.setSelectionRange(0, value.length); - - const copied = document.execCommand("copy"); - textarea.remove(); - - if (previousActiveElement && typeof previousActiveElement.focus === "function") { - try { - previousActiveElement.focus(); - } catch {} - } - - if (!copied) throw new Error("document.execCommand('copy') returned false"); - } - - async function getClipboardItemType(item, type) { - if (!item?.types?.includes(type) || typeof item.getType !== "function") return null; - return await item.getType(type); - } - - async function fallbackCopyItems(items) { - const parent = document.body || document.documentElement; - if (!parent) throw new Error("No document body available for clipboard fallback"); - - const container = document.createElement("div"); - container.contentEditable = "true"; - container.setAttribute("aria-hidden", "true"); - container.style.position = "fixed"; - container.style.left = "-9999px"; - container.style.top = "0"; - container.style.width = "1px"; - container.style.height = "1px"; - container.style.overflow = "hidden"; - - for (const item of items) { - const htmlBlob = await getClipboardItemType(item, "text/html"); - if (htmlBlob) { - container.innerHTML += await htmlBlob.text(); - continue; - } - - const textBlob = await getClipboardItemType(item, "text/plain"); - if (textBlob) { - const span = document.createElement("span"); - span.textContent = await textBlob.text(); - container.appendChild(span); - continue; - } - - const imageType = item?.types?.find((type) => type.startsWith("image/")); - if (imageType && typeof item.getType === "function") { - const imageBlob = await item.getType(imageType); - const image = document.createElement("img"); - image.src = await blobToDataUrl(imageBlob); - image.alt = ""; - container.appendChild(image); - } - } - - if (!container.innerHTML && !container.textContent && !container.children?.length) { - throw new Error("No supported clipboard item types found"); - } - - parent.appendChild(container); - selectElementForCopy(container); - container.remove(); - } - - if (originalWriteText) { - Object.defineProperty(navigator.clipboard, "writeText", { - configurable: true, - value: async (text) => { - try { - await fallbackCopyText(text); - api.logger.log("copied text via fallback", text); - } catch (fallbackError) { - api.logger.warn("text fallback failed; trying original writeText", fallbackError); - return originalWriteText(text); - } - }, - }); - } - - if (originalWrite) { - Object.defineProperty(navigator.clipboard, "write", { - configurable: true, - value: async (items) => { - try { - await fallbackCopyItems(items); - api.logger.log("copied rich clipboard payload via fallback"); - } catch (fallbackError) { - api.logger.warn("rich clipboard fallback failed; trying original write", fallbackError); - return originalWrite(items); - } - }, - }); - } - - Object.defineProperty(navigator.clipboard, PATCH_KEY, { - configurable: false, - enumerable: false, - value: true, - }); - - api.logger.log("installed"); - } catch (error) { - api.logger.error("install failed", error); - } - } - - install(); - window.addEventListener("DOMContentLoaded", install, { once: true }); -}; diff --git a/docs/plugin-system.md b/docs/plugin-system.md index bc47ed1..bfe227c 100644 --- a/docs/plugin-system.md +++ b/docs/plugin-system.md @@ -115,11 +115,6 @@ A complete example is available at: - `docs/examples/hello-plugin/preload.js` - `docs/examples/hello-plugin/renderer.js` -A practical renderer-only workaround plugin is also available at: - -- `docs/examples/clipboard-fallback-plugin/manifest.json` -- `docs/examples/clipboard-fallback-plugin/renderer.js` - To test it: 1. Copy `docs/examples/hello-plugin` into your runtime plugins directory: diff --git a/electron-builder.ts b/electron-builder.ts index 06b45c9..9a6ec11 100644 --- a/electron-builder.ts +++ b/electron-builder.ts @@ -5,8 +5,6 @@ import { applyAppImageSandboxFix } from "./scripts/build/sandboxFix.mjs"; export const config: Configuration = { appId: "app.legcord.Legcord", productName: "Legcord", - // Biome treats electron-builder macro placeholders as template syntax. - // biome-ignore lint/suspicious/noTemplateCurlyInString: electron-builder expands these placeholders. artifactName: "Legcord-${version}-${os}-${arch}.${ext}", beforePack: applyAppImageSandboxFix, protocols: [ diff --git a/package.json b/package.json index 530b752..1a5c15a 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "node": ">=26" }, "scripts": { - "build:dev": "rollup -c --environment BUILD:dev && node --experimental-strip-types scripts/copyVenmic.ts", + "build:dev": "rollup -c --environment BUILD:dev && node scripts/copyVenmic.ts", "build:plugins": "lune ci --repoSubDir src/shelter --to ts-out/plugins", - "build": "pnpm build:plugins && rolldown -c rolldown.config.ts && node --experimental-strip-types scripts/copyVenmic.ts", + "build": "pnpm build:plugins && rolldown -c rolldown.config.ts && node scripts/copyVenmic.ts", "start": "pnpm run build && electron --trace-warnings --ozone-platform-hint=auto ./ts-out/main.js", "startThemeManager": "pnpm run build:dev && electron ./ts-out/main.js themes", "package": "pnpm run build && electron-builder", @@ -18,7 +18,7 @@ "lint:fix": "biome check --write", "postinstall": "electron-builder install-app-deps", "CIbuild": "pnpm run build && electron-builder --linux zip && electron-builder --windows zip && electron-builder --macos zip", - "updateMeta": "node --experimental-strip-types scripts/utils/updateMeta.ts" + "updateMeta": "node scripts/utils/updateMeta.ts" }, "repository": { "type": "git", diff --git a/src/cssEditor/main.ts b/src/cssEditor/main.ts index 9543d97..c6609cd 100644 --- a/src/cssEditor/main.ts +++ b/src/cssEditor/main.ts @@ -14,7 +14,7 @@ export function openCssEditor(file: string) { preload: path.join(import.meta.dirname, "cssEditor", "preload.mjs"), }, }); - cssWindow.loadURL("legcord://html/editor.html"); + cssWindow.loadURL(`file://${import.meta.dirname}/html/editor.html`); ipcMain.on("editor-setCSS", (_event, css: string) => { fs.writeFileSync(file, css); diff --git a/src/discord/window.ts b/src/discord/window.ts index e95a9b9..c969714 100644 --- a/src/discord/window.ts +++ b/src/discord/window.ts @@ -5,11 +5,9 @@ import { app, BrowserWindow, type BrowserWindowConstructorOptions, - clipboard, dialog, type MessageBoxOptions, nativeImage, - net, screen, shell, } from "electron"; @@ -77,42 +75,10 @@ function saveWindowState(win: BrowserWindow): void { } } -async function copyImageFromContext( - parameters: { srcURL: string; x: number; y: number }, - win?: BrowserWindow, -): Promise { - if (parameters.srcURL) { - try { - const response = await net.fetch(parameters.srcURL); - if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`); - - const image = nativeImage.createFromBuffer(Buffer.from(await response.arrayBuffer())); - if (!image.isEmpty()) { - clipboard.writeImage(image); - return; - } - } catch (error) { - console.warn("[ContextMenu] Failed to copy image from URL, falling back to copyImageAt:", error); - } - } - - win?.webContents.copyImageAt(parameters.x, parameters.y); -} - contextMenu({ showSaveImageAs: true, - showCopyImage: false, showCopyImageAddress: true, showSearchWithGoogle: false, - append: (_defaultActions, parameters, win) => [ - { - label: "Copy Image", - visible: parameters.mediaType === "image", - click: () => { - void copyImageFromContext(parameters, win as BrowserWindow | undefined); - }, - }, - ], prepend: (_defaultActions, parameters) => [ { label: getLang("contextMenu-searchGoogle"), @@ -457,7 +423,7 @@ function doAfterDefiningTheWindow(passedWindow: BrowserWindow): void { lastPolledBounds = { x, y, width, height }; saveWindowState(passedWindow); } - } catch (_e) { + } catch (e) { // ignore transient errors } }, 1000); @@ -554,7 +520,7 @@ export function createWindow() { mainWindow.setPosition(storedBounds.x, storedBounds.y); mainWindow.setSize(storedBounds.width, storedBounds.height); } - + mainWindows.push(mainWindow); doAfterDefiningTheWindow(mainWindow); } diff --git a/src/protocol.ts b/src/protocol.ts index 01c2507..10b92b6 100644 --- a/src/protocol.ts +++ b/src/protocol.ts @@ -29,26 +29,6 @@ void app.whenReady().then(() => { }); } return net.fetch(Url.pathToFileURL(filePath).toString()); - } else if (req.url.startsWith("legcord://html/")) { - const file = req.url.replace("legcord://html/", ""); - const filePath = path.join(import.meta.dirname, "html", `${file}`); - if (filePath.includes("..")) { - return new Response("bad", { - status: 400, - headers: { "content-type": "text/html" }, - }); - } - return net.fetch(Url.pathToFileURL(filePath).toString()); - } else if (req.url.startsWith("legcord://js/")) { - const file = req.url.replace("legcord://js/", ""); - const filePath = path.join(import.meta.dirname, "js", `${file}`); - if (filePath.includes("..")) { - return new Response("bad", { - status: 400, - headers: { "content-type": "text/html" }, - }); - } - return net.fetch(Url.pathToFileURL(filePath).toString()); } else if (req.url.startsWith("legcord://assets/")) { const file = req.url.replace("legcord://assets/", ""); const filePath = path.join(import.meta.dirname, "assets", "app", `${file}`); @@ -59,16 +39,6 @@ void app.whenReady().then(() => { }); } return net.fetch(Url.pathToFileURL(filePath).toString()); - } else if (req.url.startsWith("legcord://css/")) { - const file = req.url.replace("legcord://css/", ""); - const filePath = path.join(import.meta.dirname, "css", `${file}`); - if (filePath.includes("..")) { - return new Response("bad", { - status: 400, - headers: { "content-type": "text/html" }, - }); - } - return net.fetch(Url.pathToFileURL(filePath).toString()); } else if (req.url.startsWith("legcord://local/")) { const file = req.url.replace("legcord://local/", ""); const userDataPath = path.join(app.getPath("userData"), "userAssets"); diff --git a/src/setup/main.ts b/src/setup/main.ts index 697c8c1..eadede6 100644 --- a/src/setup/main.ts +++ b/src/setup/main.ts @@ -22,6 +22,7 @@ export async function createSetupWindow(): Promise { maximizable: false, autoHideMenuBar: true, webPreferences: { + sandbox: true, spellcheck: false, preload: path.join(import.meta.dirname, "setup", "preload.mjs"), }, @@ -69,6 +70,6 @@ export async function createSetupWindow(): Promise { // workaround electron trying to relaunch from squashfs handleRestart(); }); - void setupWindow.loadURL("legcord://html/setup.html"); + void setupWindow.loadFile(path.join(import.meta.dirname, "/html/setup.html")); }); } diff --git a/src/splash/main.ts b/src/splash/main.ts index 4ace7fa..c4d5433 100644 --- a/src/splash/main.ts +++ b/src/splash/main.ts @@ -31,5 +31,5 @@ export async function createSplashWindow(): Promise { ipcMain.on("splash-clientmod", (event) => { event.returnValue = getConfig("mods"); }); - await splashWindow.loadURL("legcord://html/splash.html"); + await splashWindow.loadFile(path.join(import.meta.dirname, "html", "splash.html")); }