Merge branch 'dev' into Warning

This commit is contained in:
nin0dev 2024-05-28 16:33:38 -04:00 committed by GitHub
commit 2a1f7b7dec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 1056 additions and 194 deletions

View file

@ -1,7 +1,7 @@
{ {
"root": true, "root": true,
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"ignorePatterns": ["dist", "browser"], "ignorePatterns": ["dist", "browser", "packages/vencord-types"],
"plugins": [ "plugins": [
"@typescript-eslint", "@typescript-eslint",
"simple-header", "simple-header",

View file

@ -19,8 +19,8 @@
/// <reference path="../src/modules.d.ts" /> /// <reference path="../src/modules.d.ts" />
/// <reference path="../src/globals.d.ts" /> /// <reference path="../src/globals.d.ts" />
import monacoHtmlLocal from "~fileContent/monacoWin.html"; import monacoHtmlLocal from "file://monacoWin.html?minify";
import monacoHtmlCdn from "~fileContent/../src/main/monacoWin.html"; import monacoHtmlCdn from "file://../src/main/monacoWin.html?minify";
import * as DataStore from "../src/api/DataStore"; import * as DataStore from "../src/api/DataStore";
import { debounce } from "../src/utils"; import { debounce } from "../src/utils";
import { EXTENSION_BASE_URL } from "../src/utils/web-metadata"; import { EXTENSION_BASE_URL } from "../src/utils/web-metadata";

View file

@ -18,8 +18,9 @@
}, },
"scripts": { "scripts": {
"build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs", "build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs",
"buildStandalone": "pnpm build --standalone",
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs", "buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs",
"watch": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs --watch", "watch": "pnpm build --watch",
"generatePluginJson": "tsx scripts/generatePluginList.ts", "generatePluginJson": "tsx scripts/generatePluginList.ts",
"generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types", "generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types",
"inject": "node scripts/runInstaller.mjs", "inject": "node scripts/runInstaller.mjs",
@ -27,7 +28,7 @@
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern src/userplugins", "lint": "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern src/userplugins",
"lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins", "lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins",
"lint:fix": "pnpm lint --fix", "lint:fix": "pnpm lint --fix",
"test": "pnpm build && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson", "test": "pnpm buildStandalone && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson",
"testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc", "testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc",
"testTsc": "tsc --noEmit" "testTsc": "tsc --noEmit"
}, },
@ -61,6 +62,7 @@
"eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0", "eslint-plugin-unused-imports": "^2.0.0",
"highlight.js": "10.6.0", "highlight.js": "10.6.0",
"html-minifier-terser": "^7.2.0",
"moment": "^2.29.4", "moment": "^2.29.4",
"puppeteer-core": "^19.11.1", "puppeteer-core": "^19.11.1",
"standalone-electron-types": "^1.0.0", "standalone-electron-types": "^1.0.0",

View file

@ -98,6 +98,9 @@ importers:
highlight.js: highlight.js:
specifier: 10.6.0 specifier: 10.6.0
version: 10.6.0 version: 10.6.0
html-minifier-terser:
specifier: ^7.2.0
version: 7.2.0
moment: moment:
specifier: ^2.29.4 specifier: ^2.29.4
version: 2.29.4 version: 2.29.4
@ -393,6 +396,27 @@ packages:
'@humanwhocodes/object-schema@1.2.1': '@humanwhocodes/object-schema@1.2.1':
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
'@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
'@jridgewell/set-array@1.2.1':
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
engines: {node: '>=6.0.0'}
'@jridgewell/source-map@0.3.6':
resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
'@jridgewell/sourcemap-codec@1.4.15':
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -688,6 +712,9 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
camel-case@4.1.2:
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
camelcase-keys@6.2.2: camelcase-keys@6.2.2:
resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -716,6 +743,10 @@ packages:
resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
clean-css@5.3.3:
resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
engines: {node: '>= 10.0'}
cliui@8.0.1: cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -740,6 +771,13 @@ packages:
colord@2.9.3: colord@2.9.3:
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
commander@10.0.1:
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
engines: {node: '>=14'}
commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
component-emitter@1.3.0: component-emitter@1.3.0:
resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==}
@ -871,12 +909,19 @@ packages:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
dot-case@3.0.4:
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
emoji-regex@8.0.0: emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
end-of-stream@1.4.4: end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
error-ex@1.3.2: error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
@ -1402,6 +1447,11 @@ packages:
resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==}
engines: {node: '>=10'} engines: {node: '>=10'}
html-minifier-terser@7.2.0:
resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==}
engines: {node: ^14.13.1 || >=16.0.0}
hasBin: true
html-tags@3.3.1: html-tags@3.3.1:
resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -1673,6 +1723,9 @@ packages:
lodash.truncate@4.4.2: lodash.truncate@4.4.2:
resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
lru-cache@6.0.0: lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1770,6 +1823,9 @@ packages:
natural-compare@1.4.0: natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
no-case@3.0.4:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
node-fetch@2.6.7: node-fetch@2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0} engines: {node: 4.x || >=6.0.0}
@ -1855,6 +1911,9 @@ packages:
pako@1.0.11: pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
param-case@3.0.4:
resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
parent-module@1.0.1: parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -1863,6 +1922,9 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'} engines: {node: '>=8'}
pascal-case@3.1.2:
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
pascalcase@0.1.1: pascalcase@0.1.1:
resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -1985,6 +2047,10 @@ packages:
resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
relateurl@0.2.7:
resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==}
engines: {node: '>= 0.10'}
require-directory@2.1.1: require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -2228,6 +2294,11 @@ packages:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
terser@5.31.0:
resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==}
engines: {node: '>=10'}
hasBin: true
text-table@0.2.0: text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@ -2263,6 +2334,9 @@ packages:
tslib@1.14.1: tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
tsutils@3.21.0: tsutils@3.21.0:
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@ -2599,6 +2673,28 @@ snapshots:
'@humanwhocodes/object-schema@1.2.1': {} '@humanwhocodes/object-schema@1.2.1': {}
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/set-array@1.2.1': {}
'@jridgewell/source-map@0.3.6':
dependencies:
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/sourcemap-codec@1.4.15': {}
'@jridgewell/trace-mapping@0.3.25':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
dependencies: dependencies:
'@nodelib/fs.stat': 2.0.5 '@nodelib/fs.stat': 2.0.5
@ -2958,6 +3054,11 @@ snapshots:
callsites@3.1.0: {} callsites@3.1.0: {}
camel-case@4.1.2:
dependencies:
pascal-case: 3.1.2
tslib: 2.6.2
camelcase-keys@6.2.2: camelcase-keys@6.2.2:
dependencies: dependencies:
camelcase: 5.3.1 camelcase: 5.3.1
@ -2991,6 +3092,10 @@ snapshots:
isobject: 3.0.1 isobject: 3.0.1
static-extend: 0.1.2 static-extend: 0.1.2
clean-css@5.3.3:
dependencies:
source-map: 0.6.1
cliui@8.0.1: cliui@8.0.1:
dependencies: dependencies:
string-width: 4.2.3 string-width: 4.2.3
@ -3016,6 +3121,10 @@ snapshots:
colord@2.9.3: {} colord@2.9.3: {}
commander@10.0.1: {}
commander@2.20.3: {}
component-emitter@1.3.0: {} component-emitter@1.3.0: {}
concat-map@0.0.1: {} concat-map@0.0.1: {}
@ -3139,12 +3248,19 @@ snapshots:
dependencies: dependencies:
esutils: 2.0.3 esutils: 2.0.3
dot-case@3.0.4:
dependencies:
no-case: 3.0.4
tslib: 2.6.2
emoji-regex@8.0.0: {} emoji-regex@8.0.0: {}
end-of-stream@1.4.4: end-of-stream@1.4.4:
dependencies: dependencies:
once: 1.4.0 once: 1.4.0
entities@4.5.0: {}
error-ex@1.3.2: error-ex@1.3.2:
dependencies: dependencies:
is-arrayish: 0.2.1 is-arrayish: 0.2.1
@ -3733,6 +3849,16 @@ snapshots:
dependencies: dependencies:
lru-cache: 6.0.0 lru-cache: 6.0.0
html-minifier-terser@7.2.0:
dependencies:
camel-case: 4.1.2
clean-css: 5.3.3
commander: 10.0.1
entities: 4.5.0
param-case: 3.0.4
relateurl: 0.2.7
terser: 5.31.0
html-tags@3.3.1: {} html-tags@3.3.1: {}
https-proxy-agent@5.0.1: https-proxy-agent@5.0.1:
@ -3974,6 +4100,10 @@ snapshots:
lodash.truncate@4.4.2: {} lodash.truncate@4.4.2: {}
lower-case@2.0.2:
dependencies:
tslib: 2.6.2
lru-cache@6.0.0: lru-cache@6.0.0:
dependencies: dependencies:
yallist: 4.0.0 yallist: 4.0.0
@ -4071,6 +4201,11 @@ snapshots:
natural-compare@1.4.0: {} natural-compare@1.4.0: {}
no-case@3.0.4:
dependencies:
lower-case: 2.0.2
tslib: 2.6.2
node-fetch@2.6.7: node-fetch@2.6.7:
dependencies: dependencies:
whatwg-url: 5.0.0 whatwg-url: 5.0.0
@ -4168,6 +4303,11 @@ snapshots:
pako@1.0.11: {} pako@1.0.11: {}
param-case@3.0.4:
dependencies:
dot-case: 3.0.4
tslib: 2.6.2
parent-module@1.0.1: parent-module@1.0.1:
dependencies: dependencies:
callsites: 3.1.0 callsites: 3.1.0
@ -4179,6 +4319,11 @@ snapshots:
json-parse-even-better-errors: 2.3.1 json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4 lines-and-columns: 1.2.4
pascal-case@3.1.2:
dependencies:
no-case: 3.0.4
tslib: 2.6.2
pascalcase@0.1.1: {} pascalcase@0.1.1: {}
path-exists@4.0.0: {} path-exists@4.0.0: {}
@ -4296,6 +4441,8 @@ snapshots:
es-errors: 1.3.0 es-errors: 1.3.0
set-function-name: 2.0.2 set-function-name: 2.0.2
relateurl@0.2.7: {}
require-directory@2.1.1: {} require-directory@2.1.1: {}
require-from-string@2.0.2: {} require-from-string@2.0.2: {}
@ -4605,6 +4752,13 @@ snapshots:
inherits: 2.0.4 inherits: 2.0.4
readable-stream: 3.6.2 readable-stream: 3.6.2
terser@5.31.0:
dependencies:
'@jridgewell/source-map': 0.3.6
acorn: 8.10.0
commander: 2.20.3
source-map-support: 0.5.21
text-table@0.2.0: {} text-table@0.2.0: {}
through@2.3.8: {} through@2.3.8: {}
@ -4646,6 +4800,8 @@ snapshots:
tslib@1.14.1: {} tslib@1.14.1: {}
tslib@2.6.2: {}
tsutils@3.21.0(typescript@5.4.5): tsutils@3.21.0(typescript@5.4.5):
dependencies: dependencies:
tslib: 1.14.1 tslib: 1.14.1

