Make Settings & Settings Page
This commit is contained in:
parent
cb288e204d
commit
98cb301df5
18 changed files with 330 additions and 43 deletions
|
@ -117,7 +117,7 @@ await Promise.all([
|
|||
],
|
||||
sourcemap: "inline",
|
||||
watch,
|
||||
minify: true
|
||||
minify: false,
|
||||
})
|
||||
]).then(res => {
|
||||
const took = performance.now() - begin;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export * as Plugins from "./plugins";
|
||||
export * as Webpack from "./utils/webpack";
|
||||
export * as Webpack from "./webpack";
|
||||
export * as Api from "./api";
|
||||
export * as Components from "./components";
|
||||
|
||||
|
|
|
@ -1,12 +1,33 @@
|
|||
import { IPC_QUICK_CSS_UPDATE, IPC_GET_QUICK_CSS } from './utils/ipcEvents';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import IPC_EVENTS from './utils/IpcEvents';
|
||||
import { IpcRenderer, ipcRenderer } from 'electron';
|
||||
|
||||
export default {
|
||||
handleQuickCssUpdate(cb: (s: string) => void) {
|
||||
ipcRenderer.on(IPC_QUICK_CSS_UPDATE, (_, css) => {
|
||||
cb(css);
|
||||
});
|
||||
getVersions: () => process.versions,
|
||||
ipc: {
|
||||
send(event: string, ...args: any[]) {
|
||||
if (event in IPC_EVENTS) ipcRenderer.send(event, ...args);
|
||||
else throw new Error(`Event ${event} not allowed.`);
|
||||
},
|
||||
getQuickCss: () => ipcRenderer.invoke(IPC_GET_QUICK_CSS) as Promise<string>,
|
||||
getVersions: () => process.versions
|
||||
sendSync(event: string, ...args: any[]) {
|
||||
if (event in IPC_EVENTS) return ipcRenderer.sendSync(event, ...args);
|
||||
else throw new Error(`Event ${event} not allowed.`);
|
||||
},
|
||||
on(event: string, listener: Parameters<IpcRenderer["on"]>[1]) {
|
||||
if (event in IPC_EVENTS) ipcRenderer.on(event, listener);
|
||||
else throw new Error(`Event ${event} not allowed.`);
|
||||
},
|
||||
invoke(event: string, ...args: any[]) {
|
||||
if (event in IPC_EVENTS) return ipcRenderer.invoke(event, ...args);
|
||||
else throw new Error(`Event ${event} not allowed.`);
|
||||
}
|
||||
},
|
||||
require(mod: string) {
|
||||
const settings = ipcRenderer.sendSync(IPC_EVENTS.GET_SETTINGS);
|
||||
try {
|
||||
if (!JSON.parse(settings).unsafeRequire) throw "no";
|
||||
} catch {
|
||||
throw new Error("Unsafe require is not allowed. Enable it in settings and try again.");
|
||||
}
|
||||
return require(mod);
|
||||
}
|
||||
};
|
81
src/api/settings.ts
Normal file
81
src/api/settings.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
import plugins from "plugins";
|
||||
import IpcEvents from "../utils/IpcEvents";
|
||||
import { React } from "../webpack";
|
||||
import { mergeDefaults } from '../utils/misc';
|
||||
|
||||
interface Settings {
|
||||
unsafeRequire: boolean;
|
||||
plugins: {
|
||||
[plugin: string]: {
|
||||
enabled: boolean;
|
||||
[setting: string]: any;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const DefaultSettings: Settings = {
|
||||
unsafeRequire: false,
|
||||
plugins: {}
|
||||
};
|
||||
|
||||
for (const plugin of plugins) {
|
||||
DefaultSettings.plugins[plugin.name] = {
|
||||
enabled: plugin.required ?? false
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
var settings = JSON.parse(VencordNative.ipc.sendSync(IpcEvents.GET_SETTINGS)) as Settings;
|
||||
for (const key in DefaultSettings) {
|
||||
settings[key] ??= DefaultSettings[key];
|
||||
}
|
||||
mergeDefaults(settings, DefaultSettings);
|
||||
} catch (err) {
|
||||
console.error("Corrupt settings file. ", err);
|
||||
var settings = mergeDefaults({} as Settings, DefaultSettings);
|
||||
}
|
||||
|
||||
const subscriptions = new Set<() => void>();
|
||||
|
||||
function makeProxy(settings: Settings, root = settings): Settings {
|
||||
return new Proxy(settings, {
|
||||
get(target, p) {
|
||||
const v = target[p];
|
||||
if (typeof v === "object" && !Array.isArray(v)) return makeProxy(v, root);
|
||||
return v;
|
||||
},
|
||||
set(target, p, v) {
|
||||
if (target[p] === v) return true;
|
||||
|
||||
target[p] = v;
|
||||
for (const subscription of subscriptions) {
|
||||
subscription();
|
||||
}
|
||||
VencordNative.ipc.invoke(IpcEvents.SET_SETTINGS, JSON.stringify(root));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A smart settings object. Altering props automagically saves
|
||||
* the updated settings to disk.
|
||||
*/
|
||||
export const Settings = makeProxy(settings);
|
||||
|
||||
/**
|
||||
* Settings hook for React components. Returns a smart settings
|
||||
* object that automagically triggers a rerender if any properties
|
||||
* are altered
|
||||
* @returns Settings
|
||||
*/
|
||||
export function useSettings() {
|
||||
const [, forceUpdate] = React.useReducer(x => ({}), {});
|
||||
|
||||
React.useEffect(() => {
|
||||
subscriptions.add(forceUpdate);
|
||||
return () => void subscriptions.delete(forceUpdate);
|
||||
}, []);
|
||||
|
||||
return Settings;
|
||||
}
|
|
@ -1,4 +1,84 @@
|
|||
import { lazy, LazyComponent, useAwaiter } from "../utils/misc";
|
||||
import { findByDisplayName, Forms } from '../webpack';
|
||||
import Plugins from 'plugins';
|
||||
import { useSettings } from "../api/settings";
|
||||
import { findByProps } from '../webpack/index';
|
||||
import IpcEvents from "../utils/IpcEvents";
|
||||
|
||||
// Lazy spam because this is ran before React is a thing. Todo: Fix that and clean this up lmao
|
||||
|
||||
const SwitchItem = LazyComponent<React.PropsWithChildren<{
|
||||
value: boolean;
|
||||
onChange: (v: boolean) => void;
|
||||
note?: string;
|
||||
tooltipNote?: string;
|
||||
disabled?: boolean;
|
||||
}>>(() => findByDisplayName("SwitchItem").default);
|
||||
|
||||
const getButton = lazy(() => findByProps("ButtonLooks", "default"));
|
||||
const Button = LazyComponent(() => getButton().default);
|
||||
const getFlex = lazy(() => findByDisplayName("Flex"));
|
||||
const Flex = LazyComponent(() => getFlex().default);
|
||||
const FlexChild = LazyComponent(() => getFlex().default.Child);
|
||||
const getMargins = lazy(() => findByProps("marginTop8", "marginBottom8"));
|
||||
|
||||
export default function Settings(props) {
|
||||
console.log(props);
|
||||
return (<p>Hi</p>);
|
||||
const settingsDir = useAwaiter(() => VencordNative.ipc.invoke(IpcEvents.GET_SETTINGS_DIR), "Loading...");
|
||||
const settings = useSettings();
|
||||
|
||||
return (
|
||||
<Forms.FormSection tag="h1" title="Vencord">
|
||||
<Forms.FormText>SettingsDir: {settingsDir}</Forms.FormText>
|
||||
<Flex className={getMargins().marginTop8 + " " + getMargins().marginBottom8}>
|
||||
<FlexChild>
|
||||
<Button
|
||||
onClick={() => VencordNative.ipc.invoke(IpcEvents.OPEN_PATH, settingsDir)}
|
||||
size={getButton().ButtonSizes.SMALL}
|
||||
disabled={settingsDir === "Loading..."}
|
||||
>
|
||||
Launch Directory
|
||||
</Button>
|
||||
</FlexChild>
|
||||
<FlexChild>
|
||||
<Button
|
||||
onClick={() => VencordNative.ipc.invoke(IpcEvents.OPEN_PATH, settingsDir + "/quickCss.css")}
|
||||
size={getButton().ButtonSizes.SMALL}
|
||||
disabled={settingsDir === "Loading..."}
|
||||
>
|
||||
Open QuickCSS File
|
||||
</Button>
|
||||
</FlexChild>
|
||||
</Flex>
|
||||
<Forms.FormTitle tag="h5">Settings</Forms.FormTitle>
|
||||
<SwitchItem
|
||||
value={settings.unsafeRequire}
|
||||
onChange={v => settings.unsafeRequire = v}
|
||||
note="Enables VencordNative.require. Useful for testing, very bad for security. Leave this off unless you need it."
|
||||
>
|
||||
Enable Ensafe Require
|
||||
</SwitchItem>
|
||||
<Forms.FormDivider />
|
||||
<Forms.FormTitle tag="h5">Plugins</Forms.FormTitle>
|
||||
{Plugins.map(p => (
|
||||
<SwitchItem
|
||||
disabled={p.required === true}
|
||||
key={p.name}
|
||||
value={settings.plugins[p.name].enabled}
|
||||
onChange={v => {
|
||||
settings.plugins[p.name].enabled = v;
|
||||
if (v) {
|
||||
p.dependencies?.forEach(d => {
|
||||
settings.plugins[d].enabled = true;
|
||||
});
|
||||
}
|
||||
}}
|
||||
note={p.description}
|
||||
tooltipNote={p.required ? "This plugin is required. Thus you cannot disable it." : undefined}
|
||||
>
|
||||
{p.name}
|
||||
</SwitchItem>
|
||||
))
|
||||
}
|
||||
</Forms.FormSection >
|
||||
);
|
||||
}
|
|
@ -1,25 +1,39 @@
|
|||
import { app, BrowserWindow, ipcMain } from "electron";
|
||||
import { fstat, watch } from "fs";
|
||||
import { open, readFile } from "fs/promises";
|
||||
import { app, BrowserWindow, ipcMain, shell } from "electron";
|
||||
import { readFileSync, watch } from "fs";
|
||||
import { open, readFile, writeFile } from "fs/promises";
|
||||
import { join } from 'path';
|
||||
import { IPC_GET_SETTINGS_DIR, IPC_GET_QUICK_CSS, IPC_QUICK_CSS_UPDATE } from './utils/ipcEvents';
|
||||
import IpcEvents from './utils/IpcEvents';
|
||||
|
||||
const DATA_DIR = join(app.getPath("userData"), "..", "Vencord");
|
||||
const SETTINGS_DIR = join(DATA_DIR, "settings");
|
||||
const QUICKCSS_PATH = join(SETTINGS_DIR, "quickCss.css");
|
||||
const SETTINGS_FILE = join(SETTINGS_DIR, "settings.json");
|
||||
|
||||
function readCss() {
|
||||
return readFile(QUICKCSS_PATH, "utf-8").catch(() => "");
|
||||
}
|
||||
|
||||
ipcMain.handle(IPC_GET_SETTINGS_DIR, () => SETTINGS_DIR);
|
||||
ipcMain.handle(IPC_GET_QUICK_CSS, () => readCss());
|
||||
function readSettings() {
|
||||
try {
|
||||
return readFileSync(SETTINGS_FILE, "utf-8");
|
||||
} catch {
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
|
||||
ipcMain.handle(IpcEvents.GET_SETTINGS_DIR, () => SETTINGS_DIR);
|
||||
ipcMain.handle(IpcEvents.GET_QUICK_CSS, () => readCss());
|
||||
// .on because we need Settings synchronously (ipcRenderer.sendSync)
|
||||
ipcMain.on(IpcEvents.GET_SETTINGS, (e) => e.returnValue = readSettings());
|
||||
ipcMain.handle(IpcEvents.SET_SETTINGS, (_, s) => void writeFile(SETTINGS_FILE, s));
|
||||
ipcMain.handle(IpcEvents.OPEN_PATH, (_, path) => shell.openPath(path));
|
||||
ipcMain.handle(IpcEvents.OPEN_EXTERNAL, (_, url) => shell.openExternal(url));
|
||||
|
||||
export function initIpc(mainWindow: BrowserWindow) {
|
||||
open(QUICKCSS_PATH, "a+").then(fd => {
|
||||
fd.close();
|
||||
watch(QUICKCSS_PATH, async () => {
|
||||
mainWindow.webContents.postMessage(IPC_QUICK_CSS_UPDATE, await readCss());
|
||||
mainWindow.webContents.postMessage(IpcEvents.QUICK_CSS_UPDATE, await readCss());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Plugins from "plugins";
|
||||
import { Settings } from "../api/settings";
|
||||
import Logger from "../utils/logger";
|
||||
import { Patch } from "../utils/types";
|
||||
|
||||
|
@ -7,7 +8,7 @@ const logger = new Logger("PluginManager", "#a6d189");
|
|||
export const plugins = Plugins;
|
||||
export const patches = [] as Patch[];
|
||||
|
||||
for (const plugin of Plugins) if (plugin.patches) {
|
||||
for (const plugin of Plugins) if (plugin.patches && Settings.plugins[plugin.name].enabled) {
|
||||
for (const patch of plugin.patches) {
|
||||
patch.plugin = plugin.name;
|
||||
if (!Array.isArray(patch.replacement)) patch.replacement = [patch.replacement];
|
||||
|
@ -16,7 +17,7 @@ for (const plugin of Plugins) if (plugin.patches) {
|
|||
}
|
||||
|
||||
export function startAll() {
|
||||
for (const plugin of plugins) if (plugin.start) {
|
||||
for (const plugin of plugins) if (plugin.start && Settings.plugins[plugin.name].enabled) {
|
||||
try {
|
||||
logger.info("Starting plugin", plugin.name);
|
||||
plugin.start();
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { MessageClicks } from "../api";
|
||||
import definePlugin from "../utils/types";
|
||||
import { find, findByProps } from "../utils/webpack";
|
||||
import { find, findByProps } from "../webpack";
|
||||
|
||||
export default definePlugin({
|
||||
name: "MessageQuickActions",
|
||||
description: "Quick Delete, Quick edit",
|
||||
author: "Vendicated",
|
||||
dependencies: ["MessageClicksApi"],
|
||||
start() {
|
||||
const { deleteMessage, startEditMessage } = findByProps("deleteMessage");
|
||||
const { can } = findByProps("can", "initialize");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import definePlugin from "../utils/types";
|
||||
import { findByProps } from "../utils/webpack";
|
||||
import { findByProps } from "../webpack";
|
||||
|
||||
const DO_NOTHING = () => void 0;
|
||||
|
||||
|
@ -7,6 +7,7 @@ export default definePlugin({
|
|||
name: "NoTrack",
|
||||
description: "Disable Discord's tracking and crash reporting",
|
||||
author: "Vendicated",
|
||||
required: true,
|
||||
start() {
|
||||
findByProps("getSuperPropertiesBase64", "track").track = DO_NOTHING;
|
||||
findByProps("submitLiveCrashReport").submitLiveCrashReport = DO_NOTHING;
|
||||
|
|
|
@ -5,6 +5,7 @@ export default definePlugin({
|
|||
name: "Settings",
|
||||
description: "Adds Settings UI and debug info",
|
||||
author: "Vendicated",
|
||||
required: true,
|
||||
patches: [{
|
||||
find: "default.versionHash",
|
||||
replacement: [
|
||||
|
@ -28,7 +29,7 @@ export default definePlugin({
|
|||
}, {
|
||||
find: "Messages.ACTIVITY_SETTINGS",
|
||||
replacement: {
|
||||
match: /\{section:(.{1,2})\.SectionTypes\.HEADER,label:(.{1,2})\.default\.Messages\.ACTIVITY_SETTINGS\}/,
|
||||
match: /\{section:(.{1,2})\.SectionTypes\.HEADER,\s*label:(.{1,2})\.default\.Messages\.ACTIVITY_SETTINGS\}/,
|
||||
replace: (m, mod) =>
|
||||
`{section:${mod}.SectionTypes.HEADER,label:"Vencord"},` +
|
||||
`{section:"Vencord",label:"Vencord",element:Vencord.Components.Settings},` +
|
||||
|
|
22
src/utils/IpcEvents.ts
Normal file
22
src/utils/IpcEvents.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
type Enum<T extends Record<string, string>> = {
|
||||
[k in keyof T]: T[k];
|
||||
} & { [v in keyof T as T[v]]: v; };
|
||||
|
||||
function strEnum<T extends Record<string, string>>(obj: T): T {
|
||||
const o = {} as T;
|
||||
for (const key in obj) {
|
||||
o[key] = obj[key] as any;
|
||||
o[obj[key]] = key as any;
|
||||
};
|
||||
return o;
|
||||
}
|
||||
|
||||
export default strEnum({
|
||||
QUICK_CSS_UPDATE: "VencordQuickCssUpdate",
|
||||
GET_QUICK_CSS: "VencordGetQuickCss",
|
||||
GET_SETTINGS_DIR: "VencordGetSettingsDir",
|
||||
GET_SETTINGS: "VencordGetSettings",
|
||||
SET_SETTINGS: "VencordSetSettings",
|
||||
OPEN_EXTERNAL: "VencordOpenExternal",
|
||||
OPEN_PATH: "VencordOpenPath",
|
||||
} as const);
|
|
@ -1,3 +0,0 @@
|
|||
export const IPC_QUICK_CSS_UPDATE = "VencordQuickCssUpdate";
|
||||
export const IPC_GET_QUICK_CSS = "VencordGetQuickCss";
|
||||
export const IPC_GET_SETTINGS_DIR = "VencordGetSettingsDir";
|
61
src/utils/misc.tsx
Normal file
61
src/utils/misc.tsx
Normal file
|
@ -0,0 +1,61 @@
|
|||
import { React } from "../webpack";
|
||||
|
||||
/**
|
||||
* Makes a lazy function. On first call, the value is computed.
|
||||
* On subsequent calls, the same computed value will be returned
|
||||
* @param factory Factory function
|
||||
*/
|
||||
export function lazy<T>(factory: () => T): () => T {
|
||||
let cache: T;
|
||||
return () => {
|
||||
return cache ?? (cache = factory());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Await a promise
|
||||
* @param factory Factory
|
||||
* @param fallbackValue The fallback value that will be used until the promise resolved
|
||||
* @returns A state that will either be null or the result of the promise
|
||||
*/
|
||||
export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: T | null = null): T | null {
|
||||
const [res, setRes] = React.useState<T | null>(fallbackValue);
|
||||
|
||||
React.useEffect(() => {
|
||||
factory().then(setRes);
|
||||
}, []);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* A lazy component. The factory method is called on first render. For example useful
|
||||
* for const Component = LazyComponent(() => findByDisplayName("...").default)
|
||||
* @param factory Function returning a Component
|
||||
* @returns Result of factory function
|
||||
*/
|
||||
export function LazyComponent<T = any>(factory: () => React.ComponentType<T>) {
|
||||
return (props: T) => {
|
||||
const Component = React.useMemo(factory, []);
|
||||
return <Component {...props} />;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively merges defaults into an object and returns the same object
|
||||
* @param obj Object
|
||||
* @param defaults Defaults
|
||||
* @returns obj
|
||||
*/
|
||||
export function mergeDefaults<T>(obj: T, defaults: T): T {
|
||||
for (const key in defaults) {
|
||||
const v = defaults[key];
|
||||
if (typeof v === "object" && !Array.isArray(v)) {
|
||||
obj[key] ??= {} as any;
|
||||
mergeDefaults(obj[key], v);
|
||||
} else {
|
||||
obj[key] ??= v;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { WEBPACK_CHUNK } from './constants';
|
||||
import Logger from "./logger";
|
||||
import { _initWebpack } from "./webpack";
|
||||
import { _initWebpack } from "../webpack";
|
||||
|
||||
let webpackChunk: any[];
|
||||
|
||||
|
@ -83,9 +83,13 @@ function patchPush() {
|
|||
const lastCode = code;
|
||||
try {
|
||||
const newCode = code.replace(replacement.match, replacement.replace);
|
||||
if (newCode === code) {
|
||||
logger.warn(`Patch by ${patch.plugin} had no effect: ${replacement.match}`);
|
||||
} else {
|
||||
const newMod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`);
|
||||
code = newCode;
|
||||
mod = newMod;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Failed to apply patch of", patch.plugin, err);
|
||||
code = lastCode;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import IpcEvents from "./IpcEvents";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
const style = document.createElement("style");
|
||||
document.head.appendChild(style);
|
||||
VencordNative.handleQuickCssUpdate((css: string) => style.innerText = css);
|
||||
style.innerText = await VencordNative.getQuickCss();
|
||||
VencordNative.ipc.on(IpcEvents.QUICK_CSS_UPDATE, (_, css: string) => style.innerText = css);
|
||||
style.innerText = await VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS);
|
||||
});
|
||||
|
|
|
@ -20,6 +20,8 @@ export interface Plugin {
|
|||
author: string;
|
||||
start?(): void;
|
||||
patches?: Patch[];
|
||||
dependencies?: string[],
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
// @ts-ignore lole
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { startAll } from "../plugins";
|
||||
import Logger from "./logger";
|
||||
|
||||
let webpackCache: typeof window.webpackChunkdiscord_app;
|
||||
|
||||
|
@ -9,11 +8,10 @@ export const listeners = new Set<CallbackFn>();
|
|||
type FilterFn = (mod: any) => boolean;
|
||||
type CallbackFn = (mod: any) => void;
|
||||
|
||||
export let Common: {
|
||||
React: typeof import("react"),
|
||||
FluxDispatcher: any;
|
||||
UserStore: any;
|
||||
} = {} as any;
|
||||
export let React: typeof import("react");
|
||||
export let FluxDispatcher: any;
|
||||
export let Forms: any;
|
||||
export let UserStore: any;
|
||||
|
||||
export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
|
||||
if (webpackCache !== void 0) throw "no.";
|
||||
|
@ -24,9 +22,9 @@ export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
|
|||
// Abandon Hope All Ye Who Enter Here
|
||||
|
||||
let started = false;
|
||||
waitFor("getCurrentUser", x => Common.UserStore = x);
|
||||
waitFor("getCurrentUser", x => UserStore = x);
|
||||
waitFor(["dispatch", "subscribe"], x => {
|
||||
Common.FluxDispatcher = x;
|
||||
FluxDispatcher = x;
|
||||
const cb = () => {
|
||||
console.info("Connection open");
|
||||
x.unsubscribe("CONNECTION_OPEN", cb);
|
||||
|
@ -34,7 +32,8 @@ export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
|
|||
};
|
||||
x.subscribe("CONNECTION_OPEN", cb);
|
||||
});
|
||||
waitFor("useState", x => Common.React = x);
|
||||
waitFor("useState", x => (React = x));
|
||||
waitFor("FormSection", x => Forms = x);
|
||||
}
|
||||
|
||||
export function find(filter: FilterFn, getDefault = true) {
|
|
@ -9,7 +9,7 @@
|
|||
"noImplicitAny": false,
|
||||
"target": "ESNEXT",
|
||||
// https://esbuild.github.io/api/#jsx-factory
|
||||
"jsxFactory": "Vencord.Webpack.Common.React.createElement",
|
||||
"jsxFactory": "Vencord.Webpack.React.createElement",
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
|
|
Loading…
Reference in a new issue