diff --git a/.eslintrc.json b/.eslintrc.json
index c422d22f..42b0849e 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -3,7 +3,8 @@
"parser": "@typescript-eslint/parser",
"ignorePatterns": [
"dist",
- "browser"
+ "browser",
+ "packages/vencord-types"
],
"plugins": [
"@typescript-eslint",
diff --git a/browser/VencordNativeStub.ts b/browser/VencordNativeStub.ts
index ea3d1cb5..5a874050 100644
--- a/browser/VencordNativeStub.ts
+++ b/browser/VencordNativeStub.ts
@@ -19,8 +19,8 @@
///
///
-import monacoHtmlLocal from "~fileContent/monacoWin.html";
-import monacoHtmlCdn from "~fileContent/../src/main/monacoWin.html";
+import monacoHtmlLocal from "file://monacoWin.html?minify";
+import monacoHtmlCdn from "file://../src/main/monacoWin.html?minify";
import * as DataStore from "../src/api/DataStore";
import { debounce } from "../src/utils";
import { EXTENSION_BASE_URL } from "../src/utils/web-metadata";
diff --git a/package.json b/package.json
index 132a2a7d..64761def 100644
--- a/package.json
+++ b/package.json
@@ -23,8 +23,9 @@
},
"scripts": {
"build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs",
+ "buildStandalone": "pnpm build --standalone",
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs",
- "watch": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs --watch",
+ "watch": "pnpm build --watch",
"generatePluginJson": "tsx scripts/generatePluginList.ts",
"generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types",
"inject": "node scripts/runInstaller.mjs",
@@ -32,7 +33,7 @@
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern src/userplugins",
"lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins",
"lint:fix": "pnpm lint --fix",
- "test": "pnpm build && pnpm lint:fix && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson",
+ "test": "pnpm buildStandalone && pnpm lint:fix && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson",
"testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc",
"testTsc": "tsc --noEmit"
},
@@ -71,6 +72,7 @@
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"highlight.js": "10.6.0",
+ "html-minifier-terser": "^7.2.0",
"moment": "^2.29.4",
"puppeteer-core": "^19.11.1",
"standalone-electron-types": "^1.0.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fcd0da45..997cbe3e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -109,6 +109,9 @@ importers:
highlight.js:
specifier: 10.6.0
version: 10.6.0
+ html-minifier-terser:
+ specifier: ^7.2.0
+ version: 7.2.0
moment:
specifier: ^2.29.4
version: 2.29.4
@@ -547,6 +550,43 @@ packages:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
dev: true
+ /@jridgewell/gen-mapping@0.3.5:
+ resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
+ engines: {node: '>=6.0.0'}
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.4.15
+ '@jridgewell/trace-mapping': 0.3.25
+ dev: true
+
+ /@jridgewell/resolve-uri@3.1.2:
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+ dev: true
+
+ /@jridgewell/set-array@1.2.1:
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+ dev: true
+
+ /@jridgewell/source-map@0.3.6:
+ resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+ dev: true
+
+ /@jridgewell/sourcemap-codec@1.4.15:
+ resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
+ dev: true
+
+ /@jridgewell/trace-mapping@0.3.25:
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.4.15
+ dev: true
+
/@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -1060,6 +1100,13 @@ packages:
engines: {node: '>=6'}
dev: true
+ /camel-case@4.1.2:
+ resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
+ dependencies:
+ pascal-case: 3.1.2
+ tslib: 2.6.2
+ dev: true
+
/camelcase-keys@6.2.2:
resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==}
engines: {node: '>=8'}
@@ -1104,6 +1151,13 @@ packages:
mitt: 3.0.0
dev: true
+ /clean-css@5.3.3:
+ resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
+ engines: {node: '>= 10.0'}
+ dependencies:
+ source-map: 0.6.1
+ dev: true
+
/cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
@@ -1145,6 +1199,15 @@ packages:
delayed-stream: 1.0.0
dev: false
+ /commander@10.0.1:
+ resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
+ engines: {node: '>=14'}
+ dev: true
+
+ /commander@2.20.3:
+ resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+ dev: true
+
/concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
@@ -1261,6 +1324,13 @@ packages:
esutils: 2.0.3
dev: true
+ /dot-case@3.0.4:
+ resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
+ dependencies:
+ no-case: 3.0.4
+ tslib: 2.6.2
+ dev: true
+
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true
@@ -1271,6 +1341,11 @@ packages:
once: 1.4.0
dev: true
+ /entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+ dev: true
+
/error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
dependencies:
@@ -1971,6 +2046,20 @@ packages:
lru-cache: 6.0.0
dev: true
+ /html-minifier-terser@7.2.0:
+ resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==}
+ engines: {node: ^14.13.1 || >=16.0.0}
+ hasBin: true
+ dependencies:
+ camel-case: 4.1.2
+ clean-css: 5.3.3
+ commander: 10.0.1
+ entities: 4.5.0
+ param-case: 3.0.4
+ relateurl: 0.2.7
+ terser: 5.31.0
+ dev: true
+
/html-tags@3.3.1:
resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
engines: {node: '>=8'}
@@ -2179,6 +2268,12 @@ packages:
js-tokens: 4.0.0
dev: true
+ /lower-case@2.0.2:
+ resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
+ dependencies:
+ tslib: 2.6.2
+ dev: true
+
/lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
@@ -2310,6 +2405,13 @@ packages:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true
+ /no-case@3.0.4:
+ resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
+ dependencies:
+ lower-case: 2.0.2
+ tslib: 2.6.2
+ dev: true
+
/node-fetch@2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0}
@@ -2401,6 +2503,13 @@ packages:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
dev: true
+ /param-case@3.0.4:
+ resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
+ dependencies:
+ dot-case: 3.0.4
+ tslib: 2.6.2
+ dev: true
+
/parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@@ -2418,6 +2527,13 @@ packages:
lines-and-columns: 1.2.4
dev: true
+ /pascal-case@3.1.2:
+ resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
+ dependencies:
+ no-case: 3.0.4
+ tslib: 2.6.2
+ dev: true
+
/path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -2613,6 +2729,11 @@ packages:
strip-indent: 3.0.0
dev: true
+ /relateurl@0.2.7:
+ resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==}
+ engines: {node: '>= 0.10'}
+ dev: true
+
/require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -2940,6 +3061,17 @@ packages:
readable-stream: 3.6.2
dev: true
+ /terser@5.31.0:
+ resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ '@jridgewell/source-map': 0.3.6
+ acorn: 8.10.0
+ commander: 2.20.3
+ source-map-support: 0.5.21
+ dev: true
+
/text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
@@ -2983,7 +3115,6 @@ packages:
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
dev: true
- optional: true
/tsutils@3.21.0(typescript@5.4.5):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
@@ -3238,7 +3369,3 @@ packages:
name: gifenc
version: 1.0.3
dev: false
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs
index d4a43d68..f0f6e58d 100644
--- a/scripts/build/common.mjs
+++ b/scripts/build/common.mjs
@@ -20,8 +20,10 @@ import "../suppressExperimentalWarnings.js";
import "../checkNodeVersion.js";
import { exec, execSync } from "child_process";
+import esbuild from "esbuild";
import { constants as FsConstants, readFileSync } from "fs";
import { access, readdir, readFile } from "fs/promises";
+import { minify as minifyHtml } from "html-minifier-terser";
import { join, relative } from "path";
import { promisify } from "util";
@@ -161,21 +163,60 @@ export const gitRemotePlugin = {
/**
* @type {import("esbuild").Plugin}
*/
-export const fileIncludePlugin = {
- name: "file-include-plugin",
+export const fileUrlPlugin = {
+ name: "file-uri-plugin",
setup: build => {
- const filter = /^~fileContent\/.+$/;
+ const filter = /^file:\/\/.+$/;
build.onResolve({ filter }, args => ({
- namespace: "include-file",
+ namespace: "file-uri",
path: args.path,
pluginData: {
- path: join(args.resolveDir, args.path.slice("include-file/".length))
+ uri: args.path,
+ path: join(args.resolveDir, args.path.slice("file://".length).split("?")[0])
}
}));
- build.onLoad({ filter, namespace: "include-file" }, async ({ pluginData: { path } }) => {
- const [name, format] = path.split(";");
+ build.onLoad({ filter, namespace: "file-uri" }, async ({ pluginData: { path, uri } }) => {
+ const { searchParams } = new URL(uri);
+ const base64 = searchParams.has("base64");
+ const minify = isStandalone === "true" && searchParams.has("minify");
+ const noTrim = searchParams.get("trim") === "false";
+
+ const encoding = base64 ? "base64" : "utf-8";
+
+ let content;
+ if (!minify) {
+ content = await readFile(path, encoding);
+ if (!noTrim) content = content.trimEnd();
+ } else {
+ if (path.endsWith(".html")) {
+ content = await minifyHtml(await readFile(path, "utf-8"), {
+ collapseWhitespace: true,
+ removeComments: true,
+ minifyCSS: true,
+ minifyJS: true,
+ removeEmptyAttributes: true,
+ removeRedundantAttributes: true,
+ removeScriptTypeAttributes: true,
+ removeStyleLinkTypeAttributes: true,
+ useShortDoctype: true
+ });
+ } else if (/[mc]?[jt]sx?$/.test(path)) {
+ const res = await esbuild.build({
+ entryPoints: [path],
+ write: false,
+ minify: true
+ });
+ content = res.outputFiles[0].text;
+ } else {
+ throw new Error(`Don't know how to minify file type: ${path}`);
+ }
+
+ if (base64)
+ content = Buffer.from(content).toString("base64");
+ }
+
return {
- contents: `export default ${JSON.stringify(await readFile(name, format ?? "utf-8"))}`
+ contents: `export default ${JSON.stringify(content)}`
};
});
}
@@ -217,7 +258,7 @@ export const commonOpts = {
sourcemap: watch ? "inline" : "",
legalComments: "linked",
banner,
- plugins: [fileIncludePlugin, gitHashPlugin, gitRemotePlugin, stylePlugin],
+ plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin],
external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"],
inject: ["./scripts/build/inject/react.mjs"],
jsxFactory: "VencordCreateElement",
diff --git a/src/main/ipcMain.ts b/src/main/ipcMain.ts
index 6a83c5ec..b7e5f3d2 100644
--- a/src/main/ipcMain.ts
+++ b/src/main/ipcMain.ts
@@ -23,12 +23,11 @@ import "./settings";
import { debounce } from "@shared/debounce";
import { IpcEvents } from "@shared/IpcEvents";
import { BrowserWindow, ipcMain, shell, systemPreferences } from "electron";
+import monacoHtml from "file://monacoWin.html?minify&base64";
import { FSWatcher, mkdirSync, watch, writeFileSync } from "fs";
import { open, readdir, readFile } from "fs/promises";
import { join, normalize } from "path";
-import monacoHtml from "~fileContent/monacoWin.html;base64";
-
import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, THEMES_DIR } from "./utils/constants";
import { makeLinksOpenExternally } from "./utils/externalLinks";
diff --git a/src/modules.d.ts b/src/modules.d.ts
index 48979925..83a512b0 100644
--- a/src/modules.d.ts
+++ b/src/modules.d.ts
@@ -38,7 +38,7 @@ declare module "~git-remote" {
export default remote;
}
-declare module "~fileContent/*" {
+declare module "file://*" {
const content: string;
export default content;
}
diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx
index aa0bddcc..b596018b 100644
--- a/src/plugins/dearrow/index.tsx
+++ b/src/plugins/dearrow/index.tsx
@@ -182,8 +182,8 @@ export default definePlugin({
// add dearrow button
{
- match: /children:\[(?=null!=\i\?\i\.renderSuppressButton)/,
- replace: "children:[$self.renderButton(this),",
+ match: /children:\[(?=null!=\i\?(\i)\.renderSuppressButton)/,
+ replace: "children:[$self.renderButton($1),",
predicate: () => !settings.store.hideButton
}
]
diff --git a/src/plugins/dontRoundMyTimestamps/index.ts b/src/plugins/dontRoundMyTimestamps/index.ts
new file mode 100644
index 00000000..4c432c73
--- /dev/null
+++ b/src/plugins/dontRoundMyTimestamps/index.ts
@@ -0,0 +1,35 @@
+/*
+ * 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 .
+*/
+
+import { Devs } from "@utils/constants";
+import definePlugin from "@utils/types";
+import { moment } from "@webpack/common";
+
+export default definePlugin({
+ name: "DontRoundMyTimestamps",
+ authors: [Devs.Lexi],
+ description: "Always rounds relative timestamps down, so 7.6y becomes 7y instead of 8y",
+
+ start() {
+ moment.relativeTimeRounding(Math.floor);
+ },
+
+ stop() {
+ moment.relativeTimeRounding(Math.round);
+ }
+});
diff --git a/src/plugins/imageZoom/components/Magnifier.tsx b/src/plugins/imageZoom/components/Magnifier.tsx
index aadd0903..585026d6 100644
--- a/src/plugins/imageZoom/components/Magnifier.tsx
+++ b/src/plugins/imageZoom/components/Magnifier.tsx
@@ -67,15 +67,18 @@ export const Magnifier = ErrorBoundary.wrap(({ instance, size: i
}
};
const syncVideos = () => {
- currentVideoElementRef.current!.currentTime = originalVideoElementRef.current!.currentTime;
+ if (currentVideoElementRef.current && originalVideoElementRef.current)
+ currentVideoElementRef.current.currentTime = originalVideoElementRef.current.currentTime;
};
const updateMousePosition = (e: MouseEvent) => {
+ if (!element.current) return;
+
if (instance.state.mouseOver && instance.state.mouseDown) {
const offset = size.current / 2;
const pos = { x: e.pageX, y: e.pageY };
- const x = -((pos.x - element.current!.getBoundingClientRect().left) * zoom.current - offset);
- const y = -((pos.y - element.current!.getBoundingClientRect().top) * zoom.current - offset);
+ const x = -((pos.x - element.current.getBoundingClientRect().left) * zoom.current - offset);
+ const y = -((pos.y - element.current.getBoundingClientRect().top) * zoom.current - offset);
setLensPosition({ x: e.x - offset, y: e.y - offset });
setImagePosition({ x, y });
setOpacity(1);
@@ -184,6 +187,7 @@ export const Magnifier = ErrorBoundary.wrap(({ instance, size: i
src={originalVideoElementRef.current?.src ?? instance.props.src}
autoPlay
loop
+ muted
/>
) : (
\"Aqq&cgg-\x7F ugoh%rom)e\x7Fhdpp%$",
- 'Tnfb}"u\'~`nno!kp$vvhfzeyee"a}%Tfam*Xh`fls%Jboldos-"lj`&hn)~ce!`jcbct|)gdbhnf$wikm$zgaxkmc%afely+og"144?\'ign+iu%p$qisiefr gpfa$',
- "Ndtfv%ahfgk+ghtf$|ir(|z' Oguaw&`ggdj mgw$|ir(me|n",
- "(!ͣ³$͙ʐ'ͩ¹#",
- "(ネ◗ロ◑,マ-2ャユ✬",
- "Ynw#hjil(ze+psgwp|&sgmkr!",
- "Tikmolh`(fl+a!dvjk\x7F'y|e\x7Fe/,-",
- "3/3750?5><9>885:7",
- "mdmt",
- "Wdn`khc+(oxbeof",
- 'Ig"zkp*\'g{*xolglj`&~g|*gowg/$mgt(Eclm`.#ticf{l*xed"wl`&Kangj igbhqn\'d`dn `v#lqrw{3%$bhv-h|)kangj_imwhlhb',
- "Tscmw%Tnoa~x",
- "I‘f#npus(ec`e!vl$lhsm{`ncu\"ekw&f(defeov-$Rnf|)sdu‘pf$wcam{ceg!vl$du'D`d~x-\"jw%oi(okht-\"DJP)Kags,!mq$du'A‐|n sg`akrkq)~jkdl#pj&diefbnf\"jp)&@F\\*{ltq#Hlhrp'",
- "Ynw$v`&cg`dl fml`%rhlhs*",
- "Dnl$p%qhz{s' hv$w%hh|aceg!;#gpvt(fl+cndea`&dg|fon&v#wjjqm(",
- "\ud83d)pft`gs(ec`e!13$qojmz#",
- "a!njcmr'ide~nu\"lb%rheoedldpz$lu'gbkr",
- "dn\"zkp&kgo4",
- "hnpqkw",
- "sn\"fau",
- "Sn\"tmqnh}}*musvkaw&flf&+ldv$w%lr{}*aulr#vlao|)cetn\"jp$",
- "Dxkmc%ot(hhxomwwai'{hln",
- "hd{#}js&(pe~'sg#gprb(3#\"",
- "hd{b${",
- "<;vqkijbq33271:56<3799?24944:",
- "Thof$lu'ofdn,!qsefc'az*bnrcma+&Om{o+iu\"`khct$)bnrd\"bcdoi&",
- "snofplkb{)c'r\"lod'|f*aurv#cpno`abchijklmno",
- "Wdn`khc'|f*eghl{%"
-];
+const presetQuotes = presetQuotesText.split("\n").map(quote => /^\s*[^#\s]/.test(quote) && quote.trim()).filter(Boolean) as string[];
+const noQuotesQuote = "Did you really disable all loading quotes? What a buffoon you are...";
const settings = definePluginSettings({
replaceEvents: {
- description: "Replace Event Quotes too",
+ description: "Should this plugin also apply during events with special event themed quotes? (e.g. Halloween)",
type: OptionType.BOOLEAN,
default: true
- }
+ },
+ enablePluginPresetQuotes: {
+ description: "Enable the quotes preset by this plugin",
+ type: OptionType.BOOLEAN,
+ default: true
+ },
+ enableDiscordPresetQuotes: {
+ description: "Enable Discord's preset quotes (including event quotes, during events)",
+ type: OptionType.BOOLEAN,
+ default: false
+ },
+ additionalQuotes: {
+ description: "Additional custom quotes to possibly appear, separated by the below delimiter",
+ type: OptionType.STRING,
+ default: "",
+ },
+ additionalQuotesDelimiter: {
+ description: "Delimiter for additional quotes",
+ type: OptionType.STRING,
+ default: "|",
+ },
});
export default definePlugin({
name: "LoadingQuotes",
description: "Replace Discords loading quotes",
- authors: [Devs.Ven, Devs.KraXen72],
+ authors: [Devs.Ven, Devs.KraXen72, Devs.UlyssesZhan],
settings,
patches: [
{
- find: ".LOADING_DID_YOU_KNOW}",
+ find: ".LOADING_DID_YOU_KNOW",
replacement: [
{
- match: /"_loadingText",function\(\)\{/,
- replace: "$&return $self.quote;",
+ match: /"_loadingText".+?(?=(\i)\[.{0,10}\.random)/,
+ replace: "$&$self.mutateQuotes($1),"
},
{
- match: /"_eventLoadingText",function\(\)\{/,
- replace: "$&return $self.quote;",
+ match: /"_eventLoadingText".+?(?=(\i)\[.{0,10}\.random)/,
+ replace: "$&$self.mutateQuotes($1),",
predicate: () => settings.store.replaceEvents
}
- ],
+ ]
},
],
- xor(quote: string) {
- const key = "read if cute";
- const codes = Array.from(quote, (s, i) => s.charCodeAt(0) ^ (i % key.length));
- return String.fromCharCode(...codes);
- },
+ mutateQuotes(quotes: string[]) {
+ try {
+ const { enableDiscordPresetQuotes, additionalQuotes, additionalQuotesDelimiter, enablePluginPresetQuotes } = settings.store;
- get quote() {
- return this.xor(quotes[Math.floor(Math.random() * quotes.length)]);
+ if (!enableDiscordPresetQuotes)
+ quotes.length = 0;
+
+
+ if (enablePluginPresetQuotes)
+ quotes.push(...presetQuotes);
+
+ quotes.push(...additionalQuotes.split(additionalQuotesDelimiter).filter(Boolean));
+
+ if (!quotes.length)
+ quotes.push(noQuotesQuote);
+ } catch (e) {
+ new Logger("LoadingQuotes").error("Failed to mutate quotes", e);
+ }
}
});
diff --git a/src/plugins/loadingQuotes/quotes.txt b/src/plugins/loadingQuotes/quotes.txt
new file mode 100644
index 00000000..cfb01350
--- /dev/null
+++ b/src/plugins/loadingQuotes/quotes.txt
@@ -0,0 +1,37 @@
+# Blank lines and lines starting with "#" are ignored
+
+Explode
+Read if cute
+Have a nice day!
+Starting Lightcord...
+Loading 0BDFDB.plugin.js...
+Installing BetterDiscord...
+h
+shhhhh did you know that you're my favourite user? But don't tell the others!!
+Today's video is sponsored by Raid Shadow Legends, one of the biggest mobile role-playing games of 2019 and it's totally free!
+Never gonna give you up, Never gonna let you down
+( ͡° ͜ʖ ͡°)
+(ノ◕ヮ◕)ノ*:・゚✧
+You look so pretty today!
+Thinking of a funny quote...
+3.141592653589793
+meow
+Welcome, friend
+If you, or someone you love, has Ligma, please see the Ligma health line at https://bit.ly/ligma_hotline
+Trans Rights
+I’d just like to interject for a moment. What you’re refering to as Linux, is in fact, GNU/Linux, or as I’ve recently taken to calling it, GNU plus Linux.
+You're doing good today!
+Don't worry, it's nothing 9 cups of coffee couldn't solve!
+�(repeat like 30 times)
+a light amount of tomfoolery is okay
+do you love?
+horror
+so eepy
+So without further ado, let's just jump right into it!
+Dying is absolutely safe
+hey you! you're cute :))
+heya ~
+<:trolley:997086295010594867>
+Time is gone, space is insane. Here it comes, here again.
+sometimes it's okay to just guhhhhhhhhhhhhhh
+Welcome to nginx!
diff --git a/src/plugins/maskedLinkPaste/index.ts b/src/plugins/maskedLinkPaste/index.ts
new file mode 100644
index 00000000..dc868992
--- /dev/null
+++ b/src/plugins/maskedLinkPaste/index.ts
@@ -0,0 +1,36 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2023 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { Devs } from "@utils/constants.js";
+import definePlugin from "@utils/types";
+import { findByPropsLazy } from "@webpack";
+
+const linkRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
+
+const { SlateTransforms } = findByPropsLazy("SlateTransforms");
+
+export default definePlugin({
+ name: "MaskedLinkPaste",
+ authors: [Devs.TheSun],
+ description: "Pasting a link while having text selected will paste a hyperlink",
+ patches: [{
+ find: ".selection,preventEmojiSurrogates:",
+ replacement: {
+ match: /(?<=SlateTransforms.delete.{0,50})(\i)\.insertText\((\i)\)/,
+ replace: "$self.handlePaste($1, $2, () => $&)"
+ }
+ }],
+
+ handlePaste(editor, content: string, originalBehavior: () => void) {
+ if (content && linkRegex.test(content) && editor.operations?.[0]?.type === "remove_text") {
+ SlateTransforms.insertText(
+ editor,
+ `[${editor.operations[0].text}](${content})`
+ );
+ }
+ else originalBehavior();
+ }
+});
diff --git a/src/plugins/messageLogger/deleteStyleText.css b/src/plugins/messageLogger/deleteStyleText.css
index 3477ef22..a4e9a93c 100644
--- a/src/plugins/messageLogger/deleteStyleText.css
+++ b/src/plugins/messageLogger/deleteStyleText.css
@@ -3,6 +3,11 @@
color: var(--status-danger, #f04747) !important;
}
+/* Markdown title highlighting */
+.messagelogger-deleted [class*="contents"] :is(h1, h2, h3) {
+ color: var(--status-danger, #f04747) !important;
+}
+
/* Bot "thinking" text highlighting */
.messagelogger-deleted [class*="colorStandard"] {
color: var(--status-danger, #f04747) !important;
diff --git a/src/plugins/seeSummaries/index.tsx b/src/plugins/seeSummaries/index.tsx
index 04a25198..68c4f4a6 100644
--- a/src/plugins/seeSummaries/index.tsx
+++ b/src/plugins/seeSummaries/index.tsx
@@ -87,6 +87,7 @@ export default definePlugin({
async start() {
await DataStore.update("summaries-data", summaries => {
+ summaries ??= {};
for (const key of Object.keys(summaries)) {
for (let i = summaries[key].length - 1; i >= 0; i--) {
if (summaries[key][i].time < Date.now() - 1000 * 60 * 60 * 24 * settings.store.summaryExpiryThresholdDays) {
diff --git a/src/plugins/shikiCodeblocks.desktop/index.ts b/src/plugins/shikiCodeblocks.desktop/index.ts
index 63d3cced..3354d145 100644
--- a/src/plugins/shikiCodeblocks.desktop/index.ts
+++ b/src/plugins/shikiCodeblocks.desktop/index.ts
@@ -21,8 +21,7 @@ import "./shiki.css";
import { enableStyle } from "@api/Styles";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
-
-import previewExampleText from "~fileContent/previewExample.tsx";
+import previewExampleText from "file://previewExample.tsx";
import { shiki } from "./api/shiki";
import { createHighlighter } from "./components/Highlighter";
diff --git a/src/plugins/watchTogetherAdblock.desktop/adguard.js b/src/plugins/watchTogetherAdblock.desktop/adguard.js
new file mode 100644
index 00000000..945f76bd
--- /dev/null
+++ b/src/plugins/watchTogetherAdblock.desktop/adguard.js
@@ -0,0 +1,262 @@
+/* eslint-disable */
+
+/**
+ * This file is part of AdGuard's Block YouTube Ads (https://github.com/AdguardTeam/BlockYouTubeAdsShortcut).
+ *
+ * Copyright (C) AdGuard Team
+ *
+ * AdGuard's Block YouTube Ads 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.
+ *
+ * AdGuard's Block YouTube Ads 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 AdGuard's Block YouTube Ads. If not, see .
+ */
+
+const LOGO_ID = "block-youtube-ads-logo";
+const hiddenCSS = [
+ "#__ffYoutube1",
+ "#__ffYoutube2",
+ "#__ffYoutube3",
+ "#__ffYoutube4",
+ "#feed-pyv-container",
+ "#feedmodule-PRO",
+ "#homepage-chrome-side-promo",
+ "#merch-shelf",
+ "#offer-module",
+ '#pla-shelf > ytd-pla-shelf-renderer[class="style-scope ytd-watch"]',
+ "#pla-shelf",
+ "#premium-yva",
+ "#promo-info",
+ "#promo-list",
+ "#promotion-shelf",
+ "#related > ytd-watch-next-secondary-results-renderer > #items > ytd-compact-promoted-video-renderer.ytd-watch-next-secondary-results-renderer",
+ "#search-pva",
+ "#shelf-pyv-container",
+ "#video-masthead",
+ "#watch-branded-actions",
+ "#watch-buy-urls",
+ "#watch-channel-brand-div",
+ "#watch7-branded-banner",
+ "#YtKevlarVisibilityIdentifier",
+ "#YtSparklesVisibilityIdentifier",
+ ".carousel-offer-url-container",
+ ".companion-ad-container",
+ ".GoogleActiveViewElement",
+ '.list-view[style="margin: 7px 0pt;"]',
+ ".promoted-sparkles-text-search-root-container",
+ ".promoted-videos",
+ ".searchView.list-view",
+ ".sparkles-light-cta",
+ ".watch-extra-info-column",
+ ".watch-extra-info-right",
+ ".ytd-carousel-ad-renderer",
+ ".ytd-compact-promoted-video-renderer",
+ ".ytd-companion-slot-renderer",
+ ".ytd-merch-shelf-renderer",
+ ".ytd-player-legacy-desktop-watch-ads-renderer",
+ ".ytd-promoted-sparkles-text-search-renderer",
+ ".ytd-promoted-video-renderer",
+ ".ytd-search-pyv-renderer",
+ ".ytd-video-masthead-ad-v3-renderer",
+ ".ytp-ad-action-interstitial-background-container",
+ ".ytp-ad-action-interstitial-slot",
+ ".ytp-ad-image-overlay",
+ ".ytp-ad-overlay-container",
+ ".ytp-ad-progress",
+ ".ytp-ad-progress-list",
+ '[class*="ytd-display-ad-"]',
+ '[layout*="display-ad-"]',
+ 'a[href^="http://www.youtube.com/cthru?"]',
+ 'a[href^="https://www.youtube.com/cthru?"]',
+ "ytd-action-companion-ad-renderer",
+ "ytd-banner-promo-renderer",
+ "ytd-compact-promoted-video-renderer",
+ "ytd-companion-slot-renderer",
+ "ytd-display-ad-renderer",
+ "ytd-promoted-sparkles-text-search-renderer",
+ "ytd-promoted-sparkles-web-renderer",
+ "ytd-search-pyv-renderer",
+ "ytd-single-option-survey-renderer",
+ "ytd-video-masthead-ad-advertiser-info-renderer",
+ "ytd-video-masthead-ad-v3-renderer",
+ "YTM-PROMOTED-VIDEO-RENDERER",
+];
+/**
+* Adds CSS to the page
+*/
+const hideElements = () => {
+ const selectors = hiddenCSS;
+ if (!selectors) {
+ return;
+ }
+ const rule = selectors.join(", ") + " { display: none!important; }";
+ const style = document.createElement("style");
+ style.innerHTML = rule;
+ document.head.appendChild(style);
+};
+/**
+* Calls the "callback" function on every DOM change, but not for the tracked events
+* @param {Function} callback callback function
+*/
+const observeDomChanges = callback => {
+ const domMutationObserver = new MutationObserver(mutations => {
+ callback(mutations);
+ });
+ domMutationObserver.observe(document.documentElement, {
+ childList: true,
+ subtree: true,
+ });
+};
+/**
+* This function is supposed to be called on every DOM change
+*/
+const hideDynamicAds = () => {
+ const elements = document.querySelectorAll("#contents > ytd-rich-item-renderer ytd-display-ad-renderer");
+ if (elements.length === 0) {
+ return;
+ }
+ elements.forEach(el => {
+ if (el.parentNode && el.parentNode.parentNode) {
+ const parent = el.parentNode.parentNode;
+ if (parent.localName === "ytd-rich-item-renderer") {
+ parent.style.display = "none";
+ }
+ }
+ });
+};
+/**
+* This function checks if the video ads are currently running
+* and auto-clicks the skip button.
+*/
+const autoSkipAds = () => {
+ // If there's a video that plays the ad at this moment, scroll this ad
+ if (document.querySelector(".ad-showing")) {
+ const video = document.querySelector("video");
+ if (video && video.duration) {
+ video.currentTime = video.duration;
+ // Skip button should appear after that,
+ // now simply click it automatically
+ setTimeout(() => {
+ const skipBtn = document.querySelector("button.ytp-ad-skip-button");
+ if (skipBtn) {
+ skipBtn.click();
+ }
+ }, 100);
+ }
+ }
+};
+/**
+* This function overrides a property on the specified object.
+*
+* @param {object} obj object to look for properties in
+* @param {string} propertyName property to override
+* @param {*} overrideValue value to set
+*/
+const overrideObject = (obj, propertyName, overrideValue) => {
+ if (!obj) {
+ return false;
+ }
+ let overriden = false;
+ for (const key in obj) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (obj.hasOwnProperty(key) && key === propertyName) {
+ obj[key] = overrideValue;
+ overriden = true;
+ // eslint-disable-next-line no-prototype-builtins
+ } else if (obj.hasOwnProperty(key) && typeof obj[key] === "object") {
+ if (overrideObject(obj[key], propertyName, overrideValue)) {
+ overriden = true;
+ }
+ }
+ }
+ return overriden;
+};
+/**
+* Overrides JSON.parse and Response.json functions.
+* Examines these functions arguments, looks for properties with the specified name there
+* and if it exists, changes it's value to what was specified.
+*
+* @param {string} propertyName name of the property
+* @param {*} overrideValue new value for the property
+*/
+const jsonOverride = (propertyName, overrideValue) => {
+ const nativeJSONParse = JSON.parse;
+ JSON.parse = (...args) => {
+ const obj = nativeJSONParse.apply(this, args);
+ // Override it's props and return back to the caller
+ overrideObject(obj, propertyName, overrideValue);
+ return obj;
+ };
+ // Override Response.prototype.json
+ const nativeResponseJson = Response.prototype.json;
+ Response.prototype.json = new Proxy(nativeResponseJson, {
+ apply(...args) {
+ // Call the target function, get the original Promise
+ const promise = Reflect.apply(...args);
+ // Create a new one and override the JSON inside
+ return new Promise((resolve, reject) => {
+ promise.then(data => {
+ overrideObject(data, propertyName, overrideValue);
+ resolve(data);
+ }).catch(error => reject(error));
+ });
+ },
+ });
+};
+const addAdGuardLogoStyle = () => { };
+const addAdGuardLogo = () => {
+ if (document.getElementById(LOGO_ID)) {
+ return;
+ }
+ const logo = document.createElement("span");
+ logo.innerHTML = "__logo_text__";
+ logo.setAttribute("id", LOGO_ID);
+ if (window.location.hostname === "m.youtube.com") {
+ const btn = document.querySelector("header.mobile-topbar-header > button");
+ if (btn) {
+ btn.parentNode?.insertBefore(logo, btn.nextSibling);
+ addAdGuardLogoStyle();
+ }
+ } else if (window.location.hostname === "www.youtube.com") {
+ const code = document.getElementById("country-code");
+ if (code) {
+ code.innerHTML = "";
+ code.appendChild(logo);
+ addAdGuardLogoStyle();
+ }
+ } else if (window.location.hostname === "music.youtube.com") {
+ const el = document.querySelector(".ytmusic-nav-bar#left-content");
+ if (el) {
+ el.appendChild(logo);
+ addAdGuardLogoStyle();
+ }
+ } else if (window.location.hostname === "www.youtube-nocookie.com") {
+ const code = document.querySelector("#yt-masthead #logo-container .content-region");
+ if (code) {
+ code.innerHTML = "";
+ code.appendChild(logo);
+ addAdGuardLogoStyle();
+ }
+ }
+};
+// Removes ads metadata from YouTube XHR requests
+jsonOverride("adPlacements", []);
+jsonOverride("playerAds", []);
+// Applies CSS that hides YouTube ad elements
+hideElements();
+// Some changes should be re-evaluated on every page change
+addAdGuardLogo();
+hideDynamicAds();
+autoSkipAds();
+observeDomChanges(() => {
+ addAdGuardLogo();
+ hideDynamicAds();
+ autoSkipAds();
+});
diff --git a/src/plugins/watchTogetherAdblock.desktop/index.ts b/src/plugins/watchTogetherAdblock.desktop/index.ts
new file mode 100644
index 00000000..2dbc13d4
--- /dev/null
+++ b/src/plugins/watchTogetherAdblock.desktop/index.ts
@@ -0,0 +1,15 @@
+/*
+ * 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";
+
+// The entire code of this plugin can be found in native.ts
+export default definePlugin({
+ name: "WatchTogetherAdblock",
+ description: "Block ads in the YouTube WatchTogether activity via AdGuard",
+ authors: [Devs.ImLvna],
+});
diff --git a/src/plugins/watchTogetherAdblock.desktop/native.ts b/src/plugins/watchTogetherAdblock.desktop/native.ts
new file mode 100644
index 00000000..c4106c34
--- /dev/null
+++ b/src/plugins/watchTogetherAdblock.desktop/native.ts
@@ -0,0 +1,21 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2023 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { RendererSettings } from "@main/settings";
+import { app } from "electron";
+import adguard from "file://adguard.js?minify";
+
+app.on("browser-window-created", (_, win) => {
+ win.webContents.on("frame-created", (_, { frame }) => {
+ frame.once("dom-ready", () => {
+ if (frame.url.includes("discordsays") && frame.url.includes("youtube.com")) {
+ if (!RendererSettings.store.plugins?.WatchTogetherAdblock?.enabled) return;
+
+ frame.executeJavaScript(adguard);
+ }
+ });
+ });
+});
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 95c2e640..12a72d2d 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -188,7 +188,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({
id: 296776625432035328n,
},
TheSun: {
- name: "ActuallyTheSun",
+ name: "sunnie",
id: 406028027768733696n
},
axyie: {
@@ -404,6 +404,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "maisy",
id: 257109471589957632n,
},
+ Lexi: {
+ name: "Lexi",
+ id: 506101469787717658n
+ },
Mopi: {
name: "Mopi",
id: 1022189106614243350n