Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
98a03c8862 | ||
|
72ce7a5ad1 | ||
|
e699ea63c7 | ||
|
97e1e9eb7a | ||
|
4c4036546a | ||
|
d582e61ec3 | ||
|
ccd2ce8baf | ||
|
ede507e80c | ||
|
ffdf63563b | ||
|
55b755b2df | ||
|
ca439e9e9a | ||
|
e02fcce3dc | ||
|
3e732646e5 |
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.4.3",
|
"version": "1.4.5",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@ -31,6 +31,7 @@
|
|||||||
"watch": "node scripts/build/build.mjs --watch"
|
"watch": "node scripts/build/build.mjs --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@sapphi-red/web-noise-suppressor": "0.3.3",
|
||||||
"@vap/core": "0.0.12",
|
"@vap/core": "0.0.12",
|
||||||
"@vap/shiki": "0.10.5",
|
"@vap/shiki": "0.10.5",
|
||||||
"eslint-plugin-simple-header": "^1.0.2",
|
"eslint-plugin-simple-header": "^1.0.2",
|
||||||
|
7
pnpm-lock.yaml
generated
7
pnpm-lock.yaml
generated
@ -9,6 +9,9 @@ patchedDependencies:
|
|||||||
path: patches/eslint@8.46.0.patch
|
path: patches/eslint@8.46.0.patch
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@sapphi-red/web-noise-suppressor':
|
||||||
|
specifier: 0.3.3
|
||||||
|
version: 0.3.3
|
||||||
'@vap/core':
|
'@vap/core':
|
||||||
specifier: 0.0.12
|
specifier: 0.0.12
|
||||||
version: 0.0.12
|
version: 0.0.12
|
||||||
@ -513,6 +516,10 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@sapphi-red/web-noise-suppressor@0.3.3:
|
||||||
|
resolution: {integrity: sha512-gAC33DCXYwNTI/k1PxOVHmbbzakUSMbb/DHpoV6rn4pKZtPI1dduULSmAAm/y1ipgIlArnk2JcnQzw4n2tCZHw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/diff@5.0.3:
|
/@types/diff@5.0.3:
|
||||||
resolution: {integrity: sha512-amrLbRqTU9bXMCc6uX0sWpxsQzRIo9z6MJPkH1pkez/qOxuqSZVuryJAWoBRq94CeG8JxY+VK4Le9HtjQR5T9A==}
|
resolution: {integrity: sha512-amrLbRqTU9bXMCc6uX0sWpxsQzRIo9z6MJPkH1pkez/qOxuqSZVuryJAWoBRq94CeG8JxY+VK4Le9HtjQR5T9A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -58,7 +58,7 @@ await Promise.all([
|
|||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: true,
|
IS_DISCORD_DESKTOP: true,
|
||||||
IS_VENCORD_DESKTOP: false
|
IS_VESKTOP: false
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
@ -78,7 +78,7 @@ await Promise.all([
|
|||||||
...defines,
|
...defines,
|
||||||
IS_WEB: false,
|
IS_WEB: false,
|
||||||
IS_DISCORD_DESKTOP: true,
|
IS_DISCORD_DESKTOP: true,
|
||||||
IS_VENCORD_DESKTOP: false
|
IS_VESKTOP: false
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
@ -90,7 +90,7 @@ await Promise.all([
|
|||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: true,
|
IS_DISCORD_DESKTOP: true,
|
||||||
IS_VENCORD_DESKTOP: false
|
IS_VESKTOP: false
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ await Promise.all([
|
|||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: false,
|
IS_DISCORD_DESKTOP: false,
|
||||||
IS_VENCORD_DESKTOP: true
|
IS_VESKTOP: true
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
@ -124,7 +124,7 @@ await Promise.all([
|
|||||||
...defines,
|
...defines,
|
||||||
IS_WEB: false,
|
IS_WEB: false,
|
||||||
IS_DISCORD_DESKTOP: false,
|
IS_DISCORD_DESKTOP: false,
|
||||||
IS_VENCORD_DESKTOP: true
|
IS_VESKTOP: true
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
@ -136,7 +136,7 @@ await Promise.all([
|
|||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_DISCORD_DESKTOP: false,
|
IS_DISCORD_DESKTOP: false,
|
||||||
IS_VENCORD_DESKTOP: true
|
IS_VESKTOP: true
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
]).catch(err => {
|
]).catch(err => {
|
||||||
|
@ -45,7 +45,7 @@ const commonOptions = {
|
|||||||
IS_STANDALONE: "true",
|
IS_STANDALONE: "true",
|
||||||
IS_DEV: JSON.stringify(watch),
|
IS_DEV: JSON.stringify(watch),
|
||||||
IS_DISCORD_DESKTOP: "false",
|
IS_DISCORD_DESKTOP: "false",
|
||||||
IS_VENCORD_DESKTOP: "false",
|
IS_VESKTOP: "false",
|
||||||
VERSION: JSON.stringify(VERSION),
|
VERSION: JSON.stringify(VERSION),
|
||||||
BUILD_TIMESTAMP: Date.now(),
|
BUILD_TIMESTAMP: Date.now(),
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import { promisify } from "util";
|
|||||||
|
|
||||||
// wtf is this assert syntax
|
// wtf is this assert syntax
|
||||||
import PackageJSON from "../../package.json" assert { type: "json" };
|
import PackageJSON from "../../package.json" assert { type: "json" };
|
||||||
|
import { getPluginTarget } from "../utils.mjs";
|
||||||
|
|
||||||
export const VERSION = PackageJSON.version;
|
export const VERSION = PackageJSON.version;
|
||||||
export const BUILD_TIMESTAMP = Date.now();
|
export const BUILD_TIMESTAMP = Date.now();
|
||||||
@ -81,14 +82,13 @@ export const globPlugins = kind => ({
|
|||||||
if (file.startsWith("_") || file.startsWith(".")) continue;
|
if (file.startsWith("_") || file.startsWith(".")) continue;
|
||||||
if (file === "index.ts") continue;
|
if (file === "index.ts") continue;
|
||||||
|
|
||||||
const fileBits = file.split(".");
|
const target = getPluginTarget(file);
|
||||||
if (fileBits.length > 2 && ["ts", "tsx"].includes(fileBits.at(-1))) {
|
if (target) {
|
||||||
const mod = fileBits.at(-2);
|
if (target === "dev" && !watch) continue;
|
||||||
if (mod === "dev" && !watch) continue;
|
if (target === "web" && kind === "discordDesktop") continue;
|
||||||
if (mod === "web" && kind === "discordDesktop") continue;
|
if (target === "desktop" && kind === "web") continue;
|
||||||
if (mod === "desktop" && kind === "web") continue;
|
if (target === "discordDesktop" && kind !== "discordDesktop") continue;
|
||||||
if (mod === "discordDesktop" && kind !== "discordDesktop") continue;
|
if (target === "vencordDesktop" && kind !== "vencordDesktop") continue;
|
||||||
if (mod === "vencordDesktop" && kind !== "vencordDesktop") continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mod = `p${i}`;
|
const mod = `p${i}`;
|
||||||
|
@ -21,6 +21,8 @@ import { access, readFile } from "fs/promises";
|
|||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
|
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
|
||||||
|
|
||||||
|
import { getPluginTarget } from "./utils.mjs";
|
||||||
|
|
||||||
interface Dev {
|
interface Dev {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
@ -157,11 +159,10 @@ async function parseFile(fileName: string) {
|
|||||||
|
|
||||||
if (!data.name || !data.description || !data.authors) throw fail("name, description or authors are missing");
|
if (!data.name || !data.description || !data.authors) throw fail("name, description or authors are missing");
|
||||||
|
|
||||||
const fileBits = fileName.split(".");
|
const target = getPluginTarget(fileName);
|
||||||
if (fileBits.length > 2 && ["ts", "tsx"].includes(fileBits.at(-1)!)) {
|
if (target) {
|
||||||
const mod = fileBits.at(-2)!;
|
if (!["web", "discordDesktop", "vencordDesktop", "dev"].includes(target)) throw fail(`invalid target ${target}`);
|
||||||
if (!["web", "discordDesktop", "vencordDesktop", "dev"].includes(mod)) throw fail(`invalid target ${fileBits.at(-2)}`);
|
data.target = target as any;
|
||||||
data.target = mod as any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
30
scripts/utils.mjs
Normal file
30
scripts/utils.mjs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} filePath
|
||||||
|
* @returns {string | null}
|
||||||
|
*/
|
||||||
|
export function getPluginTarget(filePath) {
|
||||||
|
const pathParts = filePath.split(/[/\\]/);
|
||||||
|
if (/^index\.tsx?$/.test(filePath.at(-1))) pathParts.pop();
|
||||||
|
|
||||||
|
const identifier = pathParts.at(-1).replace(/\.tsx?$/, "");
|
||||||
|
const identiferBits = identifier.split(".");
|
||||||
|
return identiferBits.length === 1 ? null : identiferBits.at(-1);
|
||||||
|
}
|
6
src/globals.d.ts
vendored
6
src/globals.d.ts
vendored
@ -36,7 +36,7 @@ declare global {
|
|||||||
export var IS_DEV: boolean;
|
export var IS_DEV: boolean;
|
||||||
export var IS_STANDALONE: boolean;
|
export var IS_STANDALONE: boolean;
|
||||||
export var IS_DISCORD_DESKTOP: boolean;
|
export var IS_DISCORD_DESKTOP: boolean;
|
||||||
export var IS_VENCORD_DESKTOP: boolean;
|
export var IS_VESKTOP: boolean;
|
||||||
export var VERSION: string;
|
export var VERSION: string;
|
||||||
export var BUILD_TIMESTAMP: number;
|
export var BUILD_TIMESTAMP: number;
|
||||||
|
|
||||||
@ -58,8 +58,8 @@ declare global {
|
|||||||
* If you really must use it, mark your plugin as Desktop App only by naming it Foo.desktop.ts(x)
|
* If you really must use it, mark your plugin as Desktop App only by naming it Foo.desktop.ts(x)
|
||||||
*/
|
*/
|
||||||
export var DiscordNative: any;
|
export var DiscordNative: any;
|
||||||
export var VencordDesktop: any;
|
export var Vesktop: any;
|
||||||
export var VencordDesktopNative: any;
|
export var VesktopNative: any;
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
webpackChunkdiscord_app: {
|
webpackChunkdiscord_app: {
|
||||||
|
@ -23,7 +23,7 @@ import { ensureSafePath, getSettings } from "./ipcMain";
|
|||||||
import { IS_VANILLA, THEMES_DIR } from "./utils/constants";
|
import { IS_VANILLA, THEMES_DIR } from "./utils/constants";
|
||||||
import { installExt } from "./utils/extensions";
|
import { installExt } from "./utils/extensions";
|
||||||
|
|
||||||
if (IS_VENCORD_DESKTOP || !IS_VANILLA) {
|
if (IS_VESKTOP || !IS_VANILLA) {
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
// Source Maps! Maybe there's a better way but since the renderer is executed
|
// Source Maps! Maybe there's a better way but since the renderer is executed
|
||||||
// from a string I don't think any other form of sourcemaps would work
|
// from a string I don't think any other form of sourcemaps would work
|
||||||
|
@ -53,10 +53,12 @@ async function listThemes(): Promise<UserThemeHeader[]> {
|
|||||||
const themeInfo: UserThemeHeader[] = [];
|
const themeInfo: UserThemeHeader[] = [];
|
||||||
|
|
||||||
for (const fileName of files) {
|
for (const fileName of files) {
|
||||||
|
if (!fileName.endsWith(".css")) continue;
|
||||||
|
|
||||||
const data = await getThemeData(fileName).then(stripBOM).catch(() => null);
|
const data = await getThemeData(fileName).then(stripBOM).catch(() => null);
|
||||||
if (!data) continue;
|
if (data == null) continue;
|
||||||
const parsed = getThemeInfo(data, fileName);
|
|
||||||
themeInfo.push(parsed);
|
themeInfo.push(getThemeInfo(data, fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return themeInfo;
|
return themeInfo;
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app, autoUpdater } from "electron";
|
import { app } from "electron";
|
||||||
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "fs";
|
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "fs";
|
||||||
import { basename, dirname, join } from "path";
|
import { basename, dirname, join } from "path";
|
||||||
|
|
||||||
@ -80,20 +80,22 @@ function patchLatest() {
|
|||||||
// Windows Host Updates install to a new folder app-{HOST_VERSION}, so we
|
// Windows Host Updates install to a new folder app-{HOST_VERSION}, so we
|
||||||
// need to reinject
|
// need to reinject
|
||||||
function patchUpdater() {
|
function patchUpdater() {
|
||||||
try {
|
// Array of autoStart paths to try
|
||||||
const autoStartScript = join(require.main!.filename, "..", "autoStart", "win32.js");
|
const autoStartPaths = [
|
||||||
const { update } = require(autoStartScript);
|
join(require.main!.filename, "..", "autoStart", "win32.js"), // Vanilla
|
||||||
|
join(require.main!.filename, "..", "autoStart.js") // OpenAsar
|
||||||
|
];
|
||||||
|
|
||||||
require.cache[autoStartScript]!.exports.update = function () {
|
for (const path of autoStartPaths) {
|
||||||
update.apply(this, arguments);
|
try {
|
||||||
patchLatest();
|
const { update } = require(path);
|
||||||
};
|
|
||||||
} catch {
|
require.cache[path]!.exports.update = function () {
|
||||||
// OpenAsar uses electrons autoUpdater on Windows
|
update.apply(this, arguments);
|
||||||
const { quitAndInstall } = autoUpdater;
|
patchLatest();
|
||||||
autoUpdater.quitAndInstall = function () {
|
};
|
||||||
patchLatest();
|
} catch {
|
||||||
quitAndInstall.call(this);
|
// Ignore as non-critical
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "Messages.DISCODO_DISABLED",
|
find: "Messages.DISCODO_DISABLED",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(Messages\.DISCODO_DISABLED\);return)(.*?homeIcon.*?)(\}function)/,
|
match: /(Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
||||||
replace: "$1[$2].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))$3"
|
replace: "$1[$2].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -127,13 +127,6 @@ export default definePlugin({
|
|||||||
element: require("@components/VencordSettings/PatchHelperTab").default,
|
element: require("@components/VencordSettings/PatchHelperTab").default,
|
||||||
className: "vc-patch-helper"
|
className: "vc-patch-helper"
|
||||||
},
|
},
|
||||||
// TODO: make this use customSections
|
|
||||||
IS_VENCORD_DESKTOP && {
|
|
||||||
section: "VencordDesktop",
|
|
||||||
label: "Desktop Settings",
|
|
||||||
element: VencordDesktop.Components.Settings,
|
|
||||||
className: "vc-desktop-settings"
|
|
||||||
},
|
|
||||||
...this.customSections.map(func => func(ID)),
|
...this.customSections.map(func => func(ID)),
|
||||||
{
|
{
|
||||||
section: ID.DIVIDER
|
section: ID.DIVIDER
|
||||||
@ -175,7 +168,7 @@ export default definePlugin({
|
|||||||
get additionalInfo() {
|
get additionalInfo() {
|
||||||
if (IS_DEV) return " (Dev)";
|
if (IS_DEV) return " (Dev)";
|
||||||
if (IS_WEB) return " (Web)";
|
if (IS_WEB) return " (Web)";
|
||||||
if (IS_VENCORD_DESKTOP) return ` (VencordDesktop v${VencordDesktopNative.app.getVersion()})`;
|
if (IS_VESKTOP) return ` (Vesktop v${VesktopNative.app.getVersion()})`;
|
||||||
if (IS_STANDALONE) return " (Standalone)";
|
if (IS_STANDALONE) return " (Standalone)";
|
||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
|
@ -34,7 +34,7 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: 'preload:"none","aria',
|
find: ".embedGallerySide",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<==(.{1,3})\.alt.{0,20})\?.{0,5}\.Messages\.GIF/,
|
match: /(?<==(.{1,3})\.alt.{0,20})\?.{0,5}\.Messages\.GIF/,
|
||||||
replace:
|
replace:
|
||||||
|
@ -313,7 +313,7 @@ export default definePlugin({
|
|||||||
find: ".Messages.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION",
|
find: ".Messages.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION",
|
||||||
predicate: () => settings.store.transformEmojis,
|
predicate: () => settings.store.transformEmojis,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /((\i)=\i\.node,\i=\i\.emojiSourceDiscoverableGuild)(.+?return )(.{0,450}Messages\.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION.+?}\))/,
|
match: /((\i)=\i\.node,\i=\i\.expressionSourceGuild)(.+?return )(.{0,450}Messages\.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION.+?}\))/,
|
||||||
replace: (_, rest1, node, rest2, reactNode) => `${rest1},fakeNitroNode=${node}${rest2}$self.addFakeNotice("EMOJI",${reactNode},fakeNitroNode.fake)`
|
replace: (_, rest1, node, rest2, reactNode) => `${rest1},fakeNitroNode=${node}${rest2}$self.addFakeNotice("EMOJI",${reactNode},fakeNitroNode.fake)`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ function EncModal(props: ModalProps) {
|
|||||||
const [password, setPassword] = React.useState("password");
|
const [password, setPassword] = React.useState("password");
|
||||||
const [noCover, setNoCover] = React.useState(false);
|
const [noCover, setNoCover] = React.useState(false);
|
||||||
|
|
||||||
const isValid = secret && (noCover || (cover && /\w \w/.test(cover)));
|
const isValid = secret && (noCover || (cover && cover.trim().split(" ").length > 1));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalRoot {...props}>
|
<ModalRoot {...props}>
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
color: #f04747 !important;
|
color: #f04747 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bot "thinking" text highlighting */
|
||||||
|
.messagelogger-deleted [class*="colorStandard-"] {
|
||||||
|
color: #f04747 !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Embed highlighting */
|
/* Embed highlighting */
|
||||||
.messagelogger-deleted article :is(div, span, h1, h2, h3, p) {
|
.messagelogger-deleted article :is(div, span, h1, h2, h3, p) {
|
||||||
color: #f04747 !important;
|
color: #f04747 !important;
|
||||||
|
@ -27,10 +27,10 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: '("ApplicationStreamPreviewUploadManager")',
|
find: '("ApplicationStreamPreviewUploadManager")',
|
||||||
replacement: [
|
replacement: [
|
||||||
"\\i\\.default\\.makeChunkedRequest\\(",
|
String.raw`\i\.\i\.makeChunkedRequest\(`,
|
||||||
"\\i\\.\\i\\.post\\({url:"
|
String.raw`\i\.\i\.post\({url:`
|
||||||
].map(match => ({
|
].map(match => ({
|
||||||
match: new RegExp(`(?=return\\[(\\d),${match}\\i\\.\\i\\.STREAM_PREVIEW.+?}\\)\\];)`),
|
match: new RegExp(String.raw`(?=return\[(\d),${match}\i\.\i\.STREAM_PREVIEW.+?}\)\];)`),
|
||||||
replace: (_, code) => `return[${code},Promise.resolve({body:"",status:204})];`
|
replace: (_, code) => `return[${code},Promise.resolve({body:"",status:204})];`
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
26
src/plugins/normalizeMessageLinks/index.ts
Normal file
26
src/plugins/normalizeMessageLinks/index.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "NormalizeMessageLinks",
|
||||||
|
description: "Strip canary/ptb from message links",
|
||||||
|
authors: [Devs.bb010g],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: ".Messages.COPY_MESSAGE_LINK,",
|
||||||
|
replacement: {
|
||||||
|
match: /\.concat\(location\.host\)/,
|
||||||
|
replace: ".concat($self.normalizeHost(location.host))",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
normalizeHost(host: string) {
|
||||||
|
return host.replace(/(^|\b)(canary\.|ptb\.)(discord.com)$/, "$1$3");
|
||||||
|
},
|
||||||
|
});
|
83
src/plugins/previewMessage.tsx
Normal file
83
src/plugins/previewMessage.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { sendBotMessage } from "@api/Commands";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
import { Button, ButtonLooks, ButtonWrapperClasses, DraftStore, DraftType, SelectedChannelStore, Tooltip, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
type: {
|
||||||
|
analyticsName: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
|
||||||
|
|
||||||
|
export function PreviewButton(chatBoxProps: Props) {
|
||||||
|
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
||||||
|
const channelId = SelectedChannelStore.getChannelId();
|
||||||
|
const draft = getDraft(channelId);
|
||||||
|
|
||||||
|
if (!draft) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip text="Preview Message">
|
||||||
|
{tooltipProps => (
|
||||||
|
<Button
|
||||||
|
{...tooltipProps}
|
||||||
|
onClick={() =>
|
||||||
|
sendBotMessage(
|
||||||
|
channelId,
|
||||||
|
{
|
||||||
|
content: getDraft(channelId),
|
||||||
|
author: UserStore.getCurrentUser()
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
size=""
|
||||||
|
look={ButtonLooks.BLANK}
|
||||||
|
innerClassName={ButtonWrapperClasses.button}
|
||||||
|
style={{ padding: "0 2px", height: "100%" }}
|
||||||
|
>
|
||||||
|
<div className={ButtonWrapperClasses.buttonWrapper}>
|
||||||
|
<img width={24} height={24} src="https://discord.com/assets/4c5a77a89716352686f590a6f014770c.svg" />
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "PreviewMessage",
|
||||||
|
description: "Lets you preview your message before sending it",
|
||||||
|
authors: [Devs.Aria],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: ".activeCommandOption",
|
||||||
|
replacement: {
|
||||||
|
match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
||||||
|
replace: "$&;try{$2||$1.unshift($self.previewIcon(arguments[0]))}catch{}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
previewIcon: ErrorBoundary.wrap(PreviewButton, { noop: true }),
|
||||||
|
});
|
@ -68,7 +68,8 @@ export default definePlugin({
|
|||||||
find: ".USER_PROFILE_ACTIVITY",
|
find: ".USER_PROFILE_ACTIVITY",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /getGlobalName\(\i\);(?<=displayProfile.{0,200})/,
|
/* FIXME: old name is getGlobalName, new name is getName. Remove optional Global once stable has also migrated */
|
||||||
|
match: /\.get(?:Global)?Name\(\i\);(?<=displayProfile.{0,200})/,
|
||||||
replace: "$&const [vcPronounce,vcPronounSource]=$self.useProfilePronouns(arguments[0].user.id,true);if(arguments[0].displayProfile&&vcPronounce)arguments[0].displayProfile.pronouns=vcPronounce;"
|
replace: "$&const [vcPronounce,vcPronounSource]=$self.useProfilePronouns(arguments[0].user.id,true);if(arguments[0].displayProfile&&vcPronounce)arguments[0].displayProfile.pronouns=vcPronounce;"
|
||||||
},
|
},
|
||||||
PRONOUN_TOOLTIP_PATCH
|
PRONOUN_TOOLTIP_PATCH
|
||||||
|
@ -33,7 +33,7 @@ export default LazyComponent(() => {
|
|||||||
// this is terrible, blame mantika
|
// this is terrible, blame mantika
|
||||||
const p = filters.byProps;
|
const p = filters.byProps;
|
||||||
const [
|
const [
|
||||||
{ cozyMessage, buttons, message, groupStart },
|
{ cozyMessage, buttons, message, buttonsInner, groupStart },
|
||||||
{ container, isHeader },
|
{ container, isHeader },
|
||||||
{ avatar, clickable, username, messageContent, wrapper, cozy },
|
{ avatar, clickable, username, messageContent, wrapper, cozy },
|
||||||
buttonClasses,
|
buttonClasses,
|
||||||
@ -134,7 +134,7 @@ export default LazyComponent(() => {
|
|||||||
<div className={classes(container, isHeader, buttons)} style={{
|
<div className={classes(container, isHeader, buttons)} style={{
|
||||||
padding: "0px",
|
padding: "0px",
|
||||||
}}>
|
}}>
|
||||||
<div className={buttonClasses.wrapper} >
|
<div className={classes(buttonClasses.wrapper, buttonsInner)} >
|
||||||
<ReportButton onClick={reportRev} />
|
<ReportButton onClick={reportRev} />
|
||||||
|
|
||||||
{canDeleteReview(review, UserStore.getCurrentUser().id) && (
|
{canDeleteReview(review, UserStore.getCurrentUser().id) && (
|
||||||
|
21
src/plugins/rnnoise.web/icons.tsx
Normal file
21
src/plugins/rnnoise.web/icons.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const SupressionIcon = ({ enabled }: { enabled: boolean; }) => enabled
|
||||||
|
? <svg aria-hidden="true" role="img" width="20" height="20" viewBox="0 0 24 24"><path d="M10.889 4C10.889 3.44772 11.3367 3 11.889 3H12.1112C12.6635 3 13.1112 3.44772 13.1112 4V20C13.1112 20.5523 12.6635 21 12.1112 21H11.889C11.3367 21 10.889 20.5523 10.889 20V4Z" fill="currentColor"></path><path d="M6.44439 6.25C6.44439 5.69772 6.89211 5.25 7.44439 5.25H7.66661C8.2189 5.25 8.66661 5.69772 8.66661 6.25V17.75C8.66661 18.3023 8.2189 18.75 7.66661 18.75H7.44439C6.89211 18.75 6.44439 18.3023 6.44439 17.75V6.25Z" fill="currentColor"></path><path d="M3.22222 15.375C3.77451 15.375 4.22222 14.9273 4.22222 14.375L4.22222 9.625C4.22222 9.07272 3.77451 8.625 3.22222 8.625H3C2.44772 8.625 2 9.07272 2 9.625V14.375C2 14.9273 2.44772 15.375 3 15.375H3.22222Z" fill="currentColor"></path><path d="M22.0001 13.25C22.0001 13.8023 21.5523 14.25 21.0001 14.25H20.7778C20.2255 14.25 19.7778 13.8023 19.7778 13.25V10.75C19.7778 10.1977 20.2255 9.75 20.7778 9.75H21.0001C21.5523 9.75 22.0001 10.1977 22.0001 10.75V13.25Z" fill="currentColor"></path><path d="M16.3333 7.5C15.781 7.5 15.3333 7.94772 15.3333 8.5V15.5C15.3333 16.0523 15.781 16.5 16.3333 16.5H16.5555C17.1078 16.5 17.5555 16.0523 17.5555 15.5V8.5C17.5555 7.94772 17.1078 7.5 16.5555 7.5H16.3333Z" fill="currentColor"></path></svg>
|
||||||
|
: <svg aria-hidden="true" role="img" width="20" height="20" viewBox="0 0 48 48"><path d="M30.6666 24.9644L35.1111 20.5199V31C35.1111 32.1046 34.2156 33 33.1111 33H32.6666C31.562 33 30.6666 32.1046 30.6666 31V24.9644Z" fill="currentColor"></path><path d="M26.2224 14.1463V8C26.2224 6.89543 25.327 6 24.2224 6H23.7779C22.6734 6 21.7779 6.89543 21.7779 8V18.5907L26.2224 14.1463Z" fill="currentColor"></path><path d="M21.7779 33.8543L21.9254 33.7056L26.2224 29.4086V40C26.2224 41.1046 25.327 42 24.2224 42H23.7779C22.6734 42 21.7779 41.1046 21.7779 40V33.8543Z" fill="currentColor"></path><path d="M17.3332 23.0354L12.8888 27.4799V12.5C12.8888 11.3954 13.7842 10.5 14.8888 10.5H15.3332C16.4378 10.5 17.3332 11.3954 17.3332 12.5V23.0354Z" fill="currentColor"></path><path d="M8.44445 28.75C8.44445 29.8546 7.54902 30.75 6.44445 30.75H6C4.89543 30.75 4 29.8546 4 28.75V19.25C4 18.1454 4.89543 17.25 6 17.25H6.44445C7.54902 17.25 8.44445 18.1454 8.44445 19.25L8.44445 28.75Z" fill="currentColor"></path><path d="M44.0001 26.5C44.0001 27.6046 43.1047 28.5 42.0001 28.5H41.5557C40.4511 28.5 39.5557 27.6046 39.5557 26.5V21.5C39.5557 20.3954 40.4511 19.5 41.5557 19.5H42.0001C43.1047 19.5 44.0001 20.3954 44.0001 21.5V26.5Z" fill="currentColor"></path><path d="M42 8.54L39.46 6L6 39.46L8.54 42L16.92 33.64L19.38 31.16L22.7 27.84L29.98 20.56L42 8.54Z" fill="currentColor"></path></svg>;
|
249
src/plugins/rnnoise.web/index.tsx
Normal file
249
src/plugins/rnnoise.web/index.tsx
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { Switch } from "@components/Switch";
|
||||||
|
import { loadRnnoise, RnnoiseWorkletNode } from "@sapphi-red/web-noise-suppressor";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import { rnnoiseWasmSrc, rnnoiseWorkletSrc } from "@utils/dependencies";
|
||||||
|
import { makeLazy } from "@utils/lazy";
|
||||||
|
import { Logger } from "@utils/Logger";
|
||||||
|
import { LazyComponent } from "@utils/react";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
import { findByCode } from "@webpack";
|
||||||
|
import { FluxDispatcher, Popout, React } from "@webpack/common";
|
||||||
|
import { MouseEvent, ReactNode } from "react";
|
||||||
|
|
||||||
|
import { SupressionIcon } from "./icons";
|
||||||
|
|
||||||
|
const RNNOISE_OPTION = "RNNOISE";
|
||||||
|
|
||||||
|
interface PanelButtonProps {
|
||||||
|
tooltipText: string;
|
||||||
|
icon: () => ReactNode;
|
||||||
|
onClick: (event: MouseEvent<HTMLElement>) => void;
|
||||||
|
tooltipClassName?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
shouldShow?: boolean;
|
||||||
|
}
|
||||||
|
const PanelButton = LazyComponent<PanelButtonProps>(() => findByCode("Masks.PANEL_BUTTON"));
|
||||||
|
const enum SpinnerType {
|
||||||
|
SpinningCircle = "spinningCircle",
|
||||||
|
ChasingDots = "chasingDots",
|
||||||
|
LowMotion = "lowMotion",
|
||||||
|
PulsingEllipsis = "pulsingEllipsis",
|
||||||
|
WanderingCubes = "wanderingCubes",
|
||||||
|
}
|
||||||
|
export interface SpinnerProps {
|
||||||
|
type: SpinnerType;
|
||||||
|
animated?: boolean;
|
||||||
|
className?: string;
|
||||||
|
itemClassName?: string;
|
||||||
|
}
|
||||||
|
const Spinner = LazyComponent<SpinnerProps>(() => findByCode(".spinningCircleInner"));
|
||||||
|
|
||||||
|
function createExternalStore<S>(init: () => S) {
|
||||||
|
const subscribers = new Set<() => void>();
|
||||||
|
let state = init();
|
||||||
|
|
||||||
|
return {
|
||||||
|
get: () => state,
|
||||||
|
set: (newStateGetter: (oldState: S) => S) => {
|
||||||
|
state = newStateGetter(state);
|
||||||
|
for (const cb of subscribers) cb();
|
||||||
|
},
|
||||||
|
use: () => {
|
||||||
|
return React.useSyncExternalStore<S>(onStoreChange => {
|
||||||
|
subscribers.add(onStoreChange);
|
||||||
|
return () => subscribers.delete(onStoreChange);
|
||||||
|
}, () => state);
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cl = classNameFactory("vc-rnnoise-");
|
||||||
|
|
||||||
|
const loadedStore = createExternalStore(() => ({
|
||||||
|
isLoaded: false,
|
||||||
|
isLoading: false,
|
||||||
|
isError: false,
|
||||||
|
}));
|
||||||
|
const getRnnoiseWasm = makeLazy(() => {
|
||||||
|
loadedStore.set(s => ({ ...s, isLoading: true }));
|
||||||
|
return loadRnnoise({
|
||||||
|
url: rnnoiseWasmSrc(),
|
||||||
|
simdUrl: rnnoiseWasmSrc(true),
|
||||||
|
}).then(buffer => {
|
||||||
|
// Check WASM magic number cus fetch doesnt throw on 4XX or 5XX
|
||||||
|
if (new DataView(buffer.slice(0, 4)).getUint32(0) !== 0x0061736D) throw buffer;
|
||||||
|
|
||||||
|
loadedStore.set(s => ({ ...s, isLoaded: true }));
|
||||||
|
return buffer;
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof ArrayBuffer) error = new TextDecoder().decode(error);
|
||||||
|
logger.error("Failed to load RNNoise WASM:", error);
|
||||||
|
loadedStore.set(s => ({ ...s, isError: true }));
|
||||||
|
return null;
|
||||||
|
}).finally(() => {
|
||||||
|
loadedStore.set(s => ({ ...s, isLoading: false }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const logger = new Logger("RNNoise");
|
||||||
|
const settings = definePluginSettings({}).withPrivateSettings<{ isEnabled: boolean; }>();
|
||||||
|
const setEnabled = (enabled: boolean) => {
|
||||||
|
settings.store.isEnabled = enabled;
|
||||||
|
FluxDispatcher.dispatch({ type: "AUDIO_SET_NOISE_SUPPRESSION", enabled });
|
||||||
|
};
|
||||||
|
|
||||||
|
function NoiseSupressionPopout() {
|
||||||
|
const { isEnabled } = settings.use();
|
||||||
|
const { isLoading, isError } = loadedStore.use();
|
||||||
|
const isWorking = isEnabled && !isError;
|
||||||
|
|
||||||
|
return <div className={cl("popout")}>
|
||||||
|
<div className={cl("popout-heading")}>
|
||||||
|
<span>Noise Supression</span>
|
||||||
|
<div style={{ flex: 1 }} />
|
||||||
|
{isLoading && <Spinner type={SpinnerType.PulsingEllipsis} />}
|
||||||
|
<Switch checked={isWorking} onChange={setEnabled} disabled={isError} />
|
||||||
|
</div>
|
||||||
|
<div className={cl("popout-desc")}>
|
||||||
|
Enable AI noise suppression! Make some noise—like becoming an air conditioner, or a vending machine fan—while speaking. Your friends will hear nothing but your beautiful voice ✨
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "AI Noise Suppression",
|
||||||
|
description: "Uses an open-source AI model (RNNoise) to remove background noise from your microphone",
|
||||||
|
authors: [Devs.Vap],
|
||||||
|
settings,
|
||||||
|
enabledByDefault: true,
|
||||||
|
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
// Pass microphone stream to RNNoise
|
||||||
|
find: "window.webkitAudioContext",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\i\.acquire=function\((\i)\)\{return )navigator\.mediaDevices\.getUserMedia\(\1\)(?=\})/,
|
||||||
|
replace: m => `${m}.then(stream => $self.connectRnnoise(stream))`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Noise suppression button in call modal
|
||||||
|
find: "renderNoiseCancellation()",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=(\i)\.jsxs?.{0,70}children:\[)(?=\i\?\i\.renderNoiseCancellation\(\))/,
|
||||||
|
replace: (_, react) => `${react}.jsx($self.NoiseSupressionButton, {}),`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Give noise suppression component a "shouldShow" prop
|
||||||
|
find: "Masks.PANEL_BUTTON",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<==(\i)\.tooltipForceOpen.{0,100})(?=tooltipClassName:)/,
|
||||||
|
replace: (_, props) => `shouldShow: ${props}.shouldShow,`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Noise suppression option in voice settings
|
||||||
|
find: "Messages.USER_SETTINGS_NOISE_CANCELLATION_KRISP",
|
||||||
|
replacement: [{
|
||||||
|
match: /(?<=(\i)=\i\?\i\.KRISP:\i.{1,20}?;)/,
|
||||||
|
replace: (_, option) => `if ($self.isEnabled()) ${option} = ${JSON.stringify(RNNOISE_OPTION)};`,
|
||||||
|
}, {
|
||||||
|
match: /(?=\i&&(\i)\.push\(\{name:(?:\i\.){1,2}Messages.USER_SETTINGS_NOISE_CANCELLATION_KRISP)/,
|
||||||
|
replace: (_, options) => `${options}.push({ name: "AI (RNNoise)", value: "${RNNOISE_OPTION}" });`,
|
||||||
|
}, {
|
||||||
|
match: /(?<=onChange:function\((\i)\)\{)(?=(?:\i\.){1,2}setNoiseCancellation)/,
|
||||||
|
replace: (_, option) => `$self.setEnabled(${option}.value === ${JSON.stringify(RNNOISE_OPTION)});`,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
setEnabled,
|
||||||
|
isEnabled: () => settings.store.isEnabled,
|
||||||
|
async connectRnnoise(stream: MediaStream): Promise<MediaStream> {
|
||||||
|
if (!settings.store.isEnabled) return stream;
|
||||||
|
|
||||||
|
const audioCtx = new AudioContext();
|
||||||
|
await audioCtx.audioWorklet.addModule(rnnoiseWorkletSrc);
|
||||||
|
|
||||||
|
const rnnoiseWasm = await getRnnoiseWasm();
|
||||||
|
if (!rnnoiseWasm) {
|
||||||
|
logger.warn("Failed to load RNNoise, noise suppression won't work");
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rnnoise = new RnnoiseWorkletNode(audioCtx, {
|
||||||
|
wasmBinary: rnnoiseWasm,
|
||||||
|
maxChannels: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const source = audioCtx.createMediaStreamSource(stream);
|
||||||
|
source.connect(rnnoise);
|
||||||
|
|
||||||
|
const dest = audioCtx.createMediaStreamDestination();
|
||||||
|
rnnoise.connect(dest);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
const onEnded = () => {
|
||||||
|
rnnoise.disconnect();
|
||||||
|
source.disconnect();
|
||||||
|
audioCtx.close();
|
||||||
|
rnnoise.destroy();
|
||||||
|
};
|
||||||
|
stream.addEventListener("inactive", onEnded, { once: true });
|
||||||
|
|
||||||
|
return dest.stream;
|
||||||
|
},
|
||||||
|
NoiseSupressionButton(): ReactNode {
|
||||||
|
const { isEnabled } = settings.use();
|
||||||
|
const { isLoading, isError } = loadedStore.use();
|
||||||
|
|
||||||
|
return <Popout
|
||||||
|
key="rnnoise-popout"
|
||||||
|
align="center"
|
||||||
|
animation={Popout.Animation.TRANSLATE}
|
||||||
|
autoInvert={true}
|
||||||
|
nudgeAlignIntoViewport={true}
|
||||||
|
position="top"
|
||||||
|
renderPopout={() => <NoiseSupressionPopout />}
|
||||||
|
spacing={8}
|
||||||
|
>
|
||||||
|
{(props, { isShown }) => (
|
||||||
|
<PanelButton
|
||||||
|
{...props}
|
||||||
|
tooltipText="Noise Suppression powered by RNNoise"
|
||||||
|
tooltipClassName={cl("tooltip")}
|
||||||
|
shouldShow={!isShown}
|
||||||
|
icon={() => <div style={{
|
||||||
|
color: isError ? "var(--status-danger)" : "inherit",
|
||||||
|
opacity: isLoading ? 0.5 : 1,
|
||||||
|
}}>
|
||||||
|
<SupressionIcon enabled={isEnabled} />
|
||||||
|
</div>}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Popout>;
|
||||||
|
},
|
||||||
|
});
|
29
src/plugins/rnnoise.web/styles.css
Normal file
29
src/plugins/rnnoise.web/styles.css
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
.vc-rnnoise-popout {
|
||||||
|
background: var(--background-floating);
|
||||||
|
border-radius: 0.25em;
|
||||||
|
padding: 1em;
|
||||||
|
width: 16em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-rnnoise-popout-heading {
|
||||||
|
color: var(--text-normal);
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1.1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-rnnoise-popout-desc {
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 0.9em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-rnnoise-tooltip {
|
||||||
|
text-align: center;
|
||||||
|
}
|
@ -53,7 +53,7 @@ export default definePlugin({
|
|||||||
|
|
||||||
const client = (() => {
|
const client = (() => {
|
||||||
if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`;
|
if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`;
|
||||||
if (IS_VENCORD_DESKTOP) return `Vencord Desktop v${VencordDesktopNative.app.getVersion()}`;
|
if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`;
|
||||||
if ("armcord" in window) return `ArmCord v${window.armcord.version}`;
|
if ("armcord" in window) return `ArmCord v${window.armcord.version}`;
|
||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
@ -39,8 +39,8 @@ const settings = definePluginSettings({
|
|||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Add back the Discord context menus for images, links and the chat input bar",
|
description: "Add back the Discord context menus for images, links and the chat input bar",
|
||||||
// Web slate menu has proper spellcheck suggestions and image context menu is also pretty good,
|
// Web slate menu has proper spellcheck suggestions and image context menu is also pretty good,
|
||||||
// so disable this by default. Vencord Desktop just doesn't, so enable by default
|
// so disable this by default. Vesktop just doesn't, so enable by default
|
||||||
default: IS_VENCORD_DESKTOP,
|
default: IS_VESKTOP,
|
||||||
restartNeeded: true
|
restartNeeded: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -50,7 +50,7 @@ export default definePlugin({
|
|||||||
description: "Re-adds context menus missing in the web version of Discord: Links & Images (Copy/Open Link/Image), Text Area (Copy, Cut, Paste, SpellCheck)",
|
description: "Re-adds context menus missing in the web version of Discord: Links & Images (Copy/Open Link/Image), Text Area (Copy, Cut, Paste, SpellCheck)",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
enabledByDefault: true,
|
enabledByDefault: true,
|
||||||
required: IS_VENCORD_DESKTOP,
|
required: IS_VESKTOP,
|
||||||
|
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
// Fix silly Discord calling the non web support copy
|
// Fix silly Discord calling the non web support copy
|
||||||
{
|
{
|
||||||
match: /\w\.default\.copy/,
|
match: /\i\.\i\.copy/,
|
||||||
replace: "Vencord.Webpack.Common.Clipboard.copy"
|
replace: "Vencord.Webpack.Common.Clipboard.copy"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -102,7 +102,7 @@ export default definePlugin({
|
|||||||
replace: "if(null=="
|
replace: "if(null=="
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /return\s*?\[\i\.default\.canCopyImage\(\)/,
|
match: /return\s*?\[\i\.\i\.canCopyImage\(\)/,
|
||||||
replace: "return [true"
|
replace: "return [true"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -158,7 +158,7 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Change calls to DiscordNative.clipboard to us instead
|
// Change calls to DiscordNative.clipboard to us instead
|
||||||
match: /\b\i\.default\.(copy|cut|paste)/g,
|
match: /\b\i\.\i\.(copy|cut|paste)/g,
|
||||||
replace: "$self.$1"
|
replace: "$self.$1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -28,7 +28,7 @@ contextBridge.exposeInMainWorld("VencordNative", VencordNative);
|
|||||||
// Discord
|
// Discord
|
||||||
if (location.protocol !== "data:") {
|
if (location.protocol !== "data:") {
|
||||||
// #region cssInsert
|
// #region cssInsert
|
||||||
const rendererCss = join(__dirname, IS_VENCORD_DESKTOP ? "vencordDesktopRenderer.css" : "renderer.css");
|
const rendererCss = join(__dirname, IS_VESKTOP ? "vencordDesktopRenderer.css" : "renderer.css");
|
||||||
|
|
||||||
const style = document.createElement("style");
|
const style = document.createElement("style");
|
||||||
style.id = "vencord-css-core";
|
style.id = "vencord-css-core";
|
||||||
|
@ -347,6 +347,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||||||
name: "arrow",
|
name: "arrow",
|
||||||
id: 958158495302176778n
|
id: 958158495302176778n
|
||||||
},
|
},
|
||||||
|
bb010g: {
|
||||||
|
name: "bb010g",
|
||||||
|
id: 72791153467990016n,
|
||||||
|
},
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
// iife so #__PURE__ works correctly
|
// iife so #__PURE__ works correctly
|
||||||
|
@ -79,5 +79,9 @@ const shikiWorkerDist = "https://unpkg.com/@vap/shiki-worker@0.0.8/dist";
|
|||||||
export const shikiWorkerSrc = `${shikiWorkerDist}/${IS_DEV ? "index.js" : "index.min.js"}`;
|
export const shikiWorkerSrc = `${shikiWorkerDist}/${IS_DEV ? "index.js" : "index.min.js"}`;
|
||||||
export const shikiOnigasmSrc = "https://unpkg.com/@vap/shiki@0.10.3/dist/onig.wasm";
|
export const shikiOnigasmSrc = "https://unpkg.com/@vap/shiki@0.10.3/dist/onig.wasm";
|
||||||
|
|
||||||
|
export const rnnoiseDist = "https://unpkg.com/@sapphi-red/web-noise-suppressor@0.3.3/dist";
|
||||||
|
export const rnnoiseWasmSrc = (simd = false) => `${rnnoiseDist}/rnnoise${simd ? "_simd" : ""}.wasm`;
|
||||||
|
export const rnnoiseWorkletSrc = `${rnnoiseDist}/rnnoise/workletProcessor.js`;
|
||||||
|
|
||||||
// @ts-expect-error SHUT UP
|
// @ts-expect-error SHUT UP
|
||||||
export const getStegCloak = makeLazy(() => import("https://unpkg.com/stegcloak-dist@1.0.0/index.js"));
|
export const getStegCloak = makeLazy(() => import("https://unpkg.com/stegcloak-dist@1.0.0/index.js"));
|
||||||
|
@ -20,12 +20,12 @@ export function relaunch() {
|
|||||||
if (IS_DISCORD_DESKTOP)
|
if (IS_DISCORD_DESKTOP)
|
||||||
window.DiscordNative.app.relaunch();
|
window.DiscordNative.app.relaunch();
|
||||||
else
|
else
|
||||||
window.VencordDesktopNative.app.relaunch();
|
window.VesktopNative.app.relaunch();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showItemInFolder(path: string) {
|
export function showItemInFolder(path: string) {
|
||||||
if (IS_DISCORD_DESKTOP)
|
if (IS_DISCORD_DESKTOP)
|
||||||
window.DiscordNative.fileManager.showItemInFolder(path);
|
window.DiscordNative.fileManager.showItemInFolder(path);
|
||||||
else
|
else
|
||||||
window.VencordDesktopNative.fileManager.showItemInFolder(path);
|
window.VesktopNative.fileManager.showItemInFolder(path);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,13 @@ export const Flux: t.Flux = findByPropsLazy("connectStores");
|
|||||||
|
|
||||||
export type GenericStore = t.FluxStore & Record<string, any>;
|
export type GenericStore = t.FluxStore & Record<string, any>;
|
||||||
|
|
||||||
|
export enum DraftType {
|
||||||
|
ChannelMessage = 0,
|
||||||
|
ThreadSettings = 1,
|
||||||
|
FirstThreadMessage = 2,
|
||||||
|
ApplicationLauncherCommand = 3
|
||||||
|
}
|
||||||
|
|
||||||
export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & {
|
export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & {
|
||||||
getMessages(chanId: string): any;
|
getMessages(chanId: string): any;
|
||||||
};
|
};
|
||||||
@ -52,6 +59,7 @@ export let RelationshipStore: Stores.RelationshipStore & t.FluxStore & {
|
|||||||
|
|
||||||
export let EmojiStore: t.EmojiStore;
|
export let EmojiStore: t.EmojiStore;
|
||||||
export let WindowStore: t.WindowStore;
|
export let WindowStore: t.WindowStore;
|
||||||
|
export let DraftStore: t.DraftStore;
|
||||||
|
|
||||||
export const MaskedLinkStore = mapMangledModuleLazy('"MaskedLinkStore"', {
|
export const MaskedLinkStore = mapMangledModuleLazy('"MaskedLinkStore"', {
|
||||||
openUntrustedLink: filters.byCode(".apply(this,arguments)")
|
openUntrustedLink: filters.byCode(".apply(this,arguments)")
|
||||||
@ -76,6 +84,7 @@ export const useStateFromStores: <T>(
|
|||||||
) => T
|
) => T
|
||||||
= findByCodeLazy("useStateFromStores");
|
= findByCodeLazy("useStateFromStores");
|
||||||
|
|
||||||
|
waitForStore("DraftStore", s => DraftStore = s);
|
||||||
waitForStore("UserStore", s => UserStore = s);
|
waitForStore("UserStore", s => UserStore = s);
|
||||||
waitForStore("ChannelStore", m => ChannelStore = m);
|
waitForStore("ChannelStore", m => ChannelStore = m);
|
||||||
waitForStore("SelectedChannelStore", m => SelectedChannelStore = m);
|
waitForStore("SelectedChannelStore", m => SelectedChannelStore = m);
|
||||||
|
10
src/webpack/common/types/components.d.ts
vendored
10
src/webpack/common/types/components.d.ts
vendored
@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Moment } from "moment";
|
import type { Moment } from "moment";
|
||||||
import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react";
|
import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, KeyboardEvent, MouseEvent, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react";
|
||||||
|
|
||||||
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
|
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
|
||||||
export type FormTextTypes = Record<"DEFAULT" | "INPUT_PLACEHOLDER" | "DESCRIPTION" | "LABEL_BOLD" | "LABEL_SELECTED" | "LABEL_DESCRIPTOR" | "ERROR" | "SUCCESS", string>;
|
export type FormTextTypes = Record<"DEFAULT" | "INPUT_PLACEHOLDER" | "DESCRIPTION" | "LABEL_BOLD" | "LABEL_SELECTED" | "LABEL_DESCRIPTOR" | "ERROR" | "SUCCESS", string>;
|
||||||
@ -338,16 +338,16 @@ export type Popout = ComponentType<{
|
|||||||
thing: {
|
thing: {
|
||||||
"aria-controls": string;
|
"aria-controls": string;
|
||||||
"aria-expanded": boolean;
|
"aria-expanded": boolean;
|
||||||
onClick(event: MouseEvent): void;
|
onClick(event: MouseEvent<HTMLElement>): void;
|
||||||
onKeyDown(event: KeyboardEvent): void;
|
onKeyDown(event: KeyboardEvent<HTMLElement>): void;
|
||||||
onMouseDown(event: MouseEvent): void;
|
onMouseDown(event: MouseEvent<HTMLElement>): void;
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
isShown: boolean;
|
isShown: boolean;
|
||||||
position: string;
|
position: string;
|
||||||
}
|
}
|
||||||
): ReactNode;
|
): ReactNode;
|
||||||
shouldShow: boolean;
|
shouldShow?: boolean;
|
||||||
renderPopout(args: {
|
renderPopout(args: {
|
||||||
closePopout(): void;
|
closePopout(): void;
|
||||||
isPositioned: boolean;
|
isPositioned: boolean;
|
||||||
|
24
src/webpack/common/types/stores.d.ts
vendored
24
src/webpack/common/types/stores.d.ts
vendored
@ -16,6 +16,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { DraftType } from "@webpack/common";
|
||||||
import { Channel } from "discord-types/general";
|
import { Channel } from "discord-types/general";
|
||||||
|
|
||||||
import { FluxDispatcher, FluxEvents } from "./utils";
|
import { FluxDispatcher, FluxEvents } from "./utils";
|
||||||
@ -148,3 +149,26 @@ export class EmojiStore extends FluxStore {
|
|||||||
get favoriteEmojisWithoutFetchingLatest(): Emoji[];
|
get favoriteEmojisWithoutFetchingLatest(): Emoji[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DraftObject {
|
||||||
|
channelId: string;
|
||||||
|
timestamp: number;
|
||||||
|
draft: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DraftState {
|
||||||
|
[userId: string]: {
|
||||||
|
[channelId: string]: {
|
||||||
|
[key in DraftType]?: Omit<DraftObject, "channelId">;
|
||||||
|
} | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class DraftStore extends FluxStore {
|
||||||
|
getDraft(channelId: string, type: DraftType): string;
|
||||||
|
getRecentlyEditedDrafts(type: DraftType): DraftObject[];
|
||||||
|
getState(): DraftState;
|
||||||
|
getThreadDraftWithParentMessageId?(arg: any): any;
|
||||||
|
getThreadSettings(channelId: string): any | null;
|
||||||
|
}
|
||||||
|
@ -93,7 +93,7 @@ export const UserUtils = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Clipboard = mapMangledModuleLazy('document.queryCommandEnabled("copy")||document.queryCommandSupported("copy")', {
|
export const Clipboard = mapMangledModuleLazy('document.queryCommandEnabled("copy")||document.queryCommandSupported("copy")', {
|
||||||
copy: filters.byCode(".default.copy("),
|
copy: filters.byCode(".copy("),
|
||||||
SUPPORTS_COPY: x => typeof x === "boolean",
|
SUPPORTS_COPY: x => typeof x === "boolean",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user