merge unconflict

This commit is contained in:
defautluser0
2025-05-22 22:48:23 +02:00
27 changed files with 288 additions and 635 deletions

View File

@@ -5,4 +5,4 @@
# https://github.com/settings/personal-access-tokens/new # https://github.com/settings/personal-access-tokens/new
GITHUB_TOKEN= GITHUB_TOKEN=
ELECTRON_LAUNCH_FLAGS="--ozone-platform-hint=auto --enable-webrtc-pipewire-capturer --enable-features=WaylandWindowDecorations" ELECTRON_LAUNCH_FLAGS="--enable-source-maps --ozone-platform-hint=auto"

View File

@@ -35,14 +35,14 @@
"devDependencies": { "devDependencies": {
"@fal-works/esbuild-plugin-global-externals": "^2.1.2", "@fal-works/esbuild-plugin-global-externals": "^2.1.2",
"@stylistic/eslint-plugin": "^4.2.0", "@stylistic/eslint-plugin": "^4.2.0",
"@types/node": "^22.15.17", "@types/node": "^22.15.18",
"@types/react": "18.3.1", "@types/react": "18.3.1",
"@vencord/types": "^1.11.5", "@vencord/types": "^1.11.5",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"electron": "^36.2.0", "electron": "^36.2.1",
"electron-builder": "^26.0.12", "electron-builder": "^26.0.12",
"esbuild": "^0.25.4", "esbuild": "^0.25.4",
"eslint": "^9.26.0", "eslint": "^9.27.0",
"eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-path-alias": "^2.1.0", "eslint-plugin-path-alias": "^2.1.0",
"eslint-plugin-prettier": "^5.4.0", "eslint-plugin-prettier": "^5.4.0",
@@ -121,7 +121,8 @@
"Type": "Application", "Type": "Application",
"Categories": "Network;InstantMessaging;Chat;", "Categories": "Network;InstantMessaging;Chat;",
"Keywords": "discord;vencord;nexulien;electron;chat;", "Keywords": "discord;vencord;nexulien;electron;chat;",
"MimeType": "x-scheme-handler/discord" "MimeType": "x-scheme-handler/discord",
"StartupWMClass": "nexop"
} }
} }
}, },

625
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ import { BuildContext, BuildOptions, context } from "esbuild";
import { copyFile } from "fs/promises"; import { copyFile } from "fs/promises";
import vencordDep from "./vencordDep.mjs"; import vencordDep from "./vencordDep.mjs";
import { includeDirPlugin } from "./includeDirPlugin.mts";
const isDev = process.argv.includes("--dev"); const isDev = process.argv.includes("--dev");
@@ -78,7 +79,7 @@ await Promise.all([
jsxFactory: "VencordCreateElement", jsxFactory: "VencordCreateElement",
jsxFragment: "VencordFragment", jsxFragment: "VencordFragment",
external: ["@vencord/types/*"], external: ["@vencord/types/*"],
plugins: [vencordDep], plugins: [vencordDep, includeDirPlugin("patches", "src/renderer/patches")],
footer: { js: "//# sourceURL=VCDRenderer" } footer: { js: "//# sourceURL=VCDRenderer" }
}) })
]); ]);

View File

@@ -0,0 +1,25 @@
import { Plugin } from "esbuild";
import { readdir } from "fs/promises";
const makeImportAllCode = (files: string[]) =>
files.map(f => `require("./${f.replace(/\.[cm]?[tj]sx?$/, "")}")`).join("\n");
const makeImportDirRecursiveCode = (dir: string) => readdir(dir).then(files => makeImportAllCode(files));
export function includeDirPlugin(namespace: string, path: string): Plugin {
return {
name: `include-dir-plugin:${namespace}`,
setup(build) {
const filter = new RegExp(`^__${namespace}__$`);
build.onResolve({ filter }, args => ({ path: args.path, namespace }));
build.onLoad({ filter, namespace }, async args => {
return {
contents: await makeImportDirRecursiveCode(path),
resolveDir: path
};
});
}
};
}

View File

