Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
12fe367385 | ||
|
fc0417eb80 | ||
|
1f87d14ab2 | ||
|
7d8f3508a8 | ||
|
41f5d71e38 | ||
|
efb88a4df8 | ||
|
a73d09a2f0 | ||
|
cf7c4d63b6 | ||
|
a95311ef2c | ||
|
dd23f9802c | ||
|
f23ddf4cae | ||
|
4222c7fd9f | ||
|
09f65b401e | ||
|
7364776715 | ||
|
3a5b70d410 | ||
|
e08d49edac | ||
|
17abbd3e3e |
@ -19,9 +19,11 @@
|
|||||||
/// <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 monacoHtml from "~fileContent/../src/components/monacoWin.html";
|
import monacoHtmlLocal from "~fileContent/monacoWin.html";
|
||||||
|
import monacoHtmlCdn from "~fileContent/../src/main/monacoWin.html";
|
||||||
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 { getTheme, Theme } from "../src/utils/discord";
|
import { getTheme, Theme } from "../src/utils/discord";
|
||||||
import { getThemeInfo } from "../src/main/themes";
|
import { getThemeInfo } from "../src/main/themes";
|
||||||
|
|
||||||
@ -80,6 +82,7 @@ window.VencordNative = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
win.baseUrl = EXTENSION_BASE_URL;
|
||||||
win.setCss = setCssDebounced;
|
win.setCss = setCssDebounced;
|
||||||
win.getCurrentCss = () => VencordNative.quickCss.get();
|
win.getCurrentCss = () => VencordNative.quickCss.get();
|
||||||
win.getTheme = () =>
|
win.getTheme = () =>
|
||||||
@ -87,7 +90,7 @@ window.VencordNative = {
|
|||||||
? "vs-light"
|
? "vs-light"
|
||||||
: "vs-dark";
|
: "vs-dark";
|
||||||
|
|
||||||
win.document.write(monacoHtml);
|
win.document.write(IS_EXTENSION ? monacoHtmlLocal : monacoHtmlCdn);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -4,6 +4,11 @@ if (typeof browser === "undefined") {
|
|||||||
|
|
||||||
const script = document.createElement("script");
|
const script = document.createElement("script");
|
||||||
script.src = browser.runtime.getURL("dist/Vencord.js");
|
script.src = browser.runtime.getURL("dist/Vencord.js");
|
||||||
|
script.id = "vencord-script";
|
||||||
|
Object.assign(script.dataset, {
|
||||||
|
extensionBaseUrl: browser.runtime.getURL(""),
|
||||||
|
version: browser.runtime.getManifest().version
|
||||||
|
});
|
||||||
|
|
||||||
const style = document.createElement("link");
|
const style = document.createElement("link");
|
||||||
style.type = "text/css";
|
style.type = "text/css";
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
{
|
{
|
||||||
"resources": ["dist/Vencord.js", "dist/Vencord.css"],
|
"resources": ["dist/*", "third-party/*"],
|
||||||
"matches": ["*://*.discord.com/*"]
|
"matches": ["*://*.discord.com/*"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
43
browser/monaco.ts
Normal file
43
browser/monaco.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "./patch-worker";
|
||||||
|
|
||||||
|
import * as monaco from "monaco-editor/esm/vs/editor/editor.main.js";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
const baseUrl: string;
|
||||||
|
const getCurrentCss: () => Promise<string>;
|
||||||
|
const setCss: (css: string) => void;
|
||||||
|
const getTheme: () => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BASE = "/dist/monaco/vs";
|
||||||
|
|
||||||
|
self.MonacoEnvironment = {
|
||||||
|
getWorkerUrl(_moduleId: unknown, label: string) {
|
||||||
|
const path = label === "css" ? "/language/css/css.worker.js" : "/editor/editor.worker.js";
|
||||||
|
return new URL(BASE + path, baseUrl).toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getCurrentCss().then(css => {
|
||||||
|
const editor = monaco.editor.create(
|
||||||
|
document.getElementById("container")!,
|
||||||
|
{
|
||||||
|
value: css,
|
||||||
|
language: "css",
|
||||||
|
theme: getTheme(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
editor.onDidChangeModelContent(() =>
|
||||||
|
setCss(editor.getValue())
|
||||||
|
);
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
// make monaco re-layout
|
||||||
|
editor.layout();
|
||||||
|
});
|
||||||
|
});
|
37
browser/monacoWin.html
Normal file
37
browser/monacoWin.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Vencord QuickCSS Editor</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#container {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="container"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.src = new URL("/dist/monaco/index.js", baseUrl);
|
||||||
|
|
||||||
|
const style = document.createElement("link");
|
||||||
|
style.type = "text/css";
|
||||||
|
style.rel = "stylesheet";
|
||||||
|
style.href = new URL("/dist/monaco/index.css", baseUrl);
|
||||||
|
|
||||||
|
document.body.append(style, script);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
135
browser/patch-worker.js
Normal file
135
browser/patch-worker.js
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 Rob Wu <gwnRob@gmail.com>
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
// Target: Chrome 20+
|
||||||
|
|
||||||
|
// W3-compliant Worker proxy.
|
||||||
|
// This module replaces the global Worker object.
|
||||||
|
// When invoked, the default Worker object is called.
|
||||||
|
// If this call fails with SECURITY_ERR, the script is fetched
|
||||||
|
// using async XHR, and transparently proxies all calls and
|
||||||
|
// setters/getters to the new Worker object.
|
||||||
|
// Note: This script does not magically circumvent the Same origin policy.
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
var Worker_ = window.Worker;
|
||||||
|
var URL = window.URL || window.webkitURL;
|
||||||
|
// Create dummy worker for the following purposes:
|
||||||
|
// 1. Don't override the global Worker object if the fallback isn't
|
||||||
|
// going to work (future API changes?)
|
||||||
|
// 2. Use it to trigger early validation of postMessage calls
|
||||||
|
// Note: Blob constructor is supported since Chrome 20, but since
|
||||||
|
// some of the used Chrome APIs are only supported as of Chrome 20,
|
||||||
|
// I don't bother adding a BlobBuilder fallback.
|
||||||
|
var dummyWorker = new Worker_(
|
||||||
|
URL.createObjectURL(new Blob([], { type: 'text/javascript' })));
|
||||||
|
window.Worker = function Worker(scriptURL) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
throw new TypeError('Not enough arguments');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new Worker_(scriptURL);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code === 18/*DOMException.SECURITY_ERR*/) {
|
||||||
|
return new WorkerXHR(scriptURL);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Bind events and replay queued messages
|
||||||
|
function bindWorker(worker, workerURL) {
|
||||||
|
if (worker._terminated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
worker.Worker = new Worker_(workerURL);
|
||||||
|
worker.Worker.onerror = worker._onerror;
|
||||||
|
worker.Worker.onmessage = worker._onmessage;
|
||||||
|
var o;
|
||||||
|
while ((o = worker._replayQueue.shift())) {
|
||||||
|
worker.Worker[o.method].apply(worker.Worker, o.arguments);
|
||||||
|
}
|
||||||
|
while ((o = worker._messageQueue.shift())) {
|
||||||
|
worker.Worker.postMessage.apply(worker.Worker, o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function WorkerXHR(scriptURL) {
|
||||||
|
var worker = this;
|
||||||
|
var x = new XMLHttpRequest();
|
||||||
|
x.responseType = 'blob';
|
||||||
|
x.onload = function () {
|
||||||
|
// http://stackoverflow.com/a/10372280/938089
|
||||||
|
var workerURL = URL.createObjectURL(x.response);
|
||||||
|
bindWorker(worker, workerURL);
|
||||||
|
};
|
||||||
|
x.open('GET', scriptURL);
|
||||||
|
x.send();
|
||||||
|
worker._replayQueue = [];
|
||||||
|
worker._messageQueue = [];
|
||||||
|
}
|
||||||
|
WorkerXHR.prototype = {
|
||||||
|
constructor: Worker_,
|
||||||
|
terminate: function () {
|
||||||
|
if (!this._terminated) {
|
||||||
|
this._terminated = true;
|
||||||
|
if (this.Worker)
|
||||||
|
this.Worker.terminate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
postMessage: function (message, transfer) {
|
||||||
|
if (!(this instanceof WorkerXHR))
|
||||||
|
throw new TypeError('Illegal invocation');
|
||||||
|
if (this.Worker) {
|
||||||
|
this.Worker.postMessage.apply(this.Worker, arguments);
|
||||||
|
} else {
|
||||||
|
// Trigger validation:
|
||||||
|
dummyWorker.postMessage(message);
|
||||||
|
// Alright, push the valid message to the queue.
|
||||||
|
this._messageQueue.push(arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Implement the EventTarget interface
|
||||||
|
[
|
||||||
|
'addEventListener',
|
||||||
|
'removeEventListener',
|
||||||
|
'dispatchEvent'
|
||||||
|
].forEach(function (method) {
|
||||||
|
WorkerXHR.prototype[method] = function () {
|
||||||
|
if (!(this instanceof WorkerXHR)) {
|
||||||
|
throw new TypeError('Illegal invocation');
|
||||||
|
}
|
||||||
|
if (this.Worker) {
|
||||||
|
this.Worker[method].apply(this.Worker, arguments);
|
||||||
|
} else {
|
||||||
|
this._replayQueue.push({ method: method, arguments: arguments });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
Object.defineProperties(WorkerXHR.prototype, {
|
||||||
|
onmessage: {
|
||||||
|
get: function () { return this._onmessage || null; },
|
||||||
|
set: function (func) {
|
||||||
|
this._onmessage = typeof func === 'function' ? func : null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onerror: {
|
||||||
|
get: function () { return this._onerror || null; },
|
||||||
|
set: function (func) {
|
||||||
|
this._onerror = typeof func === 'function' ? func : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
21
browser/third-party/rnnoise/LICENSE
vendored
Normal file
21
browser/third-party/rnnoise/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 翠 / green
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
BIN
browser/third-party/rnnoise/rnnoise.wasm
vendored
Normal file
BIN
browser/third-party/rnnoise/rnnoise.wasm
vendored
Normal file
Binary file not shown.
13
browser/third-party/rnnoise/rnnoise/workletProcessor.js
vendored
Normal file
13
browser/third-party/rnnoise/rnnoise/workletProcessor.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
browser/third-party/rnnoise/rnnoise_simd.wasm
vendored
Normal file
BIN
browser/third-party/rnnoise/rnnoise_simd.wasm
vendored
Normal file
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.4.7",
|
"version": "1.5.0",
|
||||||
"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": {
|
||||||
@ -36,10 +36,13 @@
|
|||||||
"@vap/shiki": "0.10.5",
|
"@vap/shiki": "0.10.5",
|
||||||
"eslint-plugin-simple-header": "^1.0.2",
|
"eslint-plugin-simple-header": "^1.0.2",
|
||||||
"fflate": "^0.7.4",
|
"fflate": "^0.7.4",
|
||||||
|
"gifenc": "github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3",
|
||||||
|
"monaco-editor": "^0.43.0",
|
||||||
"nanoid": "^4.0.2",
|
"nanoid": "^4.0.2",
|
||||||
"virtual-merge": "^1.0.1"
|
"virtual-merge": "^1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/chrome": "^0.0.246",
|
||||||
"@types/diff": "^5.0.3",
|
"@types/diff": "^5.0.3",
|
||||||
"@types/lodash": "^4.14.194",
|
"@types/lodash": "^4.14.194",
|
||||||
"@types/node": "^18.16.3",
|
"@types/node": "^18.16.3",
|
||||||
@ -64,7 +67,8 @@
|
|||||||
"stylelint-config-standard": "^33.0.0",
|
"stylelint-config-standard": "^33.0.0",
|
||||||
"tsx": "^3.12.7",
|
"tsx": "^3.12.7",
|
||||||
"type-fest": "^3.9.0",
|
"type-fest": "^3.9.0",
|
||||||
"typescript": "^5.0.4"
|
"typescript": "^5.0.4",
|
||||||
|
"zip-local": "^0.3.5"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.1.1",
|
"packageManager": "pnpm@8.1.1",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
|
81
pnpm-lock.yaml
generated
81
pnpm-lock.yaml
generated
@ -1,5 +1,9 @@
|
|||||||
lockfileVersion: '6.0'
|
lockfileVersion: '6.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
patchedDependencies:
|
patchedDependencies:
|
||||||
eslint-plugin-path-alias@1.0.0:
|
eslint-plugin-path-alias@1.0.0:
|
||||||
hash: m6sma4g6bh67km3q6igf6uxaja
|
hash: m6sma4g6bh67km3q6igf6uxaja
|
||||||
@ -24,6 +28,12 @@ dependencies:
|
|||||||
fflate:
|
fflate:
|
||||||
specifier: ^0.7.4
|
specifier: ^0.7.4
|
||||||
version: 0.7.4
|
version: 0.7.4
|
||||||
|
gifenc:
|
||||||
|
specifier: github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3
|
||||||
|
version: github.com/mattdesl/gifenc/64842fca317b112a8590f8fef2bf3825da8f6fe3
|
||||||
|
monaco-editor:
|
||||||
|
specifier: ^0.43.0
|
||||||
|
version: 0.43.0
|
||||||
nanoid:
|
nanoid:
|
||||||
specifier: ^4.0.2
|
specifier: ^4.0.2
|
||||||
version: 4.0.2
|
version: 4.0.2
|
||||||
@ -32,6 +42,9 @@ dependencies:
|
|||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@types/chrome':
|
||||||
|
specifier: ^0.0.246
|
||||||
|
version: 0.0.246
|
||||||
'@types/diff':
|
'@types/diff':
|
||||||
specifier: ^5.0.3
|
specifier: ^5.0.3
|
||||||
version: 5.0.3
|
version: 5.0.3
|
||||||
@ -107,6 +120,9 @@ devDependencies:
|
|||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.0.4
|
specifier: ^5.0.4
|
||||||
version: 5.0.4
|
version: 5.0.4
|
||||||
|
zip-local:
|
||||||
|
specifier: ^0.3.5
|
||||||
|
version: 0.3.5
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@ -520,10 +536,31 @@ packages:
|
|||||||
resolution: {integrity: sha512-gAC33DCXYwNTI/k1PxOVHmbbzakUSMbb/DHpoV6rn4pKZtPI1dduULSmAAm/y1ipgIlArnk2JcnQzw4n2tCZHw==}
|
resolution: {integrity: sha512-gAC33DCXYwNTI/k1PxOVHmbbzakUSMbb/DHpoV6rn4pKZtPI1dduULSmAAm/y1ipgIlArnk2JcnQzw4n2tCZHw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/chrome@0.0.246:
|
||||||
|
resolution: {integrity: sha512-MxGxEomGxsJiL9xe/7ZwVgwdn8XVKWbPvxpVQl3nWOjrS0Ce63JsfzxUc4aU3GvRcUPYsfufHmJ17BFyKxeA4g==}
|
||||||
|
dependencies:
|
||||||
|
'@types/filesystem': 0.0.33
|
||||||
|
'@types/har-format': 1.2.13
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/diff@5.0.3:
|
/@types/diff@5.0.3:
|
||||||
resolution: {integrity: sha512-amrLbRqTU9bXMCc6uX0sWpxsQzRIo9z6MJPkH1pkez/qOxuqSZVuryJAWoBRq94CeG8JxY+VK4Le9HtjQR5T9A==}
|
resolution: {integrity: sha512-amrLbRqTU9bXMCc6uX0sWpxsQzRIo9z6MJPkH1pkez/qOxuqSZVuryJAWoBRq94CeG8JxY+VK4Le9HtjQR5T9A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/filesystem@0.0.33:
|
||||||
|
resolution: {integrity: sha512-2KedRPzwu2K528vFkoXnnWdsG0MtUwPjuA7pRy4vKxlxHEe8qUDZibYHXJKZZr2Cl/ELdCWYqyb/MKwsUuzBWw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/filewriter': 0.0.30
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/filewriter@0.0.30:
|
||||||
|
resolution: {integrity: sha512-lB98tui0uxc7erbj0serZfJlHKLNJHwBltPnbmO1WRpL5T325GOHRiQfr2E29V2q+S1brDO63Fpdt6vb3bES9Q==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/har-format@1.2.13:
|
||||||
|
resolution: {integrity: sha512-PwBsCBD3lDODn4xpje3Y1di0aDJp4Ww7aSfMRVw6ysnxD4I7Wmq2mBkSKaDtN403hqH5sp6c9xQUvFYY3+lkBg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/json-schema@7.0.11:
|
/@types/json-schema@7.0.11:
|
||||||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -843,6 +880,10 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/async@1.5.2:
|
||||||
|
resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/atob@2.1.2:
|
/atob@2.1.2:
|
||||||
resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
|
resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
|
||||||
engines: {node: '>= 4.5.0'}
|
engines: {node: '>= 4.5.0'}
|
||||||
@ -1867,6 +1908,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==}
|
resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/graceful-fs@4.2.11:
|
||||||
|
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/grapheme-splitter@1.0.4:
|
/grapheme-splitter@1.0.4:
|
||||||
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
|
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -2184,6 +2229,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
|
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/jszip@2.7.0:
|
||||||
|
resolution: {integrity: sha512-JIsRKRVC3gTRo2vM4Wy9WBC3TRcfnIZU8k65Phi3izkvPH975FowRYtKGT6PxevA0XnJ/yO8b0QwV0ydVyQwfw==}
|
||||||
|
dependencies:
|
||||||
|
pako: 1.0.11
|
||||||
|
dev: true
|
||||||
|
|
||||||
/kind-of@3.2.2:
|
/kind-of@3.2.2:
|
||||||
resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==}
|
resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -2354,6 +2405,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==}
|
resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/monaco-editor@0.43.0:
|
||||||
|
resolution: {integrity: sha512-cnoqwQi/9fml2Szamv1XbSJieGJ1Dc8tENVMD26Kcfl7xGQWp7OBKMjlwKVGYFJ3/AXJjSOGvcqK7Ry/j9BM1Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ms@2.0.0:
|
/ms@2.0.0:
|
||||||
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -2511,6 +2566,10 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pako@1.0.11:
|
||||||
|
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/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'}
|
||||||
@ -2662,6 +2721,11 @@ packages:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/q@1.5.1:
|
||||||
|
resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==}
|
||||||
|
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/queue-microtask@1.2.3:
|
/queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -3377,6 +3441,17 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
settings:
|
/zip-local@0.3.5:
|
||||||
autoInstallPeers: true
|
resolution: {integrity: sha512-GRV3D5TJY+/PqyeRm5CYBs7xVrKTKzljBoEXvocZu0HJ7tPEcgpSOYa2zFIsCZWgKWMuc4U3yMFgFkERGFIB9w==}
|
||||||
excludeLinksFromLockfile: false
|
dependencies:
|
||||||
|
async: 1.5.2
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jszip: 2.7.0
|
||||||
|
q: 1.5.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
github.com/mattdesl/gifenc/64842fca317b112a8590f8fef2bf3825da8f6fe3:
|
||||||
|
resolution: {tarball: https://codeload.github.com/mattdesl/gifenc/tar.gz/64842fca317b112a8590f8fef2bf3825da8f6fe3}
|
||||||
|
name: gifenc
|
||||||
|
version: 1.0.3
|
||||||
|
dev: false
|
||||||
|
@ -78,6 +78,7 @@ await Promise.all([
|
|||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_WEB: false,
|
IS_WEB: false,
|
||||||
|
IS_EXTENSION: false,
|
||||||
IS_DISCORD_DESKTOP: true,
|
IS_DISCORD_DESKTOP: true,
|
||||||
IS_VESKTOP: false
|
IS_VESKTOP: false
|
||||||
}
|
}
|
||||||
@ -124,6 +125,7 @@ await Promise.all([
|
|||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_WEB: false,
|
IS_WEB: false,
|
||||||
|
IS_EXTENSION: false,
|
||||||
IS_DISCORD_DESKTOP: false,
|
IS_DISCORD_DESKTOP: false,
|
||||||
IS_VESKTOP: true
|
IS_VESKTOP: true
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,11 @@
|
|||||||
* 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 esbuild from "esbuild";
|
import esbuild from "esbuild";
|
||||||
import { zip } from "fflate";
|
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { appendFile, mkdir, readFile, rm, writeFile } from "fs/promises";
|
import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
import Zip from "zip-local";
|
||||||
|
|
||||||
import { BUILD_TIMESTAMP, commonOpts, globPlugins, VERSION, watch } from "./common.mjs";
|
import { BUILD_TIMESTAMP, commonOpts, globPlugins, VERSION, watch } from "./common.mjs";
|
||||||
|
|
||||||
@ -42,6 +41,7 @@ const commonOptions = {
|
|||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
define: {
|
define: {
|
||||||
IS_WEB: "true",
|
IS_WEB: "true",
|
||||||
|
IS_EXTENSION: "false",
|
||||||
IS_STANDALONE: "true",
|
IS_STANDALONE: "true",
|
||||||
IS_DEV: JSON.stringify(watch),
|
IS_DEV: JSON.stringify(watch),
|
||||||
IS_DISCORD_DESKTOP: "false",
|
IS_DISCORD_DESKTOP: "false",
|
||||||
@ -52,19 +52,51 @@ const commonOptions = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MonacoWorkerEntryPoints = [
|
||||||
|
"vs/language/css/css.worker.js",
|
||||||
|
"vs/editor/editor.worker.js"
|
||||||
|
];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[
|
[
|
||||||
|
esbuild.build({
|
||||||
|
entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`),
|
||||||
|
bundle: true,
|
||||||
|
minify: true,
|
||||||
|
format: "iife",
|
||||||
|
outbase: "node_modules/monaco-editor/esm/",
|
||||||
|
outdir: "dist/monaco"
|
||||||
|
}),
|
||||||
|
esbuild.build({
|
||||||
|
entryPoints: ["browser/monaco.ts"],
|
||||||
|
bundle: true,
|
||||||
|
minify: true,
|
||||||
|
format: "iife",
|
||||||
|
outfile: "dist/monaco/index.js",
|
||||||
|
loader: {
|
||||||
|
".ttf": "file"
|
||||||
|
}
|
||||||
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
outfile: "dist/browser.js",
|
outfile: "dist/browser.js",
|
||||||
footer: { js: "//# sourceURL=VencordWeb" },
|
footer: { js: "//# sourceURL=VencordWeb" },
|
||||||
}),
|
}),
|
||||||
|
esbuild.build({
|
||||||
|
...commonOptions,
|
||||||
|
outfile: "dist/extension.js",
|
||||||
|
define: {
|
||||||
|
...commonOptions?.define,
|
||||||
|
IS_EXTENSION: "true",
|
||||||
|
},
|
||||||
|
footer: { js: "//# sourceURL=VencordWeb" },
|
||||||
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])],
|
inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])],
|
||||||
define: {
|
define: {
|
||||||
"window": "unsafeWindow",
|
...(commonOptions?.define),
|
||||||
...(commonOptions?.define)
|
window: "unsafeWindow",
|
||||||
},
|
},
|
||||||
outfile: "dist/Vencord.user.js",
|
outfile: "dist/Vencord.user.js",
|
||||||
banner: {
|
banner: {
|
||||||
@ -79,12 +111,39 @@ await Promise.all(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {(target: string, files: string[], shouldZip: boolean) => Promise<void>}
|
* @type {(dir: string) => Promise<string[]>}
|
||||||
*/
|
*/
|
||||||
async function buildPluginZip(target, files, shouldZip) {
|
async function globDir(dir) {
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
for (const child of await readdir(dir, { withFileTypes: true })) {
|
||||||
|
const p = join(dir, child.name);
|
||||||
|
if (child.isDirectory())
|
||||||
|
files.push(...await globDir(p));
|
||||||
|
else
|
||||||
|
files.push(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {(dir: string, basePath?: string) => Promise<Record<string, string>>}
|
||||||
|
*/
|
||||||
|
async function loadDir(dir, basePath = "") {
|
||||||
|
const files = await globDir(dir);
|
||||||
|
return Object.fromEntries(await Promise.all(files.map(async f => [f.slice(basePath.length), await readFile(f)])));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {(target: string, files: string[]) => Promise<void>}
|
||||||
|
*/
|
||||||
|
async function buildExtension(target, files) {
|
||||||
const entries = {
|
const entries = {
|
||||||
"dist/Vencord.js": await readFile("dist/browser.js"),
|
"dist/Vencord.js": await readFile("dist/extension.js"),
|
||||||
"dist/Vencord.css": await readFile("dist/browser.css"),
|
"dist/Vencord.css": await readFile("dist/extension.css"),
|
||||||
|
...await loadDir("dist/monaco"),
|
||||||
|
...await loadDir("browser/third-party", "browser/"),
|
||||||
...Object.fromEntries(await Promise.all(files.map(async f => {
|
...Object.fromEntries(await Promise.all(files.map(async f => {
|
||||||
let content = await readFile(join("browser", f));
|
let content = await readFile(join("browser", f));
|
||||||
if (f.startsWith("manifest")) {
|
if (f.startsWith("manifest")) {
|
||||||
@ -100,21 +159,6 @@ async function buildPluginZip(target, files, shouldZip) {
|
|||||||
}))),
|
}))),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldZip) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
zip(entries, {}, (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
const out = join("dist", target);
|
|
||||||
writeFile(out, data).then(() => {
|
|
||||||
console.info("Extension written to " + out);
|
|
||||||
resolve();
|
|
||||||
}).catch(reject);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await rm(target, { recursive: true, force: true });
|
await rm(target, { recursive: true, force: true });
|
||||||
await Promise.all(Object.entries(entries).map(async ([file, content]) => {
|
await Promise.all(Object.entries(entries).map(async ([file, content]) => {
|
||||||
const dest = join("dist", target, file);
|
const dest = join("dist", target, file);
|
||||||
@ -125,7 +169,6 @@ async function buildPluginZip(target, files, shouldZip) {
|
|||||||
|
|
||||||
console.info("Unpacked Extension written to dist/" + target);
|
console.info("Unpacked Extension written to dist/" + target);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content => {
|
const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content => {
|
||||||
const cssRuntime = `
|
const cssRuntime = `
|
||||||
@ -142,8 +185,9 @@ const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content
|
|||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
appendCssRuntime,
|
appendCssRuntime,
|
||||||
buildPluginZip("extension.zip", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"], true),
|
buildExtension("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"]),
|
||||||
buildPluginZip("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"], false),
|
buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]),
|
||||||
buildPluginZip("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"], false),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension.zip");
|
||||||
|
console.info("Packed Chromium Extension written to dist/extension.zip");
|
||||||
|
1
src/globals.d.ts
vendored
1
src/globals.d.ts
vendored
@ -33,6 +33,7 @@ declare global {
|
|||||||
* replace: `${IS_WEB}?foo:bar`
|
* replace: `${IS_WEB}?foo:bar`
|
||||||
*/
|
*/
|
||||||
export var IS_WEB: boolean;
|
export var IS_WEB: boolean;
|
||||||
|
export var IS_EXTENSION: boolean;
|
||||||
export var IS_DEV: boolean;
|
export var IS_DEV: boolean;
|
||||||
export var IS_STANDALONE: boolean;
|
export var IS_STANDALONE: boolean;
|
||||||
export var IS_UPDATER_DISABLED: boolean;
|
export var IS_UPDATER_DISABLED: boolean;
|
||||||
|
@ -27,7 +27,7 @@ import { mkdirSync, readFileSync, watch } from "fs";
|
|||||||
import { open, readdir, readFile, writeFile } from "fs/promises";
|
import { open, readdir, readFile, writeFile } from "fs/promises";
|
||||||
import { join, normalize } from "path";
|
import { join, normalize } from "path";
|
||||||
|
|
||||||
import monacoHtml from "~fileContent/../components/monacoWin.html;base64";
|
import monacoHtml from "~fileContent/monacoWin.html;base64";
|
||||||
|
|
||||||
import { getThemeInfo, stripBOM, UserThemeHeader } from "./themes";
|
import { getThemeInfo, stripBOM, UserThemeHeader } from "./themes";
|
||||||
import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, SETTINGS_DIR, SETTINGS_FILE, THEMES_DIR } from "./utils/constants";
|
import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, SETTINGS_DIR, SETTINGS_FILE, THEMES_DIR } from "./utils/constants";
|
||||||
|
@ -27,7 +27,7 @@ import { Alerts, Forms, UserStore } from "@webpack/common";
|
|||||||
import gitHash from "~git-hash";
|
import gitHash from "~git-hash";
|
||||||
import plugins from "~plugins";
|
import plugins from "~plugins";
|
||||||
|
|
||||||
import settings from "./_core/settings";
|
import settings from "./settings";
|
||||||
|
|
||||||
const REMEMBER_DISMISS_KEY = "Vencord-SupportHelper-Dismiss";
|
const REMEMBER_DISMISS_KEY = "Vencord-SupportHelper-Dismiss";
|
||||||
|
|
@ -127,6 +127,8 @@ export const defaultRules = [
|
|||||||
"redircnt@yandex.*",
|
"redircnt@yandex.*",
|
||||||
"feature@youtube.com",
|
"feature@youtube.com",
|
||||||
"kw@youtube.com",
|
"kw@youtube.com",
|
||||||
|
"si@youtube.com",
|
||||||
|
"pp@youtube.com",
|
||||||
"si@youtu.be",
|
"si@youtu.be",
|
||||||
"wt_zmc",
|
"wt_zmc",
|
||||||
"utm_source",
|
"utm_source",
|
||||||
|
5
src/plugins/dearrow/README.md
Normal file
5
src/plugins/dearrow/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Dearrow
|
||||||
|
|
||||||
|
Makes YouTube embed titles and thumbnails less sensationalist, powered by [Dearrow](https://dearrow.ajay.app/)
|
||||||
|
|
||||||
|
https://github.com/Vendicated/Vencord/assets/45497981/7bf81108-102d-47c5-8ba5-357db4db1283
|
161
src/plugins/dearrow/index.tsx
Normal file
161
src/plugins/dearrow/index.tsx
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import { Logger } from "@utils/Logger";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
import { Tooltip } from "@webpack/common";
|
||||||
|
import type { Component } from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
embed: {
|
||||||
|
rawTitle: string;
|
||||||
|
provider?: {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
thumbnail: {
|
||||||
|
proxyURL: string;
|
||||||
|
};
|
||||||
|
video: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
dearrow: {
|
||||||
|
enabled: boolean;
|
||||||
|
oldTitle?: string;
|
||||||
|
oldThumb?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const embedUrlRe = /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/;
|
||||||
|
|
||||||
|
async function embedDidMount(this: Component<Props>) {
|
||||||
|
try {
|
||||||
|
const { embed } = this.props;
|
||||||
|
if (!embed || embed.dearrow || embed.provider?.name !== "YouTube" || !embed.video?.url) return;
|
||||||
|
|
||||||
|
const videoId = embedUrlRe.exec(embed.video.url)?.[1];
|
||||||
|
if (!videoId) return;
|
||||||
|
|
||||||
|
const res = await fetch(`https://sponsor.ajay.app/api/branding?videoID=${videoId}`);
|
||||||
|
if (!res.ok) return;
|
||||||
|
|
||||||
|
const { titles, thumbnails } = await res.json();
|
||||||
|
|
||||||
|
const hasTitle = titles[0]?.votes >= 0;
|
||||||
|
const hasThumb = thumbnails[0]?.votes >= 0;
|
||||||
|
|
||||||
|
if (!hasTitle && !hasThumb) return;
|
||||||
|
|
||||||
|
embed.dearrow = {
|
||||||
|
enabled: true
|
||||||
|
};
|
||||||
|
|
||||||
|
if (titles[0]?.votes >= 0) {
|
||||||
|
embed.dearrow.oldTitle = embed.rawTitle;
|
||||||
|
embed.rawTitle = titles[0].title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thumbnails[0]?.votes >= 0) {
|
||||||
|
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
|
||||||
|
embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.forceUpdate();
|
||||||
|
} catch (err) {
|
||||||
|
new Logger("Dearrow").error("Failed to dearrow embed", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function DearrowButton({ component }: { component: Component<Props>; }) {
|
||||||
|
const { embed } = component.props;
|
||||||
|
if (!embed?.dearrow) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip text={embed.dearrow.enabled ? "This embed has been dearrowed, click to restore" : "Click to dearrow"}>
|
||||||
|
{({ onMouseEnter, onMouseLeave }) => (
|
||||||
|
<button
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
className={"vc-dearrow-toggle-" + (embed.dearrow.enabled ? "on" : "off")}
|
||||||
|
onClick={() => {
|
||||||
|
const { enabled, oldThumb, oldTitle } = embed.dearrow;
|
||||||
|
embed.dearrow.enabled = !enabled;
|
||||||
|
if (oldTitle) {
|
||||||
|
embed.dearrow.oldTitle = embed.rawTitle;
|
||||||
|
embed.rawTitle = oldTitle;
|
||||||
|
}
|
||||||
|
if (oldThumb) {
|
||||||
|
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
|
||||||
|
embed.thumbnail.proxyURL = oldThumb;
|
||||||
|
}
|
||||||
|
|
||||||
|
component.forceUpdate();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Dearrow Icon, taken from https://dearrow.ajay.app/logo.svg (and optimised) */}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24px"
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 36 36"
|
||||||
|
aria-label="Toggle Dearrow"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="#1213BD"
|
||||||
|
d="M36 18.302c0 4.981-2.46 9.198-5.655 12.462s-7.323 5.152-12.199 5.152s-9.764-1.112-12.959-4.376S0 23.283 0 18.302s2.574-9.38 5.769-12.644S13.271 0 18.146 0s9.394 2.178 12.589 5.442C33.931 8.706 36 13.322 36 18.302z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#88c9f9"
|
||||||
|
d="m 30.394282,18.410186 c 0,3.468849 -1.143025,6.865475 -3.416513,9.137917 -2.273489,2.272442 -5.670115,2.92874 -9.137918,2.92874 -3.467803,0 -6.373515,-1.147212 -8.6470033,-3.419654 -2.2734888,-2.272442 -3.5871299,-5.178154 -3.5871299,-8.647003 0,-3.46885 0.9420533,-6.746149 3.2144954,-9.0196379 2.2724418,-2.2734888 5.5507878,-3.9513905 9.0196378,-3.9513905 3.46885,0 6.492841,1.9322561 8.76633,4.204698 2.273489,2.2724424 3.788101,5.2974804 3.788101,8.7663304 z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#0a62a5"
|
||||||
|
d="m 23.95823,17.818306 c 0,3.153748 -2.644888,5.808102 -5.798635,5.808102 -3.153748,0 -5.599825,-2.654354 -5.599825,-5.808102 0,-3.153747 2.446077,-5.721714 5.599825,-5.721714 3.153747,0 5.798635,2.567967 5.798635,5.721714 z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "Dearrow",
|
||||||
|
description: "Makes YouTube embed titles and thumbnails less sensationalist, powered by Dearrow",
|
||||||
|
authors: [Devs.Ven],
|
||||||
|
|
||||||
|
embedDidMount,
|
||||||
|
renderButton(component: Component<Props>) {
|
||||||
|
return (
|
||||||
|
<ErrorBoundary noop>
|
||||||
|
<DearrowButton component={component} />
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
patches: [{
|
||||||
|
find: "this.renderInlineMediaEmbed",
|
||||||
|
replacement: [
|
||||||
|
// patch componentDidMount to replace embed thumbnail and title
|
||||||
|
{
|
||||||
|
match: /(\i).render=function.{0,50}\i\.embed/,
|
||||||
|
replace: "$1.componentDidMount=$self.embedDidMount,$&"
|
||||||
|
},
|
||||||
|
|
||||||
|
// add dearrow button
|
||||||
|
{
|
||||||
|
match: /children:\[(?=null!=\i\?\i\.renderSuppressButton)/,
|
||||||
|
replace: "children:[$self.renderButton(this),"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
});
|
12
src/plugins/dearrow/styles.css
Normal file
12
src/plugins/dearrow/styles.css
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.vc-dearrow-toggle-off svg {
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-dearrow-toggle-on, .vc-dearrow-toggle-off {
|
||||||
|
all: unset;
|
||||||
|
display: inline;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.75rem;
|
||||||
|
right: 0.75rem;
|
||||||
|
}
|
@ -19,7 +19,7 @@
|
|||||||
import { addPreEditListener, addPreSendListener, removePreEditListener, removePreSendListener } from "@api/MessageEvents";
|
import { addPreEditListener, addPreSendListener, removePreEditListener, removePreSendListener } from "@api/MessageEvents";
|
||||||
import { definePluginSettings, Settings } from "@api/Settings";
|
import { definePluginSettings, Settings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { ApngBlendOp, ApngDisposeOp, getGifEncoder, importApngJs } from "@utils/dependencies";
|
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
|
||||||
import { getCurrentGuild } from "@utils/discord";
|
import { getCurrentGuild } from "@utils/discord";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
import { proxyLazy } from "@utils/lazy";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
@ -27,6 +27,7 @@ import definePlugin, { OptionType } from "@utils/types";
|
|||||||
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
||||||
import { ChannelStore, EmojiStore, FluxDispatcher, Parser, PermissionStore, UserStore } from "@webpack/common";
|
import { ChannelStore, EmojiStore, FluxDispatcher, Parser, PermissionStore, UserStore } from "@webpack/common";
|
||||||
import type { Message } from "discord-types/general";
|
import type { Message } from "discord-types/general";
|
||||||
|
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||||
import type { ReactElement, ReactNode } from "react";
|
import type { ReactElement, ReactNode } from "react";
|
||||||
|
|
||||||
const DRAFT_TYPE = 0;
|
const DRAFT_TYPE = 0;
|
||||||
@ -212,15 +213,15 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "canStreamHighQuality:function",
|
find: "canUseHighVideoUploadQuality:function",
|
||||||
predicate: () => settings.store.enableStreamQualityBypass,
|
predicate: () => settings.store.enableStreamQualityBypass,
|
||||||
replacement: [
|
replacement: [
|
||||||
"canUseHighVideoUploadQuality",
|
"canUseHighVideoUploadQuality",
|
||||||
"canStreamHighQuality",
|
// TODO: Remove the last two when they get removed from stable
|
||||||
"canStreamMidQuality"
|
"(?:canStreamQuality|canStreamHighQuality|canStreamMidQuality)",
|
||||||
].map(func => {
|
].map(func => {
|
||||||
return {
|
return {
|
||||||
match: new RegExp(`${func}:function\\(\\i\\){`),
|
match: new RegExp(`${func}:function\\(\\i(?:,\\i)?\\){`, "g"),
|
||||||
replace: "$&return true;"
|
replace: "$&return true;"
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@ -650,15 +651,11 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async sendAnimatedSticker(stickerLink: string, stickerId: string, channelId: string) {
|
async sendAnimatedSticker(stickerLink: string, stickerId: string, channelId: string) {
|
||||||
const [{ parseURL }, {
|
const { parseURL } = importApngJs();
|
||||||
GIFEncoder,
|
|
||||||
quantize,
|
|
||||||
applyPalette
|
|
||||||
}] = await Promise.all([importApngJs(), getGifEncoder()]);
|
|
||||||
|
|
||||||
const { frames, width, height } = await parseURL(stickerLink);
|
const { frames, width, height } = await parseURL(stickerLink);
|
||||||
|
|
||||||
const gif = new GIFEncoder();
|
const gif = GIFEncoder();
|
||||||
const resolution = Settings.plugins.FakeNitro.stickerSize;
|
const resolution = Settings.plugins.FakeNitro.stickerSize;
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
|
6
src/plugins/favEmojiFirst/README.md
Normal file
6
src/plugins/favEmojiFirst/README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# FavoriteEmojiFirst
|
||||||
|
|
||||||
|
Puts your favorite emoji first in the emoji autocomplete.
|
||||||
|
|
||||||
|
![FavEmojis](https://i.imgur.com/mEFCoZG.png)
|
||||||
|
![Example](https://i.imgur.com/wY3Tc43.png)
|
5
src/plugins/favGifSearch/README.md
Normal file
5
src/plugins/favGifSearch/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# FavoriteGifSearch
|
||||||
|
|
||||||
|
Adds a search bar to favorite gifs.
|
||||||
|
|
||||||
|
![Screenshot](https://i.imgur.com/Bcgb7PD.png)
|
@ -60,7 +60,7 @@ interface Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchHeader", "gutterSize");
|
const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchHeader", "searchInput");
|
||||||
|
|
||||||
export const settings = definePluginSettings({
|
export const settings = definePluginSettings({
|
||||||
searchOption: {
|
searchOption: {
|
||||||
@ -87,7 +87,7 @@ export const settings = definePluginSettings({
|
|||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "FavoriteGifSearch",
|
name: "FavoriteGifSearch",
|
||||||
authors: [Devs.Aria],
|
authors: [Devs.Aria],
|
||||||
description: "Adds a search bar for favorite gifs",
|
description: "Adds a search bar to favorite gifs.",
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
6
src/plugins/imageZoom/README.md
Normal file
6
src/plugins/imageZoom/README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# ImageZoom
|
||||||
|
|
||||||
|
Lets you zoom in to images and gifs. Use scroll wheel to zoom in and shift + scroll wheel to increase lens radius / size
|
||||||
|
|
||||||
|
![Example](https://i.imgur.com/VJdo4aq.png)
|
||||||
|
![ContextMenu](https://i.imgur.com/0oaRM2s.png)
|
@ -99,6 +99,15 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = children => () => {
|
|||||||
ContextMenu.close();
|
ContextMenu.close();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Menu.MenuCheckboxItem
|
||||||
|
id="vc-nearest-neighbour"
|
||||||
|
label="Nearset Neighbour"
|
||||||
|
checked={settings.store.nearestNeighbour}
|
||||||
|
action={() => {
|
||||||
|
settings.store.nearestNeighbour = !settings.store.nearestNeighbour;
|
||||||
|
ContextMenu.close();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Menu.MenuControlItem
|
<Menu.MenuControlItem
|
||||||
id="vc-zoom"
|
id="vc-zoom"
|
||||||
label="Zoom"
|
label="Zoom"
|
||||||
|
@ -28,7 +28,7 @@ import { Button, Forms, React, TextInput } from "@webpack/common";
|
|||||||
import { decrypt } from "../index";
|
import { decrypt } from "../index";
|
||||||
|
|
||||||
export function DecModal(props: any) {
|
export function DecModal(props: any) {
|
||||||
const secret: string = props?.message?.content;
|
const encryptedMessage: string = props?.message?.content;
|
||||||
const [password, setPassword] = React.useState("password");
|
const [password, setPassword] = React.useState("password");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -38,9 +38,9 @@ export function DecModal(props: any) {
|
|||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Secret</Forms.FormTitle>
|
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Message with Encryption</Forms.FormTitle>
|
||||||
<TextInput defaultValue={secret} disabled={true}></TextInput>
|
<TextInput defaultValue={encryptedMessage} disabled={true}></TextInput>
|
||||||
<Forms.FormTitle tag="h5">Password</Forms.FormTitle>
|
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Password</Forms.FormTitle>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={{ marginBottom: "20px" }}
|
style={{ marginBottom: "20px" }}
|
||||||
onChange={setPassword}
|
onChange={setPassword}
|
||||||
@ -51,7 +51,7 @@ export function DecModal(props: any) {
|
|||||||
<Button
|
<Button
|
||||||
color={Button.Colors.GREEN}
|
color={Button.Colors.GREEN}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const toSend = decrypt(secret, password, true);
|
const toSend = decrypt(encryptedMessage, password, true);
|
||||||
if (!toSend || !props?.message) return;
|
if (!toSend || !props?.message) return;
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
Vencord.Plugins.plugins.InvisibleChat.buildEmbed(props?.message, toSend);
|
Vencord.Plugins.plugins.InvisibleChat.buildEmbed(props?.message, toSend);
|
@ -225,8 +225,8 @@ export function encrypt(secret: string, password: string, cover: string): string
|
|||||||
return steggo.hide(secret + "\u200b", password, cover);
|
return steggo.hide(secret + "\u200b", password, cover);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decrypt(secret: string, password: string, removeIndicator: boolean): string {
|
export function decrypt(encrypted: string, password: string, removeIndicator: boolean): string {
|
||||||
const decrypted = steggo.reveal(secret, password);
|
const decrypted = steggo.reveal(encrypted, password);
|
||||||
return removeIndicator ? decrypted.replace("\u200b", "") : decrypted;
|
return removeIndicator ? decrypted.replace("\u200b", "") : decrypted;
|
||||||
}
|
}
|
||||||
|
|
@ -72,6 +72,14 @@ const enum ActivityFlag {
|
|||||||
INSTANCE = 1 << 0,
|
INSTANCE = 1 << 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enum NameFormat {
|
||||||
|
StatusName = "status-name",
|
||||||
|
ArtistFirst = "artist-first",
|
||||||
|
SongFirst = "song-first",
|
||||||
|
ArtistOnly = "artist",
|
||||||
|
SongOnly = "song"
|
||||||
|
}
|
||||||
|
|
||||||
const applicationId = "1108588077900898414";
|
const applicationId = "1108588077900898414";
|
||||||
const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
|
const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
|
||||||
|
|
||||||
@ -117,10 +125,37 @@ const settings = definePluginSettings({
|
|||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
statusName: {
|
statusName: {
|
||||||
description: "text shown in status",
|
description: "custom status text",
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
default: "some music",
|
default: "some music",
|
||||||
},
|
},
|
||||||
|
nameFormat: {
|
||||||
|
description: "Show name of song and artist in status name",
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: "Use custom status name",
|
||||||
|
value: NameFormat.StatusName,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Use format 'artist - song'",
|
||||||
|
value: NameFormat.ArtistFirst
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Use format 'song - artist'",
|
||||||
|
value: NameFormat.SongFirst
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Use artist name only",
|
||||||
|
value: NameFormat.ArtistOnly
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Use song name only",
|
||||||
|
value: NameFormat.SongOnly
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
useListeningStatus: {
|
useListeningStatus: {
|
||||||
description: 'show "Listening to" status instead of "Playing"',
|
description: 'show "Listening to" status instead of "Playing"',
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
@ -140,13 +175,13 @@ const settings = definePluginSettings({
|
|||||||
value: "placeholder"
|
value: "placeholder"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "LastFMRichPresence",
|
name: "LastFMRichPresence",
|
||||||
description: "Little plugin for Last.fm rich presence",
|
description: "Little plugin for Last.fm rich presence",
|
||||||
authors: [Devs.dzshn, Devs.RuiNtD],
|
authors: [Devs.dzshn, Devs.RuiNtD, Devs.blahajZip, Devs.archeruwu],
|
||||||
|
|
||||||
settingsAboutComponent: () => (
|
settingsAboutComponent: () => (
|
||||||
<>
|
<>
|
||||||
@ -267,9 +302,24 @@ export default definePlugin({
|
|||||||
url: `https://www.last.fm/user/${settings.store.username}`,
|
url: `https://www.last.fm/user/${settings.store.username}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const statusName = (() => {
|
||||||
|
switch (settings.store.nameFormat) {
|
||||||
|
case NameFormat.ArtistFirst:
|
||||||
|
return trackData.artist + " - " + trackData.name;
|
||||||
|
case NameFormat.SongFirst:
|
||||||
|
return trackData.name + " - " + trackData.artist;
|
||||||
|
case NameFormat.ArtistOnly:
|
||||||
|
return trackData.artist;
|
||||||
|
case NameFormat.SongOnly:
|
||||||
|
return trackData.name;
|
||||||
|
default:
|
||||||
|
return settings.store.statusName;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
application_id: applicationId,
|
application_id: applicationId,
|
||||||
name: settings.store.statusName,
|
name: statusName,
|
||||||
|
|
||||||
details: trackData.name,
|
details: trackData.name,
|
||||||
state: trackData.artist,
|
state: trackData.artist,
|
||||||
|
@ -326,14 +326,14 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
// Attachment renderer
|
// Attachment renderer
|
||||||
// Module 96063
|
// Module 96063
|
||||||
find: "[\"className\",\"attachment\",\"inlineMedia\"",
|
find: "().removeAttachmentHoverButton",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /((\w)\.className,\w=\2\.attachment),/,
|
match: /((\w)\.className,\w=\2\.attachment),/,
|
||||||
replace: "$1,deleted=$2.attachment?.deleted,"
|
replace: "$1,deleted=$2.attachment?.deleted,"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /\["className","attachment","inlineMedia".+?className:/,
|
match: /\["className","attachment".+?className:/,
|
||||||
replace: "$& (deleted ? 'messagelogger-deleted-attachment ' : '') +"
|
replace: "$& (deleted ? 'messagelogger-deleted-attachment ' : '') +"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -185,7 +185,7 @@ const settings = definePluginSettings({
|
|||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MoreUserTags",
|
name: "MoreUserTags",
|
||||||
description: "Adds tags for webhooks and moderative roles (owner, admin, etc.)",
|
description: "Adds tags for webhooks and moderative roles (owner, admin, etc.)",
|
||||||
authors: [Devs.Cyn, Devs.TheSun, Devs.RyanCaoDev, Devs.LordElias],
|
authors: [Devs.Cyn, Devs.TheSun, Devs.RyanCaoDev, Devs.LordElias, Devs.AutumnVN],
|
||||||
settings,
|
settings,
|
||||||
patches: [
|
patches: [
|
||||||
// add tags to the tag list
|
// add tags to the tag list
|
||||||
@ -232,9 +232,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".renderBot=function(){",
|
find: ".renderBot=function(){",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /this.props.user;return null!=(\i)&&.{0,10}\?(.{0,50})\.botTag/,
|
match: /\.BOT;return null!=(\i)&&.{0,10}\?(.{0,50})\.botTag,type:\i/,
|
||||||
replace: "this.props.user;var type=$self.getTag({...this.props,origType:$1.bot?0:null,location:'not-chat'});\
|
replace: ".BOT;var type=$self.getTag({...this.props,origType:$1.bot?0:null,location:'not-chat'});return type!==null?$2.botTag,type"
|
||||||
return type!==null?$2.botTag,type"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// pass channel id down props to be used in profiles
|
// pass channel id down props to be used in profiles
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
|
|
||||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, CommandContext, findOption, sendBotMessage } from "@api/Commands";
|
import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, CommandContext, findOption, sendBotMessage } from "@api/Commands";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { getGifEncoder } from "@utils/dependencies";
|
|
||||||
import { makeLazy } from "@utils/lazy";
|
import { makeLazy } from "@utils/lazy";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||||
|
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||||
|
|
||||||
const DRAFT_TYPE = 0;
|
const DRAFT_TYPE = 0;
|
||||||
const DEFAULT_DELAY = 20;
|
const DEFAULT_DELAY = 20;
|
||||||
@ -124,7 +124,6 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
execute: async (opts, cmdCtx) => {
|
execute: async (opts, cmdCtx) => {
|
||||||
const { GIFEncoder, quantize, applyPalette } = await getGifEncoder();
|
|
||||||
const frames = await getFrames();
|
const frames = await getFrames();
|
||||||
|
|
||||||
const noServerPfp = findOption(opts, "no-server-pfp", false);
|
const noServerPfp = findOption(opts, "no-server-pfp", false);
|
||||||
@ -143,7 +142,7 @@ export default definePlugin({
|
|||||||
const delay = findOption(opts, "delay", DEFAULT_DELAY);
|
const delay = findOption(opts, "delay", DEFAULT_DELAY);
|
||||||
const resolution = findOption(opts, "resolution", DEFAULT_RESOLUTION);
|
const resolution = findOption(opts, "resolution", DEFAULT_RESOLUTION);
|
||||||
|
|
||||||
const gif = new GIFEncoder();
|
const gif = GIFEncoder();
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
canvas.width = canvas.height = resolution;
|
canvas.width = canvas.height = resolution;
|
||||||
|
@ -29,8 +29,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".onRemoveAttachment,",
|
find: ".onRemoveAttachment,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.nonMediaAttachment.{0,10}children:\[(\i),/,
|
match: /\.nonMediaAttachment,!(\i).{0,7}children:\[(\i),/,
|
||||||
replace: "$&$1&&$self.renderPiPButton(),"
|
replace: "$&$1&&$2&&$self.renderPiPButton(),"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
5
src/plugins/previewMessage/README.md
Normal file
5
src/plugins/previewMessage/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# PreviewMessage
|
||||||
|
|
||||||
|
Lets you preview your message before sending it.
|
||||||
|
|
||||||
|
![Example](https://i.imgur.com/etqbkzu.png)
|
@ -16,37 +16,96 @@
|
|||||||
* 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 { sendBotMessage } from "@api/Commands";
|
import { generateId, sendBotMessage } from "@api/Commands";
|
||||||
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 from "@utils/types";
|
||||||
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { Button, ButtonLooks, ButtonWrapperClasses, DraftStore, DraftType, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
import { Button, ButtonLooks, ButtonWrapperClasses, DraftStore, DraftType, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||||
|
import { MessageAttachment } from "discord-types/general";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
type: {
|
type: {
|
||||||
analyticsName: string;
|
analyticsName: string;
|
||||||
|
isEmpty: boolean;
|
||||||
|
attachments: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UploadStore = findByPropsLazy("getUploads");
|
||||||
|
|
||||||
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
|
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
|
||||||
|
|
||||||
|
|
||||||
|
const getImageBox = (url: string): Promise<{ width: number, height: number; } | null> =>
|
||||||
|
new Promise(res => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () =>
|
||||||
|
res({ width: img.width, height: img.height });
|
||||||
|
|
||||||
|
img.onerror = () =>
|
||||||
|
res(null);
|
||||||
|
|
||||||
|
img.src = url;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const getAttachments = async (channelId: string) =>
|
||||||
|
await Promise.all(
|
||||||
|
UploadStore.getUploads(channelId, DraftType.ChannelMessage)
|
||||||
|
.map(async (upload: any) => {
|
||||||
|
const { isImage, filename, spoiler, item: { file } } = upload;
|
||||||
|
const url = URL.createObjectURL(file);
|
||||||
|
const attachment: MessageAttachment = {
|
||||||
|
id: generateId(),
|
||||||
|
filename: spoiler ? "SPOILER_" + filename : filename,
|
||||||
|
// weird eh? if i give it the normal content type the preview doenst work
|
||||||
|
content_type: undefined,
|
||||||
|
size: await upload.getSize(),
|
||||||
|
spoiler,
|
||||||
|
// discord adds query params to the url, so we need to add a hash to prevent that
|
||||||
|
url: url + "#",
|
||||||
|
proxy_url: url + "#",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isImage) {
|
||||||
|
const box = await getImageBox(url);
|
||||||
|
if (!box) return attachment;
|
||||||
|
|
||||||
|
attachment.width = box.width;
|
||||||
|
attachment.height = box.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attachment;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
export function PreviewButton(chatBoxProps: Props) {
|
export function PreviewButton(chatBoxProps: Props) {
|
||||||
|
const { isEmpty, attachments } = chatBoxProps.type;
|
||||||
|
|
||||||
const channelId = SelectedChannelStore.getChannelId();
|
const channelId = SelectedChannelStore.getChannelId();
|
||||||
const draft = useStateFromStores([DraftStore], () => getDraft(channelId));
|
const draft = useStateFromStores([DraftStore], () => getDraft(channelId));
|
||||||
|
|
||||||
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
||||||
if (!draft) return null;
|
|
||||||
|
const hasAttachments = attachments && UploadStore.getUploads(channelId, DraftType.ChannelMessage).length > 0;
|
||||||
|
const hasContent = !isEmpty && draft?.length > 0;
|
||||||
|
|
||||||
|
if (!hasContent && !hasAttachments) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip text="Preview Message">
|
<Tooltip text="Preview Message">
|
||||||
{tooltipProps => (
|
{tooltipProps => (
|
||||||
<Button
|
<Button
|
||||||
{...tooltipProps}
|
{...tooltipProps}
|
||||||
onClick={() =>
|
onClick={async () =>
|
||||||
sendBotMessage(
|
sendBotMessage(
|
||||||
channelId,
|
channelId,
|
||||||
{
|
{
|
||||||
content: getDraft(channelId),
|
content: getDraft(channelId),
|
||||||
author: UserStore.getCurrentUser()
|
author: UserStore.getCurrentUser(),
|
||||||
|
attachments: hasAttachments ? await getAttachments(channelId) : undefined,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
size=""
|
size=""
|
||||||
@ -66,7 +125,7 @@ export function PreviewButton(chatBoxProps: Props) {
|
|||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "PreviewMessage",
|
name: "PreviewMessage",
|
||||||
description: "Lets you preview your message before sending it",
|
description: "Lets you preview your message before sending it.",
|
||||||
authors: [Devs.Aria],
|
authors: [Devs.Aria],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
5
src/plugins/searchReply/README.md
Normal file
5
src/plugins/searchReply/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# SearchReply
|
||||||
|
|
||||||
|
Adds a reply button to search results.
|
||||||
|
|
||||||
|
![Screenshot](https://i.imgur.com/SjIEHpw.png)
|
@ -213,15 +213,12 @@ function applyRules(content: string): string {
|
|||||||
if (content.length === 0)
|
if (content.length === 0)
|
||||||
return content;
|
return content;
|
||||||
|
|
||||||
// pad so that rules can use " word " to only match whole "word"
|
|
||||||
content = " " + content + " ";
|
|
||||||
|
|
||||||
if (stringRules) {
|
if (stringRules) {
|
||||||
for (const rule of stringRules) {
|
for (const rule of stringRules) {
|
||||||
if (!rule.find || !rule.replace) continue;
|
if (!rule.find || !rule.replace) continue;
|
||||||
if (rule.onlyIfIncludes && !content.includes(rule.onlyIfIncludes)) continue;
|
if (rule.onlyIfIncludes && !content.includes(rule.onlyIfIncludes)) continue;
|
||||||
|
|
||||||
content = content.replaceAll(rule.find, rule.replace.replaceAll("\\n", "\n"));
|
content = ` ${content} `.replaceAll(rule.find, rule.replace.replaceAll("\\n", "\n")).replace(/^\s|\s$/g, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1168
src/utils/apng-canvas.js
Normal file
1168
src/utils/apng-canvas.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -366,6 +366,14 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||||||
RuukuLada: {
|
RuukuLada: {
|
||||||
name: "RuukuLada",
|
name: "RuukuLada",
|
||||||
id: 119705748346241027n,
|
id: 119705748346241027n,
|
||||||
|
},
|
||||||
|
blahajZip: {
|
||||||
|
name: "blahaj.zip",
|
||||||
|
id: 683954422241427471n,
|
||||||
|
},
|
||||||
|
archeruwu: {
|
||||||
|
name: "archer_uwu",
|
||||||
|
id: 160068695383736320n
|
||||||
}
|
}
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
|
@ -17,23 +17,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { makeLazy } from "./lazy";
|
import { makeLazy } from "./lazy";
|
||||||
|
import { EXTENSION_BASE_URL } from "./web-metadata";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Add dynamically loaded dependencies for plugins here.
|
Add dynamically loaded dependencies for plugins here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// https://github.com/mattdesl/gifenc
|
|
||||||
// this lib is way better than gif.js and all other libs, they're all so terrible but this one is nice
|
|
||||||
// @ts-ignore ts mad
|
|
||||||
export const getGifEncoder = makeLazy(() => import("https://unpkg.com/gifenc@1.0.3/dist/gifenc.esm.js"));
|
|
||||||
|
|
||||||
// needed to parse APNGs in the nitroBypass plugin
|
// needed to parse APNGs in the nitroBypass plugin
|
||||||
export const importApngJs = makeLazy(async () => {
|
export const importApngJs = makeLazy(() => {
|
||||||
const exports = {};
|
return require("./apng-canvas").APNG as { parseURL(url: string): Promise<ApngFrameData>; };
|
||||||
const winProxy = new Proxy(window, { set: (_, k, v) => exports[k] = v });
|
|
||||||
Function("self", await fetch("https://cdnjs.cloudflare.com/ajax/libs/apng-canvas/2.1.1/apng-canvas.min.js").then(r => r.text()))(winProxy);
|
|
||||||
// @ts-ignore
|
|
||||||
return exports.APNG as { parseURL(url: string): Promise<ApngFrameData>; };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk
|
// https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk
|
||||||
@ -75,13 +67,20 @@ export interface ApngFrameData {
|
|||||||
playTime: number;
|
playTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const shikiWorkerDist = "https://unpkg.com/@vap/shiki-worker@0.0.8/dist";
|
// On web (extensions), use extension uri as basepath (load files from extension)
|
||||||
export const shikiWorkerSrc = `${shikiWorkerDist}/${IS_DEV ? "index.js" : "index.min.js"}`;
|
// On desktop (electron), load from cdn
|
||||||
export const shikiOnigasmSrc = "https://unpkg.com/@vap/shiki@0.10.3/dist/onig.wasm";
|
export const rnnoiseDist = IS_EXTENSION
|
||||||
|
? new URL("/third-party/rnnoise", EXTENSION_BASE_URL).toString()
|
||||||
export const rnnoiseDist = "https://unpkg.com/@sapphi-red/web-noise-suppressor@0.3.3/dist";
|
: "https://unpkg.com/@sapphi-red/web-noise-suppressor@0.3.3/dist";
|
||||||
export const rnnoiseWasmSrc = (simd = false) => `${rnnoiseDist}/rnnoise${simd ? "_simd" : ""}.wasm`;
|
export const rnnoiseWasmSrc = (simd = false) => `${rnnoiseDist}/rnnoise${simd ? "_simd" : ""}.wasm`;
|
||||||
export const rnnoiseWorkletSrc = `${rnnoiseDist}/rnnoise/workletProcessor.js`;
|
export const rnnoiseWorkletSrc = `${rnnoiseDist}/rnnoise/workletProcessor.js`;
|
||||||
|
|
||||||
// @ts-expect-error SHUT UP
|
|
||||||
export const getStegCloak = makeLazy(() => import("https://unpkg.com/stegcloak-dist@1.0.0/index.js"));
|
// The below code is only used on the Desktop (electron) build of Vencord.
|
||||||
|
// Browser (extension) builds do not contain these remote imports.
|
||||||
|
|
||||||
|
export const shikiWorkerSrc = `https://unpkg.com/@vap/shiki-worker@0.0.8/dist/${IS_DEV ? "index.js" : "index.min.js"}`;
|
||||||
|
export const shikiOnigasmSrc = "https://unpkg.com/@vap/shiki@0.10.3/dist/onig.wasm";
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
export const getStegCloak = /* #__PURE__*/ makeLazy(() => import("https://unpkg.com/stegcloak-dist@1.0.0/index.js"));
|
||||||
|
14
src/utils/web-metadata.ts
Normal file
14
src/utils/web-metadata.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
export let EXTENSION_BASE_URL: string;
|
||||||
|
export let EXTENSION_VERSION: string;
|
||||||
|
|
||||||
|
if (IS_EXTENSION) {
|
||||||
|
const script = document.querySelector("#vencord-script") as HTMLScriptElement;
|
||||||
|
EXTENSION_BASE_URL = script.dataset.extensionBaseUrl!;
|
||||||
|
EXTENSION_VERSION = script.dataset.version!;
|
||||||
|
}
|
9
src/webpack/common/types/menu.d.ts
vendored
9
src/webpack/common/types/menu.d.ts
vendored
@ -65,8 +65,13 @@ export interface Menu {
|
|||||||
id: string;
|
id: string;
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
}>;
|
}>;
|
||||||
// TODO: Type me
|
MenuSliderControl: RC<{
|
||||||
MenuSliderControl: RC<any>;
|
minValue: number,
|
||||||
|
maxValue: number,
|
||||||
|
value: number,
|
||||||
|
onChange(value: number): void,
|
||||||
|
renderValue?(value: number): string,
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContextMenuApi {
|
export interface ContextMenuApi {
|
||||||
|
Reference in New Issue
Block a user