From 593f6251a7fcfe5dc6d5b11cb405a67c861430cd Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 10 Jun 2026 12:59:20 -0400 Subject: [PATCH] equicord ipc --- src/Vencord.ts | 27 ++++++++++++- src/VencordNative.ts | 3 ++ .../themeLibrary/components/ThemeCard.tsx | 2 +- .../components/ThemeInfoModal.tsx | 6 ++- src/main/index.ts | 40 +++++++++++++++++++ src/shared/IpcEvents.ts | 1 + 6 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/Vencord.ts b/src/Vencord.ts index 08ee7e68a..64b98485d 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -31,11 +31,13 @@ export { PlainSettings, Settings }; import { coreStyleRootNode, initStyles } from "@api/Styles"; import { openSettingsTabModal, UpdaterTab } from "@components/settings"; +import { makeDummyUser } from "@equicordplugins/themeLibrary/components/ThemeCard"; +import { openThemeInfoModal } from "@equicordplugins/themeLibrary/components/ThemeInfoModal"; import { debounce } from "@shared/debounce"; import { IS_WINDOWS } from "@utils/constants"; import { createAndAppendStyle } from "@utils/css"; import { StartAt } from "@utils/types"; -import { SettingsRouter } from "@webpack/common"; +import { SettingsRouter, UserUtils } from "@webpack/common"; import { get as dsGet } from "./api/DataStore"; import { popNotice, showNotice } from "./api/Notices"; @@ -198,12 +200,35 @@ function initTrayIpc() { VencordNative.tray.setUpdateState(getIsOutdated); } +function initProtocol() { + if (IS_WEB || IS_UPDATER_DISABLED) return; + + VencordNative.themes.onInstallTheme(async id => { + try { + const res = await fetch("https://themes.equicord.org/api/themes"); + const themes = await res.json(); + const theme = themes.find(t => String(t.id) === String(id)); + if (!theme) throw new Error("Theme not found"); + + const getUser = (id, username) => UserUtils.getUser(id) ?? makeDummyUser({ username, id }); + const authors = Array.isArray(theme.author) + ? await Promise.all(theme.author.map(author => getUser(author.discord_snowflake, author.discord_name))) + : [await getUser(theme.author.discord_snowflake, theme.author.discord_name)]; + + openThemeInfoModal(theme, authors); + } catch (err) { + console.error("[Equicord] Failed to handle install-theme IPC", err); + } + }); +} + async function init() { await onceReady; startAllPlugins(StartAt.WebpackReady); syncSettings(); initTrayIpc(); + initProtocol(); if (!IS_DEV && !IS_WEB && !IS_UPDATER_DISABLED) { runUpdateCheck(); diff --git a/src/VencordNative.ts b/src/VencordNative.ts index 59969777f..0d9e9d7ae 100644 --- a/src/VencordNative.ts +++ b/src/VencordNative.ts @@ -41,6 +41,9 @@ export default { getSystemValues: () => invoke>(IpcEvents.GET_THEME_SYSTEM_VALUES), openFolder: () => invoke(IpcEvents.OPEN_THEMES_FOLDER), + onInstallTheme: (cb: (id: string) => void) => { + ipcRenderer.on(IpcEvents.INSTALL_THEME, (_, id) => cb(id)); + } }, updater: { diff --git a/src/equicordplugins/themeLibrary/components/ThemeCard.tsx b/src/equicordplugins/themeLibrary/components/ThemeCard.tsx index a2b6fc4e3..718e9b73b 100644 --- a/src/equicordplugins/themeLibrary/components/ThemeCard.tsx +++ b/src/equicordplugins/themeLibrary/components/ThemeCard.tsx @@ -32,7 +32,7 @@ interface ThemeCardProps { const UserRecord: Constructor> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any; -function makeDummyUser(user: { username: string; id?: string; avatar?: string; }) { +export function makeDummyUser(user: { username: string; id?: string; avatar?: string; }) { const newUser = new UserRecord({ username: user.username, id: user.id ?? generateId(), diff --git a/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx b/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx index 5adf4c473..95265a76c 100644 --- a/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx +++ b/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx @@ -148,7 +148,7 @@ export const ThemeInfoModal: React.FC = ({ author, theme, . @@ -239,3 +239,7 @@ export const ThemeInfoModal: React.FC = ({ author, theme, . ); }; + +export function openThemeInfoModal(theme: Theme, authors: any[]) { + openModal(props => ); +} diff --git a/src/main/index.ts b/src/main/index.ts index 80ad5e5c8..53e32d1fd 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -27,6 +27,46 @@ import { IS_VANILLA, THEMES_DIR } from "./utils/constants"; import { installExt } from "./utils/extensions"; if (!IS_VANILLA && !IS_EXTENSION) { + app.setAsDefaultProtocolClient("equicord"); + + const handleEquicordUrl = (urlStr: string) => { + try { + const url = new URL(urlStr); + if (url.protocol === "equicord:") { + switch (url.hostname) { + case "install-theme": + const id = url.searchParams.get("id"); + if (id) { + const { BrowserWindow } = require("electron"); + BrowserWindow.getAllWindows().forEach(win => { + win.webContents.send("VencordInstallTheme", id); + }); + } + break; + + } + } + } catch (e) { + console.error("[Equicord] Failed to parse protocol URL", e); + } + }; + + app.on("second-instance", (event, commandLine, workingDirectory) => { + const url = commandLine.find(arg => arg.startsWith("equicord://")); + if (url) { + console.log("[Equicord] Protocol URL opened:", url); + handleEquicordUrl(url); + } + }); + + app.on("open-url", (event, url) => { + if (url.startsWith("equicord://")) { + event.preventDefault(); + console.log("[Equicord] Protocol URL opened:", url); + handleEquicordUrl(url); + } + }); + app.whenReady().then(() => { protocol.handle("vencord", ({ url: unsafeUrl }) => { let url = decodeURI(unsafeUrl).slice("vencord://".length).replace(/\?v=\d+$/, ""); diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index 0a3c12a56..84db6574b 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -41,6 +41,7 @@ export const enum IpcEvents { GET_MONACO_THEME = "VencordGetMonacoTheme", GET_PLUGIN_IPC_METHOD_MAP = "VencordGetPluginIpcMethodMap", + INSTALL_THEME = "VencordInstallTheme", CSP_IS_DOMAIN_ALLOWED = "VencordCspIsDomainAllowed", CSP_REMOVE_OVERRIDE = "VencordCspRemoveOverride",