View file

@ -20,8 +20,10 @@ import "../suppressExperimentalWarnings.js";
import "../checkNodeVersion.js"; import "../checkNodeVersion.js";
import { exec, execSync } from "child_process"; import { exec, execSync } from "child_process";
import esbuild from "esbuild";
import { constants as FsConstants, readFileSync } from "fs"; import { constants as FsConstants, readFileSync } from "fs";
import { access, readdir, readFile } from "fs/promises"; import { access, readdir, readFile } from "fs/promises";
import { minify as minifyHtml } from "html-minifier-terser";
import { join, relative } from "path"; import { join, relative } from "path";
import { promisify } from "util"; import { promisify } from "util";
@ -161,21 +163,60 @@ export const gitRemotePlugin = {
/** /**
* @type {import("esbuild").Plugin} * @type {import("esbuild").Plugin}
*/ */
export const fileIncludePlugin = { export const fileUrlPlugin = {
name: "file-include-plugin", name: "file-uri-plugin",
setup: build => { setup: build => {
const filter = /^~fileContent\/.+$/; const filter = /^file:\/\/.+$/;
build.onResolve({ filter }, args => ({ build.onResolve({ filter }, args => ({
namespace: "include-file", namespace: "file-uri",
path: args.path, path: args.path,
pluginData: { pluginData: {
path: join(args.resolveDir, args.path.slice("include-file/".length)) uri: args.path,
path: join(args.resolveDir, args.path.slice("file://".length).split("?")[0])
} }
})); }));
build.onLoad({ filter, namespace: "include-file" }, async ({ pluginData: { path } }) => { build.onLoad({ filter, namespace: "file-uri" }, async ({ pluginData: { path, uri } }) => {
const [name, format] = path.split(";"); const { searchParams } = new URL(uri);
const base64 = searchParams.has("base64");
const minify = isStandalone === "true" && searchParams.has("minify");
const noTrim = searchParams.get("trim") === "false";
const encoding = base64 ? "base64" : "utf-8";
let content;
if (!minify) {
content = await readFile(path, encoding);
if (!noTrim) content = content.trimEnd();
} else {
if (path.endsWith(".html")) {
content = await minifyHtml(await readFile(path, "utf-8"), {
collapseWhitespace: true,
removeComments: true,
minifyCSS: true,
minifyJS: true,
removeEmptyAttributes: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
});
} else if (/[mc]?[jt]sx?$/.test(path)) {
const res = await esbuild.build({
entryPoints: [path],
write: false,
minify: true
});
content = res.outputFiles[0].text;
} else {
throw new Error(`Don't know how to minify file type: ${path}`);
}
if (base64)
content = Buffer.from(content).toString("base64");
}
return { return {
contents: `export default ${JSON.stringify(await readFile(name, format ?? "utf-8"))}` contents: `export default ${JSON.stringify(content)}`
}; };
}); });
} }
@ -217,7 +258,7 @@ export const commonOpts = {
sourcemap: watch ? "inline" : "", sourcemap: watch ? "inline" : "",
legalComments: "linked", legalComments: "linked",
banner, banner,
plugins: [fileIncludePlugin, gitHashPlugin, gitRemotePlugin, stylePlugin], plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin],
external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"], external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"],
inject: ["./scripts/build/inject/react.mjs"], inject: ["./scripts/build/inject/react.mjs"],
jsxFactory: "VencordCreateElement", jsxFactory: "VencordCreateElement",

View file

@ -23,12 +23,11 @@ import "./settings";
import { debounce } from "@shared/debounce"; import { debounce } from "@shared/debounce";
import { IpcEvents } from "@shared/IpcEvents"; import { IpcEvents } from "@shared/IpcEvents";
import { BrowserWindow, ipcMain, shell, systemPreferences } from "electron"; import { BrowserWindow, ipcMain, shell, systemPreferences } from "electron";
import monacoHtml from "file://monacoWin.html?minify&base64";
import { FSWatcher, mkdirSync, watch, writeFileSync } from "fs"; import { FSWatcher, mkdirSync, watch, writeFileSync } from "fs";
import { open, readdir, readFile } from "fs/promises"; import { open, readdir, readFile } from "fs/promises";
import { join, normalize } from "path"; import { join, normalize } from "path";
import monacoHtml from "~fileContent/monacoWin.html;base64";
import { getThemeInfo, stripBOM, UserThemeHeader } from "./themes"; import { getThemeInfo, stripBOM, UserThemeHeader } from "./themes";
import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, THEMES_DIR } from "./utils/constants"; import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, THEMES_DIR } from "./utils/constants";
import { makeLinksOpenExternally } from "./utils/externalLinks"; import { makeLinksOpenExternally } from "./utils/externalLinks";