@@ -7,6 +7,7 @@
import { app } from "electron"; import { app } from "electron";
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs"; import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs";
import { join } from "path"; import { join } from "path";
import { stripIndent } from "shared/utils/text";
interface AutoStart { interface AutoStart {
isEnabled(): boolean; isEnabled(): boolean;
@@ -31,7 +32,7 @@ function makeAutoStartLinux(): AutoStart {
return { return {
isEnabled: () => existsSync(file), isEnabled: () => existsSync(file),
enable() { enable() {
const desktopFile = ` const desktopFile = stripIndent`
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
Name=Vesktop Name=Vesktop
@@ -39,7 +40,7 @@ Comment=Vesktop autostart script
Exec=${commandLine} Exec=${commandLine}
StartupNotify=false StartupNotify=false
Terminal=false Terminal=false
`.trim(); `;
mkdirSync(dir, { recursive: true }); mkdirSync(dir, { recursive: true });
writeFileSync(file, desktopFile); writeFileSync(file, desktopFile);

View File

@@ -5,7 +5,7 @@
*/ */
import { app } from "electron"; import { app } from "electron";
import { existsSync, mkdirSync, readdirSync, renameSync, rmdirSync } from "fs"; import { existsSync, mkdirSync } from "fs";
import { dirname, join } from "path"; import { dirname, join } from "path";
const vesktopDir = dirname(process.execPath); const vesktopDir = dirname(process.execPath);
@@ -15,28 +15,11 @@ export const PORTABLE =
!process.execPath.toLowerCase().endsWith("electron.exe") && !process.execPath.toLowerCase().endsWith("electron.exe") &&
!existsSync(join(vesktopDir, "Uninstall Vesktop.exe")); !existsSync(join(vesktopDir, "Uninstall Vesktop.exe"));
const LEGACY_DATA_DIR = join(app.getPath("appData"), "VencordDesktop", "VencordDesktop");
export const DATA_DIR = export const DATA_DIR =
process.env.VENCORD_USER_DATA_DIR || (PORTABLE ? join(vesktopDir, "Data") : join(app.getPath("userData"))); process.env.VENCORD_USER_DATA_DIR || (PORTABLE ? join(vesktopDir, "Data") : join(app.getPath("userData")));
mkdirSync(DATA_DIR, { recursive: true }); mkdirSync(DATA_DIR, { recursive: true });
// TODO: remove eventually
if (existsSync(LEGACY_DATA_DIR)) {
try {
console.warn("Detected legacy settings dir", LEGACY_DATA_DIR + ".", "migrating to", DATA_DIR);
for (const file of readdirSync(LEGACY_DATA_DIR)) {
renameSync(join(LEGACY_DATA_DIR, file), join(DATA_DIR, file));
}
rmdirSync(LEGACY_DATA_DIR);
renameSync(
join(app.getPath("appData"), "VencordDesktop", "IndexedDB"),
join(DATA_DIR, "sessionData", "IndexedDB")
);
} catch (e) {
console.error("Migration failed", e);
}
}
const SESSION_DATA_DIR = join(DATA_DIR, "sessionData"); const SESSION_DATA_DIR = join(DATA_DIR, "sessionData");
app.setPath("sessionData", SESSION_DATA_DIR); app.setPath("sessionData", SESSION_DATA_DIR);

View File

@@ -17,9 +17,7 @@ import { registerScreenShareHandler } from "./screenShare";
import { Settings, State } from "./settings"; import { Settings, State } from "./settings";
import { isDeckGameMode } from "./utils/steamOS"; import { isDeckGameMode } from "./utils/steamOS";
if (IS_DEV) { if (!IS_DEV) {
require("source-map-support").install();
} else {
autoUpdater.checkForUpdatesAndNotify(); autoUpdater.checkForUpdatesAndNotify();
} }
@@ -69,8 +67,6 @@ function init() {
// WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows. // WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows.
// HardwareMediaKeyHandling, MediaSessionService: Prevent Discord from registering as a media service. // HardwareMediaKeyHandling, MediaSessionService: Prevent Discord from registering as a media service.
//
// WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790
disabledFeatures.add("WinRetrieveSuggestionsOnlyOnDemand"); disabledFeatures.add("WinRetrieveSuggestionsOnlyOnDemand");
disabledFeatures.add("HardwareMediaKeyHandling"); disabledFeatures.add("HardwareMediaKeyHandling");
disabledFeatures.add("MediaSessionService"); disabledFeatures.add("MediaSessionService");

View File

@@ -6,8 +6,9 @@
import { desktopCapturer, session, Streams } from "electron"; import { desktopCapturer, session, Streams } from "electron";
import type { StreamPick } from "renderer/components/ScreenSharePicker"; import type { StreamPick } from "renderer/components/ScreenSharePicker";
import { IpcEvents } from "shared/IpcEvents"; import { IpcCommands, IpcEvents } from "shared/IpcEvents";
import { sendRendererCommand } from "./ipcCommands";
import { handle } from "./utils/ipcWrappers"; import { handle } from "./utils/ipcWrappers";
const isWayland = const isWayland =
@@ -49,11 +50,11 @@ export function registerScreenShareHandler() {
if (isWayland) { if (isWayland) {
const video = data[0]; const video = data[0];
if (video) { if (video) {
const stream = await request const stream = await sendRendererCommand<StreamPick>(IpcCommands.SCREEN_SHARE_PICKER, {
.frame!.executeJavaScript( screens: [video],
`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify([video])},true)` skipPicker: true
) }).catch(() => null);
.catch(() => null);
if (stream === null) return callback({}); if (stream === null) return callback({});
} }
@@ -61,10 +62,10 @@ export function registerScreenShareHandler() {
return; return;
} }
const choice = await request const choice = await sendRendererCommand<StreamPick>(IpcCommands.SCREEN_SHARE_PICKER, {
.frame!.executeJavaScript(`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`) screens: data,
.then(e => e as StreamPick) skipPicker: false
.catch(e => { }).catch(e => {
console.error("Error during screenshare picker", e); console.error("Error during screenshare picker", e);
return null; return null;
}); });

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join } from "path"; import { dirname, join } from "path";
import type { Settings as TSettings, State as TState } from "shared/settings"; import type { Settings as TSettings, State as TState } from "shared/settings";
import { SettingsStore } from "shared/utils/SettingsStore"; import { SettingsStore } from "shared/utils/SettingsStore";
@@ -35,18 +35,5 @@ function loadSettings<T extends object = any>(file: string, name: string) {
} }
export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vesktop settings"); export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vesktop settings");
export const VencordSettings = loadSettings<any>(VENCORD_SETTINGS_FILE, "Vencord settings"); export const VencordSettings = loadSettings<any>(VENCORD_SETTINGS_FILE, "Vencord settings");
if (Object.hasOwn(Settings.plain, "firstLaunch") && !existsSync(STATE_FILE)) {
console.warn("legacy state in settings.json detected. migrating to state.json");
const state = {} as TState;
for (const prop of ["firstLaunch", "maximized", "minimized", "steamOSLayoutVersion", "windowBounds"] as const) {
state[prop] = Settings.plain[prop];
delete Settings.plain[prop];
}
Settings.markAsChanged();
writeFileSync(STATE_FILE, JSON.stringify(state, null, 4));
}
export const State = loadSettings<TState>(STATE_FILE, "Vesktop state"); export const State = loadSettings<TState>(STATE_FILE, "Vesktop state");

View File

@@ -8,25 +8,33 @@ import { ipcMain, IpcMainEvent, IpcMainInvokeEvent, WebFrameMain } from "electro
import { DISCORD_HOSTNAMES } from "main/constants"; import { DISCORD_HOSTNAMES } from "main/constants";
import { IpcEvents } from "shared/IpcEvents"; import { IpcEvents } from "shared/IpcEvents";
export function validateSender(frame: WebFrameMain | null) { export function validateSender(frame: WebFrameMain | null, event: string) {
if (!frame) throw new Error("ipc: No sender frame"); if (!frame) throw new Error(`ipc[${event}]: No sender frame`);
if (!frame.url) return;
try {
var { hostname, protocol } = new URL(frame.url);
} catch (e) {
throw new Error(`ipc[${event}]: Invalid URL ${frame.url}`);
}
const { hostname, protocol } = new URL(frame.url);
if (protocol === "file:") return; if (protocol === "file:") return;
if (!DISCORD_HOSTNAMES.includes(hostname)) throw new Error("ipc: Disallowed host " + hostname); if (!DISCORD_HOSTNAMES.includes(hostname)) {
throw new Error(`ipc[${event}]: Disallowed hostname ${hostname}`);
}
} }
export function handleSync(event: IpcEvents, cb: (e: IpcMainEvent, ...args: any[]) => any) { export function handleSync(event: IpcEvents, cb: (e: IpcMainEvent, ...args: any[]) => any) {
ipcMain.on(event, (e, ...args) => { ipcMain.on(event, (e, ...args) => {
validateSender(e.senderFrame); validateSender(e.senderFrame, event);
e.returnValue = cb(e, ...args); e.returnValue = cb(e, ...args);
}); });
} }
export function handle(event: IpcEvents, cb: (e: IpcMainInvokeEvent, ...args: any[]) => any) { export function handle(event: IpcEvents, cb: (e: IpcMainInvokeEvent, ...args: any[]) => any) {
ipcMain.handle(event, (e, ...args) => { ipcMain.handle(event, (e, ...args) => {
validateSender(e.senderFrame); validateSender(e.senderFrame, event);
return cb(e, ...args); return cb(e, ...args);
}); });
} }

View File

@@ -50,7 +50,7 @@ export function handleExternalUrl(url: string, protocol?: string): { action: "de
export function makeLinksOpenExternally(win: BrowserWindow) { export function makeLinksOpenExternally(win: BrowserWindow) {
win.webContents.setWindowOpenHandler(({ url, frameName, features }) => { win.webContents.setWindowOpenHandler(({ url, frameName, features }) => {
try { try {
var { protocol, hostname, pathname } = new URL(url); var { protocol, hostname, pathname, searchParams } = new URL(url);
} catch { } catch {
return { action: "deny" }; return { action: "deny" };
} }
@@ -59,8 +59,10 @@ export function makeLinksOpenExternally(win: BrowserWindow) {
return createOrFocusPopup(frameName, features); return createOrFocusPopup(frameName, features);
} }
if (url === "about:blank" || (frameName === "authorize" && DISCORD_HOSTNAMES.includes(hostname))) if (url === "about:blank") return { action: "allow" };
return { action: "allow" };
// Drop the static temp page Discord web loads for the connections popout
if (frameName === "authorize" && searchParams.get("loading") === "true") return { action: "deny" };
return handleExternalUrl(url, protocol); return handleExternalUrl(url, protocol);
}); });

10
src/module.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/*
* 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
*/
declare module "__patches__" {
const never: never;
export default never;
}

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
const { contextBridge, ipcRenderer } = require("electron/renderer"); import { contextBridge, ipcRenderer } from "electron";
contextBridge.exposeInMainWorld("VesktopSplashNative", { contextBridge.exposeInMainWorld("VesktopSplashNative", {
onUpdateMessage(callback: (message: string) => void) { onUpdateMessage(callback: (message: string) => void) {

9
src/renderer/common.ts Normal file
View File

@@ -0,0 +1,9 @@
/*
* 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 { findStoreLazy } from "@vencord/types/webpack";
export const MediaEngineStore = findStoreLazy("MediaEngineStore");

View File

@@ -7,7 +7,7 @@
import "./screenSharePicker.css"; import "./screenSharePicker.css";
import { closeModal, Logger, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils"; import { closeModal, Logger, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils";
import { findStoreLazy, onceReady } from "@vencord/types/webpack"; import { onceReady } from "@vencord/types/webpack";
import { import {
Button, Button,
Card, Card,
@@ -21,6 +21,7 @@ import {
} from "@vencord/types/webpack/common"; } from "@vencord/types/webpack/common";
import { Node } from "@vencord/venmic"; import { Node } from "@vencord/venmic";
import type { Dispatch, SetStateAction } from "react"; import type { Dispatch, SetStateAction } from "react";
import { MediaEngineStore } from "renderer/common";
import { addPatch } from "renderer/patches/shared"; import { addPatch } from "renderer/patches/shared";
import { State, useSettings, useVesktopState } from "renderer/settings"; import { State, useSettings, useVesktopState } from "renderer/settings";
import { classNameFactory, isLinux, isWindows } from "renderer/utils"; import { classNameFactory, isLinux, isWindows } from "renderer/utils";
@@ -30,8 +31,6 @@ const StreamFps = ["15", "30", "60"] as const;
const cl = classNameFactory("vcd-screen-picker-"); const cl = classNameFactory("vcd-screen-picker-");
const MediaEngineStore = findStoreLazy("MediaEngineStore");
export type StreamResolution = (typeof StreamResolutions)[number]; export type StreamResolution = (typeof StreamResolutions)[number];
export type StreamFps = (typeof StreamFps)[number]; export type StreamFps = (typeof StreamFps)[number];

View File

@@ -1,4 +0,0 @@
/* Workaround for making things in the draggable area clickable again on macOS */
.platform-osx [class*=topic_], .platform-osx [class*=notice_] button {
-webkit-app-region: no-drag;
}

View File

@@ -4,8 +4,6 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import "./fixes.css";
import { localStorage } from "./utils"; import { localStorage } from "./utils";
// Make clicking Notifications focus the window // Make clicking Notifications focus the window

View File

@@ -7,9 +7,9 @@
import "./themedSplash"; import "./themedSplash";
import "./ipcCommands"; import "./ipcCommands";
import "./appBadge"; import "./appBadge";
import "./patches";
import "./fixes"; import "./fixes";
import "./arrpc"; import "./arrpc";
import "__patches__"; // auto generated by the build script
export * as Components from "./components"; export * as Components from "./components";

View File

@@ -7,6 +7,8 @@
import { SettingsRouter } from "@vencord/types/webpack/common"; import { SettingsRouter } from "@vencord/types/webpack/common";
import { IpcCommands } from "shared/IpcEvents"; import { IpcCommands } from "shared/IpcEvents";
import { openScreenSharePicker } from "./components/ScreenSharePicker";
type IpcCommandHandler = (data: any) => any; type IpcCommandHandler = (data: any) => any;
const handlers = new Map<string, IpcCommandHandler>(); const handlers = new Map<string, IpcCommandHandler>();
@@ -46,4 +48,7 @@ export function offIpcCommand(channel: string) {
onIpcCommand(IpcCommands.NAVIGATE_SETTINGS, () => { onIpcCommand(IpcCommands.NAVIGATE_SETTINGS, () => {
SettingsRouter.open("My Account"); SettingsRouter.open("My Account");
}); });
onIpcCommand(IpcCommands.GET_LANGUAGES, () => navigator.languages); onIpcCommand(IpcCommands.GET_LANGUAGES, () => navigator.languages);
onIpcCommand(IpcCommands.SCREEN_SHARE_PICKER, data => openScreenSharePicker(data.screens, data.skipPicker));

View File

@@ -0,0 +1,51 @@
/*
* 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 { Logger } from "@vencord/types/utils";
import { MediaEngineStore } from "renderer/common";
const logger = new Logger("FixAutoGain");
function fixTrackConstraints(constraint: MediaTrackConstraints) {
const target = constraint.advanced?.find(opt => Object.hasOwn(opt, "autoGainControl")) ?? constraint;
target.autoGainControl = MediaEngineStore.getAutomaticGainControl();
}
function fixStreamConstraints(constraints: MediaStreamConstraints | undefined) {
if (!constraints?.audio) return;
if (typeof constraints.audio !== "object") {
constraints.audio = {};
}
fixTrackConstraints(constraints.audio);
}
const originalGetUserMedia = navigator.mediaDevices.getUserMedia;
navigator.mediaDevices.getUserMedia = function (constraints) {
try {
fixStreamConstraints(constraints);
logger.debug("Fixed getUserMedia constraints", constraints);
} catch (e) {
logger.error("Failed to fix getUserMedia constraints", e);
}
return originalGetUserMedia.call(this, constraints);
};
const originalApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
MediaStreamTrack.prototype.applyConstraints = function (constraints) {
if (constraints) {
try {
fixTrackConstraints(constraints);
logger.debug("Fixed applyConstraints constraints", constraints);
} catch (e) {
logger.error("Failed to fix applyConstraints constraints", e);
}
}
return originalApplyConstraints.call(this, constraints);
};

View File

@@ -13,12 +13,12 @@ addPatch({
replacement: { replacement: {
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
match: /(\i)\.\i\.getState\(\).neverShowModal/, match: /(\i)\.\i\.getState\(\).neverShowModal/,
replace: "$& || $self.shouldIgnore($1)" replace: "$& || $self.shouldIgnoreDevice($1)"
} }
} }
], ],
shouldIgnore(state: any) { shouldIgnoreDevice(state: any) {
return Object.keys(state?.default?.lastDeviceConnected ?? {})?.[0] === "vencord-screen-share"; return Object.keys(state?.default?.lastDeviceConnected ?? {})?.[0] === "vencord-screen-share";
} }
}); });

View File

@@ -1,18 +0,0 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
// TODO: Possibly auto generate glob if we have more patches in the future
import "./enableNotificationsByDefault";
import "./platformClass";
import "./allowDevToolsKeybind";
import "./hideSwitchDevice";
import "./hideVenmicInput";
import "./screenShareFixes";
import "./spellCheck";
import "./windowsTitleBar";
import "./streamerMode";
import "./windowMethods";
import "./hideDownloadAppsButton";

View File

@@ -59,10 +59,15 @@ if (isLinux) {
}, },
autoGainControl: false, autoGainControl: false,
echoCancellation: false, echoCancellation: false,
noiseSuppression: false noiseSuppression: false,
channelCount: 2,
sampleRate: 48000,
sampleSize: 16
} }
}); });
audio.getAudioTracks().forEach(t => stream.addTrack(t));
stream.getAudioTracks().forEach(t => stream.removeTrack(t));
stream.addTrack(audio.getAudioTracks()[0]);
} }
return stream; return stream;

View File

@@ -17,7 +17,6 @@ export function addPatch<P extends PatchData>(p: P) {
const { patches, ...globals } = p; const { patches, ...globals } = p;
for (const patch of patches) { for (const patch of patches) {
// TODO: Update types
Vencord.Plugins.addPatch(patch, "Vesktop", "VesktopPatchGlobals"); Vencord.Plugins.addPatch(patch, "Vesktop", "VesktopPatchGlobals");
} }

View File

@@ -62,6 +62,10 @@ export const enum IpcCommands {
RPC_ACTIVITY = "rpc:activity", RPC_ACTIVITY = "rpc:activity",
RPC_INVITE = "rpc:invite", RPC_INVITE = "rpc:invite",
RPC_DEEP_LINK = "rpc:link", RPC_DEEP_LINK = "rpc:link",
NAVIGATE_SETTINGS = "navigate:settings", NAVIGATE_SETTINGS = "navigate:settings",
GET_LANGUAGES = "navigator.languages"
GET_LANGUAGES = "navigator.languages",
SCREEN_SHARE_PICKER = "screenshare:picker"
} }

15
src/shared/utils/text.ts Normal file
View File

@@ -0,0 +1,15 @@
/*
* 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
*/
export function stripIndent(strings: TemplateStringsArray, ...values: any[]) {
const string = String.raw({ raw: strings }, ...values);
const match = string.match(/^[ \t]*(?=\S)/gm);
if (!match) return string.trim();
const minIndent = match.reduce((r, a) => Math.min(r, a.length), Infinity);
return string.replace(new RegExp(`^[ \\t]{${minIndent}}`, "gm"), "").trim();
}