@@ -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"
|
||||||
@@ -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
625
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -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" }
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|||||||
25
scripts/build/includeDirPlugin.mts
Normal file
25
scripts/build/includeDirPlugin.mts
Normal 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
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
10
src/module.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
@@ -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
9
src/renderer/common.ts
Normal 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");
|
||||||
@@ -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];
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
51
src/renderer/patches/fixAutoGainToggle.ts
Normal file
51
src/renderer/patches/fixAutoGainToggle.ts
Normal 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);
|
||||||
|
};
|
||||||
@@ -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";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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";
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
15
src/shared/utils/text.ts
Normal 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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user