2
src/modules.d.ts vendored
View file

@ -38,7 +38,7 @@ declare module "~git-remote" {
export default remote; export default remote;
} }
declare module "~fileContent/*" { declare module "file://*" {
const content: string; const content: string;
export default content; export default content;
} }

View file

@ -17,119 +17,199 @@
*/ */
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getCurrentChannel, getCurrentGuild } from "@utils/discord";
import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy";
import { relaunch } from "@utils/native"; import { relaunch } from "@utils/native";
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
import definePlugin from "@utils/types"; import definePlugin, { PluginNative, StartAt } from "@utils/types";
import * as Webpack from "@webpack"; import * as Webpack from "@webpack";
import { extract, filters, findAll, search } from "@webpack"; import { extract, filters, findAll, findModuleId, search } from "@webpack";
import { React, ReactDOM } from "@webpack/common"; import * as Common from "@webpack/common";
import type { ComponentType } from "react"; import type { ComponentType } from "react";
const WEB_ONLY = (f: string) => () => { const DESKTOP_ONLY = (f: string) => () => {
throw new Error(`'${f}' is Discord Desktop only.`); throw new Error(`'${f}' is Discord Desktop only.`);
}; };
const define: typeof Object.defineProperty =
(obj, prop, desc) => {
if (Object.hasOwn(desc, "value"))
desc.writable = true;
return Object.defineProperty(obj, prop, {
configurable: true,
enumerable: true,
...desc
});
};
function makeShortcuts() {
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) {
const cache = new Map<string, unknown>();
return function (...filterProps: unknown[]) {
const cacheKey = String(filterProps);
if (cache.has(cacheKey)) return cache.get(cacheKey);
const matches = findAll(filterFactory(...filterProps));
const result = (() => {
switch (matches.length) {
case 0: return null;
case 1: return matches[0];
default:
const uniqueMatches = [...new Set(matches)];
if (uniqueMatches.length > 1)
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
return matches[0];
}
})();
if (result && cacheKey) cache.set(cacheKey, result);
return result;
};
}
let fakeRenderWin: WeakRef<Window> | undefined;
const find = newFindWrapper(f => f);
const findByProps = newFindWrapper(filters.byProps);
return {
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
wp: Webpack,
wpc: { getter: () => Webpack.cache },
wreq: { getter: () => Webpack.wreq },
wpsearch: search,
wpex: extract,
wpexs: (code: string) => extract(findModuleId(code)!),
find,
findAll: findAll,
findByProps,
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
findByCode: newFindWrapper(filters.byCode),
findAllByCode: (code: string) => findAll(filters.byCode(code)),
findComponentByCode: newFindWrapper(filters.componentByCode),
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
findStore: newFindWrapper(filters.byStoreName),
PluginsApi: { getter: () => Vencord.Plugins },
plugins: { getter: () => Vencord.Plugins.plugins },
Settings: { getter: () => Vencord.Settings },
Api: { getter: () => Vencord.Api },
Util: { getter: () => Vencord.Util },
reload: () => location.reload(),
restart: IS_WEB ? DESKTOP_ONLY("restart") : relaunch,
canonicalizeMatch,
canonicalizeReplace,
canonicalizeReplacement,
fakeRender: (component: ComponentType, props: any) => {
const prevWin = fakeRenderWin?.deref();
const win = prevWin?.closed === false
? prevWin
: window.open("about:blank", "Fake Render", "popup,width=500,height=500")!;
fakeRenderWin = new WeakRef(win);
win.focus();
const doc = win.document;
doc.body.style.margin = "1em";
if (!win.prepared) {
win.prepared = true;
[...document.querySelectorAll("style"), ...document.querySelectorAll("link[rel=stylesheet]")].forEach(s => {
const n = s.cloneNode(true) as HTMLStyleElement | HTMLLinkElement;
if (s.parentElement?.tagName === "HEAD")
doc.head.append(n);
else if (n.id?.startsWith("vencord-") || n.id?.startsWith("vcd-"))
doc.documentElement.append(n);
else
doc.body.append(n);
});
}
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
},
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
channel: { getter: () => getCurrentChannel(), preload: false },
channelId: { getter: () => Common.SelectedChannelStore.getChannelId(), preload: false },
guild: { getter: () => getCurrentGuild(), preload: false },
guildId: { getter: () => Common.SelectedGuildStore.getGuildId(), preload: false },
me: { getter: () => Common.UserStore.getCurrentUser(), preload: false },
meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false },
messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false }
};
}
function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) {
const currentVal = val.getter();
if (!currentVal || val.preload === false) return currentVal;
const value = currentVal[SYM_LAZY_GET]
? forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED]
: currentVal;
if (value) define(window.shortcutList, key, { value });
return value;
}
export default definePlugin({ export default definePlugin({
name: "ConsoleShortcuts", name: "ConsoleShortcuts",
description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.", description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.",
authors: [Devs.Ven], authors: [Devs.Ven],
getShortcuts() { startAt: StartAt.Init,
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) { start() {
const cache = new Map<string, unknown>(); const shortcuts = makeShortcuts();
window.shortcutList = {};
return function (...filterProps: unknown[]) { for (const [key, val] of Object.entries(shortcuts)) {
const cacheKey = String(filterProps); if ("getter" in val) {
if (cache.has(cacheKey)) return cache.get(cacheKey); define(window.shortcutList, key, {
get: () => loadAndCacheShortcut(key, val, true)
});
const matches = findAll(filterFactory(...filterProps)); define(window, key, {
get: () => window.shortcutList[key]
const result = (() => { });
switch (matches.length) { } else {
case 0: return null; window.shortcutList[key] = val;
case 1: return matches[0]; window[key] = val;
default: }
const uniqueMatches = [...new Set(matches)];
if (uniqueMatches.length > 1)
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
return matches[0];
}
})();
if (result && cacheKey) cache.set(cacheKey, result);
return result;
};
} }
let fakeRenderWin: WeakRef<Window> | undefined; // unproxy loaded modules
const find = newFindWrapper(f => f); Webpack.onceReady.then(() => {
const findByProps = newFindWrapper(filters.byProps); setTimeout(() => this.eagerLoad(false), 1000);
return {
...Vencord.Webpack.Common,
wp: Vencord.Webpack,
wpc: Webpack.wreq.c,
wreq: Webpack.wreq,
wpsearch: search,
wpex: extract,
wpexs: (code: string) => extract(Webpack.findModuleId(code)!),
find,
findAll,
findByProps,
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
findByCode: newFindWrapper(filters.byCode),
findAllByCode: (code: string) => findAll(filters.byCode(code)),
findComponentByCode: newFindWrapper(filters.componentByCode),
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
findStore: newFindWrapper(filters.byStoreName),
PluginsApi: Vencord.Plugins,
plugins: Vencord.Plugins.plugins,
Settings: Vencord.Settings,
Api: Vencord.Api,
reload: () => location.reload(),
restart: IS_WEB ? WEB_ONLY("restart") : relaunch,
canonicalizeMatch,
canonicalizeReplace,
canonicalizeReplacement,
fakeRender: (component: ComponentType, props: any) => {
const prevWin = fakeRenderWin?.deref();
const win = prevWin?.closed === false ? prevWin : window.open("about:blank", "Fake Render", "popup,width=500,height=500")!;
fakeRenderWin = new WeakRef(win);
win.focus();
const doc = win.document; if (!IS_WEB) {
doc.body.style.margin = "1em"; const Native = VencordNative.pluginHelpers.ConsoleShortcuts as PluginNative<typeof import("./native")>;
Native.initDevtoolsOpenEagerLoad();
if (!win.prepared) {
win.prepared = true;
[...document.querySelectorAll("style"), ...document.querySelectorAll("link[rel=stylesheet]")].forEach(s => {
const n = s.cloneNode(true) as HTMLStyleElement | HTMLLinkElement;
if (s.parentElement?.tagName === "HEAD")
doc.head.append(n);
else if (n.id?.startsWith("vencord-") || n.id?.startsWith("vcd-"))
doc.documentElement.append(n);
else
doc.body.append(n);
});
}
ReactDOM.render(React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
} }
}; });
}, },
start() { async eagerLoad(forceLoad: boolean) {
const shortcuts = this.getShortcuts(); await Webpack.onceReady;
window.shortcutList = shortcuts;
for (const [key, val] of Object.entries(shortcuts)) const shortcuts = makeShortcuts();
window[key] = val;
for (const [key, val] of Object.entries(shortcuts)) {
if (!Object.hasOwn(val, "getter") || (val as any).preload === false) continue;
try {
loadAndCacheShortcut(key, val, forceLoad);
} catch { } // swallow not found errors in DEV
}
}, },
stop() { stop() {
delete window.shortcutList; delete window.shortcutList;
for (const key in this.getShortcuts()) for (const key in makeShortcuts()) {
delete window[key]; delete window[key];
}
} }
}); });

