diff --git a/.github/ISSUE_TEMPLATE/blank.yml b/.github/ISSUE_TEMPLATE/blank.yml new file mode 100644 index 0000000..2bed8eb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/blank.yml @@ -0,0 +1,18 @@ +name: Blank Issue +description: Reserved for developers. Use the bug report or feature request templates instead. + +body: + - type: markdown + attributes: + value: | + # READ THIS BEFORE OPENING AN ISSUE + + This form is only meant for Vesktop developers. If you don't know what you're doing, + please use the bug report or feature request templates instead. + + - type: textarea + id: content + attributes: + label: Content + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 08405e6..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - - - -**Describe the bug** - - - -**To Reproduce** - - - -**Expected behavior** - - - -**Screenshots** - - - -**Desktop (please complete the following information):** - - OS/Distro: [e.g. Windows / Fedora Linux / MacOs] - - Desktop Environment (linux only): [e.g. gnome, kde, sway] - - Version: [e.g. 22] - -**Command line output** - - - -``` -paste inside these backticks -``` - -**Additional context** - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..a9aec1f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,106 @@ +name: 🐛 Bug / Crash Report +description: Create a bug or crash report for Vesktop +labels: [bug] +title: "[Bug] " + +body: + - type: markdown + attributes: + value: | + **Thanks 🩷 for taking the time to fill out this bug report! Before proceeding, please read the following** + + Make sure a similar issue doesn't already exist by [searching the existing issues](https://github.com/Vencord/Vesktop/issues?q=is%3Aissue) for keywords! + + Make sure both Vesktop and Vencord are fully up to date. You can update Vencord by right-clicking the Vesktop tray icon and pressing "Update Vencord" + + Do not report any of the following issues: + - Purely graphical glitches like flickering, scaling issues, etc: Issue with your gpu. Nothing we can do, update drivers or disable hardware acceleration + - Vencord related issues: This is the Vesktop repo, not Vencord + - Screenshare not starting / black screening on Linux: Issue with your desktop environment, specifically its xdg-desktop-portal + + Linux users: Please only report issues with supported packages (flatpak and any builds from the README / releases). + We do not support other packages, like the AUR or Nix packages, so please first make sure your issue is reproducible with official releases, + like [our Flatpak](https://flathub.org/apps/dev.vencord.Vesktop) or [AppImage](https://vencord.dev/download/vesktop/amd64/appimage) + + - type: input + id: discord + attributes: + label: Discord Account + description: Who on Discord is making this request? Not required but encouraged for easier follow-up + placeholder: username#0000 + validations: + required: false + + - type: input + id: os + attributes: + label: Operating System + description: What operating system are you using (eg Windows 10, macOS Big Sur, Ubuntu 20.04)? + placeholder: Windows 10 + validations: + required: true + + - type: input + id: linux-de + attributes: + label: Linux Only ~ Desktop Environment + description: If you are on Linux, what Desktop environment are you using (eg GNOME, KDE, XFCE)? Are you using Wayland or Xorg? + placeholder: Gnome on Wayland + validations: + required: false + + - type: textarea + id: bug-description + attributes: + label: What happens when the bug or crash occurs? + description: Where does this bug or crash occur, when does it occur, etc. + placeholder: The bug/crash happens sometimes when I do ..., causing this to not work/the app to crash. I think it happens because of ... + validations: + required: true + + - type: textarea + id: expected-behaviour + attributes: + label: What is the expected behaviour? + description: Simply detail what the expected behaviour is. + placeholder: I expect Vencord/Discord to open the ... page instead of ..., it prevents me from doing ... + validations: + required: true + + - type: textarea + id: steps-to-take + attributes: + label: How do you recreate this bug or crash? + description: Give us a list of steps in order to recreate the bug or crash. + placeholder: | + 1. Do ... + 2. Then ... + 3. Do this ..., ... and then ... + 4. Observe "the bug" or "the crash" + validations: + required: true + + - type: textarea + id: debug-logs + attributes: + label: Debug Logs + description: Run vesktop from the command line. Include the relevant command line output here + value: | + ``` + Replace this text with your crash-log. Do not remove the backticks + ``` + validations: + required: true + + - type: checkboxes + id: agreement-check + attributes: + label: Request Agreement + description: We only accept reports for bugs that happen on supported and up to date Vesktop releases + options: + - label: I have searched the existing issues and found no similar issue + required: true + - label: I am using the latest Vesktop and Vencord versions + required: true + - label: This issue occurs on an official release (not just the AUR or Nix packages) + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..807b463 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Vencord Support Server + url: https://discord.gg/D9uwnFnqmd + about: If you need help regarding Vesktop or Vencord, please join our support server! diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md deleted file mode 100644 index 48d5f81..0000000 --- a/.github/ISSUE_TEMPLATE/custom.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Custom issue template -about: Describe this issue template's purpose here. -title: '' -labels: '' -assignees: '' - ---- - - diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..ab2db83 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,72 @@ +name: 🛠️ Feature Request +description: Create a feature request for Vesktop +labels: [bug] +title: "[Bug] <title>" + +body: + - type: markdown + attributes: + value: | + **Thanks 🩷 for taking the time to fill out this request! Before proceeding, please read the following** + + Make sure a similar request doesn't already exist by [searching the existing issues](https://github.com/Vencord/Vesktop/issues?q=is%3Aissue) for keywords! + + This form is only meant for **Vesktop feature requests**. + For plugin requests or Vencord feature requests, go [here](https://github.com/Vencord/plugin-requests/issues/new?template=request.yml) instead! + + - type: input + id: discord + attributes: + label: Discord Account + description: Who on Discord is making this request? Not required but encouraged for easier follow-up + placeholder: username#0000 + validations: + required: false + + - type: textarea + id: motivation + attributes: + label: Motivation + description: If your feature request related to a problem? Please describe + placeholder: I'm always frustrated when ..., I think it would be better if ... + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Solution + description: Describe the solution you'd like + placeholder: A clear and concise description of what you want to happen. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives + description: Describe alternatives you've considered + placeholder: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true + + - type: textarea + id: additional-context + attributes: + label: Additional context + description: Add any other context here. Screenshots or mockups could help greatly + validations: + required: false + + - type: checkboxes + id: agreement-check + attributes: + label: Request Agreement + description: This form is only for Vesktop feature requests. If the following don't apply, re-read the introduction text + options: + - label: I have searched the existing issues and found no similar issue + required: true + - label: This is not a plugin request + required: true + - label: This is not a Vencord feature request + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 11fc491..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 80de47d..6968b73 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,7 @@ on: push: tags: - v* + workflow_dispatch: jobs: release: diff --git a/README.md b/README.md index af498a8..8ea22d2 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ If you don't know the difference, pick the Installer. ### Mac -If you don't know the difference, pick amd64 +If you don't know the difference, pick the Intel build. -- [amd64 / x86_64](https://vencord.dev/download/vesktop/amd64/dmg) -- [arm64 / aarch64](https://vencord.dev/download/vesktop/arm64/dmg) +- [Intel build (amd64)](https://vencord.dev/download/vesktop/amd64/dmg) +- [Apple Silicon (arm64)](https://vencord.dev/download/vesktop/arm64/dmg) ### Linux @@ -53,7 +53,7 @@ If you don't know the difference, pick amd64. Below you can find unofficial packages created by the community. They are not officially supported by us, so before reporting issues, please first confirm the issue also happens on official builds. When in doubt, consult with their packager first. The flatpak and AppImage should work on any distro that [supports them](https://flatpak.org/setup/), so I recommend you just use those instead! - Arch Linux: [Vesktop on the Arch user repository](https://aur.archlinux.org/packages?K=vesktop) -- NixOS: https://nixos.wiki/wiki/Discord#Vesktop +- NixOS: https://wiki.nixos.org/wiki/Discord#Vesktop - Windows - Scoop: https://scoop.sh/#/apps?q=Vesktop ## Building from Source diff --git a/build/background.tiff b/build/background.tiff new file mode 100644 index 0000000..a0e3f7f Binary files /dev/null and b/build/background.tiff differ diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist new file mode 100644 index 0000000..f644a36 --- /dev/null +++ b/build/entitlements.mac.plist @@ -0,0 +1,21 @@ +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>com.apple.security.cs.allow-unsigned-executable-memory</key> + <true/> + <key>com.apple.security.cs.allow-jit</key> + <true/> + <key>com.apple.security.network.client</key> + <true/> + <key>com.apple.security.device.audio-input</key> + <true/> + <key>com.apple.security.device.camera</key> + <true/> + <key>com.apple.security.device.bluetooth</key> + <true/> + <key>com.apple.security.cs.allow-dyld-environment-variables</key> + <true/> + <key>com.apple.security.cs.disable-library-validation</key> + <true/> + </dict> +</plist> diff --git a/meta/dev.vencord.Vesktop.metainfo.xml b/meta/dev.vencord.Vesktop.metainfo.xml index 3539878..889b9fc 100644 --- a/meta/dev.vencord.Vesktop.metainfo.xml +++ b/meta/dev.vencord.Vesktop.metainfo.xml @@ -28,6 +28,20 @@ </screenshot> </screenshots> <releases> + <release version="1.5.2" date="2024-05-01" type="stable"> + <url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.2</url> + <description> + <p>What's Changed</p> + <ul> + <li>Fixed scrollbars looking wrong (actually Discord's fault)</li> + <li>Tray: Added left click hide/show feature by @0bCdian</li> + <li>MacOS: Fixed the app not properly requesting microphone permissions by @ssalggnikool</li> + <li>Linux: Various fixed related to audio screenshare by @Curve</li> + <li>Linux: Overhauled & improved screenshare with better framerate by @kaitlynkittyy</li> + <li>Users can now pass --enable/disable-features command line flags by @takase1121</li> + </ul> + </description> + </release> <release version="1.5.1" date="2024-03-12" type="stable"> <url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.1</url> <description> diff --git a/package.json b/package.json index 462562a..d5855d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vesktop", - "version": "1.5.1", + "version": "1.5.2", "private": true, "description": "", "keywords": [], @@ -24,10 +24,10 @@ "updateMeta": "tsx scripts/utils/updateMeta.mts" }, "dependencies": { - "arrpc": "github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8" + "arrpc": "github:OpenAsar/arrpc#6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c" }, "optionalDependencies": { - "@vencord/venmic": "^3.3.2" + "@vencord/venmic": "^3.4.2" }, "devDependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", @@ -57,7 +57,7 @@ "typescript": "^5.4.2", "xml-formatter": "^3.6.2" }, - "packageManager": "pnpm@8.11.0", + "packageManager": "pnpm@9.1.0", "engines": { "node": ">=18", "pnpm": ">=8" @@ -133,6 +133,26 @@ "com.apple.security.device.camera": true } }, + "dmg": { + "background": "build/background.tiff", + "icon": "build/icon.icns", + "iconSize": 105, + "window": { + "width": 512, + "height": 340 + }, + + "contents": [{ + "x": 140, + "y": 160 + }, + { + "x": 372, + "y": 160, + "type": "link", + "path": "/Applications" + }] + }, "nsis": { "include": "build/installer.nsh", "oneClick": false diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fea7224..21590d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,13 +6,13 @@ settings: dependencies: arrpc: - specifier: github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8 - version: github.com/OpenAsar/arrpc/98879cae0565e6fce34e4cb6f544bf42c6a7e7c8 + specifier: github:OpenAsar/arrpc#6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c + version: github.com/OpenAsar/arrpc/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c optionalDependencies: '@vencord/venmic': - specifier: ^3.3.2 - version: 3.3.2 + specifier: ^3.4.2 + version: 3.4.2 devDependencies: '@fal-works/esbuild-plugin-global-externals': @@ -1223,14 +1223,14 @@ packages: type-fest: 3.13.1 dev: true - /@vencord/venmic@3.3.2: - resolution: {integrity: sha512-fwGr5v+Xe4EisKxxhTDlUDamBGlBzSK3+yYZH/7d9ui1j8Q78wCbE1iP8MnDjBvV2kIKn/xV/84wjt9iQvDkFw==} + /@vencord/venmic@3.4.2: + resolution: {integrity: sha512-nwGjarof1wVvecGksGONfb+PduhY4gSuHTm39LktiQayHS69+yv4lepq4k1lxZzjOgFTy5VXfmJqxt+BpqoUUQ==} engines: {node: '>=14.15'} os: [linux] requiresBuild: true dependencies: cmake-js: 7.3.0 - node-addon-api: 7.1.0 + node-addon-api: 8.0.0 pkg-prebuilds: 0.2.1 transitivePeerDependencies: - supports-color @@ -1556,11 +1556,11 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /axios@1.6.7(debug@4.3.4): - resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==} + /axios@1.6.8(debug@4.3.4): + resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} requiresBuild: true dependencies: - follow-redirects: 1.15.5(debug@4.3.4) + follow-redirects: 1.15.6(debug@4.3.4) form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -1799,7 +1799,7 @@ packages: hasBin: true requiresBuild: true dependencies: - axios: 1.6.7(debug@4.3.4) + axios: 1.6.8(debug@4.3.4) debug: 4.3.4 fs-extra: 11.2.0 lodash.isplainobject: 4.0.6 @@ -2800,8 +2800,8 @@ packages: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true - /follow-redirects@1.15.5(debug@4.3.4): - resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==} + /follow-redirects@1.15.6(debug@4.3.4): + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} requiresBuild: true peerDependencies: @@ -3862,9 +3862,9 @@ packages: dev: true optional: true - /node-addon-api@7.1.0: - resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} - engines: {node: ^16 || ^18 || >= 20} + /node-addon-api@8.0.0: + resolution: {integrity: sha512-ipO7rsHEBqa9STO5C5T10fj732ml+5kLN1cAG8/jdHd56ldQeGj3Q7+scUS+VHK/qy1zLEwC4wMK5+yM0btPvw==} + engines: {node: ^18 || ^20 || >= 21} requiresBuild: true dev: false optional: true @@ -5112,10 +5112,10 @@ packages: readable-stream: 3.6.2 dev: true - github.com/OpenAsar/arrpc/98879cae0565e6fce34e4cb6f544bf42c6a7e7c8: - resolution: {tarball: https://codeload.github.com/OpenAsar/arrpc/tar.gz/98879cae0565e6fce34e4cb6f544bf42c6a7e7c8} + github.com/OpenAsar/arrpc/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c: + resolution: {tarball: https://codeload.github.com/OpenAsar/arrpc/tar.gz/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c} name: arrpc - version: 3.2.0 + version: 3.3.1 hasBin: true dependencies: ws: 8.13.0 diff --git a/src/main/index.ts b/src/main/index.ts index 9d6a52c..2f8191c 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -27,23 +27,35 @@ process.env.VENCORD_USER_DATA_DIR = DATA_DIR; function init() { const { disableSmoothScroll, hardwareAcceleration } = Settings.store; - if (hardwareAcceleration === false) app.disableHardwareAcceleration(); + const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(","); + const disabledFeatures = app.commandLine.getSwitchValue("disable-features").split(","); + + if (hardwareAcceleration === false) { + app.disableHardwareAcceleration(); + } else { + enabledFeatures.push("VaapiVideoDecodeLinuxGL", "VaapiVideoEncoder", "VaapiVideoDecoder"); + } + if (disableSmoothScroll) { app.commandLine.appendSwitch("disable-smooth-scrolling"); } // work around chrome 66 disabling autoplay by default app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required"); - // WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows. // HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service. // // WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790 - app.commandLine.appendSwitch( - "disable-features", - "WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService,WidgetLayering" + disabledFeatures.push( + "WinRetrieveSuggestionsOnlyOnDemand", + "HardwareMediaKeyHandling", + "MediaSessionService", + "WidgetLayering" ); + app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].filter(Boolean).join(",")); + app.commandLine.appendSwitch("disable-features", [...new Set(disabledFeatures)].filter(Boolean).join(",")); + // In the Flatpak on SteamOS the theme is detected as light, but SteamOS only has a dark mode, so we just override it if (isDeckGameMode) nativeTheme.themeSource = "dark"; diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index ddc9b58..606b741 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -12,6 +12,7 @@ import { Menu, MenuItemConstructorOptions, nativeTheme, + screen, Tray } from "electron"; import { rm } from "fs/promises"; @@ -73,6 +74,10 @@ const [addSettingsListener, removeSettingsListeners] = makeSettingsListenerHelpe const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings); function initTray(win: BrowserWindow) { + const onTrayClick = () => { + if (Settings.store.clickTrayToShowHide && win.isVisible()) win.hide(); + else win.show(); + }; const trayMenu = Menu.buildFromTemplate([ { label: "Open", @@ -120,7 +125,7 @@ function initTray(win: BrowserWindow) { tray = new Tray(TRAY_ICON_PATH); tray.setToolTip("Vesktop"); tray.setContextMenu(trayMenu); - tray.on("click", () => win.show()); + tray.on("click", onTrayClick); } async function clearData(win: BrowserWindow) { @@ -265,7 +270,9 @@ function getWindowBoundsOptions(): BrowserWindowConstructorOptions { height: height ?? DEFAULT_HEIGHT } as BrowserWindowConstructorOptions; - if (x != null && y != null) { + const storedDisplay = screen.getAllDisplays().find(display => display.id === State.store.displayid); + + if (x != null && y != null && storedDisplay) { options.x = x; options.y = y; } @@ -313,6 +320,7 @@ function initWindowBoundsListeners(win: BrowserWindow) { const saveBounds = () => { State.store.windowBounds = win.getBounds(); + State.store.displayid = screen.getDisplayMatching(State.store.windowBounds).id; }; win.on("resize", saveBounds); diff --git a/src/main/venmic.ts b/src/main/venmic.ts index c6f9d44..404c0d2 100644 --- a/src/main/venmic.ts +++ b/src/main/venmic.ts @@ -4,17 +4,58 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ -import type { PatchBay } from "@vencord/venmic"; +import type { PatchBay as PatchBayType } from "@vencord/venmic"; import { app, ipcMain } from "electron"; import { join } from "path"; import { IpcEvents } from "shared/IpcEvents"; import { STATIC_DIR } from "shared/paths"; -type LinkData = Parameters<PatchBay["link"]>[0]; +type LinkData = Parameters<PatchBayType["link"]>[0]; +let PatchBay: typeof PatchBayType | undefined; +let patchBayInstance: PatchBayType | undefined; + +let imported = false; let initialized = false; -let patchBay: import("@vencord/venmic").PatchBay | undefined; -let isGlibcxxToOld = false; + +let hasPipewirePulse = false; +let isGlibCxxOutdated = false; + +function importVenmic() { + if (imported) { + return; + } + + imported = true; + + try { + PatchBay = (require(join(STATIC_DIR, `dist/venmic-${process.arch}.node`)) as typeof import("@vencord/venmic")) + .PatchBay; + + hasPipewirePulse = PatchBay.hasPipeWire(); + } catch (e: any) { + console.error("Failed to import venmic", e); + isGlibCxxOutdated = (e?.stack || e?.message || "").toLowerCase().includes("glibc"); + } +} + +function obtainVenmic() { + if (!imported) { + importVenmic(); + } + + if (PatchBay && !initialized) { + initialized = true; + + try { + patchBayInstance = new PatchBay(); + } catch (e: any) { + console.error("Failed to instantiate venmic", e); + } + } + + return patchBayInstance; +} function getRendererAudioServicePid() { return ( @@ -25,33 +66,17 @@ function getRendererAudioServicePid() { ); } -function obtainVenmic() { - if (!initialized) { - initialized = true; - try { - const { PatchBay } = require( - join(STATIC_DIR, `dist/venmic-${process.arch}.node`) - ) as typeof import("@vencord/venmic"); - patchBay = new PatchBay(); - } catch (e: any) { - console.error("Failed to initialise venmic. Make sure you're using pipewire", e); - isGlibcxxToOld = (e?.stack || e?.message || "").toLowerCase().includes("glibc"); - } - } - - return patchBay; -} - ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => { const audioPid = getRendererAudioServicePid(); + const list = obtainVenmic() ?.list() .filter(s => s["application.process.id"] !== audioPid) .map(s => s["application.name"]); - return list - ? { ok: true, targets: [...new Set(list)] } // Remove duplicates - : { ok: false, isGlibcxxToOld }; + const uniqueTargets = [...new Set(list)]; + + return list ? { ok: true, targets: uniqueTargets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated }; }); ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boolean) => { @@ -72,11 +97,12 @@ ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boo return obtainVenmic()?.link(data); }); -ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean) => { +ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean, onlyDefaultSpeakers?: boolean) => { const pid = getRendererAudioServicePid(); const data: LinkData = { - exclude: [{ key: "application.process.id", value: pid }] + exclude: [{ key: "application.process.id", value: pid }], + only_default_speakers: onlyDefaultSpeakers }; if (workaround) { diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index 625b8c1..184b095 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -62,9 +62,12 @@ export const VesktopNative = { /** only available on Linux. */ virtmic: { list: () => - invoke<{ ok: false; isGlibcxxToOld: boolean } | { ok: true; targets: string[] }>(IpcEvents.VIRT_MIC_LIST), + invoke< + { ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: string[]; hasPipewirePulse: boolean } + >(IpcEvents.VIRT_MIC_LIST), start: (targets: string[], workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START, targets, workaround), - startSystem: (workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround), + startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) => + invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers), stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP) }, arrpc: { diff --git a/src/renderer/components/ScreenSharePicker.tsx b/src/renderer/components/ScreenSharePicker.tsx index 9842afe..ac9fc0b 100644 --- a/src/renderer/components/ScreenSharePicker.tsx +++ b/src/renderer/components/ScreenSharePicker.tsx @@ -6,7 +6,7 @@ import "./screenSharePicker.css"; -import { closeModal, Margins, Modals, openModal, useAwaiter } from "@vencord/types/utils"; +import { closeModal, Logger, Margins, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils"; import { findStoreLazy, onceReady } from "@vencord/types/webpack"; import { Button, @@ -36,7 +36,9 @@ interface StreamSettings { fps: StreamFps; audio: boolean; audioSource?: string; + contentHint?: string; workaround?: boolean; + onlyDefaultSpeakers?: boolean; } export interface StreamPick extends StreamSettings { @@ -49,7 +51,9 @@ interface Source { url: string; } -let currentSettings: StreamSettings | null = null; +export let currentSettings: StreamSettings | null = null; + +const logger = new Logger("VesktopScreenShare"); addPatch({ patches: [ @@ -73,6 +77,14 @@ addPatch({ bitrateMax: 8000000, bitrateTarget: 600000 }); + if (opts?.encode) { + Object.assign(opts.encode, { + framerate, + width, + height, + pixelCount: height * width + }); + } Object.assign(opts.capture, { framerate, width, @@ -167,74 +179,127 @@ function StreamSettings({ ); return ( - <div> - <Forms.FormTitle>What you're streaming</Forms.FormTitle> - <Card className="vcd-screen-picker-card vcd-screen-picker-preview"> - <img src={thumb} alt="" /> - <Text variant="text-sm/normal">{source.name}</Text> - </Card> + <div className={isLinux ? "vcd-screen-picker-settings-grid" : ""}> + <div> + <Forms.FormTitle>What you're streaming</Forms.FormTitle> + <Card className="vcd-screen-picker-card vcd-screen-picker-preview"> + <img + src={thumb} + alt="" + className={isLinux ? "vcd-screen-picker-preview-img-linux" : "vcd-screen-picker-preview-img"} + /> + <Text variant="text-sm/normal">{source.name}</Text> + </Card> - <Forms.FormTitle>Stream Settings</Forms.FormTitle> + <Forms.FormTitle>Stream Settings</Forms.FormTitle> - <Card className="vcd-screen-picker-card"> - <div className="vcd-screen-picker-quality"> - <section> - <Forms.FormTitle>Resolution</Forms.FormTitle> - <div className="vcd-screen-picker-radios"> - {StreamResolutions.map(res => ( - <label className="vcd-screen-picker-radio" data-checked={settings.resolution === res}> - <Text variant="text-sm/bold">{res}</Text> - <input - type="radio" - name="resolution" - value={res} - checked={settings.resolution === res} - onChange={() => setSettings(s => ({ ...s, resolution: res }))} - /> - </label> - ))} - </div> - </section> + <Card className="vcd-screen-picker-card"> + <div className="vcd-screen-picker-quality"> + <section> + <Forms.FormTitle>Resolution</Forms.FormTitle> + <div className="vcd-screen-picker-radios"> + {StreamResolutions.map(res => ( + <label + className="vcd-screen-picker-radio" + data-checked={settings.resolution === res} + > + <Text variant="text-sm/bold">{res}</Text> + <input + type="radio" + name="resolution" + value={res} + checked={settings.resolution === res} + onChange={() => setSettings(s => ({ ...s, resolution: res }))} + /> + </label> + ))} + </div> + </section> - <section> - <Forms.FormTitle>Frame Rate</Forms.FormTitle> - <div className="vcd-screen-picker-radios"> - {StreamFps.map(fps => ( - <label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}> - <Text variant="text-sm/bold">{fps}</Text> - <input - type="radio" - name="fps" - value={fps} - checked={settings.fps === fps} - onChange={() => setSettings(s => ({ ...s, fps }))} - /> - </label> - ))} - </div> - </section> - </div> - - {isWindows && ( - <Switch - value={settings.audio} - onChange={checked => setSettings(s => ({ ...s, audio: checked }))} - hideBorder - className="vcd-screen-picker-audio" - > - Stream With Audio - </Switch> - )} + <section> + <Forms.FormTitle>Frame Rate</Forms.FormTitle> + <div className="vcd-screen-picker-radios"> + {StreamFps.map(fps => ( + <label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}> + <Text variant="text-sm/bold">{fps}</Text> + <input + type="radio" + name="fps" + value={fps} + checked={settings.fps === fps} + onChange={() => setSettings(s => ({ ...s, fps }))} + /> + </label> + ))} + </div> + </section> + </div> + <div className="vcd-screen-picker-quality"> + <section> + <Forms.FormTitle>Content Type</Forms.FormTitle> + <div> + <div className="vcd-screen-picker-radios"> + <label + className="vcd-screen-picker-radio" + data-checked={settings.contentHint === "motion"} + > + <Text variant="text-sm/bold">Prefer Smoothness</Text> + <input + type="radio" + name="contenthint" + value="motion" + checked={settings.contentHint === "motion"} + onChange={() => setSettings(s => ({ ...s, contentHint: "motion" }))} + /> + </label> + <label + className="vcd-screen-picker-radio" + data-checked={settings.contentHint === "detail"} + > + <Text variant="text-sm/bold">Prefer Clarity</Text> + <input + type="radio" + name="contenthint" + value="detail" + checked={settings.contentHint === "detail"} + onChange={() => setSettings(s => ({ ...s, contentHint: "detail" }))} + /> + </label> + </div> + <div className="vcd-screen-picker-hint-description"> + <p> + Choosing "Prefer Clarity" will result in a significantly lower framerate in + exchange for a much sharper and clearer image. + </p> + </div> + </div> + {isWindows && ( + <Switch + value={settings.audio} + onChange={checked => setSettings(s => ({ ...s, audio: checked }))} + hideBorder + className="vcd-screen-picker-audio" + > + Stream With Audio + </Switch> + )} + </section> + </div> + </Card> + </div> + <div> {isLinux && ( <AudioSourcePickerLinux audioSource={settings.audioSource} workaround={settings.workaround} + onlyDefaultSpeakers={settings.onlyDefaultSpeakers} setAudioSource={source => setSettings(s => ({ ...s, audioSource: source }))} - setWorkaround={workaround => setSettings(s => ({ ...s, workaround: workaround }))} + setWorkaround={value => setSettings(s => ({ ...s, workaround: value }))} + setOnlyDefaultSpeakers={value => setSettings(s => ({ ...s, onlyDefaultSpeakers: value }))} /> )} - </Card> + </div> </div> ); } @@ -242,63 +307,102 @@ function StreamSettings({ function AudioSourcePickerLinux({ audioSource, workaround, + onlyDefaultSpeakers, setAudioSource, - setWorkaround + setWorkaround, + setOnlyDefaultSpeakers }: { audioSource?: string; workaround?: boolean; + onlyDefaultSpeakers?: boolean; setAudioSource(s: string): void; setWorkaround(b: boolean): void; + setOnlyDefaultSpeakers(b: boolean): void; }) { const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), { - fallbackValue: { ok: true, targets: [] } + fallbackValue: { ok: true, targets: [], hasPipewirePulse: true } }); + const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null; + const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true; + + const [ignorePulseWarning, setIgnorePulseWarning] = useState(false); return ( - <section> - <Forms.FormTitle>Audio</Forms.FormTitle> - {loading && <Forms.FormTitle>Loading Audio sources...</Forms.FormTitle>} - {!sources.ok && - (sources.isGlibcxxToOld ? ( + <> + <Forms.FormTitle>Audio Settings</Forms.FormTitle> + <Card className="vcd-screen-picker-card"> + {loading ? ( + <Forms.FormTitle>Loading Audio Sources...</Forms.FormTitle> + ) : ( + <Forms.FormTitle>Audio Source</Forms.FormTitle> + )} + + {!sources.ok && sources.isGlibCxxOutdated && ( <Forms.FormText> - Failed to retrieve Audio Sources because your C++ library is too old to run venmic. If you would - like to stream with Audio, see{" "} + Failed to retrieve Audio Sources because your C++ library is too old to run + <a href="https://github.com/Vencord/venmic" target="_blank"> + venmic + </a> + . See{" "} <a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank"> this guide - </a> + </a>{" "} + for possible solutions. </Forms.FormText> + )} + + {hasPipewirePulse || ignorePulseWarning ? ( + allSources && ( + <Select + options={allSources.map(s => ({ label: s, value: s, default: s === "None" }))} + isSelected={s => s === audioSource} + select={setAudioSource} + serialize={String} + /> + ) ) : ( - <Forms.FormText> - Failed to retrieve Audio Sources. If you would like to stream with Audio, make sure you're using - Pipewire, not Pulseaudio - </Forms.FormText> - ))} + <Text variant="text-sm/normal"> + Could not find pipewire-pulse. This usually means that you do not run pipewire as your main + audio-server. <br /> + You can still continue, however, please beware that you can only share audio of apps that are + running under pipewire. + <br /> + <a onClick={() => setIgnorePulseWarning(true)}>I know what I'm doing</a> + </Text> + )} - {allSources && ( - <Select - options={allSources.map(s => ({ label: s, value: s, default: s === "None" }))} - isSelected={s => s === audioSource} - select={setAudioSource} - serialize={String} - /> - )} + <Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} /> - <Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} /> + <Switch + onChange={setWorkaround} + value={workaround ?? false} + note={ + <> + Work around an issue that causes the microphone to be shared instead of the correct audio. + Only enable if you're experiencing this issue. + </> + } + > + Microphone Workaround + </Switch> - <Switch - onChange={setWorkaround} - value={workaround ?? false} - note={ - <> - Work around an issue that causes the microphone to be shared instead of the correct audio. Only - enable if you're experiencing this issue. - </> - } - > - Microphone Workaround - </Switch> - </section> + <Switch + hideBorder + onChange={setOnlyDefaultSpeakers} + disabled={audioSource !== "Entire System"} + value={onlyDefaultSpeakers ?? true} + note={ + <> + When sharing entire desktop audio, only share apps that play to the default speakers and + ignore apps that play to other speakers or devices. + </> + } + > + Only Default Speakers + </Switch> + </Card> + </> ); } @@ -319,16 +423,16 @@ function ModalComponent({ const [settings, setSettings] = useState<StreamSettings>({ resolution: "1080", fps: "60", + contentHint: "motion", audio: true }); return ( - <Modals.ModalRoot {...modalProps}> + <Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}> <Modals.ModalHeader className="vcd-screen-picker-header"> <Forms.FormTitle tag="h2">ScreenShare</Forms.FormTitle> <Modals.ModalCloseButton onClick={close} /> </Modals.ModalHeader> - <Modals.ModalContent className="vcd-screen-picker-modal"> {!selected ? ( <ScreenPicker screens={screens} chooseScreen={setSelected} /> @@ -341,35 +445,62 @@ function ModalComponent({ /> )} </Modals.ModalContent> - <Modals.ModalFooter className="vcd-screen-picker-footer"> <Button disabled={!selected} onClick={() => { currentSettings = settings; - - // If there are 2 connections, the second one is the existing stream. - // In that case, we patch its quality - const conn = [...MediaEngineStore.getMediaEngine().connections][1]; - if (conn && conn.videoStreamParameters.length > 0) { + try { + const frameRate = Number(settings.fps); const height = Number(settings.resolution); const width = Math.round(height * (16 / 9)); - Object.assign(conn.videoStreamParameters[0], { - maxFrameRate: Number(settings.fps), - maxPixelCount: width * height, - maxBitrate: 8000000, - maxResolution: { - type: "fixed", - width, - height - } - }); - } - submit({ - id: selected!, - ...settings - }); + const conn = [...MediaEngineStore.getMediaEngine().connections].find( + connection => connection.streamUserId === UserStore.getCurrentUser().id + ); + + if (conn) { + conn.videoStreamParameters[0].maxFrameRate = frameRate; + conn.videoStreamParameters[0].maxResolution.height = height; + conn.videoStreamParameters[0].maxResolution.width = width; + } + + submit({ + id: selected!, + ...settings + }); + + setTimeout(async () => { + const conn = [...MediaEngineStore.getMediaEngine().connections].find( + connection => connection.streamUserId === UserStore.getCurrentUser().id + ); + if (!conn) return; + + const track = conn.input.stream.getVideoTracks()[0]; + + const constraints = { + ...track.getConstraints(), + frameRate, + width: { min: 640, ideal: width, max: width }, + height: { min: 480, ideal: height, max: height }, + advanced: [{ width: width, height: height }], + resizeMode: "none" + }; + + try { + await track.applyConstraints(constraints); + + logger.info( + "Applied constraints successfully. New constraints:", + track.getConstraints() + ); + } catch (e) { + logger.error("Failed to apply constraints.", e); + } + }, 100); + } catch (error) { + logger.error("Error while submitting stream.", error); + } close(); }} diff --git a/src/renderer/components/screenSharePicker.css b/src/renderer/components/screenSharePicker.css index 356a1ca..c2586a1 100644 --- a/src/renderer/components/screenSharePicker.css +++ b/src/renderer/components/screenSharePicker.css @@ -11,6 +11,21 @@ gap: 1em; } +.vcd-screen-picker-settings-grid { + gap: 1em; + display: grid; + grid-template-columns: 1fr 1fr; +} + +.vcd-screen-picker-settings-grid>div { + display: flex; + flex-direction: column; +} + +.vcd-screen-picker-card { + flex-grow: 1; +} + .vcd-screen-picker-grid { display: grid; grid-template-columns: 1fr 1fr; @@ -52,11 +67,16 @@ box-sizing: border-box; } -.vcd-screen-picker-preview img { +.vcd-screen-picker-preview-img-linux { width: 100%; margin-bottom: 0.5em; } +.vcd-screen-picker-preview-img { + width: 90%; + margin-bottom: 0.5em; +} + .vcd-screen-picker-preview { display: flex; flex-direction: column; @@ -122,3 +142,10 @@ .vcd-screen-picker-audio { margin-bottom: 0; } + +.vcd-screen-picker-hint-description { + color: var(--header-secondary); + font-size: 14px; + line-height: 20px; + font-weight: 400; +} \ No newline at end of file diff --git a/src/renderer/components/settings/Settings.tsx b/src/renderer/components/settings/Settings.tsx index 342804c..52498c2 100644 --- a/src/renderer/components/settings/Settings.tsx +++ b/src/renderer/components/settings/Settings.tsx @@ -84,6 +84,12 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>> invisible: () => isMac, disabled: () => Settings.store.tray === false }, + { + key: "clickTrayToShowHide", + title: "Hide/Show on tray click", + description: "Left clicking tray icon will toggle the vesktop window visibility.", + defaultValue: false + }, { key: "disableMinSize", title: "Disable minimum window size", diff --git a/src/renderer/hideGarbage.css b/src/renderer/fixes.css similarity index 57% rename from src/renderer/hideGarbage.css rename to src/renderer/fixes.css index 2020651..aeec1bf 100644 --- a/src/renderer/hideGarbage.css +++ b/src/renderer/fixes.css @@ -3,3 +3,9 @@ [class^=listItem_]:has(+ [class^=listItem_] [data-list-item-id=guildsnav___app-download-button]) { display: none; } + +/* FIXME: remove this once Discord fixes their css to not explode scrollbars on chromium >=121 */ +* { + scrollbar-width: unset !important; + scrollbar-color: unset !important; +} \ No newline at end of file diff --git a/src/renderer/fixes.ts b/src/renderer/fixes.ts index 2524023..2758b5c 100644 --- a/src/renderer/fixes.ts +++ b/src/renderer/fixes.ts @@ -4,7 +4,7 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ -import "./hideGarbage.css"; +import "./fixes.css"; import { isWindows, localStorage } from "./utils"; diff --git a/src/renderer/patches/hideSwitchDevice.tsx b/src/renderer/patches/hideSwitchDevice.tsx new file mode 100644 index 0000000..911aed7 --- /dev/null +++ b/src/renderer/patches/hideSwitchDevice.tsx @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2023 Vendicated and Vencord contributors + */ + +import { addPatch } from "./shared"; + +addPatch({ + patches: [ + { + find: "lastOutputSystemDevice.justChanged", + replacement: { + // eslint-disable-next-line no-useless-escape + match: /(\i)\.default\.getState\(\).neverShowModal/, + replace: "$& || $self.shouldIgnore($1)" + } + } + ], + + shouldIgnore(state: any) { + return Object.keys(state?.default?.lastDeviceConnected ?? {})?.[0] === "vencord-screen-share"; + } +}); diff --git a/src/renderer/patches/hideVenmicInput.tsx b/src/renderer/patches/hideVenmicInput.tsx new file mode 100644 index 0000000..ca706ce --- /dev/null +++ b/src/renderer/patches/hideVenmicInput.tsx @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2023 Vendicated and Vencord contributors + */ + +import { addPatch } from "./shared"; + +addPatch({ + patches: [ + { + find: 'setSinkId"in', + replacement: { + // eslint-disable-next-line no-useless-escape + match: /return (\i)\?navigator\.mediaDevices\.enumerateDevices/, + replace: "return $1 ? $self.filteredDevices" + } + } + ], + + async filteredDevices() { + const original = await navigator.mediaDevices.enumerateDevices(); + return original.filter(x => x.label !== "vencord-screen-share"); + } +}); diff --git a/src/renderer/patches/index.ts b/src/renderer/patches/index.ts index 7d4c4b3..aabff3e 100644 --- a/src/renderer/patches/index.ts +++ b/src/renderer/patches/index.ts @@ -7,6 +7,8 @@ // TODO: Possibly auto generate glob if we have more patches in the future import "./enableNotificationsByDefault"; import "./platformClass"; -import "./screenShareAudio"; +import "./hideSwitchDevice"; +import "./hideVenmicInput"; +import "./screenShareFixes"; import "./spellCheck"; import "./windowsTitleBar"; diff --git a/src/renderer/patches/screenShareAudio.ts b/src/renderer/patches/screenShareFixes.ts similarity index 54% rename from src/renderer/patches/screenShareAudio.ts rename to src/renderer/patches/screenShareFixes.ts index 3b926c3..66e4b14 100644 --- a/src/renderer/patches/screenShareAudio.ts +++ b/src/renderer/patches/screenShareFixes.ts @@ -4,8 +4,12 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ +import { Logger } from "@vencord/types/utils"; +import { currentSettings } from "renderer/components/ScreenSharePicker"; import { isLinux } from "renderer/utils"; +const logger = new Logger("VesktopStreamFixes"); + if (isLinux) { const original = navigator.mediaDevices.getDisplayMedia; @@ -23,6 +27,29 @@ if (isLinux) { const stream = await original.call(this, opts); const id = await getVirtmic(); + const frameRate = Number(currentSettings?.fps); + const height = Number(currentSettings?.resolution); + const width = Math.round(height * (16 / 9)); + const track = stream.getVideoTracks()[0]; + + track.contentHint = String(currentSettings?.contentHint); + + const constraints = { + ...track.getConstraints(), + frameRate, + width: { min: 640, ideal: width, max: width }, + height: { min: 480, ideal: height, max: height }, + advanced: [{ width: width, height: height }], + resizeMode: "none" + }; + + track + .applyConstraints(constraints) + .then(() => { + logger.info("Applied constraints successfully. New constraints: ", track.getConstraints()); + }) + .catch(e => logger.error("Failed to apply constraints.", e)); + if (id) { const audio = await navigator.mediaDevices.getUserMedia({ audio: { diff --git a/src/shared/settings.d.ts b/src/shared/settings.d.ts index 49d7180..9564b73 100644 --- a/src/shared/settings.d.ts +++ b/src/shared/settings.d.ts @@ -21,7 +21,7 @@ export interface Settings { arRPC?: boolean; appBadge?: boolean; disableMinSize?: boolean; - + clickTrayToShowHide?: boolean; /** @deprecated use customTitleBar */ discordWindowsTitleBar?: boolean; customTitleBar?: boolean; @@ -37,6 +37,7 @@ export interface State { maximized?: boolean; minimized?: boolean; windowBounds?: Rectangle; + displayid: int; skippedUpdate?: string; firstLaunch?: boolean;