refactor: fix circular dependency issues & improve organisation (#3729)

Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
This commit is contained in:
V 2025-11-16 15:17:35 +01:00 committed by GitHub
parent 40fe13feda
commit 6afcce39dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
106 changed files with 1039 additions and 2273 deletions

View file

@ -19,14 +19,16 @@
/// <reference path="../src/modules.d.ts" />
/// <reference path="../src/globals.d.ts" />
// Be very careful with imports in this file to avoid circular dependency issues.
// Only import pure modules that don't import other parts of Vencord.
import monacoHtmlLocal from "file://monacoWin.html?minify";
import * as DataStore from "../src/api/DataStore";
import { debounce, localStorage } from "../src/utils";
import { EXTENSION_BASE_URL } from "../src/utils/web-metadata";
import { getTheme, Theme } from "../src/utils/discord";
import { getThemeInfo } from "../src/main/themes";
import { Settings } from "../src/Vencord";
import * as DataStore from "@api/DataStore";
import type { Settings } from "@api/Settings";
import { getThemeInfo } from "@main/themes";
import { debounce } from "@shared/debounce";
import { localStorage } from "@utils/localStorage";
import { getStylusWebStoreUrl } from "@utils/web";
import { EXTENSION_BASE_URL } from "@utils/web-metadata";
// listeners for ipc.on
const cssListeners = new Set<(css: string) => void>();
@ -90,6 +92,8 @@ window.VencordNative = {
return;
}
const { getTheme, Theme } = require("@utils/discord");
win.baseUrl = EXTENSION_BASE_URL;
win.setCss = setCssDebounced;
win.getCurrentCss = () => VencordNative.quickCss.get();

View file

@ -49,7 +49,7 @@
"@types/chrome": "^0.0.312",
"@types/diff": "^7.0.2",
"@types/lodash": "^4.17.14",
"@types/node": "^22.13.13",
"@types/node": "^24.10.1",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@types/yazl": "^2.4.5",
@ -74,7 +74,7 @@
"ts-pattern": "^5.6.0",
"tsx": "^4.19.3",
"type-fest": "^4.38.0",
"typescript": "^5.8.2",
"typescript": "^5.9.3",
"typescript-eslint": "^8.28.0",
"typescript-transform-paths": "^3.5.5",
"zip-local": "^0.3.5"

298
pnpm-lock.yaml generated
View file

@ -43,7 +43,7 @@ importers:
devDependencies:
'@stylistic/eslint-plugin':
specifier: ^4.2.0
version: 4.2.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
version: 4.2.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
'@types/chrome':
specifier: ^0.0.312
version: 0.0.312
@ -54,8 +54,8 @@ importers:
specifier: ^4.17.14
version: 4.17.15
'@types/node':
specifier: ^22.13.13
version: 22.13.13
specifier: ^24.10.1
version: 24.10.1
'@types/react':
specifier: ^19.0.10
version: 19.0.12
@ -79,7 +79,7 @@ importers:
version: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
eslint-import-resolver-alias:
specifier: ^1.1.2
version: 1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)))
version: 1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)))
eslint-plugin-path-alias:
specifier: 2.1.0
version: 2.1.0(patch_hash=87545cb13985b338c8fa2ea7b0a3c75c57ad7fbc81c56b38d6c9438329957727)(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))
@ -94,7 +94,7 @@ importers:
version: 12.1.1(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))
eslint-plugin-unused-imports:
specifier: ^4.1.4
version: 4.1.4(@typescript-eslint/eslint-plugin@8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))
version: 4.1.4(@typescript-eslint/eslint-plugin@8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))
highlight.js:
specifier: 11.11.1
version: 11.11.1
@ -112,10 +112,10 @@ importers:
version: 34.2.0
stylelint:
specifier: ^16.17.0
version: 16.17.0(typescript@5.8.2)
version: 16.17.0(typescript@5.9.3)
stylelint-config-standard:
specifier: ^37.0.0
version: 37.0.0(stylelint@16.17.0(typescript@5.8.2))
version: 37.0.0(stylelint@16.17.0(typescript@5.9.3))
ts-patch:
specifier: ^3.3.0
version: 3.3.0
@ -129,14 +129,14 @@ importers:
specifier: ^4.38.0
version: 4.38.0
typescript:
specifier: ^5.8.2
version: 5.8.2
specifier: ^5.9.3
version: 5.9.3
typescript-eslint:
specifier: ^8.28.0
version: 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
version: 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
typescript-transform-paths:
specifier: ^3.5.5
version: 3.5.5(typescript@5.8.2)
version: 3.5.5(typescript@5.9.3)
zip-local:
specifier: ^0.3.5
version: 0.3.5
@ -163,13 +163,13 @@ importers:
version: 22.13.13
'@types/react':
specifier: 18.3.1
version: 19.0.12
version: 18.3.1
'@types/react-dom':
specifier: 18.3.1
version: 18.3.1
'@vencord/discord-types':
specifier: ^1.0.0
version: 1.0.0(@types/react@19.0.12)
version: 1.0.0(@types/react@18.3.1)
highlight.js:
specifier: 11.11.1
version: 11.11.1
@ -529,6 +529,15 @@ packages:
'@types/node@22.13.13':
resolution: {integrity: sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==}
'@types/node@22.19.1':
resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==}
'@types/node@24.10.1':
resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==}
'@types/prop-types@15.7.15':
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
'@types/react-dom@18.3.1':
resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==}
@ -537,6 +546,9 @@ packages:
peerDependencies:
'@types/react': ^19.0.0
'@types/react@18.3.1':
resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==}
'@types/react@19.0.12':
resolution: {integrity: sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==}
@ -651,6 +663,10 @@ packages:
resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
engines: {node: '>= 0.4'}
array-includes@3.1.9:
resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
engines: {node: '>= 0.4'}
array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
@ -887,6 +903,9 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
csstype@3.2.1:
resolution: {integrity: sha512-98XGutrXoh75MlgLihlNxAGbUuFQc7l1cqcnEZlLNKc0UrVdPndgmaDmYTDDh929VS/eqTZV0rozmhu2qqT1/g==}
data-uri-to-buffer@6.0.2:
resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==}
engines: {node: '>= 14'}
@ -1002,6 +1021,10 @@ packages:
resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
engines: {node: '>= 0.4'}
es-abstract@1.24.0:
resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
@ -1057,8 +1080,8 @@ packages:
eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
eslint-module-utils@2.12.0:
resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==}
eslint-module-utils@2.12.1:
resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
@ -1560,6 +1583,10 @@ packages:
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'}
is-negative-zero@2.0.3:
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
engines: {node: '>= 0.4'}
is-number-object@1.1.1:
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'}
@ -2063,6 +2090,11 @@ packages:
engines: {node: '>= 0.4'}
hasBin: true
resolve@1.22.11:
resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
engines: {node: '>= 0.4'}
hasBin: true
resolve@2.0.0-next.5:
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
hasBin: true
@ -2207,6 +2239,10 @@ packages:
resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==}
engines: {node: '>=0.10.0'}
stop-iteration-iterator@1.1.0:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
streamx@2.22.0:
resolution: {integrity: sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==}
@ -2374,8 +2410,8 @@ packages:
peerDependencies:
typescript: '>=3.6.5'
typescript@5.8.2:
resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
@ -2386,6 +2422,12 @@ packages:
undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
union-value@1.0.1:
resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==}
engines: {node: '>=0.10.0'}
@ -2719,9 +2761,9 @@ snapshots:
'@rtsao/scc@1.1.0': {}
'@stylistic/eslint-plugin@4.2.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)':
'@stylistic/eslint-plugin@4.2.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
eslint-visitor-keys: 4.2.0
espree: 10.3.0
@ -2769,53 +2811,68 @@ snapshots:
dependencies:
undici-types: 6.20.0
'@types/node@22.19.1':
dependencies:
undici-types: 6.21.0
'@types/node@24.10.1':
dependencies:
undici-types: 7.16.0
'@types/prop-types@15.7.15': {}
'@types/react-dom@18.3.1':
dependencies:
'@types/react': 19.0.12
'@types/react': 18.3.1
'@types/react-dom@19.0.4(@types/react@19.0.12)':
dependencies:
'@types/react': 19.0.12
'@types/react@18.3.1':
dependencies:
'@types/prop-types': 15.7.15
csstype: 3.2.1
'@types/react@19.0.12':
dependencies:
csstype: 3.1.3
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 22.13.13
'@types/node': 24.10.1
optional: true
'@types/yazl@2.4.6':
dependencies:
'@types/node': 22.13.13
'@types/node': 24.10.1
'@typescript-eslint/eslint-plugin@8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)':
'@typescript-eslint/eslint-plugin@8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/parser': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.28.0
'@typescript-eslint/type-utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/type-utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
'@typescript-eslint/utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.28.0
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
graphemer: 1.4.0
ignore: 5.3.2
natural-compare: 1.4.0
ts-api-utils: 2.1.0(typescript@5.8.2)
typescript: 5.8.2
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)':
'@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.28.0
'@typescript-eslint/types': 8.28.0
'@typescript-eslint/typescript-estree': 8.28.0(typescript@5.8.2)
'@typescript-eslint/typescript-estree': 8.28.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.28.0
debug: 4.4.0
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
typescript: 5.8.2
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@ -2824,20 +2881,20 @@ snapshots:
'@typescript-eslint/types': 8.28.0
'@typescript-eslint/visitor-keys': 8.28.0
'@typescript-eslint/type-utils@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)':
'@typescript-eslint/type-utils@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/typescript-estree': 8.28.0(typescript@5.8.2)
'@typescript-eslint/utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/typescript-estree': 8.28.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
debug: 4.4.0
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
ts-api-utils: 2.1.0(typescript@5.8.2)
typescript: 5.8.2
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/types@8.28.0': {}
'@typescript-eslint/typescript-estree@8.28.0(typescript@5.8.2)':
'@typescript-eslint/typescript-estree@8.28.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.28.0
'@typescript-eslint/visitor-keys': 8.28.0
@ -2846,19 +2903,19 @@ snapshots:
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.7.1
ts-api-utils: 2.1.0(typescript@5.8.2)
typescript: 5.8.2
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)':
'@typescript-eslint/utils@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.5.1(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))
'@typescript-eslint/scope-manager': 8.28.0
'@typescript-eslint/types': 8.28.0
'@typescript-eslint/typescript-estree': 8.28.0(typescript@5.8.2)
'@typescript-eslint/typescript-estree': 8.28.0(typescript@5.9.3)
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
typescript: 5.8.2
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@ -2877,9 +2934,9 @@ snapshots:
vscode-oniguruma: 1.7.0
vscode-textmate: 5.2.0
'@vencord/discord-types@1.0.0(@types/react@19.0.12)':
'@vencord/discord-types@1.0.0(@types/react@18.3.1)':
dependencies:
'@types/react': 19.0.12
'@types/react': 18.3.1
moment: 2.30.1
type-fest: 4.41.0
@ -2931,6 +2988,17 @@ snapshots:
get-intrinsic: 1.3.0
is-string: 1.1.1
array-includes@3.1.9:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-abstract: 1.24.0
es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
is-string: 1.1.1
math-intrinsics: 1.1.0
array-union@2.1.0: {}
array-unique@0.3.2: {}
@ -2949,7 +3017,7 @@ snapshots:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-abstract: 1.23.9
es-abstract: 1.24.0
es-errors: 1.3.0
es-object-atoms: 1.1.1
es-shim-unscopables: 1.1.0
@ -3163,14 +3231,14 @@ snapshots:
copy-descriptor@0.1.1: {}
cosmiconfig@9.0.0(typescript@5.8.2):
cosmiconfig@9.0.0(typescript@5.9.3):
dependencies:
env-paths: 2.2.1
import-fresh: 3.3.1
js-yaml: 4.1.0
parse-json: 5.2.0
optionalDependencies:
typescript: 5.8.2
typescript: 5.9.3
cross-spawn@7.0.6:
dependencies:
@ -3189,6 +3257,8 @@ snapshots:
csstype@3.1.3: {}
csstype@3.2.1: {}
data-uri-to-buffer@6.0.2: {}
data-view-buffer@1.0.2:
@ -3347,6 +3417,63 @@ snapshots:
unbox-primitive: 1.1.0
which-typed-array: 1.1.19
es-abstract@1.24.0:
dependencies:
array-buffer-byte-length: 1.0.2
arraybuffer.prototype.slice: 1.0.4
available-typed-arrays: 1.0.7
call-bind: 1.0.8
call-bound: 1.0.4
data-view-buffer: 1.0.2
data-view-byte-length: 1.0.2
data-view-byte-offset: 1.0.1
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
es-set-tostringtag: 2.1.0
es-to-primitive: 1.3.0
function.prototype.name: 1.1.8
get-intrinsic: 1.3.0
get-proto: 1.0.1
get-symbol-description: 1.1.0
globalthis: 1.0.4
gopd: 1.2.0
has-property-descriptors: 1.0.2
has-proto: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
internal-slot: 1.1.0
is-array-buffer: 3.0.5
is-callable: 1.2.7
is-data-view: 1.0.2
is-negative-zero: 2.0.3
is-regex: 1.2.1
is-set: 2.0.3
is-shared-array-buffer: 1.0.4
is-string: 1.1.1
is-typed-array: 1.1.15
is-weakref: 1.1.1
math-intrinsics: 1.1.0
object-inspect: 1.13.4
object-keys: 1.1.1
object.assign: 4.1.7
own-keys: 1.0.1
regexp.prototype.flags: 1.5.4
safe-array-concat: 1.1.3
safe-push-apply: 1.0.0
safe-regex-test: 1.1.0
set-proto: 1.0.0
stop-iteration-iterator: 1.1.0
string.prototype.trim: 1.2.10
string.prototype.trimend: 1.0.9
string.prototype.trimstart: 1.0.8
typed-array-buffer: 1.0.3
typed-array-byte-length: 1.0.3
typed-array-byte-offset: 1.0.4
typed-array-length: 1.0.7
unbox-primitive: 1.1.0
which-typed-array: 1.1.19
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
@ -3431,32 +3558,32 @@ snapshots:
optionalDependencies:
source-map: 0.6.1
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))):
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))):
dependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))
eslint-import-resolver-node@0.3.9:
dependencies:
debug: 3.2.7
is-core-module: 2.16.1
resolve: 1.22.10
resolve: 1.22.11
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)):
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/parser': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
array-includes: 3.1.9
array.prototype.findlastindex: 1.2.6
array.prototype.flat: 1.3.3
array.prototype.flatmap: 1.3.3
@ -3464,7 +3591,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@ -3476,7 +3603,7 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/parser': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
@ -3521,11 +3648,11 @@ snapshots:
dependencies:
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)):
eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)):
dependencies:
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/eslint-plugin': 8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
eslint-scope@8.3.0:
dependencies:
@ -4020,6 +4147,8 @@ snapshots:
is-map@2.0.3: {}
is-negative-zero@2.0.3: {}
is-number-object@1.1.1:
dependencies:
call-bound: 1.0.4
@ -4307,7 +4436,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.23.9
es-abstract: 1.24.0
object.pick@1.3.0:
dependencies:
@ -4531,6 +4660,12 @@ snapshots:
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
resolve@1.22.11:
dependencies:
is-core-module: 2.16.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
resolve@2.0.0-next.5:
dependencies:
is-core-module: 2.16.1
@ -4702,13 +4837,18 @@ snapshots:
standalone-electron-types@34.2.0:
dependencies:
'@types/node': 22.13.13
'@types/node': 22.19.1
static-extend@0.1.2:
dependencies:
define-property: 0.2.5
object-copy: 0.1.0
stop-iteration-iterator@1.1.0:
dependencies:
es-errors: 1.3.0
internal-slot: 1.1.0
streamx@2.22.0:
dependencies:
fast-fifo: 1.3.2
@ -4774,16 +4914,16 @@ snapshots:
strip-json-comments@3.1.1: {}
stylelint-config-recommended@15.0.0(stylelint@16.17.0(typescript@5.8.2)):
stylelint-config-recommended@15.0.0(stylelint@16.17.0(typescript@5.9.3)):
dependencies:
stylelint: 16.17.0(typescript@5.8.2)
stylelint: 16.17.0(typescript@5.9.3)
stylelint-config-standard@37.0.0(stylelint@16.17.0(typescript@5.8.2)):
stylelint-config-standard@37.0.0(stylelint@16.17.0(typescript@5.9.3)):
dependencies:
stylelint: 16.17.0(typescript@5.8.2)
stylelint-config-recommended: 15.0.0(stylelint@16.17.0(typescript@5.8.2))
stylelint: 16.17.0(typescript@5.9.3)
stylelint-config-recommended: 15.0.0(stylelint@16.17.0(typescript@5.9.3))
stylelint@16.17.0(typescript@5.8.2):
stylelint@16.17.0(typescript@5.9.3):
dependencies:
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
'@csstools/css-tokenizer': 3.0.3
@ -4792,7 +4932,7 @@ snapshots:
'@dual-bundle/import-meta-resolve': 4.1.0
balanced-match: 2.0.0
colord: 2.9.3
cosmiconfig: 9.0.0(typescript@5.8.2)
cosmiconfig: 9.0.0(typescript@5.9.3)
css-functions-list: 3.2.3
css-tree: 3.1.0
debug: 4.4.0
@ -4890,9 +5030,9 @@ snapshots:
regex-not: 1.0.2
safe-regex: 1.1.0
ts-api-utils@2.1.0(typescript@5.8.2):
ts-api-utils@2.1.0(typescript@5.9.3):
dependencies:
typescript: 5.8.2
typescript: 5.9.3
ts-patch@3.3.0:
dependencies:
@ -4964,22 +5104,22 @@ snapshots:
typed-query-selector@2.12.0: {}
typescript-eslint@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2):
typescript-eslint@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3):
dependencies:
'@typescript-eslint/eslint-plugin': 8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/parser': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.8.2)
'@typescript-eslint/eslint-plugin': 8.28.0(@typescript-eslint/parser@8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3))(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
'@typescript-eslint/parser': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
'@typescript-eslint/utils': 8.28.0(eslint@9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215))(typescript@5.9.3)
eslint: 9.20.1(patch_hash=4f22e92770bf528b2448fbec0984b9c0761dd588ed0e83dcc41edfc9af711215)
typescript: 5.8.2
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
typescript-transform-paths@3.5.5(typescript@5.8.2):
typescript-transform-paths@3.5.5(typescript@5.9.3):
dependencies:
minimatch: 9.0.5
typescript: 5.8.2
typescript: 5.9.3
typescript@5.8.2: {}
typescript@5.9.3: {}
unbox-primitive@1.1.0:
dependencies:
@ -4990,6 +5130,10 @@ snapshots:
undici-types@6.20.0: {}
undici-types@6.21.0: {}
undici-types@7.16.0: {}
union-value@1.0.1:
dependencies:
arr-union: 3.1.0

