Compare commits
20 Commits
v1.6.0
...
vencord-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f01dfdbd0 | ||
|
|
13e7e4491f | ||
|
|
34c92cfb59 | ||
|
|
28aeab979b | ||
|
|
8ca3e4f3a1 | ||
|
|
ae47c204fa | ||
|
|
f57245f297 | ||
|
|
9193ed58c9 | ||
|
|
38b7716b2f | ||
|
|
4b735b9739 | ||
|
|
707dbf4f75 | ||
|
|
a242d5d694 | ||
|
|
b75ed0766d | ||
|
|
03aa30dfc6 | ||
|
|
cd1a40e6c7 | ||
|
|
3aa0bb806e | ||
|
|
800a97105c | ||
|
|
b02acd6a7b | ||
|
|
d293b166fe | ||
|
|
28a13be709 |
18
package.json
18
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vesktop",
|
||||
"version": "1.6.0",
|
||||
"version": "1.6.1",
|
||||
"private": true,
|
||||
"description": "Vesktop is a custom Discord desktop app",
|
||||
"keywords": [],
|
||||
@@ -35,28 +35,28 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
||||
"@stylistic/eslint-plugin": "^5.4.0",
|
||||
"@types/node": "^24.7.0",
|
||||
"@stylistic/eslint-plugin": "^5.5.0",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/react": "19.2.1",
|
||||
"@vencord/types": "^1.13.2",
|
||||
"dotenv": "^17.2.3",
|
||||
"electron": "^38.0.0",
|
||||
"electron": "^39.2.1",
|
||||
"electron-builder": "^26.0.12",
|
||||
"esbuild": "^0.25.10",
|
||||
"eslint": "^9.37.0",
|
||||
"esbuild": "^0.27.0",
|
||||
"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-simple-header": "^1.2.2",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-unused-imports": "^4.2.0",
|
||||
"eslint-plugin-unused-imports": "^4.3.0",
|
||||
"libvesktop": "link:packages/libvesktop",
|
||||
"prettier": "^3.6.2",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tsx": "^4.20.6",
|
||||
"type-fest": "^5.0.1",
|
||||
"type-fest": "^5.2.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.0",
|
||||
"typescript-eslint": "^8.47.0",
|
||||
"xml-formatter": "^3.6.7"
|
||||
},
|
||||
"packageManager": "pnpm@10.7.1",
|
||||
|
||||
953
pnpm-lock.yaml
generated
953
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -43,16 +43,25 @@ function generateDescription(description: string, descriptionNode: Element) {
|
||||
}
|
||||
}
|
||||
|
||||
const latestReleaseInformation = await fetch("https://api.github.com/repos/Vencord/Vesktop/releases/latest", {
|
||||
const releases = await fetch("https://api.github.com/repos/Vencord/Vesktop/releases", {
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
"X-Github-Api-Version": "2022-11-28"
|
||||
}
|
||||
}).then(res => res.json());
|
||||
|
||||
const metaInfo = await fetch(
|
||||
"https://github.com/Vencord/Vesktop/releases/latest/download/dev.vencord.Vesktop.metainfo.xml"
|
||||
).then(res => res.text());
|
||||
const latestReleaseInformation = releases[0];
|
||||
|
||||
const metaInfo = await (async () => {
|
||||
for (const release of releases) {
|
||||
const metaAsset = release.assets.find((a: any) => a.name === "dev.vencord.Vesktop.metainfo.xml");
|
||||
if (metaAsset) return fetch(metaAsset.browser_download_url).then(res => res.text());
|
||||
}
|
||||
})();
|
||||
|
||||
if (!metaInfo) {
|
||||
throw new Error("Could not find existing meta information from any release");
|
||||
}
|
||||
|
||||
const parser = new DOMParser().parseFromString(metaInfo, "text/xml");
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { app } from "electron";
|
||||
import { basename } from "path";
|
||||
import { stripIndent } from "shared/utils/text";
|
||||
import { parseArgs, ParseArgsOptionDescriptor } from "util";
|
||||
|
||||
@@ -64,7 +65,12 @@ const extraOptions = {
|
||||
}
|
||||
} satisfies Record<string, Option>;
|
||||
|
||||
const args = basename(process.argv[0]).toLowerCase().startsWith("electron")
|
||||
? process.argv.slice(2)
|
||||
: process.argv.slice(1);
|
||||
|
||||
export const CommandLine = parseArgs({
|
||||
args,
|
||||
options,
|
||||
strict: false as true, // we manually check later, so cast to true to get better types
|
||||
allowPositionals: true
|
||||
@@ -82,7 +88,7 @@ export function checkCommandLineForHelpOrVersion() {
|
||||
const base = stripIndent`
|
||||
Vesktop v${app.getVersion()}
|
||||
|
||||
Usage: ${process.execPath} [options] [url]
|
||||
Usage: ${basename(process.execPath)} [options] [url]
|
||||
|
||||
Electron Options:
|
||||
See <https://www.electronjs.org/docs/latest/api/command-line-switches#electron-cli-flags>
|
||||
@@ -135,3 +141,5 @@ export function checkCommandLineForHelpOrVersion() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkCommandLineForHelpOrVersion();
|
||||
|
||||
@@ -26,6 +26,7 @@ export const SESSION_DATA_DIR = join(DATA_DIR, "sessionData");
|
||||
app.setPath("sessionData", SESSION_DATA_DIR);
|
||||
|
||||
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
||||
mkdirSync(VENCORD_SETTINGS_DIR, { recursive: true });
|
||||
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
|
||||
export const VENCORD_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json");
|
||||
export const VENCORD_THEMES_DIR = join(DATA_DIR, "themes");
|
||||
|
||||
@@ -31,8 +31,8 @@ export function createFirstLaunchTour() {
|
||||
transparent: false,
|
||||
frame: true,
|
||||
autoHideMenuBar: true,
|
||||
height: 470,
|
||||
width: 550
|
||||
height: 550,
|
||||
width: 600
|
||||
});
|
||||
|
||||
makeLinksOpenExternally(win);
|
||||
@@ -44,7 +44,6 @@ export function createFirstLaunchTour() {
|
||||
if (!msg.startsWith("form:")) return;
|
||||
const data = JSON.parse(msg.slice(5)) as Data;
|
||||
|
||||
console.log(data);
|
||||
State.store.firstLaunch = false;
|
||||
Settings.store.discordBranch = data.discordBranch;
|
||||
Settings.store.minimizeToTray = !!data.minimizeToTray;
|
||||
@@ -63,7 +62,11 @@ export function createFirstLaunchTour() {
|
||||
copyFileSync(join(from, file), join(to, file));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to import settings:", e);
|
||||
if (e instanceof Error && "code" in e && e.code === "ENOENT") {
|
||||
console.log("No Vencord settings found to import.");
|
||||
} else {
|
||||
console.error("Failed to import Vencord settings:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./cli";
|
||||
import "./updater";
|
||||
import "./ipc";
|
||||
import "./userAssets";
|
||||
@@ -11,7 +12,6 @@ import "./vesktopProtocol";
|
||||
|
||||
import { app, BrowserWindow, nativeTheme } from "electron";
|
||||
|
||||
import { checkCommandLineForHelpOrVersion } from "./cli";
|
||||
import { DATA_DIR } from "./constants";
|
||||
import { createFirstLaunchTour } from "./firstLaunch";
|
||||
import { createWindows, mainWin } from "./mainWindow";
|
||||
@@ -21,8 +21,6 @@ import { Settings, State } from "./settings";
|
||||
import { setAsDefaultProtocolClient } from "./utils/setAsDefaultProtocolClient";
|
||||
import { isDeckGameMode } from "./utils/steamOS";
|
||||
|
||||
checkCommandLineForHelpOrVersion();
|
||||
|
||||
console.log("Vesktop v" + app.getVersion());
|
||||
|
||||
// Make the Vencord files use our DATA_DIR
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Menu,
|
||||
MenuItemConstructorOptions,
|
||||
nativeTheme,
|
||||
Rectangle,
|
||||
screen,
|
||||
session
|
||||
} from "electron";
|
||||
@@ -175,55 +176,6 @@ function initMenuBar(win: BrowserWindow) {
|
||||
Menu.setApplicationMenu(menu);
|
||||
}
|
||||
|
||||
function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
|
||||
// We want the default window behaivour to apply in game mode since it expects everything to be fullscreen and maximized.
|
||||
if (isDeckGameMode) return {};
|
||||
|
||||
const { x, y, width, height } = State.store.windowBounds ?? {};
|
||||
|
||||
const options = {
|
||||
width: width ?? DEFAULT_WIDTH,
|
||||
height: height ?? DEFAULT_HEIGHT
|
||||
} as BrowserWindowConstructorOptions;
|
||||
|
||||
const storedDisplay = screen.getAllDisplays().find(display => display.id === State.store.displayId);
|
||||
|
||||
if (x != null && y != null && storedDisplay) {
|
||||
options.x = x;
|
||||
options.y = y;
|
||||
}
|
||||
|
||||
if (!Settings.store.disableMinSize) {
|
||||
options.minWidth = MIN_WIDTH;
|
||||
options.minHeight = MIN_HEIGHT;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function getDarwinOptions(): BrowserWindowConstructorOptions {
|
||||
const options = {
|
||||
titleBarStyle: "hidden",
|
||||
trafficLightPosition: { x: 10, y: 10 }
|
||||
} as BrowserWindowConstructorOptions;
|
||||
|
||||
const { splashTheming, splashBackground } = Settings.store;
|
||||
const { macosTranslucency } = VencordSettings.store;
|
||||
|
||||
if (macosTranslucency) {
|
||||
options.vibrancy = "sidebar";
|
||||
options.backgroundColor = "#ffffff00";
|
||||
} else {
|
||||
if (splashTheming !== false) {
|
||||
options.backgroundColor = splashBackground;
|
||||
} else {
|
||||
options.backgroundColor = nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff";
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function initWindowBoundsListeners(win: BrowserWindow) {
|
||||
const saveState = () => {
|
||||
State.store.maximized = win.isMaximized();
|
||||
@@ -236,7 +188,6 @@ function initWindowBoundsListeners(win: BrowserWindow) {
|
||||
|
||||
const saveBounds = () => {
|
||||
State.store.windowBounds = win.getBounds();
|
||||
State.store.displayId = screen.getDisplayMatching(State.store.windowBounds).id;
|
||||
};
|
||||
|
||||
win.on("resize", saveBounds);
|
||||
@@ -324,26 +275,55 @@ function initStaticTitle(win: BrowserWindow) {
|
||||
});
|
||||
}
|
||||
|
||||
function createMainWindow() {
|
||||
// Clear up previous settings listeners
|
||||
removeSettingsListeners();
|
||||
removeVencordSettingsListeners();
|
||||
function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
|
||||
// We want the default window behaviour to apply in game mode since it expects everything to be fullscreen and maximized.
|
||||
if (isDeckGameMode) return {};
|
||||
|
||||
const { x, y, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = State.store.windowBounds ?? {};
|
||||
|
||||
const options = { width, height } as BrowserWindowConstructorOptions;
|
||||
|
||||
if (x != null && y != null) {
|
||||
function isInBounds(rect: Rectangle, display: Rectangle) {
|
||||
return !(
|
||||
rect.x + rect.width < display.x ||
|
||||
rect.x > display.x + display.width ||
|
||||
rect.y + rect.height < display.y ||
|
||||
rect.y > display.y + display.height
|
||||
);
|
||||
}
|
||||
|
||||
const inBounds = screen.getAllDisplays().some(d => isInBounds({ x, y, width, height }, d.bounds));
|
||||
if (inBounds) {
|
||||
options.x = x;
|
||||
options.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Settings.store.disableMinSize) {
|
||||
options.minWidth = MIN_WIDTH;
|
||||
options.minHeight = MIN_HEIGHT;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function buildBrowserWindowOptions(): BrowserWindowConstructorOptions {
|
||||
const { staticTitle, transparencyOption, enableMenu, customTitleBar, splashTheming, splashBackground } =
|
||||
Settings.store;
|
||||
|
||||
const { frameless, transparent } = VencordSettings.store;
|
||||
const { frameless, transparent, macosTranslucency } = VencordSettings.store;
|
||||
|
||||
const noFrame = frameless === true || customTitleBar === true;
|
||||
const backgroundColor =
|
||||
splashTheming !== false ? splashBackground : nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff";
|
||||
|
||||
const win = (mainWin = new BrowserWindow({
|
||||
show: Settings.store.enableSplashScreen === false,
|
||||
const options: BrowserWindowConstructorOptions = {
|
||||
show: Settings.store.enableSplashScreen === false && !CommandLine.values["start-minimized"],
|
||||
backgroundColor,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
sandbox: false,
|
||||
sandbox: false, // TODO
|
||||
contextIsolation: true,
|
||||
devTools: true,
|
||||
preload: join(__dirname, "preload.js"),
|
||||
@@ -352,28 +332,50 @@ function createMainWindow() {
|
||||
backgroundThrottling: false
|
||||
},
|
||||
frame: !noFrame,
|
||||
...(transparent && {
|
||||
transparent: true,
|
||||
backgroundColor: "#00000000"
|
||||
}),
|
||||
...(transparencyOption &&
|
||||
transparencyOption !== "none" && {
|
||||
backgroundColor: "#00000000",
|
||||
backgroundMaterial: transparencyOption
|
||||
}),
|
||||
// Fix transparencyOption for custom discord titlebar
|
||||
...(customTitleBar &&
|
||||
transparencyOption &&
|
||||
transparencyOption !== "none" && {
|
||||
transparent: true
|
||||
}),
|
||||
...(staticTitle && { title: "Vesktop" }),
|
||||
...(process.platform === "darwin" && getDarwinOptions()),
|
||||
...getWindowBoundsOptions(),
|
||||
autoHideMenuBar: enableMenu
|
||||
}));
|
||||
autoHideMenuBar: enableMenu,
|
||||
...getWindowBoundsOptions()
|
||||
};
|
||||
|
||||
if (transparent) {
|
||||
options.transparent = true;
|
||||
options.backgroundColor = "#00000000";
|
||||
}
|
||||
|
||||
if (transparencyOption && transparencyOption !== "none") {
|
||||
options.backgroundColor = "#00000000";
|
||||
options.backgroundMaterial = transparencyOption;
|
||||
|
||||
if (customTitleBar) {
|
||||
options.transparent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (staticTitle) {
|
||||
options.title = "Vesktop";
|
||||
}
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
options.titleBarStyle = "hidden";
|
||||
options.trafficLightPosition = { x: 10, y: 10 };
|
||||
|
||||
if (macosTranslucency) {
|
||||
options.vibrancy = "sidebar";
|
||||
options.backgroundColor = "#ffffff00";
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function createMainWindow() {
|
||||
// Clear up previous settings listeners
|
||||
removeSettingsListeners();
|
||||
removeVencordSettingsListeners();
|
||||
|
||||
const win = (mainWin = new BrowserWindow(buildBrowserWindowOptions()));
|
||||
|
||||
win.setMenuBarVisibility(false);
|
||||
if (process.platform === "darwin" && customTitleBar) win.setWindowButtonVisibility(false);
|
||||
if (process.platform === "darwin" && Settings.store.customTitleBar) win.setWindowButtonVisibility(false);
|
||||
|
||||
win.on("close", e => {
|
||||
const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false;
|
||||
|
||||
@@ -24,7 +24,7 @@ export function createSplashWindow(startMinimized = false) {
|
||||
|
||||
loadView(splash, "splash.html");
|
||||
|
||||
const { splashBackground, splashColor, splashTheming } = Settings.store;
|
||||
const { splashBackground, splashColor, splashTheming, splashPixelated } = Settings.store;
|
||||
|
||||
if (splashTheming !== false) {
|
||||
if (splashColor) {
|
||||
@@ -39,6 +39,10 @@ export function createSplashWindow(startMinimized = false) {
|
||||
}
|
||||
}
|
||||
|
||||
if (splashPixelated) {
|
||||
splash.webContents.insertCSS(`img { image-rendering: pixelated; }`);
|
||||
}
|
||||
|
||||
return splash;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
import "./screenSharePicker.css";
|
||||
|
||||
import { classNameFactory } from "@vencord/types/api/Styles";
|
||||
import { FormSwitch } from "@vencord/types/components";
|
||||
import { closeModal, Logger, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils";
|
||||
import { Button, Card, CogWheel, FormSwitch, Heading, Paragraph, RestartIcon } from "@vencord/types/components";
|
||||
import { closeModal, Logger, 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";
|
||||
import { isLinux, isWindows } from "renderer/constants";
|
||||
import { addPatch } from "renderer/patches/shared";
|
||||
import { State, useSettings, useVesktopState } from "renderer/settings";
|
||||
import { isLinux, isWindows } from "renderer/utils";
|
||||
|
||||
const StreamResolutions = ["480", "720", "1080", "1440", "2160"] as const;
|
||||
const StreamFps = ["15", "30", "60"] as const;
|
||||
@@ -165,9 +165,9 @@ function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScre
|
||||
/>
|
||||
|
||||
<img src={url} alt="" />
|
||||
<Text className={cl("screen-name")} variant="text-sm/normal">
|
||||
<Paragraph className={cl("screen-name")} size="sm">
|
||||
{name}
|
||||
</Text>
|
||||
</Paragraph>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
@@ -188,12 +188,13 @@ function AudioSettingsModal({
|
||||
return (
|
||||
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<Modals.ModalHeader className={cl("header")}>
|
||||
<Forms.FormTitle tag="h2" className={cl("header-title")}>
|
||||
<Heading tag="h2" className={cl("header-title")}>
|
||||
Venmic Settings
|
||||
</Forms.FormTitle>
|
||||
<Modals.ModalCloseButton onClick={close} />
|
||||
</Heading>
|
||||
<Modals.ModalCloseButton onClick={close} className={cl("header-close-button")} />
|
||||
</Modals.ModalHeader>
|
||||
<Modals.ModalContent className={cl("modal")}>
|
||||
|
||||
<Modals.ModalContent className={cl("modal", "venmic-settings")}>
|
||||
<FormSwitch
|
||||
title="Microphone Workaround"
|
||||
description={
|
||||
@@ -290,7 +291,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>
|
||||
@@ -311,7 +312,9 @@ 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>
|
||||
<Paragraph size="sm" weight="bold">
|
||||
{labels?.[idx] ?? option}
|
||||
</Paragraph>
|
||||
<input
|
||||
className={cl("option-input")}
|
||||
type="radio"
|
||||
@@ -349,7 +352,7 @@ function StreamSettingsUi({
|
||||
);
|
||||
|
||||
const openSettings = () => {
|
||||
const key = openModal(props => (
|
||||
openModal(props => (
|
||||
<AudioSettingsModal
|
||||
modalProps={props}
|
||||
close={() => props.onClose()}
|
||||
@@ -362,18 +365,18 @@ function StreamSettingsUi({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
||||
<Heading>What you're streaming</Heading>
|
||||
<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 size="sm">{source.name}</Paragraph>
|
||||
</Card>
|
||||
|
||||
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
|
||||
<Heading>Stream Settings</Heading>
|
||||
|
||||
<Card className={cl("card")}>
|
||||
<div className={cl("quality")}>
|
||||
<section className={cl("quality-section")}>
|
||||
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
||||
<Heading>Resolution</Heading>
|
||||
<OptionRadio
|
||||
options={StreamResolutions}
|
||||
settings={qualitySettings}
|
||||
@@ -383,7 +386,7 @@ function StreamSettingsUi({
|
||||
</section>
|
||||
|
||||
<section className={cl("quality-section")}>
|
||||
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
||||
<Heading>Frame Rate</Heading>
|
||||
<OptionRadio
|
||||
options={StreamFps}
|
||||
settings={qualitySettings}
|
||||
@@ -394,7 +397,7 @@ function StreamSettingsUi({
|
||||
</div>
|
||||
<div className={cl("quality")}>
|
||||
<section className={cl("quality-section")}>
|
||||
<Forms.FormTitle>Content Type</Forms.FormTitle>
|
||||
<Heading>Content Type</Heading>
|
||||
<div>
|
||||
<OptionRadio
|
||||
options={["motion", "detail"]}
|
||||
@@ -568,8 +571,10 @@ function AudioSourcePickerLinux({
|
||||
setIncludeSources: (s: AudioSources) => void;
|
||||
setExcludeSources: (s: AudioSources) => void;
|
||||
}) {
|
||||
const [audioSourcesSignal, refreshAudioSources] = useForceUpdater(true);
|
||||
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
|
||||
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
|
||||
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true },
|
||||
deps: [audioSourcesSignal]
|
||||
});
|
||||
|
||||
const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true;
|
||||
@@ -577,7 +582,7 @@ 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">
|
||||
venmic
|
||||
@@ -587,13 +592,13 @@ function AudioSourcePickerLinux({
|
||||
this guide
|
||||
</a>{" "}
|
||||
for possible solutions.
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasPipewirePulse && !ignorePulseWarning) {
|
||||
return (
|
||||
<Text variant="text-sm/normal">
|
||||
<Paragraph size="sm">
|
||||
Could not find pipewire-pulse. See{" "}
|
||||
<a href="https://gist.github.com/the-spyke/2de98b22ff4f978ebf0650c90e82027e#install" target="_blank">
|
||||
this guide
|
||||
@@ -602,7 +607,7 @@ function AudioSourcePickerLinux({
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -620,9 +625,9 @@ function AudioSourcePickerLinux({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={cl({ quality: includeSources === "Entire System" })}>
|
||||
<div className={cl("audio-sources")}>
|
||||
<section>
|
||||
<Forms.FormTitle>{loading ? "Loading Sources..." : "Audio Sources"}</Forms.FormTitle>
|
||||
<Heading>{loading ? "Loading Sources..." : "Audio Sources"}</Heading>
|
||||
<Select
|
||||
options={allSources.map(({ name, value }) => ({
|
||||
label: name,
|
||||
@@ -638,7 +643,7 @@ function AudioSourcePickerLinux({
|
||||
</section>
|
||||
{includeSources === "Entire System" && (
|
||||
<section>
|
||||
<Forms.FormTitle>Exclude Sources</Forms.FormTitle>
|
||||
<Heading>Exclude Sources</Heading>
|
||||
<Select
|
||||
options={allSources
|
||||
.filter(x => x.name !== "Entire System")
|
||||
@@ -656,9 +661,16 @@ function AudioSourcePickerLinux({
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
<Button color={Button.Colors.TRANSPARENT} onClick={openSettings} className={cl("settings-button")}>
|
||||
Open Audio Settings
|
||||
</Button>
|
||||
<div className={cl("settings-buttons")}>
|
||||
<Button variant="secondary" onClick={refreshAudioSources} className={cl("settings-button")}>
|
||||
<RestartIcon className={cl("settings-button-icon")} />
|
||||
Refresh Audio Sources
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={openSettings} className={cl("settings-button")}>
|
||||
<CogWheel className={cl("settings-button-icon")} />
|
||||
Open Audio Settings
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -690,9 +702,12 @@ 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} />
|
||||
<Heading tag="h2" className={cl("header-title")}>
|
||||
ScreenShare
|
||||
</Heading>
|
||||
<Modals.ModalCloseButton onClick={close} className={cl("header-close-button")} />
|
||||
</Modals.ModalHeader>
|
||||
|
||||
<Modals.ModalContent className={cl("modal")}>
|
||||
{!selected ? (
|
||||
<ScreenPicker screens={screens} chooseScreen={setSelected} />
|
||||
@@ -769,11 +784,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>
|
||||
)}
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-header-title {
|
||||
margin: 0;
|
||||
.vcd-screen-picker-header-close-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-venmic-settings {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-footer {
|
||||
@@ -13,9 +18,10 @@
|
||||
|
||||
.vcd-screen-picker-card {
|
||||
flex-grow: 1;
|
||||
padding: 0.5em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
/* Screen Grid */
|
||||
.vcd-screen-picker-screen-grid {
|
||||
display: grid;
|
||||
@@ -49,11 +55,6 @@
|
||||
margin-inline: 0.5em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-card {
|
||||
padding: 0.5em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-preview-img-linux {
|
||||
width: 60%;
|
||||
margin-bottom: 0.5em;
|
||||
@@ -117,19 +118,40 @@
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-settings-button {
|
||||
margin-left: auto;
|
||||
margin-top: 0.3rem;
|
||||
.vcd-screen-picker-settings-buttons {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
gap: 0.5em;
|
||||
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-settings-button {
|
||||
display: flex;
|
||||
gap: 0.25em;
|
||||
padding-inline: 0.5em 1em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-settings-button-icon {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-audio {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-audio-sources {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
|
||||
>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 { Heading, Paragraph } 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 { Button, Text, Toasts } from "@vencord/types/webpack/common";
|
||||
import { Settings } from "shared/settings";
|
||||
|
||||
import { cl, SettingsComponent } from "./Settings";
|
||||
@@ -35,12 +36,12 @@ function openDeveloperOptionsModal(settings: Settings) {
|
||||
|
||||
<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,7 +60,7 @@ const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Forms.FormText>
|
||||
<Paragraph>
|
||||
Vencord files are loaded from{" "}
|
||||
{vencordDir ? (
|
||||
<a
|
||||
@@ -74,7 +75,7 @@ const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
||||
) : (
|
||||
"the default location"
|
||||
)}
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
<div className={cl("button-grid")}>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
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 { Divider, ErrorBoundary } from "@vencord/types/components";
|
||||
import { Text } from "@vencord/types/webpack/common";
|
||||
import { ComponentType } from "react";
|
||||
import { isMac, isWindows } from "renderer/constants";
|
||||
import { Settings, useSettings } from "renderer/settings";
|
||||
import { isMac, isWindows } from "renderer/utils";
|
||||
|
||||
import { AutoStartToggle } from "./AutoStartToggle";
|
||||
import { DeveloperOptionsButton } from "./DeveloperOptions";
|
||||
@@ -173,7 +173,7 @@ function SettingsSections() {
|
||||
})}
|
||||
</div>
|
||||
|
||||
{i < arr.length - 1 && <Forms.FormDivider className={cl("category-divider")} />}
|
||||
{i < arr.length - 1 && <Divider className={cl("category-divider")} />}
|
||||
</div>
|
||||
));
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Paragraph } from "@vencord/types/components";
|
||||
import { useAwaiter } from "@vencord/types/utils";
|
||||
import { Button, Text } from "@vencord/types/webpack/common";
|
||||
import { Button } from "@vencord/types/webpack/common";
|
||||
|
||||
import { cl } from "./Settings";
|
||||
|
||||
@@ -16,8 +17,12 @@ export function Updater() {
|
||||
|
||||
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>
|
||||
<Paragraph size="md" weight="semibold">
|
||||
Your Vesktop is outdated!
|
||||
</Paragraph>
|
||||
<Paragraph size="sm" weight="normal">
|
||||
Staying up to date is important for security and stability.
|
||||
</Paragraph>
|
||||
|
||||
<Button
|
||||
onClick={() => VesktopNative.app.openUpdater()}
|
||||
|
||||
@@ -13,8 +13,14 @@
|
||||
}
|
||||
|
||||
.vcd-user-assets-actions {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
gap: 0.5em;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.vcd-user-assets-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
import "./UserAssets.css";
|
||||
|
||||
import { FormSwitch, Heading } from "@vencord/types/components";
|
||||
import {
|
||||
Margins,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
@@ -18,6 +20,7 @@ import {
|
||||
} from "@vencord/types/utils";
|
||||
import { Button, showToast, Text, useState } from "@vencord/types/webpack/common";
|
||||
import { UserAssetType } from "main/userAssets";
|
||||
import { useSettings } from "renderer/settings";
|
||||
|
||||
import { SettingsComponent } from "./Settings";
|
||||
|
||||
@@ -51,11 +54,18 @@ function openAssetsModal() {
|
||||
function Asset({ asset }: { asset: UserAssetType }) {
|
||||
// cache busting
|
||||
const [version, setVersion] = useState(Date.now());
|
||||
const settings = useSettings();
|
||||
|
||||
const isSplash = asset === "splash";
|
||||
const imageRendering = isSplash && settings.splashPixelated ? "pixelated" : "auto";
|
||||
|
||||
const onChooseAsset = (value?: null) => async () => {
|
||||
const res = await VesktopNative.fileManager.chooseUserAsset(asset, value);
|
||||
if (res === "ok") {
|
||||
setVersion(Date.now());
|
||||
if (isSplash && value === null) {
|
||||
settings.splashPixelated = false;
|
||||
}
|
||||
} else if (res === "failed") {
|
||||
showToast("Something went wrong. Please try again");
|
||||
}
|
||||
@@ -63,16 +73,30 @@ function Asset({ asset }: { asset: UserAssetType }) {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Text tag="h3" variant="text-md/semibold">
|
||||
{wordsToTitle(wordsFromCamel(asset))}
|
||||
</Text>
|
||||
<Heading tag="h3">{wordsToTitle(wordsFromCamel(asset))}</Heading>
|
||||
<div className="vcd-user-assets-asset">
|
||||
<img className="vcd-user-assets-image" src={`vesktop://assets/${asset}?v=${version}`} alt="" />
|
||||
<img
|
||||
className="vcd-user-assets-image"
|
||||
src={`vesktop://assets/${asset}?v=${version}`}
|
||||
alt=""
|
||||
style={{ imageRendering }}
|
||||
/>
|
||||
<div className="vcd-user-assets-actions">
|
||||
<Button onClick={onChooseAsset()}>Customize</Button>
|
||||
<Button color={Button.Colors.PRIMARY} onClick={onChooseAsset(null)}>
|
||||
Reset to default
|
||||
</Button>
|
||||
<div className="vcd-user-assets-buttons">
|
||||
<Button onClick={onChooseAsset()}>Customize</Button>
|
||||
<Button color={Button.Colors.PRIMARY} onClick={onChooseAsset(null)}>
|
||||
Reset to default
|
||||
</Button>
|
||||
</div>
|
||||
{isSplash && (
|
||||
<FormSwitch
|
||||
title="Nearest-Neighbor Scaling (for pixel art)"
|
||||
value={settings.splashPixelated ?? false}
|
||||
onChange={val => (settings.splashPixelated = val)}
|
||||
className={Margins.top16}
|
||||
hideBorder
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ErrorBoundary } from "@vencord/types/components";
|
||||
import { ErrorBoundary, 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 { SettingsComponent } from "./Settings";
|
||||
|
||||
@@ -16,10 +16,10 @@ export const WindowsTransparencyControls: SettingsComponent = ({ settings }) =>
|
||||
return (
|
||||
<ErrorBoundary noop>
|
||||
<div>
|
||||
<Forms.FormTitle className={Margins.bottom8}>Transparency Options</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom8}>
|
||||
<Heading>Transparency Options</Heading>
|
||||
<Paragraph className={Margins.bottom8}>
|
||||
Requires a full restart. You will need a theme that supports transparency for this to work.
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
|
||||
<Select
|
||||
placeholder="None"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { localStorage } from "./utils";
|
||||
import { localStorage } from "./constants";
|
||||
|
||||
// Make clicking Notifications focus the window
|
||||
const originalSetOnClick = Object.getOwnPropertyDescriptor(Notification.prototype, "onclick")!.set!;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { isMac } from "renderer/constants";
|
||||
import { Settings } from "renderer/settings";
|
||||
import { isMac } from "renderer/utils";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
import { Logger } from "@vencord/types/utils";
|
||||
import { currentSettings } from "renderer/components/ScreenSharePicker";
|
||||
import { isLinux } from "renderer/constants";
|
||||
import { State } from "renderer/settings";
|
||||
import { isLinux } from "renderer/utils";
|
||||
|
||||
const logger = new Logger("VesktopStreamFixes");
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
import { useEffect, useReducer } from "@vencord/types/webpack/common";
|
||||
import { SettingsStore } from "shared/utils/SettingsStore";
|
||||
|
||||
import { localStorage } from "./constants";
|
||||
import { VesktopLogger } from "./logger";
|
||||
import { localStorage } from "./utils";
|
||||
|
||||
export const Settings = new SettingsStore(VesktopNative.settings.get());
|
||||
Settings.addGlobalChangeListener((o, p) => VesktopNative.settings.set(o, p));
|
||||
|
||||
2
src/shared/settings.d.ts
vendored
2
src/shared/settings.d.ts
vendored
@@ -28,6 +28,7 @@ export interface Settings {
|
||||
splashTheming?: boolean;
|
||||
splashColor?: string;
|
||||
splashBackground?: string;
|
||||
splashPixelated?: boolean;
|
||||
|
||||
spellCheckLanguages?: string[];
|
||||
|
||||
@@ -50,7 +51,6 @@ export interface State {
|
||||
maximized?: boolean;
|
||||
minimized?: boolean;
|
||||
windowBounds?: Rectangle;
|
||||
displayId: number;
|
||||
|
||||
firstLaunch?: boolean;
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 145 KiB |
@@ -1,3 +1,6 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<head>
|
||||
<title>About Vesktop</title>
|
||||
|
||||
@@ -63,7 +66,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/electron-userland/electron-builder" target="_blank">Electron Builder</a>
|
||||
- A complete solution to package and build a ready for distribution Electron app with “auto update”
|
||||
- A complete solution to package and build a ready for distribution Electron app with "auto update"
|
||||
support out of the box
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -9,11 +9,15 @@
|
||||
--link-hover: light-dark(#005bb5, #0086c3);
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
"Open Sans", "Helvetica Neue", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<head>
|
||||
<title>Vesktop Setup</title>
|
||||
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="./common.css" type="text/css" />
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background: none;
|
||||
user-select: none;
|
||||
@@ -10,8 +19,9 @@
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<head>
|
||||
<title>Vesktop Updater</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="
|
||||
|
||||
Reference in New Issue
Block a user