6 Commits

Author SHA1 Message Date
V
49fb4c68b6 Updater Popup: Add Changelog; Make about page prettier 2023-06-26 01:42:51 +02:00
V
477ecbb4ba Cleanup 2023-06-26 00:41:52 +02:00
V
4abce9d084 Fix some Context Menus being broken by regression 2023-06-26 00:05:08 +02:00
V
7f54858b27 Add SpellCheck toggle in textarea context menu 2023-06-25 16:48:46 +02:00
V
be176fab71 Update README.md (#42) 2023-06-25 04:55:06 +02:00
V
e60f04bb79 Fix DevTools context menu 2023-06-25 04:20:26 +02:00
21 changed files with 175 additions and 128 deletions

View File

@@ -2,8 +2,6 @@
Vencord Desktop is a cross platform desktop app aiming to give you a snappier Discord experience with Vencord pre-installed
Vencord Desktop is currently in beta
**Not yet supported**:
- Global Keybinds

View File

@@ -1,6 +1,6 @@
{
"name": "VencordDesktop",
"version": "0.2.4",
"version": "0.2.5",
"private": true,
"description": "",
"keywords": [],

1
src/globals.d.ts vendored
View File

@@ -7,7 +7,6 @@
declare global {
export var VencordDesktopNative: typeof import("preload/VencordDesktopNative").VencordDesktopNative;
export var VencordDesktop: typeof import("renderer/index");
export var vcdLS: typeof localStorage;
export var VCDP: any;
export var IS_DEV: boolean;

View File

@@ -4,8 +4,7 @@
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { app, BrowserWindow } from "electron";
import { readFileSync } from "fs";
import { BrowserWindow } from "electron";
import { join } from "path";
import { ICON_PATH, VIEW_DIR } from "shared/paths";
@@ -15,14 +14,15 @@ export function createAboutWindow() {
const about = new BrowserWindow({
center: true,
autoHideMenuBar: true,
icon: ICON_PATH
icon: ICON_PATH,
webPreferences: {
preload: join(__dirname, "updaterPreload.js")
}
});
makeLinksOpenExternally(about);
const html = readFileSync(join(VIEW_DIR, "about.html"), "utf-8").replaceAll("%VERSION%", app.getVersion());
about.loadURL("data:text/html;charset=utf-8," + html);
about.loadFile(join(VIEW_DIR, "about.html"));
return about;
}

View File

@@ -15,6 +15,7 @@ import { autoStart } from "./autoStart";
import { DATA_DIR } from "./constants";
import { createWindows } from "./mainWindow";
import { Settings } from "./settings";
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
interface Data {
minimizeToTray: boolean;
@@ -33,6 +34,8 @@ export function createFirstLaunchTour() {
width: 550
});
makeLinksOpenExternally(win);
win.loadFile(join(VIEW_DIR, "first-launch.html"));
win.webContents.addListener("console-message", (_e, _l, msg) => {
if (msg === "cancel") return app.exit();

View File

@@ -24,20 +24,18 @@ if (IS_DEV) {
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
function init() {
// <-- BEGIN COPY PASTED FROM DISCORD -->
// 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"
"WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService,WidgetLayering"
);
// <-- END COPY PASTED FROM DISCORD -->
app.on("second-instance", (_event, _cmdLine, _cwd, data: any) => {
if (data.IS_DEV) app.quit();
else if (mainWin) {

View File

@@ -8,6 +8,7 @@ import { app, BrowserWindow, BrowserWindowConstructorOptions, Menu, Tray } from
import { join } from "path";
import { IpcEvents } from "shared/IpcEvents";
import { once } from "shared/utils/once";
import type { SettingsStore } from "shared/utils/SettingsStore";
import { ICON_PATH } from "../shared/paths";
import { createAboutWindow } from "./about";
@@ -27,6 +28,27 @@ app.on("before-quit", () => {
export let mainWin: BrowserWindow;
function makeSettingsListenerHelpers<O extends object>(o: SettingsStore<O>) {
const listeners = new Map<(data: any) => void, PropertyKey>();
const addListener: typeof o.addChangeListener = (path, cb) => {
listeners.set(cb, path);
o.addChangeListener(path, cb);
};
const removeAllListeners = () => {
for (const [listener, path] of listeners) {
o.removeChangeListener(path as any, listener);
}
listeners.clear();
};
return [addListener, removeAllListeners] as const;
}
const [addSettingsListener, removeSettingsListeners] = makeSettingsListenerHelpers(Settings);
const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings);
function initTray(win: BrowserWindow) {
const trayMenu = Menu.buildFromTemplate([
{
@@ -187,11 +209,11 @@ function initWindowBoundsListeners(win: BrowserWindow) {
}
function initSettingsListeners(win: BrowserWindow) {
Settings.addChangeListener("tray", enable => {
addSettingsListener("tray", enable => {
if (enable) initTray(win);
else tray?.destroy();
});
Settings.addChangeListener("disableMinSize", disable => {
addSettingsListener("disableMinSize", disable => {
if (disable) {
// 0 no work
win.setMinimumSize(1, 1);
@@ -206,7 +228,7 @@ function initSettingsListeners(win: BrowserWindow) {
}
});
VencordSettings.addChangeListener("macosTranslucency", enabled => {
addVencordSettingsListener("macosTranslucency", enabled => {
if (enabled) {
win.setVibrancy("sidebar");
win.setBackgroundColor("#ffffff00");
@@ -224,6 +246,10 @@ function initSpellCheck(win: BrowserWindow) {
}
function createMainWindow() {
// Clear up previous settings listeners
removeSettingsListeners();
removeVencordSettingsListeners();
const win = (mainWin = new BrowserWindow({
show: false,
autoHideMenuBar: true,

View File

@@ -9,7 +9,7 @@ import type { Settings } from "shared/settings";
import type { LiteralUnion } from "type-fest";
import { IpcEvents } from "../shared/IpcEvents";
import { invoke, sendSync } from "./typedIpcs";
import { invoke, sendSync } from "./typedIpc";
type SpellCheckerResultCallback = (word: string, suggestions: string[]) => void;

View File

@@ -22,11 +22,11 @@ Object.defineProperty(Notification.prototype, "onclick", {
configurable: true
});
// Enable Desktop Notifications by default
if (isFirstRun) {
// Hide "Download Discord Desktop now!!!!" banner
localStorage.setItem("hideNag", "true");
// Enable Desktop Notifications by default
waitFor("setDesktopType", m => {
m.setDesktopType("all");
});

View File

@@ -23,7 +23,7 @@ export function addPatch<P extends PatchData>(p: P) {
}
patch.plugin = "VencordDesktop";
Vencord.Plugins.patches.push(patch as Patch);
Vencord.Plugins.patches.push(patch);
}
Object.assign(VCDP, globals);

View File

@@ -5,13 +5,16 @@
*/
import { addContextMenuPatch } from "@vencord/types/api/ContextMenu";
import { Menu } from "@vencord/types/webpack/common";
import { findStoreLazy } from "@vencord/types/webpack";
import { ContextMenu, FluxDispatcher, Menu } from "@vencord/types/webpack/common";
import { addPatch } from "./shared";
let word: string;
let corrections: string[];
const SpellCheckStore = findStoreLazy("SpellcheckStore");
// Make spellcheck suggestions work
addPatch({
patches: [
@@ -19,14 +22,20 @@ addPatch({
find: ".enableSpellCheck)",
replacement: {
// if (isDesktop) { DiscordNative.onSpellcheck(openMenu(props)) } else { e.preventDefault(); openMenu(props) }
match: /else\{.{1,3}\.preventDefault\(\);(.{1,3}\(.{1,3}\))\}/,
match: /else\{(.{1,3})\.preventDefault\(\);(.{1,3}\(.{1,3}\))\}(?<=:(.{1,3})\.enableSpellCheck\).+?)/,
// ... else { $self.onSlateContext(() => openMenu(props)) }
replace: "else {$self.onSlateContext(() => $1)}"
replace: "else {$self.onSlateContext($1, $3?.enableSpellCheck, () => $2)}"
}
}
],
onSlateContext(openMenu: () => void) {
onSlateContext(e: MouseEvent, hasSpellcheck: boolean | undefined, openMenu: () => void) {
if (!hasSpellcheck) {
e.preventDefault();
openMenu();
return;
}
const cb = (w: string, c: string[]) => {
VencordDesktopNative.spellcheck.offSpellcheckResult(cb);
word = w;
@@ -38,22 +47,36 @@ addPatch({
});
addContextMenuPatch("textarea-context", children => () => {
if (!word || !corrections?.length) return;
const hasCorrections = Boolean(word && corrections?.length);
children.push(
<Menu.MenuGroup>
{corrections.map(c => (
<Menu.MenuItem
id={"vcd-spellcheck-suggestion-" + c}
label={c}
action={() => VencordDesktopNative.spellcheck.replaceMisspelling(c)}
/>
))}
<Menu.MenuSeparator />
<Menu.MenuItem
id="vcd-spellcheck-learn"
label={`Add ${word} to dictionary`}
action={() => VencordDesktopNative.spellcheck.addToDictionary(word)}
{hasCorrections && (
<>
{corrections.map(c => (
<Menu.MenuItem
id={"vcd-spellcheck-suggestion-" + c}
label={c}
action={() => VencordDesktopNative.spellcheck.replaceMisspelling(c)}
/>
))}
<Menu.MenuSeparator />
<Menu.MenuItem
id="vcd-spellcheck-learn"
label={`Add ${word} to dictionary`}
action={() => VencordDesktopNative.spellcheck.addToDictionary(word)}
/>
</>
)}
<Menu.MenuCheckboxItem
id="vcd-spellcheck-enabled"
label="Enable Spellcheck"
checked={SpellCheckStore.isEnabled()}
action={() => {
FluxDispatcher.dispatch({ type: "SPELLCHECK_TOGGLE" });
// Haven't found a good way to update state, so just close for now 🤷‍♀️
ContextMenu.close();
}}
/>
</Menu.MenuGroup>
);

View File

@@ -4,7 +4,7 @@
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
export const localStorage = (window.vcdLS = window.localStorage);
export const { localStorage } = window;
export const isFirstRun = (() => {
const key = "VCD_FIRST_RUN";

View File

@@ -1,23 +0,0 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
type Func = (...args: any[]) => any;
export function monkeyPatch<O extends object>(
object: O,
key: keyof O,
replacement: (original: Func, ...args: any[]) => any
): void {
const original = object[key] as Func;
const replacer = (object[key] = function (this: unknown, ...args: any[]) {
return replacement.call(this, original, ...args);
} as any);
Object.defineProperties(replacer, Object.getOwnPropertyDescriptors(original));
replacer.toString = () => original.toString();
replacer.$$original = original;
}

View File

@@ -6,9 +6,9 @@
import { app, BrowserWindow, ipcMain, shell } from "electron";
import { Settings } from "main/settings";
import { makeLinksOpenExternally } from "main/utils/makeLinksOpenExternally";
import { githubGet, ReleaseData } from "main/utils/vencordLoader";
import { join } from "path";
import { SplashProps } from "shared/browserWinProperties";
import { IpcEvents } from "shared/IpcEvents";
import { VIEW_DIR } from "shared/paths";
@@ -79,13 +79,13 @@ export async function checkUpdates() {
const oldVersion = app.getVersion();
const newVersion = data.tag_name.replace(/^v/, "");
if (Settings.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) {
updateData = {
currentVersion: oldVersion,
latestVersion: newVersion,
release: data
};
updateData = {
currentVersion: oldVersion,
latestVersion: newVersion,
release: data
};
if (Settings.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) {
openNewUpdateWindow();
}
} catch (e) {
@@ -95,11 +95,18 @@ export async function checkUpdates() {
function openNewUpdateWindow() {
const win = new BrowserWindow({
...SplashProps,
width: 500,
autoHideMenuBar: true,
alwaysOnTop: true,
webPreferences: {
preload: join(__dirname, "updaterPreload.js")
preload: join(__dirname, "updaterPreload.js"),
nodeIntegration: false,
contextIsolation: true,
sandbox: true
}
});
makeLinksOpenExternally(win);
win.loadFile(join(VIEW_DIR, "updater.html"));
}

View File

@@ -5,7 +5,7 @@
*/
import { contextBridge } from "electron";
import { invoke } from "preload/typedIpcs";
import { invoke } from "preload/typedIpc";
import { IpcEvents } from "shared/IpcEvents";
import type { UpdateData } from "./main";

View File

@@ -1,9 +1,9 @@
<head>
<link rel="stylesheet" href="./style.css" type="text/css" />
<style>
body {
padding: 2em;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
}
h1 {
@@ -13,7 +13,7 @@
</head>
<body>
<h1>Vencord Desktop %VERSION%</h1>
<h1 id="title">Vencord Desktop</h1>
<p>
Vencord Desktop is a free/libre cross platform desktop app aiming to give you a snappier Discord experience with
Vencord pre-installed
@@ -60,3 +60,12 @@
</ul>
</section>
</body>
<script type="module">
const data = await Updater.getData();
if (data.currentVersion) {
const title = document.getElementById("title");
title.textContent += ` v${data.currentVersion}`;
}
</script>

View File

@@ -1,29 +1,10 @@
<head>
<link rel="stylesheet" href="./style.css" type="text/css" />
<style>
:root {
--bg: white;
--fg: black;
--fg-secondary: #313338;
--fg-semi-trans: rgb(0 0 0 / 0.2);
--link: #006ce7;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(223 6.7% 20.6%);
--fg: white;
--fg-secondary: #b5bac1;
--fg-semi-trans: rgb(255 255 255 / 0.2);
--link: #00a8fc;
}
}
body {
height: 100vh;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
margin: 0;
padding: 1.5em;
padding-bottom: 1em;
@@ -34,13 +15,9 @@
box-sizing: border-box;
}
body,
select {
background: var(--bg);
color: var(--fg);
}
select {
padding: 0.3em;
margin: -0.3em;
border-radius: 6px;
@@ -54,10 +31,6 @@
margin: 1em 0 2em;
}
a {
color: var(--link);
}
form {
display: grid;
gap: 1em;
@@ -155,7 +128,10 @@
<label>
<div>
<h2>Rich Presence</h2>
<span>Enable Rich presence (game activity) via arRPC</span>
<span
>Enable Rich presence (game activity) via
<a href="https://github.com/OpenAsar/arrpc" target="_blank">arRPC</a></span
>
</div>
<input type="checkbox" name="richPresence" checked />
</label>

View File

@@ -1,16 +1,11 @@
<head>
<link rel="stylesheet" href="./style.css" type="text/css" />
<style>
* {
user-select: none;
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
margin: 0;
padding: 0;
}
.wrapper {
box-sizing: border-box;
height: 100%;
@@ -18,13 +13,11 @@
flex-direction: column;
justify-content: center;
align-items: center;
background-color: hsl(223 6.7% 20.6%);
border-radius: 8px;
border: 1px solid hsl(220 6.5% 18%);
border: 1px solid var(--fg-semi-trans);
}
p {
color: rgb(219, 222, 225);
text-align: center;
}

30
static/views/style.css Normal file
View File

@@ -0,0 +1,30 @@
:root {
--bg: white;
--fg: black;
--fg-secondary: #313338;
--fg-semi-trans: rgb(0 0 0 / 0.2);
--link: #006ce7;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(223 6.7% 20.6%);
--fg: white;
--fg-secondary: #b5bac1;
--fg-semi-trans: rgb(255 255 255 / 0.2);
--link: #00a8fc;
}
}
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);
}
a {
color: var(--link);
}

View File

@@ -1,22 +1,13 @@
<head>
<style>
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
margin: 0;
padding: 0;
color: rgb(219, 222, 225);
}
<link rel="stylesheet" href="./style.css" type="text/css" />
<style>
.wrapper {
display: flex;
flex-direction: column;
justify-content: space-between;
box-sizing: border-box;
height: 100%;
background-color: #313338;
border-radius: 8px;
border: 1px solid #248046;
min-height: 100%;
padding: 1em;
}
@@ -34,7 +25,7 @@
button {
cursor: pointer;
padding: 0.5em;
color: white;
color: var(--fg);
border: none;
border-radius: 3px;
font-weight: bold;
@@ -66,6 +57,9 @@
<br />
Latest: <span id="latest"></span>
</p>
<h2>Changelog</h2>
<p id="changelog">Loading...</p>
</section>
<section>
@@ -113,3 +107,17 @@
});
}
</script>
<script type="module">
import { micromark } from "https://esm.sh/micromark@3?bundle";
import { gfm, gfmHtml } from "https://esm.sh/micromark-extension-gfm@2?bundle";
const changelog = (await Updater.getData()).release.body;
if (changelog)
document.getElementById("changelog").innerHTML = micromark(changelog, {
extensions: [gfm()],
htmlExtensions: [gfmHtml()]
})
.replace(/h1>/g, "h3>")
.replace(/<a /g, '<a target="_blank" ');
</script>