View file

@ -20,34 +20,32 @@
import "~plugins";
export * as Api from "./api";
export * as Plugins from "./api/PluginManager";
export * as Components from "./components";
export * as Plugins from "./plugins";
export * as Util from "./utils";
export * as QuickCss from "./utils/quickCss";
export * as Updater from "./utils/updater";
export * as Webpack from "./webpack";
export * as WebpackPatcher from "./webpack/patchWebpack";
export { PlainSettings, Settings };
import "./utils/quickCss";
import "./webpack/patchWebpack";
import { addVencordUiStyles } from "@components/css";
import { openUpdaterModal } from "@components/settings/tabs/updater";
import { debounce } from "@shared/debounce";
import { IS_WINDOWS } from "@utils/constants";
import { createAndAppendStyle } from "@utils/css";
import { StartAt } from "@utils/types";
import { get as dsGet } from "./api/DataStore";
import { NotificationData, showNotification } from "./api/Notifications";
import { PlainSettings, Settings } from "./api/Settings";
import { patches, PMLogger, startAllPlugins } from "./plugins";
import { initPluginManager, PMLogger, startAllPlugins } from "./api/PluginManager";
import { PlainSettings, Settings, SettingsStore } from "./api/Settings";
import { getCloudSettings, putCloudSettings } from "./api/SettingsSync/cloudSync";
import { localStorage } from "./utils/localStorage";
import { relaunch } from "./utils/native";
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
import { onceReady } from "./webpack";
import { SettingsRouter } from "./webpack/common";
import { patches } from "./webpack/patchWebpack";
if (IS_REPORTER) {
require("./debug/runReporter");
@ -90,6 +88,18 @@ async function syncSettings() {
});
}
}
const saveSettingsOnFrequentAction = debounce(async () => {
if (Settings.cloud.settingsSync && Settings.cloud.authenticated) {
await putCloudSettings();
delete localStorage.Vencord_settingsDirty;
}
}, 60_000);
SettingsStore.addGlobalChangeListener(() => {
localStorage.Vencord_settingsDirty = true;
saveSettingsOnFrequentAction();
});
}
let notifiedForUpdatesThisSession = false;
@ -161,6 +171,7 @@ async function init() {
}
}
initPluginManager();
startAllPlugins(StartAt.Init);
init();

View file

