Compare commits
14 Commits
vencord-co
...
preload-sa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0a87cad8f | ||
|
|
8e91df0376 | ||
|
|
81e6caa05a | ||
|
|
da8e8f52ab | ||
|
|
98b0ba85a3 | ||
|
|
d83aa6675e | ||
|
|
b3d595ef98 | ||
|
|
24047d7fdd | ||
|
|
5c992d66a6 | ||
|
|
4cec16be1b | ||
|
|
6c320983e2 | ||
|
|
d0e7bd479a | ||
|
|
02ab7165aa | ||
|
|
0483a1abdc |
@@ -6,17 +6,34 @@
|
||||
|
||||
//@ts-check
|
||||
|
||||
import { defineConfig } from "eslint/config";
|
||||
import stylistic from "@stylistic/eslint-plugin";
|
||||
import pathAlias from "eslint-plugin-path-alias";
|
||||
import react from "eslint-plugin-react";
|
||||
import simpleHeader from "eslint-plugin-simple-header";
|
||||
import importSort from "eslint-plugin-simple-import-sort";
|
||||
import unusedImports from "eslint-plugin-unused-imports";
|
||||
import tseslint from "typescript-eslint";
|
||||
import prettier from "eslint-plugin-prettier";
|
||||
|
||||
export default tseslint.config(
|
||||
export default defineConfig(
|
||||
{ ignores: ["dist"] },
|
||||
|
||||
{
|
||||
files: ["src/**/*.{tsx,ts,mts,mjs,js,jsx}"],
|
||||
settings: {
|
||||
react: {
|
||||
version: "19"
|
||||
}
|
||||
},
|
||||
...react.configs.flat.recommended,
|
||||
rules: {
|
||||
...react.configs.flat.recommended.rules,
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
"react/no-unescaped-entities": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ["src/**/*.{tsx,ts,mts,mjs,js,jsx}"],
|
||||
plugins: {
|
||||
@@ -24,8 +41,10 @@ export default tseslint.config(
|
||||
stylistic,
|
||||
importSort,
|
||||
unusedImports,
|
||||
// @ts-expect-error Missing types
|
||||
pathAlias,
|
||||
prettier
|
||||
prettier,
|
||||
"@typescript-eslint": tseslint.plugin
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
@@ -51,7 +70,6 @@ export default tseslint.config(
|
||||
],
|
||||
|
||||
// ESLint Rules
|
||||
|
||||
yoda: "error",
|
||||
eqeqeq: ["error", "always", { null: "ignore" }],
|
||||
"prefer-destructuring": [
|
||||
@@ -67,8 +85,19 @@ export default tseslint.config(
|
||||
"no-invalid-regexp": "error",
|
||||
"no-constant-condition": ["error", { checkLoops: false }],
|
||||
"no-duplicate-imports": "error",
|
||||
"dot-notation": "error",
|
||||
"no-useless-escape": "error",
|
||||
"@typescript-eslint/dot-notation": [
|
||||
"error",
|
||||
{
|
||||
allowPrivateClassPropertyAccess: true,
|
||||
allowProtectedClassPropertyAccess: true
|
||||
}
|
||||
],
|
||||
"no-useless-escape": [
|
||||
"error",
|
||||
{
|
||||
allowRegexCharacters: ["i"]
|
||||
}
|
||||
],
|
||||
"no-fallthrough": "error",
|
||||
"for-direction": "error",
|
||||
"no-async-promise-executor": "error",
|
||||
@@ -85,7 +114,7 @@ export default tseslint.config(
|
||||
"no-unsafe-optional-chaining": "error",
|
||||
"no-useless-backreference": "error",
|
||||
"use-isnan": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-const": ["error", { destructuring: "all" }],
|
||||
"prefer-spread": "error",
|
||||
|
||||
// Styling Rules
|
||||
|
||||
19
package.json
19
package.json
@@ -35,28 +35,29 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
||||
"@stylistic/eslint-plugin": "^5.5.0",
|
||||
"@types/node": "^24.10.1",
|
||||
"@stylistic/eslint-plugin": "^5.6.1",
|
||||
"@types/node": "^25.0.1",
|
||||
"@types/react": "19.2.1",
|
||||
"@vencord/types": "^1.13.2",
|
||||
"@vencord/types": "^1.13.7",
|
||||
"dotenv": "^17.2.3",
|
||||
"electron": "^39.2.1",
|
||||
"electron": "^39.2.7",
|
||||
"electron-builder": "^26.0.12",
|
||||
"esbuild": "^0.27.0",
|
||||
"esbuild": "^0.27.1",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-plugin-path-alias": "^2.1.0",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-simple-header": "^1.2.2",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-unused-imports": "^4.3.0",
|
||||
"libvesktop": "link:packages/libvesktop",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier": "^3.7.4",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tsx": "^4.20.6",
|
||||
"type-fest": "^5.2.0",
|
||||
"tsx": "^4.21.0",
|
||||
"type-fest": "^5.3.1",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.47.0",
|
||||
"typescript-eslint": "^8.49.0",
|
||||
"xml-formatter": "^3.6.7"
|
||||
},
|
||||
"packageManager": "pnpm@10.7.1",
|
||||
|
||||
1096
pnpm-lock.yaml
generated
1096
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -18,17 +18,15 @@ import {
|
||||
session,
|
||||
shell
|
||||
} from "electron";
|
||||
import { mkdirSync, readFileSync, watch } from "fs";
|
||||
import { open, readFile } from "fs/promises";
|
||||
import { readFileSync, watch } from "fs";
|
||||
import { readFile } from "fs/promises";
|
||||
import { enableHardwareAcceleration } from "main";
|
||||
import { release } from "os";
|
||||
import { join } from "path";
|
||||
import { debounce } from "shared/utils/debounce";
|
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents";
|
||||
import { setBadgeCount } from "./appBadge";
|
||||
import { autoStart } from "./autoStart";
|
||||
import { VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
|
||||
import { mainWin } from "./mainWindow";
|
||||
import { Settings, State } from "./settings";
|
||||
import { handle, handleSync } from "./utils/ipcWrappers";
|
||||
@@ -37,13 +35,29 @@ import { isDeckGameMode, showGamePage } from "./utils/steamOS";
|
||||
import { isValidVencordInstall } from "./utils/vencordLoader";
|
||||
import { VENCORD_FILES_DIR } from "./vencordFilesDir";
|
||||
|
||||
handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js"));
|
||||
handleSync(IpcEvents.DEPRECATED_GET_VENCORD_PRELOAD_SCRIPT_PATH, () =>
|
||||
join(VENCORD_FILES_DIR, "vencordDesktopPreload.js")
|
||||
);
|
||||
handleSync(IpcEvents.GET_VENCORD_PRELOAD_SCRIPT, () =>
|
||||
readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopPreload.js"), "utf-8")
|
||||
);
|
||||
handleSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, () =>
|
||||
readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8")
|
||||
);
|
||||
|
||||
handleSync(IpcEvents.GET_RENDERER_SCRIPT, () => readFileSync(join(__dirname, "renderer.js"), "utf-8"));
|
||||
handleSync(IpcEvents.GET_RENDERER_CSS_FILE, () => join(__dirname, "renderer.css"));
|
||||
const VESKTOP_RENDERER_JS_PATH = join(__dirname, "renderer.js");
|
||||
const VESKTOP_RENDERER_CSS_PATH = join(__dirname, "renderer.css");
|
||||
handleSync(IpcEvents.GET_VESKTOP_RENDERER_SCRIPT, () => readFileSync(VESKTOP_RENDERER_JS_PATH, "utf-8"));
|
||||
handle(IpcEvents.GET_VESKTOP_RENDERER_CSS, () => readFile(VESKTOP_RENDERER_CSS_PATH, "utf-8"));
|
||||
|
||||
if (IS_DEV) {
|
||||
watch(VESKTOP_RENDERER_CSS_PATH, { persistent: false }, async () => {
|
||||
mainWin?.webContents.postMessage(
|
||||
IpcEvents.VESKTOP_RENDERER_CSS_UPDATE,
|
||||
await readFile(VESKTOP_RENDERER_CSS_PATH, "utf-8")
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
handleSync(IpcEvents.GET_SETTINGS, () => Settings.plain);
|
||||
handleSync(IpcEvents.GET_VERSION, () => app.getVersion());
|
||||
@@ -142,6 +156,11 @@ handle(IpcEvents.SELECT_VENCORD_DIR, async (_e, value?: null) => {
|
||||
|
||||
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||
|
||||
handle(IpcEvents.FLASH_FRAME, (_, flag: boolean) => {
|
||||
if (!mainWin || mainWin.isDestroyed() || (flag && mainWin.isFocused())) return;
|
||||
mainWin.flashFrame(flag);
|
||||
});
|
||||
|
||||
handle(IpcEvents.CLIPBOARD_COPY_IMAGE, async (_, buf: ArrayBuffer, src: string) => {
|
||||
clipboard.write({
|
||||
html: `<img src="${src.replaceAll('"', '\\"')}">`,
|
||||
@@ -159,27 +178,3 @@ function openDebugPage(page: string) {
|
||||
|
||||
handle(IpcEvents.DEBUG_LAUNCH_GPU, () => openDebugPage("chrome://gpu"));
|
||||
handle(IpcEvents.DEBUG_LAUNCH_WEBRTC_INTERNALS, () => openDebugPage("chrome://webrtc-internals"));
|
||||
|
||||
function readCss() {
|
||||
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
||||
}
|
||||
|
||||
open(VENCORD_QUICKCSS_FILE, "a+").then(fd => {
|
||||
fd.close();
|
||||
watch(
|
||||
VENCORD_QUICKCSS_FILE,
|
||||
{ persistent: false },
|
||||
debounce(async () => {
|
||||
mainWin?.webContents.postMessage("VencordQuickCssUpdate", await readCss());
|
||||
}, 50)
|
||||
);
|
||||
});
|
||||
|
||||
mkdirSync(VENCORD_THEMES_DIR, { recursive: true });
|
||||
watch(
|
||||
VENCORD_THEMES_DIR,
|
||||
{ persistent: false },
|
||||
debounce(() => {
|
||||
mainWin?.webContents.postMessage("VencordThemeUpdate", void 0);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -34,7 +34,7 @@ import { destroyTray, initTray } from "./tray";
|
||||
import { clearData } from "./utils/clearData";
|
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||
import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS";
|
||||
import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader";
|
||||
import { downloadVencordFiles, ensureVencordFiles, vencordSupportsSandboxing } from "./utils/vencordLoader";
|
||||
import { VENCORD_FILES_DIR } from "./vencordFilesDir";
|
||||
|
||||
let isQuitting = false;
|
||||
@@ -312,7 +312,7 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions {
|
||||
const { staticTitle, transparencyOption, enableMenu, customTitleBar, splashTheming, splashBackground } =
|
||||
Settings.store;
|
||||
|
||||
const { frameless, transparent, macosTranslucency } = VencordSettings.store;
|
||||
const { frameless, transparent, macosVibrancyStyle } = VencordSettings.store;
|
||||
|
||||
const noFrame = frameless === true || customTitleBar === true;
|
||||
const backgroundColor =
|
||||
@@ -323,7 +323,7 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions {
|
||||
backgroundColor,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
sandbox: false, // TODO
|
||||
sandbox: vencordSupportsSandboxing(),
|
||||
contextIsolation: true,
|
||||
devTools: true,
|
||||
preload: join(__dirname, "preload.js"),
|
||||
@@ -358,9 +358,9 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions {
|
||||
options.titleBarStyle = "hidden";
|
||||
options.trafficLightPosition = { x: 10, y: 10 };
|
||||
|
||||
if (macosTranslucency) {
|
||||
options.vibrancy = "sidebar";
|
||||
options.backgroundColor = "#ffffff00";
|
||||
if (macosVibrancyStyle) {
|
||||
options.vibrancy = macosVibrancyStyle;
|
||||
options.backgroundColor = "#00000000";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,6 +389,10 @@ function createMainWindow() {
|
||||
return false;
|
||||
});
|
||||
|
||||
win.on("focus", () => {
|
||||
win.flashFrame(false);
|
||||
});
|
||||
|
||||
initWindowBoundsListeners(win);
|
||||
if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin")
|
||||
initTray(win, q => (isQuitting = q));
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { type Settings as TVencordSettings } from "@vencord/types/Vencord";
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||
import { dirname, join } from "path";
|
||||
import type { Settings as TSettings, State as TState } from "shared/settings";
|
||||
@@ -27,13 +28,17 @@ function loadSettings<T extends object = any>(file: string, name: string) {
|
||||
|
||||
const store = new SettingsStore(settings);
|
||||
store.addGlobalChangeListener(o => {
|
||||
mkdirSync(dirname(file), { recursive: true });
|
||||
writeFileSync(file, JSON.stringify(o, null, 4));
|
||||
try {
|
||||
mkdirSync(dirname(file), { recursive: true });
|
||||
writeFileSync(file, JSON.stringify(o, null, 4));
|
||||
} catch (err) {
|
||||
console.error(`Failed to save settings to ${name}.json:`, err);
|
||||
}
|
||||
});
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vesktop settings");
|
||||
export const VencordSettings = loadSettings<any>(VENCORD_SETTINGS_FILE, "Vencord settings");
|
||||
export const VencordSettings = loadSettings<TVencordSettings>(VENCORD_SETTINGS_FILE, "Vencord settings");
|
||||
export const State = loadSettings<TState>(STATE_FILE, "Vesktop state");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { mkdirSync } from "fs";
|
||||
import { mkdirSync, readFileSync } from "fs";
|
||||
import { access, constants as FsConstants, writeFile } from "fs/promises";
|
||||
import { VENCORD_FILES_DIR } from "main/vencordFilesDir";
|
||||
import { join } from "path";
|
||||
@@ -75,3 +75,16 @@ export async function ensureVencordFiles() {
|
||||
|
||||
await Promise.all([downloadVencordFiles(), writeFile(join(VENCORD_FILES_DIR, "package.json"), "{}")]);
|
||||
}
|
||||
|
||||
// TODO: remove this once enough time has passed
|
||||
export function vencordSupportsSandboxing() {
|
||||
const supports = readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopMain.js"), "utf-8").includes(
|
||||
"VencordGetRendererCss"
|
||||
);
|
||||
if (!supports) {
|
||||
console.warn(
|
||||
"⚠️ [VencordLoader] Vencord version is outdated and does not support sandboxing. Please update Vencord to the latest version."
|
||||
);
|
||||
}
|
||||
return supports;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Node } from "@vencord/venmic";
|
||||
import { ipcRenderer } from "electron";
|
||||
import { IpcMessage, IpcResponse } from "main/ipcCommands";
|
||||
import type { Node } from "@vencord/venmic";
|
||||
import { ipcRenderer } from "electron/renderer";
|
||||
import type { IpcMessage, IpcResponse } from "main/ipcCommands";
|
||||
import type { Settings } from "shared/settings";
|
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents";
|
||||
@@ -34,7 +34,14 @@ export const VesktopNative = {
|
||||
supportsWindowsTransparency: () => sendSync<boolean>(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY),
|
||||
getEnableHardwareAcceleration: () => sendSync<boolean>(IpcEvents.GET_ENABLE_HARDWARE_ACCELERATION),
|
||||
isOutdated: () => invoke<boolean>(IpcEvents.UPDATER_IS_OUTDATED),
|
||||
openUpdater: () => invoke<void>(IpcEvents.UPDATER_OPEN)
|
||||
openUpdater: () => invoke<void>(IpcEvents.UPDATER_OPEN),
|
||||
// used by vencord
|
||||
getRendererCss: () => invoke<string>(IpcEvents.GET_VESKTOP_RENDERER_CSS),
|
||||
onRendererCssUpdate: (cb: (newCss: string) => void) => {
|
||||
if (!IS_DEV) return;
|
||||
|
||||
ipcRenderer.on(IpcEvents.VESKTOP_RENDERER_CSS_UPDATE, (_e, newCss: string) => cb(newCss));
|
||||
}
|
||||
},
|
||||
autostart: {
|
||||
isEnabled: () => sendSync<boolean>(IpcEvents.AUTOSTART_ENABLED),
|
||||
@@ -68,6 +75,7 @@ export const VesktopNative = {
|
||||
close: (key?: string) => invoke<void>(IpcEvents.CLOSE, key),
|
||||
minimize: (key?: string) => invoke<void>(IpcEvents.MINIMIZE, key),
|
||||
maximize: (key?: string) => invoke<void>(IpcEvents.MAXIMIZE, key),
|
||||
flashFrame: (flag: boolean) => invoke<void>(IpcEvents.FLASH_FRAME, flag),
|
||||
setDevtoolsCallbacks: (onOpen: () => void, onClose: () => void) => {
|
||||
onDevtoolsOpen = onOpen;
|
||||
onDevtoolsClose = onClose;
|
||||
|
||||
@@ -4,39 +4,29 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { contextBridge, ipcRenderer, webFrame } from "electron";
|
||||
import { readFileSync, watch } from "fs";
|
||||
import { contextBridge, ipcRenderer, webFrame } from "electron/renderer";
|
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents";
|
||||
import { VesktopNative } from "./VesktopNative";
|
||||
|
||||
contextBridge.exposeInMainWorld("VesktopNative", VesktopNative);
|
||||
|
||||
require(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_FILE));
|
||||
// TODO: remove this legacy workaround once some time has passed
|
||||
const isSandboxed = typeof __dirname === "undefined";
|
||||
if (isSandboxed) {
|
||||
// While sandboxed, Electron "polyfills" these APIs as local variables.
|
||||
// We have to pass them as arguments as they are not global
|
||||
Function(
|
||||
"require",
|
||||
"Buffer",
|
||||
"process",
|
||||
"clearImmediate",
|
||||
"setImmediate",
|
||||
ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_SCRIPT)
|
||||
)(require, Buffer, process, clearImmediate, setImmediate);
|
||||
} else {
|
||||
require(ipcRenderer.sendSync(IpcEvents.DEPRECATED_GET_VENCORD_PRELOAD_SCRIPT_PATH));
|
||||
}
|
||||
|
||||
webFrame.executeJavaScript(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT));
|
||||
webFrame.executeJavaScript(ipcRenderer.sendSync(IpcEvents.GET_RENDERER_SCRIPT));
|
||||
|
||||
// #region css
|
||||
const rendererCss = ipcRenderer.sendSync(IpcEvents.GET_RENDERER_CSS_FILE);
|
||||
|
||||
const style = document.createElement("style");
|
||||
style.id = "vcd-css-core";
|
||||
style.textContent = readFileSync(rendererCss, "utf-8");
|
||||
|
||||
if (document.readyState === "complete") {
|
||||
document.documentElement.appendChild(style);
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", () => document.documentElement.appendChild(style), {
|
||||
once: true
|
||||
});
|
||||
}
|
||||
|
||||
if (IS_DEV) {
|
||||
// persistent means keep process running if watcher is the only thing still running
|
||||
// which we obviously don't want
|
||||
watch(rendererCss, { persistent: false }, () => {
|
||||
document.getElementById("vcd-css-core")!.textContent = readFileSync(rendererCss, "utf-8");
|
||||
});
|
||||
}
|
||||
// #endregion
|
||||
webFrame.executeJavaScript(ipcRenderer.sendSync(IpcEvents.GET_VESKTOP_RENDERER_SCRIPT));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { contextBridge, ipcRenderer } from "electron";
|
||||
import { contextBridge, ipcRenderer } from "electron/renderer";
|
||||
|
||||
contextBridge.exposeInMainWorld("VesktopSplashNative", {
|
||||
onUpdateMessage(callback: (message: string) => void) {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ipcRenderer } from "electron";
|
||||
import { IpcEvents, UpdaterIpcEvents } from "shared/IpcEvents";
|
||||
import { ipcRenderer } from "electron/renderer";
|
||||
import type { IpcEvents, UpdaterIpcEvents } from "shared/IpcEvents";
|
||||
|
||||
export function invoke<T = any>(event: IpcEvents | UpdaterIpcEvents, ...args: any[]) {
|
||||
return ipcRenderer.invoke(event, ...args) as Promise<T>;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { contextBridge, ipcRenderer } from "electron";
|
||||
import { contextBridge, ipcRenderer } from "electron/renderer";
|
||||
import type { UpdateInfo } from "electron-updater";
|
||||
import { UpdaterIpcEvents } from "shared/IpcEvents";
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type arRpcPlugin from "@vencord/types/plugins/arRPC.web";
|
||||
import { Logger } from "@vencord/types/utils";
|
||||
import { findLazy, findStoreLazy, onceReady } from "@vencord/types/webpack";
|
||||
import { FluxDispatcher, InviteActions } from "@vencord/types/webpack/common";
|
||||
@@ -15,9 +16,7 @@ import { Settings } from "./settings";
|
||||
const logger = new Logger("VesktopRPC", "#5865f2");
|
||||
const StreamerModeStore = findStoreLazy("StreamerModeStore");
|
||||
|
||||
const arRPC = Vencord.Plugins.plugins["WebRichPresence (arRPC)"] as any as {
|
||||
handleEvent(e: MessageEvent): void;
|
||||
};
|
||||
const arRPC = Vencord.Plugins.plugins["WebRichPresence (arRPC)"] as typeof arRpcPlugin;
|
||||
|
||||
onIpcCommand(IpcCommands.RPC_ACTIVITY, async jsonData => {
|
||||
if (!Settings.store.arRPC) return;
|
||||
|
||||
@@ -7,10 +7,31 @@
|
||||
import "./screenSharePicker.css";
|
||||
|
||||
import { classNameFactory } from "@vencord/types/api/Styles";
|
||||
import { CogWheel, FormSwitch, RestartIcon } from "@vencord/types/components";
|
||||
import { closeModal, Logger, Modals, ModalSize, openModal, useAwaiter, useForceUpdater } from "@vencord/types/utils";
|
||||
import {
|
||||
BaseText,
|
||||
Button,
|
||||
Card,
|
||||
CogWheel,
|
||||
FormSwitch,
|
||||
Heading,
|
||||
HeadingTertiary,
|
||||
Margins,
|
||||
Paragraph,
|
||||
RestartIcon,
|
||||
Span
|
||||
} from "@vencord/types/components";
|
||||
import {
|
||||
closeModal,
|
||||
Logger,
|
||||
ModalCloseButton,
|
||||
Modals,
|
||||
ModalSize,
|
||||
openModal,
|
||||
useAwaiter,
|
||||
useForceUpdater
|
||||
} from "@vencord/types/utils";
|
||||
import { onceReady } from "@vencord/types/webpack";
|
||||
import { Button, Card, FluxDispatcher, Forms, Select, Text, UserStore, useState } from "@vencord/types/webpack/common";
|
||||
import { FluxDispatcher, Select, UserStore, useState } from "@vencord/types/webpack/common";
|
||||
import { Node } from "@vencord/venmic";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { MediaEngineStore } from "renderer/common";
|
||||
@@ -18,6 +39,8 @@ import { addPatch } from "renderer/patches/shared";
|
||||
import { State, useSettings, useVesktopState } from "renderer/settings";
|
||||
import { isLinux, isWindows } from "renderer/utils";
|
||||
|
||||
import { SimpleErrorBoundary } from "./SimpleErrorBoundary";
|
||||
|
||||
const StreamResolutions = ["480", "720", "1080", "1440", "2160"] as const;
|
||||
const StreamFps = ["15", "30", "60"] as const;
|
||||
|
||||
@@ -60,16 +83,16 @@ const logger = new Logger("VesktopScreenShare");
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
find: "this.localWant=",
|
||||
find: "this.getDefaultGoliveQuality()",
|
||||
replacement: {
|
||||
match: /this.localWant=/,
|
||||
replace: "$self.patchStreamQuality(this);$&"
|
||||
match: /this\.getDefaultGoliveQuality\(\)/,
|
||||
replace: "$self.patchStreamQuality($&)"
|
||||
}
|
||||
}
|
||||
],
|
||||
patchStreamQuality(opts: any) {
|
||||
const { screenshareQuality } = State.store;
|
||||
if (!screenshareQuality) return;
|
||||
if (!screenshareQuality) return opts;
|
||||
|
||||
const framerate = Number(screenshareQuality.frameRate);
|
||||
const height = Number(screenshareQuality.resolution);
|
||||
@@ -94,6 +117,7 @@ addPatch({
|
||||
height,
|
||||
pixelCount: height * width
|
||||
});
|
||||
return opts;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -145,6 +169,9 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) {
|
||||
onCloseRequest() {
|
||||
closeModal(key);
|
||||
reject("Aborted");
|
||||
},
|
||||
onCloseCallback() {
|
||||
reject("Aborted");
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -165,9 +192,7 @@ function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScre
|
||||
/>
|
||||
|
||||
<img src={url} alt="" />
|
||||
<Text className={cl("screen-name")} variant="text-sm/normal">
|
||||
{name}
|
||||
</Text>
|
||||
<Paragraph className={cl("screen-name")}>{name}</Paragraph>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
@@ -188,19 +213,16 @@ function AudioSettingsModal({
|
||||
return (
|
||||
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<Modals.ModalHeader className={cl("header")}>
|
||||
<Forms.FormTitle tag="h2">Venmic Settings</Forms.FormTitle>
|
||||
<Modals.ModalCloseButton onClick={close} className={cl("header-close-button")} />
|
||||
<BaseText size="lg" weight="semibold" tag="h3" style={{ flexGrow: 1 }}>
|
||||
Audio Settings
|
||||
</BaseText>
|
||||
<ModalCloseButton onClick={close} />
|
||||
</Modals.ModalHeader>
|
||||
|
||||
<Modals.ModalContent className={cl("modal", "venmic-settings")}>
|
||||
<FormSwitch
|
||||
title="Microphone Workaround"
|
||||
description={
|
||||
<>
|
||||
Work around an issue that causes the microphone to be shared instead of the correct audio.
|
||||
Only enable if you're experiencing this issue.
|
||||
</>
|
||||
}
|
||||
description="Work around an issue that causes the microphone to be shared instead of the correct audio. Only enable if you're experiencing this issue."
|
||||
hideBorder
|
||||
onChange={v => (Settings.audio = { ...Settings.audio, workaround: v })}
|
||||
value={Settings.audio?.workaround ?? false}
|
||||
@@ -208,10 +230,7 @@ function AudioSettingsModal({
|
||||
<FormSwitch
|
||||
title="Only Speakers"
|
||||
description={
|
||||
<>
|
||||
When sharing entire desktop audio, only share apps that play to a speaker. You may want to
|
||||
disable this when using "mix bussing".
|
||||
</>
|
||||
'When sharing entire desktop audio, only share apps that play to a speaker. You may want to disable this when using "mix bussing".'
|
||||
}
|
||||
hideBorder
|
||||
onChange={v => (Settings.audio = { ...Settings.audio, onlySpeakers: v })}
|
||||
@@ -231,7 +250,7 @@ function AudioSettingsModal({
|
||||
/>
|
||||
<FormSwitch
|
||||
title="Ignore Inputs"
|
||||
description={<>Exclude nodes that are intended to capture audio.</>}
|
||||
description="Exclude nodes that are intended to capture audio."
|
||||
hideBorder
|
||||
onChange={v => (Settings.audio = { ...Settings.audio, ignoreInputMedia: v })}
|
||||
value={Settings.audio?.ignoreInputMedia ?? true}
|
||||
@@ -239,10 +258,7 @@ function AudioSettingsModal({
|
||||
<FormSwitch
|
||||
title="Ignore Virtual"
|
||||
description={
|
||||
<>
|
||||
Exclude virtual nodes, such as nodes belonging to loopbacks. This might be useful when using
|
||||
"mix bussing".
|
||||
</>
|
||||
'Exclude virtual nodes, such as nodes belonging to loopbacks. This might be useful when using "mix bussing".'
|
||||
}
|
||||
hideBorder
|
||||
onChange={v => (Settings.audio = { ...Settings.audio, ignoreVirtual: v })}
|
||||
@@ -250,7 +266,7 @@ function AudioSettingsModal({
|
||||
/>
|
||||
<FormSwitch
|
||||
title="Ignore Devices"
|
||||
description={<>Exclude device nodes, such as nodes belonging to microphones or speakers.</>}
|
||||
description="Exclude device nodes, such as nodes belonging to microphones or speakers."
|
||||
hideBorder
|
||||
onChange={v =>
|
||||
(Settings.audio = {
|
||||
@@ -263,7 +279,7 @@ function AudioSettingsModal({
|
||||
/>
|
||||
<FormSwitch
|
||||
title="Granular Selection"
|
||||
description={<>Allow to select applications more granularly.</>}
|
||||
description="Allow to select applications more granularly."
|
||||
hideBorder
|
||||
onChange={value => {
|
||||
Settings.audio = { ...Settings.audio, granularSelect: value };
|
||||
@@ -289,7 +305,7 @@ function AudioSettingsModal({
|
||||
/>
|
||||
</Modals.ModalContent>
|
||||
<Modals.ModalFooter className={cl("footer")}>
|
||||
<Button color={Button.Colors.TRANSPARENT} onClick={close}>
|
||||
<Button variant="secondary" onClick={close}>
|
||||
Back
|
||||
</Button>
|
||||
</Modals.ModalFooter>
|
||||
@@ -310,7 +326,7 @@ function OptionRadio<Settings extends object, Key extends keyof Settings>(props:
|
||||
<div className={cl("option-radios")}>
|
||||
{(options as string[]).map((option, idx) => (
|
||||
<label className={cl("option-radio")} data-checked={settings[settingsKey] === option} key={option}>
|
||||
<Text variant="text-sm/bold">{labels?.[idx] ?? option}</Text>
|
||||
<Span weight="bold">{labels?.[idx] ?? option}</Span>
|
||||
<input
|
||||
className={cl("option-input")}
|
||||
type="radio"
|
||||
@@ -361,18 +377,18 @@ function StreamSettingsUi({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
||||
<HeadingTertiary className={Margins.bottom8}>What you're streaming</HeadingTertiary>
|
||||
<Card className={cl("card", "preview")}>
|
||||
<img src={thumb} alt="" className={cl(isLinux ? "preview-img-linux" : "preview-img")} />
|
||||
<Text variant="text-sm/normal">{source.name}</Text>
|
||||
<Paragraph>{source.name}</Paragraph>
|
||||
</Card>
|
||||
|
||||
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
|
||||
<HeadingTertiary className={Margins.bottom8}>Stream Settings</HeadingTertiary>
|
||||
|
||||
<Card className={cl("card")}>
|
||||
<div className={cl("quality")}>
|
||||
<section className={cl("quality-section")}>
|
||||
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
||||
<Heading tag="h5">Resolution</Heading>
|
||||
<OptionRadio
|
||||
options={StreamResolutions}
|
||||
settings={qualitySettings}
|
||||
@@ -382,7 +398,7 @@ function StreamSettingsUi({
|
||||
</section>
|
||||
|
||||
<section className={cl("quality-section")}>
|
||||
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
||||
<Heading tag="h5">Frame Rate</Heading>
|
||||
<OptionRadio
|
||||
options={StreamFps}
|
||||
settings={qualitySettings}
|
||||
@@ -393,7 +409,7 @@ function StreamSettingsUi({
|
||||
</div>
|
||||
<div className={cl("quality")}>
|
||||
<section className={cl("quality-section")}>
|
||||
<Forms.FormTitle>Content Type</Forms.FormTitle>
|
||||
<Heading tag="h5">Content Type</Heading>
|
||||
<div>
|
||||
<OptionRadio
|
||||
options={["motion", "detail"]}
|
||||
@@ -402,12 +418,11 @@ function StreamSettingsUi({
|
||||
settingsKey="contentHint"
|
||||
onChange={option => setSettings(s => ({ ...s, contentHint: option }))}
|
||||
/>
|
||||
<div className={cl("hint-description")}>
|
||||
<p>
|
||||
Choosing "Prefer Clarity" will result in a significantly lower framerate in exchange
|
||||
for a much sharper and clearer image.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Paragraph className={Margins.top8}>
|
||||
Choosing "Prefer Clarity" will result in a significantly lower framerate in exchange for
|
||||
a much sharper and clearer image.
|
||||
</Paragraph>
|
||||
</div>
|
||||
{isWindows && (
|
||||
<FormSwitch
|
||||
@@ -578,32 +593,40 @@ function AudioSourcePickerLinux({
|
||||
|
||||
if (!sources.ok && sources.isGlibCxxOutdated) {
|
||||
return (
|
||||
<Forms.FormText>
|
||||
<Paragraph>
|
||||
Failed to retrieve Audio Sources because your C++ library is too old to run
|
||||
<a href="https://github.com/Vencord/venmic" target="_blank">
|
||||
<a href="https://github.com/Vencord/venmic" target="_blank" rel="noreferrer">
|
||||
venmic
|
||||
</a>
|
||||
. See{" "}
|
||||
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
||||
<a
|
||||
href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
this guide
|
||||
</a>{" "}
|
||||
for possible solutions.
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasPipewirePulse && !ignorePulseWarning) {
|
||||
return (
|
||||
<Text variant="text-sm/normal">
|
||||
<Paragraph>
|
||||
Could not find pipewire-pulse. See{" "}
|
||||
<a href="https://gist.github.com/the-spyke/2de98b22ff4f978ebf0650c90e82027e#install" target="_blank">
|
||||
<a
|
||||
href="https://gist.github.com/the-spyke/2de98b22ff4f978ebf0650c90e82027e#install"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
this guide
|
||||
</a>{" "}
|
||||
on how to switch to pipewire. <br />
|
||||
You can still continue, however, please{" "}
|
||||
<b>beware that you can only share audio of apps that are running under pipewire</b>.{" "}
|
||||
<a onClick={() => setIgnorePulseWarning(true)}>I know what I'm doing!</a>
|
||||
</Text>
|
||||
</Paragraph>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -623,50 +646,50 @@ function AudioSourcePickerLinux({
|
||||
<>
|
||||
<div className={cl("audio-sources")}>
|
||||
<section>
|
||||
<Forms.FormTitle>{loading ? "Loading Sources..." : "Audio Sources"}</Forms.FormTitle>
|
||||
<Select
|
||||
options={allSources.map(({ name, value }) => ({
|
||||
label: name,
|
||||
value: value,
|
||||
default: name === "None"
|
||||
}))}
|
||||
isSelected={isItemSelected(includeSources)}
|
||||
select={updateItems(setIncludeSources, includeSources)}
|
||||
serialize={String}
|
||||
popoutPosition="top"
|
||||
closeOnSelect={false}
|
||||
/>
|
||||
</section>
|
||||
{includeSources === "Entire System" && (
|
||||
<section>
|
||||
<Forms.FormTitle>Exclude Sources</Forms.FormTitle>
|
||||
<Heading tag="h5">{loading ? "Loading Sources..." : "Audio Sources"}</Heading>
|
||||
<SimpleErrorBoundary>
|
||||
<Select
|
||||
options={allSources
|
||||
.filter(x => x.name !== "Entire System")
|
||||
.map(({ name, value }) => ({
|
||||
label: name,
|
||||
value: value,
|
||||
default: name === "None"
|
||||
}))}
|
||||
isSelected={isItemSelected(excludeSources)}
|
||||
select={updateItems(setExcludeSources, excludeSources)}
|
||||
options={allSources.map(({ name, value }) => ({
|
||||
label: name,
|
||||
value: value,
|
||||
default: name === "None"
|
||||
}))}
|
||||
isSelected={isItemSelected(includeSources)}
|
||||
select={updateItems(setIncludeSources, includeSources)}
|
||||
serialize={String}
|
||||
popoutPosition="top"
|
||||
closeOnSelect={false}
|
||||
/>
|
||||
</SimpleErrorBoundary>
|
||||
</section>
|
||||
{includeSources === "Entire System" && (
|
||||
<section>
|
||||
<Heading tag="h5">Exclude Sources</Heading>
|
||||
<SimpleErrorBoundary>
|
||||
<Select
|
||||
options={allSources
|
||||
.filter(x => x.name !== "Entire System")
|
||||
.map(({ name, value }) => ({
|
||||
label: name,
|
||||
value: value,
|
||||
default: name === "None"
|
||||
}))}
|
||||
isSelected={isItemSelected(excludeSources)}
|
||||
select={updateItems(setExcludeSources, excludeSources)}
|
||||
serialize={String}
|
||||
popoutPosition="top"
|
||||
closeOnSelect={false}
|
||||
/>
|
||||
</SimpleErrorBoundary>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
<div className={cl("settings-buttons")}>
|
||||
<Button
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
onClick={refreshAudioSources}
|
||||
className={cl("settings-button")}
|
||||
>
|
||||
<Button variant="secondary" onClick={refreshAudioSources} className={cl("settings-button")}>
|
||||
<RestartIcon className={cl("settings-button-icon")} />
|
||||
Refresh Audio Sources
|
||||
</Button>
|
||||
<Button color={Button.Colors.TRANSPARENT} onClick={openSettings} className={cl("settings-button")}>
|
||||
<Button variant="secondary" onClick={openSettings} className={cl("settings-button")}>
|
||||
<CogWheel className={cl("settings-button-icon")} />
|
||||
Open Audio Settings
|
||||
</Button>
|
||||
@@ -702,8 +725,10 @@ function ModalComponent({
|
||||
return (
|
||||
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<Modals.ModalHeader className={cl("header")}>
|
||||
<Forms.FormTitle tag="h2">ScreenShare</Forms.FormTitle>
|
||||
<Modals.ModalCloseButton onClick={close} />
|
||||
<BaseText size="lg" weight="semibold" tag="h3" style={{ flexGrow: 1 }}>
|
||||
Screen Share Picker
|
||||
</BaseText>
|
||||
<ModalCloseButton onClick={close} />
|
||||
</Modals.ModalHeader>
|
||||
<Modals.ModalContent className={cl("modal")}>
|
||||
{!selected ? (
|
||||
@@ -781,11 +806,11 @@ function ModalComponent({
|
||||
</Button>
|
||||
|
||||
{selected && !skipPicker ? (
|
||||
<Button color={Button.Colors.TRANSPARENT} onClick={() => setSelected(void 0)}>
|
||||
<Button variant="secondary" onClick={() => setSelected(void 0)}>
|
||||
Back
|
||||
</Button>
|
||||
) : (
|
||||
<Button color={Button.Colors.TRANSPARENT} onClick={close}>
|
||||
<Button variant="secondary" onClick={close}>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
|
||||
46
src/renderer/components/SimpleErrorBoundary.tsx
Normal file
46
src/renderer/components/SimpleErrorBoundary.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2025 Vendicated and Vesktop contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Card, ErrorBoundary, HeadingTertiary, Paragraph, TextButton } from "@vencord/types/components";
|
||||
import { FluxDispatcher, InviteActions } from "@vencord/types/webpack/common";
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
async function openSupportChannel() {
|
||||
const code = "YVbdG2ZRG4";
|
||||
|
||||
try {
|
||||
const { invite } = await InviteActions.resolveInvite(code, "Desktop Modal");
|
||||
|
||||
if (!invite) throw 0;
|
||||
|
||||
await FluxDispatcher.dispatch({
|
||||
type: "INVITE_MODAL_OPEN",
|
||||
invite,
|
||||
code,
|
||||
context: "APP"
|
||||
});
|
||||
} catch {
|
||||
window.open(`https://discord.gg/${code}`, "_blank");
|
||||
}
|
||||
}
|
||||
|
||||
function Fallback() {
|
||||
return (
|
||||
<Card variant="danger">
|
||||
<HeadingTertiary>Something went wrong.</HeadingTertiary>
|
||||
<Paragraph>
|
||||
Please make sure Vencord and Vesktop are fully up to date. You can get help in our{" "}
|
||||
<TextButton variant="link" onClick={openSupportChannel}>
|
||||
Support Channel
|
||||
</TextButton>
|
||||
</Paragraph>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export function SimpleErrorBoundary({ children }: PropsWithChildren<{}>) {
|
||||
return <ErrorBoundary fallback={Fallback}>{children}</ErrorBoundary>;
|
||||
}
|
||||
@@ -81,13 +81,13 @@
|
||||
/* Option Radios */
|
||||
|
||||
.vcd-screen-picker-option-radios {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-option-radio {
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
background-color: var(--background-secondary);
|
||||
border: 1px solid var(--primary-800);
|
||||
@@ -151,11 +151,4 @@
|
||||
>section {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.vcd-screen-picker-hint-description {
|
||||
color: var(--header-secondary);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
font-weight: 400;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { BaseText, Button, Heading, Paragraph, TextButton } from "@vencord/types/components";
|
||||
import {
|
||||
Margins,
|
||||
ModalCloseButton,
|
||||
@@ -14,7 +15,7 @@ import {
|
||||
openModal,
|
||||
useForceUpdater
|
||||
} from "@vencord/types/utils";
|
||||
import { Button, Forms, Text, Toasts } from "@vencord/types/webpack/common";
|
||||
import { Toasts } from "@vencord/types/webpack/common";
|
||||
import { Settings } from "shared/settings";
|
||||
|
||||
import { cl, SettingsComponent } from "./Settings";
|
||||
@@ -27,20 +28,20 @@ function openDeveloperOptionsModal(settings: Settings) {
|
||||
openModal(props => (
|
||||
<ModalRoot {...props} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader>
|
||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>
|
||||
<BaseText size="lg" weight="semibold" tag="h3" style={{ flexGrow: 1 }}>
|
||||
Vesktop Developer Options
|
||||
</Text>
|
||||
</BaseText>
|
||||
<ModalCloseButton onClick={props.onClose} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent>
|
||||
<div style={{ padding: "1em 0" }}>
|
||||
<Forms.FormTitle tag="h5">Vencord Location</Forms.FormTitle>
|
||||
<Heading tag="h5">Vencord Location</Heading>
|
||||
<VencordLocationPicker settings={settings} />
|
||||
|
||||
<Forms.FormTitle tag="h5" className={Margins.top16}>
|
||||
<Heading tag="h5" className={Margins.top16}>
|
||||
Debugging
|
||||
</Forms.FormTitle>
|
||||
</Heading>
|
||||
<div className={cl("button-grid")}>
|
||||
<Button onClick={() => VesktopNative.debug.launchGpu()}>Open chrome://gpu</Button>
|
||||
<Button onClick={() => VesktopNative.debug.launchWebrtcInternals()}>
|
||||
@@ -59,25 +60,24 @@ const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Forms.FormText>
|
||||
<Paragraph>
|
||||
Vencord files are loaded from{" "}
|
||||
{vencordDir ? (
|
||||
<a
|
||||
href="about:blank"
|
||||
<TextButton
|
||||
variant="link"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
VesktopNative.fileManager.showItemInFolder(vencordDir!);
|
||||
}}
|
||||
>
|
||||
{vencordDir}
|
||||
</a>
|
||||
</TextButton>
|
||||
) : (
|
||||
"the default location"
|
||||
)}
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
<div className={cl("button-grid")}>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
onClick={async () => {
|
||||
const choice = await VesktopNative.fileManager.selectVencordDir();
|
||||
switch (choice) {
|
||||
@@ -105,8 +105,7 @@ const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
||||
Change
|
||||
</Button>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.RED}
|
||||
variant="dangerPrimary"
|
||||
onClick={async () => {
|
||||
await VesktopNative.fileManager.selectVencordDir(null);
|
||||
forceUpdate();
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ErrorBoundary } from "@vencord/types/components";
|
||||
import { Select } from "@vencord/types/webpack/common";
|
||||
|
||||
import { SimpleErrorBoundary } from "../SimpleErrorBoundary";
|
||||
import { SettingsComponent } from "./Settings";
|
||||
|
||||
export const DiscordBranchPicker: SettingsComponent = ({ settings }) => {
|
||||
return (
|
||||
<ErrorBoundary noop>
|
||||
<SimpleErrorBoundary>
|
||||
<Select
|
||||
placeholder="Stable"
|
||||
options={[
|
||||
@@ -24,6 +24,6 @@ export const DiscordBranchPicker: SettingsComponent = ({ settings }) => {
|
||||
isSelected={v => v === settings.discordBranch}
|
||||
serialize={s => s}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</SimpleErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
27
src/renderer/components/settings/OutdatedVesktopWarning.tsx
Normal file
27
src/renderer/components/settings/OutdatedVesktopWarning.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2025 Vendicated and Vesktop contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Button, Card, HeadingTertiary, Paragraph } from "@vencord/types/components";
|
||||
import { useAwaiter } from "@vencord/types/utils";
|
||||
|
||||
import { cl } from "./Settings";
|
||||
|
||||
export function OutdatedVesktopWarning() {
|
||||
const [isOutdated] = useAwaiter(VesktopNative.app.isOutdated);
|
||||
|
||||
if (!isOutdated) return null;
|
||||
|
||||
return (
|
||||
<Card variant="warning" className={cl("updater-card")}>
|
||||
<HeadingTertiary>Your Vesktop is outdated!</HeadingTertiary>
|
||||
<Paragraph>Staying up to date is important for security and stability.</Paragraph>
|
||||
|
||||
<Button onClick={() => VesktopNative.app.openUpdater()} variant="secondary">
|
||||
Open Updater
|
||||
</Button>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -7,8 +7,7 @@
|
||||
import "./settings.css";
|
||||
|
||||
import { classNameFactory } from "@vencord/types/api/Styles";
|
||||
import { ErrorBoundary } from "@vencord/types/components";
|
||||
import { Forms, Text } from "@vencord/types/webpack/common";
|
||||
import { BaseText, Divider, ErrorBoundary } from "@vencord/types/components";
|
||||
import { ComponentType } from "react";
|
||||
import { Settings, useSettings } from "renderer/settings";
|
||||
import { isMac, isWindows } from "renderer/utils";
|
||||
@@ -17,7 +16,7 @@ import { AutoStartToggle } from "./AutoStartToggle";
|
||||
import { DeveloperOptionsButton } from "./DeveloperOptions";
|
||||
import { DiscordBranchPicker } from "./DiscordBranchPicker";
|
||||
import { NotificationBadgeToggle } from "./NotificationBadgeToggle";
|
||||
import { Updater } from "./Updater";
|
||||
import { OutdatedVesktopWarning } from "./OutdatedVesktopWarning";
|
||||
import { UserAssetsButton } from "./UserAssets";
|
||||
import { VesktopSettingsSwitch } from "./VesktopSettingsSwitch";
|
||||
import { WindowsTransparencyControls } from "./WindowsTransparencyControls";
|
||||
@@ -125,7 +124,15 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
|
||||
defaultValue: false
|
||||
}
|
||||
],
|
||||
Notifications: [NotificationBadgeToggle],
|
||||
Notifications: [
|
||||
NotificationBadgeToggle,
|
||||
{
|
||||
key: "enableTaskbarFlashing",
|
||||
title: "Enable Taskbar Flashing",
|
||||
description: "Flashes the app in your taskbar when you have new notifications.",
|
||||
defaultValue: false
|
||||
}
|
||||
],
|
||||
Miscellaneous: [
|
||||
{
|
||||
key: "arRPC",
|
||||
@@ -149,13 +156,13 @@ function SettingsSections() {
|
||||
|
||||
const sections = Object.entries(SettingsOptions).map(([title, settings], i, arr) => (
|
||||
<div key={title} className={cl("category")}>
|
||||
<Text variant="heading-lg/semibold" color="header-primary" className={cl("category-title")}>
|
||||
<BaseText size="lg" weight="semibold" tag="h3" className={cl("category-title")}>
|
||||
{title}
|
||||
</Text>
|
||||
</BaseText>
|
||||
|
||||
<div className={cl("category-content")}>
|
||||
{settings.map(Setting => {
|
||||
if (typeof Setting === "function") return <Setting settings={Settings} />;
|
||||
{settings.map((Setting, i) => {
|
||||
if (typeof Setting === "function") return <Setting key={`Custom-${i}`} settings={Settings} />;
|
||||
|
||||
const { defaultValue, title, description, key, disabled, invisible } = Setting;
|
||||
if (invisible?.()) return null;
|
||||
@@ -173,7 +180,7 @@ function SettingsSections() {
|
||||
})}
|
||||
</div>
|
||||
|
||||
{i < arr.length - 1 && <Forms.FormDivider className={cl("category-divider")} />}
|
||||
{i < arr.length - 1 && <Divider className={cl("category-divider")} />}
|
||||
</div>
|
||||
));
|
||||
|
||||
@@ -184,10 +191,7 @@ export default ErrorBoundary.wrap(
|
||||
function SettingsUI() {
|
||||
return (
|
||||
<section>
|
||||
<Text variant="heading-xl/semibold" color="header-primary" className={cl("title")}>
|
||||
Vesktop Settings
|
||||
</Text>
|
||||
<Updater />
|
||||
<OutdatedVesktopWarning />
|
||||
<SettingsSections />
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2025 Vendicated and Vesktop contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { useAwaiter } from "@vencord/types/utils";
|
||||
import { Button, Text } from "@vencord/types/webpack/common";
|
||||
|
||||
import { cl } from "./Settings";
|
||||
|
||||
export function Updater() {
|
||||
const [isOutdated] = useAwaiter(VesktopNative.app.isOutdated);
|
||||
|
||||
if (!isOutdated) return null;
|
||||
|
||||
return (
|
||||
<div className={cl("updater-card")}>
|
||||
<Text variant="text-md/semibold">Your Vesktop is outdated!</Text>
|
||||
<Text variant="text-sm/normal">Staying up to date is important for security and stability.</Text>
|
||||
|
||||
<Button
|
||||
onClick={() => VesktopNative.app.openUpdater()}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
>
|
||||
Open Updater
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import "./UserAssets.css";
|
||||
|
||||
import { FormSwitch } from "@vencord/types/components";
|
||||
import { BaseText, Button, FormSwitch } from "@vencord/types/components";
|
||||
import {
|
||||
Margins,
|
||||
ModalCloseButton,
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
wordsFromCamel,
|
||||
wordsToTitle
|
||||
} from "@vencord/types/utils";
|
||||
import { Button, showToast, Text, useState } from "@vencord/types/webpack/common";
|
||||
import { showToast, useState } from "@vencord/types/webpack/common";
|
||||
import { UserAssetType } from "main/userAssets";
|
||||
import { useSettings } from "renderer/settings";
|
||||
|
||||
@@ -34,9 +34,9 @@ function openAssetsModal() {
|
||||
openModal(props => (
|
||||
<ModalRoot {...props} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader>
|
||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>
|
||||
<BaseText size="lg" weight="semibold" tag="h3" style={{ flexGrow: 1 }}>
|
||||
User Assets
|
||||
</Text>
|
||||
</BaseText>
|
||||
<ModalCloseButton onClick={props.onClose} />
|
||||
</ModalHeader>
|
||||
|
||||
@@ -73,9 +73,9 @@ function Asset({ asset }: { asset: UserAssetType }) {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Text tag="h3" variant="text-md/semibold">
|
||||
<BaseText size="md" weight="medium" tag="h3">
|
||||
{wordsToTitle(wordsFromCamel(asset))}
|
||||
</Text>
|
||||
</BaseText>
|
||||
<div className="vcd-user-assets-asset">
|
||||
<img
|
||||
className="vcd-user-assets-image"
|
||||
@@ -86,7 +86,7 @@ function Asset({ asset }: { asset: UserAssetType }) {
|
||||
<div className="vcd-user-assets-actions">
|
||||
<div className="vcd-user-assets-buttons">
|
||||
<Button onClick={onChooseAsset()}>Customize</Button>
|
||||
<Button color={Button.Colors.PRIMARY} onClick={onChooseAsset(null)}>
|
||||
<Button variant="secondary" onClick={onChooseAsset(null)}>
|
||||
Reset to default
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { FormSwitch } from "@vencord/types/components";
|
||||
import { ComponentProps } from "react";
|
||||
import type { ComponentProps } from "react";
|
||||
|
||||
import { cl } from "./Settings";
|
||||
|
||||
|
||||
@@ -4,23 +4,24 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ErrorBoundary } from "@vencord/types/components";
|
||||
import { Heading, Paragraph } from "@vencord/types/components";
|
||||
import { Margins } from "@vencord/types/utils";
|
||||
import { Forms, Select } from "@vencord/types/webpack/common";
|
||||
import { Select } from "@vencord/types/webpack/common";
|
||||
|
||||
import { SimpleErrorBoundary } from "../SimpleErrorBoundary";
|
||||
import { SettingsComponent } from "./Settings";
|
||||
|
||||
export const WindowsTransparencyControls: SettingsComponent = ({ settings }) => {
|
||||
if (!VesktopNative.app.supportsWindowsTransparency()) return null;
|
||||
|
||||
return (
|
||||
<ErrorBoundary noop>
|
||||
<div>
|
||||
<Forms.FormTitle className={Margins.bottom8}>Transparency Options</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom8}>
|
||||
Requires a full restart. You will need a theme that supports transparency for this to work.
|
||||
</Forms.FormText>
|
||||
<div>
|
||||
<Heading tag="h5">Transparency Options</Heading>
|
||||
<Paragraph className={Margins.bottom8}>
|
||||
Requires a full restart. You will need a theme that supports transparency for this to work.
|
||||
</Paragraph>
|
||||
|
||||
<SimpleErrorBoundary>
|
||||
<Select
|
||||
placeholder="None"
|
||||
options={[
|
||||
@@ -44,7 +45,7 @@ export const WindowsTransparencyControls: SettingsComponent = ({ settings }) =>
|
||||
isSelected={v => v === settings.transparencyOption}
|
||||
serialize={s => s}
|
||||
/>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</SimpleErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -38,10 +38,4 @@
|
||||
margin-bottom: 1em;
|
||||
display: grid;
|
||||
gap: 0.5em;
|
||||
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-secondary);
|
||||
background: var(--background-feedback-warning);
|
||||
border: 1px solid var(--info-warning-foreground);
|
||||
color: var(--text-feedback-warning);
|
||||
}
|
||||
@@ -23,7 +23,8 @@ import type SettingsPlugin from "@vencord/types/plugins/_core/settings";
|
||||
VesktopLogger.log("read if cute :3");
|
||||
VesktopLogger.log("Vesktop v" + VesktopNative.app.getVersion());
|
||||
|
||||
const customSettingsSections = (Vencord.Plugins.plugins.Settings as any as typeof SettingsPlugin).customSections;
|
||||
// TODO
|
||||
const customSettingsSections = (Vencord.Plugins.plugins.Settings as typeof SettingsPlugin).customSections;
|
||||
|
||||
customSettingsSections.push(() => ({
|
||||
section: "Vesktop",
|
||||
@@ -31,3 +32,14 @@ customSettingsSections.push(() => ({
|
||||
element: SettingsUi,
|
||||
className: "vc-vesktop-settings"
|
||||
}));
|
||||
|
||||
// TODO: remove this legacy workaround once some time has passed
|
||||
// @ts-expect-error
|
||||
if (!Vencord.Api.Styles.vencordRootNode) {
|
||||
const style = document.createElement("style");
|
||||
style.id = "vesktop-css-core";
|
||||
|
||||
VesktopNative.app.getRendererCss().then(css => (style.textContent = css));
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => document.documentElement.append(style), { once: true });
|
||||
}
|
||||
|
||||
@@ -26,12 +26,10 @@ addPatch({
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /if\(null!=(\i)\)(?=.{0,50}\1\.window\.setDevtoolsCallbacks)/,
|
||||
replace: "if(true)"
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /\b\i\.window\.setDevtoolsCallbacks/g,
|
||||
replace: "VesktopNative.win.setDevtoolsCallbacks"
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ addPatch({
|
||||
{
|
||||
find: '"NotificationSettingsStore',
|
||||
replacement: {
|
||||
// FIXME: fix eslint rule
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /\.isPlatformEmbedded(?=\?\i\.\i\.ALL)/g,
|
||||
replace: "$&||true"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ addPatch({
|
||||
{
|
||||
find: "lastOutputSystemDevice.justChanged",
|
||||
replacement: {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /(\i)\.\i\.getState\(\).neverShowModal/,
|
||||
replace: "$& || $self.shouldIgnoreDevice($1)"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ addPatch({
|
||||
{
|
||||
find: 'setSinkId"in',
|
||||
replacement: {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /return (\i)\?navigator\.mediaDevices\.enumerateDevices/,
|
||||
replace: "return $1 ? $self.filteredDevices"
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ addPatch({
|
||||
{
|
||||
find: "platform-web",
|
||||
replacement: {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /(?<=" platform-overlay"\):)\i/,
|
||||
replace: "$self.getPlatformClass()"
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ addPatch({
|
||||
find: ".enableSpellCheck)",
|
||||
replacement: {
|
||||
// if (isDesktop) { DiscordNative.onSpellcheck(openMenu(props)) } else { e.preventDefault(); openMenu(props) }
|
||||
match: /else (.{1,3})\.preventDefault\(\),(.{1,3}\(.{1,3}\))(?<=:(.{1,3})\.enableSpellCheck\).+?)/,
|
||||
match: /else (\i)\.preventDefault\(\),(\i\(\i\))(?<=:(\i)\.enableSpellCheck\).+?)/,
|
||||
// ... else { $self.onSlateContext(() => openMenu(props)) }
|
||||
replace: "else {$self.onSlateContext($1, $3?.enableSpellCheck, () => $2)}"
|
||||
}
|
||||
@@ -66,6 +66,7 @@ addContextMenuPatch("textarea-context", children => {
|
||||
<>
|
||||
{corrections.map(c => (
|
||||
<Menu.MenuItem
|
||||
key={c}
|
||||
id={"vcd-spellcheck-suggestion-" + c}
|
||||
label={c}
|
||||
action={() => VesktopNative.spellcheck.replaceMisspelling(c)}
|
||||
@@ -95,6 +96,7 @@ addContextMenuPatch("textarea-context", children => {
|
||||
const isEnabled = spellCheckLanguages.includes(lang);
|
||||
return (
|
||||
<Menu.MenuCheckboxItem
|
||||
key={lang}
|
||||
id={"vcd-spellcheck-lang-" + lang}
|
||||
label={lang}
|
||||
checked={isEnabled}
|
||||
|
||||
@@ -12,7 +12,6 @@ addPatch({
|
||||
find: ".STREAMER_MODE_ENABLE,",
|
||||
replacement: {
|
||||
// remove if (platformEmbedded) check from streamer mode toggle
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /if\(\i\.\i\)(?=return.{0,200}?"autoToggle")/g,
|
||||
replace: ""
|
||||
}
|
||||
|
||||
27
src/renderer/patches/taskBarFlash.ts
Normal file
27
src/renderer/patches/taskBarFlash.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2025 Vendicated and Vesktop contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Settings } from "renderer/settings";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
find: ".flashFrame(!0)",
|
||||
replacement: {
|
||||
match: /(\i)&&\i\.\i\.taskbarFlash&&\i\.\i\.flashFrame\(!0\)/,
|
||||
replace: "$self.flashFrame()"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
flashFrame() {
|
||||
if (Settings.store.enableTaskbarFlashing) {
|
||||
VesktopNative.win.flashFrame(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -12,13 +12,10 @@ addPatch({
|
||||
find: ",setSystemTrayApplications",
|
||||
replacement: [
|
||||
{
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /\i\.window\.(close|minimize|maximize)/g,
|
||||
replace: `VesktopNative.win.$1`
|
||||
},
|
||||
{
|
||||
// TODO: Fix eslint rule
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /(focus(\(\i\)){).{0,150}?\.focus\(\i,\i\)/,
|
||||
replace: "$1VesktopNative.win.focus$2"
|
||||
},
|
||||
|
||||
@@ -15,8 +15,6 @@ if (Settings.store.customTitleBar)
|
||||
find: ".wordmarkWindows",
|
||||
replacement: [
|
||||
{
|
||||
// TODO: Fix eslint rule
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /case \i\.\i\.WINDOWS:/,
|
||||
replace: 'case "WEB":'
|
||||
}
|
||||
@@ -27,14 +25,10 @@ if (Settings.store.customTitleBar)
|
||||
find: ".systemBar,",
|
||||
replacement: [
|
||||
{
|
||||
// TODO: Fix eslint rule
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /\i===\i\.PlatformTypes\.WINDOWS/g,
|
||||
replace: "true"
|
||||
},
|
||||
{
|
||||
// TODO: Fix eslint rule
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /\i===\i\.PlatformTypes\.WEB/g,
|
||||
replace: "false"
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ const updateSplashColors = () => {
|
||||
const bodyStyles = document.body.computedStyleMap();
|
||||
|
||||
const color = bodyStyles.get("--text-default");
|
||||
const backgroundColor = bodyStyles.get("--background-primary");
|
||||
const backgroundColor = bodyStyles.get("--background-base-lowest");
|
||||
|
||||
if (isValidColor(color)) {
|
||||
Settings.store.splashColor = resolveColor(color[0]);
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
*/
|
||||
|
||||
export const enum IpcEvents {
|
||||
GET_VENCORD_PRELOAD_FILE = "VCD_GET_VC_PRELOAD_FILE",
|
||||
GET_VENCORD_PRELOAD_SCRIPT = "VCD_GET_VC_PRELOAD_SCRIPT",
|
||||
DEPRECATED_GET_VENCORD_PRELOAD_SCRIPT_PATH = "DEPRECATED_GET_VENCORD_PRELOAD_SCRIPT_PATH",
|
||||
GET_VENCORD_RENDERER_SCRIPT = "VCD_GET_VC_RENDERER_SCRIPT",
|
||||
GET_RENDERER_SCRIPT = "VCD_GET_RENDERER_SCRIPT",
|
||||
GET_RENDERER_CSS_FILE = "VCD_GET_RENDERER_CSS_FILE",
|
||||
|
||||
GET_VESKTOP_RENDERER_SCRIPT = "VCD_GET_RENDERER_SCRIPT",
|
||||
GET_VESKTOP_RENDERER_CSS = "VCD_GET_RENDERER_CSS",
|
||||
VESKTOP_RENDERER_CSS_UPDATE = "VCD_PRELOAD_RENDERER_CSS_UPDATE",
|
||||
|
||||
GET_VERSION = "VCD_GET_VERSION",
|
||||
SUPPORTS_WINDOWS_TRANSPARENCY = "VCD_SUPPORTS_WINDOWS_TRANSPARENCY",
|
||||
@@ -36,6 +39,7 @@ export const enum IpcEvents {
|
||||
SPELLCHECK_ADD_TO_DICTIONARY = "VCD_SPELLCHECK_ADD_TO_DICTIONARY",
|
||||
|
||||
SET_BADGE_COUNT = "VCD_SET_BADGE_COUNT",
|
||||
FLASH_FRAME = "FLASH_FRAME",
|
||||
|
||||
CAPTURER_GET_LARGE_THUMBNAIL = "VCD_CAPTURER_GET_LARGE_THUMBNAIL",
|
||||
|
||||
|
||||
1
src/shared/settings.d.ts
vendored
1
src/shared/settings.d.ts
vendored
@@ -20,6 +20,7 @@ export interface Settings {
|
||||
hardwareVideoAcceleration?: boolean;
|
||||
arRPC?: boolean;
|
||||
appBadge?: boolean;
|
||||
enableTaskbarFlashing?: boolean;
|
||||
disableMinSize?: boolean;
|
||||
clickTrayToShowHide?: boolean;
|
||||
customTitleBar?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user