Compare commits
59 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9b987d1e56 | ||
|
774318d583 | ||
|
7d954f9ade | ||
|
860d6edc7b | ||
|
223b0366c6 | ||
|
69cb7593eb | ||
|
8614e17633 | ||
|
aecd9d8fda | ||
|
faeb4fb585 | ||
|
0d18b44ba7 | ||
|
d671bd65ac | ||
|
8e952c630b | ||
|
2c758ccdf8 | ||
|
c165725297 | ||
|
29fbe3701a | ||
|
0b7c0e9587 | ||
|
d88524e8cf | ||
|
d6efd99849 | ||
|
fe6be987fd | ||
|
d688075c0a | ||
|
c752be45b2 | ||
|
07c1f5eed1 | ||
|
4df01b1e62 | ||
|
ebe10d3fad | ||
|
eca4af829f | ||
|
60458cdf1f | ||
|
714d87241c | ||
|
f628aa7a70 | ||
|
0f0282551d | ||
|
0335e1ca59 | ||
|
817f0f7473 | ||
|
98a03c8862 | ||
|
72ce7a5ad1 | ||
|
e699ea63c7 | ||
|
97e1e9eb7a | ||
|
4c4036546a | ||
|
d582e61ec3 | ||
|
ccd2ce8baf | ||
|
ede507e80c | ||
|
ffdf63563b | ||
|
55b755b2df | ||
|
ca439e9e9a | ||
|
e02fcce3dc | ||
|
3e732646e5 | ||
|
d5b3b51050 | ||
|
725fb27e54 | ||
|
243381fc91 | ||
|
54cbdfdad0 | ||
|
fe80b8cc85 | ||
|
742f5cf556 | ||
|
3b3da90c44 | ||
|
c79e065d09 | ||
|
3b8b43c7e0 | ||
|
6e7996659f | ||
|
abdf4ebb05 | ||
|
fa124d8877 | ||
|
135da2a5f3 | ||
|
c96a1a9998 | ||
|
8b6c8bc0ec |
@ -4,7 +4,7 @@
|
||||
"ignorePatterns": ["dist", "browser"],
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"header",
|
||||
"simple-header",
|
||||
"simple-import-sort",
|
||||
"unused-imports",
|
||||
"path-alias"
|
||||
@ -26,35 +26,12 @@
|
||||
// Since it's only been a month and Vencord has already been stolen
|
||||
// by random skids who rebranded it to "AlphaCord" and erased all license
|
||||
// information
|
||||
"header/header": [
|
||||
2,
|
||||
"block",
|
||||
[
|
||||
"simple-header/header": [
|
||||
"error",
|
||||
{
|
||||
"pattern": "!?",
|
||||
"template": " "
|
||||
},
|
||||
" * Vencord, a modification for Discord's desktop app",
|
||||
{
|
||||
"pattern": " \\* Copyright \\(c\\) \\d{4}",
|
||||
"template": " * 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/>.",
|
||||
""
|
||||
],
|
||||
2
|
||||
"files": ["scripts/header-new.txt", "scripts/header-old.txt"],
|
||||
"templates": { "author": [".*", "Vendicated and contributors"] }
|
||||
}
|
||||
],
|
||||
"quotes": ["error", "double", { "avoidEscape": true }],
|
||||
"jsx-quotes": ["error", "prefer-double"],
|
||||
|
@ -65,7 +65,7 @@ Also pay attention to the following:
|
||||
- Match any but a guaranteed terminating character: `[^;]+`, for example to match the entire assigned value in `var a=b||c||func();`,
|
||||
`var .{1,2}=([^;]+);`
|
||||
- If you don't care about that part, just match a bunch of chars: `.{0,50}`, for example to extract the variable "b" in `createElement("div",{a:"foo",c:"bar"},b)`, `createElement\("div".{0,30},(.{1,2})\),`. Note the `.{0,30}`, this is essentially the same as `.+`, but safer as you can't end up accidently eating thousands of characters
|
||||
- Additionally, as you might have noticed, all of the appove approaches use regex groups (`(...)`) to capture the variable name. You can then use those groups in your replacement to access those variables dynamically
|
||||
- Additionally, as you might have noticed, all of the above approaches use regex groups (`(...)`) to capture the variable name. You can then use those groups in your replacement to access those variables dynamically
|
||||
|
||||
#### "replace"
|
||||
|
||||
|
@ -4,8 +4,7 @@
|
||||
|
||||
The cutest Discord client mod
|
||||
|
||||
![](https://user-images.githubusercontent.com/45497981/235015332-0453d3eb-1da6-4601-963e-ef5e454123a1.png)
|
||||
*A screenshot of Vencord featuring the [ClearVision-v6 theme](https://github.com/ClearVision/ClearVision-v6) (Vencord does not come with it pre-installed, it is only an example)*
|
||||
![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334)
|
||||
|
||||
## Features
|
||||
|
||||
|
@ -63,7 +63,7 @@ Then fully close Discord from your taskbar or task manager, and restart it. Venc
|
||||
|
||||
If you're using Discord already, go into the `Updater` tab in settings.
|
||||
|
||||
Sometimes it may be neccessary to manually update if the GUI updater fails.
|
||||
Sometimes it may be necessary to manually update if the GUI updater fails.
|
||||
|
||||
To pull latest changes:
|
||||
|
||||
|
11
package.json
11
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.4.2",
|
||||
"version": "1.4.6",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
@ -11,7 +11,7 @@
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Vendicated/Vencord.git"
|
||||
},
|
||||
"license": "GPL-3.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"author": "Vendicated",
|
||||
"directories": {
|
||||
"doc": "docs"
|
||||
@ -31,8 +31,10 @@
|
||||
"watch": "node scripts/build/build.mjs --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sapphi-red/web-noise-suppressor": "0.3.3",
|
||||
"@vap/core": "0.0.12",
|
||||
"@vap/shiki": "0.10.5",
|
||||
"eslint-plugin-simple-header": "^1.0.2",
|
||||
"fflate": "^0.7.4",
|
||||
"nanoid": "^4.0.2",
|
||||
"virtual-merge": "^1.0.1"
|
||||
@ -49,9 +51,8 @@
|
||||
"diff": "^5.1.0",
|
||||
"discord-types": "^1.3.26",
|
||||
"esbuild": "^0.15.18",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-plugin-header": "^3.1.1",
|
||||
"eslint-plugin-path-alias": "^1.0.0",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
@ -69,7 +70,7 @@
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"eslint-plugin-path-alias@1.0.0": "patches/eslint-plugin-path-alias@1.0.0.patch",
|
||||
"eslint@8.28.0": "patches/eslint@8.28.0.patch"
|
||||
"eslint@8.46.0": "patches/eslint@8.46.0.patch"
|
||||
},
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": [
|
||||
|
@ -1,8 +1,8 @@
|
||||
diff --git a/lib/rules/no-useless-escape.js b/lib/rules/no-useless-escape.js
|
||||
index 2046a148a17fd1d5f3a4bbc9f45f7700259d11fa..f4898c6b57355a4fd72c43a9f32bf1a36a6ccf4a 100644
|
||||
index 0e0f6f09f2c35f3276173c08f832cde9f2cf56a0..7dc22851715f3574d935f513c1b5e35552985711 100644
|
||||
--- a/lib/rules/no-useless-escape.js
|
||||
+++ b/lib/rules/no-useless-escape.js
|
||||
@@ -97,12 +97,30 @@ module.exports = {
|
||||
@@ -65,13 +65,31 @@ module.exports = {
|
||||
escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
|
||||
},
|
||||
|
||||
@ -25,21 +25,25 @@ index 2046a148a17fd1d5f3a4bbc9f45f7700259d11fa..f4898c6b57355a4fd72c43a9f32bf1a3
|
||||
|
||||
create(context) {
|
||||
+ const options = context.options[0] || {};
|
||||
+ const { extra, extraCharClass } = options || ''
|
||||
const sourceCode = context.getSourceCode();
|
||||
+ const { extra, extraCharClass } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
const parser = new RegExpParser();
|
||||
|
||||
+ const NON_CHARCLASS_ESCAPES = union(REGEX_NON_CHARCLASS_ESCAPES, new Set(extra))
|
||||
+ const CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set(extraCharClass))
|
||||
+ const NON_CHARCLASS_ESCAPES = union(REGEX_NON_CHARCLASS_ESCAPES, new Set(extra));
|
||||
+ const CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set(extraCharClass));
|
||||
+
|
||||
/**
|
||||
* Reports a node
|
||||
* @param {ASTNode} node The node to report
|
||||
@@ -238,7 +256,7 @@ module.exports = {
|
||||
.filter(charInfo => charInfo.escaped)
|
||||
@@ -200,9 +218,9 @@ module.exports = {
|
||||
let allowedEscapes;
|
||||
|
||||
// Filter out characters that are valid to escape, based on their position in the regular expression.
|
||||
- .filter(charInfo => !(charInfo.inCharClass ? REGEX_GENERAL_ESCAPES : REGEX_NON_CHARCLASS_ESCAPES).has(charInfo.text))
|
||||
+ .filter(charInfo => !(charInfo.inCharClass ? CHARCLASS_ESCAPES : NON_CHARCLASS_ESCAPES).has(charInfo.text))
|
||||
|
||||
// Report all the remaining characters.
|
||||
.forEach(charInfo => report(node, charInfo.index, charInfo.text));
|
||||
if (characterClassStack.length) {
|
||||
- allowedEscapes = unicodeSets ? REGEX_CLASSSET_CHARACTER_ESCAPES : REGEX_GENERAL_ESCAPES;
|
||||
+ allowedEscapes = unicodeSets ? REGEX_CLASSSET_CHARACTER_ESCAPES : CHARCLASS_ESCAPES;
|
||||
} else {
|
||||
- allowedEscapes = REGEX_NON_CHARCLASS_ESCAPES;
|
||||
+ allowedEscapes = NON_CHARCLASS_ESCAPES;
|
||||
}
|
||||
if (allowedEscapes.has(escapedChar)) {
|
||||
return;
|
255
pnpm-lock.yaml
generated
255
pnpm-lock.yaml
generated
@ -4,17 +4,23 @@ patchedDependencies:
|
||||
eslint-plugin-path-alias@1.0.0:
|
||||
hash: m6sma4g6bh67km3q6igf6uxaja
|
||||
path: patches/eslint-plugin-path-alias@1.0.0.patch
|
||||
eslint@8.28.0:
|
||||
hash: 7wc6icvgtg3uswirb5tpsbjnbe
|
||||
path: patches/eslint@8.28.0.patch
|
||||
eslint@8.46.0:
|
||||
hash: xm46kqcmdgzlmm4aifkfpxaho4
|
||||
path: patches/eslint@8.46.0.patch
|
||||
|
||||
dependencies:
|
||||
'@sapphi-red/web-noise-suppressor':
|
||||
specifier: 0.3.3
|
||||
version: 0.3.3
|
||||
'@vap/core':
|
||||
specifier: 0.0.12
|
||||
version: 0.0.12
|
||||
'@vap/shiki':
|
||||
specifier: 0.10.5
|
||||
version: 0.10.5
|
||||
eslint-plugin-simple-header:
|
||||
specifier: ^1.0.2
|
||||
version: 1.0.2
|
||||
fflate:
|
||||
specifier: ^0.7.4
|
||||
version: 0.7.4
|
||||
@ -46,10 +52,10 @@ devDependencies:
|
||||
version: 2.4.2
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^5.59.1
|
||||
version: 5.59.1(@typescript-eslint/parser@5.59.1)(eslint@8.28.0)(typescript@5.0.4)
|
||||
version: 5.59.1(@typescript-eslint/parser@5.59.1)(eslint@8.46.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^5.59.1
|
||||
version: 5.59.1(eslint@8.28.0)(typescript@5.0.4)
|
||||
version: 5.59.1(eslint@8.46.0)(typescript@5.0.4)
|
||||
diff:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
@ -60,23 +66,20 @@ devDependencies:
|
||||
specifier: ^0.15.18
|
||||
version: 0.15.18
|
||||
eslint:
|
||||
specifier: ^8.28.0
|
||||
version: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
specifier: ^8.46.0
|
||||
version: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)
|
||||
eslint-import-resolver-alias:
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2
|
||||
eslint-plugin-header:
|
||||
specifier: ^3.1.1
|
||||
version: 3.1.1(eslint@8.28.0)
|
||||
eslint-plugin-path-alias:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(patch_hash=m6sma4g6bh67km3q6igf6uxaja)(eslint@8.28.0)
|
||||
version: 1.0.0(patch_hash=m6sma4g6bh67km3q6igf6uxaja)(eslint@8.46.0)
|
||||
eslint-plugin-simple-import-sort:
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0(eslint@8.28.0)
|
||||
version: 10.0.0(eslint@8.46.0)
|
||||
eslint-plugin-unused-imports:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(@typescript-eslint/eslint-plugin@5.59.1)(eslint@8.28.0)
|
||||
version: 2.0.0(@typescript-eslint/eslint-plugin@5.59.1)(eslint@8.46.0)
|
||||
highlight.js:
|
||||
specifier: 10.6.0
|
||||
version: 10.6.0
|
||||
@ -107,6 +110,11 @@ devDependencies:
|
||||
|
||||
packages:
|
||||
|
||||
/@aashutoshrathi/word-wrap@1.2.6:
|
||||
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/code-frame@7.21.4:
|
||||
resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@ -399,7 +407,7 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@eslint-community/eslint-utils@4.4.0(eslint@8.28.0):
|
||||
/@eslint-community/eslint-utils@4.4.0(eslint@8.46.0):
|
||||
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -408,8 +416,8 @@ packages:
|
||||
eslint:
|
||||
optional: true
|
||||
dependencies:
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
eslint-visitor-keys: 3.4.0
|
||||
eslint: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)
|
||||
eslint-visitor-keys: 3.4.2
|
||||
dev: true
|
||||
|
||||
/@eslint-community/regexpp@4.5.1:
|
||||
@ -417,15 +425,20 @@ packages:
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@eslint/eslintrc@1.3.3:
|
||||
resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==}
|
||||
/@eslint-community/regexpp@4.6.2:
|
||||
resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@eslint/eslintrc@2.1.1:
|
||||
resolution: {integrity: sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
ajv: 6.12.6
|
||||
debug: 4.3.4
|
||||
espree: 9.4.1
|
||||
globals: 13.17.0
|
||||
ignore: 5.2.0
|
||||
espree: 9.6.1
|
||||
globals: 13.20.0
|
||||
ignore: 5.2.4
|
||||
import-fresh: 3.3.0
|
||||
js-yaml: 4.1.0
|
||||
minimatch: 3.1.2
|
||||
@ -434,8 +447,13 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@humanwhocodes/config-array@0.11.7:
|
||||
resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==}
|
||||
/@eslint/js@8.46.0:
|
||||
resolution: {integrity: sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@humanwhocodes/config-array@0.11.10:
|
||||
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
|
||||
engines: {node: '>=10.10.0'}
|
||||
dependencies:
|
||||
'@humanwhocodes/object-schema': 1.2.1
|
||||
@ -498,6 +516,10 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@sapphi-red/web-noise-suppressor@0.3.3:
|
||||
resolution: {integrity: sha512-gAC33DCXYwNTI/k1PxOVHmbbzakUSMbb/DHpoV6rn4pKZtPI1dduULSmAAm/y1ipgIlArnk2JcnQzw4n2tCZHw==}
|
||||
dev: false
|
||||
|
||||
/@types/diff@5.0.3:
|
||||
resolution: {integrity: sha512-amrLbRqTU9bXMCc6uX0sWpxsQzRIo9z6MJPkH1pkez/qOxuqSZVuryJAWoBRq94CeG8JxY+VK4Le9HtjQR5T9A==}
|
||||
dev: true
|
||||
@ -569,7 +591,7 @@ packages:
|
||||
'@types/node': 18.16.3
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin@5.59.1(@typescript-eslint/parser@5.59.1)(eslint@8.28.0)(typescript@5.0.4):
|
||||
/@typescript-eslint/eslint-plugin@5.59.1(@typescript-eslint/parser@5.59.1)(eslint@8.46.0)(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -583,12 +605,12 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.5.1
|
||||
'@typescript-eslint/parser': 5.59.1(eslint@8.28.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/parser': 5.59.1(eslint@8.46.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/scope-manager': 5.59.1
|
||||
'@typescript-eslint/type-utils': 5.59.1(eslint@8.28.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/utils': 5.59.1(eslint@8.28.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/type-utils': 5.59.1(eslint@8.46.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/utils': 5.59.1(eslint@8.46.0)(typescript@5.0.4)
|
||||
debug: 4.3.4
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
eslint: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)
|
||||
grapheme-splitter: 1.0.4
|
||||
ignore: 5.2.4
|
||||
natural-compare-lite: 1.4.0
|
||||
@ -599,7 +621,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser@5.59.1(eslint@8.28.0)(typescript@5.0.4):
|
||||
/@typescript-eslint/parser@5.59.1(eslint@8.46.0)(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -615,7 +637,7 @@ packages:
|
||||
'@typescript-eslint/types': 5.59.1
|
||||
'@typescript-eslint/typescript-estree': 5.59.1(typescript@5.0.4)
|
||||
debug: 4.3.4
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
eslint: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)
|
||||
typescript: 5.0.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@ -629,7 +651,7 @@ packages:
|
||||
'@typescript-eslint/visitor-keys': 5.59.1
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/type-utils@5.59.1(eslint@8.28.0)(typescript@5.0.4):
|
||||
/@typescript-eslint/type-utils@5.59.1(eslint@8.46.0)(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -642,9 +664,9 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 5.59.1(typescript@5.0.4)
|
||||
'@typescript-eslint/utils': 5.59.1(eslint@8.28.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/utils': 5.59.1(eslint@8.46.0)(typescript@5.0.4)
|
||||
debug: 4.3.4
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
eslint: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)
|
||||
tsutils: 3.21.0(typescript@5.0.4)
|
||||
typescript: 5.0.4
|
||||
transitivePeerDependencies:
|
||||
@ -677,7 +699,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/utils@5.59.1(eslint@8.28.0)(typescript@5.0.4):
|
||||
/@typescript-eslint/utils@5.59.1(eslint@8.46.0)(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -686,13 +708,13 @@ packages:
|
||||
eslint:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.28.0)
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.46.0)
|
||||
'@types/json-schema': 7.0.11
|
||||
'@types/semver': 7.3.13
|
||||
'@typescript-eslint/scope-manager': 5.59.1
|
||||
'@typescript-eslint/types': 5.59.1
|
||||
'@typescript-eslint/typescript-estree': 5.59.1(typescript@5.0.4)
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
eslint: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)
|
||||
eslint-scope: 5.1.1
|
||||
semver: 7.5.0
|
||||
transitivePeerDependencies:
|
||||
@ -722,16 +744,16 @@ packages:
|
||||
vscode-textmate: 5.2.0
|
||||
dev: false
|
||||
|
||||
/acorn-jsx@5.3.2(acorn@8.8.0):
|
||||
/acorn-jsx@5.3.2(acorn@8.10.0):
|
||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
||||
peerDependencies:
|
||||
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
acorn: 8.10.0
|
||||
dev: true
|
||||
|
||||
/acorn@8.8.0:
|
||||
resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==}
|
||||
/acorn@8.10.0:
|
||||
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
@ -1446,18 +1468,7 @@ packages:
|
||||
optional: true
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-header@3.1.1(eslint@8.28.0):
|
||||
resolution: {integrity: sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==}
|
||||
peerDependencies:
|
||||
eslint: '>=7.7.0'
|
||||
peerDependenciesMeta:
|
||||
eslint:
|
||||
optional: true
|
||||
dependencies:
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-path-alias@1.0.0(patch_hash=m6sma4g6bh67km3q6igf6uxaja)(eslint@8.28.0):
|
||||
/eslint-plugin-path-alias@1.0.0(patch_hash=m6sma4g6bh67km3q6igf6uxaja)(eslint@8.46.0):
|
||||
resolution: {integrity: sha512-FXus57yC+Zd3sMv46pbloXYwFeNVNHJqlACr9V68FG/IzGFBBokGJpmjDbEjpt8ZCeVSndUubeDWWl2A8sCNVQ==}
|
||||
peerDependencies:
|
||||
eslint: ^7
|
||||
@ -1465,14 +1476,18 @@ packages:
|
||||
eslint:
|
||||
optional: true
|
||||
dependencies:
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
eslint: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)
|
||||
nanomatch: 1.2.13
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
patched: true
|
||||
|
||||
/eslint-plugin-simple-import-sort@10.0.0(eslint@8.28.0):
|
||||
/eslint-plugin-simple-header@1.0.2:
|
||||
resolution: {integrity: sha512-K1EJ/ueBIjPRA8qR44Ymo+GDmPYYmfoODtainGxVr7PSbX6QiaY+pTuGCrOhO+AtVsYJs8GLSVdGUTXyAxAtOA==}
|
||||
dev: false
|
||||
|
||||
/eslint-plugin-simple-import-sort@10.0.0(eslint@8.46.0):
|
||||
resolution: {integrity: sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==}
|
||||
peerDependencies:
|
||||
eslint: '>=5.0.0'
|
||||
@ -1480,10 +1495,10 @@ packages:
|
||||
eslint:
|
||||
optional: true
|
||||
dependencies:
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
eslint: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-unused-imports@2.0.0(@typescript-eslint/eslint-plugin@5.59.1)(eslint@8.28.0):
|
||||
/eslint-plugin-unused-imports@2.0.0(@typescript-eslint/eslint-plugin@5.59.1)(eslint@8.46.0):
|
||||
resolution: {integrity: sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -1495,8 +1510,8 @@ packages:
|
||||
eslint:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 5.59.1(@typescript-eslint/parser@5.59.1)(eslint@8.28.0)(typescript@5.0.4)
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
'@typescript-eslint/eslint-plugin': 5.59.1(@typescript-eslint/parser@5.59.1)(eslint@8.46.0)(typescript@5.0.4)
|
||||
eslint: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)
|
||||
eslint-rule-composer: 0.3.0
|
||||
dev: true
|
||||
|
||||
@ -1513,49 +1528,34 @@ packages:
|
||||
estraverse: 4.3.0
|
||||
dev: true
|
||||
|
||||
/eslint-scope@7.1.1:
|
||||
resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
|
||||
/eslint-scope@7.2.2:
|
||||
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
esrecurse: 4.3.0
|
||||
estraverse: 5.3.0
|
||||
dev: true
|
||||
|
||||
/eslint-utils@3.0.0(eslint@8.28.0):
|
||||
resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
|
||||
engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
|
||||
peerDependencies:
|
||||
eslint: '>=5'
|
||||
peerDependenciesMeta:
|
||||
eslint:
|
||||
optional: true
|
||||
dependencies:
|
||||
eslint: 8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe)
|
||||
eslint-visitor-keys: 2.1.0
|
||||
dev: true
|
||||
|
||||
/eslint-visitor-keys@2.1.0:
|
||||
resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/eslint-visitor-keys@3.3.0:
|
||||
resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/eslint-visitor-keys@3.4.0:
|
||||
resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/eslint@8.28.0(patch_hash=7wc6icvgtg3uswirb5tpsbjnbe):
|
||||
resolution: {integrity: sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==}
|
||||
/eslint-visitor-keys@3.4.2:
|
||||
resolution: {integrity: sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/eslint@8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4):
|
||||
resolution: {integrity: sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@eslint/eslintrc': 1.3.3
|
||||
'@humanwhocodes/config-array': 0.11.7
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.46.0)
|
||||
'@eslint-community/regexpp': 4.6.2
|
||||
'@eslint/eslintrc': 2.1.1
|
||||
'@eslint/js': 8.46.0
|
||||
'@humanwhocodes/config-array': 0.11.10
|
||||
'@humanwhocodes/module-importer': 1.0.1
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
ajv: 6.12.6
|
||||
@ -1564,60 +1564,46 @@ packages:
|
||||
debug: 4.3.4
|
||||
doctrine: 3.0.0
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 7.1.1
|
||||
eslint-utils: 3.0.0(eslint@8.28.0)
|
||||
eslint-visitor-keys: 3.3.0
|
||||
espree: 9.4.0
|
||||
esquery: 1.4.0
|
||||
eslint-scope: 7.2.2
|
||||
eslint-visitor-keys: 3.4.2
|
||||
espree: 9.6.1
|
||||
esquery: 1.5.0
|
||||
esutils: 2.0.3
|
||||
fast-deep-equal: 3.1.3
|
||||
file-entry-cache: 6.0.1
|
||||
find-up: 5.0.0
|
||||
glob-parent: 6.0.2
|
||||
globals: 13.17.0
|
||||
grapheme-splitter: 1.0.4
|
||||
ignore: 5.2.0
|
||||
import-fresh: 3.3.0
|
||||
globals: 13.20.0
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.2.4
|
||||
imurmurhash: 0.1.4
|
||||
is-glob: 4.0.3
|
||||
is-path-inside: 3.0.3
|
||||
js-sdsl: 4.1.5
|
||||
js-yaml: 4.1.0
|
||||
json-stable-stringify-without-jsonify: 1.0.1
|
||||
levn: 0.4.1
|
||||
lodash.merge: 4.6.2
|
||||
minimatch: 3.1.2
|
||||
natural-compare: 1.4.0
|
||||
optionator: 0.9.1
|
||||
regexpp: 3.2.0
|
||||
optionator: 0.9.3
|
||||
strip-ansi: 6.0.1
|
||||
strip-json-comments: 3.1.1
|
||||
text-table: 0.2.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
patched: true
|
||||
|
||||
/espree@9.4.0:
|
||||
resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==}
|
||||
/espree@9.6.1:
|
||||
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
acorn-jsx: 5.3.2(acorn@8.8.0)
|
||||
eslint-visitor-keys: 3.3.0
|
||||
acorn: 8.10.0
|
||||
acorn-jsx: 5.3.2(acorn@8.10.0)
|
||||
eslint-visitor-keys: 3.4.2
|
||||
dev: true
|
||||
|
||||
/espree@9.4.1:
|
||||
resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
acorn-jsx: 5.3.2(acorn@8.8.0)
|
||||
eslint-visitor-keys: 3.3.0
|
||||
dev: true
|
||||
|
||||
/esquery@1.4.0:
|
||||
resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
|
||||
/esquery@1.5.0:
|
||||
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
|
||||
engines: {node: '>=0.10'}
|
||||
dependencies:
|
||||
estraverse: 5.3.0
|
||||
@ -1858,8 +1844,8 @@ packages:
|
||||
which: 1.3.1
|
||||
dev: true
|
||||
|
||||
/globals@13.17.0:
|
||||
resolution: {integrity: sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==}
|
||||
/globals@13.20.0:
|
||||
resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
type-fest: 0.20.2
|
||||
@ -1885,6 +1871,10 @@ packages:
|
||||
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
|
||||
dev: true
|
||||
|
||||
/graphemer@1.4.0:
|
||||
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
|
||||
dev: true
|
||||
|
||||
/hard-rejection@2.1.0:
|
||||
resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
|
||||
engines: {node: '>=6'}
|
||||
@ -1972,11 +1962,6 @@ packages:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
dev: true
|
||||
|
||||
/ignore@5.2.0:
|
||||
resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==}
|
||||
engines: {node: '>= 4'}
|
||||
dev: true
|
||||
|
||||
/ignore@5.2.4:
|
||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||
engines: {node: '>= 4'}
|
||||
@ -2168,10 +2153,6 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/js-sdsl@4.1.5:
|
||||
resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==}
|
||||
dev: true
|
||||
|
||||
/js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
dev: true
|
||||
@ -2485,16 +2466,16 @@ packages:
|
||||
wrappy: 1.0.2
|
||||
dev: true
|
||||
|
||||
/optionator@0.9.1:
|
||||
resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
|
||||
/optionator@0.9.3:
|
||||
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
'@aashutoshrathi/word-wrap': 1.2.6
|
||||
deep-is: 0.1.4
|
||||
fast-levenshtein: 2.0.6
|
||||
levn: 0.4.1
|
||||
prelude-ls: 1.2.1
|
||||
type-check: 0.4.0
|
||||
word-wrap: 1.2.3
|
||||
dev: true
|
||||
|
||||
/p-limit@2.3.0:
|
||||
@ -2734,11 +2715,6 @@ packages:
|
||||
safe-regex: 1.1.0
|
||||
dev: true
|
||||
|
||||
/regexpp@3.2.0:
|
||||
resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -3323,11 +3299,6 @@ packages:
|
||||
isexe: 2.0.0
|
||||
dev: true
|
||||
|
||||
/word-wrap@1.2.3:
|
||||
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
@ -3405,3 +3376,7 @@ packages:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
@ -19,13 +19,14 @@
|
||||
|
||||
import esbuild from "esbuild";
|
||||
|
||||
import { commonOpts, globPlugins, isStandalone, VERSION, watch } from "./common.mjs";
|
||||
import { BUILD_TIMESTAMP, commonOpts, globPlugins, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs";
|
||||
|
||||
const defines = {
|
||||
IS_STANDALONE: isStandalone,
|
||||
IS_DEV: JSON.stringify(watch),
|
||||
IS_UPDATER_DISABLED: updaterDisabled,
|
||||
VERSION: JSON.stringify(VERSION),
|
||||
BUILD_TIMESTAMP: Date.now(),
|
||||
BUILD_TIMESTAMP,
|
||||
};
|
||||
if (defines.IS_STANDALONE === "false")
|
||||
// If this is a local build (not standalone), optimise
|
||||
@ -40,8 +41,6 @@ const nodeCommonOpts = {
|
||||
format: "cjs",
|
||||
platform: "node",
|
||||
target: ["esnext"],
|
||||
minify: true,
|
||||
bundle: true,
|
||||
external: ["electron", ...commonOpts.external],
|
||||
define: defines,
|
||||
};
|
||||
@ -50,16 +49,7 @@ const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.j
|
||||
const sourcemap = watch ? "inline" : "external";
|
||||
|
||||
await Promise.all([
|
||||
// common preload
|
||||
esbuild.build({
|
||||
...nodeCommonOpts,
|
||||
entryPoints: ["src/preload.ts"],
|
||||
outfile: "dist/preload.js",
|
||||
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
|
||||
sourcemap,
|
||||
}),
|
||||
|
||||
// Discord Desktop main & renderer
|
||||
// Discord Desktop main & renderer & preload
|
||||
esbuild.build({
|
||||
...nodeCommonOpts,
|
||||
entryPoints: ["src/main/index.ts"],
|
||||
@ -69,7 +59,7 @@ await Promise.all([
|
||||
define: {
|
||||
...defines,
|
||||
IS_DISCORD_DESKTOP: true,
|
||||
IS_VENCORD_DESKTOP: false
|
||||
IS_VESKTOP: false
|
||||
}
|
||||
}),
|
||||
esbuild.build({
|
||||
@ -89,11 +79,23 @@ await Promise.all([
|
||||
...defines,
|
||||
IS_WEB: false,
|
||||
IS_DISCORD_DESKTOP: true,
|
||||
IS_VENCORD_DESKTOP: false
|
||||
IS_VESKTOP: false
|
||||
}
|
||||
}),
|
||||
esbuild.build({
|
||||
...nodeCommonOpts,
|
||||
entryPoints: ["src/preload.ts"],
|
||||
outfile: "dist/preload.js",
|
||||
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
|
||||
sourcemap,
|
||||
define: {
|
||||
...defines,
|
||||
IS_DISCORD_DESKTOP: true,
|
||||
IS_VESKTOP: false
|
||||
}
|
||||
}),
|
||||
|
||||
// Vencord Desktop main & renderer
|
||||
// Vencord Desktop main & renderer & preload
|
||||
esbuild.build({
|
||||
...nodeCommonOpts,
|
||||
entryPoints: ["src/main/index.ts"],
|
||||
@ -103,7 +105,7 @@ await Promise.all([
|
||||
define: {
|
||||
...defines,
|
||||
IS_DISCORD_DESKTOP: false,
|
||||
IS_VENCORD_DESKTOP: true
|
||||
IS_VESKTOP: true
|
||||
}
|
||||
}),
|
||||
esbuild.build({
|
||||
@ -123,7 +125,19 @@ await Promise.all([
|
||||
...defines,
|
||||
IS_WEB: false,
|
||||
IS_DISCORD_DESKTOP: false,
|
||||
IS_VENCORD_DESKTOP: true
|
||||
IS_VESKTOP: true
|
||||
}
|
||||
}),
|
||||
esbuild.build({
|
||||
...nodeCommonOpts,
|
||||
entryPoints: ["src/preload.ts"],
|
||||
outfile: "dist/vencordDesktopPreload.js",
|
||||
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("vencordDesktopPreload") },
|
||||
sourcemap,
|
||||
define: {
|
||||
...defines,
|
||||
IS_DISCORD_DESKTOP: false,
|
||||
IS_VESKTOP: true
|
||||
}
|
||||
}),
|
||||
]).catch(err => {
|
||||
|
@ -24,7 +24,7 @@ import { readFileSync } from "fs";
|
||||
import { appendFile, mkdir, readFile, rm, writeFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
||||
import { commonOpts, globPlugins, VERSION, watch } from "./common.mjs";
|
||||
import { BUILD_TIMESTAMP, commonOpts, globPlugins, VERSION, watch } from "./common.mjs";
|
||||
|
||||
/**
|
||||
* @type {esbuild.BuildOptions}
|
||||
@ -45,9 +45,10 @@ const commonOptions = {
|
||||
IS_STANDALONE: "true",
|
||||
IS_DEV: JSON.stringify(watch),
|
||||
IS_DISCORD_DESKTOP: "false",
|
||||
IS_VENCORD_DESKTOP: "false",
|
||||
IS_VESKTOP: "false",
|
||||
IS_UPDATER_DISABLED: "true",
|
||||
VERSION: JSON.stringify(VERSION),
|
||||
BUILD_TIMESTAMP: Date.now(),
|
||||
BUILD_TIMESTAMP,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -27,17 +27,21 @@ import { promisify } from "util";
|
||||
|
||||
// wtf is this assert syntax
|
||||
import PackageJSON from "../../package.json" assert { type: "json" };
|
||||
import { getPluginTarget } from "../utils.mjs";
|
||||
|
||||
export const VERSION = PackageJSON.version;
|
||||
export const BUILD_TIMESTAMP = Date.now();
|
||||
// https://reproducible-builds.org/docs/source-date-epoch/
|
||||
export const BUILD_TIMESTAMP = Number(process.env.SOURCE_DATE_EPOCH) || Date.now();
|
||||
export const watch = process.argv.includes("--watch");
|
||||
export const isStandalone = JSON.stringify(process.argv.includes("--standalone"));
|
||||
export const gitHash = execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
||||
export const updaterDisabled = JSON.stringify(process.argv.includes("--disable-updater"));
|
||||
export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
||||
export const banner = {
|
||||
js: `
|
||||
// Vencord ${gitHash}
|
||||
// Standalone: ${isStandalone}
|
||||
// Platform: ${isStandalone === "false" ? process.platform : "Universal"}
|
||||
// Updater disabled: ${updaterDisabled}
|
||||
`.trim()
|
||||
};
|
||||
|
||||
@ -81,14 +85,13 @@ export const globPlugins = kind => ({
|
||||
if (file.startsWith("_") || file.startsWith(".")) continue;
|
||||
if (file === "index.ts") continue;
|
||||
|
||||
const fileBits = file.split(".");
|
||||
if (fileBits.length > 2 && ["ts", "tsx"].includes(fileBits.at(-1))) {
|
||||
const mod = fileBits.at(-2);
|
||||
if (mod === "dev" && !watch) continue;
|
||||
if (mod === "web" && kind === "discordDesktop") continue;
|
||||
if (mod === "desktop" && kind === "web") continue;
|
||||
if (mod === "discordDesktop" && kind !== "discordDesktop") continue;
|
||||
if (mod === "vencordDesktop" && kind !== "vencordDesktop") continue;
|
||||
const target = getPluginTarget(file);
|
||||
if (target) {
|
||||
if (target === "dev" && !watch) continue;
|
||||
if (target === "web" && kind === "discordDesktop") continue;
|
||||
if (target === "desktop" && kind === "web") continue;
|
||||
if (target === "discordDesktop" && kind !== "discordDesktop") continue;
|
||||
if (target === "vencordDesktop" && kind !== "vencordDesktop") continue;
|
||||
}
|
||||
|
||||
const mod = `p${i}`;
|
||||
@ -133,11 +136,14 @@ export const gitRemotePlugin = {
|
||||
namespace: "git-remote", path: args.path
|
||||
}));
|
||||
build.onLoad({ filter, namespace: "git-remote" }, async () => {
|
||||
let remote = process.env.VENCORD_REMOTE;
|
||||
if (!remote) {
|
||||
const res = await promisify(exec)("git remote get-url origin", { encoding: "utf-8" });
|
||||
const remote = res.stdout.trim()
|
||||
remote = res.stdout.trim()
|
||||
.replace("https://github.com/", "")
|
||||
.replace("git@github.com:", "")
|
||||
.replace(/.git$/, "");
|
||||
}
|
||||
|
||||
return { contents: `export default "${remote}"` };
|
||||
});
|
||||
|
@ -21,6 +21,8 @@ import { access, readFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
|
||||
|
||||
import { getPluginTarget } from "./utils.mjs";
|
||||
|
||||
interface Dev {
|
||||
name: string;
|
||||
id: string;
|
||||
@ -157,11 +159,10 @@ async function parseFile(fileName: string) {
|
||||
|
||||
if (!data.name || !data.description || !data.authors) throw fail("name, description or authors are missing");
|
||||
|
||||
const fileBits = fileName.split(".");
|
||||
if (fileBits.length > 2 && ["ts", "tsx"].includes(fileBits.at(-1)!)) {
|
||||
const mod = fileBits.at(-2)!;
|
||||
if (!["web", "discordDesktop", "vencordDesktop", "dev"].includes(mod)) throw fail(`invalid target ${fileBits.at(-2)}`);
|
||||
data.target = mod as any;
|
||||
const target = getPluginTarget(fileName);
|
||||
if (target) {
|
||||
if (!["web", "discordDesktop", "vencordDesktop", "desktop", "dev"].includes(target)) throw fail(`invalid target ${target}`);
|
||||
data.target = target as any;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@ -263,7 +263,7 @@ function runTime(token: string) {
|
||||
for (const id in ids) {
|
||||
const isWasm = await fetch(wreq.p + wreq.u(id))
|
||||
.then(r => r.text())
|
||||
.then(t => t.includes(".module.wasm"));
|
||||
.then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push"));
|
||||
|
||||
if (!isWasm)
|
||||
await wreq.e(id as any);
|
||||
|
3
scripts/header-new.txt
Normal file
3
scripts/header-new.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Vencord, a Discord client mod
|
||||
Copyright (c) {year} {author}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
17
scripts/header-old.txt
Normal file
17
scripts/header-old.txt
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) {year} {author}
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
30
scripts/utils.mjs
Normal file
30
scripts/utils.mjs
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export function getPluginTarget(filePath) {
|
||||
const pathParts = filePath.split(/[/\\]/);
|
||||
if (/^index\.tsx?$/.test(pathParts.at(-1))) pathParts.pop();
|
||||
|
||||
const identifier = pathParts.at(-1).replace(/\.tsx?$/, "");
|
||||
const identiferBits = identifier.split(".");
|
||||
return identiferBits.length === 1 ? null : identiferBits.at(-1);
|
||||
}
|
@ -1,19 +1,7 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022
|
||||
*
|
||||
* 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) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { IpcEvents } from "@utils/IpcEvents";
|
||||
@ -60,7 +48,7 @@ export default {
|
||||
},
|
||||
|
||||
addThemeChangeListener(cb: () => void) {
|
||||
ipcRenderer.on(IpcEvents.THEME_UPDATE, cb);
|
||||
ipcRenderer.on(IpcEvents.THEME_UPDATE, () => cb());
|
||||
},
|
||||
|
||||
openFile: () => invoke<void>(IpcEvents.OPEN_QUICKCSS),
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* eslint-disable header/header */
|
||||
/* eslint-disable simple-header/header */
|
||||
|
||||
/*!
|
||||
* idb-keyval v6.2.0
|
||||
|
@ -23,7 +23,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
||||
import { LazyComponent } from "@utils/react";
|
||||
import { OptionType, Plugin } from "@utils/types";
|
||||
@ -89,7 +89,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
||||
|
||||
const canSubmit = () => Object.values(errors).every(e => !e);
|
||||
|
||||
const hasSettings = Boolean(pluginSettings && plugin.options);
|
||||
const hasSettings = Boolean(pluginSettings && plugin.options && !isObjectEmpty(plugin.options));
|
||||
|
||||
React.useEffect(() => {
|
||||
enableStyle(hideBotTagStyle);
|
||||
|
@ -33,12 +33,11 @@ export function SettingTextComponent({ option, pluginSettings, definedSettings,
|
||||
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
|
||||
if (typeof isValid === "string") setError(isValid);
|
||||
else if (!isValid) setError("Invalid input provided.");
|
||||
else {
|
||||
setError(null);
|
||||
else setError(null);
|
||||
|
||||
setState(newValue);
|
||||
onChange(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
|
@ -28,7 +28,7 @@ import { SettingsTab } from "@components/VencordSettings/shared";
|
||||
import { ChangeList } from "@utils/ChangeList";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { openModalLazy } from "@utils/modal";
|
||||
import { LazyComponent, useAwaiter } from "@utils/react";
|
||||
import { Plugin } from "@utils/types";
|
||||
@ -161,7 +161,7 @@ function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLe
|
||||
onMouseLeave={onMouseLeave}
|
||||
infoButton={
|
||||
<button role="switch" onClick={() => openModal()} className={classes(ButtonClasses.button, cl("info-button"))}>
|
||||
{plugin.options
|
||||
{plugin.options && !isObjectEmpty(plugin.options)
|
||||
? <CogWheel />
|
||||
: <InfoIcon width="24" height="24" />}
|
||||
</button>
|
||||
|
@ -231,7 +231,7 @@ function ThemesTab() {
|
||||
ref={fileInputRef}
|
||||
onChange={onFileUpload}
|
||||
multiple={true}
|
||||
filters={[{ extensions: ["*.css"] }]}
|
||||
filters={[{ extensions: ["css"] }]}
|
||||
/>
|
||||
</Button>
|
||||
) : (
|
||||
|
@ -249,4 +249,4 @@ function Updater() {
|
||||
);
|
||||
}
|
||||
|
||||
export default IS_WEB ? null : wrapTab(Updater, "Updater");
|
||||
export default IS_UPDATER_DISABLED ? null : wrapTab(Updater, "Updater");
|
||||
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import { openNotificationLogModal } from "@api/Notifications/notificationLog";
|
||||
import { Settings, useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
|
7
src/globals.d.ts
vendored
7
src/globals.d.ts
vendored
@ -35,8 +35,9 @@ declare global {
|
||||
export var IS_WEB: boolean;
|
||||
export var IS_DEV: boolean;
|
||||
export var IS_STANDALONE: boolean;
|
||||
export var IS_UPDATER_DISABLED: boolean;
|
||||
export var IS_DISCORD_DESKTOP: boolean;
|
||||
export var IS_VENCORD_DESKTOP: boolean;
|
||||
export var IS_VESKTOP: boolean;
|
||||
export var VERSION: string;
|
||||
export var BUILD_TIMESTAMP: number;
|
||||
|
||||
@ -58,8 +59,8 @@ declare global {
|
||||
* If you really must use it, mark your plugin as Desktop App only by naming it Foo.desktop.ts(x)
|
||||
*/
|
||||
export var DiscordNative: any;
|
||||
export var VencordDesktop: any;
|
||||
export var VencordDesktopNative: any;
|
||||
export var Vesktop: any;
|
||||
export var VesktopNative: any;
|
||||
|
||||
interface Window {
|
||||
webpackChunkdiscord_app: {
|
||||
|
@ -23,7 +23,7 @@ import { ensureSafePath, getSettings } from "./ipcMain";
|
||||
import { IS_VANILLA, THEMES_DIR } from "./utils/constants";
|
||||
import { installExt } from "./utils/extensions";
|
||||
|
||||
if (IS_VENCORD_DESKTOP || !IS_VANILLA) {
|
||||
if (IS_VESKTOP || !IS_VANILLA) {
|
||||
app.whenReady().then(() => {
|
||||
// Source Maps! Maybe there's a better way but since the renderer is executed
|
||||
// from a string I don't think any other form of sourcemaps would work
|
||||
@ -44,6 +44,7 @@ if (IS_VENCORD_DESKTOP || !IS_VANILLA) {
|
||||
case "renderer.js.map":
|
||||
case "vencordDesktopRenderer.js.map":
|
||||
case "preload.js.map":
|
||||
case "vencordDesktopPreload.js.map":
|
||||
case "patcher.js.map":
|
||||
case "vencordDesktopMain.js.map":
|
||||
cb(join(__dirname, url));
|
||||
|
@ -53,10 +53,12 @@ async function listThemes(): Promise<UserThemeHeader[]> {
|
||||
const themeInfo: UserThemeHeader[] = [];
|
||||
|
||||
for (const fileName of files) {
|
||||
if (!fileName.endsWith(".css")) continue;
|
||||
|
||||
const data = await getThemeData(fileName).then(stripBOM).catch(() => null);
|
||||
if (!data) continue;
|
||||
const parsed = getThemeInfo(data, fileName);
|
||||
themeInfo.push(parsed);
|
||||
if (data == null) continue;
|
||||
|
||||
themeInfo.push(getThemeInfo(data, fileName));
|
||||
}
|
||||
|
||||
return themeInfo;
|
||||
@ -138,7 +140,7 @@ ipcMain.handle(IpcEvents.OPEN_MONACO_EDITOR, async () => {
|
||||
autoHideMenuBar: true,
|
||||
darkTheme: true,
|
||||
webPreferences: {
|
||||
preload: join(__dirname, "preload.js"),
|
||||
preload: join(__dirname, IS_DISCORD_DESKTOP ? "preload.js" : "vencordDesktopPreload.js"),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
sandbox: false
|
||||
|
@ -22,6 +22,28 @@ import { readFile } from "fs/promises";
|
||||
import { request } from "https";
|
||||
import { basename, normalize } from "path";
|
||||
|
||||
import { getSettings } from "./ipcMain";
|
||||
|
||||
// FixSpotifyEmbeds
|
||||
app.on("browser-window-created", (_, win) => {
|
||||
win.webContents.on("frame-created", (_, { frame }) => {
|
||||
frame.once("dom-ready", () => {
|
||||
if (frame.url.startsWith("https://open.spotify.com/embed/")) {
|
||||
const settings = getSettings().plugins?.FixSpotifyEmbeds;
|
||||
if (!settings?.enabled) return;
|
||||
|
||||
frame.executeJavaScript(`
|
||||
const original = Audio.prototype.play;
|
||||
Audio.prototype.play = function() {
|
||||
this.volume = ${(settings.volume / 100) || 0.1};
|
||||
return original.apply(this, arguments);
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// #region OpenInApp
|
||||
// These links don't support CORS, so this has to be native
|
||||
const validRedirectUrls = /^https:\/\/(spotify\.link|s\.team)\/.+$/;
|
||||
|
@ -16,22 +16,10 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { app, autoUpdater } from "electron";
|
||||
import { app } from "electron";
|
||||
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "fs";
|
||||
import { basename, dirname, join } from "path";
|
||||
|
||||
const { setAppUserModelId } = app;
|
||||
|
||||
// Apparently requiring Discords updater too early leads into issues,
|
||||
// copied this workaround from powerCord
|
||||
app.setAppUserModelId = function (id: string) {
|
||||
app.setAppUserModelId = setAppUserModelId;
|
||||
|
||||
setAppUserModelId.call(this, id);
|
||||
|
||||
patchUpdater();
|
||||
};
|
||||
|
||||
function isNewer($new: string, old: string) {
|
||||
const newParts = $new.slice(4).split(".").map(Number);
|
||||
const oldParts = old.slice(4).split(".").map(Number);
|
||||
@ -77,23 +65,6 @@ function patchLatest() {
|
||||
}
|
||||
}
|
||||
|
||||
// Windows Host Updates install to a new folder app-{HOST_VERSION}, so we
|
||||
// need to reinject
|
||||
function patchUpdater() {
|
||||
try {
|
||||
const autoStartScript = join(require.main!.filename, "..", "autoStart", "win32.js");
|
||||
const { update } = require(autoStartScript);
|
||||
|
||||
require.cache[autoStartScript]!.exports.update = function () {
|
||||
update.apply(this, arguments);
|
||||
patchLatest();
|
||||
};
|
||||
} catch {
|
||||
// OpenAsar uses electrons autoUpdater on Windows
|
||||
const { quitAndInstall } = autoUpdater;
|
||||
autoUpdater.quitAndInstall = function () {
|
||||
patchLatest();
|
||||
quitAndInstall.call(this);
|
||||
};
|
||||
}
|
||||
}
|
||||
// Try to patch latest on before-quit
|
||||
// Discord's Win32 updater will call app.quit() on restart and open new version on will-quit
|
||||
app.on("before-quit", patchLatest);
|
||||
|
@ -71,7 +71,7 @@ if (!IS_VANILLA) {
|
||||
constructor(options: BrowserWindowConstructorOptions) {
|
||||
if (options?.webPreferences?.preload && options.title) {
|
||||
const original = options.webPreferences.preload;
|
||||
options.webPreferences.preload = join(__dirname, "preload.js");
|
||||
options.webPreferences.preload = join(__dirname, IS_DISCORD_DESKTOP ? "preload.js" : "vencordDesktopPreload.js");
|
||||
options.webPreferences.sandbox = false;
|
||||
if (settings.frameless) {
|
||||
options.frame = false;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* eslint-disable header/header */
|
||||
/* eslint-disable simple-header/header */
|
||||
|
||||
/*!
|
||||
* BetterDiscord addon meta parser
|
||||
|
@ -16,12 +16,11 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
export const VENCORD_FILES = [
|
||||
IS_DISCORD_DESKTOP ? "patcher.js" : "vencordDesktopMain.js",
|
||||
"preload.js",
|
||||
IS_DISCORD_DESKTOP ? "preload.js" : "vencordDesktopPreload.js",
|
||||
IS_DISCORD_DESKTOP ? "renderer.js" : "vencordDesktopRenderer.js",
|
||||
"renderer.css"
|
||||
IS_DISCORD_DESKTOP ? "renderer.css" : "vencordDesktopRenderer.css",
|
||||
];
|
||||
|
||||
export function serializeErrors(func: (...args: any[]) => any) {
|
||||
|
@ -16,4 +16,5 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!IS_UPDATER_DISABLED)
|
||||
import(IS_STANDALONE ? "./http" : "./git");
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* eslint-disable header/header */
|
||||
/* eslint-disable simple-header/header */
|
||||
|
||||
/*!
|
||||
* crxToZip
|
||||
|
@ -27,8 +27,8 @@ export default definePlugin({
|
||||
{
|
||||
find: "Messages.DISCODO_DISABLED",
|
||||
replacement: {
|
||||
match: /(Messages\.DISCODO_DISABLED\);return)(.*?homeIcon.*?)(\}function)/,
|
||||
replace: "$1[$2].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))$3"
|
||||
match: /(Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
||||
replace: "$1[$2].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ import definePlugin from "@utils/types";
|
||||
export default definePlugin({
|
||||
name: "NoTrack",
|
||||
description: "Disable Discord's tracking ('science'), metrics and Sentry crash reporting",
|
||||
authors: [Devs.Cyn, Devs.Ven, Devs.Nuckyz],
|
||||
authors: [Devs.Cyn, Devs.Ven, Devs.Nuckyz, Devs.Arrow],
|
||||
required: true,
|
||||
patches: [
|
||||
{
|
||||
@ -51,6 +51,13 @@ export default definePlugin({
|
||||
replace: "return;"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: ".installedLogHooks)",
|
||||
replacement: {
|
||||
match: /if\(\i\.getDebugLogging\(\)&&!\i\.installedLogHooks\)/,
|
||||
replace: "if(false)"
|
||||
}
|
||||
},
|
||||
]
|
||||
});
|
||||
|
@ -19,7 +19,6 @@
|
||||
import { addContextMenuPatch } from "@api/ContextMenu";
|
||||
import { Settings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { React, SettingsRouter } from "@webpack/common";
|
||||
|
||||
@ -63,19 +62,14 @@ export default definePlugin({
|
||||
replacement: {
|
||||
get match() {
|
||||
switch (Settings.plugins.Settings.settingsLocation) {
|
||||
case "top": return /\{section:(.{1,2})\.ID\.HEADER,\s*label:(.{1,2})\..{1,2}\.Messages\.USER_SETTINGS\}/;
|
||||
case "aboveNitro": return /\{section:(.{1,2})\.ID\.HEADER,\s*label:(.{1,2})\..{1,2}\.Messages\.BILLING_SETTINGS\}/;
|
||||
case "belowNitro": return /\{section:(.{1,2})\.ID\.HEADER,\s*label:(.{1,2})\..{1,2}\.Messages\.APP_SETTINGS\}/;
|
||||
case "aboveActivity": return /\{section:(.{1,2})\.ID\.HEADER,\s*label:(.{1,2})\..{1,2}\.Messages\.ACTIVITY_SETTINGS\}/;
|
||||
case "belowActivity": return /(?<=\{section:(.{1,2})\.ID\.DIVIDER},)\{section:"changelog"/;
|
||||
case "bottom": return /\{section:(.{1,2})\.ID\.CUSTOM,\s*element:.+?}/;
|
||||
default: {
|
||||
new Logger("Settings").error(
|
||||
new Error("No switch case matched????? Don't mess with the settings, silly")
|
||||
);
|
||||
// matches nothing
|
||||
return /(?!a)a/;
|
||||
}
|
||||
case "top": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
|
||||
case "aboveNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
|
||||
case "belowNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
|
||||
case "belowActivity": return /(?<=\{section:(\i)\.ID\.DIVIDER},)\{section:"changelog"/;
|
||||
case "bottom": return /\{section:(\i)\.ID\.CUSTOM,\s*element:.+?}/;
|
||||
case "aboveActivity":
|
||||
default:
|
||||
return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
|
||||
}
|
||||
},
|
||||
replace: "...$self.makeSettingsCategories($1),$&"
|
||||
@ -88,48 +82,50 @@ export default definePlugin({
|
||||
return [
|
||||
{
|
||||
section: ID.HEADER,
|
||||
label: "Vencord"
|
||||
label: "Vencord",
|
||||
className: "vc-settings-header"
|
||||
},
|
||||
{
|
||||
section: "VencordSettings",
|
||||
label: "Vencord",
|
||||
element: require("@components/VencordSettings/VencordTab").default
|
||||
element: require("@components/VencordSettings/VencordTab").default,
|
||||
className: "vc-settings"
|
||||
},
|
||||
{
|
||||
section: "VencordPlugins",
|
||||
label: "Plugins",
|
||||
element: require("@components/VencordSettings/PluginsTab").default,
|
||||
className: "vc-plugins"
|
||||
},
|
||||
{
|
||||
section: "VencordThemes",
|
||||
label: "Themes",
|
||||
element: require("@components/VencordSettings/ThemesTab").default,
|
||||
className: "vc-themes"
|
||||
},
|
||||
!IS_WEB && {
|
||||
!IS_UPDATER_DISABLED && {
|
||||
section: "VencordUpdater",
|
||||
label: "Updater",
|
||||
element: require("@components/VencordSettings/UpdaterTab").default,
|
||||
className: "vc-updater"
|
||||
},
|
||||
{
|
||||
section: "VencordCloud",
|
||||
label: "Cloud",
|
||||
element: require("@components/VencordSettings/CloudTab").default,
|
||||
className: "vc-cloud"
|
||||
},
|
||||
{
|
||||
section: "VencordSettingsSync",
|
||||
label: "Backup & Restore",
|
||||
element: require("@components/VencordSettings/BackupAndRestoreTab").default,
|
||||
className: "vc-backup-restore"
|
||||
},
|
||||
IS_DEV && {
|
||||
section: "VencordPatchHelper",
|
||||
label: "Patch Helper",
|
||||
element: require("@components/VencordSettings/PatchHelperTab").default,
|
||||
},
|
||||
// TODO: make this use customSections
|
||||
IS_VENCORD_DESKTOP && {
|
||||
section: "VencordDesktop",
|
||||
label: "Desktop Settings",
|
||||
element: VencordDesktop.Components.Settings,
|
||||
className: "vc-patch-helper"
|
||||
},
|
||||
...this.customSections.map(func => func(ID)),
|
||||
{
|
||||
@ -172,7 +168,7 @@ export default definePlugin({
|
||||
get additionalInfo() {
|
||||
if (IS_DEV) return " (Dev)";
|
||||
if (IS_WEB) return " (Web)";
|
||||
if (IS_VENCORD_DESKTOP) return ` (VencordDesktop v${VencordDesktopNative.app.getVersion()})`;
|
||||
if (IS_VESKTOP) return ` (Vesktop v${VesktopNative.app.getVersion()})`;
|
||||
if (IS_STANDALONE) return " (Standalone)";
|
||||
return "";
|
||||
},
|
||||
|
@ -26,6 +26,8 @@ const enum Methods {
|
||||
Timestamp,
|
||||
}
|
||||
|
||||
const tarExtMatcher = /\.tar\.\w+$/;
|
||||
|
||||
export default definePlugin({
|
||||
name: "AnonymiseFileNames",
|
||||
authors: [Devs.obscurity],
|
||||
@ -67,7 +69,8 @@ export default definePlugin({
|
||||
|
||||
anonymise(file: string) {
|
||||
let name = "image";
|
||||
const extIdx = file.lastIndexOf(".");
|
||||
const tarMatch = tarExtMatcher.exec(file);
|
||||
const extIdx = tarMatch?.index ?? file.lastIndexOf(".");
|
||||
const ext = extIdx !== -1 ? file.slice(extIdx) : "";
|
||||
|
||||
switch (Settings.plugins.AnonymiseFileNames.method) {
|
||||
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
@ -35,7 +34,7 @@ export default definePlugin({
|
||||
},
|
||||
},
|
||||
{
|
||||
find: 'preload:"none","aria',
|
||||
find: ".embedGallerySide",
|
||||
replacement: {
|
||||
match: /(?<==(.{1,3})\.alt.{0,20})\?.{0,5}\.Messages\.GIF/,
|
||||
replace:
|
||||
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import { findStoreLazy } from "@webpack";
|
||||
|
||||
import * as t from "./types/stores";
|
||||
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
export const defaultRules = [
|
||||
"action_object_map",
|
||||
"action_type_map",
|
||||
@ -128,6 +127,7 @@ export const defaultRules = [
|
||||
"redircnt@yandex.*",
|
||||
"feature@youtube.com",
|
||||
"kw@youtube.com",
|
||||
"si@youtu.be",
|
||||
"wt_zmc",
|
||||
"utm_source",
|
||||
"utm_content",
|
||||
|
55
src/plugins/copyUserURLs.tsx
Normal file
55
src/plugins/copyUserURLs.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||
import { LinkIcon } from "@components/Icons";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Clipboard, Menu } from "@webpack/common";
|
||||
import type { Channel, User } from "discord-types/general";
|
||||
|
||||
interface UserContextProps {
|
||||
channel: Channel;
|
||||
guildId?: string;
|
||||
user: User;
|
||||
}
|
||||
|
||||
const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: UserContextProps) => () => {
|
||||
children.push(
|
||||
<Menu.MenuItem
|
||||
id="vc-copy-user-url"
|
||||
label="Copy User URL"
|
||||
action={() => Clipboard.copy(`<https://discord.com/users/${user.id}>`)}
|
||||
icon={LinkIcon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "CopyUserURLs",
|
||||
authors: [Devs.castdrian],
|
||||
description: "Adds a 'Copy User URL' option to the user context menu.",
|
||||
|
||||
start() {
|
||||
addContextMenuPatch("user-context", UserContextMenuPatch);
|
||||
},
|
||||
|
||||
stop() {
|
||||
removeContextMenuPatch("user-context", UserContextMenuPatch);
|
||||
},
|
||||
});
|
@ -23,10 +23,12 @@ import { Logger } from "@utils/Logger";
|
||||
import { closeAllModals } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { maybePromptToUpdate } from "@utils/updater";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { FluxDispatcher, NavigationRouter } from "@webpack/common";
|
||||
import type { ReactElement } from "react";
|
||||
|
||||
const CrashHandlerLogger = new Logger("CrashHandler");
|
||||
const ModalStack = findByPropsLazy("pushLazy", "popAll");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
attemptToPreventCrashes: {
|
||||
@ -51,8 +53,6 @@ export default definePlugin({
|
||||
authors: [Devs.Nuckyz],
|
||||
enabledByDefault: true,
|
||||
|
||||
popAllModals: undefined as (() => void) | undefined,
|
||||
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
@ -62,13 +62,6 @@ export default definePlugin({
|
||||
match: /(?=this\.setState\()/,
|
||||
replace: "$self.handleCrash(this)||"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: 'dispatch({type:"MODAL_POP_ALL"})',
|
||||
replacement: {
|
||||
match: /"MODAL_POP_ALL".+?};(?<=(\i)=function.+?)/,
|
||||
replace: (m, popAll) => `${m}$self.popAllModals=${popAll};`
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@ -128,7 +121,7 @@ export default definePlugin({
|
||||
CrashHandlerLogger.debug("Failed to close open context menu.", err);
|
||||
}
|
||||
try {
|
||||
this.popAllModals?.();
|
||||
ModalStack?.popAll();
|
||||
} catch (err) {
|
||||
CrashHandlerLogger.debug("Failed to close old modals.", err);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { Link } from "@components/Link";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { isTruthy } from "@utils/guards";
|
||||
@ -86,8 +86,7 @@ const settings = definePluginSettings({
|
||||
appID: {
|
||||
type: OptionType.STRING,
|
||||
description: "Application ID (required)",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
isValid: (value: string) => {
|
||||
if (!value) return "Application ID is required.";
|
||||
if (value && !/^\d+$/.test(value)) return "Application ID must be a number.";
|
||||
@ -97,8 +96,7 @@ const settings = definePluginSettings({
|
||||
appName: {
|
||||
type: OptionType.STRING,
|
||||
description: "Application name (required)",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
isValid: (value: string) => {
|
||||
if (!value) return "Application name is required.";
|
||||
if (value.length > 128) return "Application name must be not longer than 128 characters.";
|
||||
@ -108,8 +106,7 @@ const settings = definePluginSettings({
|
||||
details: {
|
||||
type: OptionType.STRING,
|
||||
description: "Details (line 1)",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
isValid: (value: string) => {
|
||||
if (value && value.length > 128) return "Details (line 1) must be not longer than 128 characters.";
|
||||
return true;
|
||||
@ -118,8 +115,7 @@ const settings = definePluginSettings({
|
||||
state: {
|
||||
type: OptionType.STRING,
|
||||
description: "State (line 2)",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
isValid: (value: string) => {
|
||||
if (value && value.length > 128) return "State (line 2) must be not longer than 128 characters.";
|
||||
return true;
|
||||
@ -128,8 +124,7 @@ const settings = definePluginSettings({
|
||||
type: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Activity type",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
options: [
|
||||
{
|
||||
label: "Playing",
|
||||
@ -157,16 +152,14 @@ const settings = definePluginSettings({
|
||||
streamLink: {
|
||||
type: OptionType.STRING,
|
||||
description: "Twitch.tv or Youtube.com link (only for Streaming activity type)",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
isDisabled: isStreamLinkDisabled,
|
||||
onChange: onChange,
|
||||
disabled: isStreamLinkDisabled,
|
||||
isValid: isStreamLinkValid
|
||||
},
|
||||
timestampMode: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Timestamp mode",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
options: [
|
||||
{
|
||||
label: "None",
|
||||
@ -190,9 +183,8 @@ const settings = definePluginSettings({
|
||||
startTime: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Start timestamp (only for custom timestamp mode)",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
isDisabled: isTimestampDisabled,
|
||||
onChange: onChange,
|
||||
disabled: isTimestampDisabled,
|
||||
isValid: (value: number) => {
|
||||
if (value && value < 0) return "Start timestamp must be greater than 0.";
|
||||
return true;
|
||||
@ -201,9 +193,8 @@ const settings = definePluginSettings({
|
||||
endTime: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "End timestamp (only for custom timestamp mode)",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
isDisabled: isTimestampDisabled,
|
||||
onChange: onChange,
|
||||
disabled: isTimestampDisabled,
|
||||
isValid: (value: number) => {
|
||||
if (value && value < 0) return "End timestamp must be greater than 0.";
|
||||
return true;
|
||||
@ -211,16 +202,14 @@ const settings = definePluginSettings({
|
||||
},
|
||||
imageBig: {
|
||||
type: OptionType.STRING,
|
||||
description: "Big image key",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
description: "Big image key/link",
|
||||
onChange: onChange,
|
||||
isValid: isImageKeyValid
|
||||
},
|
||||
imageBigTooltip: {
|
||||
type: OptionType.STRING,
|
||||
description: "Big image tooltip",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
isValid: (value: string) => {
|
||||
if (value && value.length > 128) return "Big image tooltip must be not longer than 128 characters.";
|
||||
return true;
|
||||
@ -228,16 +217,14 @@ const settings = definePluginSettings({
|
||||
},
|
||||
imageSmall: {
|
||||
type: OptionType.STRING,
|
||||
description: "Small image key",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
description: "Small image key/link",
|
||||
onChange: onChange,
|
||||
isValid: isImageKeyValid
|
||||
},
|
||||
imageSmallTooltip: {
|
||||
type: OptionType.STRING,
|
||||
description: "Small image tooltip",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
isValid: (value: string) => {
|
||||
if (value && value.length > 128) return "Small image tooltip must be not longer than 128 characters.";
|
||||
return true;
|
||||
@ -246,8 +233,7 @@ const settings = definePluginSettings({
|
||||
buttonOneText: {
|
||||
type: OptionType.STRING,
|
||||
description: "Button 1 text",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
isValid: (value: string) => {
|
||||
if (value && value.length > 31) return "Button 1 text must be not longer than 31 characters.";
|
||||
return true;
|
||||
@ -256,14 +242,12 @@ const settings = definePluginSettings({
|
||||
buttonOneURL: {
|
||||
type: OptionType.STRING,
|
||||
description: "Button 1 URL",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc
|
||||
onChange: onChange
|
||||
},
|
||||
buttonTwoText: {
|
||||
type: OptionType.STRING,
|
||||
description: "Button 2 text",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc,
|
||||
onChange: onChange,
|
||||
isValid: (value: string) => {
|
||||
if (value && value.length > 31) return "Button 2 text must be not longer than 31 characters.";
|
||||
return true;
|
||||
@ -272,26 +256,29 @@ const settings = definePluginSettings({
|
||||
buttonTwoURL: {
|
||||
type: OptionType.STRING,
|
||||
description: "Button 2 URL",
|
||||
restartNeeded: true,
|
||||
onChange: setRpc
|
||||
onChange: onChange
|
||||
}
|
||||
});
|
||||
|
||||
function isStreamLinkDisabled(): boolean {
|
||||
function onChange() {
|
||||
setRpc(true);
|
||||
if (Settings.plugins.CustomRPC.enabled) setRpc();
|
||||
}
|
||||
|
||||
function isStreamLinkDisabled() {
|
||||
return settings.store.type !== ActivityType.STREAMING;
|
||||
}
|
||||
|
||||
function isStreamLinkValid(): boolean | string {
|
||||
if (settings.store.type === ActivityType.STREAMING && settings.store.streamLink && !/(https?:\/\/(www\.)?(twitch\.tv|youtube\.com)\/\w+)/.test(settings.store.streamLink)) return "Streaming link must be a valid URL.";
|
||||
function isStreamLinkValid(value: string) {
|
||||
if (!isStreamLinkDisabled() && !/https?:\/\/(www\.)?(twitch\.tv|youtube\.com)\/\w+/.test(value)) return "Streaming link must be a valid URL.";
|
||||
return true;
|
||||
}
|
||||
|
||||
function isTimestampDisabled(): boolean {
|
||||
function isTimestampDisabled() {
|
||||
return settings.store.timestampMode !== TimestampMode.CUSTOM;
|
||||
}
|
||||
|
||||
function isImageKeyValid(value: string) {
|
||||
if (!/https?:\/\//.test(value)) return true;
|
||||
if (/https?:\/\/(?!i\.)?imgur\.com\//.test(value)) return "Imgur link must be a direct link to the image. (e.g. https://i.imgur.com/...)";
|
||||
if (/https?:\/\/(?!media\.)?tenor\.com\//.test(value)) return "Tenor link must be a direct link to the image. (e.g. https://media.tenor.com/...)";
|
||||
return true;
|
||||
@ -342,13 +329,10 @@ async function createActivity(): Promise<Activity | undefined> {
|
||||
};
|
||||
break;
|
||||
case TimestampMode.CUSTOM:
|
||||
if (startTime) {
|
||||
activity.timestamps = {
|
||||
start: startTime,
|
||||
};
|
||||
if (endTime) {
|
||||
activity.timestamps.end = endTime;
|
||||
}
|
||||
if (startTime || endTime) {
|
||||
activity.timestamps = {};
|
||||
if (startTime) activity.timestamps.start = startTime;
|
||||
if (endTime) activity.timestamps.end = endTime;
|
||||
}
|
||||
break;
|
||||
case TimestampMode.NONE:
|
||||
|
@ -313,7 +313,7 @@ export default definePlugin({
|
||||
find: ".Messages.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION",
|
||||
predicate: () => settings.store.transformEmojis,
|
||||
replacement: {
|
||||
match: /((\i)=\i\.node,\i=\i\.emojiSourceDiscoverableGuild)(.+?return )(.{0,450}Messages\.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION.+?}\))/,
|
||||
match: /((\i)=\i\.node,\i=\i\.expressionSourceGuild)(.+?return )(.{0,450}Messages\.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION.+?}\))/,
|
||||
replace: (_, rest1, node, rest2, reactNode) => `${rest1},fakeNitroNode=${node}${rest2}$self.addFakeNotice("EMOJI",${reactNode},fakeNitroNode.fake)`
|
||||
}
|
||||
}
|
||||
|
26
src/plugins/fixSpotifyEmbeds.desktop.ts
Normal file
26
src/plugins/fixSpotifyEmbeds.desktop.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
// The entire code of this plugin can be found in ipcPlugins
|
||||
export default definePlugin({
|
||||
name: "FixSpotifyEmbeds",
|
||||
description: "Fixes spotify embeds being incredibly loud by letting you customise the volume",
|
||||
authors: [Devs.Ven],
|
||||
settings: definePluginSettings({
|
||||
volume: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "The volume % to set for spotify embeds. Anything above 10% is veeeery loud",
|
||||
markers: makeRange(0, 100, 10),
|
||||
stickToMarkers: false,
|
||||
default: 10
|
||||
}
|
||||
})
|
||||
});
|
@ -32,12 +32,18 @@ function makeIcon(showCurrentGame?: boolean) {
|
||||
return function () {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 96 960 960"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path fill="currentColor" d="M182 856q-51 0-79-35.5T82 734l42-300q9-60 53.5-99T282 296h396q60 0 104.5 39t53.5 99l42 300q7 51-21 86.5T778 856q-21 0-39-7.5T706 826l-90-90H344l-90 90q-15 15-33 22.5t-39 7.5Zm498-240q17 0 28.5-11.5T720 576q0-17-11.5-28.5T680 536q-17 0-28.5 11.5T640 576q0 17 11.5 28.5T680 616Zm-80-120q17 0 28.5-11.5T640 456q0-17-11.5-28.5T600 416q-17 0-28.5 11.5T560 456q0 17 11.5 28.5T600 496ZM310 616h60v-70h70v-60h-70v-70h-60v70h-70v60h70v70Z" />
|
||||
{!showCurrentGame && <line x1="920" y1="280" x2="40" y2="880" stroke="var(--status-danger)" stroke-width="80" />}
|
||||
<path fill="currentColor" mask="url(#gameActivityMask)" d="M3.06 20.4q-1.53 0-2.37-1.065T.06 16.74l1.26-9q.27-1.8 1.605-2.97T6.06 3.6h11.88q1.8 0 3.135 1.17t1.605 2.97l1.26 9q.21 1.53-.63 2.595T20.94 20.4q-.63 0-1.17-.225T18.78 19.5l-2.7-2.7H7.92l-2.7 2.7q-.45.45-.99.675t-1.17.225Zm14.94-7.2q.51 0 .855-.345T19.2 12q0-.51-.345-.855T18 10.8q-.51 0-.855.345T16.8 12q0 .51.345 .855T18 13.2Zm-2.4-3.6q.51 0 .855-.345T16.8 8.4q0-.51-.345-.855T15.6 7.2q-.51 0-.855.345T14.4 8.4q0 .51.345 .855T15.6 9.6ZM6.9 13.2h1.8v-2.1h2.1v-1.8h-2.1v-2.1h-1.8v2.1h-2.1v1.8h2.1v2.1Z" />
|
||||
{!showCurrentGame && <>
|
||||
<mask id="gameActivityMask" >
|
||||
<rect fill="white" x="0" y="0" width="24" height="24" />
|
||||
<path fill="black" d="M23.27 4.54 19.46.73 .73 19.46 4.54 23.27 23.27 4.54Z"/>
|
||||
</mask>
|
||||
<path fill="var(--status-danger)" d="M23 2.27 21.73 1 1 21.73 2.27 23 23 2.27Z" />
|
||||
</>}
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@ -60,7 +66,7 @@ function GameActivityToggleButton() {
|
||||
export default definePlugin({
|
||||
name: "GameActivityToggle",
|
||||
description: "Adds a button next to the mic and deafen button to toggle game activity.",
|
||||
authors: [Devs.Nuckyz],
|
||||
authors: [Devs.Nuckyz, Devs.RuukuLada],
|
||||
dependencies: ["SettingsStoreAPI"],
|
||||
|
||||
patches: [
|
||||
|
@ -49,7 +49,7 @@ export default definePlugin({
|
||||
await this.buildCss();
|
||||
|
||||
addButton("HideAttachments", msg => {
|
||||
if (!msg.attachments.length && !msg.embeds.length) return null;
|
||||
if (!msg.attachments.length && !msg.embeds.length && !msg.stickerItems.length) return null;
|
||||
|
||||
const isHidden = hiddenMessages.has(msg.id);
|
||||
|
||||
@ -72,7 +72,7 @@ export default definePlugin({
|
||||
async buildCss() {
|
||||
const elements = [...hiddenMessages].map(id => `#message-accessories-${id}`).join(",");
|
||||
style.textContent = `
|
||||
:is(${elements}) [class*="embedWrapper"] {
|
||||
:is(${elements}) :is([class*="embedWrapper", [class*"clickableSticker"]) {
|
||||
/* important is not necessary, but add it to make sure bad themes won't break it */
|
||||
display: none !important;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ function EncModal(props: ModalProps) {
|
||||
const [password, setPassword] = React.useState("password");
|
||||
const [noCover, setNoCover] = React.useState(false);
|
||||
|
||||
const isValid = secret && (noCover || (cover && /\w \w/.test(cover)));
|
||||
const isValid = secret && (noCover || (cover && cover.trim().split(" ").length > 1));
|
||||
|
||||
return (
|
||||
<ModalRoot {...props}>
|
||||
|
@ -30,6 +30,9 @@ const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
|
||||
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
|
||||
};
|
||||
|
||||
const sharedIntlNumberFormat = new Intl.NumberFormat();
|
||||
const numberFormat = (value: number) => sharedIntlNumberFormat.format(value);
|
||||
|
||||
function MemberCount() {
|
||||
const { id: channelId, guild_id: guildId } = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
|
||||
const { groups } = useStateFromStores(
|
||||
@ -52,13 +55,12 @@ function MemberCount() {
|
||||
return (
|
||||
<Flex id="vc-membercount" style={{
|
||||
marginTop: "1em",
|
||||
marginBottom: "-.5em",
|
||||
paddingInline: "1em",
|
||||
justifyContent: "center",
|
||||
alignContent: "center",
|
||||
gap: 0
|
||||
}}>
|
||||
<Tooltip text={`${online} Online in this Channel`} position="bottom">
|
||||
<Tooltip text={`${numberFormat(online)} online in this channel`} position="bottom">
|
||||
{props => (
|
||||
<div {...props}>
|
||||
<span
|
||||
@ -71,11 +73,11 @@ function MemberCount() {
|
||||
marginRight: "0.5em"
|
||||
}}
|
||||
/>
|
||||
<span style={{ color: "var(--green-360)" }}>{online}</span>
|
||||
<span style={{ color: "var(--green-360)" }}>{numberFormat(online)}</span>
|
||||
</div>
|
||||
)}
|
||||
</Tooltip>
|
||||
<Tooltip text={`${total} Total Server Members`} position="bottom">
|
||||
<Tooltip text={`${numberFormat(total)} total server members`} position="bottom">
|
||||
{props => (
|
||||
<div {...props}>
|
||||
<span
|
||||
@ -89,7 +91,7 @@ function MemberCount() {
|
||||
marginLeft: "1em"
|
||||
}}
|
||||
/>
|
||||
<span style={{ color: "var(--primary-400)" }}>{total}</span>
|
||||
<span style={{ color: "var(--primary-400)" }}>{numberFormat(total)}</span>
|
||||
</div>
|
||||
)}
|
||||
</Tooltip>
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
import { addAccessory } from "@api/MessageAccessories";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { getSettingStoreLazy } from "@api/SettingsStore";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants.js";
|
||||
import { classes } from "@utils/misc";
|
||||
@ -318,9 +319,10 @@ function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbe
|
||||
/>;
|
||||
}
|
||||
|
||||
const compactModeEnabled = getSettingStoreLazy<boolean>("textAndImages", "messageDisplayCompact")!;
|
||||
|
||||
function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
||||
const { message, channel, guildID } = props;
|
||||
|
||||
const isDM = guildID === "@me";
|
||||
const images = getImages(message);
|
||||
const { parse } = Parser;
|
||||
@ -336,7 +338,7 @@ function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
||||
<span>{isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name}</span>
|
||||
</Text>
|
||||
}
|
||||
compact={false}
|
||||
compact={compactModeEnabled.getSetting()}
|
||||
content={
|
||||
<>
|
||||
{message.content || message.attachments.length <= images.length
|
||||
@ -363,7 +365,7 @@ export default definePlugin({
|
||||
name: "MessageLinkEmbeds",
|
||||
description: "Adds a preview to messages that link another message",
|
||||
authors: [Devs.TheSun, Devs.Ven, Devs.RyanCaoDev],
|
||||
dependencies: ["MessageAccessoriesAPI"],
|
||||
dependencies: ["MessageAccessoriesAPI", "SettingsStoreAPI"],
|
||||
patches: [
|
||||
{
|
||||
find: ".embedCard",
|
||||
|
@ -3,6 +3,11 @@
|
||||
color: #f04747 !important;
|
||||
}
|
||||
|
||||
/* Bot "thinking" text highlighting */
|
||||
.messagelogger-deleted [class*="colorStandard-"] {
|
||||
color: #f04747 !important;
|
||||
}
|
||||
|
||||
/* Embed highlighting */
|
||||
.messagelogger-deleted article :is(div, span, h1, h2, h3, p) {
|
||||
color: #f04747 !important;
|
||||
|
@ -26,7 +26,7 @@ import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { FluxDispatcher, i18n, Menu, moment, Parser, Timestamp, UserStore } from "@webpack/common";
|
||||
import { ChannelStore, FluxDispatcher, i18n, Menu, moment, Parser, Timestamp, UserStore } from "@webpack/common";
|
||||
|
||||
import overlayStyle from "./deleteStyleOverlay.css?managed";
|
||||
import textStyle from "./deleteStyleText.css?managed";
|
||||
@ -92,7 +92,7 @@ const patchMessageContextMenu: NavContextMenuPatchCallback = (children, props) =
|
||||
export default definePlugin({
|
||||
name: "MessageLogger",
|
||||
description: "Temporarily logs deleted and edited messages.",
|
||||
authors: [Devs.rushii, Devs.Ven],
|
||||
authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN],
|
||||
|
||||
start() {
|
||||
addDeleteStyle();
|
||||
@ -169,21 +169,14 @@ export default definePlugin({
|
||||
try {
|
||||
if (cache == null || (!isBulk && !cache.has(data.id))) return cache;
|
||||
|
||||
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds } = Settings.plugins.MessageLogger;
|
||||
const myId = UserStore.getCurrentUser().id;
|
||||
|
||||
function mutate(id: string) {
|
||||
const mutate = (id: string) => {
|
||||
const msg = cache.get(id);
|
||||
if (!msg) return;
|
||||
|
||||
const EPHEMERAL = 64;
|
||||
const shouldIgnore = data.mlDeleted ||
|
||||
(msg.flags & EPHEMERAL) === EPHEMERAL ||
|
||||
ignoreBots && msg.author?.bot ||
|
||||
ignoreSelf && msg.author?.id === myId ||
|
||||
ignoreUsers.includes(msg.author?.id) ||
|
||||
ignoreChannels.includes(msg.channel_id) ||
|
||||
ignoreGuilds.includes(msg.guild_id);
|
||||
this.shouldIgnore(msg);
|
||||
|
||||
if (shouldIgnore) {
|
||||
cache = cache.remove(id);
|
||||
@ -192,7 +185,7 @@ export default definePlugin({
|
||||
.set("deleted", true)
|
||||
.set("attachments", m.attachments.map(a => (a.deleted = true, a))));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (isBulk) {
|
||||
data.ids.forEach(mutate);
|
||||
@ -205,6 +198,17 @@ export default definePlugin({
|
||||
return cache;
|
||||
},
|
||||
|
||||
shouldIgnore(message: any) {
|
||||
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds } = Settings.plugins.MessageLogger;
|
||||
const myId = UserStore.getCurrentUser().id;
|
||||
|
||||
return ignoreBots && message.author?.bot ||
|
||||
ignoreSelf && message.author?.id === myId ||
|
||||
ignoreUsers.includes(message.author?.id) ||
|
||||
ignoreChannels.includes(message.channel_id) ||
|
||||
ignoreGuilds.includes(ChannelStore.getChannel(message.channel_id)?.guild_id);
|
||||
},
|
||||
|
||||
// Based on canary 9ab8626bcebceaea6da570b9c586172d02b9c996
|
||||
patches: [
|
||||
{
|
||||
@ -237,7 +241,7 @@ export default definePlugin({
|
||||
match: /(MESSAGE_UPDATE:function\((\w)\).+?)\.update\((\w)/,
|
||||
replace: "$1" +
|
||||
".update($3,m =>" +
|
||||
" (($2.message.flags & 64) === 64 || (Vencord.Settings.plugins.MessageLogger.ignoreBots && $2.message.author?.bot) || (Vencord.Settings.plugins.MessageLogger.ignoreSelf && $2.message.author?.id === Vencord.Webpack.Common.UserStore.getCurrentUser().id)) ? m :" +
|
||||
" (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message)) ? m :" +
|
||||
" $2.message.content !== m.editHistory?.[0]?.content && $2.message.content !== m.content ?" +
|
||||
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" +
|
||||
" m" +
|
||||
|
@ -54,6 +54,8 @@ interface IVoiceChannelEffectSendEvent {
|
||||
const MOYAI = "🗿";
|
||||
const MOYAI_URL =
|
||||
"https://raw.githubusercontent.com/MeguminSama/VencordPlugins/main/plugins/moyai/moyai.mp3";
|
||||
const MOYAI_URL_HD =
|
||||
"https://raw.githubusercontent.com/MeguminSama/VencordPlugins/main/plugins/moyai/moyai_hd.wav";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
volume: {
|
||||
@ -63,6 +65,14 @@ const settings = definePluginSettings({
|
||||
default: 0.5,
|
||||
stickToMarkers: false
|
||||
},
|
||||
quality: {
|
||||
description: "Quality of the 🗿🗿🗿",
|
||||
type: OptionType.SELECT,
|
||||
options: [
|
||||
{ label: "Normal", value: "Normal", default: true },
|
||||
{ label: "HD", value: "HD" }
|
||||
],
|
||||
},
|
||||
triggerWhenUnfocused: {
|
||||
description: "Trigger the 🗿 even when the window is unfocused",
|
||||
type: OptionType.BOOLEAN,
|
||||
@ -157,7 +167,11 @@ function getMoyaiCount(message: string) {
|
||||
function boom() {
|
||||
if (!settings.store.triggerWhenUnfocused && !document.hasFocus()) return;
|
||||
const audioElement = document.createElement("audio");
|
||||
audioElement.src = MOYAI_URL;
|
||||
|
||||
audioElement.src = settings.store.quality === "HD"
|
||||
? MOYAI_URL_HD
|
||||
: MOYAI_URL;
|
||||
|
||||
audioElement.volume = settings.store.volume;
|
||||
audioElement.play();
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import { isNonNullish } from "@utils/guards";
|
||||
import definePlugin from "@utils/types";
|
||||
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
@ -27,10 +27,10 @@ export default definePlugin({
|
||||
{
|
||||
find: '("ApplicationStreamPreviewUploadManager")',
|
||||
replacement: [
|
||||
"\\i\\.default\\.makeChunkedRequest\\(",
|
||||
"\\i\\.\\i\\.post\\({url:"
|
||||
String.raw`\i\.\i\.makeChunkedRequest\(`,
|
||||
String.raw`\i\.\i\.post\({url:`
|
||||
].map(match => ({
|
||||
match: new RegExp(`(?=return\\[(\\d),${match}\\i\\.\\i\\.STREAM_PREVIEW.+?}\\)\\];)`),
|
||||
match: new RegExp(String.raw`(?=return\[(\d),${match}\i\.\i\.STREAM_PREVIEW.+?}\)\];)`),
|
||||
replace: (_, code) => `return[${code},Promise.resolve({body:"",status:204})];`
|
||||
}))
|
||||
}
|
||||
|
26
src/plugins/normalizeMessageLinks/index.ts
Normal file
26
src/plugins/normalizeMessageLinks/index.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "NormalizeMessageLinks",
|
||||
description: "Strip canary/ptb from message links",
|
||||
authors: [Devs.bb010g],
|
||||
patches: [
|
||||
{
|
||||
find: ".Messages.COPY_MESSAGE_LINK,",
|
||||
replacement: {
|
||||
match: /\.concat\(location\.host\)/,
|
||||
replace: ".concat($self.normalizeHost(location.host))",
|
||||
},
|
||||
},
|
||||
],
|
||||
normalizeHost(host: string) {
|
||||
return host.replace(/(^|\b)(canary\.|ptb\.)(discord.com)$/, "$1$3");
|
||||
},
|
||||
});
|
@ -55,7 +55,7 @@ export default definePlugin({
|
||||
{
|
||||
find: '"MaskedLinkStore"',
|
||||
replacement: {
|
||||
match: /return ((\i)\.apply\(this,arguments\))(?=\}function \i.{0,200}\.trusted)/,
|
||||
match: /return ((\i)\.apply\(this,arguments\))(?=\}function \i.{0,250}\.trusted)/,
|
||||
replace: "return $self.handleLink(...arguments).then(handled => handled||$1)"
|
||||
}
|
||||
},
|
||||
|
@ -178,12 +178,12 @@ export default definePlugin({
|
||||
start() {
|
||||
addContextMenuPatch("user-context", this.userContextMenuPatch);
|
||||
addContextMenuPatch("channel-context", this.channelContextMenuPatch);
|
||||
addContextMenuPatch("guild-context", this.guildContextMenuPatch);
|
||||
addContextMenuPatch(["guild-context", "guild-header-popout"], this.guildContextMenuPatch);
|
||||
},
|
||||
|
||||
stop() {
|
||||
removeContextMenuPatch("user-context", this.userContextMenuPatch);
|
||||
removeContextMenuPatch("channel-context", this.channelContextMenuPatch);
|
||||
removeContextMenuPatch("guild-context", this.guildContextMenuPatch);
|
||||
removeContextMenuPatch(["guild-context", "guild-header-popout"], this.guildContextMenuPatch);
|
||||
},
|
||||
});
|
||||
|
@ -122,6 +122,14 @@ export default definePlugin({
|
||||
// ....concat(pins).concat(toArray(channelIds).filter(c => !isPinned(c)))
|
||||
replace: ".concat($self.getSnapshot()).concat($2.filter(c=>!$self.isPinned(c)))"
|
||||
}
|
||||
},
|
||||
// fix alt+shift+up/down
|
||||
{
|
||||
find: '"alt+shift+down"',
|
||||
replacement: {
|
||||
match: /(?<=return \i===\i\.ME\?)\i\.\i\.getPrivateChannelIds\(\)/,
|
||||
replace: "$self.getSnapshot().concat($&.filter(c=>!$self.isPinned(c)))"
|
||||
}
|
||||
},
|
||||
]
|
||||
});
|
||||
|
82
src/plugins/previewMessage.tsx
Normal file
82
src/plugins/previewMessage.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { sendBotMessage } from "@api/Commands";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Button, ButtonLooks, ButtonWrapperClasses, DraftStore, DraftType, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||
|
||||
interface Props {
|
||||
type: {
|
||||
analyticsName: string;
|
||||
};
|
||||
}
|
||||
|
||||
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
|
||||
|
||||
export function PreviewButton(chatBoxProps: Props) {
|
||||
const channelId = SelectedChannelStore.getChannelId();
|
||||
const draft = useStateFromStores([DraftStore], () => getDraft(channelId));
|
||||
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
||||
if (!draft) return null;
|
||||
|
||||
return (
|
||||
<Tooltip text="Preview Message">
|
||||
{tooltipProps => (
|
||||
<Button
|
||||
{...tooltipProps}
|
||||
onClick={() =>
|
||||
sendBotMessage(
|
||||
channelId,
|
||||
{
|
||||
content: getDraft(channelId),
|
||||
author: UserStore.getCurrentUser()
|
||||
}
|
||||
)}
|
||||
size=""
|
||||
look={ButtonLooks.BLANK}
|
||||
innerClassName={ButtonWrapperClasses.button}
|
||||
style={{ padding: "0 2px", height: "100%" }}
|
||||
>
|
||||
<div className={ButtonWrapperClasses.buttonWrapper}>
|
||||
<img width={24} height={24} src="https://discord.com/assets/4c5a77a89716352686f590a6f014770c.svg" />
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "PreviewMessage",
|
||||
description: "Lets you preview your message before sending it",
|
||||
authors: [Devs.Aria],
|
||||
patches: [
|
||||
{
|
||||
find: ".activeCommandOption",
|
||||
replacement: {
|
||||
match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
||||
replace: "$&;try{$2||$1.unshift($self.previewIcon(arguments[0]))}catch{}",
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
previewIcon: ErrorBoundary.wrap(PreviewButton, { noop: true }),
|
||||
});
|
@ -68,7 +68,8 @@ export default definePlugin({
|
||||
find: ".USER_PROFILE_ACTIVITY",
|
||||
replacement: [
|
||||
{
|
||||
match: /getGlobalName\(\i\);(?<=displayProfile.{0,200})/,
|
||||
/* FIXME: old name is getGlobalName, new name is getName. Remove optional Global once stable has also migrated */
|
||||
match: /\.get(?:Global)?Name\(\i\);(?<=displayProfile.{0,200})/,
|
||||
replace: "$&const [vcPronounce,vcPronounSource]=$self.useProfilePronouns(arguments[0].user.id,true);if(arguments[0].displayProfile&&vcPronounce)arguments[0].displayProfile.pronouns=vcPronounce;"
|
||||
},
|
||||
PRONOUN_TOOLTIP_PATCH
|
||||
|
@ -33,7 +33,7 @@ export default LazyComponent(() => {
|
||||
// this is terrible, blame mantika
|
||||
const p = filters.byProps;
|
||||
const [
|
||||
{ cozyMessage, buttons, message, groupStart },
|
||||
{ cozyMessage, buttons, message, buttonsInner, groupStart },
|
||||
{ container, isHeader },
|
||||
{ avatar, clickable, username, messageContent, wrapper, cozy },
|
||||
buttonClasses,
|
||||
@ -134,7 +134,7 @@ export default LazyComponent(() => {
|
||||
<div className={classes(container, isHeader, buttons)} style={{
|
||||
padding: "0px",
|
||||
}}>
|
||||
<div className={buttonClasses.wrapper} >
|
||||
<div className={classes(buttonClasses.wrapper, buttonsInner)} >
|
||||
<ReportButton onClick={reportRev} />
|
||||
|
||||
{canDeleteReview(review, UserStore.getCurrentUser().id) && (
|
||||
|
@ -19,7 +19,7 @@
|
||||
import { classes } from "@utils/misc";
|
||||
import { useAwaiter, useForceUpdater } from "@utils/react";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Forms, React, UserStore } from "@webpack/common";
|
||||
import { Forms, React, RelationshipStore, UserStore } from "@webpack/common";
|
||||
import type { KeyboardEvent } from "react";
|
||||
|
||||
import { Review } from "../entities";
|
||||
@ -60,6 +60,9 @@ export default function ReviewsView({
|
||||
fallbackValue: null,
|
||||
deps: [refetchSignal, signal, page],
|
||||
onSuccess: data => {
|
||||
if (settings.store.hideBlockedUsers)
|
||||
data!.reviews = data!.reviews?.filter(r => !RelationshipStore.isBlocked(r.sender.discordID));
|
||||
|
||||
scrollToTop?.();
|
||||
onFetchReviews(data!);
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import { Review, ReviewDBUser } from "./entities";
|
||||
import { settings } from "./settings";
|
||||
import { authorize, showToast } from "./utils";
|
||||
|
@ -48,6 +48,11 @@ export const settings = definePluginSettings({
|
||||
description: "Hide timestamps on reviews",
|
||||
default: false,
|
||||
},
|
||||
hideBlockedUsers: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Hide reviews from blocked users",
|
||||
default: true,
|
||||
},
|
||||
website: {
|
||||
type: OptionType.COMPONENT,
|
||||
description: "ReviewDB website",
|
||||
|
21
src/plugins/rnnoise.web/icons.tsx
Normal file
21
src/plugins/rnnoise.web/icons.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export const SupressionIcon = ({ enabled }: { enabled: boolean; }) => enabled
|
||||
? <svg aria-hidden="true" role="img" width="20" height="20" viewBox="0 0 24 24"><path d="M10.889 4C10.889 3.44772 11.3367 3 11.889 3H12.1112C12.6635 3 13.1112 3.44772 13.1112 4V20C13.1112 20.5523 12.6635 21 12.1112 21H11.889C11.3367 21 10.889 20.5523 10.889 20V4Z" fill="currentColor"></path><path d="M6.44439 6.25C6.44439 5.69772 6.89211 5.25 7.44439 5.25H7.66661C8.2189 5.25 8.66661 5.69772 8.66661 6.25V17.75C8.66661 18.3023 8.2189 18.75 7.66661 18.75H7.44439C6.89211 18.75 6.44439 18.3023 6.44439 17.75V6.25Z" fill="currentColor"></path><path d="M3.22222 15.375C3.77451 15.375 4.22222 14.9273 4.22222 14.375L4.22222 9.625C4.22222 9.07272 3.77451 8.625 3.22222 8.625H3C2.44772 8.625 2 9.07272 2 9.625V14.375C2 14.9273 2.44772 15.375 3 15.375H3.22222Z" fill="currentColor"></path><path d="M22.0001 13.25C22.0001 13.8023 21.5523 14.25 21.0001 14.25H20.7778C20.2255 14.25 19.7778 13.8023 19.7778 13.25V10.75C19.7778 10.1977 20.2255 9.75 20.7778 9.75H21.0001C21.5523 9.75 22.0001 10.1977 22.0001 10.75V13.25Z" fill="currentColor"></path><path d="M16.3333 7.5C15.781 7.5 15.3333 7.94772 15.3333 8.5V15.5C15.3333 16.0523 15.781 16.5 16.3333 16.5H16.5555C17.1078 16.5 17.5555 16.0523 17.5555 15.5V8.5C17.5555 7.94772 17.1078 7.5 16.5555 7.5H16.3333Z" fill="currentColor"></path></svg>
|
||||
: <svg aria-hidden="true" role="img" width="20" height="20" viewBox="0 0 48 48"><path d="M30.6666 24.9644L35.1111 20.5199V31C35.1111 32.1046 34.2156 33 33.1111 33H32.6666C31.562 33 30.6666 32.1046 30.6666 31V24.9644Z" fill="currentColor"></path><path d="M26.2224 14.1463V8C26.2224 6.89543 25.327 6 24.2224 6H23.7779C22.6734 6 21.7779 6.89543 21.7779 8V18.5907L26.2224 14.1463Z" fill="currentColor"></path><path d="M21.7779 33.8543L21.9254 33.7056L26.2224 29.4086V40C26.2224 41.1046 25.327 42 24.2224 42H23.7779C22.6734 42 21.7779 41.1046 21.7779 40V33.8543Z" fill="currentColor"></path><path d="M17.3332 23.0354L12.8888 27.4799V12.5C12.8888 11.3954 13.7842 10.5 14.8888 10.5H15.3332C16.4378 10.5 17.3332 11.3954 17.3332 12.5V23.0354Z" fill="currentColor"></path><path d="M8.44445 28.75C8.44445 29.8546 7.54902 30.75 6.44445 30.75H6C4.89543 30.75 4 29.8546 4 28.75V19.25C4 18.1454 4.89543 17.25 6 17.25H6.44445C7.54902 17.25 8.44445 18.1454 8.44445 19.25L8.44445 28.75Z" fill="currentColor"></path><path d="M44.0001 26.5C44.0001 27.6046 43.1047 28.5 42.0001 28.5H41.5557C40.4511 28.5 39.5557 27.6046 39.5557 26.5V21.5C39.5557 20.3954 40.4511 19.5 41.5557 19.5H42.0001C43.1047 19.5 44.0001 20.3954 44.0001 21.5V26.5Z" fill="currentColor"></path><path d="M42 8.54L39.46 6L6 39.46L8.54 42L16.92 33.64L19.38 31.16L22.7 27.84L29.98 20.56L42 8.54Z" fill="currentColor"></path></svg>;
|
249
src/plugins/rnnoise.web/index.tsx
Normal file
249
src/plugins/rnnoise.web/index.tsx
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Switch } from "@components/Switch";
|
||||
import { loadRnnoise, RnnoiseWorkletNode } from "@sapphi-red/web-noise-suppressor";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { rnnoiseWasmSrc, rnnoiseWorkletSrc } from "@utils/dependencies";
|
||||
import { makeLazy } from "@utils/lazy";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { LazyComponent } from "@utils/react";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findByCode } from "@webpack";
|
||||
import { FluxDispatcher, Popout, React } from "@webpack/common";
|
||||
import { MouseEvent, ReactNode } from "react";
|
||||
|
||||
import { SupressionIcon } from "./icons";
|
||||
|
||||
const RNNOISE_OPTION = "RNNOISE";
|
||||
|
||||
interface PanelButtonProps {
|
||||
tooltipText: string;
|
||||
icon: () => ReactNode;
|
||||
onClick: (event: MouseEvent<HTMLElement>) => void;
|
||||
tooltipClassName?: string;
|
||||
disabled?: boolean;
|
||||
shouldShow?: boolean;
|
||||
}
|
||||
const PanelButton = LazyComponent<PanelButtonProps>(() => findByCode("Masks.PANEL_BUTTON"));
|
||||
const enum SpinnerType {
|
||||
SpinningCircle = "spinningCircle",
|
||||
ChasingDots = "chasingDots",
|
||||
LowMotion = "lowMotion",
|
||||
PulsingEllipsis = "pulsingEllipsis",
|
||||
WanderingCubes = "wanderingCubes",
|
||||
}
|
||||
export interface SpinnerProps {
|
||||
type: SpinnerType;
|
||||
animated?: boolean;
|
||||
className?: string;
|
||||
itemClassName?: string;
|
||||
}
|
||||
const Spinner = LazyComponent<SpinnerProps>(() => findByCode(".spinningCircleInner"));
|
||||
|
||||
function createExternalStore<S>(init: () => S) {
|
||||
const subscribers = new Set<() => void>();
|
||||
let state = init();
|
||||
|
||||
return {
|
||||
get: () => state,
|
||||
set: (newStateGetter: (oldState: S) => S) => {
|
||||
state = newStateGetter(state);
|
||||
for (const cb of subscribers) cb();
|
||||
},
|
||||
use: () => {
|
||||
return React.useSyncExternalStore<S>(onStoreChange => {
|
||||
subscribers.add(onStoreChange);
|
||||
return () => subscribers.delete(onStoreChange);
|
||||
}, () => state);
|
||||
},
|
||||
} as const;
|
||||
}
|
||||
|
||||
const cl = classNameFactory("vc-rnnoise-");
|
||||
|
||||
const loadedStore = createExternalStore(() => ({
|
||||
isLoaded: false,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
}));
|
||||
const getRnnoiseWasm = makeLazy(() => {
|
||||
loadedStore.set(s => ({ ...s, isLoading: true }));
|
||||
return loadRnnoise({
|
||||
url: rnnoiseWasmSrc(),
|
||||
simdUrl: rnnoiseWasmSrc(true),
|
||||
}).then(buffer => {
|
||||
// Check WASM magic number cus fetch doesnt throw on 4XX or 5XX
|
||||
if (new DataView(buffer.slice(0, 4)).getUint32(0) !== 0x0061736D) throw buffer;
|
||||
|
||||
loadedStore.set(s => ({ ...s, isLoaded: true }));
|
||||
return buffer;
|
||||
}).catch(error => {
|
||||
if (error instanceof ArrayBuffer) error = new TextDecoder().decode(error);
|
||||
logger.error("Failed to load RNNoise WASM:", error);
|
||||
loadedStore.set(s => ({ ...s, isError: true }));
|
||||
return null;
|
||||
}).finally(() => {
|
||||
loadedStore.set(s => ({ ...s, isLoading: false }));
|
||||
});
|
||||
});
|
||||
|
||||
const logger = new Logger("RNNoise");
|
||||
const settings = definePluginSettings({}).withPrivateSettings<{ isEnabled: boolean; }>();
|
||||
const setEnabled = (enabled: boolean) => {
|
||||
settings.store.isEnabled = enabled;
|
||||
FluxDispatcher.dispatch({ type: "AUDIO_SET_NOISE_SUPPRESSION", enabled });
|
||||
};
|
||||
|
||||
function NoiseSupressionPopout() {
|
||||
const { isEnabled } = settings.use();
|
||||
const { isLoading, isError } = loadedStore.use();
|
||||
const isWorking = isEnabled && !isError;
|
||||
|
||||
return <div className={cl("popout")}>
|
||||
<div className={cl("popout-heading")}>
|
||||
<span>Noise Supression</span>
|
||||
<div style={{ flex: 1 }} />
|
||||
{isLoading && <Spinner type={SpinnerType.PulsingEllipsis} />}
|
||||
<Switch checked={isWorking} onChange={setEnabled} disabled={isError} />
|
||||
</div>
|
||||
<div className={cl("popout-desc")}>
|
||||
Enable AI noise suppression! Make some noise—like becoming an air conditioner, or a vending machine fan—while speaking. Your friends will hear nothing but your beautiful voice ✨
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "AI Noise Suppression",
|
||||
description: "Uses an open-source AI model (RNNoise) to remove background noise from your microphone",
|
||||
authors: [Devs.Vap],
|
||||
settings,
|
||||
enabledByDefault: true,
|
||||
|
||||
patches: [
|
||||
{
|
||||
// Pass microphone stream to RNNoise
|
||||
find: "window.webkitAudioContext",
|
||||
replacement: {
|
||||
match: /(?<=\i\.acquire=function\((\i)\)\{return )navigator\.mediaDevices\.getUserMedia\(\1\)(?=\})/,
|
||||
replace: m => `${m}.then(stream => $self.connectRnnoise(stream))`
|
||||
},
|
||||
},
|
||||
{
|
||||
// Noise suppression button in call modal
|
||||
find: "renderNoiseCancellation()",
|
||||
replacement: {
|
||||
match: /(?<=(\i)\.jsxs?.{0,70}children:\[)(?=\i\?\i\.renderNoiseCancellation\(\))/,
|
||||
replace: (_, react) => `${react}.jsx($self.NoiseSupressionButton, {}),`
|
||||
},
|
||||
},
|
||||
{
|
||||
// Give noise suppression component a "shouldShow" prop
|
||||
find: "Masks.PANEL_BUTTON",
|
||||
replacement: {
|
||||
match: /(?<==(\i)\.tooltipForceOpen.{0,100})(?=tooltipClassName:)/,
|
||||
replace: (_, props) => `shouldShow: ${props}.shouldShow,`
|
||||
}
|
||||
},
|
||||
{
|
||||
// Noise suppression option in voice settings
|
||||
find: "Messages.USER_SETTINGS_NOISE_CANCELLATION_KRISP",
|
||||
replacement: [{
|
||||
match: /(?<=(\i)=\i\?\i\.KRISP:\i.{1,20}?;)/,
|
||||
replace: (_, option) => `if ($self.isEnabled()) ${option} = ${JSON.stringify(RNNOISE_OPTION)};`,
|
||||
}, {
|
||||
match: /(?=\i&&(\i)\.push\(\{name:(?:\i\.){1,2}Messages.USER_SETTINGS_NOISE_CANCELLATION_KRISP)/,
|
||||
replace: (_, options) => `${options}.push({ name: "AI (RNNoise)", value: "${RNNOISE_OPTION}" });`,
|
||||
}, {
|
||||
match: /(?<=onChange:function\((\i)\)\{)(?=(?:\i\.){1,2}setNoiseCancellation)/,
|
||||
replace: (_, option) => `$self.setEnabled(${option}.value === ${JSON.stringify(RNNOISE_OPTION)});`,
|
||||
}],
|
||||
},
|
||||
],
|
||||
|
||||
setEnabled,
|
||||
isEnabled: () => settings.store.isEnabled,
|
||||
async connectRnnoise(stream: MediaStream): Promise<MediaStream> {
|
||||
if (!settings.store.isEnabled) return stream;
|
||||
|
||||
const audioCtx = new AudioContext();
|
||||
await audioCtx.audioWorklet.addModule(rnnoiseWorkletSrc);
|
||||
|
||||
const rnnoiseWasm = await getRnnoiseWasm();
|
||||
if (!rnnoiseWasm) {
|
||||
logger.warn("Failed to load RNNoise, noise suppression won't work");
|
||||
return stream;
|
||||
}
|
||||
|
||||
const rnnoise = new RnnoiseWorkletNode(audioCtx, {
|
||||
wasmBinary: rnnoiseWasm,
|
||||
maxChannels: 1,
|
||||
});
|
||||
|
||||
const source = audioCtx.createMediaStreamSource(stream);
|
||||
source.connect(rnnoise);
|
||||
|
||||
const dest = audioCtx.createMediaStreamDestination();
|
||||
rnnoise.connect(dest);
|
||||
|
||||
// Cleanup
|
||||
const onEnded = () => {
|
||||
rnnoise.disconnect();
|
||||
source.disconnect();
|
||||
audioCtx.close();
|
||||
rnnoise.destroy();
|
||||
};
|
||||
stream.addEventListener("inactive", onEnded, { once: true });
|
||||
|
||||
return dest.stream;
|
||||
},
|
||||
NoiseSupressionButton(): ReactNode {
|
||||
const { isEnabled } = settings.use();
|
||||
const { isLoading, isError } = loadedStore.use();
|
||||
|
||||
return <Popout
|
||||
key="rnnoise-popout"
|
||||
align="center"
|
||||
animation={Popout.Animation.TRANSLATE}
|
||||
autoInvert={true}
|
||||
nudgeAlignIntoViewport={true}
|
||||
position="top"
|
||||
renderPopout={() => <NoiseSupressionPopout />}
|
||||
spacing={8}
|
||||
>
|
||||
{(props, { isShown }) => (
|
||||
<PanelButton
|
||||
{...props}
|
||||
tooltipText="Noise Suppression powered by RNNoise"
|
||||
tooltipClassName={cl("tooltip")}
|
||||
shouldShow={!isShown}
|
||||
icon={() => <div style={{
|
||||
color: isError ? "var(--status-danger)" : "inherit",
|
||||
opacity: isLoading ? 0.5 : 1,
|
||||
}}>
|
||||
<SupressionIcon enabled={isEnabled} />
|
||||
</div>}
|
||||
/>
|
||||
)}
|
||||
</Popout>;
|
||||
},
|
||||
});
|
29
src/plugins/rnnoise.web/styles.css
Normal file
29
src/plugins/rnnoise.web/styles.css
Normal file
@ -0,0 +1,29 @@
|
||||
.vc-rnnoise-popout {
|
||||
background: var(--background-floating);
|
||||
border-radius: 0.25em;
|
||||
padding: 1em;
|
||||
width: 16em;
|
||||
}
|
||||
|
||||
.vc-rnnoise-popout-heading {
|
||||
color: var(--text-normal);
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 1em;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.vc-rnnoise-popout-desc {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.vc-rnnoise-tooltip {
|
||||
text-align: center;
|
||||
}
|
35
src/plugins/secretRingTone.ts
Normal file
35
src/plugins/secretRingTone.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "SecretRingToneEnabler",
|
||||
description: "Always play the secret version of the discord ringtone",
|
||||
authors: [Devs.AndrewDLO],
|
||||
patches: [
|
||||
{
|
||||
find: "84a1b4e11d634dbfa1e5dd97a96de3ad",
|
||||
replacement: {
|
||||
match: "84a1b4e11d634dbfa1e5dd97a96de3ad.mp3",
|
||||
replace: "b9411af07f154a6fef543e7e442e4da9.mp3",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
@ -146,7 +146,7 @@ export default definePlugin({
|
||||
<div style={{ display: "flex" }}>
|
||||
<Button
|
||||
aria-haspopup="dialog"
|
||||
aria-label=""
|
||||
aria-label="Insert Timestamp"
|
||||
size=""
|
||||
look={ButtonLooks.BLANK}
|
||||
onMouseEnter={onMouseEnter}
|
||||
|
247
src/plugins/serverProfile/GuildProfileModal.tsx
Normal file
247
src/plugins/serverProfile/GuildProfileModal.tsx
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { openImageModal, openUserProfile } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { LazyComponent, useAwaiter } from "@utils/react";
|
||||
import { findByCode, findByPropsLazy } from "@webpack";
|
||||
import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, moment, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common";
|
||||
import { Guild, User } from "discord-types/general";
|
||||
|
||||
const IconUtils = findByPropsLazy("getGuildBannerURL");
|
||||
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
|
||||
const UserRow = LazyComponent(() => findByCode(".listDiscriminator"));
|
||||
|
||||
const cl = classNameFactory("vc-gp-");
|
||||
|
||||
export function openGuildProfileModal(guild: Guild) {
|
||||
openModal(props =>
|
||||
<ModalRoot {...props} size={ModalSize.MEDIUM}>
|
||||
<GuildProfileModal guild={guild} />
|
||||
</ModalRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const enum Tabs {
|
||||
ServerInfo,
|
||||
Friends,
|
||||
BlockedUsers
|
||||
}
|
||||
|
||||
interface GuildProps {
|
||||
guild: Guild;
|
||||
}
|
||||
|
||||
interface RelationshipProps extends GuildProps {
|
||||
setCount(count: number): void;
|
||||
}
|
||||
|
||||
const fetched = {
|
||||
friends: false,
|
||||
blocked: false
|
||||
};
|
||||
|
||||
function renderTimestamp(timestamp: number) {
|
||||
return (
|
||||
<Timestamp timestamp={moment(timestamp)} />
|
||||
);
|
||||
}
|
||||
|
||||
function GuildProfileModal({ guild }: GuildProps) {
|
||||
const [friendCount, setFriendCount] = useState<number>();
|
||||
const [blockedCount, setBlockedCount] = useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
fetched.friends = false;
|
||||
fetched.blocked = false;
|
||||
}, []);
|
||||
|
||||
const [currentTab, setCurrentTab] = useState(Tabs.ServerInfo);
|
||||
|
||||
const bannerUrl = guild.banner && IconUtils.getGuildBannerURL({
|
||||
id: guild.id,
|
||||
banner: guild.banner
|
||||
}, true).replace(/\?size=\d+$/, "?size=1024");
|
||||
|
||||
const iconUrl = guild.icon && IconUtils.getGuildIconURL({
|
||||
id: guild.id,
|
||||
icon: guild.icon,
|
||||
canAnimate: true,
|
||||
size: 512
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={cl("root")}>
|
||||
{bannerUrl && currentTab === Tabs.ServerInfo && (
|
||||
<img
|
||||
className={cl("banner")}
|
||||
src={bannerUrl}
|
||||
alt=""
|
||||
onClick={() => openImageModal(bannerUrl)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className={cl("header")}>
|
||||
{guild.icon
|
||||
? <img
|
||||
src={iconUrl}
|
||||
alt=""
|
||||
onClick={() => openImageModal(iconUrl)}
|
||||
/>
|
||||
: <div aria-hidden className={classes(IconClasses.childWrapper, IconClasses.acronym)}>{guild.acronym}</div>
|
||||
}
|
||||
|
||||
<div className={cl("name-and-description")}>
|
||||
<Forms.FormTitle tag="h5" className={cl("name")}>{guild.name}</Forms.FormTitle>
|
||||
{guild.description && <Forms.FormText>{guild.description}</Forms.FormText>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabBar
|
||||
type="top"
|
||||
look="brand"
|
||||
className={cl("tab-bar")}
|
||||
selectedItem={currentTab}
|
||||
onItemSelect={setCurrentTab}
|
||||
>
|
||||
<TabBar.Item
|
||||
className={cl("tab", { selected: currentTab === Tabs.ServerInfo })}
|
||||
id={Tabs.ServerInfo}
|
||||
>
|
||||
Server Info
|
||||
</TabBar.Item>
|
||||
<TabBar.Item
|
||||
className={cl("tab", { selected: currentTab === Tabs.Friends })}
|
||||
id={Tabs.Friends}
|
||||
>
|
||||
Friends{friendCount !== undefined ? ` (${friendCount})` : ""}
|
||||
</TabBar.Item>
|
||||
<TabBar.Item
|
||||
className={cl("tab", { selected: currentTab === Tabs.BlockedUsers })}
|
||||
id={Tabs.BlockedUsers}
|
||||
>
|
||||
Blocked Users{blockedCount !== undefined ? ` (${blockedCount})` : ""}
|
||||
</TabBar.Item>
|
||||
</TabBar>
|
||||
|
||||
<div className={cl("tab-content")}>
|
||||
{currentTab === Tabs.ServerInfo && <ServerInfoTab guild={guild} />}
|
||||
{currentTab === Tabs.Friends && <FriendsTab guild={guild} setCount={setFriendCount} />}
|
||||
{currentTab === Tabs.BlockedUsers && <BlockedUsersTab guild={guild} setCount={setBlockedCount} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function Owner(guildId: string, owner: User) {
|
||||
const guildAvatar = GuildMemberStore.getMember(guildId, owner.id)?.avatar;
|
||||
const ownerAvatarUrl =
|
||||
guildAvatar
|
||||
? IconUtils.getGuildMemberAvatarURLSimple({
|
||||
userId: owner!.id,
|
||||
avatar: guildAvatar,
|
||||
guildId,
|
||||
canAnimate: true
|
||||
}, true)
|
||||
: IconUtils.getUserAvatarURL(owner, true);
|
||||
|
||||
return (
|
||||
<div className={cl("owner")}>
|
||||
<img src={ownerAvatarUrl} alt="" onClick={() => openImageModal(ownerAvatarUrl)} />
|
||||
{Parser.parse(`<@${owner.id}>`)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ServerInfoTab({ guild }: GuildProps) {
|
||||
const [owner] = useAwaiter(() => UserUtils.fetchUser(guild.ownerId), {
|
||||
deps: [guild.ownerId],
|
||||
fallbackValue: null
|
||||
});
|
||||
|
||||
const Fields = {
|
||||
"Server Owner": owner ? Owner(guild.id, owner) : "Loading...",
|
||||
"Created At": renderTimestamp(SnowflakeUtils.extractTimestamp(guild.id)),
|
||||
"Joined At": renderTimestamp(guild.joinedAt.getTime()),
|
||||
"Vanity Link": guild.vanityURLCode ? (<a>{`discord.gg/${guild.vanityURLCode}`}</a>) : "-", // Making the anchor href valid would cause Discord to reload
|
||||
"Preferred Locale": guild.preferredLocale || "-",
|
||||
"Verification Level": ["None", "Low", "Medium", "High", "Highest"][guild.verificationLevel] || "?",
|
||||
"Nitro Boosts": `${guild.premiumSubscriberCount ?? 0} (Level ${guild.premiumTier ?? 0})`,
|
||||
"Channels": GuildChannelStore.getChannels(guild.id)?.count - 1 || "?", // - null category
|
||||
"Roles": Object.keys(guild.roles).length - 1, // - @everyone
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cl("info")}>
|
||||
{Object.entries(Fields).map(([name, node]) =>
|
||||
<div className={cl("server-info-pair")} key={name}>
|
||||
<Forms.FormTitle tag="h5">{name}</Forms.FormTitle>
|
||||
{typeof node === "string" ? <span>{node}</span> : node}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FriendsTab({ guild, setCount }: RelationshipProps) {
|
||||
return UserList("friends", guild, RelationshipStore.getFriendIDs(), setCount);
|
||||
}
|
||||
|
||||
function BlockedUsersTab({ guild, setCount }: RelationshipProps) {
|
||||
const blockedIds = Object.keys(RelationshipStore.getRelationships()).filter(id => RelationshipStore.isBlocked(id));
|
||||
return UserList("blocked", guild, blockedIds, setCount);
|
||||
}
|
||||
|
||||
function UserList(type: "friends" | "blocked", guild: Guild, ids: string[], setCount: (count: number) => void) {
|
||||
const missing = [] as string[];
|
||||
const members = [] as string[];
|
||||
|
||||
for (const id of ids) {
|
||||
if (GuildMemberStore.isMember(guild.id, id))
|
||||
members.push(id);
|
||||
else
|
||||
missing.push(id);
|
||||
}
|
||||
|
||||
// Used for side effects (rerender on member request success)
|
||||
useStateFromStores(
|
||||
[GuildMemberStore],
|
||||
() => GuildMemberStore.getMemberIds(guild.id),
|
||||
null,
|
||||
(old, curr) => old.length === curr.length
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!fetched[type] && missing.length) {
|
||||
fetched[type] = true;
|
||||
FluxDispatcher.dispatch({
|
||||
type: "GUILD_MEMBERS_REQUEST",
|
||||
guildIds: [guild.id],
|
||||
userIds: missing
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => setCount(members.length), [members.length]);
|
||||
|
||||
return (
|
||||
<ScrollerThin fade className={cl("scroller")}>
|
||||
{members.map(id =>
|
||||
<UserRow
|
||||
user={UserStore.getUser(id)}
|
||||
status={PresenceStore.getStatus(id) || "offline"}
|
||||
onSelect={() => openUserProfile(id)}
|
||||
onContextMenu={() => { }}
|
||||
/>
|
||||
)}
|
||||
</ScrollerThin>
|
||||
);
|
||||
}
|
7
src/plugins/serverProfile/README.md
Normal file
7
src/plugins/serverProfile/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# ServerProfile
|
||||
|
||||
Allows you to view info about servers and see friends and blocked users
|
||||
|
||||
![image](https://github.com/Vendicated/Vencord/assets/45497981/a49783b5-e8fc-41d8-968f-58600e9f6580)
|
||||
![image](https://github.com/Vendicated/Vencord/assets/45497981/5efc158a-e671-4196-a15a-77edf79a2630)
|
||||
![image](https://github.com/Vendicated/Vencord/assets/45497981/f43be943-6dc4-4232-9709-fbeb382d8e54)
|
40
src/plugins/serverProfile/index.tsx
Normal file
40
src/plugins/serverProfile/index.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Menu } from "@webpack/common";
|
||||
import { Guild } from "discord-types/general";
|
||||
|
||||
import { openGuildProfileModal } from "./GuildProfileModal";
|
||||
|
||||
const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => () => {
|
||||
const group = findGroupChildrenByChildId("privacy", children);
|
||||
|
||||
group?.push(
|
||||
<Menu.MenuItem
|
||||
id="vc-server-profile"
|
||||
label="Server Profile"
|
||||
action={() => openGuildProfileModal(guild)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "ServerProfile",
|
||||
description: "Allows you to view info about a server by right clicking it in the server list",
|
||||
authors: [Devs.Ven, Devs.Nuckyz],
|
||||
tags: ["guild", "info"],
|
||||
|
||||
start() {
|
||||
addContextMenuPatch(["guild-context", "guild-header-popout"], Patch);
|
||||
},
|
||||
|
||||
stop() {
|
||||
removeContextMenuPatch(["guild-context", "guild-header-popout"], Patch);
|
||||
}
|
||||
});
|
97
src/plugins/serverProfile/styles.css
Normal file
97
src/plugins/serverProfile/styles.css
Normal file
@ -0,0 +1,97 @@
|
||||
.vc-gp-root {
|
||||
height: 100%;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.vc-gp-banner {
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vc-gp-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.vc-gp-header img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vc-gp-name-and-description {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2em;
|
||||
}
|
||||
|
||||
.vc-gp-name {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.vc-gp-tab-bar {
|
||||
border-bottom: 2px solid var(--background-modifier-accent);
|
||||
margin: 20px 12px 0;
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
align-items: stretch;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.vc-gp-tab {
|
||||
border-bottom: 2px solid transparent;
|
||||
color: var(--interactive-normal);
|
||||
cursor: pointer;
|
||||
height: 39px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.vc-gp-tab-content {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.vc-gp-tab:where(.vc-gp-selected, :hover, :focus) {
|
||||
border-bottom-color: var(--interactive-active);
|
||||
}
|
||||
|
||||
.vc-gp-info {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.vc-gp-server-info-pair {
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.vc-gp-server-info-pair [class^="timestamp"] {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.vc-gp-owner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.2em;
|
||||
}
|
||||
|
||||
.vc-gp-owner img {
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vc-gp-scroller {
|
||||
width: 100%;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
.vc-gp-scroller [class^="listRow"] {
|
||||
margin: 1px 0;
|
||||
}
|
||||
|
||||
.vc-gp-scroller [class^="listRow"]:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import { shikiOnigasmSrc, shikiWorkerSrc } from "@utils/dependencies";
|
||||
import { WorkerClient } from "@vap/core/ipc";
|
||||
import type { IShikiTheme, IThemedToken } from "@vap/shiki";
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* eslint-disable header/header */
|
||||
/* eslint-disable simple-header/header */
|
||||
import React from "react";
|
||||
|
||||
const handleClick = async () =>
|
||||
|
@ -21,13 +21,14 @@ import "./styles.css";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Message } from "discord-types/general";
|
||||
import { Message, User } from "discord-types/general";
|
||||
|
||||
interface UsernameProps {
|
||||
author: { nick: string; };
|
||||
message: Message;
|
||||
withMentionPrefix?: boolean;
|
||||
isRepliedMessage: boolean;
|
||||
userOverride?: User;
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
@ -40,6 +41,11 @@ const settings = definePluginSettings({
|
||||
{ label: "Username only", value: "user" },
|
||||
],
|
||||
},
|
||||
displayNames: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Use display names in place of usernames",
|
||||
default: false
|
||||
},
|
||||
inReplies: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
@ -50,7 +56,7 @@ const settings = definePluginSettings({
|
||||
export default definePlugin({
|
||||
name: "ShowMeYourName",
|
||||
description: "Display usernames next to nicks, or no nicks at all",
|
||||
authors: [Devs.dzshn],
|
||||
authors: [Devs.dzshn, Devs.TheKodeToad],
|
||||
patches: [
|
||||
{
|
||||
find: ".withMentionPrefix",
|
||||
@ -62,10 +68,13 @@ export default definePlugin({
|
||||
],
|
||||
settings,
|
||||
|
||||
renderUsername: ({ author, message, isRepliedMessage, withMentionPrefix }: UsernameProps) => {
|
||||
if (message.interaction) return author?.nick;
|
||||
renderUsername: ({ author, message, isRepliedMessage, withMentionPrefix, userOverride }: UsernameProps) => {
|
||||
try {
|
||||
const { username } = message.author;
|
||||
const user = userOverride ?? message.author;
|
||||
let { username } = user;
|
||||
if (settings.store.displayNames)
|
||||
username = (user as any).globalName || username;
|
||||
|
||||
const { nick } = author;
|
||||
const prefix = withMentionPrefix ? "@" : "";
|
||||
if (username === nick || isRepliedMessage && !settings.store.inReplies)
|
||||
|
35
src/plugins/showTimeouts.ts
Normal file
35
src/plugins/showTimeouts.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "ShowTimeouts",
|
||||
description: "Display member timeout icons in chat regardless of permissions.",
|
||||
authors: [Devs.Dolfies],
|
||||
patches: [
|
||||
{
|
||||
find: "showCommunicationDisabledStyles",
|
||||
replacement: {
|
||||
match: /&&\i\.\i\.canManageUser\(\i\.\i\.MODERATE_MEMBERS,\i\.author,\i\)/,
|
||||
replace: "",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import { LazyComponent } from "@utils/react";
|
||||
import definePlugin from "@utils/types";
|
||||
|
@ -53,7 +53,7 @@ export default definePlugin({
|
||||
|
||||
const client = (() => {
|
||||
if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`;
|
||||
if (IS_VENCORD_DESKTOP) return `Vencord Desktop v${VencordDesktopNative.app.getVersion()}`;
|
||||
if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`;
|
||||
if ("armcord" in window) return `ArmCord v${window.armcord.version}`;
|
||||
|
||||
// @ts-expect-error
|
||||
|
20
src/plugins/themeAttributes/README.md
Normal file
20
src/plugins/themeAttributes/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# ThemeAttributes
|
||||
|
||||
This plugin adds data attributes to various elements inside Discord
|
||||
|
||||
This allows themes to more easily theme those elements or even do things that otherwise wouldn't be possible
|
||||
|
||||
## Available Attributes
|
||||
|
||||
### All Tab Bars (User Settings, Server Settings, etc)
|
||||
|
||||
`data-tab-id` contains the id of that tab
|
||||
|
||||
![image](https://github.com/Vendicated/Vencord/assets/45497981/1263b782-f673-4f09-820c-4cc366d062ad)
|
||||
|
||||
### Chat Messages
|
||||
|
||||
- `data-author-id` contains the id of the author
|
||||
- `data-is-self` is a boolean indicating whether this is the current user's message
|
||||
|
||||
![image](https://github.com/Vendicated/Vencord/assets/45497981/34bd5053-3381-402f-82b2-9c812cc7e122)
|
45
src/plugins/themeAttributes/index.ts
Normal file
45
src/plugins/themeAttributes/index.ts
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { UserStore } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
|
||||
export default definePlugin({
|
||||
name: "ThemeAttributes",
|
||||
description: "Adds data attributes to various elements for theming purposes",
|
||||
authors: [Devs.Ven],
|
||||
|
||||
patches: [
|
||||
// Add data-tab-id to all tab bar items
|
||||
// This for examples applies to the User and Server settings sidebars
|
||||
{
|
||||
find: ".tabBarRef",
|
||||
replacement: {
|
||||
match: /style:this\.getStyle\(\),role:"tab"/,
|
||||
replace: "$&,'data-tab-id':this.props.id"
|
||||
}
|
||||
},
|
||||
|
||||
// Add data-author-id and data-is-self to all messages
|
||||
{
|
||||
find: ".messageListItem",
|
||||
replacement: {
|
||||
match: /\.messageListItem(?=,"aria)/,
|
||||
replace: "$&,...$self.getMessageProps(arguments[0])"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
getMessageProps(props: { message: Message; }) {
|
||||
const authorId = props.message?.author?.id;
|
||||
return {
|
||||
"data-author-id": authorId,
|
||||
"data-is-self": authorId && authorId === UserStore.getCurrentUser()?.id
|
||||
};
|
||||
}
|
||||
});
|
@ -51,7 +51,7 @@ export function TranslateChatBarIcon({ slateProps }: { slateProps: { type: { ana
|
||||
<div style={{ display: "flex" }}>
|
||||
<Button
|
||||
aria-haspopup="dialog"
|
||||
aria-label=""
|
||||
aria-label="Open Translate Modal"
|
||||
size=""
|
||||
look={ButtonLooks.BLANK}
|
||||
onMouseEnter={onMouseEnter}
|
||||
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
To generate:
|
||||
- Visit https://translate.google.com/?sl=auto&tl=en&op=translate
|
||||
|
@ -19,6 +19,7 @@
|
||||
import "./index.css";
|
||||
|
||||
import { openNotificationLogModal } from "@api/Notifications/notificationLog";
|
||||
import { Settings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { LazyComponent } from "@utils/react";
|
||||
@ -66,6 +67,15 @@ function VencordPopout(onClose: () => void) {
|
||||
label="Open Notification Log"
|
||||
action={openNotificationLogModal}
|
||||
/>
|
||||
<Menu.MenuCheckboxItem
|
||||
id="vc-toolbox-quickcss-toggle"
|
||||
checked={Settings.useQuickCss}
|
||||
label={"Enable QuickCSS"}
|
||||
action={() => {
|
||||
Settings.useQuickCss = !Settings.useQuickCss;
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
<Menu.MenuItem
|
||||
id="vc-toolbox-quickcss"
|
||||
label="Open QuickCSS"
|
||||
|
@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||
import { addButton, removeButton } from "@api/MessagePopover";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
@ -25,9 +26,11 @@ 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 { Button, ChannelStore, Forms, Parser, Text } from "@webpack/common";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Button, ChannelStore, Forms, Menu, Parser, Text } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
|
||||
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer");
|
||||
|
||||
const CopyIcon = () => {
|
||||
return <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" width="22" height="22">
|
||||
@ -60,17 +63,13 @@ function cleanMessage(msg: Message) {
|
||||
|
||||
function CodeBlock(props: { content: string, lang: string; }) {
|
||||
return (
|
||||
// make text selectable
|
||||
<div style={{ userSelect: "text" }}>
|
||||
<div className={CodeContainerClasses.markup}>
|
||||
{Parser.defaultRules.codeBlock.react(props, null, {})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function openViewRawModal(msg: Message) {
|
||||
msg = cleanMessage(msg);
|
||||
const msgJson = JSON.stringify(msg, null, 4);
|
||||
|
||||
function openViewRawModal(json: string, type: string, msgContent?: string) {
|
||||
const key = openModal(props => (
|
||||
<ErrorBoundary>
|
||||
<ModalRoot {...props} size={ModalSize.LARGE}>
|
||||
@ -80,26 +79,28 @@ function openViewRawModal(msg: Message) {
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div style={{ padding: "16px 0" }}>
|
||||
{!!msg.content && (
|
||||
{!!msgContent && (
|
||||
<>
|
||||
<Forms.FormTitle tag="h5">Content</Forms.FormTitle>
|
||||
<CodeBlock content={msg.content} lang="" />
|
||||
<CodeBlock content={msgContent} lang="" />
|
||||
<Forms.FormDivider className={Margins.bottom20} />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Forms.FormTitle tag="h5">Message Data</Forms.FormTitle>
|
||||
<CodeBlock content={msgJson} lang="json" />
|
||||
<Forms.FormTitle tag="h5">{type} Data</Forms.FormTitle>
|
||||
<CodeBlock content={json} lang="json" />
|
||||
</div>
|
||||
</ModalContent >
|
||||
<ModalFooter>
|
||||
<Flex cellSpacing={10}>
|
||||
<Button onClick={() => copyWithToast(msgJson, "Message data copied to clipboard!")}>
|
||||
Copy Message JSON
|
||||
<Button onClick={() => copyWithToast(json, `${type} data copied to clipboard!`)}>
|
||||
Copy {type} JSON
|
||||
</Button>
|
||||
<Button onClick={() => copyWithToast(msg.content, "Content copied to clipboard!")}>
|
||||
{!!msgContent && (
|
||||
<Button onClick={() => copyWithToast(msgContent, "Content copied to clipboard!")}>
|
||||
Copy Raw Content
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot >
|
||||
@ -107,6 +108,13 @@ function openViewRawModal(msg: Message) {
|
||||
));
|
||||
}
|
||||
|
||||
function openViewRawModalMessage(msg: Message) {
|
||||
msg = cleanMessage(msg);
|
||||
const msgJson = JSON.stringify(msg, null, 4);
|
||||
|
||||
return openViewRawModal(msgJson, "Message", msg.content);
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
clickMethod: {
|
||||
description: "Change the button to view the raw content/data of any message.",
|
||||
@ -118,10 +126,33 @@ const settings = definePluginSettings({
|
||||
}
|
||||
});
|
||||
|
||||
function MakeContextCallback(name: string) {
|
||||
const callback: NavContextMenuPatchCallback = (children, props) => () => {
|
||||
const lastChild = children.at(-1);
|
||||
if (lastChild?.key === "developer-actions") {
|
||||
const p = lastChild.props;
|
||||
if (!Array.isArray(p.children))
|
||||
p.children = [p.children];
|
||||
({ children } = p);
|
||||
}
|
||||
|
||||
children.splice(-1, 0,
|
||||
<Menu.MenuItem
|
||||
id={`vc-view-${name.toLowerCase()}-raw`}
|
||||
label="View Raw"
|
||||
action={() => openViewRawModal(JSON.stringify(props[name.toLowerCase()], null, 4), name)}
|
||||
icon={CopyIcon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
return callback;
|
||||
}
|
||||
|
||||
|
||||
export default definePlugin({
|
||||
name: "ViewRaw",
|
||||
description: "Copy and view the raw content/data of any message.",
|
||||
authors: [Devs.KingFish, Devs.Ven, Devs.rad],
|
||||
description: "Copy and view the raw content/data of any message, channel or guild",
|
||||
authors: [Devs.KingFish, Devs.Ven, Devs.rad, Devs.ImLvna],
|
||||
dependencies: ["MessagePopoverAPI"],
|
||||
settings,
|
||||
|
||||
@ -131,7 +162,7 @@ export default definePlugin({
|
||||
if (settings.store.clickMethod === "Right") {
|
||||
copyWithToast(msg.content);
|
||||
} else {
|
||||
openViewRawModal(msg);
|
||||
openViewRawModalMessage(msg);
|
||||
}
|
||||
};
|
||||
|
||||
@ -143,7 +174,7 @@ export default definePlugin({
|
||||
} else {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
openViewRawModal(msg);
|
||||
openViewRawModalMessage(msg);
|
||||
}
|
||||
};
|
||||
|
||||
@ -160,9 +191,16 @@ export default definePlugin({
|
||||
onContextMenu: handleContextMenu
|
||||
};
|
||||
});
|
||||
|
||||
addContextMenuPatch("guild-context", MakeContextCallback("Guild"));
|
||||
addContextMenuPatch("channel-context", MakeContextCallback("Channel"));
|
||||
addContextMenuPatch("user-context", MakeContextCallback("User"));
|
||||
},
|
||||
|
||||
stop() {
|
||||
removeButton("CopyRawMessage");
|
||||
removeContextMenuPatch("guild-context", MakeContextCallback("Guild"));
|
||||
removeContextMenuPatch("channel-context", MakeContextCallback("Channel"));
|
||||
removeContextMenuPatch("user-context", MakeContextCallback("User"));
|
||||
}
|
||||
});
|
||||
|
@ -39,8 +39,8 @@ const settings = definePluginSettings({
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Add back the Discord context menus for images, links and the chat input bar",
|
||||
// Web slate menu has proper spellcheck suggestions and image context menu is also pretty good,
|
||||
// so disable this by default. Vencord Desktop just doesn't, so enable by default
|
||||
default: IS_VENCORD_DESKTOP,
|
||||
// so disable this by default. Vesktop just doesn't, so enable by default
|
||||
default: IS_VESKTOP,
|
||||
restartNeeded: true
|
||||
}
|
||||
});
|
||||
@ -50,7 +50,7 @@ export default definePlugin({
|
||||
description: "Re-adds context menus missing in the web version of Discord: Links & Images (Copy/Open Link/Image), Text Area (Copy, Cut, Paste, SpellCheck)",
|
||||
authors: [Devs.Ven],
|
||||
enabledByDefault: true,
|
||||
required: IS_VENCORD_DESKTOP,
|
||||
required: IS_VESKTOP,
|
||||
|
||||
settings,
|
||||
|
||||
@ -86,7 +86,7 @@ export default definePlugin({
|
||||
},
|
||||
// Fix silly Discord calling the non web support copy
|
||||
{
|
||||
match: /\w\.default\.copy/,
|
||||
match: /\i\.\i\.copy/,
|
||||
replace: "Vencord.Webpack.Common.Clipboard.copy"
|
||||
}
|
||||
]
|
||||
@ -102,7 +102,7 @@ export default definePlugin({
|
||||
replace: "if(null=="
|
||||
},
|
||||
{
|
||||
match: /return\s*?\[\i\.default\.canCopyImage\(\)/,
|
||||
match: /return\s*?\[\i\.\i\.canCopyImage\(\)/,
|
||||
replace: "return [true"
|
||||
},
|
||||
{
|
||||
@ -158,7 +158,7 @@ export default definePlugin({
|
||||
},
|
||||
{
|
||||
// Change calls to DiscordNative.clipboard to us instead
|
||||
match: /\b\i\.default\.(copy|cut|paste)/g,
|
||||
match: /\b\i\.\i\.(copy|cut|paste)/g,
|
||||
replace: "$self.$1"
|
||||
}
|
||||
]
|
||||
|
@ -28,7 +28,7 @@ contextBridge.exposeInMainWorld("VencordNative", VencordNative);
|
||||
// Discord
|
||||
if (location.protocol !== "data:") {
|
||||
// #region cssInsert
|
||||
const rendererCss = join(__dirname, "renderer.css");
|
||||
const rendererCss = join(__dirname, IS_VESKTOP ? "vencordDesktopRenderer.css" : "renderer.css");
|
||||
|
||||
const style = document.createElement("style");
|
||||
style.id = "vencord-css-core";
|
||||
@ -51,9 +51,9 @@ if (location.protocol !== "data:") {
|
||||
}
|
||||
// #endregion
|
||||
|
||||
if (process.env.DISCORD_PRELOAD) {
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
webFrame.executeJavaScript(readFileSync(join(__dirname, "renderer.js"), "utf-8"));
|
||||
require(process.env.DISCORD_PRELOAD);
|
||||
require(process.env.DISCORD_PRELOAD!);
|
||||
}
|
||||
} // Monaco popout
|
||||
else {
|
||||
|
@ -331,10 +331,38 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||
name: "rad",
|
||||
id: 610945092504780823n
|
||||
},
|
||||
AndrewDLO: {
|
||||
name: "Andrew-DLO",
|
||||
id: 434135504792059917n
|
||||
},
|
||||
HypedDomi: {
|
||||
name: "HypedDomi",
|
||||
id: 354191516979429376n
|
||||
}
|
||||
},
|
||||
Rini: {
|
||||
name: "Rini",
|
||||
id: 1079479184478441643n
|
||||
},
|
||||
castdrian: {
|
||||
name: "castdrian",
|
||||
id: 224617799434108928n
|
||||
},
|
||||
Arrow: {
|
||||
name: "arrow",
|
||||
id: 958158495302176778n
|
||||
},
|
||||
bb010g: {
|
||||
name: "bb010g",
|
||||
id: 72791153467990016n,
|
||||
},
|
||||
Dolfies: {
|
||||
name: "Dolfies",
|
||||
id: 852892297661906993n,
|
||||
},
|
||||
RuukuLada: {
|
||||
name: "RuukuLada",
|
||||
id: 119705748346241027n,
|
||||
},
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
// iife so #__PURE__ works correctly
|
||||
|
@ -79,5 +79,9 @@ const shikiWorkerDist = "https://unpkg.com/@vap/shiki-worker@0.0.8/dist";
|
||||
export const shikiWorkerSrc = `${shikiWorkerDist}/${IS_DEV ? "index.js" : "index.min.js"}`;
|
||||
export const shikiOnigasmSrc = "https://unpkg.com/@vap/shiki@0.10.3/dist/onig.wasm";
|
||||
|
||||
export const rnnoiseDist = "https://unpkg.com/@sapphi-red/web-noise-suppressor@0.3.3/dist";
|
||||
export const rnnoiseWasmSrc = (simd = false) => `${rnnoiseDist}/rnnoise${simd ? "_simd" : ""}.wasm`;
|
||||
export const rnnoiseWorkletSrc = `${rnnoiseDist}/rnnoise/workletProcessor.js`;
|
||||
|
||||
// @ts-expect-error SHUT UP
|
||||
export const getStegCloak = makeLazy(() => import("https://unpkg.com/stegcloak-dist@1.0.0/index.js"));
|
||||
|
@ -74,6 +74,16 @@ export function isObject(obj: unknown): obj is object {
|
||||
return typeof obj === "object" && obj !== null && !Array.isArray(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an object is empty or in other words has no own properties
|
||||
*/
|
||||
export function isObjectEmpty(obj: object) {
|
||||
for (const k in obj)
|
||||
if (Object.hasOwn(obj, k)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if value is not a URL, otherwise return URL object.
|
||||
* Avoids having to wrap url checks in a try/catch
|
||||
|
@ -20,12 +20,12 @@ export function relaunch() {
|
||||
if (IS_DISCORD_DESKTOP)
|
||||
window.DiscordNative.app.relaunch();
|
||||
else
|
||||
window.VencordDesktopNative.app.relaunch();
|
||||
window.VesktopNative.app.relaunch();
|
||||
}
|
||||
|
||||
export function showItemInFolder(path: string) {
|
||||
if (IS_DISCORD_DESKTOP)
|
||||
window.DiscordNative.fileManager.showItemInFolder(path);
|
||||
else
|
||||
window.VencordDesktopNative.fileManager.showItemInFolder(path);
|
||||
window.VesktopNative.fileManager.showItemInFolder(path);
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ export async function update() {
|
||||
export const getRepo = () => Unwrap(VencordNative.updater.getRepo());
|
||||
|
||||
export async function maybePromptToUpdate(confirmMessage: string, checkForDev = false) {
|
||||
if (IS_WEB) return;
|
||||
if (IS_WEB || IS_UPDATER_DISABLED) return;
|
||||
if (checkForDev && IS_DEV) return;
|
||||
|
||||
try {
|
||||
|
@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
// eslint-disable-next-line path-alias/no-relative
|
||||
import { filters, mapMangledModuleLazy, waitFor } from "../webpack";
|
||||
import type * as t from "./types/menu";
|
||||
|
@ -27,6 +27,13 @@ export const Flux: t.Flux = findByPropsLazy("connectStores");
|
||||
|
||||
export type GenericStore = t.FluxStore & Record<string, any>;
|
||||
|
||||
export enum DraftType {
|
||||
ChannelMessage = 0,
|
||||
ThreadSettings = 1,
|
||||
FirstThreadMessage = 2,
|
||||
ApplicationLauncherCommand = 3
|
||||
}
|
||||
|
||||
export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & {
|
||||
getMessages(chanId: string): any;
|
||||
};
|
||||
@ -52,6 +59,7 @@ export let RelationshipStore: Stores.RelationshipStore & t.FluxStore & {
|
||||
|
||||
export let EmojiStore: t.EmojiStore;
|
||||
export let WindowStore: t.WindowStore;
|
||||
export let DraftStore: t.DraftStore;
|
||||
|
||||
export const MaskedLinkStore = mapMangledModuleLazy('"MaskedLinkStore"', {
|
||||
openUntrustedLink: filters.byCode(".apply(this,arguments)")
|
||||
@ -76,6 +84,7 @@ export const useStateFromStores: <T>(
|
||||
) => T
|
||||
= findByCodeLazy("useStateFromStores");
|
||||
|
||||
waitForStore("DraftStore", s => DraftStore = s);
|
||||
waitForStore("UserStore", s => UserStore = s);
|
||||
waitForStore("ChannelStore", m => ChannelStore = m);
|
||||
waitForStore("SelectedChannelStore", m => SelectedChannelStore = m);
|
||||
|
10
src/webpack/common/types/components.d.ts
vendored
10
src/webpack/common/types/components.d.ts
vendored
@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
import type { Moment } from "moment";
|
||||
import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react";
|
||||
import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, KeyboardEvent, MouseEvent, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react";
|
||||
|
||||
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
|
||||
export type FormTextTypes = Record<"DEFAULT" | "INPUT_PLACEHOLDER" | "DESCRIPTION" | "LABEL_BOLD" | "LABEL_SELECTED" | "LABEL_DESCRIPTOR" | "ERROR" | "SUCCESS", string>;
|
||||
@ -338,16 +338,16 @@ export type Popout = ComponentType<{
|
||||
thing: {
|
||||
"aria-controls": string;
|
||||
"aria-expanded": boolean;
|
||||
onClick(event: MouseEvent): void;
|
||||
onKeyDown(event: KeyboardEvent): void;
|
||||
onMouseDown(event: MouseEvent): void;
|
||||
onClick(event: MouseEvent<HTMLElement>): void;
|
||||
onKeyDown(event: KeyboardEvent<HTMLElement>): void;
|
||||
onMouseDown(event: MouseEvent<HTMLElement>): void;
|
||||
},
|
||||
data: {
|
||||
isShown: boolean;
|
||||
position: string;
|
||||
}
|
||||
): ReactNode;
|
||||
shouldShow: boolean;
|
||||
shouldShow?: boolean;
|
||||
renderPopout(args: {
|
||||
closePopout(): void;
|
||||
isPositioned: boolean;
|
||||
|
24
src/webpack/common/types/stores.d.ts
vendored
24
src/webpack/common/types/stores.d.ts
vendored
@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { DraftType } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
|
||||
import { FluxDispatcher, FluxEvents } from "./utils";
|
||||
@ -148,3 +149,26 @@ export class EmojiStore extends FluxStore {
|
||||
get favoriteEmojisWithoutFetchingLatest(): Emoji[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface DraftObject {
|
||||
channelId: string;
|
||||
timestamp: number;
|
||||
draft: string;
|
||||
}
|
||||
|
||||
interface DraftState {
|
||||
[userId: string]: {
|
||||
[channelId: string]: {
|
||||
[key in DraftType]?: Omit<DraftObject, "channelId">;
|
||||
} | undefined;
|
||||
} | undefined;
|
||||
}
|
||||
|
||||
|
||||
export class DraftStore extends FluxStore {
|
||||
getDraft(channelId: string, type: DraftType): string;
|
||||
getRecentlyEditedDrafts(type: DraftType): DraftObject[];
|
||||
getState(): DraftState;
|
||||
getThreadDraftWithParentMessageId?(arg: any): any;
|
||||
getThreadSettings(channelId: string): any | null;
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ export const UserUtils = {
|
||||
};
|
||||
|
||||
export const Clipboard = mapMangledModuleLazy('document.queryCommandEnabled("copy")||document.queryCommandSupported("copy")', {
|
||||
copy: filters.byCode(".default.copy("),
|
||||
copy: filters.byCode(".copy("),
|
||||
SUPPORTS_COPY: x => typeof x === "boolean",
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user