@ -17,7 +17,7 @@
*/
import ErrorBoundary from "@components/ErrorBoundary";
import BadgeAPIPlugin from "plugins/_api/badges";
import BadgeAPIPlugin from "@plugins/_api/badges";
import { ComponentType, HTMLProps } from "react";
export const enum BadgePosition {

View file

@ -28,6 +28,7 @@ import { addMessagePopoverButton, removeMessagePopoverButton } from "@api/Messag
import { Settings, SettingsStore } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import { Logger } from "@utils/Logger";
import { onlyOnce } from "@utils/onlyOnce";
import { canonicalizeFind, canonicalizeReplacement } from "@utils/patches";
import { Patch, Plugin, PluginDef, ReporterTestable, StartAt } from "@utils/types";
import { FluxEvents } from "@vencord/discord-types";
@ -35,27 +36,23 @@ import { FluxDispatcher } from "@webpack/common";
import { patches } from "@webpack/patcher";
import Plugins from "~plugins";
export { Plugins as plugins };
import { traceFunction } from "../debug/Tracer";
const logger = new Logger("PluginManager", "#a6d189");
export const PMLogger = logger;
export const plugins = Plugins;
export { patches };
/** Whether we have subscribed to flux events of all the enabled plugins when FluxDispatcher was ready */
let enabledPluginsSubscribedFlux = false;
const subscribedFluxEventsPlugins = new Set<string>();
const pluginsValues = Object.values(Plugins);
const settings = Settings.plugins;
export function isPluginEnabled(p: string) {
return (
Plugins[p]?.required ||
Plugins[p]?.isDependency ||
settings[p]?.enabled
Settings.plugins[p]?.enabled
) ?? false;
}
@ -94,85 +91,6 @@ function isReporterTestable(p: Plugin, part: ReporterTestable) {
: (p.reporterTestable & part) === part;
}
const pluginKeysToBind: Array<keyof PluginDef & `${"on" | "render"}${string}`> = [
"onBeforeMessageEdit", "onBeforeMessageSend", "onMessageClick",
"renderChatBarButton", "renderMemberListDecorator", "renderMessageAccessory", "renderMessageDecoration", "renderMessagePopoverButton"
];
const neededApiPlugins = new Set<string>();
// First round-trip to mark and force enable dependencies
//
// FIXME: might need to revisit this if there's ever nested (dependencies of dependencies) dependencies since this only
// goes for the top level and their children, but for now this works okay with the current API plugins
for (const p of pluginsValues) if (isPluginEnabled(p.name)) {
p.dependencies?.forEach(d => {
const dep = Plugins[d];
if (!dep) {
const error = new Error(`Plugin ${p.name} has unresolved dependency ${d}`);
if (IS_DEV) {
throw error;
}
logger.warn(error);
return;
}
settings[d].enabled = true;
dep.isDependency = true;
});
if (p.commands?.length) neededApiPlugins.add("CommandsAPI");
if (p.onBeforeMessageEdit || p.onBeforeMessageSend || p.onMessageClick) neededApiPlugins.add("MessageEventsAPI");
if (p.renderChatBarButton) neededApiPlugins.add("ChatInputButtonAPI");
if (p.renderMemberListDecorator) neededApiPlugins.add("MemberListDecoratorsAPI");
if (p.renderMessageAccessory) neededApiPlugins.add("MessageAccessoriesAPI");
if (p.renderMessageDecoration) neededApiPlugins.add("MessageDecorationsAPI");
if (p.renderMessagePopoverButton) neededApiPlugins.add("MessagePopoverAPI");
if (p.userProfileBadge) neededApiPlugins.add("BadgeAPI");
for (const key of pluginKeysToBind) {
p[key] &&= p[key].bind(p) as any;
}
}
for (const p of neededApiPlugins) {
Plugins[p].isDependency = true;
settings[p].enabled = true;
}
for (const p of pluginsValues) {
if (p.settings) {
p.options ??= {};
p.settings.pluginName = p.name;
for (const name in p.settings.def) {
const def = p.settings.def[name];
const checks = p.settings.checks?.[name];
p.options[name] = { ...def, ...checks };
}
}
if (p.options) {
for (const name in p.options) {
const opt = p.options[name];
if (opt.onChange != null) {
SettingsStore.addChangeListener(`plugins.${p.name}.${name}`, opt.onChange);
}
}
}
if (p.patches && isPluginEnabled(p.name)) {
if (!IS_REPORTER || isReporterTestable(p, ReporterTestable.Patches)) {
for (const patch of p.patches) {
addPatch(patch, p.name);
}
}
}
}
export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins(target: StartAt) {
logger.info(`Starting plugins (stage ${target})`);
for (const name in Plugins) {
@ -188,6 +106,7 @@ export const startAllPlugins = traceFunction("startAllPlugins", function startAl
});
export function startDependenciesRecursive(p: Plugin) {
const settings = Settings.plugins;
let restartNeeded = false;
const failures: string[] = [];
@ -379,3 +298,87 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu
return true;
}, p => `stopPlugin ${p.name}`);
export const initPluginManager = onlyOnce(function init() {
const pluginsValues = Object.values(Plugins);
const settings = Settings.plugins;
const pluginKeysToBind: Array<keyof PluginDef & `${"on" | "render"}${string}`> = [
"onBeforeMessageEdit", "onBeforeMessageSend", "onMessageClick",
"renderChatBarButton", "renderMemberListDecorator", "renderMessageAccessory", "renderMessageDecoration", "renderMessagePopoverButton"
];
const neededApiPlugins = new Set<string>();
// First round-trip to mark and force enable dependencies
//
// FIXME: might need to revisit this if there's ever nested (dependencies of dependencies) dependencies since this only
// goes for the top level and their children, but for now this works okay with the current API plugins
for (const p of pluginsValues) if (isPluginEnabled(p.name)) {
p.dependencies?.forEach(d => {
const dep = Plugins[d];
if (!dep) {
const error = new Error(`Plugin ${p.name} has unresolved dependency ${d}`);
if (IS_DEV) {
throw error;
}
logger.warn(error);
return;
}
settings[d].enabled = true;
dep.isDependency = true;
});
if (p.commands?.length) neededApiPlugins.add("CommandsAPI");
if (p.onBeforeMessageEdit || p.onBeforeMessageSend || p.onMessageClick) neededApiPlugins.add("MessageEventsAPI");
if (p.renderChatBarButton) neededApiPlugins.add("ChatInputButtonAPI");
if (p.renderMemberListDecorator) neededApiPlugins.add("MemberListDecoratorsAPI");
if (p.renderMessageAccessory) neededApiPlugins.add("MessageAccessoriesAPI");
if (p.renderMessageDecoration) neededApiPlugins.add("MessageDecorationsAPI");
if (p.renderMessagePopoverButton) neededApiPlugins.add("MessagePopoverAPI");
if (p.userProfileBadge) neededApiPlugins.add("BadgeAPI");
for (const key of pluginKeysToBind) {
p[key] &&= p[key].bind(p) as any;
}
}
for (const p of neededApiPlugins) {
Plugins[p].isDependency = true;
settings[p].enabled = true;
}
for (const p of pluginsValues) {
if (p.settings) {
p.options ??= {};
p.settings.pluginName = p.name;
for (const name in p.settings.def) {
const def = p.settings.def[name];
const checks = p.settings.checks?.[name];
p.options[name] = { ...def, ...checks };
}
}
if (p.options) {
for (const name in p.options) {
const opt = p.options[name];
if (opt.onChange != null) {
SettingsStore.addChangeListener(`plugins.${p.name}.${name}`, opt.onChange);
}
}
}
if (p.patches && isPluginEnabled(p.name)) {
if (!IS_REPORTER || isReporterTestable(p, ReporterTestable.Patches)) {
for (const patch of p.patches) {
addPatch(patch, p.name);
}
}
}
}
});

View file

@ -16,12 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { debounce } from "@shared/debounce";
import { SettingsStore as SettingsStoreClass } from "@shared/SettingsStore";
import { localStorage } from "@utils/localStorage";
import { Logger } from "@utils/Logger";
import { mergeDefaults } from "@utils/mergeDefaults";
import { putCloudSettings } from "@utils/settingsSync";
import { DefinedSettings, OptionType, SettingsChecks, SettingsDefinition } from "@utils/types";
import { React, useEffect } from "@webpack/common";
@ -111,14 +108,6 @@ const DefaultSettings: Settings = {
const settings = !IS_REPORTER ? VencordNative.settings.get() : {} as Settings;
mergeDefaults(settings, DefaultSettings);
const saveSettingsOnFrequentAction = debounce(async () => {
if (Settings.cloud.settingsSync && Settings.cloud.authenticated) {
await putCloudSettings();
delete localStorage.Vencord_settingsDirty;
}
}, 60_000);
export const SettingsStore = new SettingsStoreClass(settings, {
readOnly: true,
getDefaultValue({
@ -161,8 +150,6 @@ export const SettingsStore = new SettingsStoreClass(settings, {
if (!IS_REPORTER) {
SettingsStore.addGlobalChangeListener((_, path) => {
SettingsStore.plain.cloud.settingsSyncVersion = Date.now();
localStorage.Vencord_settingsDirty = true;
saveSettingsOnFrequentAction();
VencordNative.settings.set(SettingsStore.plain, path);
});
}

View file

@ -1,31 +1,18 @@
/*
* 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/>.
*/
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import * as DataStore from "@api/DataStore";
import { showNotification } from "@api/Notifications";
import { Settings } from "@api/Settings";
import { Logger } from "@utils/Logger";
import { openModal } from "@utils/modal";
import { relaunch } from "@utils/native";
import { Alerts, OAuth2AuthorizeModal, UserStore } from "@webpack/common";
import { Logger } from "./Logger";
import { openModal } from "./modal";
import { relaunch } from "./native";
export const cloudLogger = new Logger("Cloud", "#39b7e0");
export const logger = new Logger("SettingsSync:CloudSetup", "#39b7e0");
export const getCloudUrl = () => new URL(Settings.cloud.url);
const getCloudUrlOrigin = () => getCloudUrl().origin;
@ -137,7 +124,7 @@ export async function authorizeCloud() {
});
const { secret } = await res.json();
if (secret) {
cloudLogger.info("Authorized with secret");
logger.info("Authorized with secret");
await setAuthorization(secret);
showNotification({
title: "Cloud Integration",
@ -152,7 +139,7 @@ export async function authorizeCloud() {
Settings.cloud.authenticated = false;
}
} catch (e: any) {
cloudLogger.error("Failed to authorize", e);
logger.error("Failed to authorize", e);
showNotification({
title: "Cloud Integration",
body: `Setup failed (${e.toString()}).`

View file

@ -1,116 +1,19 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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/>.
*/
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { showNotification } from "@api/Notifications";
import { PlainSettings, Settings } from "@api/Settings";
import { moment, Toasts } from "@webpack/common";
import { Logger } from "@utils/Logger";
import { relaunch } from "@utils/native";
import { deflateSync, inflateSync } from "fflate";
import { checkCloudUrlCsp, getCloudAuth, getCloudUrl } from "./cloud";
import { Logger } from "./Logger";
import { relaunch } from "./native";
import { chooseFile, saveFile } from "./web";
import { checkCloudUrlCsp, deauthorizeCloud, getCloudAuth, getCloudUrl } from "./cloudSetup";
import { exportSettings, importSettings } from "./offline";
export async function importSettings(data: string) {
try {
var parsed = JSON.parse(data);
} catch (err) {
console.log(data);
throw new Error("Failed to parse JSON: " + String(err));
}
if ("settings" in parsed && "quickCss" in parsed) {
Object.assign(PlainSettings, parsed.settings);
await VencordNative.settings.set(parsed.settings);
await VencordNative.quickCss.set(parsed.quickCss);
} else
throw new Error("Invalid Settings. Is this even a Vencord Settings file?");
}
export async function exportSettings({ minify }: { minify?: boolean; } = {}) {
const settings = VencordNative.settings.get();
const quickCss = await VencordNative.quickCss.get();
return JSON.stringify({ settings, quickCss }, null, minify ? undefined : 4);
}
export async function downloadSettingsBackup() {
const filename = `vencord-settings-backup-${moment().format("YYYY-MM-DD")}.json`;
const backup = await exportSettings();
const data = new TextEncoder().encode(backup);
if (IS_DISCORD_DESKTOP) {
DiscordNative.fileManager.saveWithDialog(data, filename);
} else {
saveFile(new File([data], filename, { type: "application/json" }));
}
}
const toast = (type: string, message: string) =>
Toasts.show({
type,
message,
id: Toasts.genId()
});
const toastSuccess = () =>
toast(Toasts.Type.SUCCESS, "Settings successfully imported. Restart to apply changes!");
const toastFailure = (err: any) =>
toast(Toasts.Type.FAILURE, `Failed to import settings: ${String(err)}`);
export async function uploadSettingsBackup(showToast = true): Promise<void> {
if (IS_DISCORD_DESKTOP) {
const [file] = await DiscordNative.fileManager.openFiles({
filters: [
{ name: "Vencord Settings Backup", extensions: ["json"] },
{ name: "all", extensions: ["*"] }
]
});
if (file) {
try {
await importSettings(new TextDecoder().decode(file.data));
if (showToast) toastSuccess();
} catch (err) {
new Logger("SettingsSync").error(err);
if (showToast) toastFailure(err);
}
}
} else {
const file = await chooseFile("application/json");
if (!file) return;
const reader = new FileReader();
reader.onload = async () => {
try {
await importSettings(reader.result as string);
if (showToast) toastSuccess();
} catch (err) {
new Logger("SettingsSync").error(err);
if (showToast) toastFailure(err);
}
};
reader.readAsText(file);
}
}
// Cloud settings
const cloudSettingsLogger = new Logger("Cloud:Settings", "#39b7e0");
const logger = new Logger("SettingsSync:Cloud", "#39b7e0");
export async function putCloudSettings(manual?: boolean) {
const settings = await exportSettings({ minify: true });
@ -124,11 +27,11 @@ export async function putCloudSettings(manual?: boolean) {
Authorization: await getCloudAuth(),
"Content-Type": "application/octet-stream"
},
body: deflateSync(new TextEncoder().encode(settings))
body: deflateSync(new TextEncoder().encode(settings)) as Uint8Array<ArrayBuffer>
});
if (!res.ok) {
cloudSettingsLogger.error(`Failed to sync up, API returned ${res.status}`);
logger.error(`Failed to sync up, API returned ${res.status}`);
showNotification({
title: "Cloud Settings",
body: `Could not synchronize settings to cloud (API returned ${res.status}).`,
@ -141,7 +44,7 @@ export async function putCloudSettings(manual?: boolean) {
PlainSettings.cloud.settingsSyncVersion = written;
VencordNative.settings.set(PlainSettings);
cloudSettingsLogger.info("Settings uploaded to cloud successfully");
logger.info("Settings uploaded to cloud successfully");
if (manual) {
showNotification({
@ -151,7 +54,7 @@ export async function putCloudSettings(manual?: boolean) {
});
}
} catch (e: any) {
cloudSettingsLogger.error("Failed to sync up", e);
logger.error("Failed to sync up", e);
showNotification({
title: "Cloud Settings",
body: `Could not synchronize settings to the cloud (${e.toString()}).`,
@ -174,7 +77,7 @@ export async function getCloudSettings(shouldNotify = true, force = false) {
});
if (res.status === 404) {
cloudSettingsLogger.info("No settings on the cloud");
logger.info("No settings on the cloud");
if (shouldNotify)
showNotification({
title: "Cloud Settings",
@ -185,7 +88,7 @@ export async function getCloudSettings(shouldNotify = true, force = false) {
}
if (res.status === 304) {
cloudSettingsLogger.info("Settings up to date");
logger.info("Settings up to date");
if (shouldNotify)
showNotification({
title: "Cloud Settings",
@ -196,7 +99,7 @@ export async function getCloudSettings(shouldNotify = true, force = false) {
}
if (!res.ok) {
cloudSettingsLogger.error(`Failed to sync down, API returned ${res.status}`);
logger.error(`Failed to sync down, API returned ${res.status}`);
showNotification({
title: "Cloud Settings",
body: `Could not synchronize settings from the cloud (API returned ${res.status}).`,
@ -228,7 +131,7 @@ export async function getCloudSettings(shouldNotify = true, force = false) {
PlainSettings.cloud.settingsSyncVersion = written;
VencordNative.settings.set(PlainSettings);
cloudSettingsLogger.info("Settings loaded from cloud successfully");
logger.info("Settings loaded from cloud successfully");
if (shouldNotify)
showNotification({
title: "Cloud Settings",
@ -240,7 +143,7 @@ export async function getCloudSettings(shouldNotify = true, force = false) {
return true;
} catch (e: any) {
cloudSettingsLogger.error("Failed to sync down", e);
logger.error("Failed to sync down", e);
showNotification({
title: "Cloud Settings",
body: `Could not synchronize settings from the cloud (${e.toString()}).`,
@ -261,7 +164,7 @@ export async function deleteCloudSettings() {
});
if (!res.ok) {
cloudSettingsLogger.error(`Failed to delete, API returned ${res.status}`);
logger.error(`Failed to delete, API returned ${res.status}`);
showNotification({
title: "Cloud Settings",
body: `Could not delete settings (API returned ${res.status}).`,
@ -270,14 +173,14 @@ export async function deleteCloudSettings() {
return;
}
cloudSettingsLogger.info("Settings deleted from cloud successfully");
logger.info("Settings deleted from cloud successfully");
showNotification({
title: "Cloud Settings",
body: "Settings deleted from cloud!",
color: "var(--green-360)"
});
} catch (e: any) {
cloudSettingsLogger.error("Failed to delete", e);
logger.error("Failed to delete", e);
showNotification({
title: "Cloud Settings",
body: `Could not delete settings (${e.toString()}).`,
@ -285,3 +188,31 @@ export async function deleteCloudSettings() {
});
}
}
export async function eraseAllCloudData() {
if (!await checkCloudUrlCsp()) return;
const res = await fetch(new URL("/v1/", getCloudUrl()), {
method: "DELETE",
headers: { Authorization: await getCloudAuth() }
});
if (!res.ok) {
logger.error(`Failed to erase data, API returned ${res.status}`);
showNotification({
title: "Cloud Integrations",
body: `Could not erase all data (API returned ${res.status}), please contact support.`,
color: "var(--red-360)"
});
return;
}
Settings.cloud.authenticated = false;
await deauthorizeCloud();
showNotification({
title: "Cloud Integrations",
body: "Successfully erased all data.",
color: "var(--green-360)"
});
}

View file

@ -0,0 +1,95 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { PlainSettings } from "@api/Settings";
import { Logger } from "@utils/Logger";
import { chooseFile, saveFile } from "@utils/web";
import { moment, Toasts } from "@webpack/common";
const toast = (type: string, message: string) =>
Toasts.show({
type,
message,
id: Toasts.genId()
});
const toastSuccess = () =>
toast(Toasts.Type.SUCCESS, "Settings successfully imported. Restart to apply changes!");
const toastFailure = (err: any) =>
toast(Toasts.Type.FAILURE, `Failed to import settings: ${String(err)}`);
const logger = new Logger("SettingsSync:Offline", "#39b7e0");
export async function importSettings(data: string) {
try {
var parsed = JSON.parse(data);
} catch (err) {
console.log(data);
throw new Error("Failed to parse JSON: " + String(err));
}
if ("settings" in parsed && "quickCss" in parsed) {
Object.assign(PlainSettings, parsed.settings);
await VencordNative.settings.set(parsed.settings);
await VencordNative.quickCss.set(parsed.quickCss);
} else
throw new Error("Invalid Settings. Is this even a Vencord Settings file?");
}
export async function exportSettings({ minify }: { minify?: boolean; } = {}) {
const settings = VencordNative.settings.get();
const quickCss = await VencordNative.quickCss.get();
return JSON.stringify({ settings, quickCss }, null, minify ? undefined : 4);
}
export async function downloadSettingsBackup() {
const filename = `vencord-settings-backup-${moment().format("YYYY-MM-DD")}.json`;
const backup = await exportSettings();
const data = new TextEncoder().encode(backup);
if (IS_DISCORD_DESKTOP) {
DiscordNative.fileManager.saveWithDialog(data, filename);
} else {
saveFile(new File([data], filename, { type: "application/json" }));
}
}
export async function uploadSettingsBackup(showToast = true): Promise<void> {
if (IS_DISCORD_DESKTOP) {
const [file] = await DiscordNative.fileManager.openFiles({
filters: [
{ name: "Vencord Settings Backup", extensions: ["json"] },
{ name: "all", extensions: ["*"] }
]
});
if (file) {
try {
await importSettings(new TextDecoder().decode(file.data));
if (showToast) toastSuccess();
} catch (err) {
logger.error(err);
if (showToast) toastFailure(err);
}
}
} else {
const file = await chooseFile("application/json");
if (!file) return;
const reader = new FileReader();
reader.onload = async () => {
try {
await importSettings(reader.result as string);
if (showToast) toastSuccess();
} catch (err) {
logger.error(err);
if (showToast) toastFailure(err);
}
};
reader.readAsText(file);
}
}

View file

@ -17,9 +17,8 @@
*/
import { Settings, SettingsStore } from "@api/Settings";
import { ThemeStore } from "@webpack/common";
import { createAndAppendStyle } from "./css";
import { createAndAppendStyle } from "@utils/css";
import { ThemeStore } from "@vencord/discord-types";
let style: HTMLStyleElement;
let themesStyle: HTMLStyleElement;
@ -54,6 +53,8 @@ async function initThemes() {
const { themeLinks, enabledThemes } = Settings;
const { ThemeStore } = require("@webpack/common/stores") as typeof import("@webpack/common/stores");
// "darker" and "midnight" both count as dark
// This function is first called on DOMContentLoaded, so ThemeStore may not have been loaded yet
const activeTheme = ThemeStore == null
@ -102,16 +103,16 @@ document.addEventListener("DOMContentLoaded", () => {
}
}, { once: true });
export function initQuickCssThemeStore() {
export function initQuickCssThemeStore(themeStore: ThemeStore) {
if (IS_USERSCRIPT) return;
initThemes();
let currentTheme = ThemeStore.theme;
ThemeStore.addChangeListener(() => {
if (currentTheme === ThemeStore.theme) return;
let currentTheme = themeStore.theme;
themeStore.addChangeListener(() => {
if (currentTheme === themeStore.theme) return;
currentTheme = ThemeStore.theme;
currentTheme = themeStore.theme;
initThemes();
});
}

View file

@ -20,6 +20,8 @@ import { proxyLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger";
import { findModuleId, proxyLazyWebpack, wreq } from "@webpack";
import { isPluginEnabled } from "./PluginManager";
interface UserSettingDefinition<T> {
/**
* Get the setting value
@ -45,7 +47,7 @@ interface UserSettingDefinition<T> {
export const UserSettings: Record<PropertyKey, UserSettingDefinition<any>> | undefined = proxyLazyWebpack(() => {
const modId = findModuleId('"textAndImages","renderSpoilers"');
if (modId == null) return new Logger("UserSettingsAPI ").error("Didn't find settings module.");
if (modId == null) return new Logger("UserSettingsAPI").error("Didn't find settings module.");
return wreq(modId as any);
});
@ -57,7 +59,7 @@ export const UserSettings: Record<PropertyKey, UserSettingDefinition<any>> | und
* @param name The name of the setting
*/
export function getUserSetting<T = any>(group: string, name: string): UserSettingDefinition<T> | undefined {
if (!Vencord.Plugins.isPluginEnabled("UserSettingsAPI")) throw new Error("Cannot use UserSettingsAPI without setting as dependency.");
if (!isPluginEnabled("UserSettingsAPI")) throw new Error("Cannot use UserSettingsAPI without setting it as a dependency.");
for (const key in UserSettings) {
const userSetting = UserSettings[key];

View file

@ -29,9 +29,11 @@ import * as $MessagePopover from "./MessagePopover";
import * as $MessageUpdater from "./MessageUpdater";
import * as $Notices from "./Notices";
import * as $Notifications from "./Notifications";
export * as PluginManager from "./PluginManager";
import * as $ServerList from "./ServerList";
import * as $Settings from "./Settings";
import * as $Styles from "./Styles";
import * as $Themes from "./Themes";
import * as $UserSettings from "./UserSettings";
/**
@ -122,3 +124,8 @@ export const MessageUpdater = $MessageUpdater;
* An API allowing you to get an user setting
*/
export const UserSettings = $UserSettings;
/**
* Don't use this
*/
export const Themes = $Themes;

View file

@ -22,8 +22,8 @@ import { Flex } from "@components/Flex";
import { HeadingTertiary } from "@components/Heading";
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
import { debounce } from "@shared/debounce";
import { copyWithToast } from "@utils/discord";
import { Margins } from "@utils/margins";
import { copyWithToast } from "@utils/misc";
import { stripIndent } from "@utils/text";
import { ReplaceFn } from "@utils/types";
import { search } from "@webpack";

View file

@ -5,9 +5,9 @@
*/
import { showNotice } from "@api/Notices";
import { isPluginEnabled, startDependenciesRecursive, startPlugin, stopPlugin } from "@api/PluginManager";
import { CogWheel, InfoIcon } from "@components/Icons";
import { AddonCard } from "@components/settings/AddonCard";
import { proxyLazy } from "@utils/lazy";
import { isObjectEmpty } from "@utils/misc";
import { Plugin } from "@utils/types";
import { React, showToast, Toasts } from "@webpack/common";
@ -16,9 +16,6 @@ import { Settings } from "Vencord";
import { cl, logger } from ".";
import { openPluginModal } from "./PluginModal";
// Avoid circular dependency
const { startDependenciesRecursive, startPlugin, stopPlugin, isPluginEnabled } = proxyLazy(() => require("plugins") as typeof import("plugins"));
interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
plugin: Plugin;
disabled: boolean;

View file

@ -19,6 +19,7 @@
import "./styles.css";
import * as DataStore from "@api/DataStore";
import { isPluginEnabled } from "@api/PluginManager";
import { useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import { Divider } from "@components/Divider";
@ -157,7 +158,7 @@ function PluginSettings() {
const pluginFilter = (plugin: typeof Plugins[keyof typeof Plugins]) => {
const { status } = searchValue;
const enabled = Vencord.Plugins.isPluginEnabled(plugin.name);
const enabled = isPluginEnabled(plugin.name);
switch (status) {
case SearchStatus.DISABLED:

View file

@ -16,11 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { downloadSettingsBackup, uploadSettingsBackup } from "@api/SettingsSync/offline";
import { Flex } from "@components/Flex";
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
import { Margins } from "@utils/margins";
import { classes } from "@utils/misc";
import { downloadSettingsBackup, uploadSettingsBackup } from "@utils/settingsSync";
import { Button, Card, Text } from "@webpack/common";
function BackupAndRestoreTab() {

View file

@ -16,8 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { showNotification } from "@api/Notifications";
import { Settings, useSettings } from "@api/Settings";
import { useSettings } from "@api/Settings";
import { authorizeCloud, deauthorizeCloud } from "@api/SettingsSync/cloudSetup";
import { deleteCloudSettings, eraseAllCloudData, getCloudSettings, putCloudSettings } from "@api/SettingsSync/cloudSync";
import { CheckedTextInput } from "@components/CheckedTextInput";
import { Divider } from "@components/Divider";
import { FormSwitch } from "@components/FormSwitch";
@ -25,9 +26,7 @@ import { Grid } from "@components/Grid";
import { Link } from "@components/Link";
import { Paragraph } from "@components/Paragraph";
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
import { authorizeCloud, checkCloudUrlCsp, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud";
import { Margins } from "@utils/margins";
import { deleteCloudSettings, getCloudSettings, putCloudSettings } from "@utils/settingsSync";
import { Alerts, Button, Forms, Tooltip } from "@webpack/common";
function validateUrl(url: string) {
@ -39,34 +38,6 @@ function validateUrl(url: string) {
}
}
async function eraseAllData() {
if (!await checkCloudUrlCsp()) return;
const res = await fetch(new URL("/v1/", getCloudUrl()), {
method: "DELETE",
headers: { Authorization: await getCloudAuth() }
});
if (!res.ok) {
cloudLogger.error(`Failed to erase data, API returned ${res.status}`);
showNotification({
title: "Cloud Integrations",
body: `Could not erase all data (API returned ${res.status}), please contact support.`,
color: "var(--red-360)"
});
return;
}
Settings.cloud.authenticated = false;
await deauthorizeCloud();
showNotification({
title: "Cloud Integrations",
body: "Successfully erased all data.",
color: "var(--green-360)"
});
}
function SettingsSyncSection() {
const { cloud } = useSettings(["cloud.authenticated", "cloud.settingsSync"]);
const sectionEnabled = cloud.authenticated && cloud.settingsSync;
@ -181,7 +152,7 @@ function CloudTab() {
onClick={() => Alerts.show({
title: "Are you sure?",
body: "Once your data is erased, we cannot recover it. There's no going back!",
onConfirm: eraseAllData,
onConfirm: eraseAllCloudData,
confirmText: "Erase it!",
confirmColor: "vc-cloud-erase-data-danger-btn",
cancelText: "Nevermind"

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { isPluginEnabled } from "@api/PluginManager";
import { Settings, useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import { FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
@ -11,9 +12,9 @@ import { Link } from "@components/Link";
import { QuickAction, QuickActionCard } from "@components/settings/QuickAction";
import { openPluginModal } from "@components/settings/tabs/plugins/PluginModal";
import { UserThemeHeader } from "@main/themes";
import ClientThemePlugin from "@plugins/clientTheme";
import { findLazy } from "@webpack";
import { Card, Forms, useEffect, useRef, useState } from "@webpack/common";
import ClientThemePlugin from "plugins/clientTheme";
import type { ComponentType, Ref, SyntheticEvent } from "react";
import { ThemeCard } from "./ThemeCard";
@ -140,7 +141,7 @@ export function LocalThemesTab() {
Icon={PaintbrushIcon}
/>
{Vencord.Plugins.isPluginEnabled(ClientThemePlugin.name) && (
{isPluginEnabled(ClientThemePlugin.name) && (
<QuickAction
text="Edit ClientTheme"
action={() => openPluginModal(ClientThemePlugin)}

View file

@ -5,9 +5,9 @@
*/
import DonateButton from "@components/settings/DonateButton";
import BadgeAPI from "@plugins/_api/badges";
import { DONOR_ROLE_ID, VENCORD_GUILD_ID } from "@utils/constants";
import { Button, GuildMemberStore } from "@webpack/common";
import BadgeAPI from "plugins/_api/badges";
export const isDonor = (userId: string) => !!(
BadgeAPI.getDonorBadges(userId)?.length > 0

View file

@ -20,7 +20,7 @@ import { openNotificationLogModal } from "@api/Notifications/notificationLog";
import { useSettings } from "@api/Settings";
import { Divider } from "@components/Divider";
import { FormSwitch } from "@components/FormSwitch";
import { FolderIcon, GithubIcon, LogIcon, PaintbrushIcon, RestartIcon } from "@components/index";
import { FolderIcon, GithubIcon, LogIcon, PaintbrushIcon, RestartIcon } from "@components/Icons";
import { QuickAction, QuickActionCard } from "@components/settings/QuickAction";
import { SpecialCard } from "@components/settings/SpecialCard";
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";

View file

@ -4,11 +4,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addPatch } from "@api/PluginManager";
import { Logger } from "@utils/Logger";
import * as Webpack from "@webpack";
import { getBuildNumber, patchTimings } from "@webpack/patcher";
import { getBuildNumber, patches, patchTimings } from "@webpack/patcher";
import { addPatch, patches } from "../plugins";
import { loadLazyChunks } from "./loadLazyChunks";
async function runReporter() {

View file

@ -25,9 +25,10 @@ import { Heart } from "@components/Heart";
import DonateButton from "@components/settings/DonateButton";
import { openContributorModal } from "@components/settings/tabs";
import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/discord";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins";
import { copyWithToast, shouldShowContributorBadge } from "@utils/misc";
import { shouldShowContributorBadge } from "@utils/misc";
import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal";
import definePlugin from "@utils/types";
import { User } from "@vencord/discord-types";

View file

@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { isPluginEnabled } from "@api/PluginManager";
import { definePluginSettings } from "@api/Settings";
import { getUserSettingLazy } from "@api/UserSettings";
import ErrorBoundary from "@components/ErrorBoundary";
@ -95,7 +96,6 @@ async function generateDebugInfoMessage() {
}
const commonIssues = {
"NoRPC enabled": Vencord.Plugins.isPluginEnabled("NoRPC"),
"Activity Sharing disabled": tryOrElse(() => !ShowCurrentGame.getSetting(), false),
"Vencord DevBuild": !IS_STANDALONE,
"Has UserPlugins": Object.values(PluginMeta).some(m => m.userPlugin),
@ -114,7 +114,7 @@ function generatePluginList() {
const isApiPlugin = (plugin: string) => plugin.endsWith("API") || plugins[plugin].required;
const enabledPlugins = Object.keys(plugins)
.filter(p => Vencord.Plugins.isPluginEnabled(p) && !isApiPlugin(p));
.filter(p => isPluginEnabled(p) && !isApiPlugin(p));
const enabledStockPlugins = enabledPlugins.filter(p => !PluginMeta[p].userPlugin);
const enabledUserPlugins = enabledPlugins.filter(p => PluginMeta[p].userPlugin);

View file

@ -18,7 +18,7 @@
import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/misc";
import { copyWithToast } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
export default definePlugin({

View file

@ -5,9 +5,9 @@
*/
import { Button } from "@components/Button";
import { SessionInfo } from "@plugins/betterSessions/types";
import { openModal } from "@utils/modal";
import { SessionInfo } from "../types";
import { RenameModal } from "./RenameModal";
export function RenameButton({ session, state }: { session: SessionInfo["session"], state: [string, React.Dispatch<React.SetStateAction<string>>]; }) {

View file

@ -16,13 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { SessionInfo } from "@plugins/betterSessions/types";
import { getDefaultName, savedSessionsCache, saveSessionsToDataStore } from "@plugins/betterSessions/utils";
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
import { Button, Forms, React, TextInput } from "@webpack/common";
import { KeyboardEvent } from "react";
import { SessionInfo } from "../types";
import { getDefaultName, savedSessionsCache, saveSessionsToDataStore } from "../utils";
export function RenameModal({ props, session, state }: { props: ModalProps, session: SessionInfo["session"], state: [string, React.Dispatch<React.SetStateAction<string>>]; }) {
const [title, setTitle] = state;
const [value, setValue] = React.useState(savedSessionsCache.get(session.id_hash)?.name ?? "");

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { DataStore } from "@api/index";
import * as DataStore from "@api/DataStore";
import { UserStore } from "@webpack/common";
import { ChromeIcon, DiscordIcon, EdgeIcon, FirefoxIcon, IEIcon, MobileIcon, OperaIcon, SafariIcon, UnknownIcon } from "./components/icons";

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { isPluginEnabled } from "@api/PluginManager";
import { openPluginModal } from "@components/settings/tabs";
import { getIntlMessage } from "@utils/discord";
import { isObjectEmpty } from "@utils/misc";
@ -28,7 +29,7 @@ export default function PluginsSubmenu() {
const search = query.toLowerCase();
const include = (p: typeof Plugins[keyof typeof Plugins]) => (
Vencord.Plugins.isPluginEnabled(p.name)
isPluginEnabled(p.name)
&& p.options && !isObjectEmpty(p.options)
&& (
p.name.toLowerCase().includes(search)

View file

@ -6,13 +6,13 @@
import { classNameFactory } from "@api/Styles";
import { ErrorCard } from "@components/ErrorCard";
import { relativeLuminance } from "@plugins/clientTheme/utils/colorUtils";
import { createOrUpdateThemeColorVars } from "@plugins/clientTheme/utils/styleUtils";
import { Margins } from "@utils/margins";
import { findByCodeLazy, findStoreLazy } from "@webpack";
import { Button, ColorPicker, Forms, ThemeStore, useStateFromStores } from "@webpack/common";
import { settings } from "..";
import { relativeLuminance } from "../utils/colorUtils";
import { createOrUpdateThemeColorVars } from "../utils/styleUtils";
const saveClientTheme = findByCodeLazy('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"===');
const NitroThemeStore = findStoreLazy("ClientThemesBackgroundStore");

View file

@ -170,7 +170,7 @@ function makeShortcuts() {
openModal: { getter: () => ModalAPI.openModal },
openModalLazy: { getter: () => ModalAPI.openModalLazy },
Stores: Webpack.fluxStores,
Stores: { getter: () => Object.fromEntries(Webpack.fluxStores) },
// e.g. "2024-05_desktop_visual_refresh", 0
setExperiment: (id: string, bucket: number) => {

View file

@ -6,7 +6,7 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/misc";
import { copyWithToast } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { Menu } from "@webpack/common";

View file

@ -9,7 +9,7 @@ import "./style.css";
import ErrorBoundary from "@components/ErrorBoundary";
import { CopyIcon, NoEntrySignIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/misc";
import { copyWithToast } from "@utils/discord";
import definePlugin from "@utils/types";
import { Tooltip, useState } from "@webpack/common";

View file

@ -17,12 +17,13 @@
*/
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { isPluginEnabled } from "@api/PluginManager";
import ExpressionClonerPlugin from "@plugins/expressionCloner";
import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/misc";
import { copyWithToast } from "@utils/discord";
import definePlugin from "@utils/types";
import { Message, Sticker } from "@vencord/discord-types";
import { Menu, React, StickersStore } from "@webpack/common";
import ExpressionClonerPlugin from "plugins/expressionCloner";
const StickerExt = [, "png", "png", "json", "gif"] as const;
@ -79,7 +80,7 @@ const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { t
const sticker = StickersStore.getStickerById(id);
if (sticker) {
children.push(buildMenuItem(sticker, Vencord.Plugins.isPluginEnabled(ExpressionClonerPlugin.name)));
children.push(buildMenuItem(sticker, isPluginEnabled(ExpressionClonerPlugin.name)));
}
};

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Notices } from "@api/index";
import { currentNotice, noticesQueue, popNotice, showNotice } from "@api/Notices";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { makeRange, OptionType } from "@utils/types";
@ -62,12 +62,12 @@ export default definePlugin({
const backOnlineMessage = "Welcome back! Click the button to go online. Click the X to stay idle until reload.";
if (
Notices.currentNotice?.[1] === backOnlineMessage ||
Notices.noticesQueue.some(([, noticeMessage]) => noticeMessage === backOnlineMessage)
currentNotice?.[1] === backOnlineMessage ||
noticesQueue.some(([, noticeMessage]) => noticeMessage === backOnlineMessage)
) return;
Notices.showNotice(backOnlineMessage, "Exit idle", () => {
Notices.popNotice();
showNotice(backOnlineMessage, "Exit idle", () => {
popNotice();
FluxDispatcher.dispatch({
type: "IDLE",
idle: false

View file

@ -6,6 +6,7 @@
import "./settings.css";
import { isPluginEnabled } from "@api/PluginManager";
import { classNameFactory } from "@api/Styles";
import { Divider } from "@components/Divider";
import { Heading } from "@components/Heading";
@ -14,7 +15,7 @@ import { debounce } from "@shared/debounce";
import { ActivityType } from "@vencord/discord-types/enums";
import { Select, Text, TextInput, useState } from "@webpack/common";
import { setRpc, settings, TimestampMode } from ".";
import CustomRPCPlugin, { setRpc, settings, TimestampMode } from ".";
const cl = classNameFactory("vc-customRPC-settings-");
@ -50,7 +51,7 @@ function isAppIdValid(value: string) {
const updateRPC = debounce(() => {
setRpc(true);
if (Vencord.Plugins.isPluginEnabled("CustomRPC")) setRpc();
if (isPluginEnabled(CustomRPCPlugin.name)) setRpc();
});
function isStreamLinkDisabled() {

View file

@ -4,14 +4,13 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { DataStore } from "@api/index";
import * as DataStore from "@api/DataStore";
import { AUTHORIZE_URL, CLIENT_ID } from "@plugins/decor/lib/constants";
import { proxyLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger";
import { openModal } from "@utils/modal";
import { OAuth2AuthorizeModal, showToast, Toasts, UserStore, zustandCreate, zustandPersist } from "@webpack/common";
import { AUTHORIZE_URL, CLIENT_ID } from "../constants";
interface AuthorizationState {
token: string | null;
tokens: Record<string, string>;

View file

@ -4,11 +4,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Decoration, deleteDecoration, getUserDecoration, getUserDecorations, NewDecoration, setUserDecoration } from "@plugins/decor/lib/api";
import { decorationToAsset } from "@plugins/decor/lib/utils/decoration";
import { proxyLazy } from "@utils/lazy";
import { UserStore, zustandCreate } from "@webpack/common";
import { Decoration, deleteDecoration, getUserDecoration, getUserDecorations, NewDecoration, setUserDecoration } from "../api";
import { decorationToAsset } from "../utils/decoration";
import { useUsersDecorationsStore } from "./UsersDecorationsStore";
interface UserDecorationsState {

View file

@ -4,15 +4,14 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { AvatarDecoration } from "@plugins/decor";
import { getUsersDecorations } from "@plugins/decor/lib/api";
import { DECORATION_FETCH_COOLDOWN, SKU_ID } from "@plugins/decor/lib/constants";
import { debounce } from "@shared/debounce";
import { proxyLazy } from "@utils/lazy";
import { User } from "@vencord/discord-types";
import { useEffect, useState, zustandCreate } from "@webpack/common";
import { AvatarDecoration } from "../../";
import { getUsersDecorations } from "../api";
import { DECORATION_FETCH_COOLDOWN, SKU_ID } from "../constants";
interface UserDecorationData {
asset: string | null;
fetchedAt: Date;

View file

@ -4,9 +4,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { AvatarDecoration } from "../../";
import { Decoration } from "../api";
import { SKU_ID } from "../constants";
import { AvatarDecoration } from "@plugins/decor";
import { Decoration } from "@plugins/decor/lib/api";
import { SKU_ID } from "@plugins/decor/lib/constants";
export function decorationToAsset(decoration: Decoration) {
return `${decoration.animated ? "a_" : ""}${decoration.hash}`;

View file

@ -12,13 +12,14 @@ import { closeAllModals } from "@utils/modal";
import { OptionType } from "@utils/types";
import { FluxDispatcher, Forms } from "@webpack/common";
import DecorPlugin from ".";
import DecorSection from "./ui/components/DecorSection";
export const settings = definePluginSettings({
changeDecoration: {
type: OptionType.COMPONENT,
component() {
if (!Vencord.Plugins.plugins.Decor.started) return <Forms.FormText>
if (!DecorPlugin.started) return <Forms.FormText>
Enable Decor and restart your client to change your avatar decoration.
</Forms.FormText>;

View file

@ -4,11 +4,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Decoration } from "@plugins/decor/lib/api";
import { decorationToAvatarDecoration } from "@plugins/decor/lib/utils/decoration";
import { ContextMenuApi } from "@webpack/common";
import type { HTMLProps } from "react";
import { Decoration } from "../../lib/api";
import { decorationToAvatarDecoration } from "../../lib/utils/decoration";
import { DecorationGridDecoration } from ".";
import DecorationContextMenu from "./DecorationContextMenu";

View file

@ -6,14 +6,13 @@
import { Button } from "@components/Button";
import { Flex } from "@components/Flex";
import { useAuthorizationStore } from "@plugins/decor/lib/stores/AuthorizationStore";
import { useCurrentUserDecorationsStore } from "@plugins/decor/lib/stores/CurrentUserDecorationsStore";
import { cl } from "@plugins/decor/ui";
import { openChangeDecorationModal } from "@plugins/decor/ui/modals/ChangeDecorationModal";
import { findComponentByCodeLazy } from "@webpack";
import { useEffect } from "@webpack/common";
import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore";
import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore";
import { cl } from "../";
import { openChangeDecorationModal } from "../modals/ChangeDecorationModal";
const CustomizationSection = findComponentByCodeLazy(".customizationSectionBackground");
export interface DecorSectionProps {

View file

@ -5,13 +5,12 @@
*/
import { CopyIcon, DeleteIcon } from "@components/Icons";
import { Decoration } from "@plugins/decor/lib/api";
import { useCurrentUserDecorationsStore } from "@plugins/decor/lib/stores/CurrentUserDecorationsStore";
import { cl } from "@plugins/decor/ui";
import { copyToClipboard } from "@utils/clipboard";
import { Alerts, ContextMenuApi, Menu, UserStore } from "@webpack/common";
import { Decoration } from "../../lib/api";
import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore";
import { cl } from "../";
export default function DecorationContextMenu({ decoration }: { decoration: Decoration; }) {
const { delete: deleteDecoration } = useCurrentUserDecorationsStore();

View file

@ -4,11 +4,10 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { cl } from "@plugins/decor/ui";
import { React } from "@webpack/common";
import { JSX } from "react";
import { cl } from "../";
export interface GridProps<ItemT> {
renderItem: (item: ItemT) => JSX.Element;
getItemKey: (item: ItemT) => string;

View file

@ -4,12 +4,12 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { cl } from "@plugins/decor/ui";
import { classes } from "@utils/misc";
import { findByPropsLazy } from "@webpack";
import { React } from "@webpack/common";
import { JSX } from "react";
import { cl } from "../";
import Grid, { GridProps } from "./Grid";
const ScrollerClasses = findByPropsLazy("managedReactiveScroller");

View file

@ -4,12 +4,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { AvatarDecoration } from "@plugins/decor";
import { findComponentByCode, LazyComponentWebpack } from "@webpack";
import { React } from "@webpack/common";
import type { ComponentType, HTMLProps, PropsWithChildren } from "react";
import { AvatarDecoration } from "../..";
type DecorationGridItemComponent = ComponentType<PropsWithChildren<HTMLProps<HTMLDivElement>> & {
onSelect: () => void,
isSelected: boolean,

View file

@ -7,26 +7,25 @@
import { Button as NewButton } from "@components/Button";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { openInviteModal } from "@utils/discord";
import { Decoration, getPresets, Preset } from "@plugins/decor/lib/api";
import { GUILD_ID, INVITE_KEY } from "@plugins/decor/lib/constants";
import { useAuthorizationStore } from "@plugins/decor/lib/stores/AuthorizationStore";
import { useCurrentUserDecorationsStore } from "@plugins/decor/lib/stores/CurrentUserDecorationsStore";
import { decorationToAvatarDecoration } from "@plugins/decor/lib/utils/decoration";
import { settings } from "@plugins/decor/settings";
import { cl, DecorationModalStyles, requireAvatarDecorationModal } from "@plugins/decor/ui";
import { AvatarDecorationModalPreview } from "@plugins/decor/ui/components";
import DecorationGridCreate from "@plugins/decor/ui/components/DecorationGridCreate";
import DecorationGridNone from "@plugins/decor/ui/components/DecorationGridNone";
import DecorDecorationGridDecoration from "@plugins/decor/ui/components/DecorDecorationGridDecoration";
import SectionedGridList from "@plugins/decor/ui/components/SectionedGridList";
import { copyWithToast, openInviteModal } from "@utils/discord";
import { Margins } from "@utils/margins";
import { copyWithToast } from "@utils/misc";
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { Queue } from "@utils/Queue";
import { User } from "@vencord/discord-types";
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserSummaryItem, UserUtils, useState } from "@webpack/common";
import { Decoration, getPresets, Preset } from "../../lib/api";
import { GUILD_ID, INVITE_KEY } from "../../lib/constants";
import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore";
import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore";
import { decorationToAvatarDecoration } from "../../lib/utils/decoration";
import { settings } from "../../settings";
import { cl, DecorationModalStyles, requireAvatarDecorationModal } from "../";
import { AvatarDecorationModalPreview } from "../components";
import DecorationGridCreate from "../components/DecorationGridCreate";
import DecorationGridNone from "../components/DecorationGridNone";
import DecorDecorationGridDecoration from "../components/DecorDecorationGridDecoration";
import SectionedGridList from "../components/SectionedGridList";
import { openCreateDecorationModal } from "./CreateDecorationModal";
import { openGuidelinesModal } from "./GuidelinesModal";

View file

@ -6,17 +6,16 @@
import ErrorBoundary from "@components/ErrorBoundary";
import { Link } from "@components/Link";
import { GUILD_ID, INVITE_KEY, RAW_SKU_ID } from "@plugins/decor/lib/constants";
import { useCurrentUserDecorationsStore } from "@plugins/decor/lib/stores/CurrentUserDecorationsStore";
import { cl, DecorationModalStyles, requireAvatarDecorationModal, requireCreateStickerModal } from "@plugins/decor/ui";
import { AvatarDecorationModalPreview } from "@plugins/decor/ui/components";
import { openInviteModal } from "@utils/discord";
import { Margins } from "@utils/margins";
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { filters, findComponentByCodeLazy, mapMangledModuleLazy } from "@webpack";
import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Text, TextInput, useEffect, useMemo, UserStore, useState } from "@webpack/common";
import { GUILD_ID, INVITE_KEY, RAW_SKU_ID } from "../../lib/constants";
import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore";
import { cl, DecorationModalStyles, requireAvatarDecorationModal, requireCreateStickerModal } from "../";
import { AvatarDecorationModalPreview } from "../components";
const FileUpload = findComponentByCodeLazy(".fileUpload),");
const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE="positive', {

View file

@ -6,11 +6,11 @@
import { Flex } from "@components/Flex";
import { Link } from "@components/Link";
import { settings } from "@plugins/decor/settings";
import { cl, DecorationModalStyles, requireAvatarDecorationModal } from "@plugins/decor/ui";
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { Button, Forms, Text } from "@webpack/common";
import { settings } from "../../settings";
import { cl, DecorationModalStyles, requireAvatarDecorationModal } from "../";
import { openCreateDecorationModal } from "./CreateDecorationModal";
function GuidelinesModal(props: ModalProps) {

View file

@ -18,8 +18,8 @@
import { addMessagePreEditListener, addMessagePreSendListener, removeMessagePreEditListener, removeMessagePreSendListener } from "@api/MessageEvents";
import { definePluginSettings } from "@api/Settings";
import { ApngBlendOp, ApngDisposeOp, parseAPNG } from "@utils/apng";
import { Devs } from "@utils/constants";
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
import { getCurrentGuild, getEmojiURL } from "@utils/discord";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType, Patch } from "@utils/types";
@ -706,9 +706,10 @@ export default definePlugin({
},
async sendAnimatedSticker(stickerLink: string, stickerId: string, channelId: string) {
const { parseURL } = importApngJs();
const { frames, width, height } = await parseURL(stickerLink);
const { frames, width, height } = await fetch(stickerLink)
.then(res => res.arrayBuffer())
.then(parseAPNG);
const gif = GIFEncoder();
const resolution = settings.store.stickerSize;
@ -757,7 +758,7 @@ export default definePlugin({
gif.finish();
const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" });
const file = new File([gif.bytesView() as Uint8Array<ArrayBuffer>], `${stickerId}.gif`, { type: "image/gif" });
UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), DraftType.ChannelMessage);
},

View file

@ -17,15 +17,15 @@
*/
// This plugin is a port from Alyxia's Vendetta plugin
import "./index.css";
import "./styles.css";
import { definePluginSettings } from "@api/Settings";
import { Divider } from "@components/Divider";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { fetchUserProfile } from "@utils/discord";
import { copyWithToast, fetchUserProfile } from "@utils/discord";
import { Margins } from "@utils/margins";
import { classes, copyWithToast } from "@utils/misc";
import { classes } from "@utils/misc";
import { useAwaiter } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { User, UserProfile } from "@vencord/discord-types";

View file

@ -18,12 +18,11 @@
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { settings } from "@plugins/imageZoom";
import { ELEMENT_ID } from "@plugins/imageZoom/constants";
import { waitFor } from "@plugins/imageZoom/utils/waitFor";
import { FluxDispatcher, useLayoutEffect, useMemo, useRef, useState } from "@webpack/common";
import { ELEMENT_ID } from "../constants";
import { settings } from "../index";
import { waitFor } from "../utils/waitFor";
interface Vec2 {
x: number,
y: number;

View file

@ -1,77 +0,0 @@
/*
* 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 {
ModalContent,
ModalFooter,
ModalHeader,
ModalRoot,
openModal,
} from "@utils/modal";
import { Button, Forms, React, TextInput } from "@webpack/common";
import { decrypt } from "../index";
export function DecModal(props: any) {
const encryptedMessage: string = props?.message?.content;
const [password, setPassword] = React.useState("password");
return (
<ModalRoot {...props}>
<ModalHeader>
<Forms.FormTitle tag="h4">Decrypt Message</Forms.FormTitle>
</ModalHeader>
<ModalContent>
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Message with Encryption</Forms.FormTitle>
<TextInput defaultValue={encryptedMessage} disabled={true}></TextInput>
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Password</Forms.FormTitle>
<TextInput
style={{ marginBottom: "20px" }}
onChange={setPassword}
/>
</ModalContent>
<ModalFooter>
<Button
color={Button.Colors.GREEN}
onClick={() => {
const toSend = decrypt(encryptedMessage, password, true);
if (!toSend || !props?.message) return;
// @ts-expect-error
Vencord.Plugins.plugins.InvisibleChat.buildEmbed(props?.message, toSend);
props.onClose();
}}>
Decrypt
</Button>
<Button
color={Button.Colors.TRANSPARENT}
look={Button.Looks.LINK}
style={{ left: 15, position: "absolute" }}
onClick={props.onClose}
>
Cancel
</Button>
</ModalFooter>
</ModalRoot>
);
}
export function buildDecModal(msg: any): any {
openModal((props: any) => <DecModal {...props} {...msg} />);
}

View file

@ -1,112 +0,0 @@
/*
* 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 { FormSwitch } from "@components/FormSwitch";
import { insertTextIntoChatInputBox } from "@utils/discord";
import {
ModalContent,
ModalFooter,
ModalHeader,
ModalProps,
ModalRoot,
openModal,
} from "@utils/modal";
import { Button, Forms, React, TextInput } from "@webpack/common";
import { encrypt } from "../index";
function EncModal(props: ModalProps) {
const [secret, setSecret] = React.useState("");
const [cover, setCover] = React.useState("");
const [password, setPassword] = React.useState("password");
const [noCover, setNoCover] = React.useState(false);
const isValid = secret && (noCover || (cover && cover.trim().split(" ").length > 1));
return (
<ModalRoot {...props}>
<ModalHeader>
<Forms.FormTitle tag="h4">Encrypt Message</Forms.FormTitle>
</ModalHeader>
<ModalContent>
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Secret</Forms.FormTitle>
<TextInput
onChange={(e: string) => {
setSecret(e);
}}
/>
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Cover (2 or more Words!!)</Forms.FormTitle>
<TextInput
disabled={noCover}
onChange={(e: string) => {
setCover(e);
}}
/>
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Password</Forms.FormTitle>
<TextInput
style={{ marginBottom: "20px" }}
defaultValue={"password"}
onChange={(e: string) => {
setPassword(e);
}}
/>
<FormSwitch
title="Don't use a Cover"
value={noCover}
onChange={(e: boolean) => {
setNoCover(e);
}}
/>
</ModalContent>
<ModalFooter>
<Button
color={Button.Colors.GREEN}
disabled={!isValid}
onClick={() => {
if (!isValid) return;
const encrypted = encrypt(secret, password, noCover ? "d d" : cover);
const toSend = noCover ? encrypted.replaceAll("d", "") : encrypted;
if (!toSend) return;
insertTextIntoChatInputBox(toSend);
props.onClose();
}}
>
Send
</Button>
<Button
color={Button.Colors.TRANSPARENT}
look={Button.Looks.LINK}
style={{ left: 15, position: "absolute" }}
onClick={() => {
props.onClose();
}}
>
Cancel
</Button>
</ModalFooter>
</ModalRoot>
);
}
export function buildEncModal(): any {
openModal(props => <EncModal {...props} />);
}

View file

@ -1,227 +0,0 @@
/*
* 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 { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";
import { updateMessage } from "@api/MessageUpdater";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { getStegCloak } from "@utils/dependencies";
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
import { Message } from "@vencord/discord-types";
import { ChannelStore, Constants, RestAPI, Tooltip } from "@webpack/common";
import { buildDecModal } from "./components/DecryptionModal";
import { buildEncModal } from "./components/EncryptionModal";
let steggo: any;
function PopOverIcon() {
return (
<svg
fill="var(--header-secondary)"
width={24} height={24}
viewBox={"0 0 64 64"}
>
<path d="M 32 9 C 24.832 9 19 14.832 19 22 L 19 27.347656 C 16.670659 28.171862 15 30.388126 15 33 L 15 49 C 15 52.314 17.686 55 21 55 L 43 55 C 46.314 55 49 52.314 49 49 L 49 33 C 49 30.388126 47.329341 28.171862 45 27.347656 L 45 22 C 45 14.832 39.168 9 32 9 z M 32 13 C 36.963 13 41 17.038 41 22 L 41 27 L 23 27 L 23 22 C 23 17.038 27.037 13 32 13 z" />
</svg>
);
}
function Indicator() {
return (
<Tooltip text="This message has a hidden message! (InvisibleChat)">
{({ onMouseEnter, onMouseLeave }) => (
<img
aria-label="Hidden Message Indicator (InvisibleChat)"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
src="https://github.com/SammCheese/invisible-chat/raw/NewReplugged/src/assets/lock.png"
width={20}
height={20}
style={{ transform: "translateY(4p)", paddingInline: 4 }}
/>
)}
</Tooltip>
);
}
const ChatBarIcon: ChatBarButtonFactory = ({ isMainChat }) => {
if (!isMainChat) return null;
return (
<ChatBarButton
tooltip="Encrypt Message"
onClick={() => buildEncModal()}
buttonProps={{
"aria-haspopup": "dialog",
}}
>
<svg
aria-hidden
role="img"
width="20"
height="20"
viewBox={"0 0 64 64"}
style={{ scale: "1.39", translate: "0 -1px" }}
>
<path fill="currentColor" d="M 32 9 C 24.832 9 19 14.832 19 22 L 19 27.347656 C 16.670659 28.171862 15 30.388126 15 33 L 15 49 C 15 52.314 17.686 55 21 55 L 43 55 C 46.314 55 49 52.314 49 49 L 49 33 C 49 30.388126 47.329341 28.171862 45 27.347656 L 45 22 C 45 14.832 39.168 9 32 9 z M 32 13 C 36.963 13 41 17.038 41 22 L 41 27 L 23 27 L 23 22 C 23 17.038 27.037 13 32 13 z" />
</svg>
</ChatBarButton>
);
};
const settings = definePluginSettings({
savedPasswords: {
type: OptionType.STRING,
default: "password, Password",
description: "Saved Passwords (Seperated with a , )"
}
});
export default definePlugin({
name: "InvisibleChat",
description: "Encrypt your Messages in a non-suspicious way!",
authors: [Devs.SammCheese],
dependencies: ["MessageUpdaterAPI"],
reporterTestable: ReporterTestable.Patches,
settings,
patches: [
{
// Indicator
find: ".SEND_FAILED,",
replacement: {
match: /let\{className:\i,message:\i[^}]*\}=(\i)/,
replace: "try {$1 && $self.INV_REGEX.test($1.message.content) ? $1.content.push($self.indicator()) : null } catch {};$&"
}
},
],
EMBED_API_URL: "https://embed.sammcheese.net",
INV_REGEX: new RegExp(/( \u200c|\u200d |[\u2060-\u2064])[^\u200b]/),
URL_REGEX: new RegExp(
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/,
),
async start() {
const { default: StegCloak } = await getStegCloak();
steggo = new StegCloak(true, false);
},
renderMessagePopoverButton(message) {
return this.INV_REGEX.test(message?.content)
? {
label: "Decrypt Message",
icon: this.popOverIcon,
message: message,
channel: ChannelStore.getChannel(message.channel_id),
onClick: async () => {
const res = await iteratePasswords(message);
if (res)
this.buildEmbed(message, res);
else
buildDecModal({ message });
}
}
: null;
},
renderChatBarButton: ChatBarIcon,
colorCodeFromNumber(color: number): string {
return `#${[color >> 16, color >> 8, color]
.map(x => (x & 0xFF).toString(16))
.join("")}`;
},
// Gets the Embed of a Link
async getEmbed(url: URL): Promise<Object | {}> {
const { body } = await RestAPI.post({
url: Constants.Endpoints.UNFURL_EMBED_URLS,
body: {
urls: [url]
}
});
// The endpoint returns the color as a number, but Discord expects a string
body.embeds[0].color = this.colorCodeFromNumber(body.embeds[0].color);
return await body.embeds[0];
},
async buildEmbed(message: any, revealed: string): Promise<void> {
const urlCheck = revealed.match(this.URL_REGEX);
message.embeds.push({
type: "rich",
rawTitle: "Decrypted Message",
color: "#45f5f5",
rawDescription: revealed,
footer: {
text: "Made with ❤️ by c0dine and Sammy!",
},
});
if (urlCheck?.length) {
const embed = await this.getEmbed(new URL(urlCheck[0]));
if (embed)
message.embeds.push(embed);
}
updateMessage(message.channel_id, message.id, { embeds: message.embeds });
},
popOverIcon: () => <PopOverIcon />,
indicator: ErrorBoundary.wrap(Indicator, { noop: true })
});
export function encrypt(secret: string, password: string, cover: string): string {
return steggo.hide(secret + "\u200b", password, cover);
}
export function decrypt(encrypted: string, password: string, removeIndicator: boolean): string {
const decrypted = steggo.reveal(encrypted, password);
return removeIndicator ? decrypted.replace("\u200b", "") : decrypted;
}
export function isCorrectPassword(result: string): boolean {
return result.endsWith("\u200b");
}
export async function iteratePasswords(message: Message): Promise<string | false> {
const passwords = settings.store.savedPasswords.split(",").map(s => s.trim());
if (!message?.content || !passwords?.length) return false;
let { content } = message;
// we use an extra variable so we dont have to edit the message content directly
if (/^\W/.test(message.content)) content = `d ${message.content}d`;
for (let i = 0; i < passwords.length; i++) {
const result = decrypt(content, passwords[i], false);
if (isCorrectPassword(result)) {
return result;
}
}
return false;
}

View file

@ -16,13 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { isPluginEnabled } from "@api/PluginManager";
import { definePluginSettings } from "@api/Settings";
import NoReplyMentionPlugin from "@plugins/noReplyMention";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { MessageFlags } from "@vencord/discord-types/enums";
import { findByPropsLazy } from "@webpack";
import { FluxDispatcher, MessageTypeSets, PermissionsBits, PermissionStore, UserStore, WindowStore } from "@webpack/common";
import NoReplyMentionPlugin from "plugins/noReplyMention";
const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage");
const EditStore = findByPropsLazy("isEditing", "isEditingAny");
@ -93,7 +94,7 @@ export default definePlugin({
if (!MessageTypeSets.REPLYABLE.has(msg.type) || msg.hasFlag(MessageFlags.EPHEMERAL)) return;
const isShiftPress = event.shiftKey && !settings.store.requireModifier;
const shouldMention = Vencord.Plugins.isPluginEnabled(NoReplyMentionPlugin.name)
const shouldMention = isPluginEnabled(NoReplyMentionPlugin.name)
? NoReplyMentionPlugin.shouldMention(msg, isShiftPress)
: !isShiftPress;

View file

@ -19,6 +19,7 @@
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { InfoIcon, OwnerCrownIcon } from "@components/Icons";
import { cl, getGuildPermissionSpecMap } from "@plugins/permissionsViewer/utils";
import { copyToClipboard } from "@utils/clipboard";
import { getIntlMessage, getUniqueUsername } from "@utils/discord";
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
@ -27,7 +28,6 @@ import { findByCodeLazy } from "@webpack";
import { ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildRoleStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, useMemo, UserStore, useState, useStateFromStores } from "@webpack/common";
import { settings } from "..";
import { cl, getGuildPermissionSpecMap } from "../utils";
import { PermissionAllowedIcon, PermissionDefaultIcon, PermissionDeniedIcon } from "./icons";
export const enum PermissionType {

View file

@ -18,6 +18,7 @@
import ErrorBoundary from "@components/ErrorBoundary";
import { HeadingTertiary } from "@components/Heading";
import { cl, getGuildPermissionSpecMap, getSortedRolesForMember, sortUserRoles } from "@plugins/permissionsViewer/utils";
import { getIntlMessage } from "@utils/discord";
import { classes } from "@utils/misc";
import type { Guild, GuildMember } from "@vencord/discord-types";
@ -25,7 +26,6 @@ import { filters, findBulk, proxyLazyWebpack } from "@webpack";
import { PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common";
import { PermissionsSortOrder, settings } from "..";
import { cl, getGuildPermissionSpecMap, getSortedRolesForMember, sortUserRoles } from "../utils";
import openRolesAndUsersPermissionsModal, { PermissionType, type RoleOrUserPermission } from "./RolesAndUsersPermissions";
interface UserPermission {

View file

@ -6,13 +6,12 @@
import { classNameFactory } from "@api/Styles";
import { Divider } from "@components/Divider";
import { DEFAULT_COLOR, SWATCHES } from "@plugins/pinDms/constants";
import { categoryLen, createCategory, getCategory } from "@plugins/pinDms/data";
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal";
import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack";
import { Button, ColorPicker, Forms, Text, TextInput, Toasts, useMemo, useState } from "@webpack/common";
import { DEFAULT_COLOR, SWATCHES } from "../constants";
import { categoryLen, createCategory, getCategory } from "../data";
interface ColorPickerWithSwatchesProps {
defaultColor: number;
colors: number[];

View file

@ -5,10 +5,10 @@
*/
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { PinOrder, settings } from "@plugins/pinDms";
import { addChannelToCategory, canMoveChannelInDirection, currentUserCategories, isPinned, moveChannel, removeChannelFromCategory } from "@plugins/pinDms/data";
import { Menu } from "@webpack/common";
import { addChannelToCategory, canMoveChannelInDirection, currentUserCategories, isPinned, moveChannel, removeChannelFromCategory } from "../data";
import { PinOrder, settings } from "../index";
import { openCategoryModal } from "./CreateCategoryModal";
function createPinMenuItem(channelId: string) {

View file

@ -4,11 +4,10 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { PinOrder, PrivateChannelSortStore, settings } from "@plugins/pinDms";
import { useForceUpdater } from "@utils/react";
import { UserStore } from "@webpack/common";
import { PinOrder, PrivateChannelSortStore, settings } from "./index";
export interface Category {
id: string;
name: string;

View file

@ -16,14 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { isPluginEnabled } from "@api/PluginManager";
import { definePluginSettings } from "@api/Settings";
import NoBlockedMessagesPlugin from "@plugins/noBlockedMessages";
import NoReplyMentionPlugin from "@plugins/noReplyMention";
import { Devs, IS_MAC } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { Message } from "@vencord/discord-types";
import { MessageFlags } from "@vencord/discord-types/enums";
import { ChannelStore, ComponentDispatch, FluxDispatcher as Dispatcher, MessageActions, MessageStore, MessageTypeSets, PermissionsBits, PermissionStore, RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common";
import NoBlockedMessagesPlugin from "plugins/noBlockedMessages";
import NoReplyMentionPlugin from "plugins/noReplyMention";
let currentlyReplyingId: string | null = null;
let currentlyEditingId: string | null = null;
@ -134,7 +135,7 @@ function getNextMessage(isUp: boolean, isReply: boolean) {
let messages: Message[] = MessageStore.getMessages(SelectedChannelStore.getChannelId())._array;
const meId = UserStore.getCurrentUser().id;
const hasNoBlockedMessages = Vencord.Plugins.isPluginEnabled(NoBlockedMessagesPlugin.name);
const hasNoBlockedMessages = isPluginEnabled(NoBlockedMessagesPlugin.name);
messages = messages.filter(m => {
if (m.deleted) return false;
@ -170,7 +171,7 @@ function getNextMessage(isUp: boolean, isReply: boolean) {
function shouldMention(message: Message) {
switch (settings.store.shouldMention) {
case MentionOptions.NO_REPLY_MENTION_PLUGIN:
if (!Vencord.Plugins.isPluginEnabled(NoReplyMentionPlugin.name)) return true;
if (!isPluginEnabled(NoReplyMentionPlugin.name)) return true;
return NoReplyMentionPlugin.shouldMention(message, false);
case MentionOptions.DISABLED:
return false;

View file

@ -16,7 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { DataStore, Notices } from "@api/index";
import * as DataStore from "@api/DataStore";
import { popNotice, showNotice } from "@api/Notices";
import { showNotification } from "@api/Notifications";
import { getUniqueUsername, openUserProfile } from "@utils/discord";
import { FluxStore } from "@vencord/discord-types";
@ -111,7 +112,7 @@ export async function syncAndRunChecks() {
export function notify(text: string, icon?: string, onClick?: () => void) {
if (settings.store.notices)
Notices.showNotice(text, "OK", () => Notices.popNotice());
showNotice(text, "OK", () => popNotice());
showNotification({
title: "Relationship Notifier",

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { DataStore } from "@api/index";
import * as DataStore from "@api/DataStore";
import { Logger } from "@utils/Logger";
import { openModal } from "@utils/modal";
import { OAuth2AuthorizeModal, showToast, Toasts, UserStore } from "@webpack/common";

View file

@ -4,16 +4,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Auth } from "@plugins/reviewDB/auth";
import { ReviewDBUser } from "@plugins/reviewDB/entities";
import { fetchBlocks, unblockUser } from "@plugins/reviewDB/reviewDbApi";
import { cl } from "@plugins/reviewDB/utils";
import { Logger } from "@utils/Logger";
import { ModalCloseButton, ModalContent, ModalHeader, ModalRoot, openModal } from "@utils/modal";
import { useAwaiter } from "@utils/react";
import { Forms, Tooltip, useState } from "@webpack/common";
import { Auth } from "../auth";
import { ReviewDBUser } from "../entities";
import { fetchBlocks, unblockUser } from "../reviewDbApi";
import { cl } from "../utils";
function UnblockButton(props: { onClick?(): void; }) {
return (
<Tooltip text="Unblock user">

View file

@ -16,12 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Badge } from "@plugins/reviewDB/entities";
import { cl } from "@plugins/reviewDB/utils";
import { MaskedLink, React, Tooltip } from "@webpack/common";
import { HTMLAttributes } from "react";
import { Badge } from "../entities";
import { cl } from "../utils";
export default function ReviewBadge(badge: Badge & { onClick?(): void; }) {
const Wrapper = badge.redirectURL
? MaskedLink

View file

@ -16,17 +16,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Auth, getToken } from "@plugins/reviewDB/auth";
import { Review, ReviewType } from "@plugins/reviewDB/entities";
import { blockUser, deleteReview, reportReview, unblockUser } from "@plugins/reviewDB/reviewDbApi";
import { settings } from "@plugins/reviewDB/settings";
import { canBlockReviewAuthor, canDeleteReview, canReportReview, cl, showToast } from "@plugins/reviewDB/utils";
import { openUserProfile } from "@utils/discord";
import { classes } from "@utils/misc";
import { LazyComponent } from "@utils/react";
import { filters, findBulk } from "@webpack";
import { Alerts, Parser, Timestamp, useState } from "@webpack/common";
import { Auth, getToken } from "../auth";
import { Review, ReviewType } from "../entities";
import { blockUser, deleteReview, reportReview, unblockUser } from "../reviewDbApi";
import { settings } from "../settings";
import { canBlockReviewAuthor, canDeleteReview, canReportReview, cl, showToast } from "../utils";
import { openBlockModal } from "./BlockedUserModal";
import { BlockButton, DeleteButton, ReportButton } from "./MessageButton";
import ReviewBadge from "./ReviewBadge";

View file

@ -17,14 +17,14 @@
*/
import ErrorBoundary from "@components/ErrorBoundary";
import { Auth } from "@plugins/reviewDB/auth";
import { ReviewType } from "@plugins/reviewDB/entities";
import { Response, REVIEWS_PER_PAGE } from "@plugins/reviewDB/reviewDbApi";
import { cl } from "@plugins/reviewDB/utils";
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { useForceUpdater } from "@utils/react";
import { Paginator, Text, useRef, useState } from "@webpack/common";
import { Auth } from "../auth";
import { ReviewType } from "../entities";
import { Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
import { cl } from "../utils";
import ReviewComponent from "./ReviewComponent";
import ReviewsView, { ReviewsInputComponent } from "./ReviewsView";

View file

@ -16,15 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Auth, authorize } from "@plugins/reviewDB/auth";
import { Review, ReviewType } from "@plugins/reviewDB/entities";
import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "@plugins/reviewDB/reviewDbApi";
import { settings } from "@plugins/reviewDB/settings";
import { cl, showToast } from "@plugins/reviewDB/utils";
import { useAwaiter, useForceUpdater } from "@utils/react";
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack";
import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common";
import { Auth, authorize } from "../auth";
import { Review, ReviewType } from "../entities";
import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
import { settings } from "../settings";
import { cl, showToast } from "../utils";
import ReviewComponent from "./ReviewComponent";
const Transforms = findByPropsLazy("insertNodes", "textToText");

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { DataStore } from "@api/index";
import * as DataStore from "@api/DataStore";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { hasGuildFeature } from "@utils/discord";

View file

@ -16,12 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { dispatchTheme } from "@plugins/shikiCodeblocks.desktop/hooks/useTheme";
import type { ShikiSpec } from "@plugins/shikiCodeblocks.desktop/types";
import { shikiOnigasmSrc, shikiWorkerSrc } from "@utils/dependencies";
import { WorkerClient } from "@vap/core/ipc";
import type { IShikiTheme, IThemedToken } from "@vap/shiki";
import { dispatchTheme } from "../hooks/useTheme";
import type { ShikiSpec } from "../types";
import { getGrammar, languages, loadLanguages, resolveLang } from "./languages";
import { themes } from "./themes";

View file

@ -16,7 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { cl } from "../utils/misc";
import { cl } from "@plugins/shikiCodeblocks.desktop/utils/misc";
import { CopyButton } from "./CopyButton";
export interface ButtonRowProps {

View file

@ -16,11 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { cl } from "@plugins/shikiCodeblocks.desktop/utils/misc";
import type { IThemedToken } from "@vap/shiki";
import { hljs } from "@webpack/common";
import { JSX } from "react";
import { cl } from "../utils/misc";
import { ThemeBase } from "./Highlighter";
export interface CodeProps {

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { useCopyCooldown } from "../hooks/useCopyCooldown";
import { useCopyCooldown } from "@plugins/shikiCodeblocks.desktop/hooks/useCopyCooldown";
export interface CopyButtonProps extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
content: string;

View file

@ -16,9 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Language } from "../api/languages";
import { DeviconSetting } from "../types";
import { cl } from "../utils/misc";
import { Language } from "@plugins/shikiCodeblocks.desktop/api/languages";
import { DeviconSetting } from "@plugins/shikiCodeblocks.desktop/types";
import { cl } from "@plugins/shikiCodeblocks.desktop/utils/misc";
export interface HeaderProps {
langName?: string;

View file

@ -17,15 +17,15 @@
*/
import ErrorBoundary from "@components/ErrorBoundary";
import { resolveLang } from "@plugins/shikiCodeblocks.desktop/api/languages";
import { shiki } from "@plugins/shikiCodeblocks.desktop/api/shiki";
import { useShikiSettings } from "@plugins/shikiCodeblocks.desktop/hooks/useShikiSettings";
import { useTheme } from "@plugins/shikiCodeblocks.desktop/hooks/useTheme";
import { hex2Rgb } from "@plugins/shikiCodeblocks.desktop/utils/color";
import { cl, shouldUseHljs } from "@plugins/shikiCodeblocks.desktop/utils/misc";
import { useAwaiter, useIntersection } from "@utils/react";
import { hljs, React } from "@webpack/common";
import { resolveLang } from "../api/languages";
import { shiki } from "../api/shiki";
import { useShikiSettings } from "../hooks/useShikiSettings";
import { useTheme } from "../hooks/useTheme";
import { hex2Rgb } from "../utils/color";
import { cl, shouldUseHljs } from "../utils/misc";
import { ButtonRow } from "./ButtonRow";
import { Code } from "./Code";
import { Header } from "./Header";

View file

@ -16,11 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { shiki } from "@plugins/shikiCodeblocks.desktop/api/shiki";
import { settings as pluginSettings, ShikiSettings } from "@plugins/shikiCodeblocks.desktop/settings";
import { React } from "@webpack/common";
import { shiki } from "../api/shiki";
import { settings as pluginSettings, ShikiSettings } from "../settings";
export function useShikiSettings<F extends keyof ShikiSettings>(settingKeys: F[]) {
const settings = pluginSettings.use([...settingKeys, "customTheme", "theme"]);
const [isLoading, setLoading] = React.useState(false);

View file

@ -17,12 +17,11 @@
*/
import { classNameFactory } from "@api/Styles";
import { resolveLang } from "@plugins/shikiCodeblocks.desktop/api/languages";
import { HighlighterProps } from "@plugins/shikiCodeblocks.desktop/components/Highlighter";
import { HljsSetting } from "@plugins/shikiCodeblocks.desktop/types";
import { hljs } from "@webpack/common";
import { resolveLang } from "../api/languages";
import { HighlighterProps } from "../components/Highlighter";
import { HljsSetting } from "../types";
export const cl = classNameFactory("vc-shiki-");
export const shouldUseHljs = ({

View file

@ -18,17 +18,18 @@
import "./styles.css";
import { isPluginEnabled } from "@api/PluginManager";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { CopyIcon, LinkIcon } from "@components/Icons";
import OpenInAppPlugin from "@plugins/openInApp";
import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/misc";
import { copyWithToast } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { ConnectedAccount, User } from "@vencord/discord-types";
import { findByCodeLazy, findByPropsLazy } from "@webpack";
import { Tooltip, UserProfileStore } from "@webpack/common";
import OpenInAppPlugin from "plugins/openInApp";
import { VerifiedIcon } from "./VerifiedIcon";
@ -133,7 +134,7 @@ function CompactConnectionComponent({ connection, theme }: { connection: Connect
target="_blank"
rel="noreferrer"
onClick={e => {
if (Vencord.Plugins.isPluginEnabled("OpenInApp")) {
if (isPluginEnabled(OpenInAppPlugin.name)) {
// handleLink will .preventDefault() if applicable
OpenInAppPlugin.handleLink(e.currentTarget, e);
}

View file

@ -16,16 +16,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { isPluginEnabled } from "@api/PluginManager";
import { Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import PermissionsViewerPlugin from "@plugins/permissionsViewer";
import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "@plugins/permissionsViewer/components/RolesAndUsersPermissions";
import { sortPermissionOverwrites } from "@plugins/permissionsViewer/utils";
import { classes } from "@utils/misc";
import { formatDuration } from "@utils/text";
import type { Channel } from "@vencord/discord-types";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "../../permissionsViewer/components/RolesAndUsersPermissions";
import { sortPermissionOverwrites } from "../../permissionsViewer/utils";
import { cl, settings } from "..";
const enum SortOrderTypes {
@ -274,7 +276,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
}
<div className={cl("allowed-users-and-roles-container")}>
<div className={cl("allowed-users-and-roles-container-title")}>
{Vencord.Plugins.isPluginEnabled("PermissionsViewer") && (
{isPluginEnabled(PermissionsViewerPlugin.name) && (
<Tooltip text="Permission Details">
{({ onMouseLeave, onMouseEnter }) => (
<button

View file

@ -25,8 +25,8 @@ import { CopyIcon, ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Ico
import { Paragraph } from "@components/Paragraph";
import { Span } from "@components/Span";
import { debounce } from "@shared/debounce";
import { openImageModal } from "@utils/discord";
import { classes, copyWithToast } from "@utils/misc";
import { copyWithToast, openImageModal } from "@utils/discord";
import { classes } from "@utils/misc";
import { ContextMenuApi, FluxDispatcher, Menu, React, useEffect, useState, useStateFromStores } from "@webpack/common";
import { SeekBar } from "./SeekBar";

View file

@ -16,7 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { isPluginEnabled } from "@api/PluginManager";
import { Settings } from "@api/Settings";
import OpenInAppPlugin from "@plugins/openInApp";
import { findByProps, findByPropsLazy, proxyLazyWebpack } from "@webpack";
import { Flux, FluxDispatcher } from "@webpack/common";
@ -89,7 +91,7 @@ export const SpotifyStore = proxyLazyWebpack(() => {
public isSettingPosition = false;
public openExternal(path: string) {
const url = Settings.plugins.SpotifyControls.useSpotifyUris || Vencord.Plugins.isPluginEnabled("OpenInApp")
const url = Settings.plugins.SpotifyControls.useSpotifyUris || isPluginEnabled(OpenInAppPlugin.name)
? "spotify:" + path.replaceAll("/", (_, idx) => idx === 0 ? "" : ":")
: "https://open.spotify.com" + path;

View file

@ -20,14 +20,13 @@ import "./style.css";
import { definePluginSettings, Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { buildSeveralUsers } from "@plugins/typingTweaks";
import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { findComponentByCodeLazy, findStoreLazy } from "@webpack";
import { GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, TypingStore, UserStore, UserSummaryItem, useStateFromStores } from "@webpack/common";
import { buildSeveralUsers } from "../typingTweaks";
const ThreeDots = findComponentByCodeLazy(".dots,", "dotRadius:");
const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore");

View file

@ -30,7 +30,7 @@ export default definePlugin({
find: "inQuote:",
replacement: {
match: /,content:([^,]+),inQuote/,
replace: (_, content) => `,content:Vencord.Plugins.plugins.Unindent.unindent(${content}),inQuote`
replace: (_, content) => `,content:$self.unindent(${content}),inQuote`
}
}
],

View file

@ -4,8 +4,10 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { isPluginEnabled } from "@api/PluginManager";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import ShowHiddenChannelsPlugin from "@plugins/showHiddenChannels";
import { classes } from "@utils/misc";
import { Channel } from "@vencord/discord-types";
import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
@ -141,7 +143,7 @@ export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId, isProfile, is
if (channel == null) return null;
const isDM = channel.isDM() || channel.isMultiUserDM();
if (!isDM && !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) && !Vencord.Plugins.isPluginEnabled("ShowHiddenChannels")) return null;
if (!isDM && !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) && !isPluginEnabled(ShowHiddenChannelsPlugin.name)) return null;
const isLocked = !isDM && (!PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) || !PermissionStore.can(PermissionsBits.CONNECT, channel));

View file

@ -16,9 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import "./index.css";
import "./styles.css";
import { openNotificationLogModal } from "@api/Notifications/notificationLog";
import { isPluginEnabled, plugins } from "@api/PluginManager";
import { Settings, useSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
@ -34,8 +35,8 @@ function VencordPopout(onClose: () => void) {
const pluginEntries = [] as ReactNode[];
for (const plugin of Object.values(Vencord.Plugins.plugins)) {
if (plugin.toolboxActions && Vencord.Plugins.isPluginEnabled(plugin.name)) {
for (const plugin of Object.values(plugins)) {
if (plugin.toolboxActions && isPluginEnabled(plugin.name)) {
pluginEntries.push(
<Menu.MenuGroup
label={plugin.name}

View file

@ -23,9 +23,8 @@ import { Divider } from "@components/Divider";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { Devs } from "@utils/constants";
import { getCurrentGuild, getIntlMessage } from "@utils/discord";
import { copyWithToast, getCurrentGuild, getIntlMessage } from "@utils/discord";
import { Margins } from "@utils/margins";
import { copyWithToast } from "@utils/misc";
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
import definePlugin, { OptionType } from "@utils/types";
import { Message } from "@vencord/discord-types";

File diff suppressed because it is too large Load diff

258
src/utils/apng.ts Normal file
View file

@ -0,0 +1,258 @@
/* eslint-disable */
/**
* apng-canvas v2.1.2 - heavily modified and converted to Typescript
*
* @copyright 2011-2019 David Mzareulyan
* @link https://github.com/davidmz/apng-canvas
* @license MIT
*/
// https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk
export const enum ApngDisposeOp {
/**
* no disposal is done on this frame before rendering the next; the contents of the output buffer are left as is.
*/
NONE,
/**
* the frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame.
*/
BACKGROUND,
/**
* the frame's region of the output buffer is to be reverted to the previous contents before rendering the next frame.
*/
PREVIOUS
}
// TODO: Might need to somehow implement this
export const enum ApngBlendOp {
SOURCE,
OVER
}
interface Frame {
width: number;
height: number;
left: number;
top: number;
delay: number;
disposeOp: ApngDisposeOp;
blendOp: ApngBlendOp;
dataParts?: U8Arr[];
img: HTMLImageElement;
}
class Animation {
width = 0;
height = 0;
numPlays = 0;
playTime = 0;
frames: Frame[] = [];
}
// Typescript has become more strict, requiring you to explicitly specify the ArrayBuffer generic
type U8Arr = Uint8Array<ArrayBuffer>;
const table = new Uint32Array(256);
for (let i = 0; i < 256; i++) {
let c = i;
for (let k = 0; k < 8; k++) c = (c & 1) ? 0xEDB88320 ^ (c >>> 1) : c >>> 1;
table[i] = c;
}
function crc32(bytes: U8Arr, start: number = 0, length?: number): number {
length = length ?? (bytes.length - start);
let crc = -1;
for (let i = start, l = start + length; i < l; i++) {
crc = (crc >>> 8) ^ table[(crc ^ bytes[i]) & 0xFF];
}
return crc ^ (-1);
}
const PNG_SIGNATURE_BYTES = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
export function parseAPNG(buffer: ArrayBuffer): Promise<Animation> {
const bytes = new Uint8Array(buffer);
return new Promise(function (resolve, reject) {
for (let i = 0; i < PNG_SIGNATURE_BYTES.length; i++) {
if (PNG_SIGNATURE_BYTES[i] != bytes[i]) {
reject("Not a PNG file (invalid file signature)");
return;
}
}
// fast animation test
let isAnimated = false;
parseChunks(bytes, function (type) {
if (type == "acTL") {
isAnimated = true;
return false;
}
return true;
});
if (!isAnimated) {
reject("Not an animated PNG");
return;
}
const preDataParts: U8Arr[] = [];
const postDataParts: U8Arr[] = [];
let headerDataBytes: U8Arr | null = null;
let frame: Frame | null = null;
const anim = new Animation();
parseChunks(bytes, function (type, bytes, off, length) {
switch (type) {
case "IHDR":
headerDataBytes = bytes.subarray(off + 8, off + 8 + length);
anim.width = readDWord(bytes, off + 8);
anim.height = readDWord(bytes, off + 12);
break;
case "acTL":
anim.numPlays = readDWord(bytes, off + 8 + 4);
break;
case "fcTL":
if (frame) anim.frames.push(frame);
frame = {} as Frame;
frame.width = readDWord(bytes, off + 8 + 4);
frame.height = readDWord(bytes, off + 8 + 8);
frame.left = readDWord(bytes, off + 8 + 12);
frame.top = readDWord(bytes, off + 8 + 16);
let delayN = readWord(bytes, off + 8 + 20);
let delayD = readWord(bytes, off + 8 + 22);
if (delayD == 0) delayD = 100;
frame.delay = 1000 * delayN / delayD;
// see http://mxr.mozilla.org/mozilla/source/gfx/src/shared/gfxImageFrame.cpp#343
if (frame.delay <= 10) frame.delay = 100;
anim.playTime += frame.delay;
frame.disposeOp = readByte(bytes, off + 8 + 24);
frame.blendOp = readByte(bytes, off + 8 + 25);
frame.dataParts = [];
break;
case "fdAT":
if (frame) frame.dataParts!.push(bytes.subarray(off + 8 + 4, off + 8 + length));
break;
case "IDAT":
if (frame) frame.dataParts!.push(bytes.subarray(off + 8, off + 8 + length));
break;
case "IEND":
postDataParts.push(subBuffer(bytes, off, 12 + length));
break;
default:
preDataParts.push(subBuffer(bytes, off, 12 + length));
}
});
if (frame) anim.frames.push(frame);
if (anim.frames.length == 0) {
reject("Not an animated PNG");
return;
}
// creating images
let createdImages = 0;
const preBlob = new Blob(preDataParts);
const postBlob = new Blob(postDataParts);
for (let f = 0; f < anim.frames.length; f++) {
frame = anim.frames[f];
const bb: (U8Arr | Blob)[] = [];
bb.push(PNG_SIGNATURE_BYTES);
headerDataBytes!.set(makeDWordArray(frame.width), 0);
headerDataBytes!.set(makeDWordArray(frame.height), 4);
bb.push(makeChunkBytes("IHDR", headerDataBytes!));
bb.push(preBlob);
for (let j = 0; j < frame.dataParts!.length; j++) {
bb.push(makeChunkBytes("IDAT", frame.dataParts![j]));
}
bb.push(postBlob);
const url = URL.createObjectURL(new Blob(bb, { "type": "image/png" }));
delete frame.dataParts;
/**
* Using "createElement" instead of "new Image" because of bug in Chrome 27
* https://code.google.com/p/chromium/issues/detail?id=238071
* http://stackoverflow.com/questions/16377375/using-canvas-drawimage-in-chrome-extension-content-script/16378270
*/
const img = frame.img = new Image();
img.onload = function () {
URL.revokeObjectURL(img.src);
createdImages++;
if (createdImages == anim.frames.length) {
resolve(anim);
}
};
img.onerror = function () {
reject("Image creation error");
};
img.src = url;
}
});
}
function parseChunks(bytes: U8Arr, callback: (type: string, bytes: U8Arr, off: number, length: number) => boolean | void): void {
let off = 8;
let res: boolean | void;
let type: string;
do {
const length = readDWord(bytes, off);
type = readString(bytes, off + 4, 4);
res = callback(type, bytes, off, length);
off += 12 + length;
} while (res !== false && type != "IEND" && off < bytes.length);
}
function readDWord(bytes: U8Arr, off: number): number {
let x = 0;
// Force the most-significant byte to unsigned.
x += ((bytes[0 + off] << 24) >>> 0);
for (let i = 1; i < 4; i++) x += ((bytes[i + off] << ((3 - i) * 8)));
return x;
}
function readWord(bytes: U8Arr, off: number): number {
let x = 0;
for (let i = 0; i < 2; i++) x += (bytes[i + off] << ((1 - i) * 8));
return x;
}
function readByte(bytes: U8Arr, off: number): number {
return bytes[off];
}
function subBuffer(bytes: U8Arr, start: number, length: number): U8Arr {
const a = new Uint8Array(length);
a.set(bytes.subarray(start, start + length));
return a;
}
function readString(bytes: U8Arr, off: number, length: number): string {
const chars = Array.prototype.slice.call(bytes.subarray(off, off + length));
return String.fromCharCode.apply(String, chars);
}
function makeDWordArray(x: number): number[] {
return [(x >>> 24) & 0xff, (x >>> 16) & 0xff, (x >>> 8) & 0xff, x & 0xff];
}
function makeStringArray(x: string): number[] {
const res: number[] = [];
for (let i = 0; i < x.length; i++) res.push(x.charCodeAt(i));
return res;
}
function makeChunkBytes(type: string, dataBytes: U8Arr): U8Arr {
const crcLen = type.length + dataBytes.length;
const bytes = new Uint8Array(new ArrayBuffer(crcLen + 8));
bytes.set(makeDWordArray(dataBytes.length), 0);
bytes.set(makeStringArray(type), 4);
bytes.set(dataBytes, 8);
const crc = crc32(bytes, 4, crcLen);
bytes.set(makeDWordArray(crc), crcLen + 4);
return bytes;
};

View file

@ -30,6 +30,9 @@ const platform = navigator.platform.toLowerCase();
export const IS_WINDOWS = platform.startsWith("win");
export const IS_MAC = platform.startsWith("mac");
export const IS_LINUX = platform.startsWith("linux");
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#mobile_tablet_or_desktop
// "In summary, we recommend looking for the string Mobi anywhere in the User Agent to detect a mobile device."
export const IS_MOBILE = navigator.userAgent.includes("Mobi");
export interface Dev {
name: string;

View file

@ -16,61 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { makeLazy } from "./lazy";
/*
Add dynamically loaded dependencies for plugins here.
*/
// needed to parse APNGs in the nitroBypass plugin
export const importApngJs = makeLazy(() => {
return require("./apng-canvas").APNG as { parseURL(url: string): Promise<ApngFrameData>; };
});
// https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk
export const enum ApngDisposeOp {
/**
* no disposal is done on this frame before rendering the next; the contents of the output buffer are left as is.
*/
NONE,
/**
* the frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame.
*/
BACKGROUND,
/**
* the frame's region of the output buffer is to be reverted to the previous contents before rendering the next frame.
*/
PREVIOUS
}
// TODO: Might need to somehow implement this
export const enum ApngBlendOp {
SOURCE,
OVER
}
export interface ApngFrame {
left: number;
top: number;
width: number;
height: number;
img: HTMLImageElement;
delay: number;
blendOp: ApngBlendOp;
disposeOp: ApngDisposeOp;
}
export interface ApngFrameData {
width: number;
height: number;
frames: ApngFrame[];
playTime: number;
}
// The below code is only used on the Desktop (electron) build of Vencord.
// Browser (extension) builds do not contain these remote imports.
export const shikiWorkerSrc = `https://cdn.jsdelivr.net/npm/@vap/shiki-worker@0.0.8/dist/${IS_DEV ? "index.js" : "index.min.js"}`;
export const shikiOnigasmSrc = "https://cdn.jsdelivr.net/npm/@vap/shiki@0.10.3/dist/onig.wasm";
// @ts-expect-error
export const getStegCloak = /* #__PURE__*/ makeLazy(() => import("https://cdn.jsdelivr.net/npm/stegcloak-dist@1.0.0/index.js"));

View file

@ -16,11 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { MessageObject } from "@api/MessageEvents";
import { Channel, CloudUpload, Guild, GuildFeatures, Message, User } from "@vencord/discord-types";
import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
import type { MessageObject } from "@api/MessageEvents";
import type { Channel, CloudUpload, Guild, GuildFeatures, Message, User } from "@vencord/discord-types";
import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, Toasts, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
import { Except } from "type-fest";
import { copyToClipboard } from "./clipboard";
import { runtimeHashMessageKey, runtimeHashMessageKeyLegacy } from "./intlHash";
import { Logger } from "./Logger";
import { MediaModalItem, MediaModalProps, openMediaModal } from "./modal";
@ -116,6 +117,15 @@ export function insertTextIntoChatInputBox(text: string) {
});
}
export async function copyWithToast(text: string, toastMessage = "Copied to clipboard!") {
await copyToClipboard(text);
Toasts.show({
message: toastMessage,
id: Toasts.genId(),
type: Toasts.Type.SUCCESS
});
}
interface MessageOptions {
messageReference: Message["messageReference"];
allowedMentions: {

View file

@ -22,6 +22,7 @@ export * from "./ChangeList";
export * from "./clipboard";
export * from "./constants";
export * from "./cspViolations";
export * from "./css";
export * from "./discord";
export * from "./guards";
export * from "./intlHash";

View file

@ -16,9 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Toasts } from "@webpack/common";
import { copyToClipboard } from "./clipboard";
import { DevsById } from "./constants";
/**
@ -36,15 +33,6 @@ export function sleep(ms: number): Promise<void> {
return new Promise(r => setTimeout(r, ms));
}
export async function copyWithToast(text: string, toastMessage = "Copied to clipboard!") {
await copyToClipboard(text);
Toasts.show({
message: toastMessage,
id: Toasts.genId(),
type: Toasts.Type.SUCCESS
});
}
/**
* Check if obj is a true object: of type "object" and not null or array
*/
@ -87,10 +75,6 @@ export function identity<T>(value: T): T {
return value;
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#mobile_tablet_or_desktop
// "In summary, we recommend looking for the string Mobi anywhere in the User Agent to detect a mobile device."
export const isMobile = navigator.userAgent.includes("Mobi");
export const isPluginDev = (id: string) => Object.hasOwn(DevsById, id);
export const shouldShowContributorBadge = (id: string) => isPluginDev(id) && DevsById[id].badge !== false;

View file

@ -19,13 +19,8 @@
export function relaunch() {
if (IS_DISCORD_DESKTOP)
window.DiscordNative.app.relaunch();
else
else if (IS_VESKTOP)
window.VesktopNative.app.relaunch();
}
export function showItemInFolder(path: string) {
if (IS_DISCORD_DESKTOP)
window.DiscordNative.fileManager.showItemInFolder(path);
else
window.VesktopNative.fileManager.showItemInFolder(path);
location.reload();
}

View file

@ -17,7 +17,7 @@
*/
import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common";
import { ActionDispatch, ReactNode } from "react";
import type { ActionDispatch, ReactNode } from "react";
import { checkIntersecting } from "./misc";

View file

@ -16,8 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { moment } from "@webpack/common";
// Utils for readable text transformations eg: `toTitle(fromKebab())`
// Case style to words
@ -54,6 +52,7 @@ function getUnitStr(unit: Units, isOne: boolean, short: boolean) {
* @param short Whether to use short units like "d" instead of "days"
*/
export function formatDuration(time: number, unit: Units, short: boolean = false) {
const { moment } = require("@webpack/common") as typeof import("@webpack/common");
const dur = moment.duration(time, unit);
let unitsAmounts = units.map(unit => ({ amount: dur[unit](), unit }));

Some files were not shown because too many files have changed in this diff Show more