mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-02-25 07:48:32 +00:00
Merge branch 'main' of https://github.com/Vendicated/Vencord
This commit is contained in:
commit
c310d8c800
65 changed files with 1181 additions and 284 deletions
|
@ -84,7 +84,9 @@
|
||||||
"no-extra-semi": "error",
|
"no-extra-semi": "error",
|
||||||
"consistent-return": ["warn", { "treatUndefinedAsUnspecified": true }],
|
"consistent-return": ["warn", { "treatUndefinedAsUnspecified": true }],
|
||||||
"dot-notation": "error",
|
"dot-notation": "error",
|
||||||
"no-useless-escape": "error",
|
"no-useless-escape": ["error", {
|
||||||
|
"extra": "i"
|
||||||
|
}],
|
||||||
"no-fallthrough": "error",
|
"no-fallthrough": "error",
|
||||||
"for-direction": "error",
|
"for-direction": "error",
|
||||||
"no-async-promise-executor": "error",
|
"no-async-promise-executor": "error",
|
||||||
|
|
13
.github/FUNDING.yml
vendored
13
.github/FUNDING.yml
vendored
|
@ -1,13 +0,0 @@
|
||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: Vendicated
|
|
||||||
patreon: Aliucord
|
|
||||||
open_collective: # Replace with a single Open Collective username
|
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
||||||
liberapay: # Replace with a single Liberapay username
|
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
|
||||||
otechie: # Replace with a single Otechie username
|
|
||||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
|
||||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
21
.github/workflows/build.yml
vendored
21
.github/workflows/build.yml
vendored
|
@ -55,10 +55,29 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
echo "release_tag=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
echo "release_tag=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Upload Devbuild
|
- name: Upload Devbuild as release
|
||||||
run: |
|
run: |
|
||||||
gh release upload devbuild --clobber dist/*
|
gh release upload devbuild --clobber dist/*
|
||||||
gh release edit devbuild --title "DevBuild $RELEASE_TAG"
|
gh release edit devbuild --title "DevBuild $RELEASE_TAG"
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
RELEASE_TAG: ${{ env.release_tag }}
|
RELEASE_TAG: ${{ env.release_tag }}
|
||||||
|
|
||||||
|
- name: Upload Devbuild to builds repo
|
||||||
|
run: |
|
||||||
|
git config --global user.name "$USERNAME"
|
||||||
|
git config --global user.email actions@github.com
|
||||||
|
|
||||||
|
git clone https://$USERNAME:$API_TOKEN@github.com/$GH_REPO.git upload
|
||||||
|
cd upload
|
||||||
|
rm -rf *
|
||||||
|
cp -r ../dist/* .
|
||||||
|
|
||||||
|
git add -A
|
||||||
|
git commit -m "Builds for https://github.com/$GITHUB_REPOSITORY/commit/$GITHUB_SHA"
|
||||||
|
git push --force https://$USERNAME:$API_TOKEN@github.com/$GH_REPO.git
|
||||||
|
env:
|
||||||
|
API_TOKEN: ${{ secrets.BUILDS_TOKEN }}
|
||||||
|
GLOBIGNORE: .git:.gitignore:README.md:LICENSE
|
||||||
|
GH_REPO: Vencord/builds
|
||||||
|
USERNAME: GitHub-Actions
|
||||||
|
|
14
.github/workflows/reportBrokenPlugins.yml
vendored
14
.github/workflows/reportBrokenPlugins.yml
vendored
|
@ -41,3 +41,17 @@ jobs:
|
||||||
env:
|
env:
|
||||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||||
|
|
||||||
|
- name: Create Report (Canary)
|
||||||
|
timeout-minutes: 10
|
||||||
|
if: success() || failure() # even run if previous one failed
|
||||||
|
run: |
|
||||||
|
export PATH="$PWD/node_modules/.bin:$PATH"
|
||||||
|
export CHROMIUM_BIN=$(which chromium-browser)
|
||||||
|
export USE_CANARY=true
|
||||||
|
|
||||||
|
esbuild test/generateReport.ts > dist/report.mjs
|
||||||
|
node dist/report.mjs >> $GITHUB_STEP_SUMMARY
|
||||||
|
env:
|
||||||
|
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||||
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||||
|
|
|
@ -23,7 +23,7 @@ If you're a power user who wants to contribute and make plugins or just want to
|
||||||
|
|
||||||
Or install the browser extension for
|
Or install the browser extension for
|
||||||
- [](https://github.com/Vendicated/Vencord/releases/latest/download/Vencord-for-Chrome-and-Edge.zip)
|
- [](https://github.com/Vendicated/Vencord/releases/latest/download/Vencord-for-Chrome-and-Edge.zip)
|
||||||
- [UserScript](https://github.com/Vendicated/Vencord/releases/download/devbuild/Vencord.user.js) - Please note that QuickCSS, shiki and other plugins making use of external resources will not work with the UserScript.
|
- [UserScript](https://raw.githubusercontent.com/Vencord/builds/main/Vencord.user.js) - Please note that QuickCSS, shiki and other plugins making use of external resources will not work with the UserScript.
|
||||||
|
|
||||||
|
|
||||||
You may also build them from source, to do that do the same steps as in the manual regular install method,
|
You may also build them from source, to do that do the same steps as in the manual regular install method,
|
||||||
|
|
|
@ -2,7 +2,18 @@ if (typeof browser === "undefined") {
|
||||||
var browser = chrome;
|
var browser = chrome;
|
||||||
}
|
}
|
||||||
|
|
||||||
var script = document.createElement("script");
|
const script = document.createElement("script");
|
||||||
script.src = browser.runtime.getURL("dist/Vencord.js");
|
script.src = browser.runtime.getURL("dist/Vencord.js");
|
||||||
// documentElement because we load before body/head are ready
|
|
||||||
document.documentElement.appendChild(script);
|
const style = document.createElement("link");
|
||||||
|
style.type = "text/css";
|
||||||
|
style.rel = "stylesheet";
|
||||||
|
style.href = browser.runtime.getURL("dist/Vencord.css");
|
||||||
|
|
||||||
|
document.documentElement.append(script);
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
"DOMContentLoaded",
|
||||||
|
() => document.documentElement.append(style),
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
"js": ["content.js"]
|
"js": ["content.js"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"web_accessible_resources": ["dist/Vencord.js"],
|
"web_accessible_resources": ["dist/Vencord.js", "dist/Vencord.css"],
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": ["background.js"]
|
"scripts": ["background.js"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
{
|
{
|
||||||
"resources": ["dist/Vencord.js"],
|
"resources": ["dist/Vencord.js", "dist/Vencord.css"],
|
||||||
"matches": ["*://*.discord.com/*"]
|
"matches": ["*://*.discord.com/*"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
15
package.json
15
package.json
|
@ -34,6 +34,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/diff": "^5.0.2",
|
"@types/diff": "^5.0.2",
|
||||||
|
"@types/lodash": "^4.14.0",
|
||||||
"@types/node": "^18.11.9",
|
"@types/node": "^18.11.9",
|
||||||
"@types/react": "^18.0.25",
|
"@types/react": "^18.0.25",
|
||||||
"@types/react-dom": "^18.0.9",
|
"@types/react-dom": "^18.0.9",
|
||||||
|
@ -62,7 +63,19 @@
|
||||||
"packageManager": "pnpm@7.13.4",
|
"packageManager": "pnpm@7.13.4",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"eslint-plugin-path-alias@1.0.0": "patches/eslint-plugin-path-alias@1.0.0.patch"
|
"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"
|
||||||
|
},
|
||||||
|
"peerDependencyRules": {
|
||||||
|
"ignoreMissing": [
|
||||||
|
"eslint-plugin-import"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"allowedDeprecatedVersions": {
|
||||||
|
"source-map-resolve": "*",
|
||||||
|
"resolve-url": "*",
|
||||||
|
"source-map-url": "*",
|
||||||
|
"urix": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webExt": {
|
"webExt": {
|
||||||
|
|
45
patches/eslint@8.28.0.patch
Normal file
45
patches/eslint@8.28.0.patch
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
diff --git a/lib/rules/no-useless-escape.js b/lib/rules/no-useless-escape.js
|
||||||
|
index 2046a148a17fd1d5f3a4bbc9f45f7700259d11fa..f4898c6b57355a4fd72c43a9f32bf1a36a6ccf4a 100644
|
||||||
|
--- a/lib/rules/no-useless-escape.js
|
||||||
|
+++ b/lib/rules/no-useless-escape.js
|
||||||
|
@@ -97,12 +97,30 @@ module.exports = {
|
||||||
|
escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
|
||||||
|
},
|
||||||
|
|
||||||
|
- schema: []
|
||||||
|
+ schema: [{
|
||||||
|
+ type: "object",
|
||||||
|
+ properties: {
|
||||||
|
+ extra: {
|
||||||
|
+ type: "string",
|
||||||
|
+ default: ""
|
||||||
|
+ },
|
||||||
|
+ extraCharClass: {
|
||||||
|
+ type: "string",
|
||||||
|
+ default: ""
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
|
+ additionalProperties: false
|
||||||
|
+ }]
|
||||||
|
},
|
||||||
|
|
||||||
|
create(context) {
|
||||||
|
+ const options = context.options[0] || {};
|
||||||
|
+ const { extra, extraCharClass } = options || ''
|
||||||
|
const sourceCode = context.getSourceCode();
|
||||||
|
|
||||||
|
+ 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)
|
||||||
|
|
||||||
|
// 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));
|
32
pnpm-lock.yaml
generated
32
pnpm-lock.yaml
generated
|
@ -4,9 +4,13 @@ patchedDependencies:
|
||||||
eslint-plugin-path-alias@1.0.0:
|
eslint-plugin-path-alias@1.0.0:
|
||||||
hash: m6sma4g6bh67km3q6igf6uxaja
|
hash: m6sma4g6bh67km3q6igf6uxaja
|
||||||
path: patches/eslint-plugin-path-alias@1.0.0.patch
|
path: patches/eslint-plugin-path-alias@1.0.0.patch
|
||||||
|
eslint@8.28.0:
|
||||||
|
hash: 7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
|
path: patches/eslint@8.28.0.patch
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
'@types/diff': ^5.0.2
|
'@types/diff': ^5.0.2
|
||||||
|
'@types/lodash': ^4.14.0
|
||||||
'@types/node': ^18.11.9
|
'@types/node': ^18.11.9
|
||||||
'@types/react': ^18.0.25
|
'@types/react': ^18.0.25
|
||||||
'@types/react-dom': ^18.0.9
|
'@types/react-dom': ^18.0.9
|
||||||
|
@ -38,6 +42,7 @@ dependencies:
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/diff': 5.0.2
|
'@types/diff': 5.0.2
|
||||||
|
'@types/lodash': 4.14.189
|
||||||
'@types/node': 18.11.9
|
'@types/node': 18.11.9
|
||||||
'@types/react': 18.0.25
|
'@types/react': 18.0.25
|
||||||
'@types/react-dom': 18.0.9
|
'@types/react-dom': 18.0.9
|
||||||
|
@ -50,7 +55,7 @@ devDependencies:
|
||||||
diff: 5.1.0
|
diff: 5.1.0
|
||||||
discord-types: 1.3.26
|
discord-types: 1.3.26
|
||||||
esbuild: 0.15.16
|
esbuild: 0.15.16
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
eslint-import-resolver-alias: 1.1.2
|
eslint-import-resolver-alias: 1.1.2
|
||||||
eslint-plugin-header: 3.1.1_eslint@8.28.0
|
eslint-plugin-header: 3.1.1_eslint@8.28.0
|
||||||
eslint-plugin-path-alias: 1.0.0_m6sma4g6bh67km3q6igf6uxaja_eslint@8.28.0
|
eslint-plugin-path-alias: 1.0.0_m6sma4g6bh67km3q6igf6uxaja_eslint@8.28.0
|
||||||
|
@ -149,6 +154,10 @@ packages:
|
||||||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/lodash/4.14.189:
|
||||||
|
resolution: {integrity: sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/node/18.11.9:
|
/@types/node/18.11.9:
|
||||||
resolution: {integrity: sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==}
|
resolution: {integrity: sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -216,7 +225,7 @@ packages:
|
||||||
'@typescript-eslint/type-utils': 5.45.0_hsf322ms6xhhd4b5ne6lb74y4a
|
'@typescript-eslint/type-utils': 5.45.0_hsf322ms6xhhd4b5ne6lb74y4a
|
||||||
'@typescript-eslint/utils': 5.45.0_hsf322ms6xhhd4b5ne6lb74y4a
|
'@typescript-eslint/utils': 5.45.0_hsf322ms6xhhd4b5ne6lb74y4a
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
ignore: 5.2.0
|
ignore: 5.2.0
|
||||||
natural-compare-lite: 1.4.0
|
natural-compare-lite: 1.4.0
|
||||||
regexpp: 3.2.0
|
regexpp: 3.2.0
|
||||||
|
@ -241,7 +250,7 @@ packages:
|
||||||
'@typescript-eslint/types': 5.45.0
|
'@typescript-eslint/types': 5.45.0
|
||||||
'@typescript-eslint/typescript-estree': 5.45.0_typescript@4.9.3
|
'@typescript-eslint/typescript-estree': 5.45.0_typescript@4.9.3
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
typescript: 4.9.3
|
typescript: 4.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -268,7 +277,7 @@ packages:
|
||||||
'@typescript-eslint/typescript-estree': 5.45.0_typescript@4.9.3
|
'@typescript-eslint/typescript-estree': 5.45.0_typescript@4.9.3
|
||||||
'@typescript-eslint/utils': 5.45.0_hsf322ms6xhhd4b5ne6lb74y4a
|
'@typescript-eslint/utils': 5.45.0_hsf322ms6xhhd4b5ne6lb74y4a
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
tsutils: 3.21.0_typescript@4.9.3
|
tsutils: 3.21.0_typescript@4.9.3
|
||||||
typescript: 4.9.3
|
typescript: 4.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -312,7 +321,7 @@ packages:
|
||||||
'@typescript-eslint/scope-manager': 5.45.0
|
'@typescript-eslint/scope-manager': 5.45.0
|
||||||
'@typescript-eslint/types': 5.45.0
|
'@typescript-eslint/types': 5.45.0
|
||||||
'@typescript-eslint/typescript-estree': 5.45.0_typescript@4.9.3
|
'@typescript-eslint/typescript-estree': 5.45.0_typescript@4.9.3
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
eslint-scope: 5.1.1
|
eslint-scope: 5.1.1
|
||||||
eslint-utils: 3.0.0_eslint@8.28.0
|
eslint-utils: 3.0.0_eslint@8.28.0
|
||||||
semver: 7.3.7
|
semver: 7.3.7
|
||||||
|
@ -894,7 +903,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=7.7.0'
|
eslint: '>=7.7.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-path-alias/1.0.0_m6sma4g6bh67km3q6igf6uxaja_eslint@8.28.0:
|
/eslint-plugin-path-alias/1.0.0_m6sma4g6bh67km3q6igf6uxaja_eslint@8.28.0:
|
||||||
|
@ -902,7 +911,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^7
|
eslint: ^7
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
nanomatch: 1.2.13
|
nanomatch: 1.2.13
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -914,7 +923,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=5.0.0'
|
eslint: '>=5.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-unused-imports/2.0.0_5am2datodjm2qi4eijrjrnoz54:
|
/eslint-plugin-unused-imports/2.0.0_5am2datodjm2qi4eijrjrnoz54:
|
||||||
|
@ -928,7 +937,7 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/eslint-plugin': 5.45.0_czs5uoqkd3podpy6vgtsxfc7au
|
'@typescript-eslint/eslint-plugin': 5.45.0_czs5uoqkd3podpy6vgtsxfc7au
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
eslint-rule-composer: 0.3.0
|
eslint-rule-composer: 0.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -959,7 +968,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=5'
|
eslint: '>=5'
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.28.0
|
eslint: 8.28.0_7wc6icvgtg3uswirb5tpsbjnbe
|
||||||
eslint-visitor-keys: 2.1.0
|
eslint-visitor-keys: 2.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -973,7 +982,7 @@ packages:
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint/8.28.0:
|
/eslint/8.28.0_7wc6icvgtg3uswirb5tpsbjnbe:
|
||||||
resolution: {integrity: sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==}
|
resolution: {integrity: sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -1020,6 +1029,7 @@ packages:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
patched: true
|
||||||
|
|
||||||
/espree/9.4.0:
|
/espree/9.4.0:
|
||||||
resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==}
|
resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==}
|
||||||
|
|
66
scripts/build/buildWeb.mjs
Executable file → Normal file
66
scripts/build/buildWeb.mjs
Executable file → Normal file
|
@ -20,13 +20,13 @@
|
||||||
|
|
||||||
import esbuild from "esbuild";
|
import esbuild from "esbuild";
|
||||||
import { zip } from "fflate";
|
import { zip } from "fflate";
|
||||||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { readFile } from "fs/promises";
|
import { appendFile, mkdir, readFile, rm, writeFile } from "fs/promises";
|
||||||
import { join, resolve } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
// wtf is this assert syntax
|
// wtf is this assert syntax
|
||||||
import PackageJSON from "../../package.json" assert { type: "json" };
|
import PackageJSON from "../../package.json" assert { type: "json" };
|
||||||
import { commonOpts, fileIncludePlugin, gitHashPlugin, gitRemotePlugin, globPlugins, watch } from "./common.mjs";
|
import { commonOpts, globPlugins, watch } from "./common.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.BuildOptions}
|
* @type {esbuild.BuildOptions}
|
||||||
|
@ -39,9 +39,7 @@ const commonOptions = {
|
||||||
external: ["plugins", "git-hash"],
|
external: ["plugins", "git-hash"],
|
||||||
plugins: [
|
plugins: [
|
||||||
globPlugins,
|
globPlugins,
|
||||||
gitHashPlugin,
|
...commonOpts.plugins,
|
||||||
gitRemotePlugin,
|
|
||||||
fileIncludePlugin
|
|
||||||
],
|
],
|
||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
define: {
|
define: {
|
||||||
|
@ -77,9 +75,13 @@ await Promise.all(
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {(target: string, files: string[], shouldZip: boolean) => Promise<void>}
|
||||||
|
*/
|
||||||
async function buildPluginZip(target, files, shouldZip) {
|
async function buildPluginZip(target, files, shouldZip) {
|
||||||
const entries = {
|
const entries = {
|
||||||
"dist/Vencord.js": readFileSync("dist/browser.js"),
|
"dist/Vencord.js": await readFile("dist/browser.js"),
|
||||||
|
"dist/Vencord.css": await readFile("dist/browser.css"),
|
||||||
...Object.fromEntries(await Promise.all(files.map(async f => [
|
...Object.fromEntries(await Promise.all(files.map(async f => [
|
||||||
(f.startsWith("manifest") ? "manifest.json" : f),
|
(f.startsWith("manifest") ? "manifest.json" : f),
|
||||||
await readFile(join("browser", f))
|
await readFile(join("browser", f))
|
||||||
|
@ -87,29 +89,47 @@ async function buildPluginZip(target, files, shouldZip) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldZip) {
|
if (shouldZip) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
zip(entries, {}, (err, data) => {
|
zip(entries, {}, (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
reject(err);
|
||||||
process.exitCode = 1;
|
|
||||||
} else {
|
} else {
|
||||||
writeFileSync("dist/" + target, data);
|
const out = join("dist", target);
|
||||||
console.info("Extension written to dist/" + target);
|
writeFile(out, data).then(() => {
|
||||||
|
console.info("Extension written to " + out);
|
||||||
|
resolve();
|
||||||
|
}).catch(reject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
if (existsSync(target))
|
await rm(target, { recursive: true, force: true });
|
||||||
rmSync(target, { recursive: true });
|
await Promise.all(Object.entries(entries).map(async ([file, content]) => {
|
||||||
for (const entry in entries) {
|
const dest = join("dist", target, file);
|
||||||
const destination = "dist/" + target + "/" + entry;
|
const parentDirectory = join(dest, "..");
|
||||||
const parentDirectory = resolve(destination, "..");
|
await mkdir(parentDirectory, { recursive: true });
|
||||||
mkdirSync(parentDirectory, { recursive: true });
|
await writeFile(dest, content);
|
||||||
writeFileSync(destination, entries[entry]);
|
}));
|
||||||
}
|
|
||||||
console.info("Unpacked Extension written to dist/" + target);
|
console.info("Unpacked Extension written to dist/" + target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await buildPluginZip("extension-v3.zip", ["modifyResponseHeaders.json", "content.js", "manifestv3.json"], true);
|
const cssText = "`" + readFileSync("dist/Vencord.user.css", "utf-8").replaceAll("`", "\\`") + "`";
|
||||||
await buildPluginZip("extension-v2.zip", ["background.js", "content.js", "manifestv2.json"], true);
|
const cssRuntime = `
|
||||||
await buildPluginZip("extension-v2-unpacked", ["background.js", "content.js", "manifestv2.json"], false);
|
;document.addEventListener("DOMContentLoaded", () => document.documentElement.appendChild(
|
||||||
|
Object.assign(document.createElement("style"), {
|
||||||
|
textContent: ${cssText},
|
||||||
|
id: "vencord-css-core"
|
||||||
|
}),
|
||||||
|
{ once: true }
|
||||||
|
));
|
||||||
|
`;
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
appendFile("dist/Vencord.user.js", cssRuntime),
|
||||||
|
buildPluginZip("extension-v3.zip", ["modifyResponseHeaders.json", "content.js", "manifestv3.json"], true),
|
||||||
|
buildPluginZip("extension-v2.zip", ["background.js", "content.js", "manifestv2.json"], true),
|
||||||
|
buildPluginZip("extension-v2-unpacked", ["background.js", "content.js", "manifestv2.json"], false),
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { exec, execSync } from "child_process";
|
import { exec, execSync } from "child_process";
|
||||||
import { existsSync } from "fs";
|
import { existsSync, readFileSync } from "fs";
|
||||||
import { readdir, readFile } from "fs/promises";
|
import { readdir, readFile } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join, relative } from "path";
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
|
|
||||||
export const watch = process.argv.includes("--watch");
|
export const watch = process.argv.includes("--watch");
|
||||||
|
@ -35,7 +35,7 @@ export const banner = {
|
||||||
|
|
||||||
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294
|
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.Plugin}
|
* @type {import("esbuild").Plugin}
|
||||||
*/
|
*/
|
||||||
export const makeAllPackagesExternalPlugin = {
|
export const makeAllPackagesExternalPlugin = {
|
||||||
name: "make-all-packages-external",
|
name: "make-all-packages-external",
|
||||||
|
@ -46,7 +46,7 @@ export const makeAllPackagesExternalPlugin = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.Plugin}
|
* @type {import("esbuild").Plugin}
|
||||||
*/
|
*/
|
||||||
export const globPlugins = {
|
export const globPlugins = {
|
||||||
name: "glob-plugins",
|
name: "glob-plugins",
|
||||||
|
@ -87,7 +87,7 @@ export const globPlugins = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.Plugin}
|
* @type {import("esbuild").Plugin}
|
||||||
*/
|
*/
|
||||||
export const gitHashPlugin = {
|
export const gitHashPlugin = {
|
||||||
name: "git-hash-plugin",
|
name: "git-hash-plugin",
|
||||||
|
@ -103,7 +103,7 @@ export const gitHashPlugin = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.Plugin}
|
* @type {import("esbuild").Plugin}
|
||||||
*/
|
*/
|
||||||
export const gitRemotePlugin = {
|
export const gitRemotePlugin = {
|
||||||
name: "git-remote-plugin",
|
name: "git-remote-plugin",
|
||||||
|
@ -125,7 +125,7 @@ export const gitRemotePlugin = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.Plugin}
|
* @type {import("esbuild").Plugin}
|
||||||
*/
|
*/
|
||||||
export const fileIncludePlugin = {
|
export const fileIncludePlugin = {
|
||||||
name: "file-include-plugin",
|
name: "file-include-plugin",
|
||||||
|
@ -147,6 +147,31 @@ export const fileIncludePlugin = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styleModule = readFileSync("./scripts/build/module/style.js", "utf-8");
|
||||||
|
/**
|
||||||
|
* @type {import("esbuild").Plugin}
|
||||||
|
*/
|
||||||
|
export const stylePlugin = {
|
||||||
|
name: "style-plugin",
|
||||||
|
setup: ({ onResolve, onLoad }) => {
|
||||||
|
onResolve({ filter: /\.css\?managed$/, namespace: "file" }, ({ path, resolveDir }) => ({
|
||||||
|
path: relative(process.cwd(), join(resolveDir, path.replace("?managed", ""))),
|
||||||
|
namespace: "managed-style",
|
||||||
|
}));
|
||||||
|
onLoad({ filter: /\.css$/, namespace: "managed-style" }, async ({ path }) => {
|
||||||
|
const css = await readFile(path, "utf-8");
|
||||||
|
const name = relative(process.cwd(), path).replaceAll("\\", "/");
|
||||||
|
|
||||||
|
return {
|
||||||
|
loader: "js",
|
||||||
|
contents: styleModule
|
||||||
|
.replaceAll("STYLE_SOURCE", JSON.stringify(css))
|
||||||
|
.replaceAll("STYLE_NAME", JSON.stringify(name))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {import("esbuild").BuildOptions}
|
* @type {import("esbuild").BuildOptions}
|
||||||
*/
|
*/
|
||||||
|
@ -158,7 +183,7 @@ export const commonOpts = {
|
||||||
sourcemap: watch ? "inline" : "",
|
sourcemap: watch ? "inline" : "",
|
||||||
legalComments: "linked",
|
legalComments: "linked",
|
||||||
banner,
|
banner,
|
||||||
plugins: [fileIncludePlugin, gitHashPlugin, gitRemotePlugin],
|
plugins: [fileIncludePlugin, gitHashPlugin, gitRemotePlugin, stylePlugin],
|
||||||
external: ["~plugins", "~git-hash", "~git-remote"],
|
external: ["~plugins", "~git-hash", "~git-remote"],
|
||||||
inject: ["./scripts/build/inject/react.mjs"],
|
inject: ["./scripts/build/inject/react.mjs"],
|
||||||
jsxFactory: "VencordCreateElement",
|
jsxFactory: "VencordCreateElement",
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const VencordFragment = Symbol.for("react.fragment");
|
export const VencordFragment = /* #__PURE__*/ Symbol.for("react.fragment");
|
||||||
export let VencordCreateElement =
|
export let VencordCreateElement =
|
||||||
(...args) => (VencordCreateElement = Vencord.Webpack.Common.React.createElement)(...args);
|
(...args) => (VencordCreateElement = Vencord.Webpack.Common.React.createElement)(...args);
|
||||||
|
|
26
scripts/build/module/style.js
Normal file
26
scripts/build/module/style.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(window.VencordStyles ??= new Map()).set(STYLE_NAME, {
|
||||||
|
name: STYLE_NAME,
|
||||||
|
source: STYLE_SOURCE,
|
||||||
|
classNames: {},
|
||||||
|
dom: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default STYLE_NAME;
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
export * as Api from "./api";
|
export * as Api from "./api";
|
||||||
export * as Plugins from "./plugins";
|
export * as Plugins from "./plugins";
|
||||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
|
||||||
export * as Util from "./utils";
|
export * as Util from "./utils";
|
||||||
export * as QuickCss from "./utils/quickCss";
|
export * as QuickCss from "./utils/quickCss";
|
||||||
export * as Updater from "./utils/updater";
|
export * as Updater from "./utils/updater";
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
import { HTMLProps } from "react";
|
import { ComponentType, HTMLProps } from "react";
|
||||||
|
|
||||||
import Plugins from "~plugins";
|
import Plugins from "~plugins";
|
||||||
|
|
||||||
|
@ -27,20 +27,21 @@ export enum BadgePosition {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProfileBadge {
|
export interface ProfileBadge {
|
||||||
/** The tooltip to show on hover */
|
/** The tooltip to show on hover. Required for image badges */
|
||||||
tooltip: string;
|
tooltip?: string;
|
||||||
|
/** Custom component for the badge (tooltip not included) */
|
||||||
|
component?: ComponentType<ProfileBadge & BadgeUserArgs>;
|
||||||
/** The custom image to use */
|
/** The custom image to use */
|
||||||
image?: string;
|
image?: string;
|
||||||
/** Action to perform when you click the badge */
|
/** Action to perform when you click the badge */
|
||||||
onClick?(): void;
|
onClick?(): void;
|
||||||
/** Should the user display this badge? */
|
/** Should the user display this badge? */
|
||||||
shouldShow?(userInfo: BadgeUserArgs): boolean;
|
shouldShow?(userInfo: BadgeUserArgs): boolean;
|
||||||
/** Optional props (e.g. style) for the badge */
|
/** Optional props (e.g. style) for the badge, ignored for component badges */
|
||||||
props?: HTMLProps<HTMLImageElement>;
|
props?: HTMLProps<HTMLImageElement>;
|
||||||
/** Insert at start or end? */
|
/** Insert at start or end? */
|
||||||
position?: BadgePosition;
|
position?: BadgePosition;
|
||||||
|
/** The badge name to display, Discord uses this. Required for component badges */
|
||||||
/** The badge name to display. Discord uses this, but we don't. */
|
|
||||||
key?: string;
|
key?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +71,8 @@ export function inject(badgeArray: ProfileBadge[], args: BadgeUserArgs) {
|
||||||
for (const badge of Badges) {
|
for (const badge of Badges) {
|
||||||
if (!badge.shouldShow || badge.shouldShow(args)) {
|
if (!badge.shouldShow || badge.shouldShow(args)) {
|
||||||
badge.position === BadgePosition.START
|
badge.position === BadgePosition.START
|
||||||
? badgeArray.unshift(badge)
|
? badgeArray.unshift({ ...badge, ...args })
|
||||||
: badgeArray.push(badge);
|
: badgeArray.push({ ...badge, ...args });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Plugins.BadgeAPI as any).addDonorBadge(badgeArray, args.user.id);
|
(Plugins.BadgeAPI as any).addDonorBadge(badgeArray, args.user.id);
|
||||||
|
|
65
src/api/MemberListDecorators.ts
Normal file
65
src/api/MemberListDecorators.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Channel, User } from "discord-types/general/index.js";
|
||||||
|
|
||||||
|
interface DecoratorProps {
|
||||||
|
activities: any[];
|
||||||
|
canUseAvatarDecorations: boolean;
|
||||||
|
channel: Channel;
|
||||||
|
/**
|
||||||
|
* Only for DM members
|
||||||
|
*/
|
||||||
|
channelName?: string;
|
||||||
|
/**
|
||||||
|
* Only for server members
|
||||||
|
*/
|
||||||
|
currentUser?: User;
|
||||||
|
guildId?: string;
|
||||||
|
isMobile: boolean;
|
||||||
|
isOwner?: boolean;
|
||||||
|
isTyping: boolean;
|
||||||
|
selected: boolean;
|
||||||
|
status: string;
|
||||||
|
user: User;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
export type Decorator = (props: DecoratorProps) => JSX.Element | null;
|
||||||
|
type OnlyIn = "guilds" | "dms";
|
||||||
|
|
||||||
|
export const decorators = new Map<string, { decorator: Decorator, onlyIn?: OnlyIn; }>();
|
||||||
|
|
||||||
|
export function addDecorator(identifier: string, decorator: Decorator, onlyIn?: OnlyIn) {
|
||||||
|
decorators.set(identifier, { decorator, onlyIn });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeDecorator(identifier: string) {
|
||||||
|
decorators.delete(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function __addDecoratorsToList(props: DecoratorProps): (JSX.Element | null)[] {
|
||||||
|
const isInGuild = !!(props.guildId);
|
||||||
|
return [...decorators.values()].map(decoratorObj => {
|
||||||
|
const { decorator, onlyIn } = decoratorObj;
|
||||||
|
// this can most likely be done cleaner
|
||||||
|
if (!onlyIn || (onlyIn === "guilds" && isInGuild) || (onlyIn === "dms" && !isInGuild)) {
|
||||||
|
return decorator(props);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
63
src/api/MessageDecorations.ts
Normal file
63
src/api/MessageDecorations.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Channel, Message } from "discord-types/general/index.js";
|
||||||
|
|
||||||
|
interface DecorationProps {
|
||||||
|
author: {
|
||||||
|
/**
|
||||||
|
* Will be username if the user has no nickname
|
||||||
|
*/
|
||||||
|
nick: string;
|
||||||
|
iconRoleId: string;
|
||||||
|
guildMemberAvatar: string;
|
||||||
|
colorRoleName: string;
|
||||||
|
colorString: string;
|
||||||
|
};
|
||||||
|
channel: Channel;
|
||||||
|
compact: boolean;
|
||||||
|
decorations: {
|
||||||
|
/**
|
||||||
|
* Element for the [BOT] tag if there is one
|
||||||
|
*/
|
||||||
|
0: JSX.Element | null;
|
||||||
|
/**
|
||||||
|
* Other decorations (including ones added with this api)
|
||||||
|
*/
|
||||||
|
1: JSX.Element[];
|
||||||
|
};
|
||||||
|
message: Message;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
export type Decoration = (props: DecorationProps) => JSX.Element | null;
|
||||||
|
|
||||||
|
export const decorations = new Map<string, Decoration>();
|
||||||
|
|
||||||
|
export function addDecoration(identifier: string, decoration: Decoration) {
|
||||||
|
decorations.set(identifier, decoration);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeDecoration(identifier: string) {
|
||||||
|
decorations.delete(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function __addDecorationsToMessage(props: DecorationProps): (JSX.Element | null)[] {
|
||||||
|
return [...decorations.values()].map(decoration => {
|
||||||
|
return decoration(props);
|
||||||
|
});
|
||||||
|
}
|
162
src/api/Styles.ts
Normal file
162
src/api/Styles.ts
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { MapValue } from "type-fest/source/entry";
|
||||||
|
|
||||||
|
export type Style = MapValue<typeof VencordStyles>;
|
||||||
|
|
||||||
|
export const styleMap = window.VencordStyles ??= new Map();
|
||||||
|
|
||||||
|
export function requireStyle(name: string) {
|
||||||
|
const style = styleMap.get(name);
|
||||||
|
if (!style) throw new Error(`Style "${name}" does not exist`);
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A style's name can be obtained from importing a stylesheet with `?managed` at the end of the import
|
||||||
|
* @param name The name of the style
|
||||||
|
* @returns `false` if the style was already enabled, `true` otherwise
|
||||||
|
* @example
|
||||||
|
* import pluginStyle from "./plugin.css?managed";
|
||||||
|
*
|
||||||
|
* // Inside some plugin method like "start()" or "[option].onChange()"
|
||||||
|
* enableStyle(pluginStyle);
|
||||||
|
*/
|
||||||
|
export function enableStyle(name: string) {
|
||||||
|
const style = requireStyle(name);
|
||||||
|
|
||||||
|
if (style.dom?.isConnected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!style.dom) {
|
||||||
|
style.dom = document.createElement("style");
|
||||||
|
style.dom.dataset.vencordName = style.name;
|
||||||
|
}
|
||||||
|
compileStyle(style);
|
||||||
|
|
||||||
|
document.head.appendChild(style.dom);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name The name of the style
|
||||||
|
* @returns `false` if the style was already disabled, `true` otherwise
|
||||||
|
* @see {@link enableStyle} for info on getting the name of an imported style
|
||||||
|
*/
|
||||||
|
export function disableStyle(name: string) {
|
||||||
|
const style = requireStyle(name);
|
||||||
|
if (!style.dom?.isConnected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
style.dom.remove();
|
||||||
|
style.dom = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name The name of the style
|
||||||
|
* @returns `true` in most cases, may return `false` in some edge cases
|
||||||
|
* @see {@link enableStyle} for info on getting the name of an imported style
|
||||||
|
*/
|
||||||
|
export const toggleStyle = (name: string) => isStyleEnabled(name) ? disableStyle(name) : enableStyle(name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name The name of the style
|
||||||
|
* @returns Whether the style is enabled
|
||||||
|
* @see {@link enableStyle} for info on getting the name of an imported style
|
||||||
|
*/
|
||||||
|
export const isStyleEnabled = (name: string) => requireStyle(name).dom?.isConnected ?? false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the variables of a style
|
||||||
|
* ```ts
|
||||||
|
* // -- plugin.ts --
|
||||||
|
* import pluginStyle from "./plugin.css?managed";
|
||||||
|
* import { setStyleVars } from "@api/Styles";
|
||||||
|
* import { findByPropsLazy } from "@webpack";
|
||||||
|
* const classNames = findByPropsLazy("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... }
|
||||||
|
*
|
||||||
|
* // Inside some plugin method like "start()"
|
||||||
|
* setStyleClassNames(pluginStyle, classNames);
|
||||||
|
* enableStyle(pluginStyle);
|
||||||
|
* ```
|
||||||
|
* ```scss
|
||||||
|
* // -- plugin.css --
|
||||||
|
* .plugin-root [--thin]::-webkit-scrollbar { ... }
|
||||||
|
* ```
|
||||||
|
* ```scss
|
||||||
|
* // -- final stylesheet --
|
||||||
|
* .plugin-root .thin-31rlnD.scrollerBase-_bVAAt::-webkit-scrollbar { ... }
|
||||||
|
* ```
|
||||||
|
* @param name The name of the style
|
||||||
|
* @param classNames An object where the keys are the variable names and the values are the variable values
|
||||||
|
* @param recompile Whether to recompile the style after setting the variables, defaults to `true`
|
||||||
|
* @see {@link enableStyle} for info on getting the name of an imported style
|
||||||
|
*/
|
||||||
|
export const setStyleClassNames = (name: string, classNames: Record<string, string>, recompile = true) => {
|
||||||
|
const style = requireStyle(name);
|
||||||
|
style.classNames = classNames;
|
||||||
|
if (recompile && isStyleEnabled(style.name))
|
||||||
|
compileStyle(style);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the stylesheet after doing the following to the sourcecode:
|
||||||
|
* - Interpolate style classnames
|
||||||
|
* @param style **_Must_ be a style with a DOM element**
|
||||||
|
* @see {@link setStyleClassNames} for more info on style classnames
|
||||||
|
*/
|
||||||
|
export const compileStyle = (style: Style) => {
|
||||||
|
if (!style.dom) throw new Error("Style has no DOM element");
|
||||||
|
|
||||||
|
style.dom.textContent = style.source
|
||||||
|
.replace(/\[--(\w+)\]/g, (match, name) => {
|
||||||
|
const className = style.classNames[name];
|
||||||
|
return className ? classNameToSelector(className) : match;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name The classname
|
||||||
|
* @param prefix A prefix to add each class, defaults to `""`
|
||||||
|
* @return A css selector for the classname
|
||||||
|
* @example
|
||||||
|
* classNameToSelector("foo bar") // => ".foo.bar"
|
||||||
|
*/
|
||||||
|
export const classNameToSelector = (name: string, prefix = "") => name.split(" ").map(n => `.${prefix}${n}`).join("");
|
||||||
|
|
||||||
|
type ClassNameFactoryArg = string | string[] | Record<string, unknown>;
|
||||||
|
/**
|
||||||
|
* @param prefix The prefix to add to each class, defaults to `""`
|
||||||
|
* @returns A classname generator function
|
||||||
|
* @example
|
||||||
|
* const cl = classNameFactory("plugin-");
|
||||||
|
*
|
||||||
|
* cl("base", ["item", "editable"], { selected: null, disabled: true })
|
||||||
|
* // => "plugin-base plugin-item plugin-editable plugin-disabled"
|
||||||
|
*/
|
||||||
|
export const classNameFactory = (prefix: string = "") => (...args: ClassNameFactoryArg[]) => {
|
||||||
|
const classNames = new Set<string>();
|
||||||
|
for (const arg of args) {
|
||||||
|
if (typeof arg === "string") classNames.add(arg);
|
||||||
|
else if (Array.isArray(arg)) arg.forEach(name => classNames.add(name));
|
||||||
|
else if (typeof arg === "object") Object.entries(arg).forEach(([name, value]) => value && classNames.add(name));
|
||||||
|
}
|
||||||
|
return Array.from(classNames, name => prefix + name).join(" ");
|
||||||
|
};
|
|
@ -19,11 +19,14 @@
|
||||||
import * as $Badges from "./Badges";
|
import * as $Badges from "./Badges";
|
||||||
import * as $Commands from "./Commands";
|
import * as $Commands from "./Commands";
|
||||||
import * as $DataStore from "./DataStore";
|
import * as $DataStore from "./DataStore";
|
||||||
|
import * as $MemberListDecorators from "./MemberListDecorators";
|
||||||
import * as $MessageAccessories from "./MessageAccessories";
|
import * as $MessageAccessories from "./MessageAccessories";
|
||||||
|
import * as $MessageDecorations from "./MessageDecorations";
|
||||||
import * as $MessageEventsAPI from "./MessageEvents";
|
import * as $MessageEventsAPI from "./MessageEvents";
|
||||||
import * as $MessagePopover from "./MessagePopover";
|
import * as $MessagePopover from "./MessagePopover";
|
||||||
import * as $Notices from "./Notices";
|
import * as $Notices from "./Notices";
|
||||||
import * as $ServerList from "./ServerList";
|
import * as $ServerList from "./ServerList";
|
||||||
|
import * as $Styles from "./Styles";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An API allowing you to listen to Message Clicks or run your own logic
|
* An API allowing you to listen to Message Clicks or run your own logic
|
||||||
|
@ -31,16 +34,16 @@ import * as $ServerList from "./ServerList";
|
||||||
*
|
*
|
||||||
* If your plugin uses this, you must add MessageEventsAPI to its dependencies
|
* If your plugin uses this, you must add MessageEventsAPI to its dependencies
|
||||||
*/
|
*/
|
||||||
const MessageEvents = $MessageEventsAPI;
|
export const MessageEvents = $MessageEventsAPI;
|
||||||
/**
|
/**
|
||||||
* An API allowing you to create custom notices
|
* An API allowing you to create custom notices
|
||||||
* (snackbars on the top, like the Update prompt)
|
* (snackbars on the top, like the Update prompt)
|
||||||
*/
|
*/
|
||||||
const Notices = $Notices;
|
export const Notices = $Notices;
|
||||||
/**
|
/**
|
||||||
* An API allowing you to register custom commands
|
* An API allowing you to register custom commands
|
||||||
*/
|
*/
|
||||||
const Commands = $Commands;
|
export const Commands = $Commands;
|
||||||
/**
|
/**
|
||||||
* A wrapper around IndexedDB. This can store arbitrarily
|
* A wrapper around IndexedDB. This can store arbitrarily
|
||||||
* large data and supports a lot of datatypes (Blob, Map, ...).
|
* large data and supports a lot of datatypes (Blob, Map, ...).
|
||||||
|
@ -55,22 +58,33 @@ const Commands = $Commands;
|
||||||
* This is actually just idb-keyval, so if you're familiar with that, you're golden!
|
* This is actually just idb-keyval, so if you're familiar with that, you're golden!
|
||||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types}
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types}
|
||||||
*/
|
*/
|
||||||
const DataStore = $DataStore;
|
export const DataStore = $DataStore;
|
||||||
/**
|
/**
|
||||||
* An API allowing you to add custom components as message accessories
|
* An API allowing you to add custom components as message accessories
|
||||||
*/
|
*/
|
||||||
const MessageAccessories = $MessageAccessories;
|
export const MessageAccessories = $MessageAccessories;
|
||||||
/**
|
/**
|
||||||
* An API allowing you to add custom buttons in the message popover
|
* An API allowing you to add custom buttons in the message popover
|
||||||
*/
|
*/
|
||||||
const MessagePopover = $MessagePopover;
|
export const MessagePopover = $MessagePopover;
|
||||||
/**
|
/**
|
||||||
* An API allowing you to add badges to user profiles
|
* An API allowing you to add badges to user profiles
|
||||||
*/
|
*/
|
||||||
const Badges = $Badges;
|
export const Badges = $Badges;
|
||||||
/**
|
/**
|
||||||
* An API allowing you to add custom elements to the server list
|
* An API allowing you to add custom elements to the server list
|
||||||
*/
|
*/
|
||||||
const ServerList = $ServerList;
|
export const ServerList = $ServerList;
|
||||||
|
/**
|
||||||
export { Badges, Commands, DataStore, MessageAccessories, MessageEvents, MessagePopover, Notices, ServerList };
|
* An API allowing you to add components as message accessories
|
||||||
|
*/
|
||||||
|
export const MessageDecorations = $MessageDecorations;
|
||||||
|
/**
|
||||||
|
* An API allowing you to add components to member list users, in both DM's and servers
|
||||||
|
*/
|
||||||
|
export const MemberListDecorators = $MemberListDecorators;
|
||||||
|
/**
|
||||||
|
* An API allowing you to dynamically load styles
|
||||||
|
* a
|
||||||
|
*/
|
||||||
|
export const Styles = $Styles;
|
||||||
|
|
|
@ -29,7 +29,12 @@ const setCss = debounce((css: string) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function launchMonacoEditor() {
|
export async function launchMonacoEditor() {
|
||||||
const win = open("about:blank", void 0, "popup,width=1000,height=1000")!;
|
const features = `popup,width=${Math.min(window.innerWidth, 1000)},height=${Math.min(window.innerHeight, 1000)}`;
|
||||||
|
const win = open("about:blank", "VencordQuickCss", features);
|
||||||
|
if (!win) {
|
||||||
|
alert("Failed to open QuickCSS popup. Make sure to allow popups!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
win.setCss = setCss;
|
win.setCss = setCss;
|
||||||
win.getCurrentCss = () => VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS);
|
win.getCurrentCss = () => VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS);
|
||||||
|
@ -41,4 +46,6 @@ export async function launchMonacoEditor() {
|
||||||
: "vs-dark";
|
: "vs-dark";
|
||||||
|
|
||||||
win.document.write(monacoHtml);
|
win.document.write(monacoHtml);
|
||||||
|
|
||||||
|
window.__VENCORD_MONACO_WIN__ = new WeakRef(win);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
import { makeCodeblock } from "@utils/misc";
|
import { makeCodeblock } from "@utils/misc";
|
||||||
|
import { canonicalizeMatch, canonicalizeReplace, ReplaceFn } from "@utils/patches";
|
||||||
import { search } from "@webpack";
|
import { search } from "@webpack";
|
||||||
import { Button, Clipboard, Forms, Margins, Parser, React, Switch, Text, TextInput } from "@webpack/common";
|
import { Button, Clipboard, Forms, Margins, Parser, React, Switch, Text, TextInput } from "@webpack/common";
|
||||||
|
|
||||||
|
@ -41,20 +42,29 @@ const findCandidates = debounce(function ({ find, setModule, setError }) {
|
||||||
setModule([keys[0], candidates[keys[0]]]);
|
setModule([keys[0], candidates[keys[0]]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
function ReplacementComponent({ module, match, replacement, setReplacementError }) {
|
interface ReplacementComponentProps {
|
||||||
|
module: [id: number, factory: Function];
|
||||||
|
match: string | RegExp;
|
||||||
|
replacement: string | ReplaceFn;
|
||||||
|
setReplacementError(error: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReplacementComponent({ module, match, replacement, setReplacementError }: ReplacementComponentProps) {
|
||||||
const [id, fact] = module;
|
const [id, fact] = module;
|
||||||
const [compileResult, setCompileResult] = React.useState<[boolean, string]>();
|
const [compileResult, setCompileResult] = React.useState<[boolean, string]>();
|
||||||
|
|
||||||
const [patchedCode, matchResult, diff] = React.useMemo(() => {
|
const [patchedCode, matchResult, diff] = React.useMemo(() => {
|
||||||
const src: string = fact.toString().replaceAll("\n", "");
|
const src: string = fact.toString().replaceAll("\n", "");
|
||||||
|
const canonicalMatch = canonicalizeMatch(match);
|
||||||
try {
|
try {
|
||||||
var patched = src.replace(match, replacement);
|
const canonicalReplace = canonicalizeReplace(replacement, "YourPlugin");
|
||||||
|
var patched = src.replace(canonicalMatch, canonicalReplace as string);
|
||||||
setReplacementError(void 0);
|
setReplacementError(void 0);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setReplacementError((e as Error).message);
|
setReplacementError((e as Error).message);
|
||||||
return ["", [], []];
|
return ["", [], []];
|
||||||
}
|
}
|
||||||
const m = src.match(match);
|
const m = src.match(canonicalMatch);
|
||||||
return [patched, m, makeDiff(src, patched, m)];
|
return [patched, m, makeDiff(src, patched, m)];
|
||||||
}, [id, match, replacement]);
|
}, [id, match, replacement]);
|
||||||
|
|
||||||
|
@ -179,9 +189,10 @@ function ReplacementInput({ replacement, setReplacement, replacementError }) {
|
||||||
{Object.entries({
|
{Object.entries({
|
||||||
"$$": "Insert a $",
|
"$$": "Insert a $",
|
||||||
"$&": "Insert the entire match",
|
"$&": "Insert the entire match",
|
||||||
"$`": "Insert the substring before the match",
|
"$`\u200b": "Insert the substring before the match",
|
||||||
"$'": "Insert the substring after the match",
|
"$'": "Insert the substring after the match",
|
||||||
"$n": "Insert the nth capturing group ($1, $2...)"
|
"$n": "Insert the nth capturing group ($1, $2...)",
|
||||||
|
"$self": "Insert the plugin instance",
|
||||||
}).map(([placeholder, desc]) => (
|
}).map(([placeholder, desc]) => (
|
||||||
<Forms.FormText key={placeholder}>
|
<Forms.FormText key={placeholder}>
|
||||||
{Parser.parse("`" + placeholder + "`")}: {desc}
|
{Parser.parse("`" + placeholder + "`")}: {desc}
|
||||||
|
@ -206,7 +217,7 @@ function ReplacementInput({ replacement, setReplacement, replacementError }) {
|
||||||
function PatchHelper() {
|
function PatchHelper() {
|
||||||
const [find, setFind] = React.useState<string>("");
|
const [find, setFind] = React.useState<string>("");
|
||||||
const [match, setMatch] = React.useState<string>("");
|
const [match, setMatch] = React.useState<string>("");
|
||||||
const [replacement, setReplacement] = React.useState<string | Function>("");
|
const [replacement, setReplacement] = React.useState<string | ReplaceFn>("");
|
||||||
|
|
||||||
const [replacementError, setReplacementError] = React.useState<string>();
|
const [replacementError, setReplacementError] = React.useState<string>();
|
||||||
|
|
||||||
|
|
30
src/components/PluginSettings/components/BadgeComponent.tsx
Normal file
30
src/components/PluginSettings/components/BadgeComponent.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BadgeStyle } from "@components/PluginSettings/styles";
|
||||||
|
|
||||||
|
export function Badge({ text, color }): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
backgroundColor: color,
|
||||||
|
justifySelf: "flex-end",
|
||||||
|
marginLeft: "auto",
|
||||||
|
...BadgeStyle
|
||||||
|
}}>{text}</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ export interface ISettingElementProps<T extends PluginOptionBase> {
|
||||||
onError(hasError: boolean): void;
|
onError(hasError: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export * from "./BadgeComponent";
|
||||||
export * from "./SettingBooleanComponent";
|
export * from "./SettingBooleanComponent";
|
||||||
export * from "./SettingCustomComponent";
|
export * from "./SettingCustomComponent";
|
||||||
export * from "./SettingNumericComponent";
|
export * from "./SettingNumericComponent";
|
||||||
|
|
|
@ -16,15 +16,19 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as DataStore from "@api/DataStore";
|
||||||
import { showNotice } from "@api/Notices";
|
import { showNotice } from "@api/Notices";
|
||||||
import { Settings, useSettings } from "@api/settings";
|
import { Settings, useSettings } from "@api/settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { handleComponentFailed } from "@components/handleComponentFailed";
|
import { handleComponentFailed } from "@components/handleComponentFailed";
|
||||||
|
import { Badge } from "@components/PluginSettings/components";
|
||||||
|
import PluginModal from "@components/PluginSettings/PluginModal";
|
||||||
|
import * as styles from "@components/PluginSettings/styles";
|
||||||
import { ChangeList } from "@utils/ChangeList";
|
import { ChangeList } from "@utils/ChangeList";
|
||||||
import Logger from "@utils/Logger";
|
import Logger from "@utils/Logger";
|
||||||
import { classes, LazyComponent } from "@utils/misc";
|
import { classes, LazyComponent, useAwaiter } from "@utils/misc";
|
||||||
import { openModalLazy } from "@utils/modal";
|
import { openModalLazy } from "@utils/modal";
|
||||||
import { Plugin } from "@utils/types";
|
import { Plugin } from "@utils/types";
|
||||||
import { findByCode, findByPropsLazy } from "@webpack";
|
import { findByCode, findByPropsLazy } from "@webpack";
|
||||||
|
@ -33,8 +37,6 @@ import { Alerts, Button, Forms, Margins, Parser, React, Select, Switch, Text, Te
|
||||||
import Plugins from "~plugins";
|
import Plugins from "~plugins";
|
||||||
|
|
||||||
import { startDependenciesRecursive, startPlugin, stopPlugin } from "../../plugins";
|
import { startDependenciesRecursive, startPlugin, stopPlugin } from "../../plugins";
|
||||||
import PluginModal from "./PluginModal";
|
|
||||||
import * as styles from "./styles";
|
|
||||||
|
|
||||||
const logger = new Logger("PluginSettings", "#a6d189");
|
const logger = new Logger("PluginSettings", "#a6d189");
|
||||||
|
|
||||||
|
@ -78,9 +80,10 @@ interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
|
||||||
plugin: Plugin;
|
plugin: Plugin;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
onRestartNeeded(name: string): void;
|
onRestartNeeded(name: string): void;
|
||||||
|
isNew?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave }: PluginCardProps) {
|
function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
const pluginSettings = settings.plugins[plugin.name];
|
const pluginSettings = settings.plugins[plugin.name];
|
||||||
|
|
||||||
|
@ -162,8 +165,15 @@ function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLe
|
||||||
</Text>}
|
</Text>}
|
||||||
hideBorder={true}
|
hideBorder={true}
|
||||||
>
|
>
|
||||||
<Flex style={{ marginTop: "auto", width: "100%", height: "100%", alignItems: "center" }}>
|
<Flex style={{ marginTop: "auto", width: "100%", height: "100%", alignItems: "center", gap: "8px" }}>
|
||||||
<Text variant="text-md/bold" style={{ flexGrow: "1" }}>{plugin.name}</Text>
|
<Text
|
||||||
|
variant="text-md/bold"
|
||||||
|
style={{
|
||||||
|
display: "flex", width: "100%", alignItems: "center", flexGrow: "1", gap: "8px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{plugin.name}{(isNew) && <Badge text="NEW" color="#ED4245" />}
|
||||||
|
</Text>
|
||||||
<button role="switch" onClick={() => openModal()} style={styles.SettingsIcon} className="button-12Fmur">
|
<button role="switch" onClick={() => openModal()} style={styles.SettingsIcon} className="button-12Fmur">
|
||||||
{plugin.options
|
{plugin.options
|
||||||
? <CogWheel
|
? <CogWheel
|
||||||
|
@ -243,6 +253,23 @@ export default ErrorBoundary.wrap(function Settings() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [newPlugins] = useAwaiter(() => DataStore.get("Vencord_existingPlugins").then((cachedPlugins: Record<string, number> | undefined) => {
|
||||||
|
const now = Date.now() / 1000;
|
||||||
|
const existingTimestamps: Record<string, number> = {};
|
||||||
|
const sortedPluginNames = Object.values(sortedPlugins).map(plugin => plugin.name);
|
||||||
|
|
||||||
|
const newPlugins: string[] = [];
|
||||||
|
for (const { name: p } of sortedPlugins) {
|
||||||
|
const time = existingTimestamps[p] = cachedPlugins?.[p] ?? now;
|
||||||
|
if ((time + 60 * 60 * 24 * 2) > now) {
|
||||||
|
newPlugins.push(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataStore.set("Vencord_existingPlugins", existingTimestamps);
|
||||||
|
|
||||||
|
return window._.isEqual(newPlugins, sortedPluginNames) ? [] : newPlugins;
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Forms.FormSection>
|
<Forms.FormSection>
|
||||||
<Forms.FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}>
|
<Forms.FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}>
|
||||||
|
@ -281,6 +308,7 @@ export default ErrorBoundary.wrap(function Settings() {
|
||||||
onRestartNeeded={name => changes.add(name)}
|
onRestartNeeded={name => changes.add(name)}
|
||||||
disabled={plugin.required || !!dependency}
|
disabled={plugin.required || !!dependency}
|
||||||
plugin={plugin}
|
plugin={plugin}
|
||||||
|
isNew={newPlugins?.includes(plugin.name)}
|
||||||
key={plugin.name}
|
key={plugin.name}
|
||||||
/>;
|
/>;
|
||||||
})
|
})
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const PluginsGridItem: React.CSSProperties = {
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
display: "block",
|
display: "block",
|
||||||
height: "min-content",
|
height: "100%",
|
||||||
padding: 10,
|
padding: 10,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
};
|
};
|
||||||
|
@ -48,3 +48,14 @@ export const SettingsIcon: React.CSSProperties = {
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
marginRight: 8
|
marginRight: 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const BadgeStyle: React.CSSProperties = {
|
||||||
|
padding: "0 6px",
|
||||||
|
fontFamily: "var(--font-display)",
|
||||||
|
fontWeight: "500",
|
||||||
|
borderRadius: "8px",
|
||||||
|
height: "16px",
|
||||||
|
fontSize: "12px",
|
||||||
|
lineHeight: "16px",
|
||||||
|
color: "var(--white-500)",
|
||||||
|
};
|
||||||
|
|
|
@ -16,22 +16,18 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import "./settingsStyles.css";
|
||||||
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { findByCodeLazy } from "@webpack";
|
import { findByCodeLazy } from "@webpack";
|
||||||
import { Forms, Router, Text } from "@webpack/common";
|
import { Forms, Router, Text } from "@webpack/common";
|
||||||
|
|
||||||
import cssText from "~fileContent/settingsStyles.css";
|
|
||||||
|
|
||||||
import BackupRestoreTab from "./BackupRestoreTab";
|
import BackupRestoreTab from "./BackupRestoreTab";
|
||||||
import PluginsTab from "./PluginsTab";
|
import PluginsTab from "./PluginsTab";
|
||||||
import ThemesTab from "./ThemesTab";
|
import ThemesTab from "./ThemesTab";
|
||||||
import Updater from "./Updater";
|
import Updater from "./Updater";
|
||||||
import VencordSettings from "./VencordTab";
|
import VencordSettings from "./VencordTab";
|
||||||
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.textContent = cssText;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
|
|
||||||
const st = (style: string) => `vcSettings${style}`;
|
const st = (style: string) => `vcSettings${style}`;
|
||||||
|
|
||||||
const TabBar = findByCodeLazy('[role="tab"][aria-disabled="false"]');
|
const TabBar = findByCodeLazy('[role="tab"][aria-disabled="false"]');
|
||||||
|
|
8
src/globals.d.ts
vendored
8
src/globals.d.ts
vendored
|
@ -16,6 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { LoDashStatic } from "lodash";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
/**
|
/**
|
||||||
|
@ -37,6 +38,12 @@ declare global {
|
||||||
|
|
||||||
export var VencordNative: typeof import("./VencordNative").default;
|
export var VencordNative: typeof import("./VencordNative").default;
|
||||||
export var Vencord: typeof import("./Vencord");
|
export var Vencord: typeof import("./Vencord");
|
||||||
|
export var VencordStyles: Map<string, {
|
||||||
|
name: string;
|
||||||
|
source: string;
|
||||||
|
classNames: Record<string, string>;
|
||||||
|
dom: HTMLStyleElement | null;
|
||||||
|
}>;
|
||||||
export var appSettings: {
|
export var appSettings: {
|
||||||
set(setting: string, v: any): void;
|
set(setting: string, v: any): void;
|
||||||
};
|
};
|
||||||
|
@ -54,6 +61,7 @@ declare global {
|
||||||
push(chunk: any): any;
|
push(chunk: any): any;
|
||||||
pop(): any;
|
pop(): any;
|
||||||
};
|
};
|
||||||
|
_: LoDashStatic;
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import "./legacy";
|
||||||
import "./updater";
|
import "./updater";
|
||||||
|
|
||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
|
|
31
src/ipcMain/legacy.ts
Normal file
31
src/ipcMain/legacy.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import IpcEvents from "@utils/IpcEvents";
|
||||||
|
import { ipcMain } from "electron";
|
||||||
|
import { writeFile } from "fs/promises";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
|
import { get } from "./simpleGet";
|
||||||
|
|
||||||
|
ipcMain.handleOnce(IpcEvents.DOWNLOAD_VENCORD_CSS, async () => {
|
||||||
|
const buf = await get("https://github.com/Vendicated/Vencord/releases/download/devbuild/renderer.css");
|
||||||
|
await writeFile(join(__dirname, "renderer.css"), buf);
|
||||||
|
return buf.toString("utf-8");
|
||||||
|
});
|
||||||
|
|
|
@ -24,7 +24,7 @@ export async function calculateHashes() {
|
||||||
const hashes = {} as Record<string, string>;
|
const hashes = {} as Record<string, string>;
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
["patcher.js", "preload.js", "renderer.js"].map(file => new Promise<void>(r => {
|
["patcher.js", "preload.js", "renderer.js", "renderer.css"].map(file => new Promise<void>(r => {
|
||||||
const fis = createReadStream(join(__dirname, file));
|
const fis = createReadStream(join(__dirname, file));
|
||||||
const hash = createHash("sha1", { encoding: "hex" });
|
const hash = createHash("sha1", { encoding: "hex" });
|
||||||
fis.once("end", () => {
|
fis.once("end", () => {
|
||||||
|
|
|
@ -69,7 +69,7 @@ async function fetchUpdates() {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
data.assets.forEach(({ name, browser_download_url }) => {
|
data.assets.forEach(({ name, browser_download_url }) => {
|
||||||
if (["patcher.js", "preload.js", "renderer.js"].some(s => name.startsWith(s))) {
|
if (["patcher.js", "preload.js", "renderer.js", "renderer.css"].some(s => name.startsWith(s))) {
|
||||||
PendingUpdates.push([name, browser_download_url]);
|
PendingUpdates.push([name, browser_download_url]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
6
src/modules.d.ts
vendored
6
src/modules.d.ts
vendored
|
@ -37,3 +37,9 @@ declare module "~fileContent/*" {
|
||||||
const content: string;
|
const content: string;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "*.css" { }
|
||||||
|
declare module "*.css?managed" {
|
||||||
|
const name: string;
|
||||||
|
export default name;
|
||||||
|
}
|
||||||
|
|
35
src/plugins/DisableDMCallIdle.ts
Normal file
35
src/plugins/DisableDMCallIdle.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "DisableDMCallIdle",
|
||||||
|
description: "Disables automatically getting kicked from a DM voice call after 5 minutes.",
|
||||||
|
authors: [Devs.Nuckyz],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: ".Messages.BOT_CALL_IDLE_DISCONNECT",
|
||||||
|
replacement: {
|
||||||
|
match: /function (?<functionName>.{1,3})\(\){.{1,100}\.Messages\.BOT_CALL_IDLE_DISCONNECT.+?}}/,
|
||||||
|
replace: "function $<functionName>(){}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
|
@ -66,11 +66,20 @@ export default definePlugin({
|
||||||
/* Patch the badge list component on user profiles */
|
/* Patch the badge list component on user profiles */
|
||||||
{
|
{
|
||||||
find: "Messages.PROFILE_USER_BADGES,role:",
|
find: "Messages.PROFILE_USER_BADGES,role:",
|
||||||
replacement: {
|
replacement: [
|
||||||
|
{
|
||||||
match: /src:(\w{1,3})\[(\w{1,3})\.key\],/,
|
match: /src:(\w{1,3})\[(\w{1,3})\.key\],/,
|
||||||
// <img src={badge.image ?? imageMap[badge.key]} {...badge.props} />
|
// <img src={badge.image ?? imageMap[badge.key]} {...badge.props} />
|
||||||
replace: (_, imageMap, badge) => `src: ${badge}.image ?? ${imageMap}[${badge}.key], ...${badge}.props,`
|
replace: (_, imageMap, badge) => `src: ${badge}.image ?? ${imageMap}[${badge}.key], ...${badge}.props,`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /spacing:(\d{1,2}),children:(.{1,40}(.{1,2})\.jsx.+(.{1,2})\.onClick.+\)})},/,
|
||||||
|
// if the badge provides it's own component, render that instead of an image
|
||||||
|
// the badge also includes info about the user that has it (type BadgeUserArgs), which is why it's passed as props
|
||||||
|
replace: (_, s, origBadgeComponent, React, badge) =>
|
||||||
|
`spacing:${s},children:${badge}.component ? () => (0,${React}.jsx)(${badge}.component, { ...${badge} }) : ${origBadgeComponent}},`
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
42
src/plugins/apiMemberListDecorators.ts
Normal file
42
src/plugins/apiMemberListDecorators.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "MemberListDecoratorsAPI",
|
||||||
|
description: "API to add decorators to member list (both in servers and DMs)",
|
||||||
|
authors: [Devs.TheSun],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: "lostPermissionTooltipText,",
|
||||||
|
replacement: {
|
||||||
|
match: /Fragment,{children:\[(.{30,80})\]/,
|
||||||
|
replace: "Fragment,{children:Vencord.Api.MemberListDecorators.__addDecoratorsToList(this.props).concat($1)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: "PrivateChannel.renderAvatar",
|
||||||
|
replacement: {
|
||||||
|
match: /(subText:(.{1,2})\.renderSubtitle\(\).{1,50}decorators):(.{30,100}:null)/,
|
||||||
|
replace: "$1:Vencord.Api.MemberListDecorators.__addDecoratorsToList($2.props).concat($3)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
35
src/plugins/apiMessageDecorations.ts
Normal file
35
src/plugins/apiMessageDecorations.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "MessageDecorationsAPI",
|
||||||
|
description: "API to add decorations to messages",
|
||||||
|
authors: [Devs.TheSun],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: ".withMentionPrefix",
|
||||||
|
replacement: {
|
||||||
|
match: /(\(\).roleDot.{10,50}{children:.{1,2})}\)/,
|
||||||
|
replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
|
@ -16,12 +16,9 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { migratePluginSettings } from "@api/settings";
|
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
migratePluginSettings("NoticesAPI", "NoticesApi");
|
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "NoticesAPI",
|
name: "NoticesAPI",
|
||||||
description: "Fixes notices being automatically dismissed",
|
description: "Fixes notices being automatically dismissed",
|
||||||
|
@ -29,12 +26,12 @@ export default definePlugin({
|
||||||
required: true,
|
required: true,
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "updateNotice:",
|
find: 'displayName="NoticeStore"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /;(.{1,2}=null;)(?=.{0,50}updateNotice)/g,
|
match: /;.{1,2}=null;.{0,70}getPremiumSubscription/g,
|
||||||
replace:
|
replace:
|
||||||
";if(Vencord.Api.Notices.currentNotice)return !1;$1"
|
";if(Vencord.Api.Notices.currentNotice)return false$&"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(?<=NOTICE_DISMISS:function.+?){(?=if\(null==(.+?)\))/,
|
match: /(?<=NOTICE_DISMISS:function.+?){(?=if\(null==(.+?)\))/,
|
||||||
|
|
|
@ -50,7 +50,7 @@ function getGuildCandidates(isAnimated: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doClone(guildId: string, id: string, name: string, isAnimated: boolean) {
|
async function doClone(guildId: string, id: string, name: string, isAnimated: boolean) {
|
||||||
const data = await fetch(`https://cdn.discordapp.com/emojis/${id}.${isAnimated ? "gif" : "png"}`)
|
const data = await fetch(`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${id}.${isAnimated ? "gif" : "png"}`)
|
||||||
.then(r => r.blob());
|
.then(r => r.blob());
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ export default definePlugin({
|
||||||
<img
|
<img
|
||||||
role="presentation"
|
role="presentation"
|
||||||
aria-hidden
|
aria-hidden
|
||||||
src={`https://cdn.discordapp.com/emojis/${id}.${isAnimated ? "gif" : "png"}`}
|
src={`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${id}.${isAnimated ? "gif" : "png"}`}
|
||||||
alt=""
|
alt=""
|
||||||
height={24}
|
height={24}
|
||||||
width={24}
|
width={24}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import "./messageLogger.css";
|
||||||
|
|
||||||
import { Settings } from "@api/settings";
|
import { Settings } from "@api/settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
@ -42,51 +44,14 @@ export default definePlugin({
|
||||||
timestampModule: null as any,
|
timestampModule: null as any,
|
||||||
moment: null as Function | null,
|
moment: null as Function | null,
|
||||||
|
|
||||||
css: `
|
|
||||||
.messagelogger-red-overlay .messageLogger-deleted {
|
|
||||||
background-color: rgba(240, 71, 71, 0.15);
|
|
||||||
}
|
|
||||||
.messagelogger-red-text .messageLogger-deleted div {
|
|
||||||
color: #f04747;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageLogger-deleted [class^="buttons"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageLogger-deleted-attachment {
|
|
||||||
filter: grayscale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageLogger-deleted-attachment:hover {
|
|
||||||
filter: grayscale(0);
|
|
||||||
transition: 250ms filter linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-dark .messageLogger-edited {
|
|
||||||
filter: brightness(80%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-light .messageLogger-edited {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this.moment = findByPropsLazy("relativeTimeRounding", "relativeTimeThreshold");
|
this.moment = findByPropsLazy("relativeTimeRounding", "relativeTimeThreshold");
|
||||||
this.timestampModule = findByPropsLazy("messageLogger_TimestampComponent");
|
this.timestampModule = findByPropsLazy("messageLogger_TimestampComponent");
|
||||||
|
|
||||||
const style = this.style = document.createElement("style");
|
|
||||||
style.textContent = this.css;
|
|
||||||
style.id = "MessageLogger-css";
|
|
||||||
document.head.appendChild(style);
|
|
||||||
|
|
||||||
addDeleteStyleClass();
|
addDeleteStyleClass();
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
this.style?.remove();
|
|
||||||
|
|
||||||
document.querySelectorAll(".messageLogger-deleted").forEach(e => e.remove());
|
document.querySelectorAll(".messageLogger-deleted").forEach(e => e.remove());
|
||||||
document.querySelectorAll(".messageLogger-edited").forEach(e => e.remove());
|
document.querySelectorAll(".messageLogger-edited").forEach(e => e.remove());
|
||||||
document.body.classList.remove("messagelogger-red-overlay");
|
document.body.classList.remove("messagelogger-red-overlay");
|
||||||
|
|
27
src/plugins/messageLogger/messageLogger.css
Normal file
27
src/plugins/messageLogger/messageLogger.css
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
.messagelogger-red-overlay .messageLogger-deleted {
|
||||||
|
background-color: rgba(240, 71, 71, 0.15);
|
||||||
|
}
|
||||||
|
.messagelogger-red-text .messageLogger-deleted div {
|
||||||
|
color: #f04747;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageLogger-deleted [class^="buttons"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageLogger-deleted-attachment {
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageLogger-deleted-attachment:hover {
|
||||||
|
filter: grayscale(0);
|
||||||
|
transition: 250ms filter linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .messageLogger-edited {
|
||||||
|
filter: brightness(80%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .messageLogger-edited {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
38
src/plugins/noScreensharePreview.ts
Normal file
38
src/plugins/noScreensharePreview.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "NoScreensharePreview",
|
||||||
|
description: "Disables screenshare previews from being sent.",
|
||||||
|
authors: [Devs.Nuckyz],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: '("ApplicationStreamPreviewUploadManager")',
|
||||||
|
replacement: [
|
||||||
|
".\\.default\\.makeChunkedRequest\\(",
|
||||||
|
".{1,2}\\..\\.post\\({url:"
|
||||||
|
].map(match => ({
|
||||||
|
match: new RegExp(`return\\[(?<code>\\d),${match}.\\..{1,3}\\.STREAM_PREVIEW.+?}\\)\\];`),
|
||||||
|
replace: 'return[$<code>,Promise.resolve({body:"",status:204})];'
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
|
@ -16,22 +16,27 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { addBadge, BadgePosition, ProfileBadge, removeBadge } from "@api/Badges";
|
||||||
|
import { addDecorator, removeDecorator } from "@api/MemberListDecorators";
|
||||||
|
import { addDecoration, removeDecoration } from "@api/MessageDecorations";
|
||||||
import { Settings } from "@api/settings";
|
import { Settings } from "@api/settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||||
import { PresenceStore, Tooltip } from "@webpack/common";
|
import { PresenceStore, Tooltip, UserStore } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
|
const SessionStore = findByPropsLazy("getActiveSession");
|
||||||
|
|
||||||
function Icon(path: string, viewBox = "0 0 24 24") {
|
function Icon(path: string, viewBox = "0 0 24 24") {
|
||||||
return ({ color, tooltip }: { color: string; tooltip: string; }) => (
|
return ({ color, tooltip }: { color: string; tooltip: string; }) => (
|
||||||
<Tooltip text={tooltip} >
|
<Tooltip text={tooltip} >
|
||||||
{(tooltipProps: any) => (
|
{(tooltipProps: any) => (
|
||||||
<svg
|
<svg
|
||||||
{...tooltipProps}
|
{...tooltipProps}
|
||||||
height="18"
|
height="20"
|
||||||
width="18"
|
width="20"
|
||||||
viewBox={viewBox}
|
viewBox={viewBox}
|
||||||
fill={color}
|
fill={color}
|
||||||
>
|
>
|
||||||
|
@ -59,9 +64,33 @@ const PlatformIcon = ({ platform, status }: { platform: Platform, status: string
|
||||||
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} />;
|
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PlatformIndicator = ({ user }: { user: User; }) => {
|
const getStatus = (id: string): Record<Platform, string> => PresenceStore.getState()?.clientStatuses?.[id];
|
||||||
|
|
||||||
|
const PlatformIndicator = ({ user, inline = false, marginLeft = "4px" }: { user: User; inline?: boolean; marginLeft?: string; }) => {
|
||||||
if (!user || user.bot) return null;
|
if (!user || user.bot) return null;
|
||||||
|
|
||||||
|
if (user.id === UserStore.getCurrentUser().id) {
|
||||||
|
const sessions = SessionStore.getSessions();
|
||||||
|
if (typeof sessions !== "object") return null;
|
||||||
|
const sortedSessions = Object.values(sessions).sort(({ status: a }: any, { status: b }: any) => {
|
||||||
|
if (a === b) return 0;
|
||||||
|
if (a === "online") return 1;
|
||||||
|
if (b === "online") return -1;
|
||||||
|
if (a === "idle") return 1;
|
||||||
|
if (b === "idle") return -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const ownStatus = Object.values(sortedSessions).reduce((acc: any, curr: any) => {
|
||||||
|
if (curr.clientInfo.client !== "unknown")
|
||||||
|
acc[curr.clientInfo.client] = curr.status;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const { clientStatuses } = PresenceStore.getState();
|
||||||
|
clientStatuses[UserStore.getCurrentUser().id] = ownStatus;
|
||||||
|
}
|
||||||
|
|
||||||
const status = PresenceStore.getState()?.clientStatuses?.[user.id] as Record<Platform, string>;
|
const status = PresenceStore.getState()?.clientStatuses?.[user.id] as Record<Platform, string>;
|
||||||
if (!status) return null;
|
if (!status) return null;
|
||||||
|
|
||||||
|
@ -79,7 +108,11 @@ const PlatformIndicator = ({ user }: { user: User; }) => {
|
||||||
<div
|
<div
|
||||||
className="vc-platform-indicator"
|
className="vc-platform-indicator"
|
||||||
style={{
|
style={{
|
||||||
display: "flex", alignItems: "center", marginLeft: "4px", gap: "4px"
|
marginLeft,
|
||||||
|
gap: "4px",
|
||||||
|
display: inline ? "inline-flex" : "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
transform: inline ? "translateY(4px)" : undefined
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{icons}
|
{icons}
|
||||||
|
@ -87,67 +120,84 @@ const PlatformIndicator = ({ user }: { user: User; }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const badge: ProfileBadge = {
|
||||||
|
component: p => <PlatformIndicator {...p} marginLeft="" />,
|
||||||
|
position: BadgePosition.START,
|
||||||
|
shouldShow: userInfo => !!Object.keys(getStatus(userInfo.user.id) ?? {}).length,
|
||||||
|
key: "indicator"
|
||||||
|
};
|
||||||
|
|
||||||
|
const indicatorLocations = {
|
||||||
|
list: {
|
||||||
|
description: "In the member list",
|
||||||
|
onEnable: () => addDecorator("platform-indicator", props =>
|
||||||
|
<ErrorBoundary noop>
|
||||||
|
<PlatformIndicator user={props.user} />
|
||||||
|
</ErrorBoundary>
|
||||||
|
),
|
||||||
|
onDisable: () => removeDecorator("platform-indicator")
|
||||||
|
},
|
||||||
|
badges: {
|
||||||
|
description: "In user profiles, as badges",
|
||||||
|
onEnable: () => addBadge(badge),
|
||||||
|
onDisable: () => removeBadge(badge)
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
description: "Inside messages",
|
||||||
|
onEnable: () => addDecoration("platform-indicator", props =>
|
||||||
|
<ErrorBoundary noop>
|
||||||
|
<PlatformIndicator user={
|
||||||
|
props.decorations[1]?.find(i => i.key === "new-member")?.props.message?.author
|
||||||
|
} inline />
|
||||||
|
</ErrorBoundary>
|
||||||
|
),
|
||||||
|
onDisable: () => removeDecoration("platform-indicator")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "PlatformIndicators",
|
name: "PlatformIndicators",
|
||||||
description: "Adds platform indicators (Desktop, Mobile, Web...) to users",
|
description: "Adds platform indicators (Desktop, Mobile, Web...) to users",
|
||||||
authors: [Devs.kemo],
|
authors: [Devs.kemo, Devs.TheSun],
|
||||||
|
dependencies: ["MessageDecorationsAPI", "MemberListDecoratorsAPI"],
|
||||||
|
|
||||||
patches: [
|
start() {
|
||||||
{
|
const settings = Settings.plugins.PlatformIndicators;
|
||||||
// Server member list decorators
|
const { displayMode } = settings;
|
||||||
find: "this.renderPremium()",
|
|
||||||
predicate: () => ["both", "list"].includes(Settings.plugins.PlatformIndicators.displayMode),
|
|
||||||
replacement: {
|
|
||||||
match: /this.renderPremium\(\)[^\]]*?\]/,
|
|
||||||
replace: "$&.concat(Vencord.Plugins.plugins.PlatformIndicators.renderPlatformIndicators(this.props))"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Dm list decorators
|
|
||||||
find: "PrivateChannel.renderAvatar",
|
|
||||||
predicate: () => ["both", "list"].includes(Settings.plugins.PlatformIndicators.displayMode),
|
|
||||||
replacement: {
|
|
||||||
match: /(subText:(.{1,3})\..+?decorators:)(.+?:null)/,
|
|
||||||
replace: "$1[$3].concat(Vencord.Plugins.plugins.PlatformIndicators.renderPlatformIndicators($2.props))"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// User badges
|
|
||||||
find: "Messages.PROFILE_USER_BADGES",
|
|
||||||
predicate: () => ["both", "badges"].includes(Settings.plugins.PlatformIndicators.displayMode),
|
|
||||||
replacement: {
|
|
||||||
match: /(Messages\.PROFILE_USER_BADGES,role:"group",children:)(.+?\.key\)\}\)\))/,
|
|
||||||
replace: "$1[Vencord.Plugins.plugins.PlatformIndicators.renderPlatformIndicators(e)].concat($2)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
renderPlatformIndicators: ({ user }: { user: User; }) => (
|
// transfer settings from the old ones, which had a select menu instead of booleans
|
||||||
<ErrorBoundary noop>
|
if (displayMode) {
|
||||||
<PlatformIndicator user={user} />
|
if (displayMode !== "both") settings[displayMode] = true;
|
||||||
</ErrorBoundary>
|
else {
|
||||||
),
|
settings.list = true;
|
||||||
|
settings.badges = true;
|
||||||
|
}
|
||||||
|
settings.messages = true;
|
||||||
|
delete settings.displayMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.entries(indicatorLocations).forEach(([key, value]) => {
|
||||||
|
if (settings[key]) value.onEnable();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
Object.entries(indicatorLocations).forEach(([_, value]) => {
|
||||||
|
value.onDisable();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
options: {
|
options: {
|
||||||
displayMode: {
|
...Object.fromEntries(
|
||||||
type: OptionType.SELECT,
|
Object.entries(indicatorLocations).map(([key, value]) => {
|
||||||
description: "Where to display the platform indicators",
|
return [key, {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: `Show indicators ${value.description.toLowerCase()}`,
|
||||||
|
// onChange doesn't give any way to know which setting was changed, so restart required
|
||||||
restartNeeded: true,
|
restartNeeded: true,
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: "Member List & Badges",
|
|
||||||
value: "both",
|
|
||||||
default: true
|
default: true
|
||||||
},
|
}];
|
||||||
{
|
})
|
||||||
label: "Member List Only",
|
)
|
||||||
value: "list"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Badges Only",
|
|
||||||
value: "badges"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -43,17 +43,10 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
// Hijack the discord pronouns section (hidden without experiment) and add a wrapper around the text section
|
// Hijack the discord pronouns section (hidden without experiment) and add a wrapper around the text section
|
||||||
{
|
{
|
||||||
find: "currentPronouns:",
|
find: ".Messages.BOT_PROFILE_SLASH_COMMANDS",
|
||||||
all: true,
|
|
||||||
noWarn: true,
|
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\(0,.{1,3}\.jsxs?\)\((.{1,10}),(\{[^[}]*currentPronouns:[^}]*(\w)\.pronouns[^}]*\})\)/,
|
match: /\(0,.\.jsx\)\((?<PronounComponent>.{1,2}\..),(?<pronounProps>{currentPronouns.+?:(?<fullProps>.{1,2})\.pronouns.+?})\)/,
|
||||||
replace: (original, PronounComponent, pronounProps, fullProps) => {
|
replace: "$<fullProps>&&Vencord.Plugins.plugins.PronounDB.PronounsProfileWrapper($<PronounComponent>,$<pronounProps>,$<fullProps>)"
|
||||||
// UserSettings
|
|
||||||
if (pronounProps.includes("onPronounsChange")) return original;
|
|
||||||
|
|
||||||
return `${fullProps}&&Vencord.Plugins.plugins.PronounDB.PronounsProfileWrapper(${PronounComponent}, ${pronounProps}, ${fullProps})`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Make pronouns experiment be enabled by default
|
// Make pronouns experiment be enabled by default
|
||||||
|
|
|
@ -33,7 +33,7 @@ export function Header({ langName, useDevIcon, shikiLang }: HeaderProps) {
|
||||||
<div className={cl("lang")}>
|
<div className={cl("lang")}>
|
||||||
{useDevIcon !== DeviconSetting.Disabled && shikiLang?.devicon && (
|
{useDevIcon !== DeviconSetting.Disabled && shikiLang?.devicon && (
|
||||||
<i
|
<i
|
||||||
className={`devicon-${shikiLang.devicon}${useDevIcon === DeviconSetting.Color ? " colored" : ""}`}
|
className={`${cl("devicon")} devicon-${shikiLang.devicon}${useDevIcon === DeviconSetting.Color ? " colored" : ""}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{langName}
|
{langName}
|
||||||
|
|
|
@ -90,14 +90,10 @@ export const Highlighter = ({
|
||||||
let langName;
|
let langName;
|
||||||
if (lang) langName = useHljs ? hljs?.getLanguage?.(lang)?.name : shikiLang?.name;
|
if (lang) langName = useHljs ? hljs?.getLanguage?.(lang)?.name : shikiLang?.name;
|
||||||
|
|
||||||
const preClasses = [cl("root")];
|
|
||||||
if (!langName) preClasses.push(cl("plain"));
|
|
||||||
if (isPreview) preClasses.push(cl("preview"));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={rootRef}
|
ref={rootRef}
|
||||||
className={preClasses.join(" ")}
|
className={cl("root", { plain: !langName, preview: isPreview })}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: useHljs
|
backgroundColor: useHljs
|
||||||
? themeBase.backgroundColor
|
? themeBase.backgroundColor
|
||||||
|
|
1
src/plugins/shikiCodeblocks/devicon.css
Normal file
1
src/plugins/shikiCodeblocks/devicon.css
Normal file
|
@ -0,0 +1 @@
|
||||||
|
@import url('https://cdn.jsdelivr.net/gh/devicons/devicon@v2.10.1/devicon.min.css');
|
|
@ -16,23 +16,25 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import "./shiki.css";
|
||||||
|
|
||||||
|
import { disableStyle, enableStyle } from "@api/Styles";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { parseUrl } from "@utils/misc";
|
import { parseUrl } from "@utils/misc";
|
||||||
import { wordsFromPascal, wordsToTitle } from "@utils/text";
|
import { wordsFromPascal, wordsToTitle } from "@utils/text";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
import previewExampleText from "~fileContent/previewExample.tsx";
|
import previewExampleText from "~fileContent/previewExample.tsx";
|
||||||
import cssText from "~fileContent/style.css";
|
|
||||||
|
|
||||||
import { Settings } from "../../Vencord";
|
import { Settings } from "../../Vencord";
|
||||||
import { shiki } from "./api/shiki";
|
import { shiki } from "./api/shiki";
|
||||||
import { themes } from "./api/themes";
|
import { themes } from "./api/themes";
|
||||||
import { createHighlighter } from "./components/Highlighter";
|
import { createHighlighter } from "./components/Highlighter";
|
||||||
import { DeviconSetting, HljsSetting, ShikiSettings, StyleSheets } from "./types";
|
import deviconStyle from "./devicon.css?managed";
|
||||||
import { clearStyles, removeStyle, setStyle } from "./utils/createStyle";
|
import { DeviconSetting, HljsSetting, ShikiSettings } from "./types";
|
||||||
|
import { clearStyles } from "./utils/createStyle";
|
||||||
|
|
||||||
const themeNames = Object.keys(themes);
|
const themeNames = Object.keys(themes);
|
||||||
const devIconCss = "@import url('https://cdn.jsdelivr.net/gh/devicons/devicon@v2.10.1/devicon.min.css');";
|
|
||||||
|
|
||||||
const getSettings = () => Settings.plugins.ShikiCodeblocks as ShikiSettings;
|
const getSettings = () => Settings.plugins.ShikiCodeblocks as ShikiSettings;
|
||||||
|
|
||||||
|
@ -44,15 +46,14 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "codeBlock:{react:function",
|
find: "codeBlock:{react:function",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /codeBlock:\{react:function\((.),(.),(.)\)\{/,
|
match: /codeBlock:\{react:function\((\i),(\i),(\i)\)\{/,
|
||||||
replace: "$&return Vencord.Plugins.plugins.ShikiCodeblocks.renderHighlighter($1,$2,$3);",
|
replace: "$&return $self.renderHighlighter($1,$2,$3);",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
start: async () => {
|
start: async () => {
|
||||||
setStyle(cssText, StyleSheets.Main);
|
|
||||||
if (getSettings().useDevIcon !== DeviconSetting.Disabled)
|
if (getSettings().useDevIcon !== DeviconSetting.Disabled)
|
||||||
setStyle(devIconCss, StyleSheets.DevIcons);
|
enableStyle(deviconStyle);
|
||||||
|
|
||||||
await shiki.init(getSettings().customTheme || getSettings().theme);
|
await shiki.init(getSettings().customTheme || getSettings().theme);
|
||||||
},
|
},
|
||||||
|
@ -135,8 +136,8 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
onChange: (newValue: DeviconSetting) => {
|
onChange: (newValue: DeviconSetting) => {
|
||||||
if (newValue === DeviconSetting.Disabled) removeStyle(StyleSheets.DevIcons);
|
if (newValue === DeviconSetting.Disabled) disableStyle(deviconStyle);
|
||||||
else setStyle(devIconCss, StyleSheets.DevIcons);
|
else enableStyle(deviconStyle);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
bgOpacity: {
|
bgOpacity: {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
.shiki-container {
|
||||||
|
border: 4px;
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
.shiki-root {
|
.shiki-root {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
/* fallback background */
|
|
||||||
background-color: var(--background-secondary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-root code {
|
.shiki-root code {
|
||||||
|
@ -19,8 +21,7 @@
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-root [class^='devicon-'],
|
.shiki-devicon {
|
||||||
.shiki-root [class*=' devicon-'] {
|
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
|
@ -16,13 +16,14 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { hljs } from "@webpack/common";
|
import { hljs } from "@webpack/common";
|
||||||
|
|
||||||
import { resolveLang } from "../api/languages";
|
import { resolveLang } from "../api/languages";
|
||||||
import { HighlighterProps } from "../components/Highlighter";
|
import { HighlighterProps } from "../components/Highlighter";
|
||||||
import { HljsSetting, ShikiSettings } from "../types";
|
import { HljsSetting, ShikiSettings } from "../types";
|
||||||
|
|
||||||
export const cl = (className: string) => `shiki-${className}`;
|
export const cl = classNameFactory("shiki-");
|
||||||
|
|
||||||
export const shouldUseHljs = ({
|
export const shouldUseHljs = ({
|
||||||
lang,
|
lang,
|
||||||
|
|
|
@ -16,13 +16,15 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import "./spotifyStyles.css";
|
||||||
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
import { classes, LazyComponent } from "@utils/misc";
|
import { classes, LazyComponent } from "@utils/misc";
|
||||||
import { filters, find, findByCodeLazy } from "@webpack";
|
import { filters, find, findByCodeLazy } from "@webpack";
|
||||||
import { ContextMenu, FluxDispatcher, Forms, Menu, React } from "@webpack/common";
|
import { ContextMenu, FluxDispatcher, Forms, Menu, React, useEffect, useState } from "@webpack/common";
|
||||||
|
|
||||||
import { SpotifyStore, Track } from "./SpotifyStore";
|
import { SpotifyStore, Track } from "./SpotifyStore";
|
||||||
|
|
||||||
|
@ -142,10 +144,10 @@ function SeekBar() {
|
||||||
() => [SpotifyStore.mPosition, SpotifyStore.isSettingPosition, SpotifyStore.isPlaying]
|
() => [SpotifyStore.mPosition, SpotifyStore.isSettingPosition, SpotifyStore.isPlaying]
|
||||||
);
|
);
|
||||||
|
|
||||||
const [position, setPosition] = React.useState(storePosition);
|
const [position, setPosition] = useState(storePosition);
|
||||||
|
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
if (isPlaying && !isSettingPosition) {
|
if (isPlaying && !isSettingPosition) {
|
||||||
setPosition(SpotifyStore.position);
|
setPosition(SpotifyStore.position);
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
|
@ -232,7 +234,7 @@ function AlbumContextMenu({ track }: { track: Track; }) {
|
||||||
function Info({ track }: { track: Track; }) {
|
function Info({ track }: { track: Track; }) {
|
||||||
const img = track?.album?.image;
|
const img = track?.album?.image;
|
||||||
|
|
||||||
const [coverExpanded, setCoverExpanded] = React.useState(false);
|
const [coverExpanded, setCoverExpanded] = useState(false);
|
||||||
|
|
||||||
const i = (
|
const i = (
|
||||||
<>
|
<>
|
||||||
|
@ -327,7 +329,7 @@ export function Player() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const isPlaying = useStateFromStores([SpotifyStore], () => SpotifyStore.isPlaying);
|
const isPlaying = useStateFromStores([SpotifyStore], () => SpotifyStore.isPlaying);
|
||||||
const [shouldHide, setShouldHide] = React.useState(false);
|
const [shouldHide, setShouldHide] = useState(false);
|
||||||
|
|
||||||
// Hide player after 5 minutes of inactivity
|
// Hide player after 5 minutes of inactivity
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
|
|
|
@ -21,8 +21,6 @@ import { proxyLazy } from "@utils/proxyLazy";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { Flux, FluxDispatcher } from "@webpack/common";
|
import { Flux, FluxDispatcher } from "@webpack/common";
|
||||||
|
|
||||||
import cssText from "~fileContent/spotifyStyles.css";
|
|
||||||
|
|
||||||
export interface Track {
|
export interface Track {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -69,11 +67,6 @@ type Repeat = "off" | "track" | "context";
|
||||||
|
|
||||||
// Don't wanna run before Flux and Dispatcher are ready!
|
// Don't wanna run before Flux and Dispatcher are ready!
|
||||||
export const SpotifyStore = proxyLazy(() => {
|
export const SpotifyStore = proxyLazy(() => {
|
||||||
// TODO: Move this elsewhere
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.innerText = cssText;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
|
|
||||||
// For some reason ts hates extends Flux.Store
|
// For some reason ts hates extends Flux.Store
|
||||||
const { Store } = Flux;
|
const { Store } = Flux;
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ export default definePlugin({
|
||||||
patches: [{
|
patches: [{
|
||||||
find: "PAYMENT_FLOW_MODAL_TEST_PAGE,",
|
find: "PAYMENT_FLOW_MODAL_TEST_PAGE,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /({section:[\w.]+?\.PAYMENT_FLOW_MODAL_TEST_PAGE,)/,
|
match: /{section:.{1,2}\..{1,3}\.PAYMENT_FLOW_MODAL_TEST_PAGE/,
|
||||||
replace: '{section:"StartupTimings",label:"Startup Timings",element:Vencord.Plugins.plugins.StartupTimings.StartupTimingPage},$1'
|
replace: '{section:"StartupTimings",label:"Startup Timings",element:Vencord.Plugins.plugins.StartupTimings.StartupTimingPage},$&'
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
StartupTimingPage: LazyComponent(() => require("./StartupTimingPage").default)
|
StartupTimingPage: LazyComponent(() => require("./StartupTimingPage").default)
|
||||||
|
|
|
@ -44,6 +44,34 @@ contextBridge.exposeInMainWorld("VencordNative", VencordNative);
|
||||||
if (location.protocol !== "data:") {
|
if (location.protocol !== "data:") {
|
||||||
// Discord
|
// Discord
|
||||||
webFrame.executeJavaScript(readFileSync(join(__dirname, "renderer.js"), "utf-8"));
|
webFrame.executeJavaScript(readFileSync(join(__dirname, "renderer.js"), "utf-8"));
|
||||||
|
const rendererCss = join(__dirname, "renderer.css");
|
||||||
|
|
||||||
|
function insertCss(css: string) {
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.id = "vencord-css-core";
|
||||||
|
style.textContent = css;
|
||||||
|
|
||||||
|
if (document.readyState === "complete") {
|
||||||
|
document.documentElement.appendChild(style);
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", () => document.documentElement.appendChild(style), {
|
||||||
|
once: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const css = readFileSync(rendererCss, "utf-8");
|
||||||
|
insertCss(css);
|
||||||
|
} catch (err) {
|
||||||
|
if ((err as NodeJS.ErrnoException)?.code !== "ENOENT")
|
||||||
|
throw err;
|
||||||
|
|
||||||
|
// hack: the pre update updater does not download this file, so manually download it
|
||||||
|
// TODO: remove this in a future version
|
||||||
|
ipcRenderer.invoke(IpcEvents.DOWNLOAD_VENCORD_CSS)
|
||||||
|
.then(insertCss);
|
||||||
|
}
|
||||||
require(process.env.DISCORD_PRELOAD!);
|
require(process.env.DISCORD_PRELOAD!);
|
||||||
} else {
|
} else {
|
||||||
// Monaco Popout
|
// Monaco Popout
|
||||||
|
|
|
@ -44,5 +44,6 @@ export default strEnum({
|
||||||
UPDATE: "VencordUpdate",
|
UPDATE: "VencordUpdate",
|
||||||
BUILD: "VencordBuild",
|
BUILD: "VencordBuild",
|
||||||
GET_DESKTOP_CAPTURE_SOURCES: "VencordGetDesktopCaptureSources",
|
GET_DESKTOP_CAPTURE_SOURCES: "VencordGetDesktopCaptureSources",
|
||||||
OPEN_MONACO_EDITOR: "VencordOpenMonacoEditor"
|
OPEN_MONACO_EDITOR: "VencordOpenMonacoEditor",
|
||||||
|
DOWNLOAD_VENCORD_CSS: "VencordDownloadVencordCss"
|
||||||
} as const);
|
} as const);
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const REACT_GLOBAL = "Vencord.Webpack.Common.React";
|
||||||
export const VENCORD_USER_AGENT = `Vencord/${gitHash}${gitRemote ? ` (https://github.com/${gitRemote})` : ""}`;
|
export const VENCORD_USER_AGENT = `Vencord/${gitHash}${gitRemote ? ` (https://github.com/${gitRemote})` : ""}`;
|
||||||
|
|
||||||
// Add yourself here if you made a plugin
|
// Add yourself here if you made a plugin
|
||||||
export const Devs = Object.freeze({
|
export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
Ven: {
|
Ven: {
|
||||||
name: "Vendicated",
|
name: "Vendicated",
|
||||||
id: 343383572805058560n
|
id: 343383572805058560n
|
||||||
|
@ -177,4 +177,8 @@ export const Devs = Object.freeze({
|
||||||
name: "'ax",
|
name: "'ax",
|
||||||
id: 273562710745284628n,
|
id: 273562710745284628n,
|
||||||
},
|
},
|
||||||
|
pointy: {
|
||||||
|
name: "pointy",
|
||||||
|
id: 99914384989519872n
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Clipboard, React, Toasts } from "@webpack/common";
|
import { Clipboard, React, Toasts, useEffect, useState } from "@webpack/common";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a lazy function. On first call, the value is computed.
|
* Makes a lazy function. On first call, the value is computed.
|
||||||
|
@ -48,13 +48,13 @@ export function useAwaiter<T>(factory: () => Promise<T>, providedOpts?: AwaiterO
|
||||||
deps: [],
|
deps: [],
|
||||||
onError: null,
|
onError: null,
|
||||||
}, providedOpts);
|
}, providedOpts);
|
||||||
const [state, setState] = React.useState({
|
const [state, setState] = useState({
|
||||||
value: opts.fallbackValue,
|
value: opts.fallbackValue,
|
||||||
error: null,
|
error: null,
|
||||||
pending: true
|
pending: true
|
||||||
});
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
let isAlive = true;
|
let isAlive = true;
|
||||||
if (!state.pending) setState({ ...state, pending: true });
|
if (!state.pending) setState({ ...state, pending: true });
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ export function useAwaiter<T>(factory: () => Promise<T>, providedOpts?: AwaiterO
|
||||||
* Returns a function that can be used to force rerender react components
|
* Returns a function that can be used to force rerender react components
|
||||||
*/
|
*/
|
||||||
export function useForceUpdater() {
|
export function useForceUpdater() {
|
||||||
const [, set] = React.useState(0);
|
const [, set] = useState(0);
|
||||||
return () => set(s => s + 1);
|
return () => set(s => s + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
55
src/utils/patches.ts
Normal file
55
src/utils/patches.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PatchReplacement } from "./types";
|
||||||
|
|
||||||
|
export type ReplaceFn = (match: string, ...groups: string[]) => string;
|
||||||
|
|
||||||
|
export function canonicalizeMatch(match: RegExp | string) {
|
||||||
|
if (typeof match === "string") return match;
|
||||||
|
const canonSource = match.source
|
||||||
|
.replaceAll("\\i", "[A-Za-z_$][\\w$]*");
|
||||||
|
return new RegExp(canonSource, match.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canonicalizeReplace(replace: string | ReplaceFn, pluginName: string) {
|
||||||
|
if (typeof replace === "function") return replace;
|
||||||
|
return replace.replaceAll("$self", `Vencord.Plugins.plugins.${pluginName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canonicalizeDescriptor<T>(descriptor: TypedPropertyDescriptor<T>, canonicalize: (value: T) => T) {
|
||||||
|
if (descriptor.get) {
|
||||||
|
const original = descriptor.get;
|
||||||
|
descriptor.get = function () {
|
||||||
|
return canonicalize(original.call(this));
|
||||||
|
};
|
||||||
|
} else if (descriptor.value) {
|
||||||
|
descriptor.value = canonicalize(descriptor.value);
|
||||||
|
}
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canonicalizeReplacement(replacement: Pick<PatchReplacement, "match" | "replace">, plugin: string) {
|
||||||
|
const descriptors = Object.getOwnPropertyDescriptors(replacement);
|
||||||
|
descriptors.match = canonicalizeDescriptor(descriptors.match, canonicalizeMatch);
|
||||||
|
descriptors.replace = canonicalizeDescriptor(
|
||||||
|
descriptors.replace,
|
||||||
|
replace => canonicalizeReplace(replace, plugin),
|
||||||
|
);
|
||||||
|
Object.defineProperties(replacement, descriptors);
|
||||||
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { React } from "@webpack/common";
|
import { React, useState } from "@webpack/common";
|
||||||
|
|
||||||
import { checkIntersecting } from "./misc";
|
import { checkIntersecting } from "./misc";
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ export const useIntersection = (intersectOnly = false): [
|
||||||
isIntersecting: boolean,
|
isIntersecting: boolean,
|
||||||
] => {
|
] => {
|
||||||
const observerRef = React.useRef<IntersectionObserver | null>(null);
|
const observerRef = React.useRef<IntersectionObserver | null>(null);
|
||||||
const [isIntersecting, setIntersecting] = React.useState(false);
|
const [isIntersecting, setIntersecting] = useState(false);
|
||||||
|
|
||||||
const refCallback = (element: Element | null) => {
|
const refCallback = (element: Element | null) => {
|
||||||
observerRef.current?.disconnect();
|
observerRef.current?.disconnect();
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
import { Command } from "@api/Commands";
|
import { Command } from "@api/Commands";
|
||||||
import { Promisable } from "type-fest";
|
import { Promisable } from "type-fest";
|
||||||
|
|
||||||
|
import type { ReplaceFn } from "./patches";
|
||||||
|
|
||||||
// exists to export default definePlugin({...})
|
// exists to export default definePlugin({...})
|
||||||
export default function definePlugin<P extends PluginDef>(p: P & Record<string, any>) {
|
export default function definePlugin<P extends PluginDef>(p: P & Record<string, any>) {
|
||||||
return p;
|
return p;
|
||||||
|
@ -26,7 +28,7 @@ export default function definePlugin<P extends PluginDef>(p: P & Record<string,
|
||||||
|
|
||||||
export interface PatchReplacement {
|
export interface PatchReplacement {
|
||||||
match: string | RegExp;
|
match: string | RegExp;
|
||||||
replace: string | ((match: string, ...groups: string[]) => string);
|
replace: string | ReplaceFn;
|
||||||
predicate?(): boolean;
|
predicate?(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ export function getRepo() {
|
||||||
return Unwrap(VencordNative.ipc.invoke<IpcRes<string>>(IpcEvents.GET_REPO));
|
return Unwrap(VencordNative.ipc.invoke<IpcRes<string>>(IpcEvents.GET_REPO));
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hashes = Record<"patcher.js" | "preload.js" | "renderer.js", string>;
|
type Hashes = Record<"patcher.js" | "preload.js" | "renderer.js" | "renderer.css", string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns true if hard restart is required
|
* @returns true if hard restart is required
|
||||||
|
|
|
@ -31,7 +31,13 @@ export const Margins = findByPropsLazy("marginTop20");
|
||||||
|
|
||||||
export let FluxDispatcher: Other.FluxDispatcher;
|
export let FluxDispatcher: Other.FluxDispatcher;
|
||||||
export const Flux = findByPropsLazy("connectStores");
|
export const Flux = findByPropsLazy("connectStores");
|
||||||
|
|
||||||
export let React: typeof import("react");
|
export let React: typeof import("react");
|
||||||
|
export let useState: typeof React.useState;
|
||||||
|
export let useEffect: typeof React.useEffect;
|
||||||
|
export let useMemo: typeof React.useMemo;
|
||||||
|
export let useRef: typeof React.useRef;
|
||||||
|
|
||||||
export const ReactDOM: typeof import("react-dom") = findByPropsLazy("createPortal", "render");
|
export const ReactDOM: typeof import("react-dom") = findByPropsLazy("createPortal", "render");
|
||||||
|
|
||||||
export const RestAPI = findByPropsLazy("getAPIBaseURL", "get");
|
export const RestAPI = findByPropsLazy("getAPIBaseURL", "get");
|
||||||
|
@ -76,7 +82,7 @@ export const TextArea = findByCodeLazy("handleSetRef", "textArea") as React.Comp
|
||||||
export const Select = LazyComponent(() => findByCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems"));
|
export const Select = LazyComponent(() => findByCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems"));
|
||||||
export const Slider = LazyComponent(() => findByCode("closestMarkerIndex", "stickToMarkers"));
|
export const Slider = LazyComponent(() => findByCode("closestMarkerIndex", "stickToMarkers"));
|
||||||
|
|
||||||
export let SnowflakeUtils: { fromTimestamp: (timestamp: number) => string, extractTimestamp: (snowflake: string) => number };
|
export let SnowflakeUtils: { fromTimestamp: (timestamp: number) => string, extractTimestamp: (snowflake: string) => number; };
|
||||||
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
|
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
|
||||||
|
|
||||||
export let Parser: any;
|
export let Parser: any;
|
||||||
|
@ -151,7 +157,10 @@ export const NavigationRouter = mapMangledModuleLazy("Transitioning to external
|
||||||
goForward: filters.byCode("goForward()"),
|
goForward: filters.byCode("goForward()"),
|
||||||
});
|
});
|
||||||
|
|
||||||
waitFor("useState", m => React = m);
|
waitFor("useState", m => {
|
||||||
|
React = m;
|
||||||
|
({ useEffect, useState, useMemo, useRef } = React);
|
||||||
|
});
|
||||||
|
|
||||||
waitFor(["dispatch", "subscribe"], m => {
|
waitFor(["dispatch", "subscribe"], m => {
|
||||||
FluxDispatcher = m;
|
FluxDispatcher = m;
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
import { WEBPACK_CHUNK } from "@utils/constants";
|
import { WEBPACK_CHUNK } from "@utils/constants";
|
||||||
import Logger from "@utils/Logger";
|
import Logger from "@utils/Logger";
|
||||||
|
import { canonicalizeReplacement } from "@utils/patches";
|
||||||
|
import { PatchReplacement } from "@utils/types";
|
||||||
|
|
||||||
import { _initWebpack } from ".";
|
import { _initWebpack } from ".";
|
||||||
|
|
||||||
|
@ -135,15 +137,17 @@ function patchPush() {
|
||||||
if (code.includes(patch.find)) {
|
if (code.includes(patch.find)) {
|
||||||
patchedBy.add(patch.plugin);
|
patchedBy.add(patch.plugin);
|
||||||
|
|
||||||
// @ts-ignore we change all patch.replacement to array in plugins/index
|
// we change all patch.replacement to array in plugins/index
|
||||||
for (const replacement of patch.replacement) {
|
for (const replacement of patch.replacement as PatchReplacement[]) {
|
||||||
if (replacement.predicate && !replacement.predicate()) continue;
|
if (replacement.predicate && !replacement.predicate()) continue;
|
||||||
const lastMod = mod;
|
const lastMod = mod;
|
||||||
const lastCode = code;
|
const lastCode = code;
|
||||||
|
|
||||||
|
canonicalizeReplacement(replacement, patch.plugin);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newCode = code.replace(replacement.match, replacement.replace);
|
const newCode = code.replace(replacement.match, replacement.replace as string);
|
||||||
if (newCode === code && !replacement.noWarn) {
|
if (newCode === code && !patch.noWarn) {
|
||||||
logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${id}): ${replacement.match}`);
|
logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${id}): ${replacement.match}`);
|
||||||
if (IS_DEV) {
|
if (IS_DEV) {
|
||||||
logger.debug("Function Source:\n", code);
|
logger.debug("Function Source:\n", code);
|
||||||
|
|
|
@ -31,13 +31,15 @@ for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CANARY = process.env.USE_CANARY === "true";
|
||||||
|
|
||||||
const browser = await pup.launch({
|
const browser = await pup.launch({
|
||||||
headless: true,
|
headless: true,
|
||||||
executablePath: process.env.CHROMIUM_BIN
|
executablePath: process.env.CHROMIUM_BIN
|
||||||
});
|
});
|
||||||
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
await page.setUserAgent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36");
|
await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36");
|
||||||
|
|
||||||
function maybeGetError(handle: JSHandle) {
|
function maybeGetError(handle: JSHandle) {
|
||||||
return (handle as JSHandle<Error>)?.getProperty("message")
|
return (handle as JSHandle<Error>)?.getProperty("message")
|
||||||
|
@ -65,7 +67,7 @@ function toCodeBlock(s: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function printReport() {
|
async function printReport() {
|
||||||
console.log("# Vencord Report");
|
console.log("# Vencord Report" + (CANARY ? " (Canary)" : ""));
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
console.log("## Bad Patches");
|
console.log("## Bad Patches");
|
||||||
|
@ -98,7 +100,7 @@ async function printReport() {
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
description: "Here's the latest Vencord Report!",
|
description: "Here's the latest Vencord Report!",
|
||||||
username: "Vencord Reporter",
|
username: "Vencord Reporter" + (CANARY ? " (Canary)" : ""),
|
||||||
avatar_url: "https://cdn.discordapp.com/icons/1015060230222131221/f0204a918c6c9c9a43195997e97d8adf.webp",
|
avatar_url: "https://cdn.discordapp.com/icons/1015060230222131221/f0204a918c6c9c9a43195997e97d8adf.webp",
|
||||||
embeds: [
|
embeds: [
|
||||||
{
|
{
|
||||||
|
@ -271,4 +273,4 @@ await page.evaluateOnNewDocument(`
|
||||||
;(${runTime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)});
|
;(${runTime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)});
|
||||||
`);
|
`);
|
||||||
|
|
||||||
await page.goto("https://discord.com/login");
|
await page.goto(CANARY ? "https://canary.discord.com/login" : "https://discord.com/login");
|
||||||
|
|
Loading…
Add table
Reference in a new issue