View file

@ -0,0 +1,16 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { IpcMainInvokeEvent } from "electron";
export function initDevtoolsOpenEagerLoad(e: IpcMainInvokeEvent) {
const handleDevtoolsOpened = () => e.sender.executeJavaScript("Vencord.Plugins.plugins.ConsoleShortcuts.eagerLoad(true)");
if (e.sender.isDevToolsOpened())
handleDevtoolsOpened();
else
e.sender.once("devtools-opened", () => handleDevtoolsOpened());
}

View file

@ -18,7 +18,7 @@ export default definePlugin({
type: OptionType.SELECT, type: OptionType.SELECT,
options: [ options: [
{ {
label: "Ctrl+Enter (Enter or Shift+Enter for new line)", label: "Ctrl+Enter (Enter or Shift+Enter for new line) (cmd+enter on macOS)",
value: "ctrl+enter" value: "ctrl+enter"
}, },
{ {
@ -54,7 +54,7 @@ export default definePlugin({
result = event.shiftKey; result = event.shiftKey;
break; break;
case "ctrl+enter": case "ctrl+enter":
result = event.ctrlKey; result = navigator.platform.includes("Mac") ? event.metaKey : event.ctrlKey;
break; break;
case "enter": case "enter":
result = !event.shiftKey && !event.ctrlKey; result = !event.shiftKey && !event.ctrlKey;

View file

@ -182,8 +182,8 @@ export default definePlugin({
// add dearrow button // add dearrow button
{ {
match: /children:\[(?=null!=\i\?\i\.renderSuppressButton)/, match: /children:\[(?=null!=\i\?(\i)\.renderSuppressButton)/,
replace: "children:[$self.renderButton(this),", replace: "children:[$self.renderButton($1),",
predicate: () => !settings.store.hideButton predicate: () => !settings.store.hideButton
} }
] ]

View file

@ -0,0 +1,35 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { moment } from "@webpack/common";
export default definePlugin({
name: "DontRoundMyTimestamps",
authors: [Devs.Lexi],
description: "Always rounds relative timestamps down, so 7.6y becomes 7y instead of 8y",
start() {
moment.relativeTimeRounding(Math.floor);
},
stop() {
moment.relativeTimeRounding(Math.round);
}
});

View file

@ -813,7 +813,7 @@ export default definePlugin({
}, },
canUseEmote(e: Emoji, channelId: string) { canUseEmote(e: Emoji, channelId: string) {
if (e.type === "UNICODE") return true; if (e.type === 0) return true;
if (e.available === false) return false; if (e.available === false) return false;
const isUnusableRoleSubEmoji = RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmojiOriginal ?? RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmoji; const isUnusableRoleSubEmoji = RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmojiOriginal ?? RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmoji;

View file

@ -52,6 +52,8 @@ export default definePlugin({
getFriendSince(userId: string) { getFriendSince(userId: string) {
try { try {
if (!RelationshipStore.isFriend(userId)) return null;
return RelationshipStore.getSince(userId); return RelationshipStore.getSince(userId);
} catch (err) { } catch (err) {
new Logger("FriendsSince").error(err); new Logger("FriendsSince").error(err);
@ -60,6 +62,8 @@ export default definePlugin({
}, },
friendsSince: ErrorBoundary.wrap(({ userId, textClassName }: { userId: string; textClassName?: string; }) => { friendsSince: ErrorBoundary.wrap(({ userId, textClassName }: { userId: string; textClassName?: string; }) => {
if (!RelationshipStore.isFriend(userId)) return null;
const friendsSince = RelationshipStore.getSince(userId); const friendsSince = RelationshipStore.getSince(userId);
if (!friendsSince) return null; if (!friendsSince) return null;

View file

@ -67,15 +67,18 @@ export const Magnifier = ErrorBoundary.wrap<MagnifierProps>(({ instance, size: i
} }
}; };
const syncVideos = () => { const syncVideos = () => {
currentVideoElementRef.current!.currentTime = originalVideoElementRef.current!.currentTime; if (currentVideoElementRef.current && originalVideoElementRef.current)
currentVideoElementRef.current.currentTime = originalVideoElementRef.current.currentTime;
}; };
const updateMousePosition = (e: MouseEvent) => { const updateMousePosition = (e: MouseEvent) => {
if (!element.current) return;
if (instance.state.mouseOver && instance.state.mouseDown) { if (instance.state.mouseOver && instance.state.mouseDown) {
const offset = size.current / 2; const offset = size.current / 2;
const pos = { x: e.pageX, y: e.pageY }; const pos = { x: e.pageX, y: e.pageY };
const x = -((pos.x - element.current!.getBoundingClientRect().left) * zoom.current - offset); const x = -((pos.x - element.current.getBoundingClientRect().left) * zoom.current - offset);
const y = -((pos.y - element.current!.getBoundingClientRect().top) * zoom.current - offset); const y = -((pos.y - element.current.getBoundingClientRect().top) * zoom.current - offset);
setLensPosition({ x: e.x - offset, y: e.y - offset }); setLensPosition({ x: e.x - offset, y: e.y - offset });
setImagePosition({ x, y }); setImagePosition({ x, y });
setOpacity(1); setOpacity(1);
@ -184,6 +187,7 @@ export const Magnifier = ErrorBoundary.wrap<MagnifierProps>(({ instance, size: i
src={originalVideoElementRef.current?.src ?? instance.props.src} src={originalVideoElementRef.current?.src ?? instance.props.src}
autoPlay autoPlay
loop loop
muted
/> />
) : ( ) : (
<img <img

View file

@ -18,88 +18,82 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import presetQuotesText from "file://quotes.txt";
// These are Xor encrypted to prevent you from spoiling yourself when you read the source code. const presetQuotes = presetQuotesText.split("\n").map(quote => /^\s*[^#\s]/.test(quote) && quote.trim()).filter(Boolean) as string[];
// don't worry about it :P const noQuotesQuote = "Did you really disable all loading quotes? What a buffoon you are...";
const quotes = [
"Eyrokac",
"Rdcg$l`'k|~n",
'H`tf$d&iajo+d`{"',
"Sucqplh`(Eclhualva()&",
"Lncgmka'8KNMDC,shpanf'`x./,",
"Ioqweijnfn*IeuvfvAotkfxo./,",
'Hd{#cp\x7Ft$)nbd!{lq%mig~*\x7Fh`v#mk&sm{gx nd#idjb(a\x7Ffao"bja&amdkge!Rloìkhf)hyedfjjb*\'^hzdrdmm$lu\'|ao+mnqw$fijxh~bbmg#Tjmîefd+fnp#lpkffz5',
"h",
"sijklm&cam*rot\"hjjq'|ak\x7F xmv#wc'ep*mawmvvlrb(|ynr>\"Aqq&cgg-\x7F ugoh%rom)e\x7Fhdpp%$",
'Tnfb}"u\'~`nno!kp$vvhfzeyee"a}%Tfam*Xh`fls%Jboldos-"lj`&hn)~ce!`jcbct|)gdbhnf$wikm$zgaxkmc%afely+og"144?\'ign+iu%p$qisiefr gpfa$',
"Ndtfv%ahfgk+ghtf$|ir(|z' Oguaw&`ggdj mgw$|ir(me|n",
"(!ͣ³$͙ʐ'ͩ¹#",
"(ネ◗ロ◑,マ-2ャユ✬",
"Ynw#hjil(ze+psgwp|&sgmkr!",
"Tikmolh`(fl+a!dvjk\x7F'y|e\x7Fe/,-",
"3/3750?5><9>885:7",
"mdmt",
"Wdn`khc+(oxbeof",
'Ig"zkp*\'g{*xolglj`&~g|*gowg/$mgt(Eclm`.#ticf{l*xed"wl`&Kangj igbhqn\'d`dn `v#lqrw{3%$bhv-h|)kangj_imwhlhb',
"Tscmw%Tnoa~x",
"If#npus(ec`e!vl$lhsm{`ncu\"ekw&f(defeov-$Rnf|)sdupf$wcam{ceg!vl$du'D`d~x-\"jw%oi(okht-\"DJP)Kags,!mq$du'A|n sg`akrkq)~jkdl#pj&diefbnf\"jp)&@F\\*{ltq#Hlhrp'",
"Ynw$v`&cg`dl fml`%rhlhs*",
"Dnl$p%qhz{s' hv$w%hh|aceg!;#gpvt(fl+cndea`&dg|fon&v#wjjqm(",
"\ud83d)pft`gs(ec`e!13$qojmz#",
"a!njcmr'ide~nu\"lb%rheoedldpz$lu'gbkr",
"dn\"zkp&kgo4",
"hnpqkw",
"sn\"fau",
"Sn\"tmqnh}}*musvkaw&flf&+ldv$w%lr{}*aulr#vlao|)cetn\"jp$",
"Dxkmc%ot(hhxomwwai'{hln",
"hd{#}js&(pe~'sg#gprb(3#\"",
"hd{b${",
"<;vqkijbq33271:56<3799?24944:",
"Thof$lu'ofdn,!qsefc'az*bnrcma+&Om{o+iu\"`khct$)bnrd\"bcdoi&",
"snofplkb{)c'r\"lod'|f*aurv#cpno`abchijklmno",
"Wdn`khc'|f*eghl{%"
];
const settings = definePluginSettings({ const settings = definePluginSettings({
replaceEvents: { replaceEvents: {
description: "Replace Event Quotes too", description: "Should this plugin also apply during events with special event themed quotes? (e.g. Halloween)",
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,
default: true default: true
} },
enablePluginPresetQuotes: {
description: "Enable the quotes preset by this plugin",
type: OptionType.BOOLEAN,
default: true
},
enableDiscordPresetQuotes: {
description: "Enable Discord's preset quotes (including event quotes, during events)",
type: OptionType.BOOLEAN,
default: false
},
additionalQuotes: {
description: "Additional custom quotes to possibly appear, separated by the below delimiter",
type: OptionType.STRING,
default: "",
},
additionalQuotesDelimiter: {
description: "Delimiter for additional quotes",
type: OptionType.STRING,
default: "|",
},
}); });
export default definePlugin({ export default definePlugin({
name: "LoadingQuotes", name: "LoadingQuotes",
description: "Replace Discords loading quotes", description: "Replace Discords loading quotes",
authors: [Devs.Ven, Devs.KraXen72], authors: [Devs.Ven, Devs.KraXen72, Devs.UlyssesZhan],
settings, settings,
patches: [ patches: [
{ {
find: ".LOADING_DID_YOU_KNOW}", find: ".LOADING_DID_YOU_KNOW",
replacement: [ replacement: [
{ {
match: /"_loadingText",function\(\)\{/, match: /"_loadingText".+?(?=(\i)\[.{0,10}\.random)/,
replace: "$&return $self.quote;", replace: "$&$self.mutateQuotes($1),"
}, },
{ {
match: /"_eventLoadingText",function\(\)\{/, match: /"_eventLoadingText".+?(?=(\i)\[.{0,10}\.random)/,
replace: "$&return $self.quote;", replace: "$&$self.mutateQuotes($1),",
predicate: () => settings.store.replaceEvents predicate: () => settings.store.replaceEvents
} }
], ]
}, },
], ],
xor(quote: string) { mutateQuotes(quotes: string[]) {
const key = "read if cute"; try {
const codes = Array.from(quote, (s, i) => s.charCodeAt(0) ^ (i % key.length)); const { enableDiscordPresetQuotes, additionalQuotes, additionalQuotesDelimiter, enablePluginPresetQuotes } = settings.store;
return String.fromCharCode(...codes);
},
get quote() { if (!enableDiscordPresetQuotes)
return this.xor(quotes[Math.floor(Math.random() * quotes.length)]); quotes.length = 0;
if (enablePluginPresetQuotes)
quotes.push(...presetQuotes);
quotes.push(...additionalQuotes.split(additionalQuotesDelimiter).filter(Boolean));
if (!quotes.length)
quotes.push(noQuotesQuote);
} catch (e) {
new Logger("LoadingQuotes").error("Failed to mutate quotes", e);
}
} }
}); });

View file

@ -0,0 +1,37 @@
# Blank lines and lines starting with "#" are ignored
Explode
Read if cute
Have a nice day!
Starting Lightcord...
Loading 0BDFDB.plugin.js...
Installing BetterDiscord...
h
shhhhh did you know that you're my favourite user? But don't tell the others!!
Today's video is sponsored by Raid Shadow Legends, one of the biggest mobile role-playing games of 2019 and it's totally free!
Never gonna give you up, Never gonna let you down
( ͡° ͜ʖ ͡°)
(ノ◕ヮ◕)ノ*:・゚✧
You look so pretty today!
Thinking of a funny quote...
3.141592653589793
meow
Welcome, friend
If you, or someone you love, has Ligma, please see the Ligma health line at https://bit.ly/ligma_hotline
Trans Rights
Id just like to interject for a moment. What youre refering to as Linux, is in fact, GNU/Linux, or as Ive recently taken to calling it, GNU plus Linux.
You're doing good today!
Don't worry, it's nothing 9 cups of coffee couldn't solve!
<EFBFBD>(repeat like 30 times)
a light amount of tomfoolery is okay
do you love?
horror
so eepy
So without further ado, let's just jump right into it!
Dying is absolutely safe
hey you! you're cute :))
heya ~
<:trolley:997086295010594867>
Time is gone, space is insane. Here it comes, here again.
sometimes it's okay to just guhhhhhhhhhhhhhh
Welcome to nginx!

View file

@ -0,0 +1,5 @@
# MaskedLinkPaste
Pasting a link while you have text selected will paste your link as a masked link at that location
![](https://github.com/Vendicated/Vencord/assets/78964224/1d3be2c6-7957-44c9-92ec-551069d46c02)

View file

@ -0,0 +1,36 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Devs } from "@utils/constants.js";
import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack";
const linkRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
const { SlateTransforms } = findByPropsLazy("SlateTransforms");
export default definePlugin({
name: "MaskedLinkPaste",
authors: [Devs.TheSun],
description: "Pasting a link while having text selected will paste a hyperlink",
patches: [{
find: ".selection,preventEmojiSurrogates:",
replacement: {
match: /(?<=SlateTransforms.delete.{0,50})(\i)\.insertText\((\i)\)/,
replace: "$self.handlePaste($1, $2, () => $&)"
}
}],
handlePaste(editor, content: string, originalBehavior: () => void) {
if (content && linkRegex.test(content) && editor.operations?.[0]?.type === "remove_text") {
SlateTransforms.insertText(
editor,
`[${editor.operations[0].text}](${content})`
);
}
else originalBehavior();
}
});

View file

@ -97,6 +97,9 @@ export default definePlugin({
// Message wasn't received through gateway // Message wasn't received through gateway
if (!isNonNullish(nonce)) return null; if (!isNonNullish(nonce)) return null;
// Bots basically never send a nonce, and if someone does do it then it's usually not a snowflake
if (message.bot) return null;
let isDiscordKotlin = false; let isDiscordKotlin = false;
let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds
if (!showMillis) { if (!showMillis) {

View file

@ -3,6 +3,11 @@
color: var(--status-danger, #f04747) !important; color: var(--status-danger, #f04747) !important;
} }
/* Markdown title highlighting */
.messagelogger-deleted [class*="contents"] :is(h1, h2, h3) {
color: var(--status-danger, #f04747) !important;
}
/* Bot "thinking" text highlighting */ /* Bot "thinking" text highlighting */
.messagelogger-deleted [class*="colorStandard"] { .messagelogger-deleted [class*="colorStandard"] {
color: var(--status-danger, #f04747) !important; color: var(--status-danger, #f04747) !important;

View file

@ -37,7 +37,7 @@ const settings = definePluginSettings({
}); });
function search(src: string, engine: string) { function search(src: string, engine: string) {
open(engine + encodeURIComponent(src), "_blank"); open(engine + encodeURIComponent(src.trim()), "_blank");
} }
function makeSearchItem(src: string) { function makeSearchItem(src: string) {

View file

@ -0,0 +1,7 @@
# Summaries
Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations.
Note that this plugin can't fetch old summaries, it can only display ones created while your Discord is running with the plugin enabled.
![](https://github.com/Vendicated/Vencord/assets/45497981/bd931b0c-2e85-4c10-9f7c-8ba01eb55745)

View file

@ -0,0 +1,115 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { DataStore } from "@api/index";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { ChannelStore, GuildStore } from "@webpack/common";
const SummaryStore = findByPropsLazy("allSummaries", "findSummary");
const { createSummaryFromServer } = findByPropsLazy("createSummaryFromServer");
const settings = definePluginSettings({
summaryExpiryThresholdDays: {
type: OptionType.SLIDER,
description: "The time in days before a summary is removed. Note that only up to 50 summaries are kept per channel",
markers: [1, 3, 5, 7, 10, 15, 20, 25, 30],
stickToMarkers: false,
default: 3,
}
});
interface Summary {
count: number;
end_id: string;
id: string;
message_ids: string[];
people: string[];
source: number;
start_id: string;
summ_short: string;
topic: string;
type: number;
unsafe: boolean;
}
interface ChannelSummaries {
type: string;
channel_id: string;
guild_id: string;
summaries: Summary[];
// custom property
time?: number;
}
export default definePlugin({
name: "Summaries",
description: "Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations",
authors: [Devs.mantikafasi],
settings,
patches: [
{
find: "ChannelTypesSets.SUMMARIZEABLE.has",
replacement: {
match: /\i\.hasFeature\(\i\.GuildFeatures\.SUMMARIES_ENABLED\w+?\)/g,
replace: "true"
}
},
{
find: "RECEIVE_CHANNEL_SUMMARY(",
replacement: {
match: /shouldFetch\((\i),\i\){/,
replace: "$& if(!$self.shouldFetch($1)) return false;"
}
}
],
flux: {
CONVERSATION_SUMMARY_UPDATE(data) {
const incomingSummaries: ChannelSummaries[] = data.summaries.map((summary: any) => ({ ...createSummaryFromServer(summary), time: Date.now() }));
// idk if this is good for performance but it doesnt seem to be a problem in my experience
DataStore.update("summaries-data", summaries => {
summaries ??= {};
summaries[data.channel_id] ? summaries[data.channel_id].unshift(...incomingSummaries) : (summaries[data.channel_id] = incomingSummaries);
if (summaries[data.channel_id].length > 50)
summaries[data.channel_id] = summaries[data.channel_id].slice(0, 50);
return summaries;
});
}
},
async start() {
await DataStore.update("summaries-data", summaries => {
summaries ??= {};
for (const key of Object.keys(summaries)) {
for (let i = summaries[key].length - 1; i >= 0; i--) {
if (summaries[key][i].time < Date.now() - 1000 * 60 * 60 * 24 * settings.store.summaryExpiryThresholdDays) {
summaries[key].splice(i, 1);
}
}
if (summaries[key].length === 0) {
delete summaries[key];
}
}
Object.assign(SummaryStore.allSummaries(), summaries);
return summaries;
});
},
shouldFetch(channelId: string) {
const channel = ChannelStore.getChannel(channelId);
// SUMMARIES_ENABLED feature is not in discord-types
const guild = GuildStore.getGuild(channel.guild_id);
// @ts-ignore
return guild.hasFeature("SUMMARIES_ENABLED_GA");
}
});

View file

@ -21,8 +21,7 @@ import "./shiki.css";
import { enableStyle } from "@api/Styles"; import { enableStyle } from "@api/Styles";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import previewExampleText from "file://previewExample.tsx";
import previewExampleText from "~fileContent/previewExample.tsx";
import { shiki } from "./api/shiki"; import { shiki } from "./api/shiki";
import { createHighlighter } from "./components/Highlighter"; import { createHighlighter } from "./components/Highlighter";

View file

@ -27,10 +27,10 @@ import style from "./index.css?managed";
const API_URL = "https://usrbg.is-hardly.online/users"; const API_URL = "https://usrbg.is-hardly.online/users";
interface UsrbgApiReturn { interface UsrbgApiReturn {
endpoint: string endpoint: string;
bucket: string bucket: string;
prefix: string prefix: string;
users: Record<string, string> users: Record<string, string>;
} }
const settings = definePluginSettings({ const settings = definePluginSettings({
@ -73,6 +73,19 @@ export default definePlugin({
} }
] ]
}, },
{
find: /overrideBannerSrc:\i,profileType:/,
replacement: [
{
match: /(\i)\.premiumType/,
replace: "$self.premiumHook($1)||$&"
},
{
match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},overrideBannerSrc:)/,
replace: "$1.overrideBannerSrc=$self.useBannerHook($1);"
}
]
},
{ {
find: "\"data-selenium-video-tile\":", find: "\"data-selenium-video-tile\":",
predicate: () => settings.store.voiceBackground, predicate: () => settings.store.voiceBackground,
@ -123,7 +136,7 @@ export default definePlugin({
return !!this.data?.users[userId]; return !!this.data?.users[userId];
}, },
getImageUrl(userId: string): string|null { getImageUrl(userId: string): string | null {
if (!this.userHasBackground(userId)) return null; if (!this.userHasBackground(userId)) return null;
// We can assert that data exists because userHasBackground returned true // We can assert that data exists because userHasBackground returned true

View file

@ -0,0 +1,6 @@
# WatchTogetherAdblock
Block ads in the YouTube WatchTogether activity via AdGuard
Note that this only works for yourself, other users in the activity will still see ads.
Powered by a modified version of [Adguard's BlockYoutubeAdsShortcut](https://github.com/AdguardTeam/BlockYouTubeAdsShortcut)

View file

@ -0,0 +1,262 @@
/* eslint-disable */
/**
* This file is part of AdGuard's Block YouTube Ads (https://github.com/AdguardTeam/BlockYouTubeAdsShortcut).
*
* Copyright (C) AdGuard Team
*
* AdGuard's Block YouTube Ads is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AdGuard's Block YouTube Ads is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AdGuard's Block YouTube Ads. If not, see <http://www.gnu.org/licenses/>.
*/
const LOGO_ID = "block-youtube-ads-logo";
const hiddenCSS = [
"#__ffYoutube1",
"#__ffYoutube2",
"#__ffYoutube3",
"#__ffYoutube4",
"#feed-pyv-container",
"#feedmodule-PRO",
"#homepage-chrome-side-promo",
"#merch-shelf",
"#offer-module",
'#pla-shelf > ytd-pla-shelf-renderer[class="style-scope ytd-watch"]',
"#pla-shelf",
"#premium-yva",
"#promo-info",
"#promo-list",
"#promotion-shelf",
"#related > ytd-watch-next-secondary-results-renderer > #items > ytd-compact-promoted-video-renderer.ytd-watch-next-secondary-results-renderer",
"#search-pva",
"#shelf-pyv-container",
"#video-masthead",
"#watch-branded-actions",
"#watch-buy-urls",
"#watch-channel-brand-div",
"#watch7-branded-banner",
"#YtKevlarVisibilityIdentifier",
"#YtSparklesVisibilityIdentifier",
".carousel-offer-url-container",
".companion-ad-container",
".GoogleActiveViewElement",
'.list-view[style="margin: 7px 0pt;"]',
".promoted-sparkles-text-search-root-container",
".promoted-videos",
".searchView.list-view",
".sparkles-light-cta",
".watch-extra-info-column",
".watch-extra-info-right",
".ytd-carousel-ad-renderer",
".ytd-compact-promoted-video-renderer",
".ytd-companion-slot-renderer",
".ytd-merch-shelf-renderer",
".ytd-player-legacy-desktop-watch-ads-renderer",
".ytd-promoted-sparkles-text-search-renderer",
".ytd-promoted-video-renderer",
".ytd-search-pyv-renderer",
".ytd-video-masthead-ad-v3-renderer",
".ytp-ad-action-interstitial-background-container",
".ytp-ad-action-interstitial-slot",
".ytp-ad-image-overlay",
".ytp-ad-overlay-container",
".ytp-ad-progress",
".ytp-ad-progress-list",
'[class*="ytd-display-ad-"]',
'[layout*="display-ad-"]',
'a[href^="http://www.youtube.com/cthru?"]',
'a[href^="https://www.youtube.com/cthru?"]',
"ytd-action-companion-ad-renderer",
"ytd-banner-promo-renderer",
"ytd-compact-promoted-video-renderer",
"ytd-companion-slot-renderer",
"ytd-display-ad-renderer",
"ytd-promoted-sparkles-text-search-renderer",
"ytd-promoted-sparkles-web-renderer",
"ytd-search-pyv-renderer",
"ytd-single-option-survey-renderer",
"ytd-video-masthead-ad-advertiser-info-renderer",
"ytd-video-masthead-ad-v3-renderer",
"YTM-PROMOTED-VIDEO-RENDERER",
];
/**
* Adds CSS to the page
*/
const hideElements = () => {
const selectors = hiddenCSS;
if (!selectors) {
return;
}
const rule = selectors.join(", ") + " { display: none!important; }";
const style = document.createElement("style");
style.innerHTML = rule;
document.head.appendChild(style);
};
/**
* Calls the "callback" function on every DOM change, but not for the tracked events
* @param {Function} callback callback function
*/
const observeDomChanges = callback => {
const domMutationObserver = new MutationObserver(mutations => {
callback(mutations);
});
domMutationObserver.observe(document.documentElement, {
childList: true,
subtree: true,
});
};
/**
* This function is supposed to be called on every DOM change
*/
const hideDynamicAds = () => {
const elements = document.querySelectorAll("#contents > ytd-rich-item-renderer ytd-display-ad-renderer");
if (elements.length === 0) {
return;
}
elements.forEach(el => {
if (el.parentNode && el.parentNode.parentNode) {
const parent = el.parentNode.parentNode;
if (parent.localName === "ytd-rich-item-renderer") {
parent.style.display = "none";
}
}
});
};
/**
* This function checks if the video ads are currently running
* and auto-clicks the skip button.
*/
const autoSkipAds = () => {
// If there's a video that plays the ad at this moment, scroll this ad
if (document.querySelector(".ad-showing")) {
const video = document.querySelector("video");
if (video && video.duration) {
video.currentTime = video.duration;
// Skip button should appear after that,
// now simply click it automatically
setTimeout(() => {
const skipBtn = document.querySelector("button.ytp-ad-skip-button");
if (skipBtn) {
skipBtn.click();
}
}, 100);
}
}
};
/**
* This function overrides a property on the specified object.
*
* @param {object} obj object to look for properties in
* @param {string} propertyName property to override
* @param {*} overrideValue value to set
*/
const overrideObject = (obj, propertyName, overrideValue) => {
if (!obj) {
return false;
}
let overriden = false;
for (const key in obj) {
// eslint-disable-next-line no-prototype-builtins
if (obj.hasOwnProperty(key) && key === propertyName) {
obj[key] = overrideValue;
overriden = true;
// eslint-disable-next-line no-prototype-builtins
} else if (obj.hasOwnProperty(key) && typeof obj[key] === "object") {
if (overrideObject(obj[key], propertyName, overrideValue)) {
overriden = true;
}
}
}
return overriden;
};
/**
* Overrides JSON.parse and Response.json functions.
* Examines these functions arguments, looks for properties with the specified name there
* and if it exists, changes it's value to what was specified.
*
* @param {string} propertyName name of the property
* @param {*} overrideValue new value for the property
*/
const jsonOverride = (propertyName, overrideValue) => {
const nativeJSONParse = JSON.parse;
JSON.parse = (...args) => {
const obj = nativeJSONParse.apply(this, args);
// Override it's props and return back to the caller
overrideObject(obj, propertyName, overrideValue);
return obj;
};
// Override Response.prototype.json
const nativeResponseJson = Response.prototype.json;
Response.prototype.json = new Proxy(nativeResponseJson, {
apply(...args) {
// Call the target function, get the original Promise
const promise = Reflect.apply(...args);
// Create a new one and override the JSON inside
return new Promise((resolve, reject) => {
promise.then(data => {
overrideObject(data, propertyName, overrideValue);
resolve(data);
}).catch(error => reject(error));
});
},
});
};
const addAdGuardLogoStyle = () => { };
const addAdGuardLogo = () => {
if (document.getElementById(LOGO_ID)) {
return;
}
const logo = document.createElement("span");
logo.innerHTML = "__logo_text__";
logo.setAttribute("id", LOGO_ID);
if (window.location.hostname === "m.youtube.com") {
const btn = document.querySelector("header.mobile-topbar-header > button");
if (btn) {
btn.parentNode?.insertBefore(logo, btn.nextSibling);
addAdGuardLogoStyle();
}
} else if (window.location.hostname === "www.youtube.com") {
const code = document.getElementById("country-code");
if (code) {
code.innerHTML = "";
code.appendChild(logo);
addAdGuardLogoStyle();
}
} else if (window.location.hostname === "music.youtube.com") {
const el = document.querySelector(".ytmusic-nav-bar#left-content");
if (el) {
el.appendChild(logo);
addAdGuardLogoStyle();
}
} else if (window.location.hostname === "www.youtube-nocookie.com") {
const code = document.querySelector("#yt-masthead #logo-container .content-region");
if (code) {
code.innerHTML = "";
code.appendChild(logo);
addAdGuardLogoStyle();
}
}
};
// Removes ads metadata from YouTube XHR requests
jsonOverride("adPlacements", []);
jsonOverride("playerAds", []);
// Applies CSS that hides YouTube ad elements
hideElements();
// Some changes should be re-evaluated on every page change
addAdGuardLogo();
hideDynamicAds();
autoSkipAds();
observeDomChanges(() => {
addAdGuardLogo();
hideDynamicAds();
autoSkipAds();
});

View file

@ -0,0 +1,15 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
// The entire code of this plugin can be found in native.ts
export default definePlugin({
name: "WatchTogetherAdblock",
description: "Block ads in the YouTube WatchTogether activity via AdGuard",
authors: [Devs.ImLvna],
});

View file

@ -0,0 +1,21 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { RendererSettings } from "@main/settings";
import { app } from "electron";
import adguard from "file://adguard.js?minify";
app.on("browser-window-created", (_, win) => {
win.webContents.on("frame-created", (_, { frame }) => {
frame.once("dom-ready", () => {
if (frame.url.includes("discordsays") && frame.url.includes("youtube.com")) {
if (!RendererSettings.store.plugins?.WatchTogetherAdblock?.enabled) return;
frame.executeJavaScript(adguard);
}
});
});
});

View file

@ -186,7 +186,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({
id: 296776625432035328n, id: 296776625432035328n,
}, },
TheSun: { TheSun: {
name: "ActuallyTheSun", name: "sunnie",
id: 406028027768733696n id: 406028027768733696n
}, },
axyie: { axyie: {
@ -402,6 +402,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "maisy", name: "maisy",
id: 257109471589957632n, id: 257109471589957632n,
}, },
Lexi: {
name: "Lexi",
id: 506101469787717658n
},
Mopi: { Mopi: {
name: "Mopi", name: "Mopi",
id: 1022189106614243350n id: 1022189106614243350n

View file

@ -35,8 +35,8 @@ const unconfigurable = ["arguments", "caller", "prototype"];
const handler: ProxyHandler<any> = {}; const handler: ProxyHandler<any> = {};
const kGET = Symbol.for("vencord.lazy.get"); export const SYM_LAZY_GET = Symbol.for("vencord.lazy.get");
const kCACHE = Symbol.for("vencord.lazy.cached"); export const SYM_LAZY_CACHED = Symbol.for("vencord.lazy.cached");
for (const method of [ for (const method of [
"apply", "apply",
@ -53,11 +53,11 @@ for (const method of [
"setPrototypeOf" "setPrototypeOf"
]) { ]) {
handler[method] = handler[method] =
(target: any, ...args: any[]) => Reflect[method](target[kGET](), ...args); (target: any, ...args: any[]) => Reflect[method](target[SYM_LAZY_GET](), ...args);
} }
handler.ownKeys = target => { handler.ownKeys = target => {
const v = target[kGET](); const v = target[SYM_LAZY_GET]();
const keys = Reflect.ownKeys(v); const keys = Reflect.ownKeys(v);
for (const key of unconfigurable) { for (const key of unconfigurable) {
if (!keys.includes(key)) keys.push(key); if (!keys.includes(key)) keys.push(key);
@ -69,7 +69,7 @@ handler.getOwnPropertyDescriptor = (target, p) => {
if (typeof p === "string" && unconfigurable.includes(p)) if (typeof p === "string" && unconfigurable.includes(p))
return Reflect.getOwnPropertyDescriptor(target, p); return Reflect.getOwnPropertyDescriptor(target, p);
const descriptor = Reflect.getOwnPropertyDescriptor(target[kGET](), p); const descriptor = Reflect.getOwnPropertyDescriptor(target[SYM_LAZY_GET](), p);
if (descriptor) Object.defineProperty(target, p, descriptor); if (descriptor) Object.defineProperty(target, p, descriptor);
return descriptor; return descriptor;
@ -92,31 +92,34 @@ export function proxyLazy<T>(factory: () => T, attempts = 5, isChild = false): T
let tries = 0; let tries = 0;
const proxyDummy = Object.assign(function () { }, { const proxyDummy = Object.assign(function () { }, {
[kCACHE]: void 0 as T | undefined, [SYM_LAZY_CACHED]: void 0 as T | undefined,
[kGET]() { [SYM_LAZY_GET]() {
if (!proxyDummy[kCACHE] && attempts > tries++) { if (!proxyDummy[SYM_LAZY_CACHED] && attempts > tries++) {
proxyDummy[kCACHE] = factory(); proxyDummy[SYM_LAZY_CACHED] = factory();
if (!proxyDummy[kCACHE] && attempts === tries) if (!proxyDummy[SYM_LAZY_CACHED] && attempts === tries)
console.error("Lazy factory failed:", factory); console.error("Lazy factory failed:", factory);
} }
return proxyDummy[kCACHE]; return proxyDummy[SYM_LAZY_CACHED];
} }
}); });
return new Proxy(proxyDummy, { return new Proxy(proxyDummy, {
...handler, ...handler,
get(target, p, receiver) { get(target, p, receiver) {
if (p === SYM_LAZY_CACHED || p === SYM_LAZY_GET)
return Reflect.get(target, p, receiver);
// if we're still in the same tick, it means the lazy was immediately used. // if we're still in the same tick, it means the lazy was immediately used.
// thus, we lazy proxy the get access to make things like destructuring work as expected // thus, we lazy proxy the get access to make things like destructuring work as expected
// meow here will also be a lazy // meow here will also be a lazy
// `const { meow } = findByPropsLazy("meow");` // `const { meow } = findByPropsLazy("meow");`
if (!isChild && isSameTick) if (!isChild && isSameTick)
return proxyLazy( return proxyLazy(
() => Reflect.get(target[kGET](), p, receiver), () => Reflect.get(target[SYM_LAZY_GET](), p, receiver),
attempts, attempts,
true true
); );
const lazyTarget = target[kGET](); const lazyTarget = target[SYM_LAZY_GET]();
if (typeof lazyTarget === "object" || typeof lazyTarget === "function") { if (typeof lazyTarget === "object" || typeof lazyTarget === "function") {
return Reflect.get(lazyTarget, p, receiver); return Reflect.get(lazyTarget, p, receiver);
} }

File diff suppressed because one or more lines are too long

View file

@ -63,7 +63,7 @@ export interface CustomEmoji {
originalName?: string; originalName?: string;
require_colons: boolean; require_colons: boolean;
roles: string[]; roles: string[];
type: "GUILD_EMOJI"; type: 1;
} }
export interface UnicodeEmoji { export interface UnicodeEmoji {
@ -75,7 +75,7 @@ export interface UnicodeEmoji {
}; };
index: number; index: number;
surrogates: string; surrogates: string;
type: "UNICODE"; type: 0;
uniqueName: string; uniqueName: string;
useSpriteSheet: boolean; useSpriteSheet: boolean;
get allNamesString(): string; get allNamesString(): string;