mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-01-10 09:56:24 +00:00
Merge branch 'dev' into openMoreConnections
This commit is contained in:
commit
162be49239
50 changed files with 2045 additions and 1439 deletions
|
@ -1,98 +0,0 @@
|
||||||
{
|
|
||||||
"root": true,
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"ignorePatterns": ["dist", "browser", "packages/vencord-types"],
|
|
||||||
"plugins": [
|
|
||||||
"@typescript-eslint",
|
|
||||||
"simple-header",
|
|
||||||
"simple-import-sort",
|
|
||||||
"unused-imports",
|
|
||||||
"path-alias"
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"import/resolver": {
|
|
||||||
"alias": {
|
|
||||||
"map": [
|
|
||||||
["@webpack", "./src/webpack"],
|
|
||||||
["@webpack/common", "./src/webpack/common"],
|
|
||||||
["@utils", "./src/utils"],
|
|
||||||
["@api", "./src/api"],
|
|
||||||
["@components", "./src/components"]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
// Since it's only been a month and Vencord has already been stolen
|
|
||||||
// by random skids who rebranded it to "AlphaCord" and erased all license
|
|
||||||
// information
|
|
||||||
"simple-header/header": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"files": ["scripts/header-new.txt", "scripts/header-old.txt"],
|
|
||||||
"templates": { "author": [".*", "Vendicated and contributors"] }
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"quotes": ["error", "double", { "avoidEscape": true }],
|
|
||||||
"jsx-quotes": ["error", "prefer-double"],
|
|
||||||
"no-mixed-spaces-and-tabs": "error",
|
|
||||||
"indent": ["error", 4, { "SwitchCase": 1 }],
|
|
||||||
"arrow-parens": ["error", "as-needed"],
|
|
||||||
"eol-last": ["error", "always"],
|
|
||||||
"@typescript-eslint/func-call-spacing": ["error", "never"],
|
|
||||||
"no-multi-spaces": "error",
|
|
||||||
"no-trailing-spaces": "error",
|
|
||||||
"no-whitespace-before-property": "error",
|
|
||||||
"semi": ["error", "always"],
|
|
||||||
"semi-style": ["error", "last"],
|
|
||||||
"space-in-parens": ["error", "never"],
|
|
||||||
"block-spacing": ["error", "always"],
|
|
||||||
"object-curly-spacing": ["error", "always"],
|
|
||||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
|
||||||
"spaced-comment": ["error", "always", { "markers": ["!"] }],
|
|
||||||
"yoda": "error",
|
|
||||||
"prefer-destructuring": ["error", {
|
|
||||||
"VariableDeclarator": { "array": false, "object": true },
|
|
||||||
"AssignmentExpression": { "array": false, "object": false }
|
|
||||||
}],
|
|
||||||
"operator-assignment": ["error", "always"],
|
|
||||||
"no-useless-computed-key": "error",
|
|
||||||
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
|
||||||
"no-invalid-regexp": "error",
|
|
||||||
"no-constant-condition": ["error", { "checkLoops": false }],
|
|
||||||
"no-duplicate-imports": "error",
|
|
||||||
"no-extra-semi": "error",
|
|
||||||
"dot-notation": "error",
|
|
||||||
"no-useless-escape": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"extra": "i"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-fallthrough": "error",
|
|
||||||
"for-direction": "error",
|
|
||||||
"no-async-promise-executor": "error",
|
|
||||||
"no-cond-assign": "error",
|
|
||||||
"no-dupe-else-if": "error",
|
|
||||||
"no-duplicate-case": "error",
|
|
||||||
"no-irregular-whitespace": "error",
|
|
||||||
"no-loss-of-precision": "error",
|
|
||||||
"no-misleading-character-class": "error",
|
|
||||||
"no-prototype-builtins": "error",
|
|
||||||
"no-regex-spaces": "error",
|
|
||||||
"no-shadow-restricted-names": "error",
|
|
||||||
"no-unexpected-multiline": "error",
|
|
||||||
"no-unsafe-optional-chaining": "error",
|
|
||||||
"no-useless-backreference": "error",
|
|
||||||
"use-isnan": "error",
|
|
||||||
"prefer-const": "error",
|
|
||||||
"prefer-spread": "error",
|
|
||||||
|
|
||||||
"simple-import-sort/imports": "error",
|
|
||||||
"simple-import-sort/exports": "error",
|
|
||||||
|
|
||||||
"unused-imports/no-unused-imports": "error",
|
|
||||||
|
|
||||||
"path-alias/no-relative": "error"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"extends": "stylelint-config-standard",
|
"extends": "stylelint-config-standard",
|
||||||
"rules": {
|
"rules": {
|
||||||
"indentation": 4,
|
|
||||||
"selector-class-pattern": [
|
"selector-class-pattern": [
|
||||||
"^[a-z][a-zA-Z0-9]*(-[a-z0-9][a-zA-Z0-9]*)*$",
|
"^[a-z][a-zA-Z0-9]*(-[a-z0-9][a-zA-Z0-9]*)*$",
|
||||||
{
|
{
|
||||||
|
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -14,8 +14,6 @@
|
||||||
"typescript.preferences.quoteStyle": "double",
|
"typescript.preferences.quoteStyle": "double",
|
||||||
"javascript.preferences.quoteStyle": "double",
|
"javascript.preferences.quoteStyle": "double",
|
||||||
|
|
||||||
"eslint.experimental.useFlatConfig": false,
|
|
||||||
|
|
||||||
"gitlens.remotes": [
|
"gitlens.remotes": [
|
||||||
{
|
{
|
||||||
"domain": "codeberg.org",
|
"domain": "codeberg.org",
|
||||||
|
|
126
eslint.config.mjs
Normal file
126
eslint.config.mjs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import stylistic from "@stylistic/eslint-plugin";
|
||||||
|
import pathAlias from "eslint-plugin-path-alias";
|
||||||
|
import header from "eslint-plugin-simple-header";
|
||||||
|
import simpleImportSort from "eslint-plugin-simple-import-sort";
|
||||||
|
import unusedImports from "eslint-plugin-unused-imports";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ["dist", "browser", "packages/vencord-types"] },
|
||||||
|
{
|
||||||
|
files: ["src/**/*.{tsx,ts,mts,mjs,js,jsx}", "eslint.config.mjs"],
|
||||||
|
plugins: {
|
||||||
|
"simple-header": header,
|
||||||
|
"@stylistic": stylistic,
|
||||||
|
"@typescript-eslint": tseslint.plugin,
|
||||||
|
"simple-import-sort": simpleImportSort,
|
||||||
|
"unused-imports": unusedImports,
|
||||||
|
"path-alias": pathAlias,
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
"import/resolver": {
|
||||||
|
map: [
|
||||||
|
["@webpack", "./src/webpack"],
|
||||||
|
["@webpack/common", "./src/webpack/common"],
|
||||||
|
["@utils", "./src/utils"],
|
||||||
|
["@api", "./src/api"],
|
||||||
|
["@components", "./src/components"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
parser: tseslint.parser,
|
||||||
|
parserOptions: {
|
||||||
|
project: ["./tsconfig.json"],
|
||||||
|
tsconfigRootDir: import.meta.dirname
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
/*
|
||||||
|
* Since it's only been a month and Vencord has already been stolen
|
||||||
|
* by random skids who rebranded it to "AlphaCord" and erased all license
|
||||||
|
* information
|
||||||
|
*/
|
||||||
|
"simple-header/header": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"files": ["scripts/header-new.txt", "scripts/header-old.txt"],
|
||||||
|
"templates": { "author": [".*", "Vendicated and contributors"] }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Style Rules
|
||||||
|
"@stylistic/jsx-quotes": ["error", "prefer-double"],
|
||||||
|
"@stylistic/quotes": ["error", "double", { "avoidEscape": true }],
|
||||||
|
"@stylistic/no-mixed-spaces-and-tabs": "error",
|
||||||
|
"@stylistic/arrow-parens": ["error", "as-needed"],
|
||||||
|
"@stylistic/eol-last": ["error", "always"],
|
||||||
|
"@stylistic/no-multi-spaces": "error",
|
||||||
|
"@stylistic/no-trailing-spaces": "error",
|
||||||
|
"@stylistic/no-whitespace-before-property": "error",
|
||||||
|
"@stylistic/semi": ["error", "always"],
|
||||||
|
"@stylistic/semi-style": ["error", "last"],
|
||||||
|
"@stylistic/space-in-parens": ["error", "never"],
|
||||||
|
"@stylistic/block-spacing": ["error", "always"],
|
||||||
|
"@stylistic/object-curly-spacing": ["error", "always"],
|
||||||
|
"@stylistic/spaced-comment": ["error", "always", { "markers": ["!"] }],
|
||||||
|
"@stylistic/no-extra-semi": "error",
|
||||||
|
|
||||||
|
// TS Rules
|
||||||
|
"@stylistic/func-call-spacing": ["error", "never"],
|
||||||
|
|
||||||
|
// ESLint Rules
|
||||||
|
"yoda": "error",
|
||||||
|
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||||
|
"prefer-destructuring": ["error", {
|
||||||
|
"VariableDeclarator": { "array": false, "object": true },
|
||||||
|
"AssignmentExpression": { "array": false, "object": false }
|
||||||
|
}],
|
||||||
|
"operator-assignment": ["error", "always"],
|
||||||
|
"no-useless-computed-key": "error",
|
||||||
|
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
||||||
|
"no-invalid-regexp": "error",
|
||||||
|
"no-constant-condition": ["error", { "checkLoops": false }],
|
||||||
|
"no-duplicate-imports": "error",
|
||||||
|
"dot-notation": "error",
|
||||||
|
"no-useless-escape": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"extra": "i"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-fallthrough": "error",
|
||||||
|
"for-direction": "error",
|
||||||
|
"no-async-promise-executor": "error",
|
||||||
|
"no-cond-assign": "error",
|
||||||
|
"no-dupe-else-if": "error",
|
||||||
|
"no-duplicate-case": "error",
|
||||||
|
"no-irregular-whitespace": "error",
|
||||||
|
"no-loss-of-precision": "error",
|
||||||
|
"no-misleading-character-class": "error",
|
||||||
|
"no-prototype-builtins": "error",
|
||||||
|
"no-regex-spaces": "error",
|
||||||
|
"no-shadow-restricted-names": "error",
|
||||||
|
"no-unexpected-multiline": "error",
|
||||||
|
"no-unsafe-optional-chaining": "error",
|
||||||
|
"no-useless-backreference": "error",
|
||||||
|
"use-isnan": "error",
|
||||||
|
"prefer-const": "error",
|
||||||
|
"prefer-spread": "error",
|
||||||
|
|
||||||
|
// Plugin Rules
|
||||||
|
"simple-import-sort/imports": "error",
|
||||||
|
"simple-import-sort/exports": "error",
|
||||||
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
"path-alias/no-relative": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
62
package.json
62
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.9.6",
|
"version": "1.9.7",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
"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",
|
||||||
"uninject": "node scripts/runInstaller.mjs",
|
"uninject": "node scripts/runInstaller.mjs",
|
||||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern src/userplugins",
|
"lint": "eslint",
|
||||||
"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 buildStandalone && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson",
|
"test": "pnpm buildStandalone && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson",
|
||||||
|
@ -35,53 +35,53 @@
|
||||||
"testTsc": "tsc --noEmit"
|
"testTsc": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sapphi-red/web-noise-suppressor": "0.3.3",
|
"@sapphi-red/web-noise-suppressor": "0.3.5",
|
||||||
"@vap/core": "0.0.12",
|
"@vap/core": "0.0.12",
|
||||||
"@vap/shiki": "0.10.5",
|
"@vap/shiki": "0.10.5",
|
||||||
"eslint-plugin-simple-header": "^1.0.2",
|
"fflate": "^0.8.2",
|
||||||
"fflate": "^0.7.4",
|
|
||||||
"gifenc": "github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3",
|
"gifenc": "github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3",
|
||||||
"monaco-editor": "^0.50.0",
|
"monaco-editor": "^0.50.0",
|
||||||
"nanoid": "^4.0.2",
|
"nanoid": "^5.0.7",
|
||||||
"virtual-merge": "^1.0.1"
|
"virtual-merge": "^1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chrome": "^0.0.246",
|
"@stylistic/eslint-plugin": "^2.6.1",
|
||||||
"@types/diff": "^5.0.3",
|
"@types/chrome": "^0.0.269",
|
||||||
"@types/lodash": "^4.14.194",
|
"@types/diff": "^5.2.1",
|
||||||
"@types/node": "^18.16.3",
|
"@types/lodash": "^4.17.7",
|
||||||
"@types/react": "^18.2.0",
|
"@types/node": "^22.0.3",
|
||||||
"@types/react-dom": "^18.2.1",
|
"@types/react": "^18.3.3",
|
||||||
"@types/yazl": "^2.4.2",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.1",
|
"@types/yazl": "^2.4.5",
|
||||||
"@typescript-eslint/parser": "^5.59.1",
|
"diff": "^5.2.0",
|
||||||
"diff": "^5.1.0",
|
|
||||||
"discord-types": "^1.3.26",
|
"discord-types": "^1.3.26",
|
||||||
"esbuild": "^0.15.18",
|
"esbuild": "^0.15.18",
|
||||||
"eslint": "^8.46.0",
|
"eslint": "^9.8.0",
|
||||||
"eslint-import-resolver-alias": "^1.1.2",
|
"eslint-import-resolver-alias": "^1.1.2",
|
||||||
"eslint-plugin-path-alias": "^1.0.0",
|
"eslint-plugin-path-alias": "2.1.0",
|
||||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
"eslint-plugin-simple-header": "^1.1.1",
|
||||||
"eslint-plugin-unused-imports": "^2.0.0",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
"highlight.js": "10.6.0",
|
"eslint-plugin-unused-imports": "^4.0.1",
|
||||||
|
"highlight.js": "10.7.3",
|
||||||
"html-minifier-terser": "^7.2.0",
|
"html-minifier-terser": "^7.2.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.30.1",
|
||||||
"puppeteer-core": "^19.11.1",
|
"puppeteer-core": "^22.15.0",
|
||||||
"standalone-electron-types": "^1.0.0",
|
"standalone-electron-types": "^1.0.0",
|
||||||
"stylelint": "^15.6.0",
|
"stylelint": "^16.8.1",
|
||||||
"stylelint-config-standard": "^33.0.0",
|
"stylelint-config-standard": "^36.0.1",
|
||||||
"ts-patch": "^3.1.2",
|
"ts-patch": "^3.2.1",
|
||||||
"tsx": "^3.12.7",
|
"tsx": "^4.16.5",
|
||||||
"type-fest": "^3.9.0",
|
"type-fest": "^4.23.0",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.5.4",
|
||||||
|
"typescript-eslint": "^8.0.0",
|
||||||
"typescript-transform-paths": "^3.4.7",
|
"typescript-transform-paths": "^3.4.7",
|
||||||
"zip-local": "^0.3.5"
|
"zip-local": "^0.3.5"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.1.0",
|
"packageManager": "pnpm@9.1.0",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"eslint-plugin-path-alias@1.0.0": "patches/eslint-plugin-path-alias@1.0.0.patch",
|
"eslint@9.8.0": "patches/eslint@9.8.0.patch",
|
||||||
"eslint@8.46.0": "patches/eslint@8.46.0.patch"
|
"eslint-plugin-path-alias@2.1.0": "patches/eslint-plugin-path-alias@2.1.0.patch"
|
||||||
},
|
},
|
||||||
"peerDependencyRules": {
|
"peerDependencyRules": {
|
||||||
"ignoreMissing": [
|
"ignoreMissing": [
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
diff --git a/lib/rules/no-relative.js b/lib/rules/no-relative.js
|
|
||||||
index 71594c83f1f4f733ffcc6047d7f7084348335dbe..d8623d87c89499c442171db3272cba07c9efabbe 100644
|
|
||||||
--- a/lib/rules/no-relative.js
|
|
||||||
+++ b/lib/rules/no-relative.js
|
|
||||||
@@ -41,7 +41,7 @@ module.exports = {
|
|
||||||
ImportDeclaration(node) {
|
|
||||||
const importPath = node.source.value;
|
|
||||||
|
|
||||||
- if (!/^(\.?\.\/)/.test(importPath)) {
|
|
||||||
+ if (!/^(\.\.\/)/.test(importPath)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
14
patches/eslint-plugin-path-alias@2.1.0.patch
Normal file
14
patches/eslint-plugin-path-alias@2.1.0.patch
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
diff --git a/dist/index.js b/dist/index.js
|
||||||
|
index 67de6fb139070fd0e49beca65e3b63c531202e16..aa2883c8126e4952a42872ee920f59547a066430 100644
|
||||||
|
--- a/dist/index.js
|
||||||
|
+++ b/dist/index.js
|
||||||
|
@@ -1 +1 @@
|
||||||
|
-var C=Object.create;var f=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var $=(e,t)=>{for(var r in t)f(e,r,{get:t[r],enumerable:!0})},y=(e,t,r,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of U(t))!F.call(e,s)&&s!==r&&f(e,s,{get:()=>t[s],enumerable:!(i=I(t,s))||i.enumerable});return e};var b=(e,t,r)=>(r=e!=null?C(S(e)):{},y(t||!e||!e.__esModule?f(r,"default",{value:e,enumerable:!0}):r,e)),D=e=>y(f({},"__esModule",{value:!0}),e);var N={};$(N,{default:()=>J});module.exports=D(N);var h="eslint-plugin-path-alias",v="2.0.0";var l=require("path"),M=b(require("nanomatch"));function j(e){return`https://github/com/msfragala/eslint-plugin-path-alias/blob/master/docs/rules/${e}.md`}var R=require("get-tsconfig"),a=require("path"),w=b(require("find-pkg")),O=require("fs");function P(e){if(e.options[0]?.paths)return z(e);let t=e.getFilename?.()??e.filename,r=(0,R.getTsconfig)(t);if(r?.config?.compilerOptions?.paths)return q(r);let i=w.default.sync((0,a.dirname)(t));if(!i)return;let s=JSON.parse((0,O.readFileSync)(i).toString());if(s?.imports)return L(s,i)}function L(e,t){let r=new Map,i=e.imports??{},s=(0,a.dirname)(t);return Object.entries(i).forEach(([o,n])=>{if(!n||typeof n!="string")return;let p=(0,a.resolve)(s,n);r.set(o,[p])}),r}function q(e){let t=new Map,r=e?.config?.compilerOptions?.paths??{},i=(0,a.dirname)(e.path);return e.config.compilerOptions?.baseUrl&&(i=(0,a.resolve)((0,a.dirname)(e.path),e.config.compilerOptions.baseUrl)),Object.entries(r).forEach(([s,o])=>{s=s.replace(/\/\*$/,""),o=o.map(n=>(0,a.resolve)(i,n.replace(/\/\*$/,""))),t.set(s,o)}),t}function z(e){let t=new Map,r=e.options[0]?.paths??{};return Object.entries(r).forEach(([i,s])=>{if(!s||typeof s!="string")return;if(s.startsWith("/")){t.set(i,[s]);return}let o=e.getCwd?.()??e.cwd,n=(0,a.resolve)(o,s);t.set(i,[n])}),t}var T={meta:{type:"suggestion",docs:{description:"Ensure imports use path aliases whenever possible vs. relative paths",url:j("no-relative")},fixable:"code",schema:[{type:"object",properties:{exceptions:{type:"array",items:{type:"string"}},paths:{type:"object"}},additionalProperties:!1}],messages:{shouldUseAlias:"Import should use path alias instead of relative path"}},create(e){let t=e.options[0]?.exceptions,r=e.getFilename?.()??e.filename,i=P(e);return i?.size?{ImportExpression(s){if(s.source.type!=="Literal"||typeof s.source.value!="string")return;let o=s.source.raw,n=s.source.value;if(!/^(\.?\.\/)/.test(n))return;let p=(0,l.resolve)((0,l.dirname)(r),n);if(A(p,t))return;let c=k(p,i);c&&e.report({node:s,messageId:"shouldUseAlias",data:{alias:c},fix(m){let g=E(p,c,i.get(c)),d=o.replace(n,g);return m.replaceText(s.source,d)}})},ImportDeclaration(s){if(typeof s.source.value!="string")return;let o=s.source.value;if(!/^(\.?\.\/)/.test(o))return;let n=(0,l.resolve)((0,l.dirname)(r),o),p=A(n,t),u=k(n,i);p||u&&e.report({node:s,messageId:"shouldUseAlias",data:{alias:u},fix(c){let m=s.source.raw,g=E(n,u,i.get(u)),d=m.replace(o,g);return c.replaceText(s.source,d)}})}}:{}}};function k(e,t){return Array.from(t.keys()).find(r=>t.get(r).some(s=>e.indexOf(s)===0))}function A(e,t){if(!t)return!1;let r=(0,l.basename)(e);return(0,M.default)(r,t).includes(r)}function E(e,t,r){for(let i of r)if(e.indexOf(i)===0)return e.replace(i,t)}var J={name:h,version:v,meta:{name:h,version:v},rules:{"no-relative":T}};
|
||||||
|
+var C=Object.create;var f=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var $=(e,t)=>{for(var r in t)f(e,r,{get:t[r],enumerable:!0})},y=(e,t,r,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of U(t))!F.call(e,s)&&s!==r&&f(e,s,{get:()=>t[s],enumerable:!(i=I(t,s))||i.enumerable});return e};var b=(e,t,r)=>(r=e!=null?C(S(e)):{},y(t||!e||!e.__esModule?f(r,"default",{value:e,enumerable:!0}):r,e)),D=e=>y(f({},"__esModule",{value:!0}),e);var N={};$(N,{default:()=>J});module.exports=D(N);var h="eslint-plugin-path-alias",v="2.0.0";var l=require("path"),M=b(require("nanomatch"));function j(e){return`https://github/com/msfragala/eslint-plugin-path-alias/blob/master/docs/rules/${e}.md`}var R=require("get-tsconfig"),a=require("path"),w=b(require("find-pkg")),O=require("fs");function P(e){if(e.options[0]?.paths)return z(e);let t=e.getFilename?.()??e.filename,r=(0,R.getTsconfig)(t);if(r?.config?.compilerOptions?.paths)return q(r);let i=w.default.sync((0,a.dirname)(t));if(!i)return;let s=JSON.parse((0,O.readFileSync)(i).toString());if(s?.imports)return L(s,i)}function L(e,t){let r=new Map,i=e.imports??{},s=(0,a.dirname)(t);return Object.entries(i).forEach(([o,n])=>{if(!n||typeof n!="string")return;let p=(0,a.resolve)(s,n);r.set(o,[p])}),r}function q(e){let t=new Map,r=e?.config?.compilerOptions?.paths??{},i=(0,a.dirname)(e.path);return e.config.compilerOptions?.baseUrl&&(i=(0,a.resolve)((0,a.dirname)(e.path),e.config.compilerOptions.baseUrl)),Object.entries(r).forEach(([s,o])=>{s=s.replace(/\/\*$/,""),o=o.map(n=>(0,a.resolve)(i,n.replace(/\/\*$/,""))),t.set(s,o)}),t}function z(e){let t=new Map,r=e.options[0]?.paths??{};return Object.entries(r).forEach(([i,s])=>{if(!s||typeof s!="string")return;if(s.startsWith("/")){t.set(i,[s]);return}let o=e.getCwd?.()??e.cwd,n=(0,a.resolve)(o,s);t.set(i,[n])}),t}var T={meta:{type:"suggestion",docs:{description:"Ensure imports use path aliases whenever possible vs. relative paths",url:j("no-relative")},fixable:"code",schema:[{type:"object",properties:{exceptions:{type:"array",items:{type:"string"}},paths:{type:"object"}},additionalProperties:!1}],messages:{shouldUseAlias:"Import should use path alias instead of relative path"}},create(e){let t=e.options[0]?.exceptions,r=e.getFilename?.()??e.filename,i=P(e);return i?.size?{ImportExpression(s){if(s.source.type!=="Literal"||typeof s.source.value!="string")return;let o=s.source.raw,n=s.source.value;if(!/^(\.\.\/)/.test(n))return;let p=(0,l.resolve)((0,l.dirname)(r),n);if(A(p,t))return;let c=k(p,i);c&&e.report({node:s,messageId:"shouldUseAlias",data:{alias:c},fix(m){let g=E(p,c,i.get(c)),d=o.replace(n,g);return m.replaceText(s.source,d)}})},ImportDeclaration(s){if(typeof s.source.value!="string")return;let o=s.source.value;if(!/^(\.\.\/)/.test(o))return;let n=(0,l.resolve)((0,l.dirname)(r),o),p=A(n,t),u=k(n,i);p||u&&e.report({node:s,messageId:"shouldUseAlias",data:{alias:u},fix(c){let m=s.source.raw,g=E(n,u,i.get(u)),d=m.replace(o,g);return c.replaceText(s.source,d)}})}}:{}}};function k(e,t){return Array.from(t.keys()).find(r=>t.get(r).some(s=>e.indexOf(s)===0))}function A(e,t){if(!t)return!1;let r=(0,l.basename)(e);return(0,M.default)(r,t).includes(r)}function E(e,t,r){for(let i of r)if(e.indexOf(i)===0)return e.replace(i,t)}var J={name:h,version:v,meta:{name:h,version:v},rules:{"no-relative":T}};
|
||||||
|
diff --git a/dist/index.mjs b/dist/index.mjs
|
||||||
|
index 96de18e06d4cc413e11af038cd760e4804c32e59..27e8c4e3e2c942400cc3982e52159904ca6eedfa 100644
|
||||||
|
--- a/dist/index.mjs
|
||||||
|
+++ b/dist/index.mjs
|
||||||
|
@@ -1 +1 @@
|
||||||
|
-var d="eslint-plugin-path-alias",h="2.0.0";import{dirname as x,resolve as j,basename as I}from"path";import U from"nanomatch";function y(e){return`https://github/com/msfragala/eslint-plugin-path-alias/blob/master/docs/rules/${e}.md`}import{getTsconfig as k}from"get-tsconfig";import{resolve as c,dirname as u}from"path";import A from"find-pkg";import{readFileSync as E}from"fs";function b(e){if(e.options[0]?.paths)return C(e);let s=e.getFilename?.()??e.filename,i=k(s);if(i?.config?.compilerOptions?.paths)return T(i);let r=A.sync(u(s));if(!r)return;let t=JSON.parse(E(r).toString());if(t?.imports)return M(t,r)}function M(e,s){let i=new Map,r=e.imports??{},t=u(s);return Object.entries(r).forEach(([o,n])=>{if(!n||typeof n!="string")return;let a=c(t,n);i.set(o,[a])}),i}function T(e){let s=new Map,i=e?.config?.compilerOptions?.paths??{},r=u(e.path);return e.config.compilerOptions?.baseUrl&&(r=c(u(e.path),e.config.compilerOptions.baseUrl)),Object.entries(i).forEach(([t,o])=>{t=t.replace(/\/\*$/,""),o=o.map(n=>c(r,n.replace(/\/\*$/,""))),s.set(t,o)}),s}function C(e){let s=new Map,i=e.options[0]?.paths??{};return Object.entries(i).forEach(([r,t])=>{if(!t||typeof t!="string")return;if(t.startsWith("/")){s.set(r,[t]);return}let o=e.getCwd?.()??e.cwd,n=c(o,t);s.set(r,[n])}),s}var P={meta:{type:"suggestion",docs:{description:"Ensure imports use path aliases whenever possible vs. relative paths",url:y("no-relative")},fixable:"code",schema:[{type:"object",properties:{exceptions:{type:"array",items:{type:"string"}},paths:{type:"object"}},additionalProperties:!1}],messages:{shouldUseAlias:"Import should use path alias instead of relative path"}},create(e){let s=e.options[0]?.exceptions,i=e.getFilename?.()??e.filename,r=b(e);return r?.size?{ImportExpression(t){if(t.source.type!=="Literal"||typeof t.source.value!="string")return;let o=t.source.raw,n=t.source.value;if(!/^(\.?\.\/)/.test(n))return;let a=j(x(i),n);if(w(a,s))return;let l=R(a,r);l&&e.report({node:t,messageId:"shouldUseAlias",data:{alias:l},fix(f){let m=O(a,l,r.get(l)),g=o.replace(n,m);return f.replaceText(t.source,g)}})},ImportDeclaration(t){if(typeof t.source.value!="string")return;let o=t.source.value;if(!/^(\.?\.\/)/.test(o))return;let n=j(x(i),o),a=w(n,s),p=R(n,r);a||p&&e.report({node:t,messageId:"shouldUseAlias",data:{alias:p},fix(l){let f=t.source.raw,m=O(n,p,r.get(p)),g=f.replace(o,m);return l.replaceText(t.source,g)}})}}:{}}};function R(e,s){return Array.from(s.keys()).find(i=>s.get(i).some(t=>e.indexOf(t)===0))}function w(e,s){if(!s)return!1;let i=I(e);return U(i,s).includes(i)}function O(e,s,i){for(let r of i)if(e.indexOf(r)===0)return e.replace(r,s)}var Q={name:d,version:h,meta:{name:d,version:h},rules:{"no-relative":P}};export{Q as default};
|
||||||
|
+var d="eslint-plugin-path-alias",h="2.0.0";import{dirname as x,resolve as j,basename as I}from"path";import U from"nanomatch";function y(e){return`https://github/com/msfragala/eslint-plugin-path-alias/blob/master/docs/rules/${e}.md`}import{getTsconfig as k}from"get-tsconfig";import{resolve as c,dirname as u}from"path";import A from"find-pkg";import{readFileSync as E}from"fs";function b(e){if(e.options[0]?.paths)return C(e);let s=e.getFilename?.()??e.filename,i=k(s);if(i?.config?.compilerOptions?.paths)return T(i);let r=A.sync(u(s));if(!r)return;let t=JSON.parse(E(r).toString());if(t?.imports)return M(t,r)}function M(e,s){let i=new Map,r=e.imports??{},t=u(s);return Object.entries(r).forEach(([o,n])=>{if(!n||typeof n!="string")return;let a=c(t,n);i.set(o,[a])}),i}function T(e){let s=new Map,i=e?.config?.compilerOptions?.paths??{},r=u(e.path);return e.config.compilerOptions?.baseUrl&&(r=c(u(e.path),e.config.compilerOptions.baseUrl)),Object.entries(i).forEach(([t,o])=>{t=t.replace(/\/\*$/,""),o=o.map(n=>c(r,n.replace(/\/\*$/,""))),s.set(t,o)}),s}function C(e){let s=new Map,i=e.options[0]?.paths??{};return Object.entries(i).forEach(([r,t])=>{if(!t||typeof t!="string")return;if(t.startsWith("/")){s.set(r,[t]);return}let o=e.getCwd?.()??e.cwd,n=c(o,t);s.set(r,[n])}),s}var P={meta:{type:"suggestion",docs:{description:"Ensure imports use path aliases whenever possible vs. relative paths",url:y("no-relative")},fixable:"code",schema:[{type:"object",properties:{exceptions:{type:"array",items:{type:"string"}},paths:{type:"object"}},additionalProperties:!1}],messages:{shouldUseAlias:"Import should use path alias instead of relative path"}},create(e){let s=e.options[0]?.exceptions,i=e.getFilename?.()??e.filename,r=b(e);return r?.size?{ImportExpression(t){if(t.source.type!=="Literal"||typeof t.source.value!="string")return;let o=t.source.raw,n=t.source.value;if(!/^(\.\.\/)/.test(n))return;let a=j(x(i),n);if(w(a,s))return;let l=R(a,r);l&&e.report({node:t,messageId:"shouldUseAlias",data:{alias:l},fix(f){let m=O(a,l,r.get(l)),g=o.replace(n,m);return f.replaceText(t.source,g)}})},ImportDeclaration(t){if(typeof t.source.value!="string")return;let o=t.source.value;if(!/^(\.\.\/)/.test(o))return;let n=j(x(i),o),a=w(n,s),p=R(n,r);a||p&&e.report({node:t,messageId:"shouldUseAlias",data:{alias:p},fix(l){let f=t.source.raw,m=O(n,p,r.get(p)),g=f.replace(o,m);return l.replaceText(t.source,g)}})}}:{}}};function R(e,s){return Array.from(s.keys()).find(i=>s.get(i).some(t=>e.indexOf(t)===0))}function w(e,s){if(!s)return!1;let i=I(e);return U(i,s).includes(i)}function O(e,s,i){for(let r of i)if(e.indexOf(r)===0)return e.replace(r,s)}var Q={name:d,version:h,meta:{name:d,version:h},rules:{"no-relative":P}};export{Q as default};
|
2582
pnpm-lock.yaml
2582
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -36,7 +36,7 @@ for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) {
|
||||||
const CANARY = process.env.USE_CANARY === "true";
|
const CANARY = process.env.USE_CANARY === "true";
|
||||||
|
|
||||||
const browser = await pup.launch({
|
const browser = await pup.launch({
|
||||||
headless: "new",
|
headless: true,
|
||||||
executablePath: process.env.CHROMIUM_BIN
|
executablePath: process.env.CHROMIUM_BIN
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -16,16 +16,17 @@
|
||||||
* 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 ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { Channel, Message } from "discord-types/general";
|
import { Channel, Message } from "discord-types/general";
|
||||||
import type { MouseEventHandler } from "react";
|
import type { ComponentType, MouseEventHandler } from "react";
|
||||||
|
|
||||||
const logger = new Logger("MessagePopover");
|
const logger = new Logger("MessagePopover");
|
||||||
|
|
||||||
export interface ButtonItem {
|
export interface ButtonItem {
|
||||||
key?: string,
|
key?: string,
|
||||||
label: string,
|
label: string,
|
||||||
icon: React.ComponentType<any>,
|
icon: ComponentType<any>,
|
||||||
message: Message,
|
message: Message,
|
||||||
channel: Channel,
|
channel: Channel,
|
||||||
onClick?: MouseEventHandler<HTMLButtonElement>,
|
onClick?: MouseEventHandler<HTMLButtonElement>,
|
||||||
|
@ -48,22 +49,26 @@ export function removeButton(identifier: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _buildPopoverElements(
|
export function _buildPopoverElements(
|
||||||
msg: Message,
|
Component: React.ComponentType<ButtonItem>,
|
||||||
makeButton: (item: ButtonItem) => React.ComponentType
|
message: Message
|
||||||
) {
|
) {
|
||||||
const items = [] as React.ComponentType[];
|
const items: React.ReactNode[] = [];
|
||||||
|
|
||||||
for (const [identifier, getItem] of buttons.entries()) {
|
for (const [identifier, getItem] of buttons.entries()) {
|
||||||
try {
|
try {
|
||||||
const item = getItem(msg);
|
const item = getItem(message);
|
||||||
if (item) {
|
if (item) {
|
||||||
item.key ??= identifier;
|
item.key ??= identifier;
|
||||||
items.push(makeButton(item));
|
items.push(
|
||||||
|
<ErrorBoundary noop>
|
||||||
|
<Component {...item} />
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`[${identifier}]`, err);
|
logger.error(`[${identifier}]`, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return <>{items}</>;
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
.vc-expandableheader-center-flex {
|
.vc-expandableheader-center-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-items: center;
|
place-items: center;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-expandableheader-btn {
|
.vc-expandableheader-btn {
|
||||||
|
|
|
@ -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 { get } from "@main/utils/simpleGet";
|
||||||
import { IpcEvents } from "@shared/IpcEvents";
|
import { IpcEvents } from "@shared/IpcEvents";
|
||||||
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
|
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
|
||||||
import { ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
|
@ -25,7 +26,6 @@ import { join } from "path";
|
||||||
import gitHash from "~git-hash";
|
import gitHash from "~git-hash";
|
||||||
import gitRemote from "~git-remote";
|
import gitRemote from "~git-remote";
|
||||||
|
|
||||||
import { get } from "../utils/simpleGet";
|
|
||||||
import { serializeErrors, VENCORD_FILES } from "./common";
|
import { serializeErrors, VENCORD_FILES } from "./common";
|
||||||
|
|
||||||
const API_BASE = `https://api.github.com/repos/${gitRemote}`;
|
const API_BASE = `https://api.github.com/repos/${gitRemote}`;
|
||||||
|
|
1
src/modules.d.ts
vendored
1
src/modules.d.ts
vendored
|
@ -16,7 +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/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// eslint-disable-next-line spaced-comment
|
|
||||||
/// <reference types="standalone-electron-types"/>
|
/// <reference types="standalone-electron-types"/>
|
||||||
|
|
||||||
declare module "~plugins" {
|
declare module "~plugins" {
|
||||||
|
|
|
@ -26,9 +26,8 @@ export default definePlugin({
|
||||||
patches: [{
|
patches: [{
|
||||||
find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
|
find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
|
||||||
replacement: {
|
replacement: {
|
||||||
// foo && !bar ? createElement(reactionStuffs)... createElement(blah,...makeElement(reply-other))
|
match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.Messages\.MESSAGE_ACTION_REPLY.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/,
|
||||||
match: /\i&&!\i\?\(0,\i\.jsxs?\)\(.{0,200}renderEmojiPicker:.{0,500}\?(\i)\(\{key:"reply-other"(?<=message:(\i).+?)/,
|
replace: "$&,Vencord.Api.MessagePopover._buildPopoverElements($1,$2)"
|
||||||
replace: (m, makeElement, msg) => `...Vencord.Api.MessagePopover._buildPopoverElements(${msg},${makeElement}),${m}`
|
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
|
|
|
@ -249,6 +249,10 @@ export default definePlugin({
|
||||||
dispatchingFoldersClose = false;
|
dispatchingFoldersClose = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
LOGOUT() {
|
||||||
|
closeFolders();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { openInviteModal } from "@utils/discord";
|
import { openInviteModal } from "@utils/discord";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes, copyWithToast } from "@utils/misc";
|
||||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { findComponentByCodeLazy } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
|
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
|
||||||
|
@ -45,7 +45,11 @@ interface Section {
|
||||||
authorIds?: string[];
|
authorIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function SectionHeader({ section }: { section: Section; }) {
|
interface SectionHeaderProps {
|
||||||
|
section: Section;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SectionHeader({ section }: SectionHeaderProps) {
|
||||||
const hasSubtitle = typeof section.subtitle !== "undefined";
|
const hasSubtitle = typeof section.subtitle !== "undefined";
|
||||||
const hasAuthorIds = typeof section.authorIds !== "undefined";
|
const hasAuthorIds = typeof section.authorIds !== "undefined";
|
||||||
|
|
||||||
|
@ -62,6 +66,7 @@ function SectionHeader({ section }: { section: Section; }) {
|
||||||
})();
|
})();
|
||||||
}, [section.authorIds]);
|
}, [section.authorIds]);
|
||||||
|
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Forms.FormTitle style={{ flexGrow: 1 }}>{section.title}</Forms.FormTitle>
|
<Forms.FormTitle style={{ flexGrow: 1 }}>{section.title}</Forms.FormTitle>
|
||||||
|
@ -74,8 +79,7 @@ function SectionHeader({ section }: { section: Section; }) {
|
||||||
size={16}
|
size={16}
|
||||||
showUserPopout
|
showUserPopout
|
||||||
className={Margins.bottom8}
|
className={Margins.bottom8}
|
||||||
/>
|
/>}
|
||||||
}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
{hasSubtitle &&
|
{hasSubtitle &&
|
||||||
<Forms.FormText type="description" className={Margins.bottom8}>
|
<Forms.FormText type="description" className={Margins.bottom8}>
|
||||||
|
@ -204,7 +208,16 @@ function ChangeDecorationModal(props: ModalProps) {
|
||||||
{activeSelectedDecoration?.alt}
|
{activeSelectedDecoration?.alt}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
{activeDecorationHasAuthor && <Text key={`createdBy-${activeSelectedDecoration.authorId}`}>Created by {Parser.parse(`<@${activeSelectedDecoration.authorId}>`)}</Text>}
|
{activeDecorationHasAuthor && (
|
||||||
|
<Text key={`createdBy-${activeSelectedDecoration.authorId}`}>
|
||||||
|
Created by {Parser.parse(`<@${activeSelectedDecoration.authorId}>`)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{isActiveDecorationPreset && (
|
||||||
|
<Button onClick={() => copyWithToast(activeDecorationPreset.id)}>
|
||||||
|
Copy Preset ID
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
|
@ -57,7 +57,7 @@ function decode(bio: string): Array<number> | null {
|
||||||
if (bio == null) return null;
|
if (bio == null) return null;
|
||||||
|
|
||||||
const colorString = bio.match(
|
const colorString = bio.match(
|
||||||
/\u{e005b}\u{e0023}([\u{e0061}-\u{e0066}\u{e0041}-\u{e0046}\u{e0030}-\u{e0039}]+?)\u{e002c}\u{e0023}([\u{e0061}-\u{e0066}\u{e0041}-\u{e0046}\u{e0030}-\u{e0039}]+?)\u{e005d}/u,
|
/\u{e005b}\u{e0023}([\u{e0061}-\u{e0066}\u{e0041}-\u{e0046}\u{e0030}-\u{e0039}]{1,6})\u{e002c}\u{e0023}([\u{e0061}-\u{e0066}\u{e0041}-\u{e0046}\u{e0030}-\u{e0039}]{1,6})\u{e005d}/u,
|
||||||
);
|
);
|
||||||
if (colorString != null) {
|
if (colorString != null) {
|
||||||
const parsed = [...colorString[0]]
|
const parsed = [...colorString[0]]
|
||||||
|
|
|
@ -27,7 +27,7 @@ export default definePlugin({
|
||||||
authors: [Devs.D3SOX, Devs.Nickyux],
|
authors: [Devs.D3SOX, Devs.Nickyux],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".PREMIUM_GUILD_SUBSCRIPTION_TOOLTIP",
|
find: ".Messages.GUILD_OWNER,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /,isOwner:(\i),/,
|
match: /,isOwner:(\i),/,
|
||||||
replace: ",_isOwner:$1=$self.isGuildOwner(e),"
|
replace: ",_isOwner:$1=$self.isGuildOwner(e),"
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
# 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)
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* 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("insertText", "selectCommandOption");
|
|
||||||
|
|
||||||
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: /(?<=\i.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();
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -6,12 +6,21 @@
|
||||||
|
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
|
import { definePluginSettings } 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 from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { SelectedGuildStore, useState } from "@webpack/common";
|
import { SelectedGuildStore, useState } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
showAtSymbol: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Whether the the @ symbol should be displayed",
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MentionAvatars",
|
name: "MentionAvatars",
|
||||||
description: "Shows user avatars inside mentions",
|
description: "Shows user avatars inside mentions",
|
||||||
|
@ -25,11 +34,13 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
renderUsername: ErrorBoundary.wrap((props: { user: User, username: string; }) => {
|
renderUsername: ErrorBoundary.wrap((props: { user: User, username: string; }) => {
|
||||||
const { user, username } = props;
|
const { user, username } = props;
|
||||||
const [isHovering, setIsHovering] = useState(false);
|
const [isHovering, setIsHovering] = useState(false);
|
||||||
|
|
||||||
if (!user) return <>@{username}</>;
|
if (!user) return <>{getUsernameString(username)}</>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
|
@ -37,8 +48,15 @@ export default definePlugin({
|
||||||
onMouseLeave={() => setIsHovering(false)}
|
onMouseLeave={() => setIsHovering(false)}
|
||||||
>
|
>
|
||||||
<img src={user.getAvatarURL(SelectedGuildStore.getGuildId(), 16, isHovering)} className="vc-mentionAvatars-avatar" />
|
<img src={user.getAvatarURL(SelectedGuildStore.getGuildId(), 16, isHovering)} className="vc-mentionAvatars-avatar" />
|
||||||
@{username}
|
{getUsernameString(username)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}, { noop: true })
|
}, { noop: true })
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getUsernameString(username: string) {
|
||||||
|
return settings.store.showAtSymbol
|
||||||
|
? `@${username}`
|
||||||
|
: username;
|
||||||
|
}
|
||||||
|
|
|
@ -151,6 +151,7 @@ export default definePlugin({
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
"message": patchMessageContextMenu,
|
"message": patchMessageContextMenu,
|
||||||
"channel-context": patchChannelContextMenu,
|
"channel-context": patchChannelContextMenu,
|
||||||
|
"thread-context": patchChannelContextMenu,
|
||||||
"user-context": patchChannelContextMenu,
|
"user-context": patchChannelContextMenu,
|
||||||
"gdm-context": patchChannelContextMenu
|
"gdm-context": patchChannelContextMenu
|
||||||
},
|
},
|
||||||
|
|
|
@ -49,7 +49,7 @@ export default definePlugin({
|
||||||
find: ".Messages.MUTUAL_GUILDS_WITH_END_COUNT", // Note: the module is lazy-loaded
|
find: ".Messages.MUTUAL_GUILDS_WITH_END_COUNT", // Note: the module is lazy-loaded
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=\.tabBarItem.{0,50}MUTUAL_GUILDS.+?}\),)(?=.+?(\(0,\i\.jsxs?\)\(.{0,100}id:))/,
|
match: /(?<=\.tabBarItem.{0,50}MUTUAL_GUILDS.+?}\),)(?=.+?(\(0,\i\.jsxs?\)\(.{0,100}id:))/,
|
||||||
replace: '$self.isBotOrSelf(arguments[0].user)?null:$1"MUTUAL_GDMS",children:"Mutual Groups"}),'
|
replace: '$self.isBotOrSelf(arguments[0].user)?null:$1"MUTUAL_GDMS",children:$self.getMutualGDMCountText(arguments[0].user)}),'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -64,7 +64,7 @@ export default definePlugin({
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(?<=onItemSelect:\i,children:)(\i)\.map/,
|
match: /(?<=onItemSelect:\i,children:)(\i)\.map/,
|
||||||
replace: "[...$1, ...($self.isBotOrSelf(arguments[0].user) ? [] : [{section:'MUTUAL_GDMS',text:'Mutual Groups'}])].map"
|
replace: "[...$1, ...($self.isBotOrSelf(arguments[0].user) ? [] : [{section:'MUTUAL_GDMS',text:$self.getMutualGDMCountText(arguments[0].user)}])].map"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /\(0,\i\.jsx\)\(\i,\{items:\i,section:(\i)/,
|
match: /\(0,\i\.jsx\)\(\i,\{items:\i,section:(\i)/,
|
||||||
|
@ -76,6 +76,11 @@ export default definePlugin({
|
||||||
|
|
||||||
isBotOrSelf: (user: User) => user.bot || user.id === UserStore.getCurrentUser().id,
|
isBotOrSelf: (user: User) => user.bot || user.id === UserStore.getCurrentUser().id,
|
||||||
|
|
||||||
|
getMutualGDMCountText: (user: User) => {
|
||||||
|
const count = ChannelStore.getSortedPrivateChannels().filter(c => c.isGroupDM() && c.recipients.includes(user.id)).length;
|
||||||
|
return `${count === 0 ? "No" : count} Mutual Group${count !== 1 ? "s" : ""}`;
|
||||||
|
},
|
||||||
|
|
||||||
renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => {
|
renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => {
|
||||||
const entries = ChannelStore.getSortedPrivateChannels().filter(c => c.isGroupDM() && c.recipients.includes(user.id)).map(c => (
|
const entries = ChannelStore.getSortedPrivateChannels().filter(c => c.isGroupDM() && c.recipients.includes(user.id)).map(c => (
|
||||||
<Clickable
|
<Clickable
|
||||||
|
|
|
@ -26,8 +26,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-st-modal-header {
|
.vc-st-modal-header {
|
||||||
justify-content: space-between;
|
place-content: center space-between;
|
||||||
align-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-st-modal-header h1 {
|
.vc-st-modal-header h1 {
|
||||||
|
|
|
@ -311,7 +311,7 @@ export default definePlugin({
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// Create a variable for the channel prop
|
// Create a variable for the channel prop
|
||||||
match: /maxUsers:\i,users:\i.+?}=(\i).*?;/,
|
match: /users:\i,maxUsers:\i.+?}=(\i).*?;/,
|
||||||
replace: (m, props) => `${m}let{shcChannel}=${props};`
|
replace: (m, props) => `${m}let{shcChannel}=${props};`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,7 +68,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
// fixes a bug where Members page must be loaded to see highest role, why is Discord depending on MemberSafetyStore.getEnhancedMember for something that can be obtained here?
|
// fixes a bug where Members page must be loaded to see highest role, why is Discord depending on MemberSafetyStore.getEnhancedMember for something that can be obtained here?
|
||||||
{
|
{
|
||||||
find: "Messages.GUILD_MEMBER_MOD_VIEW_PERMISSION_GRANTED_BY_ARIA_LABEL,tooltipContentClassName",
|
find: "Messages.GUILD_MEMBER_MOD_VIEW_PERMISSION_GRANTED_BY_ARIA_LABEL,allowOverflow",
|
||||||
predicate: () => settings.store.showModView,
|
predicate: () => settings.store.showModView,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(role:)\i(?=,guildId.{0,100}role:(\i\[))/,
|
match: /(role:)\i(?=,guildId.{0,100}role:(\i\[))/,
|
||||||
|
|
|
@ -165,7 +165,6 @@ function SeekBar() {
|
||||||
|
|
||||||
const [position, setPosition] = useState(storePosition);
|
const [position, setPosition] = useState(storePosition);
|
||||||
|
|
||||||
// eslint-disable-next-line consistent-return
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isPlaying && !isSettingPosition) {
|
if (isPlaying && !isSettingPosition) {
|
||||||
setPosition(SpotifyStore.position);
|
setPosition(SpotifyStore.position);
|
||||||
|
@ -358,7 +357,7 @@ export function Player() {
|
||||||
const [shouldHide, setShouldHide] = 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
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setShouldHide(false);
|
setShouldHide(false);
|
||||||
if (!isPlaying) {
|
if (!isPlaying) {
|
||||||
|
|
|
@ -101,9 +101,8 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0.2rem;
|
padding: 0.2rem;
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
align-content: flex-start;
|
place-content: flex-start center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ChatBarButton } from "@api/ChatButtons";
|
import { ChatBarButton } from "@api/ChatButtons";
|
||||||
import { Margins } from "@utils/margins";
|
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { openModal } from "@utils/modal";
|
import { openModal } from "@utils/modal";
|
||||||
import { Alerts, Forms } from "@webpack/common";
|
import { Alerts, Forms, Tooltip, useEffect, useState } from "@webpack/common";
|
||||||
|
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { TranslateModal } from "./TranslateModal";
|
import { TranslateModal } from "./TranslateModal";
|
||||||
|
@ -39,9 +38,17 @@ export function TranslateIcon({ height = 24, width = 24, className }: { height?:
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export let setShouldShowTranslateEnabledTooltip: undefined | ((show: boolean) => void);
|
||||||
|
|
||||||
export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
||||||
const { autoTranslate, showChatBarButton } = settings.use(["autoTranslate", "showChatBarButton"]);
|
const { autoTranslate, showChatBarButton } = settings.use(["autoTranslate", "showChatBarButton"]);
|
||||||
|
|
||||||
|
const [shouldShowTranslateEnabledTooltip, setter] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
setShouldShowTranslateEnabledTooltip = setter;
|
||||||
|
return () => setShouldShowTranslateEnabledTooltip = undefined;
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (!isMainChat || !showChatBarButton) return null;
|
if (!isMainChat || !showChatBarButton) return null;
|
||||||
|
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
|
@ -52,21 +59,20 @@ export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
||||||
title: "Vencord Auto-Translate Enabled",
|
title: "Vencord Auto-Translate Enabled",
|
||||||
body: <>
|
body: <>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
You just enabled auto translate (by right clicking the Translate icon). Any message you send will automatically be translated before being sent.
|
You just enabled Auto Translate! Any message <b>will automatically be translated</b> before being sent.
|
||||||
</Forms.FormText>
|
|
||||||
<Forms.FormText className={Margins.top16}>
|
|
||||||
If this was an accident, disable it again, or it will change your message content before sending.
|
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
</>,
|
</>,
|
||||||
cancelText: "Disable Auto-Translate",
|
confirmText: "Disable Auto-Translate",
|
||||||
confirmText: "Got it",
|
cancelText: "Got it",
|
||||||
secondaryConfirmText: "Don't show again",
|
secondaryConfirmText: "Don't show again",
|
||||||
onConfirmSecondary: () => settings.store.showAutoTranslateAlert = false,
|
onConfirmSecondary: () => settings.store.showAutoTranslateAlert = false,
|
||||||
onCancel: () => settings.store.autoTranslate = false
|
onConfirm: () => settings.store.autoTranslate = false,
|
||||||
|
// troll
|
||||||
|
confirmColor: "vc-notification-log-danger-btn",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const button = (
|
||||||
<ChatBarButton
|
<ChatBarButton
|
||||||
tooltip="Open Translate Modal"
|
tooltip="Open Translate Modal"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
|
@ -76,7 +82,7 @@ export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
||||||
<TranslateModal rootProps={props} />
|
<TranslateModal rootProps={props} />
|
||||||
));
|
));
|
||||||
}}
|
}}
|
||||||
onContextMenu={() => toggle()}
|
onContextMenu={toggle}
|
||||||
buttonProps={{
|
buttonProps={{
|
||||||
"aria-haspopup": "dialog"
|
"aria-haspopup": "dialog"
|
||||||
}}
|
}}
|
||||||
|
@ -84,4 +90,13 @@ export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
||||||
<TranslateIcon className={cl({ "auto-translate": autoTranslate, "chat-button": true })} />
|
<TranslateIcon className={cl({ "auto-translate": autoTranslate, "chat-button": true })} />
|
||||||
</ChatBarButton>
|
</ChatBarButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (shouldShowTranslateEnabledTooltip && settings.store.showAutoTranslateTooltip)
|
||||||
|
return (
|
||||||
|
<Tooltip text="Auto Translate Enabled" forceOpen>
|
||||||
|
{() => button}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
return button;
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,9 +20,8 @@ import { Margins } from "@utils/margins";
|
||||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||||
import { Forms, SearchableSelect, Switch, useMemo } from "@webpack/common";
|
import { Forms, SearchableSelect, Switch, useMemo } from "@webpack/common";
|
||||||
|
|
||||||
import { Languages } from "./languages";
|
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { cl } from "./utils";
|
import { cl, getLanguages } from "./utils";
|
||||||
|
|
||||||
const LanguageSettingKeys = ["receivedInput", "receivedOutput", "sentInput", "sentOutput"] as const;
|
const LanguageSettingKeys = ["receivedInput", "receivedOutput", "sentInput", "sentOutput"] as const;
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@ function LanguageSelect({ settingsKey, includeAuto }: { settingsKey: typeof Lang
|
||||||
|
|
||||||
const options = useMemo(
|
const options = useMemo(
|
||||||
() => {
|
() => {
|
||||||
const options = Object.entries(Languages).map(([value, label]) => ({ value, label }));
|
const options = Object.entries(getLanguages()).map(([value, label]) => ({ value, label }));
|
||||||
if (!includeAuto)
|
if (!includeAuto)
|
||||||
options.shift();
|
options.shift();
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
import { Parser, useEffect, useState } from "@webpack/common";
|
import { Parser, useEffect, useState } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
import { Languages } from "./languages";
|
|
||||||
import { TranslateIcon } from "./TranslateIcon";
|
import { TranslateIcon } from "./TranslateIcon";
|
||||||
import { cl, TranslationValue } from "./utils";
|
import { cl, TranslationValue } from "./utils";
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ export function TranslationAccessory({ message }: { message: Message; }) {
|
||||||
<TranslateIcon width={16} height={16} />
|
<TranslateIcon width={16} height={16} />
|
||||||
{Parser.parse(translation.text)}
|
{Parser.parse(translation.text)}
|
||||||
{" "}
|
{" "}
|
||||||
(translated from {Languages[translation.src] ?? translation.src} - <Dismiss onDismiss={() => setTranslation(undefined)} />)
|
(translated from {translation.sourceLanguage} - <Dismiss onDismiss={() => setTranslation(undefined)} />)
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import definePlugin from "@utils/types";
|
||||||
import { ChannelStore, Menu } from "@webpack/common";
|
import { ChannelStore, Menu } from "@webpack/common";
|
||||||
|
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { TranslateChatBarIcon, TranslateIcon } from "./TranslateIcon";
|
import { setShouldShowTranslateEnabledTooltip, TranslateChatBarIcon, TranslateIcon } from "./TranslateIcon";
|
||||||
import { handleTranslate, TranslationAccessory } from "./TranslationAccessory";
|
import { handleTranslate, TranslationAccessory } from "./TranslationAccessory";
|
||||||
import { translate } from "./utils";
|
import { translate } from "./utils";
|
||||||
|
|
||||||
|
@ -53,8 +53,8 @@ const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) =>
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "Translate",
|
name: "Translate",
|
||||||
description: "Translate messages with Google Translate",
|
description: "Translate messages with Google Translate or DeepL",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven, Devs.AshtonMemer],
|
||||||
dependencies: ["MessageAccessoriesAPI", "MessagePopoverAPI", "MessageEventsAPI", "ChatInputButtonAPI"],
|
dependencies: ["MessageAccessoriesAPI", "MessagePopoverAPI", "MessageEventsAPI", "ChatInputButtonAPI"],
|
||||||
settings,
|
settings,
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
|
@ -83,11 +83,18 @@ export default definePlugin({
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let tooltipTimeout: any;
|
||||||
this.preSend = addPreSendListener(async (_, message) => {
|
this.preSend = addPreSendListener(async (_, message) => {
|
||||||
if (!settings.store.autoTranslate) return;
|
if (!settings.store.autoTranslate) return;
|
||||||
if (!message.content) return;
|
if (!message.content) return;
|
||||||
|
|
||||||
message.content = (await translate("sent", message.content)).text;
|
setShouldShowTranslateEnabledTooltip?.(true);
|
||||||
|
clearTimeout(tooltipTimeout);
|
||||||
|
tooltipTimeout = setTimeout(() => setShouldShowTranslateEnabledTooltip?.(false), 2000);
|
||||||
|
|
||||||
|
const trans = await translate("sent", message.content);
|
||||||
|
message.content = trans.text;
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,10 @@ copy(Object.fromEntries(
|
||||||
))
|
))
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type Language = keyof typeof Languages;
|
export type GoogleLanguage = keyof typeof GoogleLanguages;
|
||||||
|
export type DeeplLanguage = keyof typeof DeeplLanguages;
|
||||||
|
|
||||||
export const Languages = {
|
export const GoogleLanguages = {
|
||||||
"auto": "Detect language",
|
"auto": "Detect language",
|
||||||
"af": "Afrikaans",
|
"af": "Afrikaans",
|
||||||
"sq": "Albanian",
|
"sq": "Albanian",
|
||||||
|
@ -169,3 +170,57 @@ export const Languages = {
|
||||||
"yo": "Yoruba",
|
"yo": "Yoruba",
|
||||||
"zu": "Zulu"
|
"zu": "Zulu"
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const DeeplLanguages = {
|
||||||
|
"": "Detect language",
|
||||||
|
"ar": "Arabic",
|
||||||
|
"bg": "Bulgarian",
|
||||||
|
"zh-hans": "Chinese (Simplified)",
|
||||||
|
"zh-hant": "Chinese (Traditional)",
|
||||||
|
"cs": "Czech",
|
||||||
|
"da": "Danish",
|
||||||
|
"nl": "Dutch",
|
||||||
|
"en-us": "English (American)",
|
||||||
|
"en-gb": "English (British)",
|
||||||
|
"et": "Estonian",
|
||||||
|
"fi": "Finnish",
|
||||||
|
"fr": "French",
|
||||||
|
"de": "German",
|
||||||
|
"el": "Greek",
|
||||||
|
"hu": "Hungarian",
|
||||||
|
"id": "Indonesian",
|
||||||
|
"it": "Italian",
|
||||||
|
"ja": "Japanese",
|
||||||
|
"ko": "Korean",
|
||||||
|
"lv": "Latvian",
|
||||||
|
"lt": "Lithuanian",
|
||||||
|
"nb": "Norwegian",
|
||||||
|
"pl": "Polish",
|
||||||
|
"pt-br": "Portuguese (Brazilian)",
|
||||||
|
"pt-pt": "Portuguese (European)",
|
||||||
|
"ro": "Romanian",
|
||||||
|
"ru": "Russian",
|
||||||
|
"sk": "Slovak",
|
||||||
|
"sl": "Slovenian",
|
||||||
|
"es": "Spanish",
|
||||||
|
"sv": "Swedish",
|
||||||
|
"tr": "Turkish",
|
||||||
|
"uk": "Ukrainian"
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export function deeplLanguageToGoogleLanguage(language: string) {
|
||||||
|
switch (language) {
|
||||||
|
case "": return "auto";
|
||||||
|
case "nb": return "no";
|
||||||
|
case "zh-hans": return "zh-CN";
|
||||||
|
case "zh-hant": return "zh-TW";
|
||||||
|
case "en-us":
|
||||||
|
case "en-gb":
|
||||||
|
return "en";
|
||||||
|
case "pt-br":
|
||||||
|
case "pt-pt":
|
||||||
|
return "pt";
|
||||||
|
default:
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
29
src/plugins/translate/native.ts
Normal file
29
src/plugins/translate/native.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IpcMainInvokeEvent } from "electron";
|
||||||
|
|
||||||
|
export async function makeDeeplTranslateRequest(_: IpcMainInvokeEvent, pro: boolean, apiKey: string, payload: string) {
|
||||||
|
const url = pro
|
||||||
|
? "https://api.deepl.com/v2/translate"
|
||||||
|
: "https://api-free.deepl.com/v2/translate";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `DeepL-Auth-Key ${apiKey}`
|
||||||
|
},
|
||||||
|
body: payload
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.text();
|
||||||
|
return { status: res.status, data };
|
||||||
|
} catch (e) {
|
||||||
|
return { status: -1, data: String(e) };
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,38 +22,76 @@ import { OptionType } from "@utils/types";
|
||||||
export const settings = definePluginSettings({
|
export const settings = definePluginSettings({
|
||||||
receivedInput: {
|
receivedInput: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "Input language for received messages",
|
description: "Language that received messages should be translated from",
|
||||||
default: "auto",
|
default: "auto",
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
receivedOutput: {
|
receivedOutput: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "Output language for received messages",
|
description: "Language that received messages should be translated to",
|
||||||
default: "en",
|
default: "en",
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
sentInput: {
|
sentInput: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "Input language for sent messages",
|
description: "Language that your own messages should be translated from",
|
||||||
default: "auto",
|
default: "auto",
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
sentOutput: {
|
sentOutput: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "Output language for sent messages",
|
description: "Language that your own messages should be translated to",
|
||||||
default: "en",
|
default: "en",
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showChatBarButton: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show translate button in chat bar",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
service: {
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
description: IS_WEB ? "Translation service (Not supported on Web!)" : "Translation service",
|
||||||
|
disabled: () => IS_WEB,
|
||||||
|
options: [
|
||||||
|
{ label: "Google Translate", value: "google", default: true },
|
||||||
|
{ label: "DeepL Free", value: "deepl" },
|
||||||
|
{ label: "DeepL Pro", value: "deepl-pro" }
|
||||||
|
] as const,
|
||||||
|
onChange: resetLanguageDefaults
|
||||||
|
},
|
||||||
|
deeplApiKey: {
|
||||||
|
type: OptionType.STRING,
|
||||||
|
description: "DeepL API key",
|
||||||
|
default: "",
|
||||||
|
placeholder: "Get your API key from https://deepl.com/your-account",
|
||||||
|
disabled: () => IS_WEB
|
||||||
|
},
|
||||||
autoTranslate: {
|
autoTranslate: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Automatically translate your messages before sending. You can also shift/right click the translate button to toggle this",
|
description: "Automatically translate your messages before sending. You can also shift/right click the translate button to toggle this",
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
showChatBarButton: {
|
showAutoTranslateTooltip: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Show translate button in chat bar",
|
description: "Show a tooltip on the ChatBar button whenever a message is automatically translated",
|
||||||
default: true
|
default: true
|
||||||
}
|
},
|
||||||
}).withPrivateSettings<{
|
}).withPrivateSettings<{
|
||||||
showAutoTranslateAlert: boolean;
|
showAutoTranslateAlert: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
export function resetLanguageDefaults() {
|
||||||
|
if (IS_WEB || settings.store.service === "google") {
|
||||||
|
settings.store.receivedInput = "auto";
|
||||||
|
settings.store.receivedOutput = "en";
|
||||||
|
settings.store.sentInput = "auto";
|
||||||
|
settings.store.sentOutput = "en";
|
||||||
|
} else {
|
||||||
|
settings.store.receivedInput = "";
|
||||||
|
settings.store.receivedOutput = "en-us";
|
||||||
|
settings.store.sentInput = "";
|
||||||
|
settings.store.sentOutput = "en-us";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-trans-modal-header {
|
.vc-trans-modal-header {
|
||||||
justify-content: space-between;
|
place-content: center space-between;
|
||||||
align-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-trans-modal-header h1 {
|
.vc-trans-modal-header h1 {
|
||||||
|
|
|
@ -17,12 +17,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { onlyOnce } from "@utils/onlyOnce";
|
||||||
|
import { PluginNative } from "@utils/types";
|
||||||
|
import { showToast, Toasts } from "@webpack/common";
|
||||||
|
|
||||||
import { settings } from "./settings";
|
import { DeeplLanguages, deeplLanguageToGoogleLanguage, GoogleLanguages } from "./languages";
|
||||||
|
import { resetLanguageDefaults, settings } from "./settings";
|
||||||
|
|
||||||
export const cl = classNameFactory("vc-trans-");
|
export const cl = classNameFactory("vc-trans-");
|
||||||
|
|
||||||
interface TranslationData {
|
const Native = VencordNative.pluginHelpers.Translate as PluginNative<typeof import("./native")>;
|
||||||
|
|
||||||
|
interface GoogleData {
|
||||||
src: string;
|
src: string;
|
||||||
sentences: {
|
sentences: {
|
||||||
// 🏳️⚧️
|
// 🏳️⚧️
|
||||||
|
@ -30,15 +36,47 @@ interface TranslationData {
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DeeplData {
|
||||||
|
translations: {
|
||||||
|
detected_source_language: string;
|
||||||
|
text: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface TranslationValue {
|
export interface TranslationValue {
|
||||||
src: string;
|
sourceLanguage: string;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function translate(kind: "received" | "sent", text: string): Promise<TranslationValue> {
|
export const getLanguages = () => IS_WEB || settings.store.service === "google"
|
||||||
const sourceLang = settings.store[kind + "Input"];
|
? GoogleLanguages
|
||||||
const targetLang = settings.store[kind + "Output"];
|
: DeeplLanguages;
|
||||||
|
|
||||||
|
export async function translate(kind: "received" | "sent", text: string): Promise<TranslationValue> {
|
||||||
|
const translate = IS_WEB || settings.store.service === "google"
|
||||||
|
? googleTranslate
|
||||||
|
: deeplTranslate;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await translate(
|
||||||
|
text,
|
||||||
|
settings.store[`${kind}Input`],
|
||||||
|
settings.store[`${kind}Output`]
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
const userMessage = typeof e === "string"
|
||||||
|
? e
|
||||||
|
: "Something went wrong. If this issue persists, please check the console or ask for help in the support server.";
|
||||||
|
|
||||||
|
showToast(userMessage, Toasts.Type.FAILURE);
|
||||||
|
|
||||||
|
throw e instanceof Error
|
||||||
|
? e
|
||||||
|
: new Error(userMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function googleTranslate(text: string, sourceLang: string, targetLang: string): Promise<TranslationValue> {
|
||||||
const url = "https://translate.googleapis.com/translate_a/single?" + new URLSearchParams({
|
const url = "https://translate.googleapis.com/translate_a/single?" + new URLSearchParams({
|
||||||
// see https://stackoverflow.com/a/29537590 for more params
|
// see https://stackoverflow.com/a/29537590 for more params
|
||||||
// holy shidd nvidia
|
// holy shidd nvidia
|
||||||
|
@ -63,13 +101,69 @@ export async function translate(kind: "received" | "sent", text: string): Promis
|
||||||
+ `\n${res.status} ${res.statusText}`
|
+ `\n${res.status} ${res.statusText}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const { src, sentences }: TranslationData = await res.json();
|
const { src, sentences }: GoogleData = await res.json();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
src,
|
sourceLanguage: GoogleLanguages[src] ?? src,
|
||||||
text: sentences.
|
text: sentences.
|
||||||
map(s => s?.trans).
|
map(s => s?.trans).
|
||||||
filter(Boolean).
|
filter(Boolean).
|
||||||
join("")
|
join("")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fallbackToGoogle(text: string, sourceLang: string, targetLang: string): Promise<TranslationValue> {
|
||||||
|
return googleTranslate(
|
||||||
|
text,
|
||||||
|
deeplLanguageToGoogleLanguage(sourceLang),
|
||||||
|
deeplLanguageToGoogleLanguage(targetLang)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const showDeeplApiQuotaToast = onlyOnce(
|
||||||
|
() => showToast("Deepl API quota exceeded. Falling back to Google Translate", Toasts.Type.FAILURE)
|
||||||
|
);
|
||||||
|
|
||||||
|
async function deeplTranslate(text: string, sourceLang: string, targetLang: string): Promise<TranslationValue> {
|
||||||
|
if (!settings.store.deeplApiKey) {
|
||||||
|
showToast("DeepL API key is not set. Resetting to Google", Toasts.Type.FAILURE);
|
||||||
|
|
||||||
|
settings.store.service = "google";
|
||||||
|
resetLanguageDefaults();
|
||||||
|
|
||||||
|
return fallbackToGoogle(text, sourceLang, targetLang);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CORS jumpscare
|
||||||
|
const { status, data } = await Native.makeDeeplTranslateRequest(
|
||||||
|
settings.store.service === "deepl-pro",
|
||||||
|
settings.store.deeplApiKey,
|
||||||
|
JSON.stringify({
|
||||||
|
text: [text],
|
||||||
|
target_lang: targetLang,
|
||||||
|
source_lang: sourceLang.split("-")[0]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 200:
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
throw "Failed to connect to DeepL API: " + data;
|
||||||
|
case 403:
|
||||||
|
throw "Invalid DeepL API key or version";
|
||||||
|
case 456:
|
||||||
|
showDeeplApiQuotaToast();
|
||||||
|
return fallbackToGoogle(text, sourceLang, targetLang);
|
||||||
|
default:
|
||||||
|
throw new Error(`Failed to translate "${text}" (${sourceLang} -> ${targetLang})\n${status} ${data}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { translations }: DeeplData = JSON.parse(data);
|
||||||
|
const src = translations[0].detected_source_language;
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceLanguage: DeeplLanguages[src] ?? src,
|
||||||
|
text: translations[0].text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -153,6 +153,7 @@ export default definePlugin({
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
"guild-context": MakeContextCallback("Guild"),
|
"guild-context": MakeContextCallback("Guild"),
|
||||||
"channel-context": MakeContextCallback("Channel"),
|
"channel-context": MakeContextCallback("Channel"),
|
||||||
|
"thread-context": MakeContextCallback("Channel"),
|
||||||
"user-context": MakeContextCallback("User")
|
"user-context": MakeContextCallback("User")
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { definePluginSettings } from "@api/Settings";
|
||||||
import { makeRange } from "@components/PluginSettings/components";
|
import { makeRange } from "@components/PluginSettings/components";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
|
||||||
import { findByCodeLazy, findLazy } from "@webpack";
|
import { findByCodeLazy, findLazy } from "@webpack";
|
||||||
import { Button, ChannelStore, GuildStore, UserStore } from "@webpack/common";
|
import { Button, ChannelStore, GuildStore, UserStore } from "@webpack/common";
|
||||||
import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general";
|
import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general";
|
||||||
|
@ -102,6 +102,12 @@ const settings = definePluginSettings({
|
||||||
await start();
|
await start();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
preferUDP: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Enable if you use an older build of XSOverlay unable to connect through websockets. This setting is ignored on web.",
|
||||||
|
default: false,
|
||||||
|
disabled: () => IS_WEB
|
||||||
|
},
|
||||||
botNotifications: {
|
botNotifications: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Allow bot notifications",
|
description: "Allow bot notifications",
|
||||||
|
@ -178,6 +184,8 @@ async function start() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Native = VencordNative.pluginHelpers.XSOverlay as PluginNative<typeof import("./native")>;
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "XSOverlay",
|
name: "XSOverlay",
|
||||||
description: "Forwards discord notifications to XSOverlay, for easy viewing in VR",
|
description: "Forwards discord notifications to XSOverlay, for easy viewing in VR",
|
||||||
|
@ -349,6 +357,10 @@ function sendOtherNotif(content: string, titleString: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendToOverlay(notif: NotificationObject) {
|
async function sendToOverlay(notif: NotificationObject) {
|
||||||
|
if (!IS_WEB && settings.store.preferUDP) {
|
||||||
|
Native.sendToOverlay(notif);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const apiObject: ApiObject = {
|
const apiObject: ApiObject = {
|
||||||
sender: "Vencord",
|
sender: "Vencord",
|
||||||
target: "xsoverlay",
|
target: "xsoverlay",
|
||||||
|
|
16
src/plugins/xsOverlay/native.ts
Normal file
16
src/plugins/xsOverlay/native.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createSocket, Socket } from "dgram";
|
||||||
|
|
||||||
|
let xsoSocket: Socket;
|
||||||
|
|
||||||
|
export function sendToOverlay(_, data: any) {
|
||||||
|
data.messageType = data.type;
|
||||||
|
const json = JSON.stringify(data);
|
||||||
|
xsoSocket ??= createSocket("udp4");
|
||||||
|
xsoSocket.send(json, 42069, "127.0.0.1");
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
# WatchTogetherAdblock
|
# WatchTogetherAdblock
|
||||||
|
|
||||||
Block ads in the YouTube WatchTogether activity via AdGuard
|
Block ads in YouTube embeds and the WatchTogether activity via AdGuard
|
||||||
|
|
||||||
Note that this only works for yourself, other users in the activity will still see ads.
|
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)
|
Powered by a modified version of [Adguard's BlockYoutubeAdsShortcut](https://github.com/AdguardTeam/BlockYouTubeAdsShortcut)
|
|
@ -19,7 +19,6 @@
|
||||||
* along with AdGuard's Block YouTube Ads. If not, see <http://www.gnu.org/licenses/>.
|
* along with AdGuard's Block YouTube Ads. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const LOGO_ID = "block-youtube-ads-logo";
|
|
||||||
const hiddenCSS = [
|
const hiddenCSS = [
|
||||||
"#__ffYoutube1",
|
"#__ffYoutube1",
|
||||||
"#__ffYoutube2",
|
"#__ffYoutube2",
|
||||||
|
@ -98,7 +97,7 @@ const hideElements = () => {
|
||||||
}
|
}
|
||||||
const rule = selectors.join(", ") + " { display: none!important; }";
|
const rule = selectors.join(", ") + " { display: none!important; }";
|
||||||
const style = document.createElement("style");
|
const style = document.createElement("style");
|
||||||
style.innerHTML = rule;
|
style.textContent = rule;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
|
@ -165,11 +164,9 @@ const overrideObject = (obj, propertyName, overrideValue) => {
|
||||||
}
|
}
|
||||||
let overriden = false;
|
let overriden = false;
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
// eslint-disable-next-line no-prototype-builtins
|
|
||||||
if (obj.hasOwnProperty(key) && key === propertyName) {
|
if (obj.hasOwnProperty(key) && key === propertyName) {
|
||||||
obj[key] = overrideValue;
|
obj[key] = overrideValue;
|
||||||
overriden = true;
|
overriden = true;
|
||||||
// eslint-disable-next-line no-prototype-builtins
|
|
||||||
} else if (obj.hasOwnProperty(key) && typeof obj[key] === "object") {
|
} else if (obj.hasOwnProperty(key) && typeof obj[key] === "object") {
|
||||||
if (overrideObject(obj[key], propertyName, overrideValue)) {
|
if (overrideObject(obj[key], propertyName, overrideValue)) {
|
||||||
overriden = true;
|
overriden = true;
|
||||||
|
@ -195,68 +192,25 @@ const jsonOverride = (propertyName, overrideValue) => {
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
// Override Response.prototype.json
|
// Override Response.prototype.json
|
||||||
const nativeResponseJson = Response.prototype.json;
|
Response.prototype.json = new Proxy(Response.prototype.json, {
|
||||||
Response.prototype.json = new Proxy(nativeResponseJson, {
|
async apply(...args) {
|
||||||
apply(...args) {
|
|
||||||
// Call the target function, get the original Promise
|
// Call the target function, get the original Promise
|
||||||
const promise = Reflect.apply(...args);
|
const result = await Reflect.apply(...args);
|
||||||
// Create a new one and override the JSON inside
|
// Create a new one and override the JSON inside
|
||||||
return new Promise((resolve, reject) => {
|
overrideObject(result, propertyName, overrideValue);
|
||||||
promise.then(data => {
|
return result;
|
||||||
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
|
// Removes ads metadata from YouTube XHR requests
|
||||||
jsonOverride("adPlacements", []);
|
jsonOverride("adPlacements", []);
|
||||||
jsonOverride("playerAds", []);
|
jsonOverride("playerAds", []);
|
||||||
// Applies CSS that hides YouTube ad elements
|
// Applies CSS that hides YouTube ad elements
|
||||||
hideElements();
|
hideElements();
|
||||||
// Some changes should be re-evaluated on every page change
|
// Some changes should be re-evaluated on every page change
|
||||||
addAdGuardLogo();
|
|
||||||
hideDynamicAds();
|
hideDynamicAds();
|
||||||
autoSkipAds();
|
autoSkipAds();
|
||||||
observeDomChanges(() => {
|
observeDomChanges(() => {
|
||||||
addAdGuardLogo();
|
|
||||||
hideDynamicAds();
|
hideDynamicAds();
|
||||||
autoSkipAds();
|
autoSkipAds();
|
||||||
});
|
});
|
|
@ -4,12 +4,14 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
// The entire code of this plugin can be found in native.ts
|
// The entire code of this plugin can be found in native.ts
|
||||||
|
migratePluginSettings("YoutubeAdblock", "WatchTogetherAdblock");
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "WatchTogetherAdblock",
|
name: "YoutubeAdblock",
|
||||||
description: "Block ads in the YouTube WatchTogether activity via AdGuard",
|
description: "Block ads in YouTube embeds and the WatchTogether activity via AdGuard",
|
||||||
authors: [Devs.ImLvna],
|
authors: [Devs.ImLvna, Devs.Ven],
|
||||||
});
|
});
|
|
@ -11,9 +11,9 @@ import adguard from "file://adguard.js?minify";
|
||||||
app.on("browser-window-created", (_, win) => {
|
app.on("browser-window-created", (_, win) => {
|
||||||
win.webContents.on("frame-created", (_, { frame }) => {
|
win.webContents.on("frame-created", (_, { frame }) => {
|
||||||
frame.once("dom-ready", () => {
|
frame.once("dom-ready", () => {
|
||||||
if (frame.url.includes("discordsays") && frame.url.includes("youtube.com")) {
|
if (!RendererSettings.store.plugins?.YoutubeAdblock?.enabled) return;
|
||||||
if (!RendererSettings.store.plugins?.WatchTogetherAdblock?.enabled) return;
|
|
||||||
|
|
||||||
|
if (frame.url.includes("youtube.com/embed/") || (frame.url.includes("discordsays") && frame.url.includes("youtube.com"))) {
|
||||||
frame.executeJavaScript(adguard);
|
frame.executeJavaScript(adguard);
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -542,6 +542,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "Joona",
|
name: "Joona",
|
||||||
id: 297410829589020673n
|
id: 297410829589020673n
|
||||||
},
|
},
|
||||||
|
AshtonMemer: {
|
||||||
|
name: "AshtonMemer",
|
||||||
|
id: 373657230530052099n
|
||||||
|
},
|
||||||
surgedevs: {
|
surgedevs: {
|
||||||
name: "Chloe",
|
name: "Chloe",
|
||||||
id: 1084592643784331324n
|
id: 1084592643784331324n
|
||||||
|
|
|
@ -16,7 +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/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// eslint-disable-next-line path-alias/no-relative
|
|
||||||
import { filters, findByPropsLazy, waitFor } from "@webpack";
|
import { filters, findByPropsLazy, waitFor } from "@webpack";
|
||||||
|
|
||||||
import { waitForComponent } from "./internal";
|
import { waitForComponent } from "./internal";
|
||||||
|
|
|
@ -66,7 +66,6 @@ export let DraftStore: t.DraftStore;
|
||||||
*
|
*
|
||||||
* @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id);
|
* @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id);
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line prefer-destructuring
|
|
||||||
export const useStateFromStores: t.useStateFromStores = findByCodeLazy("useStateFromStores");
|
export const useStateFromStores: t.useStateFromStores = findByCodeLazy("useStateFromStores");
|
||||||
|
|
||||||
waitForStore("DraftStore", s => DraftStore = s);
|
waitForStore("DraftStore", s => DraftStore = s);
|
||||||
|
|
|
@ -299,7 +299,7 @@ export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "f
|
||||||
* Note that the example below exists already as an api, see {@link findByPropsLazy}
|
* Note that the example below exists already as an api, see {@link findByPropsLazy}
|
||||||
* @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah);
|
* @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah);
|
||||||
*/
|
*/
|
||||||
export function proxyLazyWebpack<T = any>(factory: () => any, attempts?: number) {
|
export function proxyLazyWebpack<T = any>(factory: () => T, attempts?: number) {
|
||||||
if (IS_REPORTER) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]);
|
||||||
|
|
||||||
return proxyLazy<T>(factory, attempts);
|
return proxyLazy<T>(factory, attempts);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
"allowJs": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"DOM",
|
"DOM",
|
||||||
"DOM.Iterable",
|
"DOM.Iterable",
|
||||||
|
@ -37,7 +38,8 @@
|
||||||
"transform": "typescript-transform-paths",
|
"transform": "typescript-transform-paths",
|
||||||
"afterDeclarations": true
|
"afterDeclarations": true
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"outDir": "who-fucking-cares-dude"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "browser/**/*", "scripts/**/*"]
|
"include": ["src/**/*", "browser/**/*", "scripts/**/*", "eslint.config.mjs"],
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue