Compare commits
97 Commits
serverProf
...
v1.5.5
Author | SHA1 | Date | |
---|---|---|---|
|
9e63da6d78 | ||
|
79295683ee | ||
|
5eb9dd04df | ||
|
03b5dc9c27 | ||
|
726a1b5d96 | ||
|
581fe252a4 | ||
|
30b2e88e77 | ||
|
1f38a8eeab | ||
|
6db9721c06 | ||
|
9891791fa7 | ||
|
8dd5eeead2 | ||
|
abf8667a5d | ||
|
e33ac900bc | ||
|
8a026060c7 | ||
|
c8b77bb187 | ||
|
88b06191b9 | ||
|
62277770a8 | ||
|
4facc3cad7 | ||
|
837d1fc083 | ||
|
608a67c9ae | ||
|
f32d25b641 | ||
|
4c7a2ba340 | ||
|
da7f0cfff6 | ||
|
ae6584da7c | ||
|
c6b1b9463c | ||
|
376aaf39ce | ||
|
5454a41243 | ||
|
044f64e446 | ||
|
0b7fca864a | ||
|
30ac256070 | ||
|
d0e2a32471 | ||
|
ec026ca34c | ||
|
4baaa9bd91 | ||
|
d9933c5793 | ||
|
2735037a67 | ||
|
83bfe28fa4 | ||
|
08c5d23636 | ||
|
ac0f834155 | ||
|
fa16e1b56f | ||
|
cfca393f2b | ||
|
eacc673bcc | ||
|
dba6c4cea6 | ||
|
6b10947d06 | ||
|
97b6699afe | ||
|
7e91edc757 | ||
|
a82544e93e | ||
|
d8c8b74ed7 | ||
|
fadd1598f5 | ||
|
e5c0898dd6 | ||
|
9550b74b2a | ||
|
6cfb67a52e | ||
|
12fe367385 | ||
|
fc0417eb80 | ||
|
1f87d14ab2 | ||
|
7d8f3508a8 | ||
|
41f5d71e38 | ||
|
efb88a4df8 | ||
|
a73d09a2f0 | ||
|
cf7c4d63b6 | ||
|
a95311ef2c | ||
|
dd23f9802c | ||
|
f23ddf4cae | ||
|
4222c7fd9f | ||
|
09f65b401e | ||
|
7364776715 | ||
|
3a5b70d410 | ||
|
e08d49edac | ||
|
17abbd3e3e | ||
|
25a1d934c6 | ||
|
620319a4bc | ||
|
4c9996d620 | ||
|
885c75fdaa | ||
|
f2a22c5e57 | ||
|
5e3a485edc | ||
|
452bf72e56 | ||
|
afa47addd7 | ||
|
8ecee3d09f | ||
|
d2aac5edc5 | ||
|
fc45510053 | ||
|
77bfaf38a5 | ||
|
a4cadc03d8 | ||
|
315fcf1972 | ||
|
f1b3b3c0a3 | ||
|
4c805d08be | ||
|
9b987d1e56 | ||
|
774318d583 | ||
|
7d954f9ade | ||
|
860d6edc7b | ||
|
223b0366c6 | ||
|
69cb7593eb | ||
|
8614e17633 | ||
|
aecd9d8fda | ||
|
faeb4fb585 | ||
|
0d18b44ba7 | ||
|
d671bd65ac | ||
|
8e952c630b | ||
|
2c758ccdf8 |
30
.github/ISSUE_TEMPLATE/blank.yml
vendored
Normal file
30
.github/ISSUE_TEMPLATE/blank.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Blank Issue
|
||||||
|
description: Create a blank issue. ALWAYS FIRST USE OUR SUPPORT CHANNEL! ONLY USE THIS FORM IF YOU ARE A CONTRIBUTOR OR WERE TOLD TO DO SO IN THE SUPPORT CHANNEL.
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# READ THIS BEFORE OPENING AN ISSUE
|
||||||
|
|
||||||
|
This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS.
|
||||||
|
|
||||||
|
DO NOT USE THIS FORM, unless
|
||||||
|
- you are a vencord contributor
|
||||||
|
- you were given explicit permission to use this form by a moderator in our support server
|
||||||
|
- you are filing a security related report
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: content
|
||||||
|
attributes:
|
||||||
|
label: Content
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: agreement-check
|
||||||
|
attributes:
|
||||||
|
label: Request Agreement
|
||||||
|
options:
|
||||||
|
- label: I have read the requirements for opening an issue above
|
||||||
|
required: true
|
16
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
16
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,9 +1,21 @@
|
|||||||
name: Bug/Crash Report
|
name: Bug/Crash Report
|
||||||
description: Create a bug or crash report for Vencord
|
description: Create a bug or crash report for Vencord. ALWAYS FIRST USE OUR SUPPORT CHANNEL! ONLY USE THIS FORM IF YOU ARE A CONTRIBUTOR OR WERE TOLD TO DO SO IN THE SUPPORT CHANNEL.
|
||||||
labels: [bug]
|
labels: [bug]
|
||||||
title: "[Bug] <title>"
|
title: "[Bug] <title>"
|
||||||
|
|
||||||
body:
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# READ THIS BEFORE OPENING AN ISSUE
|
||||||
|
|
||||||
|
This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS.
|
||||||
|
|
||||||
|
DO NOT USE THIS FORM, unless
|
||||||
|
- you are a vencord contributor
|
||||||
|
- you were given explicit permission to use this form by a moderator in our support server
|
||||||
|
- you are filing a security related report
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: discord
|
id: discord
|
||||||
attributes:
|
attributes:
|
||||||
@ -64,3 +76,5 @@ body:
|
|||||||
options:
|
options:
|
||||||
- label: I am using Discord Stable or tried on Stable and this bug happens there as well
|
- label: I am using Discord Stable or tried on Stable and this bug happens there as well
|
||||||
required: true
|
required: true
|
||||||
|
- label: I have read the requirements for opening an issue above
|
||||||
|
required: true
|
||||||
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Vencord Support Server
|
- name: Vencord Support Server
|
||||||
url: https://discord.gg/D9uwnFnqmd
|
url: https://discord.gg/D9uwnFnqmd
|
||||||
|
32
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
32
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,32 +0,0 @@
|
|||||||
name: Feature Request
|
|
||||||
description: Create a feature request for Vencord. To request new plugins, please use the Discussions tab
|
|
||||||
labels: [enhancement]
|
|
||||||
title: "[Feature Request] <title>"
|
|
||||||
|
|
||||||
body:
|
|
||||||
- type: input
|
|
||||||
id: discord
|
|
||||||
attributes:
|
|
||||||
label: Discord Account
|
|
||||||
description: Who on Discord is making this request? Not required but encouraged for easier follow-up
|
|
||||||
placeholder: username#0000
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: feature-basic-description
|
|
||||||
attributes:
|
|
||||||
label: What is it that you'd like to see?
|
|
||||||
description: Describe the feature you want added as detailed as possible
|
|
||||||
placeholder: I think ... would be a cool feature to add. This would be awesome, thanks!
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: checkboxes
|
|
||||||
id: agreement-check
|
|
||||||
attributes:
|
|
||||||
label: Request Agreement
|
|
||||||
description: DO NOT USE THIS TEMPLATE FOR PLUGIN REQUESTS!!! For plugin requests, **use discussions**
|
|
||||||
options:
|
|
||||||
- label: This is not a plugin request
|
|
||||||
required: true
|
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -38,11 +38,11 @@ jobs:
|
|||||||
run: pnpm build --standalone
|
run: pnpm build --standalone
|
||||||
|
|
||||||
- name: Generate plugin list
|
- name: Generate plugin list
|
||||||
run: pnpm generatePluginJson dist/plugins.json
|
run: pnpm generatePluginJson dist/plugins.json dist/plugin-readmes.json
|
||||||
|
|
||||||
- name: Clean up obsolete files
|
- name: Clean up obsolete files
|
||||||
run: |
|
run: |
|
||||||
rm -rf dist/*-unpacked Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
|
rm -rf dist/*-unpacked dist/monaco Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
|
||||||
|
|
||||||
- name: Get some values needed for the release
|
- name: Get some values needed for the release
|
||||||
id: release_values
|
id: release_values
|
||||||
|
@ -65,7 +65,7 @@ Also pay attention to the following:
|
|||||||
- Match any but a guaranteed terminating character: `[^;]+`, for example to match the entire assigned value in `var a=b||c||func();`,
|
- Match any but a guaranteed terminating character: `[^;]+`, for example to match the entire assigned value in `var a=b||c||func();`,
|
||||||
`var .{1,2}=([^;]+);`
|
`var .{1,2}=([^;]+);`
|
||||||
- If you don't care about that part, just match a bunch of chars: `.{0,50}`, for example to extract the variable "b" in `createElement("div",{a:"foo",c:"bar"},b)`, `createElement\("div".{0,30},(.{1,2})\),`. Note the `.{0,30}`, this is essentially the same as `.+`, but safer as you can't end up accidently eating thousands of characters
|
- If you don't care about that part, just match a bunch of chars: `.{0,50}`, for example to extract the variable "b" in `createElement("div",{a:"foo",c:"bar"},b)`, `createElement\("div".{0,30},(.{1,2})\),`. Note the `.{0,30}`, this is essentially the same as `.+`, but safer as you can't end up accidently eating thousands of characters
|
||||||
- Additionally, as you might have noticed, all of the appove approaches use regex groups (`(...)`) to capture the variable name. You can then use those groups in your replacement to access those variables dynamically
|
- Additionally, as you might have noticed, all of the above approaches use regex groups (`(...)`) to capture the variable name. You can then use those groups in your replacement to access those variables dynamically
|
||||||
|
|
||||||
#### "replace"
|
#### "replace"
|
||||||
|
|
||||||
|
@ -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";
|
||||||
|
|
||||||
@ -46,7 +48,8 @@ window.VencordNative = {
|
|||||||
getThemesList: () => DataStore.entries(themeStore).then(entries =>
|
getThemesList: () => DataStore.entries(themeStore).then(entries =>
|
||||||
entries.map(([name, css]) => getThemeInfo(css, name.toString()))
|
entries.map(([name, css]) => getThemeInfo(css, name.toString()))
|
||||||
),
|
),
|
||||||
getThemeData: (fileName: string) => DataStore.get(fileName, themeStore)
|
getThemeData: (fileName: string) => DataStore.get(fileName, themeStore),
|
||||||
|
getSystemValues: async () => ({}),
|
||||||
},
|
},
|
||||||
|
|
||||||
native: {
|
native: {
|
||||||
@ -80,6 +83,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 +91,7 @@ window.VencordNative = {
|
|||||||
? "vs-light"
|
? "vs-light"
|
||||||
: "vs-dark";
|
: "vs-dark";
|
||||||
|
|
||||||
win.document.write(monacoHtml);
|
win.document.write(IS_EXTENSION ? monacoHtmlLocal : monacoHtmlCdn);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @param {T[]} arr
|
|
||||||
* @param {(v: T) => boolean} predicate
|
|
||||||
*/
|
|
||||||
function removeFirst(arr, predicate) {
|
|
||||||
const idx = arr.findIndex(predicate);
|
|
||||||
if (idx !== -1) arr.splice(idx, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.webRequest.onHeadersReceived.addListener(
|
|
||||||
({ responseHeaders, type, url }) => {
|
|
||||||
if (!responseHeaders) return;
|
|
||||||
|
|
||||||
if (type === "main_frame") {
|
|
||||||
// In main frame requests, the CSP needs to be removed to enable fetching of custom css
|
|
||||||
// as desired by the user
|
|
||||||
removeFirst(responseHeaders, h => h.name.toLowerCase() === "content-security-policy");
|
|
||||||
} else if (type === "stylesheet" && url.startsWith("https://raw.githubusercontent.com/")) {
|
|
||||||
// Most users will load css from GitHub, but GitHub doesn't set the correct content type,
|
|
||||||
// so we fix it here
|
|
||||||
removeFirst(responseHeaders, h => h.name.toLowerCase() === "content-type");
|
|
||||||
responseHeaders.push({
|
|
||||||
name: "Content-Type",
|
|
||||||
value: "text/css"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return { responseHeaders };
|
|
||||||
},
|
|
||||||
{ urls: ["https://raw.githubusercontent.com/*", "*://*.discord.com/*"], types: ["main_frame", "stylesheet"] },
|
|
||||||
["blocking", "responseHeaders"]
|
|
||||||
);
|
|
@ -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/*"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -26,11 +26,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"background": {
|
"web_accessible_resources": ["dist/*", "third-party/*"],
|
||||||
"scripts": ["background.js"]
|
|
||||||
},
|
|
||||||
|
|
||||||
"web_accessible_resources": ["dist/Vencord.js", "dist/Vencord.css"],
|
|
||||||
|
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
@ -63,7 +63,7 @@ Then fully close Discord from your taskbar or task manager, and restart it. Venc
|
|||||||
|
|
||||||
If you're using Discord already, go into the `Updater` tab in settings.
|
If you're using Discord already, go into the `Updater` tab in settings.
|
||||||
|
|
||||||
Sometimes it may be neccessary to manually update if the GUI updater fails.
|
Sometimes it may be necessary to manually update if the GUI updater fails.
|
||||||
|
|
||||||
To pull latest changes:
|
To pull latest changes:
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.4.5",
|
"version": "1.5.5",
|
||||||
"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
|
||||||
|
@ -25,6 +25,8 @@ const defines = {
|
|||||||
IS_STANDALONE: isStandalone,
|
IS_STANDALONE: isStandalone,
|
||||||
IS_DEV: JSON.stringify(watch),
|
IS_DEV: JSON.stringify(watch),
|
||||||
IS_UPDATER_DISABLED: updaterDisabled,
|
IS_UPDATER_DISABLED: updaterDisabled,
|
||||||
|
IS_WEB: false,
|
||||||
|
IS_EXTENSION: false,
|
||||||
VERSION: JSON.stringify(VERSION),
|
VERSION: JSON.stringify(VERSION),
|
||||||
BUILD_TIMESTAMP,
|
BUILD_TIMESTAMP,
|
||||||
};
|
};
|
||||||
@ -77,7 +79,6 @@ await Promise.all([
|
|||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_WEB: false,
|
|
||||||
IS_DISCORD_DESKTOP: true,
|
IS_DISCORD_DESKTOP: true,
|
||||||
IS_VESKTOP: false
|
IS_VESKTOP: false
|
||||||
}
|
}
|
||||||
@ -123,7 +124,6 @@ await Promise.all([
|
|||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_WEB: 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,58 @@ const commonOptions = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MonacoWorkerEntryPoints = [
|
||||||
|
"vs/language/css/css.worker.js",
|
||||||
|
"vs/editor/editor.worker.js"
|
||||||
|
];
|
||||||
|
|
||||||
|
const RnNoiseFiles = [
|
||||||
|
"dist/rnnoise.wasm",
|
||||||
|
"dist/rnnoise_simd.wasm",
|
||||||
|
"dist/rnnoise/workletProcessor.js",
|
||||||
|
"LICENSE"
|
||||||
|
];
|
||||||
|
|
||||||
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 +118,41 @@ 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, noMonaco = false) {
|
||||||
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"),
|
||||||
|
...(noMonaco ? {} : await loadDir("dist/monaco")),
|
||||||
|
...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file =>
|
||||||
|
[`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)]
|
||||||
|
))),
|
||||||
...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,31 +168,15 @@ async function buildPluginZip(target, files, shouldZip) {
|
|||||||
}))),
|
}))),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldZip) {
|
await rm(target, { recursive: true, force: true });
|
||||||
return new Promise((resolve, reject) => {
|
await Promise.all(Object.entries(entries).map(async ([file, content]) => {
|
||||||
zip(entries, {}, (err, data) => {
|
const dest = join("dist", target, file);
|
||||||
if (err) {
|
const parentDirectory = join(dest, "..");
|
||||||
reject(err);
|
await mkdir(parentDirectory, { recursive: true });
|
||||||
} else {
|
await writeFile(dest, content);
|
||||||
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 Promise.all(Object.entries(entries).map(async ([file, content]) => {
|
|
||||||
const dest = join("dist", target, file);
|
|
||||||
const parentDirectory = join(dest, "..");
|
|
||||||
await mkdir(parentDirectory, { recursive: true });
|
|
||||||
await writeFile(dest, content);
|
|
||||||
}));
|
|
||||||
|
|
||||||
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 => {
|
||||||
@ -142,8 +194,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", ["content.js", "manifestv2.json", "icon.png"], true),
|
||||||
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");
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
import { Dirent, readdirSync, readFileSync, writeFileSync } from "fs";
|
import { Dirent, readdirSync, readFileSync, writeFileSync } from "fs";
|
||||||
import { access, readFile } from "fs/promises";
|
import { access, readFile } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join, sep } from "path";
|
||||||
|
import { normalize as posixNormalize, sep as posixSep } from "path/posix";
|
||||||
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
|
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
|
||||||
|
|
||||||
import { getPluginTarget } from "./utils.mjs";
|
import { getPluginTarget } from "./utils.mjs";
|
||||||
@ -39,6 +40,7 @@ interface PluginData {
|
|||||||
required: boolean;
|
required: boolean;
|
||||||
enabledByDefault: boolean;
|
enabledByDefault: boolean;
|
||||||
target: "discordDesktop" | "vencordDesktop" | "web" | "dev";
|
target: "discordDesktop" | "vencordDesktop" | "web" | "dev";
|
||||||
|
filePath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const devs = {} as Record<string, Dev>;
|
const devs = {} as Record<string, Dev>;
|
||||||
@ -165,7 +167,17 @@ async function parseFile(fileName: string) {
|
|||||||
data.target = target as any;
|
data.target = target as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
data.filePath = posixNormalize(fileName)
|
||||||
|
.split(sep)
|
||||||
|
.join(posixSep)
|
||||||
|
.replace(/\/index\.([jt]sx?)$/, "")
|
||||||
|
.replace(/^src\/plugins\//, "");
|
||||||
|
|
||||||
|
let readme = "";
|
||||||
|
try {
|
||||||
|
readme = readFileSync(join(fileName, "..", "README.md"), "utf-8");
|
||||||
|
} catch { }
|
||||||
|
return [data, readme] as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw fail("no default export called 'definePlugin' found");
|
throw fail("no default export called 'definePlugin' found");
|
||||||
@ -194,18 +206,24 @@ function isPluginFile({ name }: { name: string; }) {
|
|||||||
(async () => {
|
(async () => {
|
||||||
parseDevs();
|
parseDevs();
|
||||||
|
|
||||||
const plugins = ["src/plugins", "src/plugins/_core"].flatMap(dir =>
|
const plugins = [] as PluginData[];
|
||||||
|
const readmes = {} as Record<string, string>;
|
||||||
|
|
||||||
|
await Promise.all(["src/plugins", "src/plugins/_core"].flatMap(dir =>
|
||||||
readdirSync(dir, { withFileTypes: true })
|
readdirSync(dir, { withFileTypes: true })
|
||||||
.filter(isPluginFile)
|
.filter(isPluginFile)
|
||||||
.map(async dirent =>
|
.map(async dirent => {
|
||||||
parseFile(await getEntryPoint(dir, dirent))
|
const [data, readme] = await parseFile(await getEntryPoint(dir, dirent));
|
||||||
)
|
plugins.push(data);
|
||||||
);
|
if (readme) readmes[data.name] = readme;
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
const data = JSON.stringify(await Promise.all(plugins));
|
const data = JSON.stringify(plugins);
|
||||||
|
|
||||||
if (process.argv.length > 2) {
|
if (process.argv.length > 3) {
|
||||||
writeFileSync(process.argv[2], data);
|
writeFileSync(process.argv[2], data);
|
||||||
|
writeFileSync(process.argv[3], JSON.stringify(readmes));
|
||||||
} else {
|
} else {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ page.on("console", async e => {
|
|||||||
plugin,
|
plugin,
|
||||||
type,
|
type,
|
||||||
id,
|
id,
|
||||||
match: regex,
|
match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"),
|
||||||
error: cause
|
error: cause
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -23,7 +23,8 @@ export default {
|
|||||||
deleteTheme: (fileName: string) => invoke<void>(IpcEvents.DELETE_THEME, fileName),
|
deleteTheme: (fileName: string) => invoke<void>(IpcEvents.DELETE_THEME, fileName),
|
||||||
getThemesDir: () => invoke<string>(IpcEvents.GET_THEMES_DIR),
|
getThemesDir: () => invoke<string>(IpcEvents.GET_THEMES_DIR),
|
||||||
getThemesList: () => invoke<Array<UserThemeHeader>>(IpcEvents.GET_THEMES_LIST),
|
getThemesList: () => invoke<Array<UserThemeHeader>>(IpcEvents.GET_THEMES_LIST),
|
||||||
getThemeData: (fileName: string) => invoke<string | undefined>(IpcEvents.GET_THEME_DATA, fileName)
|
getThemeData: (fileName: string) => invoke<string | undefined>(IpcEvents.GET_THEME_DATA, fileName),
|
||||||
|
getSystemValues: () => invoke<Record<string, string>>(IpcEvents.GET_THEME_SYSTEM_VALUES),
|
||||||
},
|
},
|
||||||
|
|
||||||
updater: {
|
updater: {
|
||||||
|
@ -20,7 +20,6 @@ import { Channel, User } from "discord-types/general/index.js";
|
|||||||
|
|
||||||
interface DecoratorProps {
|
interface DecoratorProps {
|
||||||
activities: any[];
|
activities: any[];
|
||||||
canUseAvatarDecorations: boolean;
|
|
||||||
channel: Channel;
|
channel: Channel;
|
||||||
/**
|
/**
|
||||||
* Only for DM members
|
* Only for DM members
|
||||||
@ -52,9 +51,9 @@ export function removeDecorator(identifier: string) {
|
|||||||
decorators.delete(identifier);
|
decorators.delete(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function __addDecoratorsToList(props: DecoratorProps): (JSX.Element | null)[] {
|
export function __getDecorators(props: DecoratorProps): (JSX.Element | null)[] {
|
||||||
const isInGuild = !!(props.guildId);
|
const isInGuild = !!(props.guildId);
|
||||||
return [...decorators.values()].map(decoratorObj => {
|
return Array.from(decorators.values(), decoratorObj => {
|
||||||
const { decorator, onlyIn } = decoratorObj;
|
const { decorator, onlyIn } = decoratorObj;
|
||||||
// this can most likely be done cleaner
|
// this can most likely be done cleaner
|
||||||
if (!onlyIn || (onlyIn === "guilds" && isInGuild) || (onlyIn === "dms" && !isInGuild)) {
|
if (!onlyIn || (onlyIn === "guilds" && isInGuild) || (onlyIn === "dms" && !isInGuild)) {
|
||||||
|
21
src/components/CodeBlock.tsx
Normal file
21
src/components/CodeBlock.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
import { Parser } from "@webpack/common";
|
||||||
|
|
||||||
|
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders code in a Discord codeblock
|
||||||
|
*/
|
||||||
|
export function CodeBlock(props: { content?: string, lang: string; }) {
|
||||||
|
return (
|
||||||
|
<div className={CodeContainerClasses.markup}>
|
||||||
|
{Parser.defaultRules.codeBlock.react(props, null, {})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -28,8 +28,8 @@ interface BaseIconProps extends IconProps {
|
|||||||
|
|
||||||
interface IconProps extends SVGProps<SVGSVGElement> {
|
interface IconProps extends SVGProps<SVGSVGElement> {
|
||||||
className?: string;
|
className?: string;
|
||||||
height?: number;
|
height?: string | number;
|
||||||
width?: number;
|
width?: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
||||||
@ -97,7 +97,7 @@ export function OpenExternalIcon(props: IconProps) {
|
|||||||
>
|
>
|
||||||
<polygon
|
<polygon
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
fill-rule="nonzero"
|
fillRule="nonzero"
|
||||||
points="13 20 11 20 11 8 5.5 13.5 4.08 12.08 12 4.16 19.92 12.08 18.5 13.5 13 8"
|
points="13 20 11 20 11 8 5.5 13.5 4.08 12.08 12 4.16 19.92 12.08 18.5 13.5 13 8"
|
||||||
/>
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
@ -121,9 +121,13 @@ export function InfoIcon(props: IconProps) {
|
|||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "vc-info-icon")}
|
className={classes(props.className, "vc-info-icon")}
|
||||||
viewBox="0 0 12 12"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path fill="currentColor" d="M6 1C3.243 1 1 3.244 1 6c0 2.758 2.243 5 5 5s5-2.242 5-5c0-2.756-2.243-5-5-5zm0 2.376a.625.625 0 110 1.25.625.625 0 010-1.25zM7.5 8.5h-3v-1h1V6H5V5h1a.5.5 0 01.5.5v2h1v1z" />
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
transform="translate(2 2)"
|
||||||
|
d="M9,7 L11,7 L11,5 L9,5 L9,7 Z M10,18 C5.59,18 2,14.41 2,10 C2,5.59 5.59,2 10,2 C14.41,2 18,5.59 18,10 C18,14.41 14.41,18 10,18 L10,18 Z M10,4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16,4.4771525 0,10 C-1.33226763e-15,12.6521649 1.0535684,15.195704 2.92893219,17.0710678 C4.80429597,18.9464316 7.3478351,20 10,20 C12.6521649,20 15.195704,18.9464316 17.0710678,17.0710678 C18.9464316,15.195704 20,12.6521649 20,10 C20,7.3478351 18.9464316,4.80429597 17.0710678,2.92893219 C15.195704,1.0535684 12.6521649,2.22044605e-16 10,0 L10,4.4408921e-16 Z M9,15 L11,15 L11,9 L9,9 L9,15 L9,15 Z"
|
||||||
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -139,8 +143,8 @@ export function OwnerCrownIcon(props: IconProps) {
|
|||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
fill-rule="evenodd"
|
fillRule="evenodd"
|
||||||
clip-rule="evenodd"
|
clipRule="evenodd"
|
||||||
d="M13.6572 5.42868C13.8879 5.29002 14.1806 5.30402 14.3973 5.46468C14.6133 5.62602 14.7119 5.90068 14.6473 6.16202L13.3139 11.4954C13.2393 11.7927 12.9726 12.0007 12.6666 12.0007H3.33325C3.02725 12.0007 2.76058 11.792 2.68592 11.4954L1.35258 6.16202C1.28792 5.90068 1.38658 5.62602 1.60258 5.46468C1.81992 5.30468 2.11192 5.29068 2.34325 5.42868L5.13192 7.10202L7.44592 3.63068C7.46173 3.60697 7.48377 3.5913 7.50588 3.57559C7.5192 3.56612 7.53255 3.55663 7.54458 3.54535L6.90258 2.90268C6.77325 2.77335 6.77325 2.56068 6.90258 2.43135L7.76458 1.56935C7.89392 1.44002 8.10658 1.44002 8.23592 1.56935L9.09792 2.43135C9.22725 2.56068 9.22725 2.77335 9.09792 2.90268L8.45592 3.54535C8.46794 3.55686 8.48154 3.56651 8.49516 3.57618C8.51703 3.5917 8.53897 3.60727 8.55458 3.63068L10.8686 7.10202L13.6572 5.42868ZM2.66667 12.6673H13.3333V14.0007H2.66667V12.6673Z"
|
d="M13.6572 5.42868C13.8879 5.29002 14.1806 5.30402 14.3973 5.46468C14.6133 5.62602 14.7119 5.90068 14.6473 6.16202L13.3139 11.4954C13.2393 11.7927 12.9726 12.0007 12.6666 12.0007H3.33325C3.02725 12.0007 2.76058 11.792 2.68592 11.4954L1.35258 6.16202C1.28792 5.90068 1.38658 5.62602 1.60258 5.46468C1.81992 5.30468 2.11192 5.29068 2.34325 5.42868L5.13192 7.10202L7.44592 3.63068C7.46173 3.60697 7.48377 3.5913 7.50588 3.57559C7.5192 3.56612 7.53255 3.55663 7.54458 3.54535L6.90258 2.90268C6.77325 2.77335 6.77325 2.56068 6.90258 2.43135L7.76458 1.56935C7.89392 1.44002 8.10658 1.44002 8.23592 1.56935L9.09792 2.43135C9.22725 2.56068 9.22725 2.77335 9.09792 2.90268L8.45592 3.54535C8.46794 3.55686 8.48154 3.56651 8.49516 3.57618C8.51703 3.5917 8.53897 3.60727 8.55458 3.63068L10.8686 7.10202L13.6572 5.42868ZM2.66667 12.6673H13.3333V14.0007H2.66667V12.6673Z"
|
||||||
/>
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
@ -159,8 +163,6 @@ export function ScreenshareIcon(props: IconProps) {
|
|||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
fill-rule="evenodd"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
d="M2 4.5C2 3.397 2.897 2.5 4 2.5H20C21.103 2.5 22 3.397 22 4.5V15.5C22 16.604 21.103 17.5 20 17.5H13V19.5H17V21.5H7V19.5H11V17.5H4C2.897 17.5 2 16.604 2 15.5V4.5ZM13.2 14.3375V11.6C9.864 11.6 7.668 12.6625 6 15C6.672 11.6625 8.532 8.3375 13.2 7.6625V5L18 9.6625L13.2 14.3375Z"
|
d="M2 4.5C2 3.397 2.897 2.5 4 2.5H20C21.103 2.5 22 3.397 22 4.5V15.5C22 16.604 21.103 17.5 20 17.5H13V19.5H17V21.5H7V19.5H11V17.5H4C2.897 17.5 2 16.604 2 15.5V4.5ZM13.2 14.3375V11.6C9.864 11.6 7.668 12.6625 6 15C6.672 11.6625 8.532 8.3375 13.2 7.6625V5L18 9.6625L13.2 14.3375Z"
|
||||||
/>
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
@ -198,8 +200,58 @@ export function Microphone(props: IconProps) {
|
|||||||
className={classes(props.className, "vc-microphone")}
|
className={classes(props.className, "vc-microphone")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V21H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1ZM12 4C11.2 4 11 4.66667 11 5V11C11 11.3333 11.2 12 12 12C12.8 12 13 11.3333 13 11V5C13 4.66667 12.8 4 12 4Z" fill="currentColor" />
|
<path fillRule="evenodd" clipRule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V21H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1ZM12 4C11.2 4 11 4.66667 11 5V11C11 11.3333 11.2 12 12 12C12.8 12 13 11.3333 13 11V5C13 4.66667 12.8 4 12 4Z" fill="currentColor" />
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V22H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1Z" fill="currentColor" />
|
<path fillRule="evenodd" clipRule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V22H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1Z" fill="currentColor" />
|
||||||
</Icon >
|
</Icon >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function CogWheel(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-cog-wheel")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
clipRule="evenodd"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M19.738 10H22V14H19.739C19.498 14.931 19.1 15.798 18.565 16.564L20 18L18 20L16.565 18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069 19.498 8.203 19.099 7.436 18.564L6 20L4 18L5.436 16.564C4.901 15.799 4.502 14.932 4.262 14H2V10H4.262C4.502 9.068 4.9 8.202 5.436 7.436L4 6L6 4L7.436 5.436C8.202 4.9 9.068 4.502 10 4.262V2H14V4.261C14.932 4.502 15.797 4.9 16.565 5.435L18 3.999L20 5.999L18.564 7.436C19.099 8.202 19.498 9.069 19.738 10ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReplyIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-reply-icon")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M10 8.26667V4L3 11.4667L10 18.9333V14.56C15 14.56 18.5 16.2667 21 20C20 14.6667 17 9.33333 10 8.26667Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DeleteIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-delete-icon")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M15 3.999V2H9V3.999H3V5.999H21V3.999H15Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
113
src/components/PluginSettings/ContributorModal.tsx
Normal file
113
src/components/PluginSettings/ContributorModal.tsx
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "./contributorModal.css";
|
||||||
|
|
||||||
|
import { useSettings } from "@api/Settings";
|
||||||
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
import { DevsById } from "@utils/constants";
|
||||||
|
import { fetchUserProfile, getTheme, Theme } from "@utils/discord";
|
||||||
|
import { ModalContent, ModalRoot, openModal } from "@utils/modal";
|
||||||
|
import { Forms, MaskedLink, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
|
||||||
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
|
import Plugins from "~plugins";
|
||||||
|
|
||||||
|
import { PluginCard } from ".";
|
||||||
|
|
||||||
|
const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
|
||||||
|
const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
|
||||||
|
const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
|
||||||
|
const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
|
||||||
|
|
||||||
|
const cl = classNameFactory("vc-author-modal-");
|
||||||
|
|
||||||
|
export function openContributorModal(user: User) {
|
||||||
|
openModal(modalProps =>
|
||||||
|
<ModalRoot {...modalProps}>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<ModalContent className={cl("root")}>
|
||||||
|
<ContributorModal user={user} />
|
||||||
|
</ModalContent>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</ModalRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GithubIcon() {
|
||||||
|
const src = getTheme() === Theme.Light ? GithubIconLight : GithubIconDark;
|
||||||
|
return <img src={src} alt="GitHub" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function WebsiteIcon() {
|
||||||
|
const src = getTheme() === Theme.Light ? WebsiteIconLight : WebsiteIconDark;
|
||||||
|
return <img src={src} alt="Website" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ContributorModal({ user }: { user: User; }) {
|
||||||
|
useSettings();
|
||||||
|
|
||||||
|
const profile = useStateFromStores([UserProfileStore], () => UserProfileStore.getUserProfile(user.id));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!profile && !user.bot && user.id)
|
||||||
|
fetchUserProfile(user.id);
|
||||||
|
}, [user.id]);
|
||||||
|
|
||||||
|
const githubName = profile?.connectedAccounts?.find(a => a.type === "github")?.name;
|
||||||
|
const website = profile?.connectedAccounts?.find(a => a.type === "domain")?.name;
|
||||||
|
|
||||||
|
const plugins = useMemo(() => {
|
||||||
|
const allPlugins = Object.values(Plugins);
|
||||||
|
const pluginsByAuthor = DevsById[user.id]
|
||||||
|
? allPlugins.filter(p => p.authors.includes(DevsById[user.id]))
|
||||||
|
: allPlugins.filter(p => p.authors.some(a => a.name === user.username));
|
||||||
|
|
||||||
|
return pluginsByAuthor
|
||||||
|
.filter(p => !p.name.endsWith("API"))
|
||||||
|
.sort((a, b) => Number(a.required ?? false) - Number(b.required ?? false));
|
||||||
|
}, [user.id, user.username]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={cl("header")}>
|
||||||
|
<img
|
||||||
|
className={cl("avatar")}
|
||||||
|
src={user.getAvatarURL(void 0, 512, true)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<Forms.FormTitle tag="h2" className={cl("name")}>{user.username}</Forms.FormTitle>
|
||||||
|
|
||||||
|
<div className={cl("links")}>
|
||||||
|
{website && (
|
||||||
|
<MaskedLink
|
||||||
|
href={"https://" + website}
|
||||||
|
>
|
||||||
|
<WebsiteIcon />
|
||||||
|
</MaskedLink>
|
||||||
|
)}
|
||||||
|
{githubName && (
|
||||||
|
<MaskedLink href={`https://github.com/${githubName}`}>
|
||||||
|
<GithubIcon />
|
||||||
|
</MaskedLink>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cl("plugins")}>
|
||||||
|
{plugins.map(p =>
|
||||||
|
<PluginCard
|
||||||
|
key={p.name}
|
||||||
|
plugin={p}
|
||||||
|
disabled={p.required ?? false}
|
||||||
|
onRestartNeeded={() => showToast("Restart to apply changes!")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -18,17 +18,16 @@
|
|||||||
|
|
||||||
import { generateId } from "@api/Commands";
|
import { generateId } from "@api/Commands";
|
||||||
import { useSettings } from "@api/Settings";
|
import { useSettings } from "@api/Settings";
|
||||||
import { disableStyle, enableStyle } from "@api/Styles";
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
import { proxyLazy } from "@utils/lazy";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes, isObjectEmpty } from "@utils/misc";
|
||||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
||||||
import { LazyComponent } from "@utils/react";
|
import { LazyComponent } from "@utils/react";
|
||||||
import { OptionType, Plugin } from "@utils/types";
|
import { OptionType, Plugin } from "@utils/types";
|
||||||
import { findByCode, findByPropsLazy } from "@webpack";
|
import { findByCode, findByPropsLazy } from "@webpack";
|
||||||
import { Button, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
import { Constructor } from "type-fest";
|
import { Constructor } from "type-fest";
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ import {
|
|||||||
SettingSliderComponent,
|
SettingSliderComponent,
|
||||||
SettingTextComponent
|
SettingTextComponent
|
||||||
} from "./components";
|
} from "./components";
|
||||||
import hideBotTagStyle from "./userPopoutHideBotTag.css?managed";
|
import { openContributorModal } from "./ContributorModal";
|
||||||
|
|
||||||
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
|
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
|
||||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||||
@ -89,30 +88,19 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||||||
|
|
||||||
const canSubmit = () => Object.values(errors).every(e => !e);
|
const canSubmit = () => Object.values(errors).every(e => !e);
|
||||||
|
|
||||||
const hasSettings = Boolean(pluginSettings && plugin.options);
|
const hasSettings = Boolean(pluginSettings && plugin.options && !isObjectEmpty(plugin.options));
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
enableStyle(hideBotTagStyle);
|
|
||||||
|
|
||||||
let originalUser: User;
|
|
||||||
(async () => {
|
(async () => {
|
||||||
for (const user of plugin.authors.slice(0, 6)) {
|
for (const user of plugin.authors.slice(0, 6)) {
|
||||||
const author = user.id
|
const author = user.id
|
||||||
? await UserUtils.fetchUser(`${user.id}`)
|
? await UserUtils.fetchUser(`${user.id}`)
|
||||||
// only show name & pfp and no actions so users cannot harass plugin devs for support (send dms, add as friend, etc)
|
|
||||||
.then(u => (originalUser = u, makeDummyUser(u)))
|
|
||||||
.catch(() => makeDummyUser({ username: user.name }))
|
.catch(() => makeDummyUser({ username: user.name }))
|
||||||
: makeDummyUser({ username: user.name });
|
: makeDummyUser({ username: user.name });
|
||||||
|
|
||||||
setAuthors(a => [...a, author]);
|
setAuthors(a => [...a, author]);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return () => {
|
|
||||||
disableStyle(hideBotTagStyle);
|
|
||||||
if (originalUser)
|
|
||||||
FluxDispatcher.dispatch({ type: "USER_UPDATE", user: originalUser });
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function saveAndClose() {
|
async function saveAndClose() {
|
||||||
@ -214,6 +202,19 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||||||
showDefaultAvatarsForNullUsers
|
showDefaultAvatarsForNullUsers
|
||||||
showUserPopout
|
showUserPopout
|
||||||
renderMoreUsers={renderMoreUsers}
|
renderMoreUsers={renderMoreUsers}
|
||||||
|
renderUser={(user: User) => (
|
||||||
|
<Clickable
|
||||||
|
className={AvatarStyles.clickableAvatar}
|
||||||
|
onClick={() => openContributorModal(user)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className={AvatarStyles.avatar}
|
||||||
|
src={user.getAvatarURL(void 0, 80, true)}
|
||||||
|
alt={user.username}
|
||||||
|
title={user.username}
|
||||||
|
/>
|
||||||
|
</Clickable>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Forms.FormSection>
|
</Forms.FormSection>
|
||||||
@ -237,7 +238,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||||||
<Button
|
<Button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
size={Button.Sizes.SMALL}
|
size={Button.Sizes.SMALL}
|
||||||
color={Button.Colors.WHITE}
|
color={Button.Colors.PRIMARY}
|
||||||
look={Button.Looks.LINK}
|
look={Button.Looks.LINK}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
57
src/components/PluginSettings/contributorModal.css
Normal file
57
src/components/PluginSettings/contributorModal.css
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
.vc-author-modal-root {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-name {
|
||||||
|
text-transform: none;
|
||||||
|
flex-grow: 0;
|
||||||
|
background: var(--background-tertiary);
|
||||||
|
border-radius: 0 9999px 9999px 0;
|
||||||
|
padding: 6px 0.8em 6px 0.5em;
|
||||||
|
font-size: 20px;
|
||||||
|
height: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-name::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 16px;
|
||||||
|
background: var(--background-tertiary);
|
||||||
|
z-index: -1;
|
||||||
|
left: -16px;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-avatar {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-links {
|
||||||
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-links img {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 4px solid var(--background-tertiary);
|
||||||
|
box-sizing: border-box
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-plugins {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
@ -22,17 +22,18 @@ import * as DataStore from "@api/DataStore";
|
|||||||
import { showNotice } from "@api/Notices";
|
import { showNotice } from "@api/Notices";
|
||||||
import { Settings, useSettings } from "@api/Settings";
|
import { Settings, useSettings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { CogWheel, InfoIcon } from "@components/Icons";
|
||||||
import PluginModal from "@components/PluginSettings/PluginModal";
|
import PluginModal from "@components/PluginSettings/PluginModal";
|
||||||
import { AddonCard } from "@components/VencordSettings/AddonCard";
|
import { AddonCard } from "@components/VencordSettings/AddonCard";
|
||||||
import { SettingsTab } from "@components/VencordSettings/shared";
|
import { SettingsTab } from "@components/VencordSettings/shared";
|
||||||
import { ChangeList } from "@utils/ChangeList";
|
import { ChangeList } from "@utils/ChangeList";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes, isObjectEmpty } from "@utils/misc";
|
||||||
import { openModalLazy } from "@utils/modal";
|
import { openModalLazy } from "@utils/modal";
|
||||||
import { LazyComponent, useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { Plugin } from "@utils/types";
|
import { Plugin } from "@utils/types";
|
||||||
import { findByCode, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { Alerts, Button, Card, Forms, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common";
|
import { Alerts, Button, Card, Forms, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common";
|
||||||
|
|
||||||
import Plugins from "~plugins";
|
import Plugins from "~plugins";
|
||||||
@ -46,8 +47,6 @@ const logger = new Logger("PluginSettings", "#a6d189");
|
|||||||
const InputStyles = findByPropsLazy("inputDefault", "inputWrapper");
|
const InputStyles = findByPropsLazy("inputDefault", "inputWrapper");
|
||||||
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
||||||
|
|
||||||
const CogWheel = LazyComponent(() => findByCode("18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069"));
|
|
||||||
const InfoIcon = LazyComponent(() => findByCode("4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16"));
|
|
||||||
|
|
||||||
function showErrorToast(message: string) {
|
function showErrorToast(message: string) {
|
||||||
Toasts.show({
|
Toasts.show({
|
||||||
@ -91,7 +90,7 @@ interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
|
|||||||
isNew?: boolean;
|
isNew?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
|
export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
|
||||||
const settings = Settings.plugins[plugin.name];
|
const settings = Settings.plugins[plugin.name];
|
||||||
|
|
||||||
const isEnabled = () => settings.enabled ?? false;
|
const isEnabled = () => settings.enabled ?? false;
|
||||||
@ -161,9 +160,9 @@ function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLe
|
|||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
infoButton={
|
infoButton={
|
||||||
<button role="switch" onClick={() => openModal()} className={classes(ButtonClasses.button, cl("info-button"))}>
|
<button role="switch" onClick={() => openModal()} className={classes(ButtonClasses.button, cl("info-button"))}>
|
||||||
{plugin.options
|
{plugin.options && !isObjectEmpty(plugin.options)
|
||||||
? <CogWheel />
|
? <CogWheel />
|
||||||
: <InfoIcon width="24" height="24" />}
|
: <InfoIcon />}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
[class|="userPopoutOuter"] [class*="botTag"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||||
|
import { CodeBlock } from "@components/CodeBlock";
|
||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||||
@ -299,7 +300,7 @@ function PatchHelper() {
|
|||||||
{!!(find && match && replacement) && (
|
{!!(find && match && replacement) && (
|
||||||
<>
|
<>
|
||||||
<Forms.FormTitle className={Margins.top20}>Code</Forms.FormTitle>
|
<Forms.FormTitle className={Margins.top20}>Code</Forms.FormTitle>
|
||||||
<div style={{ userSelect: "text" }}>{Parser.parse(makeCodeblock(code, "ts"))}</div>
|
<CodeBlock lang="js" content={code} />
|
||||||
<Button onClick={() => Clipboard.copy(code)}>Copy to Clipboard</Button>
|
<Button onClick={() => Clipboard.copy(code)}>Copy to Clipboard</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -18,8 +18,11 @@
|
|||||||
|
|
||||||
import { useSettings } from "@api/Settings";
|
import { useSettings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
|
import { DeleteIcon } from "@components/Icons";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
|
import { IsFirefox } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { showItemInFolder } from "@utils/native";
|
import { showItemInFolder } from "@utils/native";
|
||||||
@ -40,7 +43,6 @@ type FileInput = ComponentType<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
const InviteActions = findByPropsLazy("resolveInvite");
|
const InviteActions = findByPropsLazy("resolveInvite");
|
||||||
const TrashIcon = findByCodeLazy("M5 6.99902V18.999C5 20.101 5.897 20.999");
|
|
||||||
const FileInput: FileInput = findByCodeLazy("activateUploadDialogue=");
|
const FileInput: FileInput = findByCodeLazy("activateUploadDialogue=");
|
||||||
const TextAreaProps = findLazy(m => typeof m.textarea === "string");
|
const TextAreaProps = findLazy(m => typeof m.textarea === "string");
|
||||||
|
|
||||||
@ -112,7 +114,7 @@ function ThemeCard({ theme, enabled, onChange, onDelete }: ThemeCardProps) {
|
|||||||
infoButton={
|
infoButton={
|
||||||
IS_WEB && (
|
IS_WEB && (
|
||||||
<div style={{ cursor: "pointer", color: "var(--status-danger" }} onClick={onDelete}>
|
<div style={{ cursor: "pointer", color: "var(--status-danger" }} onClick={onDelete}>
|
||||||
<TrashIcon />
|
<DeleteIcon />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -249,12 +251,14 @@ function ThemesTab() {
|
|||||||
>
|
>
|
||||||
Load missing Themes
|
Load missing Themes
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{!IsFirefox && (
|
||||||
onClick={() => VencordNative.quickCss.openEditor()}
|
<Button
|
||||||
size={Button.Sizes.SMALL}
|
onClick={() => VencordNative.quickCss.openEditor()}
|
||||||
>
|
size={Button.Sizes.SMALL}
|
||||||
Edit QuickCSS
|
>
|
||||||
</Button>
|
Edit QuickCSS
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@ -316,6 +320,15 @@ function ThemesTab() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsTab title="Themes">
|
<SettingsTab title="Themes">
|
||||||
|
{IsFirefox && (
|
||||||
|
<ErrorCard>
|
||||||
|
<Forms.FormTitle tag="h5">Warning</Forms.FormTitle>
|
||||||
|
<Forms.FormText>
|
||||||
|
You are using Firefox. Expect the vast majority of themes to not work.
|
||||||
|
If this is a problem, use a chromium browser or Discord Desktop / Vesktop.
|
||||||
|
</Forms.FormText>
|
||||||
|
</ErrorCard>
|
||||||
|
)}
|
||||||
<TabBar
|
<TabBar
|
||||||
type="top"
|
type="top"
|
||||||
look="brand"
|
look="brand"
|
||||||
|
@ -21,6 +21,7 @@ import { Settings, useSettings } from "@api/Settings";
|
|||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import DonateButton from "@components/DonateButton";
|
import DonateButton from "@components/DonateButton";
|
||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
|
import { IsFirefox } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { identity } from "@utils/misc";
|
import { identity } from "@utils/misc";
|
||||||
import { relaunch, showItemInFolder } from "@utils/native";
|
import { relaunch, showItemInFolder } from "@utils/native";
|
||||||
@ -109,12 +110,14 @@ function VencordSettings() {
|
|||||||
Restart Client
|
Restart Client
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
{!IsFirefox && (
|
||||||
onClick={() => VencordNative.quickCss.openEditor()}
|
<Button
|
||||||
size={Button.Sizes.SMALL}
|
onClick={() => VencordNative.quickCss.openEditor()}
|
||||||
disabled={settingsDir === "Loading..."}>
|
size={Button.Sizes.SMALL}
|
||||||
Open QuickCSS File
|
disabled={settingsDir === "Loading..."}>
|
||||||
</Button>
|
Open QuickCSS File
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => showItemInFolder(settingsDir)}
|
onClick={() => showItemInFolder(settingsDir)}
|
||||||
@ -257,7 +260,11 @@ function DonateCard({ image }: DonateCardProps) {
|
|||||||
src={image}
|
src={image}
|
||||||
alt=""
|
alt=""
|
||||||
height={128}
|
height={128}
|
||||||
style={{ marginLeft: "auto", transform: image === DEFAULT_DONATE_IMAGE ? "rotate(10deg)" : "" }}
|
style={{
|
||||||
|
imageRendering: image === SHIGGY_DONATE_IMAGE ? "pixelated" : void 0,
|
||||||
|
marginLeft: "auto",
|
||||||
|
transform: image === DEFAULT_DONATE_IMAGE ? "rotate(10deg)" : void 0
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
transition: 0.1s ease-out;
|
transition: 0.1s ease-out;
|
||||||
transition-property: box-shadow, transform, background, opacity;
|
transition-property: box-shadow, transform, background, opacity;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-addon-card-disabled {
|
.vc-addon-card-disabled {
|
||||||
|
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;
|
||||||
|
@ -22,12 +22,12 @@ import "./ipcPlugins";
|
|||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
import { IpcEvents } from "@utils/IpcEvents";
|
import { IpcEvents } from "@utils/IpcEvents";
|
||||||
import { Queue } from "@utils/Queue";
|
import { Queue } from "@utils/Queue";
|
||||||
import { BrowserWindow, ipcMain, shell } from "electron";
|
import { BrowserWindow, ipcMain, shell, systemPreferences } from "electron";
|
||||||
import { mkdirSync, readFileSync, watch } from "fs";
|
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";
|
||||||
@ -112,6 +112,10 @@ ipcMain.handle(IpcEvents.SET_QUICK_CSS, (_, css) =>
|
|||||||
ipcMain.handle(IpcEvents.GET_THEMES_DIR, () => THEMES_DIR);
|
ipcMain.handle(IpcEvents.GET_THEMES_DIR, () => THEMES_DIR);
|
||||||
ipcMain.handle(IpcEvents.GET_THEMES_LIST, () => listThemes());
|
ipcMain.handle(IpcEvents.GET_THEMES_LIST, () => listThemes());
|
||||||
ipcMain.handle(IpcEvents.GET_THEME_DATA, (_, fileName) => getThemeData(fileName));
|
ipcMain.handle(IpcEvents.GET_THEME_DATA, (_, fileName) => getThemeData(fileName));
|
||||||
|
ipcMain.handle(IpcEvents.GET_THEME_SYSTEM_VALUES, () => ({
|
||||||
|
// win & mac only
|
||||||
|
"os-accent-color": `#${systemPreferences.getAccentColor?.() || ""}`
|
||||||
|
}));
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.GET_SETTINGS_DIR, () => SETTINGS_DIR);
|
ipcMain.handle(IpcEvents.GET_SETTINGS_DIR, () => SETTINGS_DIR);
|
||||||
ipcMain.on(IpcEvents.GET_SETTINGS, e => e.returnValue = readSettings());
|
ipcMain.on(IpcEvents.GET_SETTINGS, e => e.returnValue = readSettings());
|
||||||
|
@ -26,7 +26,7 @@ export default definePlugin({
|
|||||||
patches: [
|
patches: [
|
||||||
// obtain BUILT_IN_COMMANDS instance
|
// obtain BUILT_IN_COMMANDS instance
|
||||||
{
|
{
|
||||||
find: '"giphy","tenor"',
|
find: ',"tenor"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// Matches BUILT_IN_COMMANDS. This is not exported so this is
|
// Matches BUILT_IN_COMMANDS. This is not exported so this is
|
||||||
@ -34,7 +34,7 @@ export default definePlugin({
|
|||||||
// patch simpler
|
// patch simpler
|
||||||
|
|
||||||
// textCommands = builtInCommands.filter(...)
|
// textCommands = builtInCommands.filter(...)
|
||||||
match: /(?<=\w=)(\w)(\.filter\(.{0,30}giphy)/,
|
match: /(?<=\w=)(\w)(\.filter\(.{0,60}tenor)/,
|
||||||
replace: "Vencord.Api.Commands._init($1)$2",
|
replace: "Vencord.Api.Commands._init($1)$2",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -22,21 +22,28 @@ import definePlugin from "@utils/types";
|
|||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MemberListDecoratorsAPI",
|
name: "MemberListDecoratorsAPI",
|
||||||
description: "API to add decorators to member list (both in servers and DMs)",
|
description: "API to add decorators to member list (both in servers and DMs)",
|
||||||
authors: [Devs.TheSun],
|
authors: [Devs.TheSun, Devs.Ven],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "lostPermissionTooltipText,",
|
find: "lostPermissionTooltipText,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /Fragment,{children:\[(.{30,80})\]/,
|
match: /decorators:.{0,100}?children:\[(?<=(\i)\.lostPermissionTooltipText.+?)/,
|
||||||
replace: "Fragment,{children:Vencord.Api.MemberListDecorators.__addDecoratorsToList(this.props).concat($1)"
|
replace: "$&...Vencord.Api.MemberListDecorators.__getDecorators($1),"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "PrivateChannel.renderAvatar",
|
find: "PrivateChannel.renderAvatar",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /(subText:(.{1,2})\.renderSubtitle\(\).{1,50}decorators):(.{30,100}:null)/,
|
// props are shadowed by nested props so we have to do this
|
||||||
replace: "$1:Vencord.Api.MemberListDecorators.__addDecoratorsToList($2.props).concat($3)"
|
{
|
||||||
}
|
match: /\i=(\i)\.applicationStream,/,
|
||||||
|
replace: "$&vencordProps=$1,"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/,
|
||||||
|
replace: "decorators:[...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)), $1?$2:null]"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { DataStore } from "@api/index";
|
import { DataStore } from "@api/index";
|
||||||
import { Devs, SUPPORT_CHANNEL_ID } from "@utils/constants";
|
import { Devs, IsFirefox, SUPPORT_CHANNEL_ID } from "@utils/constants";
|
||||||
import { isPluginDev } from "@utils/misc";
|
import { isPluginDev } from "@utils/misc";
|
||||||
import { makeCodeblock } from "@utils/text";
|
import { makeCodeblock } from "@utils/text";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
@ -27,9 +27,10 @@ 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";
|
||||||
|
const FIREFOX_DISMISS_KEY = "Vencord-Firefox-Warning-Dismiss";
|
||||||
|
|
||||||
const AllowedChannelIds = [
|
const AllowedChannelIds = [
|
||||||
SUPPORT_CHANNEL_ID,
|
SUPPORT_CHANNEL_ID,
|
||||||
@ -48,7 +49,7 @@ export default definePlugin({
|
|||||||
name: "vencord-debug",
|
name: "vencord-debug",
|
||||||
description: "Send Vencord Debug info",
|
description: "Send Vencord Debug info",
|
||||||
predicate: ctx => AllowedChannelIds.includes(ctx.channel.id),
|
predicate: ctx => AllowedChannelIds.includes(ctx.channel.id),
|
||||||
execute() {
|
async execute() {
|
||||||
const { RELEASE_CHANNEL } = window.GLOBAL_ENV;
|
const { RELEASE_CHANNEL } = window.GLOBAL_ENV;
|
||||||
|
|
||||||
const client = (() => {
|
const client = (() => {
|
||||||
@ -75,6 +76,10 @@ export default definePlugin({
|
|||||||
OpenAsar: "openasar" in window,
|
OpenAsar: "openasar" in window,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (IS_DISCORD_DESKTOP) {
|
||||||
|
info["Last Crash Reason"] = (await DiscordNative.processUtils.getLastCrash())?.rendererCrashReason ?? "N/A";
|
||||||
|
}
|
||||||
|
|
||||||
const debugInfo = `
|
const debugInfo = `
|
||||||
**Vencord Debug Info**
|
**Vencord Debug Info**
|
||||||
>>> ${Object.entries(info).map(([k, v]) => `${k}: ${v}`).join("\n")}
|
>>> ${Object.entries(info).map(([k, v]) => `${k}: ${v}`).join("\n")}
|
||||||
@ -111,6 +116,22 @@ ${makeCodeblock(enabledPlugins.join(", ") + "\n\n" + enabledApiPlugins.join(", "
|
|||||||
onConfirm: rememberDismiss
|
onConfirm: rememberDismiss
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsFirefox) {
|
||||||
|
const rememberDismiss = () => DataStore.set(FIREFOX_DISMISS_KEY, true);
|
||||||
|
|
||||||
|
Alerts.show({
|
||||||
|
title: "Hold on!",
|
||||||
|
body: <div>
|
||||||
|
<Forms.FormText>You are using Firefox.</Forms.FormText>
|
||||||
|
<Forms.FormText>Due to Firefox's stupid extension guidelines, most themes and many plugins will not function correctly.</Forms.FormText>
|
||||||
|
<Forms.FormText>Do not report bugs. Do not ask for help with broken plugins.</Forms.FormText>
|
||||||
|
<Forms.FormText>Instead, use a chromium browser, Discord Desktop, or Vesktop.</Forms.FormText>
|
||||||
|
</div>,
|
||||||
|
onCancel: rememberDismiss,
|
||||||
|
onConfirm: rememberDismiss
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -23,7 +23,7 @@ import { Clipboard, Toasts } from "@webpack/common";
|
|||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "BetterRoleDot",
|
name: "BetterRoleDot",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven, Devs.AutumnVN],
|
||||||
description:
|
description:
|
||||||
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
|
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
|
||||||
|
|
||||||
@ -43,6 +43,25 @@ export default definePlugin({
|
|||||||
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
||||||
replace: "true",
|
replace: "true",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
find: ".ADD_ROLE_A11Y_LABEL",
|
||||||
|
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||||
|
noWarn: true,
|
||||||
|
replacement: {
|
||||||
|
match: /"dot"===\i/,
|
||||||
|
replace: "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".roleVerifiedIcon",
|
||||||
|
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||||
|
noWarn: true,
|
||||||
|
replacement: {
|
||||||
|
match: /"dot"===\i/,
|
||||||
|
replace: "true"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -50,7 +69,14 @@ export default definePlugin({
|
|||||||
bothStyles: {
|
bothStyles: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Show both role dot and coloured names",
|
description: "Show both role dot and coloured names",
|
||||||
|
restartNeeded: true,
|
||||||
default: false,
|
default: false,
|
||||||
|
},
|
||||||
|
copyRoleColorInProfilePopout: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Allow click on role dot in profile popout to copy role color",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -49,7 +49,7 @@ export default definePlugin({
|
|||||||
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')"
|
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')"
|
||||||
}, {
|
}, {
|
||||||
match: /(\.renderAttachments=.+?(.)=this\.props)(.+?\.embedWrapper)/g,
|
match: /(\.renderAttachments=.+?(.)=this\.props)(.+?\.embedWrapper)/g,
|
||||||
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')"
|
replace: "$1,vcProps=$2$3+(vcProps.nsfw?' vc-nsfw-img':'')"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
],
|
],
|
@ -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",
|
||||||
@ -136,4 +138,5 @@ export const defaultRules = [
|
|||||||
"utm_term",
|
"utm_term",
|
||||||
"si@open.spotify.com",
|
"si@open.spotify.com",
|
||||||
"igshid",
|
"igshid",
|
||||||
|
"share_id@reddit.com",
|
||||||
];
|
];
|
||||||
|
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;
|
||||||
|
}
|
@ -24,11 +24,9 @@ import { Margins } from "@utils/margins";
|
|||||||
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
||||||
import { EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
import { EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
||||||
import { Promisable } from "type-fest";
|
import { Promisable } from "type-fest";
|
||||||
|
|
||||||
const MANAGE_EMOJIS_AND_STICKERS = 1n << 30n;
|
|
||||||
|
|
||||||
const StickersStore = findStoreLazy("StickersStore");
|
const StickersStore = findStoreLazy("StickersStore");
|
||||||
const uploadEmoji = findByCodeLazy('"EMOJI_UPLOAD_START"', "GUILD_EMOJIS(");
|
const uploadEmoji = findByCodeLazy('"EMOJI_UPLOAD_START"', "GUILD_EMOJIS(");
|
||||||
|
|
||||||
@ -120,7 +118,7 @@ function getGuildCandidates(data: Data) {
|
|||||||
|
|
||||||
return Object.values(GuildStore.getGuilds()).filter(g => {
|
return Object.values(GuildStore.getGuilds()).filter(g => {
|
||||||
const canCreate = g.ownerId === meId ||
|
const canCreate = g.ownerId === meId ||
|
||||||
BigInt(PermissionStore.getGuildPermissions({ id: g.id }) & MANAGE_EMOJIS_AND_STICKERS) === MANAGE_EMOJIS_AND_STICKERS;
|
(PermissionStore.getGuildPermissions({ id: g.id }) & PermissionsBits.CREATE_GUILD_EXPRESSIONS) === PermissionsBits.CREATE_GUILD_EXPRESSIONS;
|
||||||
if (!canCreate) return false;
|
if (!canCreate) return false;
|
||||||
|
|
||||||
if (data.t === "Sticker") return true;
|
if (data.t === "Sticker") return true;
|
@ -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;
|
||||||
@ -102,6 +103,11 @@ interface StickerPack {
|
|||||||
stickers: Sticker[];
|
stickers: Sticker[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enum FakeNoticeType {
|
||||||
|
Sticker,
|
||||||
|
Emoji
|
||||||
|
}
|
||||||
|
|
||||||
const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/;
|
const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/;
|
||||||
const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./;
|
const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./;
|
||||||
const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/;
|
const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/;
|
||||||
@ -212,15 +218,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;"
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@ -305,16 +311,24 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(emojiSection.{0,50}description:)(\i)(?<=(\i)\.sticker,.+?)(?=,)/,
|
match: /(emojiSection.{0,50}description:)(\i)(?<=(\i)\.sticker,.+?)(?=,)/,
|
||||||
replace: (_, rest, reactNode, props) => `${rest}$self.addFakeNotice("STICKER",${reactNode},!!${props}.renderableSticker?.fake)`
|
replace: (_, rest, reactNode, props) => `${rest}$self.addFakeNotice(${FakeNoticeType.Sticker},${reactNode},!!${props}.renderableSticker?.fake)`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".Messages.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION",
|
find: ".EMOJI_UPSELL_POPOUT_MORE_EMOJIS_OPENED,",
|
||||||
predicate: () => settings.store.transformEmojis,
|
predicate: () => settings.store.transformEmojis,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /((\i)=\i\.node,\i=\i\.expressionSourceGuild)(.+?return )(.{0,450}Messages\.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION.+?}\))/,
|
match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<=(\i)=\i\.node.+?)/,
|
||||||
replace: (_, rest1, node, rest2, reactNode) => `${rest1},fakeNitroNode=${node}${rest2}$self.addFakeNotice("EMOJI",${reactNode},fakeNitroNode.fake)`
|
replace: (m, node) => `${m}fakeNitroNode:${node},`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION",
|
||||||
|
predicate: () => settings.store.transformEmojis,
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\.Messages\.EMOJI_POPOUT_ADDED_PACK_DESCRIPTION.+?return ).{0,1200}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?(?=}\()/,
|
||||||
|
replace: reactNode => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!arguments[0]?.fakeNitroNode?.fake)`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -610,18 +624,18 @@ export default definePlugin({
|
|||||||
return link.target && fakeNitroEmojiRegex.test(link.target);
|
return link.target && fakeNitroEmojiRegex.test(link.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
addFakeNotice(type: "STICKER" | "EMOJI", node: Array<ReactNode>, fake: boolean) {
|
addFakeNotice(type: FakeNoticeType, node: Array<ReactNode>, fake: boolean) {
|
||||||
if (!fake) return node;
|
if (!fake) return node;
|
||||||
|
|
||||||
node = Array.isArray(node) ? node : [node];
|
node = Array.isArray(node) ? node : [node];
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "STICKER": {
|
case FakeNoticeType.Sticker: {
|
||||||
node.push(" This is a FakeNitro sticker and renders like a real sticker only for you. Appears as a link to non-plugin users.");
|
node.push(" This is a FakeNitro sticker and renders like a real sticker only for you. Appears as a link to non-plugin users.");
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
case "EMOJI": {
|
case FakeNoticeType.Emoji: {
|
||||||
node.push(" This is a FakeNitro emoji and renders like a real emoji only for you. Appears as a link to non-plugin users.");
|
node.push(" This is a FakeNitro emoji and renders like a real emoji only for you. Appears as a link to non-plugin users.");
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@ -650,15 +664,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.
|
||||||
|
|
||||||
|
![a screenshot of the favourite emojis section](https://github.com/Vendicated/Vencord/assets/45497981/419c8c16-1afc-46e0-9cc2-20b9c3489711)
|
||||||
|
![a comparison of the emoji picker before and after enabling this plugin](https://github.com/Vendicated/Vencord/assets/45497981/4f57626d-cfc6-4155-a47c-2eac191231bb)
|
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://github.com/Vendicated/Vencord/assets/45497981/19552adc-d921-4153-976e-e9361dc8fdaf)
|
@ -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: [
|
||||||
{
|
{
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
import { Forms } from "@webpack/common";
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "FixInbox",
|
|
||||||
description: "Fixes the Unreads Inbox from crashing Discord when you're in lots of guilds.",
|
|
||||||
authors: [Devs.Megu],
|
|
||||||
|
|
||||||
patches: [{
|
|
||||||
find: "INBOX_OPEN:function",
|
|
||||||
replacement: {
|
|
||||||
// This function normally dispatches a subscribe event to every guild.
|
|
||||||
// this is badbadbadbadbad so we just get rid of it.
|
|
||||||
match: /INBOX_OPEN:function.+?\{/,
|
|
||||||
replace: "$&return true;"
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
|
|
||||||
settingsAboutComponent() {
|
|
||||||
return (
|
|
||||||
<Forms.FormSection>
|
|
||||||
<Forms.FormTitle tag="h3">What's the problem?</Forms.FormTitle>
|
|
||||||
<Forms.FormText style={{ marginBottom: 8 }}>
|
|
||||||
By default, Discord emits a GUILD_SUBSCRIPTIONS event for every guild you're in.
|
|
||||||
When you're in a lot of guilds, this can cause the gateway to ratelimit you.
|
|
||||||
This causes the client to crash and get stuck in an infinite ratelimit loop as it tries to reconnect.
|
|
||||||
</Forms.FormText>
|
|
||||||
|
|
||||||
<Forms.FormTitle tag="h3">How does it work?</Forms.FormTitle>
|
|
||||||
<Forms.FormText>
|
|
||||||
This plugin works by stopping the client from sending GUILD_SUBSCRIPTIONS events to the gateway when you open the unreads inbox.
|
|
||||||
This means that not all unreads will be shown, instead only already-subscribed guilds' unreads will be shown, but your client won't crash anymore.
|
|
||||||
</Forms.FormText>
|
|
||||||
</Forms.FormSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Vencord, a modification for Discord's desktop app
|
* Vencord, a modification for Discord's desktop app
|
||||||
* Copyright (c) 2022 Vendicated and contributors
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ApplicationCommandInputType, sendBotMessage } from "@api/Commands";
|
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands";
|
||||||
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 { findByPropsLazy } from "@webpack";
|
||||||
@ -35,30 +35,47 @@ export default definePlugin({
|
|||||||
name: "create friend invite",
|
name: "create friend invite",
|
||||||
description: "Generates a friend invite link.",
|
description: "Generates a friend invite link.",
|
||||||
inputType: ApplicationCommandInputType.BOT,
|
inputType: ApplicationCommandInputType.BOT,
|
||||||
execute: async (_, ctx) => {
|
options: [{
|
||||||
if (!UserStore.getCurrentUser().phone)
|
name: "Uses",
|
||||||
|
description: "How many uses?",
|
||||||
|
choices: [
|
||||||
|
{ label: "1", name: "1", value: "1" },
|
||||||
|
{ label: "5", name: "5", value: "5" }
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
type: ApplicationCommandOptionType.INTEGER
|
||||||
|
}],
|
||||||
|
|
||||||
|
execute: async (args, ctx) => {
|
||||||
|
const uses = findOption<number>(args, "Uses", 5);
|
||||||
|
|
||||||
|
if (uses === 1 && !UserStore.getCurrentUser().phone)
|
||||||
return sendBotMessage(ctx.channel.id, {
|
return sendBotMessage(ctx.channel.id, {
|
||||||
content: "You need to have a phone number connected to your account to create a friend invite!"
|
content: "You need to have a phone number connected to your account to create a friend invite with 1 use!"
|
||||||
});
|
});
|
||||||
|
|
||||||
const random = uuid.v4();
|
let invite: any;
|
||||||
const invite = await RestAPI.post({
|
if (uses === 1) {
|
||||||
url: "/friend-finder/find-friends",
|
const random = uuid.v4();
|
||||||
body: {
|
const { body: { invite_suggestions } } = await RestAPI.post({
|
||||||
modified_contacts: {
|
url: "/friend-finder/find-friends",
|
||||||
[random]: [1, "", ""]
|
body: {
|
||||||
},
|
modified_contacts: {
|
||||||
phone_contact_methods_count: 1
|
[random]: [1, "", ""]
|
||||||
}
|
},
|
||||||
}).then(res =>
|
phone_contact_methods_count: 1
|
||||||
FriendInvites.createFriendInvite({
|
}
|
||||||
code: res.body.invite_suggestions[0][3],
|
});
|
||||||
|
invite = await FriendInvites.createFriendInvite({
|
||||||
|
code: invite_suggestions[0][3],
|
||||||
recipient_phone_number_or_email: random,
|
recipient_phone_number_or_email: random,
|
||||||
contact_visibility: 1,
|
contact_visibility: 1,
|
||||||
filter_visibilities: [],
|
filter_visibilities: [],
|
||||||
filtered_invite_suggestions_index: 1
|
filtered_invite_suggestions_index: 1
|
||||||
})
|
});
|
||||||
);
|
} else {
|
||||||
|
invite = await FriendInvites.createFriendInvite();
|
||||||
|
}
|
||||||
|
|
||||||
sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
content: `
|
content: `
|
||||||
@ -67,7 +84,7 @@ export default definePlugin({
|
|||||||
Max uses: \`${invite.max_uses}\`
|
Max uses: \`${invite.max_uses}\`
|
||||||
`.trim().replace(/\s+/g, " ")
|
`.trim().replace(/\s+/g, " ")
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "view friend invites",
|
name: "view friend invites",
|
||||||
@ -95,7 +112,7 @@ export default definePlugin({
|
|||||||
execute: async (_, ctx) => {
|
execute: async (_, ctx) => {
|
||||||
await FriendInvites.revokeFriendInvites();
|
await FriendInvites.revokeFriendInvites();
|
||||||
|
|
||||||
return void sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
content: "All friend invites have been revoked."
|
content: "All friend invites have been revoked."
|
||||||
});
|
});
|
||||||
},
|
},
|
@ -49,7 +49,7 @@ export default definePlugin({
|
|||||||
await this.buildCss();
|
await this.buildCss();
|
||||||
|
|
||||||
addButton("HideAttachments", msg => {
|
addButton("HideAttachments", msg => {
|
||||||
if (!msg.attachments.length && !msg.embeds.length) return null;
|
if (!msg.attachments.length && !msg.embeds.length && !msg.stickerItems.length) return null;
|
||||||
|
|
||||||
const isHidden = hiddenMessages.has(msg.id);
|
const isHidden = hiddenMessages.has(msg.id);
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ export default definePlugin({
|
|||||||
async buildCss() {
|
async buildCss() {
|
||||||
const elements = [...hiddenMessages].map(id => `#message-accessories-${id}`).join(",");
|
const elements = [...hiddenMessages].map(id => `#message-accessories-${id}`).join(",");
|
||||||
style.textContent = `
|
style.textContent = `
|
||||||
:is(${elements}) [class*="embedWrapper"] {
|
:is(${elements}) :is([class*="embedWrapper"], [class*="clickableSticker"]) {
|
||||||
/* important is not necessary, but add it to make sure bad themes won't break it */
|
/* important is not necessary, but add it to make sure bad themes won't break it */
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
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
|
||||||
|
|
||||||
|
![the plugin in action](https://github.com/Vendicated/Vencord/assets/45497981/408cd77d-c5f4-40bc-8de2-f977a31b3e5f)
|
||||||
|
![the context menu options offered by the plugin](https://github.com/Vendicated/Vencord/assets/45497981/3bede636-f1ce-493f-af46-788b920cb81c)
|
@ -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"
|
||||||
@ -156,7 +165,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: '"renderLinkComponent","maxWidth"',
|
find: '"renderLinkComponent","maxWidth"',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(return\(.{1,100}\(\)\.wrapper.{1,100})(src)/,
|
match: /(return\(.{1,100}\(\)\.wrapper.{1,200})(src)/,
|
||||||
replace: `$1id: '${ELEMENT_ID}',$2`
|
replace: `$1id: '${ELEMENT_ID}',$2`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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,
|
@ -56,7 +56,8 @@ const quotes = [
|
|||||||
"hd{b${",
|
"hd{b${",
|
||||||
"<;vqkijbq33271:56<3799?24944:",
|
"<;vqkijbq33271:56<3799?24944:",
|
||||||
"Thof$lu'ofdn,!qsefc'az*bnrcma+&Om{o+iu\"`khct$)bnrd\"bcdoi&",
|
"Thof$lu'ofdn,!qsefc'az*bnrcma+&Om{o+iu\"`khct$)bnrd\"bcdoi&",
|
||||||
"snofplkb{)c'r\"lod'|f*aurv#cpno`abchijklmno"
|
"snofplkb{)c'r\"lod'|f*aurv#cpno`abchijklmno",
|
||||||
|
"Wdn`khc'|f*eghl{%"
|
||||||
];
|
];
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
@ -30,6 +30,9 @@ const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
|
|||||||
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
|
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sharedIntlNumberFormat = new Intl.NumberFormat();
|
||||||
|
const numberFormat = (value: number) => sharedIntlNumberFormat.format(value);
|
||||||
|
|
||||||
function MemberCount() {
|
function MemberCount() {
|
||||||
const { id: channelId, guild_id: guildId } = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
|
const { id: channelId, guild_id: guildId } = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
|
||||||
const { groups } = useStateFromStores(
|
const { groups } = useStateFromStores(
|
||||||
@ -57,7 +60,7 @@ function MemberCount() {
|
|||||||
alignContent: "center",
|
alignContent: "center",
|
||||||
gap: 0
|
gap: 0
|
||||||
}}>
|
}}>
|
||||||
<Tooltip text={`${online} Online in this Channel`} position="bottom">
|
<Tooltip text={`${numberFormat(online)} online in this channel`} position="bottom">
|
||||||
{props => (
|
{props => (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<span
|
<span
|
||||||
@ -70,11 +73,11 @@ function MemberCount() {
|
|||||||
marginRight: "0.5em"
|
marginRight: "0.5em"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span style={{ color: "var(--green-360)" }}>{online}</span>
|
<span style={{ color: "var(--green-360)" }}>{numberFormat(online)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip text={`${total} Total Server Members`} position="bottom">
|
<Tooltip text={`${numberFormat(total)} total server members`} position="bottom">
|
||||||
{props => (
|
{props => (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<span
|
<span
|
||||||
@ -88,7 +91,7 @@ function MemberCount() {
|
|||||||
marginLeft: "1em"
|
marginLeft: "1em"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span style={{ color: "var(--primary-400)" }}>{total}</span>
|
<span style={{ color: "var(--primary-400)" }}>{numberFormat(total)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
5
src/plugins/messageClickActions/README.md
Normal file
5
src/plugins/messageClickActions/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# MessageClickActions
|
||||||
|
|
||||||
|
Allows you to double click to edit/reply to a message or delete it if you hold the backspace key
|
||||||
|
|
||||||
|
![](https://github.com/Vendicated/Vencord/assets/55940580/6885aca2-4021-4910-b636-bb40f877a816)
|
@ -21,18 +21,17 @@ import { definePluginSettings, Settings } from "@api/Settings";
|
|||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { FluxDispatcher, PermissionStore, UserStore } from "@webpack/common";
|
import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
let isDeletePressed = false;
|
let isDeletePressed = false;
|
||||||
const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true);
|
const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true);
|
||||||
const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false);
|
const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false);
|
||||||
|
|
||||||
const MANAGE_CHANNELS = 1n << 4n;
|
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
enableDeleteOnClick: {
|
enableDeleteOnClick: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Enable delete on click",
|
description: "Enable delete on click while holding backspace",
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
enableDoubleClickToEdit: {
|
enableDoubleClickToEdit: {
|
||||||
@ -72,6 +71,7 @@ export default definePlugin({
|
|||||||
if (!isDeletePressed) {
|
if (!isDeletePressed) {
|
||||||
if (event.detail < 2) return;
|
if (event.detail < 2) return;
|
||||||
if (settings.store.requireModifier && !event.ctrlKey && !event.shiftKey) return;
|
if (settings.store.requireModifier && !event.ctrlKey && !event.shiftKey) return;
|
||||||
|
if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return;
|
||||||
|
|
||||||
if (isMe) {
|
if (isMe) {
|
||||||
if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id)) return;
|
if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id)) return;
|
||||||
@ -89,7 +89,7 @@ export default definePlugin({
|
|||||||
showMentionToggle: channel.guild_id !== null
|
showMentionToggle: channel.guild_id !== null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (settings.store.enableDeleteOnClick && (isMe || PermissionStore.can(MANAGE_CHANNELS, channel))) {
|
} else if (settings.store.enableDeleteOnClick && (isMe || PermissionStore.can(PermissionsBits.MANAGE_MESSAGES, channel))) {
|
||||||
if (msg.deleted) {
|
if (msg.deleted) {
|
||||||
FluxDispatcher.dispatch({
|
FluxDispatcher.dispatch({
|
||||||
type: "MESSAGE_DELETE",
|
type: "MESSAGE_DELETE",
|
@ -169,21 +169,14 @@ export default definePlugin({
|
|||||||
try {
|
try {
|
||||||
if (cache == null || (!isBulk && !cache.has(data.id))) return cache;
|
if (cache == null || (!isBulk && !cache.has(data.id))) return cache;
|
||||||
|
|
||||||
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds } = Settings.plugins.MessageLogger;
|
const mutate = (id: string) => {
|
||||||
const myId = UserStore.getCurrentUser().id;
|
|
||||||
|
|
||||||
function mutate(id: string) {
|
|
||||||
const msg = cache.get(id);
|
const msg = cache.get(id);
|
||||||
if (!msg) return;
|
if (!msg) return;
|
||||||
|
|
||||||
const EPHEMERAL = 64;
|
const EPHEMERAL = 64;
|
||||||
const shouldIgnore = data.mlDeleted ||
|
const shouldIgnore = data.mlDeleted ||
|
||||||
(msg.flags & EPHEMERAL) === EPHEMERAL ||
|
(msg.flags & EPHEMERAL) === EPHEMERAL ||
|
||||||
ignoreBots && msg.author?.bot ||
|
this.shouldIgnore(msg);
|
||||||
ignoreSelf && msg.author?.id === myId ||
|
|
||||||
ignoreUsers.includes(msg.author?.id) ||
|
|
||||||
ignoreChannels.includes(msg.channel_id) ||
|
|
||||||
ignoreGuilds.includes(ChannelStore.getChannel(msg.channel_id)?.guild_id);
|
|
||||||
|
|
||||||
if (shouldIgnore) {
|
if (shouldIgnore) {
|
||||||
cache = cache.remove(id);
|
cache = cache.remove(id);
|
||||||
@ -192,7 +185,7 @@ export default definePlugin({
|
|||||||
.set("deleted", true)
|
.set("deleted", true)
|
||||||
.set("attachments", m.attachments.map(a => (a.deleted = true, a))));
|
.set("attachments", m.attachments.map(a => (a.deleted = true, a))));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (isBulk) {
|
if (isBulk) {
|
||||||
data.ids.forEach(mutate);
|
data.ids.forEach(mutate);
|
||||||
@ -205,6 +198,18 @@ export default definePlugin({
|
|||||||
return cache;
|
return cache;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shouldIgnore(message: any) {
|
||||||
|
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds } = Settings.plugins.MessageLogger;
|
||||||
|
const myId = UserStore.getCurrentUser().id;
|
||||||
|
|
||||||
|
return ignoreBots && message.author?.bot ||
|
||||||
|
ignoreSelf && message.author?.id === myId ||
|
||||||
|
ignoreUsers.includes(message.author?.id) ||
|
||||||
|
ignoreChannels.includes(message.channel_id) ||
|
||||||
|
ignoreChannels.includes(ChannelStore.getChannel(message.channel_id)?.parent_id) ||
|
||||||
|
ignoreGuilds.includes(ChannelStore.getChannel(message.channel_id)?.guild_id);
|
||||||
|
},
|
||||||
|
|
||||||
// Based on canary 9ab8626bcebceaea6da570b9c586172d02b9c996
|
// Based on canary 9ab8626bcebceaea6da570b9c586172d02b9c996
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
@ -237,7 +242,7 @@ export default definePlugin({
|
|||||||
match: /(MESSAGE_UPDATE:function\((\w)\).+?)\.update\((\w)/,
|
match: /(MESSAGE_UPDATE:function\((\w)\).+?)\.update\((\w)/,
|
||||||
replace: "$1" +
|
replace: "$1" +
|
||||||
".update($3,m =>" +
|
".update($3,m =>" +
|
||||||
" (($2.message.flags & 64) === 64 || (Vencord.Settings.plugins.MessageLogger.ignoreBots && $2.message.author?.bot) || (Vencord.Settings.plugins.MessageLogger.ignoreSelf && $2.message.author?.id === Vencord.Webpack.Common.UserStore.getCurrentUser().id)) ? m :" +
|
" (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message)) ? m :" +
|
||||||
" $2.message.content !== m.editHistory?.[0]?.content && $2.message.content !== m.content ?" +
|
" $2.message.content !== m.editHistory?.[0]?.content && $2.message.content !== m.content ?" +
|
||||||
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" +
|
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" +
|
||||||
" m" +
|
" m" +
|
||||||
@ -321,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
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user