diff --git a/src/Vencord.ts b/src/Vencord.ts index 0f2b25a..b0ff540 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -1,6 +1,7 @@ export * as Plugins from "./plugins"; export * as Webpack from "./webpack"; export * as Api from "./api"; +export { Settings } from "./api/settings"; import "./utils/patchWebpack"; import "./utils/quickCss"; diff --git a/src/api/settings.ts b/src/api/settings.ts index a6f24a4..8646412 100644 --- a/src/api/settings.ts +++ b/src/api/settings.ts @@ -5,6 +5,7 @@ import { mergeDefaults } from '../utils/misc'; interface Settings { unsafeRequire: boolean; + useQuickCss: boolean; plugins: { [plugin: string]: { enabled: boolean; @@ -15,8 +16,9 @@ interface Settings { const DefaultSettings: Settings = { unsafeRequire: false, + useQuickCss: true, plugins: {} -}; +} as any; for (const plugin in plugins) { DefaultSettings.plugins[plugin] = { @@ -35,21 +37,26 @@ try { var settings = mergeDefaults({} as Settings, DefaultSettings); } -const subscriptions = new Set<() => void>(); +type SubscriptionCallback = ((newValue: any) => void) & { _path?: string; }; +const subscriptions = new Set(); -function makeProxy(settings: Settings, root = settings): Settings { +function makeProxy(settings: Settings, root = settings, path = ""): Settings { return new Proxy(settings, { - get(target, p) { + get(target, p: string) { const v = target[p]; - if (typeof v === "object" && !Array.isArray(v)) return makeProxy(v, root); + if (typeof v === "object" && !Array.isArray(v)) + return makeProxy(v, root, `${path}${path && "."}${p}`); return v; }, - set(target, p, v) { + set(target, p: string, v) { if (target[p] === v) return true; target[p] = v; + const setPath = `${path}${path && "."}${p}`; for (const subscription of subscriptions) { - subscription(); + if (!subscription._path || subscription._path === setPath) { + subscription(v); + } } VencordNative.ipc.invoke(IpcEvents.SET_SETTINGS, JSON.stringify(root, null, 4)); return true; @@ -78,4 +85,16 @@ export function useSettings() { }, []); return Settings; +} + +// Resolves a possibly nested prop in the form of "some.nested.prop" to type of T.some.nested.prop +type ResolvePropDeep = P extends "" ? T : + P extends `${infer Pre}.${infer Suf}` ? + Pre extends keyof T ? ResolvePropDeep : never : P extends keyof T ? T[P] : never; + +export function addSettingsListener(path: Path, onUpdate: (newValue: Settings[Path]) => void): void; +export function addSettingsListener(path: Path, onUpdate: (newValue: ResolvePropDeep) => void): void; +export function addSettingsListener(path: string, onUpdate: (newValue: any) => void) { + (onUpdate as SubscriptionCallback)._path = path; + subscriptions.add(onUpdate); } \ No newline at end of file diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index c7e73be..1e13d7f 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -26,6 +26,8 @@ export default ErrorBoundary.wrap(function Settings(props) { return o; }, []); + const sortedPlugins = React.useMemo(() => Object.values(Plugins).sort((a, b) => a.name.localeCompare(b.name)), []); + return ( SettingsDir: {settingsDir} @@ -50,16 +52,23 @@ export default ErrorBoundary.wrap(function Settings(props) { Settings + settings.useQuickCss = v} + note="Enable QuickCss" + > + Use QuickCss + settings.unsafeRequire = v} note="Enables VencordNative.require. Useful for testing, very bad for security. Leave this off unless you need it." > - Enable Ensafe Require + Enable Unsafe Require Plugins - {Object.values(Plugins).map(p => { + {sortedPlugins.map(p => { const enabledDependants = depMap[p.name]?.filter(d => settings.plugins[d].enabled); const dependency = enabledDependants?.length; diff --git a/src/utils/quickCss.ts b/src/utils/quickCss.ts index 5c9e830..eea44df 100644 --- a/src/utils/quickCss.ts +++ b/src/utils/quickCss.ts @@ -1,8 +1,21 @@ +import { addSettingsListener, Settings } from "../api/settings"; import IpcEvents from "./IpcEvents"; -document.addEventListener("DOMContentLoaded", async () => { - const style = document.createElement("style"); - document.head.appendChild(style); - VencordNative.ipc.on(IpcEvents.QUICK_CSS_UPDATE, (_, css: string) => style.innerText = css); - style.innerText = await VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS); +let style: HTMLStyleElement; + +export async function toggle(isEnabled: boolean) { + if (!style) { + if (isEnabled) { + style = document.createElement("style"); + style.id = "vencord-custom-css"; + document.head.appendChild(style); + VencordNative.ipc.on(IpcEvents.QUICK_CSS_UPDATE, (_, css: string) => style.innerText = css); + style.innerText = await VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS); + } + } else style.disabled = !isEnabled; +} + +document.addEventListener("DOMContentLoaded", () => { + toggle(Settings.useQuickCss); + addSettingsListener("useQuickCss", toggle); });