Compare commits
122 commits
immediate-
...
main
Author | SHA1 | Date | |
---|---|---|---|
Ulysia | 1d5709763a | ||
9d4e859a0a | |||
439a4f8eb6 | |||
00f82e96bd | |||
5216bcca1e | |||
e7e298d2e7 | |||
d897dab054 | |||
88e8fa7e90 | |||
f5f59be1b6 | |||
534ab3eb5f | |||
a6ea03bacc | |||
Ulysia | c3bdc71512 | ||
e620431210 | |||
5afc24b41a | |||
7f8e241b9c | |||
553293ceee | |||
0af820c874 | |||
a11ccde40f | |||
58c3032bb2 | |||
4e89352758 | |||
e818905520 | |||
aa1b446c07 | |||
2dce060cf9 | |||
89bb3ee30a | |||
47db61d00e | |||
Ulysia | a592586af7 | ||
Ulysia | 0567320fc6 | ||
416d85dcf0 | |||
013c8d061d | |||
b5f626d1ff | |||
1e01f85217 | |||
91a32e22de | |||
43b3c137ce | |||
18f7b74210 | |||
eab0cf9966 | |||
e7956413e2 | |||
832e874c35 | |||
55146db760 | |||
bc59fc41b3 | |||
1abfb5f0cf | |||
cb2848f186 | |||
c3f2e76b9c | |||
eb63a54fa6 | |||
5881716c57 | |||
d7cbe270e5 | |||
c29362ca89 | |||
fd0fbace2e | |||
409f47bf24 | |||
b0e2f310bc | |||
65069c673c | |||
b1db18c319 | |||
db5fe2a394 | |||
e4318a887a | |||
eaf62d8c1c | |||
22a5b18bfa | |||
1dc2d92493 | |||
492b0cff08 | |||
2d675b4b2e | |||
49b0a38c37 | |||
467157539c | |||
e8242f22c9 | |||
f7587d9b2e | |||
755e869db7 | |||
a015cf96f6 | |||
c7e5295da0 | |||
8afd79dd50 | |||
65c5897dc3 | |||
6cce8a8bc4 | |||
1848b16536 | |||
c572116b97 | |||
e26986f66a | |||
f12335a371 | |||
640d99dcda | |||
bcfef05a8a | |||
f17b92c2fd | |||
292f7d71d3 | |||
9d67421b34 | |||
b822542352 | |||
f27361f017 | |||
a765212cfe | |||
c07d78fa96 | |||
56459bdcad | |||
868b2ea9f0 | |||
9e7f8829f2 | |||
e5a4db6460 | |||
8890c8c6b4 | |||
61d3c08f1f | |||
e6994e1946 | |||
40512d7294 | |||
be02baffaa | |||
99cd423efb | |||
7333f40db6 | |||
244cb26c32 | |||
df8aec8e3c | |||
d10e649b63 | |||
7f784befc2 | |||
30e4e83158 | |||
4c4f2894fb | |||
c51d7b8fb4 | |||
accfc15125 | |||
27e81b20db | |||
d0ad4e6c1d | |||
0c71d6c3fa | |||
74fd85bd3d | |||
968e688c10 | |||
b595a3e33c | |||
e07a4e19e6 | |||
273981deb7 | |||
81eabc7ee2 | |||
d5eaae9d51 | |||
00276cad7c | |||
ee9fbc4d25 | |||
0cea347264 | |||
eb0d91fd8e | |||
07e629d8d4 | |||
e473a57c3d | |||
ad3d936dfd | |||
db2f5c9292 | |||
5bfc608f7d | |||
7d8214fc37 | |||
cdf72cf4be | |||
4b16fbcaa9 |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -18,7 +18,5 @@ lerna-debug.log*
|
|||
.pnpm-debug.log*
|
||||
*.tsbuildinfo
|
||||
|
||||
src/userplugins
|
||||
|
||||
ExtensionCache/
|
||||
settings/
|
||||
|
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "src/userplugins/vc-message-logger-enhanced"]
|
||||
path = src/userplugins/vc-message-logger-enhanced
|
||||
url = https://github.com/Syncxv/vc-message-logger-enhanced.git
|
|
@ -5,6 +5,7 @@
|
|||
// @author Vendicated (https://github.com/Vendicated)
|
||||
// @namespace https://github.com/Vendicated/Vencord
|
||||
// @supportURL https://github.com/Vendicated/Vencord
|
||||
// @icon https://raw.githubusercontent.com/Vendicated/Vencord/refs/heads/main/browser/icon.png
|
||||
// @license GPL-3.0
|
||||
// @match *://*.discord.com/*
|
||||
// @grant GM_xmlhttpRequest
|
||||
|
|
|
@ -90,13 +90,7 @@ export default tseslint.config(
|
|||
"no-invalid-regexp": "error",
|
||||
"no-constant-condition": ["error", { "checkLoops": false }],
|
||||
"no-duplicate-imports": "error",
|
||||
"@typescript-eslint/dot-notation": [
|
||||
"error",
|
||||
{
|
||||
"allowPrivateClassPropertyAccess": true,
|
||||
"allowProtectedClassPropertyAccess": true
|
||||
}
|
||||
],
|
||||
"dot-notation": "error",
|
||||
"no-useless-escape": [
|
||||
"error",
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.9.8",
|
||||
"version": "1.10.6",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
@ -35,6 +35,7 @@
|
|||
"testTsc": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@intrnl/xxhash64": "^0.1.2",
|
||||
"@sapphi-red/web-noise-suppressor": "0.3.5",
|
||||
"@vap/core": "0.0.12",
|
||||
"@vap/shiki": "0.10.5",
|
||||
|
@ -70,6 +71,7 @@
|
|||
"stylelint": "^16.8.1",
|
||||
"stylelint-config-standard": "^36.0.1",
|
||||
"ts-patch": "^3.2.1",
|
||||
"ts-pattern": "^5.3.1",
|
||||
"tsx": "^4.16.5",
|
||||
"type-fest": "^4.23.0",
|
||||
"typescript": "^5.5.4",
|
||||
|
|
|
@ -16,6 +16,9 @@ importers:
|
|||
|
||||
.:
|
||||
dependencies:
|
||||
'@intrnl/xxhash64':
|
||||
specifier: ^0.1.2
|
||||
version: 0.1.2
|
||||
'@sapphi-red/web-noise-suppressor':
|
||||
specifier: 0.3.5
|
||||
version: 0.3.5
|
||||
|
@ -116,6 +119,9 @@ importers:
|
|||
ts-patch:
|
||||
specifier: ^3.2.1
|
||||
version: 3.2.1
|
||||
ts-pattern:
|
||||
specifier: ^5.3.1
|
||||
version: 5.3.1
|
||||
tsx:
|
||||
specifier: ^4.16.5
|
||||
version: 4.16.5
|
||||
|
@ -534,6 +540,9 @@ packages:
|
|||
resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@intrnl/xxhash64@0.1.2':
|
||||
resolution: {integrity: sha512-1+lx7j99fdph+uy3EnjQyr39KQZ7LP56+aWOr6finJWpgYpvb7XrhFUqDwnEk/wpPC98nCjAT6RulpW3crWjlg==}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.5':
|
||||
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
@ -2524,6 +2533,9 @@ packages:
|
|||
resolution: {integrity: sha512-hlR43v+GUIUy8/ZGFP1DquEqPh7PFKQdDMTAmYt671kCCA6AkDQMoeFaFmZ7ObPLYOmpMgyKUqL1C+coFMf30w==}
|
||||
hasBin: true
|
||||
|
||||
ts-pattern@5.3.1:
|
||||
resolution: {integrity: sha512-1RUMKa8jYQdNfmnK4jyzBK3/PS/tnjcZ1CW0v1vWDeYe5RBklc/nquw03MEoB66hVBm4BnlCfmOqDVxHyT1DpA==}
|
||||
|
||||
tsconfig-paths@3.15.0:
|
||||
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
|
||||
|
||||
|
@ -2933,6 +2945,8 @@ snapshots:
|
|||
|
||||
'@humanwhocodes/retry@0.3.0': {}
|
||||
|
||||
'@intrnl/xxhash64@0.1.2': {}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.5':
|
||||
dependencies:
|
||||
'@jridgewell/set-array': 1.2.1
|
||||
|
@ -5158,6 +5172,8 @@ snapshots:
|
|||
semver: 7.6.3
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
ts-pattern@5.3.1: {}
|
||||
|
||||
tsconfig-paths@3.15.0:
|
||||
dependencies:
|
||||
'@types/json5': 0.0.29
|
||||
|
|
|
@ -312,7 +312,7 @@ export const commonOpts = {
|
|||
logLevel: "info",
|
||||
bundle: true,
|
||||
watch,
|
||||
minify: !watch && !IS_REPORTER,
|
||||
minify: !watch,
|
||||
sourcemap: watch ? "inline" : "",
|
||||
legalComments: "linked",
|
||||
banner,
|
||||
|
@ -334,5 +334,6 @@ export const commonRendererPlugins = [
|
|||
banImportPlugin(builtinModuleRegex, "Cannot import node inbuilt modules in browser code. You need to use a native.ts file"),
|
||||
banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"),
|
||||
banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"),
|
||||
banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"),
|
||||
...commonOpts.plugins
|
||||
];
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
import { readFileSync } from "fs";
|
||||
import pup, { JSHandle } from "puppeteer-core";
|
||||
|
||||
for (const variable of ["CHROMIUM_BIN"]) {
|
||||
for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) {
|
||||
if (!process.env[variable]) {
|
||||
console.error(`Missing environment variable ${variable}`);
|
||||
process.exit(1);
|
||||
|
@ -214,7 +214,7 @@ page.on("console", async e => {
|
|||
|
||||
switch (tag) {
|
||||
case "WebpackInterceptor:":
|
||||
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module|took [\d.]+?ms) \(Module id is (.+?)\): (.+)/)!;
|
||||
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!;
|
||||
if (!patchFailMatch) break;
|
||||
|
||||
console.error(await getText());
|
||||
|
@ -291,7 +291,7 @@ page.on("error", e => console.error("[Error]", e.message));
|
|||
page.on("pageerror", e => {
|
||||
if (e.message.includes("Sentry successfully disabled")) return;
|
||||
|
||||
if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module") && !/^.{1,2}$/.test(e.message)) {
|
||||
if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) {
|
||||
console.error("[Page Error]", e.message);
|
||||
report.otherErrors.push(e.message);
|
||||
} else {
|
||||
|
@ -299,9 +299,20 @@ page.on("pageerror", e => {
|
|||
}
|
||||
});
|
||||
|
||||
async function reporterRuntime(token: string) {
|
||||
Vencord.Webpack.waitFor(
|
||||
"loginToken",
|
||||
m => {
|
||||
console.log("[PUP_DEBUG]", "Logging in with token...");
|
||||
m.loginToken(token);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await page.evaluateOnNewDocument(`
|
||||
if (location.host.endsWith("discord.com")) {
|
||||
${readFileSync("./dist/browser.js", "utf-8")};
|
||||
(${reporterRuntime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)});
|
||||
}
|
||||
`);
|
||||
|
||||
|
|
2
setup-origins.sh
Executable file
2
setup-origins.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
git remote add upstream https://github.com/Vendicated/Vencord.git
|
||||
git remote set-url --pull upstream DISABLED
|
|
@ -23,10 +23,10 @@ export * as Util from "./utils";
|
|||
export * as QuickCss from "./utils/quickCss";
|
||||
export * as Updater from "./utils/updater";
|
||||
export * as Webpack from "./webpack";
|
||||
export * as WebpackPatcher from "./webpack/patchWebpack";
|
||||
export { PlainSettings, Settings };
|
||||
|
||||
import "./utils/quickCss";
|
||||
import "./webpack/patchWebpack";
|
||||
|
||||
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
|
||||
import { StartAt } from "@utils/types";
|
||||
|
@ -39,7 +39,7 @@ import { localStorage } from "./utils/localStorage";
|
|||
import { relaunch } from "./utils/native";
|
||||
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
|
||||
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
|
||||
import { onceDiscordLoaded } from "./webpack";
|
||||
import { onceReady } from "./webpack";
|
||||
import { SettingsRouter } from "./webpack/common";
|
||||
|
||||
if (IS_REPORTER) {
|
||||
|
@ -86,7 +86,7 @@ async function syncSettings() {
|
|||
}
|
||||
|
||||
async function init() {
|
||||
await onceDiscordLoaded;
|
||||
await onceReady;
|
||||
startAllPlugins(StartAt.WebpackReady);
|
||||
|
||||
syncSettings();
|
||||
|
@ -125,7 +125,7 @@ async function init() {
|
|||
const pendingPatches = patches.filter(p => !p.all && p.predicate?.() !== false);
|
||||
if (pendingPatches.length)
|
||||
PMLogger.warn(
|
||||
"Webpack has finished initializing, but some patches haven't been applied yet.",
|
||||
"Webpack has finished initialising, but some patches haven't been applied yet.",
|
||||
"This might be expected since some Modules are lazy loaded, but please verify",
|
||||
"that all plugins are working as intended.",
|
||||
"You are seeing this warning because this is a Development build of Vencord.",
|
||||
|
|
|
@ -8,12 +8,13 @@ import "./ChatButton.css";
|
|||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { findByProps } from "@webpack";
|
||||
import { waitFor } from "@webpack";
|
||||
import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
import { HTMLProps, MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
const ChannelTextAreaClasses = findByProps<Record<"button" | "buttonContainer", string>>("buttonContainer", "channelTextArea");
|
||||
let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>;
|
||||
waitFor(["buttonContainer", "channelTextArea"], m => ChannelTextAreaClasses = m);
|
||||
|
||||
export interface ChatBarProps {
|
||||
channel: Channel;
|
||||
|
|
|
@ -17,14 +17,14 @@
|
|||
*/
|
||||
|
||||
import { mergeDefaults } from "@utils/mergeDefaults";
|
||||
import { findByCode } from "@webpack";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { MessageActions, SnowflakeUtils } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
import type { PartialDeep } from "type-fest";
|
||||
|
||||
import { Argument } from "./types";
|
||||
|
||||
const createBotMessage = findByCode('username:"Clyde"');
|
||||
const createBotMessage = findByCodeLazy('username:"Clyde"');
|
||||
|
||||
export function generateId() {
|
||||
return `-${SnowflakeUtils.fromTimestamp(Date.now())}`;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { makeCodeblock } from "@utils/text";
|
||||
|
||||
import { sendBotMessage } from "./commandHelpers";
|
||||
|
@ -46,10 +47,10 @@ export let RequiredMessageOption: Option = ReqPlaceholder;
|
|||
export const _init = function (cmds: Command[]) {
|
||||
try {
|
||||
BUILT_IN = cmds;
|
||||
OptionalMessageOption = cmds.find(c => c.name === "shrug")!.options![0];
|
||||
RequiredMessageOption = cmds.find(c => c.name === "me")!.options![0];
|
||||
OptionalMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "shrug")!.options![0];
|
||||
RequiredMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "me")!.options![0];
|
||||
} catch (e) {
|
||||
console.error("Failed to load CommandsApi");
|
||||
new Logger("CommandsAPI").error("Failed to load CommandsApi", e, " - cmds is", cmds);
|
||||
}
|
||||
return cmds;
|
||||
} as never;
|
||||
|
@ -138,6 +139,8 @@ export function registerCommand<C extends Command>(command: C, plugin: string) {
|
|||
throw new Error(`Command '${command.name}' already exists.`);
|
||||
|
||||
command.isVencordCommand = true;
|
||||
command.untranslatedName ??= command.name;
|
||||
command.untranslatedDescription ??= command.description;
|
||||
command.id ??= `-${BUILT_IN.length + 1}`;
|
||||
command.applicationId ??= "-1"; // BUILT_IN;
|
||||
command.type ??= ApplicationCommandType.CHAT_INPUT;
|
||||
|
|
|
@ -93,8 +93,10 @@ export interface Command {
|
|||
isVencordCommand?: boolean;
|
||||
|
||||
name: string;
|
||||
untranslatedName?: string;
|
||||
displayName?: string;
|
||||
description: string;
|
||||
untranslatedDescription?: string;
|
||||
displayDescription?: string;
|
||||
|
||||
options?: Option[];
|
||||
|
|
|
@ -90,19 +90,20 @@ export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallba
|
|||
* A helper function for finding the children array of a group nested inside a context menu based on the id(s) of its children
|
||||
* @param id The id of the child. If an array is specified, all ids will be tried
|
||||
* @param children The context menu children
|
||||
* @param matchSubstring Whether to check if the id is a substring of the child id
|
||||
*/
|
||||
export function findGroupChildrenByChildId(id: string | string[], children: Array<ReactElement | null>): Array<ReactElement | null> | null {
|
||||
export function findGroupChildrenByChildId(id: string | string[], children: Array<ReactElement | null | undefined>, matchSubstring = false): Array<ReactElement | null | undefined> | null {
|
||||
for (const child of children) {
|
||||
if (child == null) continue;
|
||||
|
||||
if (Array.isArray(child)) {
|
||||
const found = findGroupChildrenByChildId(id, child);
|
||||
const found = findGroupChildrenByChildId(id, child, matchSubstring);
|
||||
if (found !== null) return found;
|
||||
}
|
||||
|
||||
if (
|
||||
(Array.isArray(id) && id.some(id => child.props?.id === id))
|
||||
|| child.props?.id === id
|
||||
(Array.isArray(id) && id.some(id => matchSubstring ? child.props?.id?.includes(id) : child.props?.id === id))
|
||||
|| (matchSubstring ? child.props?.id?.includes(id) : child.props?.id === id)
|
||||
) return children;
|
||||
|
||||
let nextChildren = child.props?.children;
|
||||
|
@ -112,7 +113,7 @@ export function findGroupChildrenByChildId(id: string | string[], children: Arra
|
|||
child.props.children = nextChildren;
|
||||
}
|
||||
|
||||
const found = findGroupChildrenByChildId(id, nextChildren);
|
||||
const found = findGroupChildrenByChildId(id, nextChildren, matchSubstring);
|
||||
if (found !== null) return found;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,22 +16,23 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { find } from "@webpack";
|
||||
import { waitFor } from "@webpack";
|
||||
|
||||
const Notices = find(m => m.show && m.dismiss && !m.suppressAll);
|
||||
let NoticesModule: any;
|
||||
waitFor(m => m.show && m.dismiss && !m.suppressAll, m => NoticesModule = m);
|
||||
|
||||
export const noticesQueue = [] as any[];
|
||||
export let currentNotice: any = null;
|
||||
|
||||
export function popNotice() {
|
||||
Notices.dismiss();
|
||||
NoticesModule.dismiss();
|
||||
}
|
||||
|
||||
export function nextNotice() {
|
||||
currentNotice = noticesQueue.shift();
|
||||
|
||||
if (currentNotice) {
|
||||
Notices.show(...currentNotice, "VencordNotice");
|
||||
NoticesModule.show(...currentNotice, "VencordNotice");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,9 @@ export interface Settings {
|
|||
autoUpdate: boolean;
|
||||
autoUpdateNotification: boolean,
|
||||
useQuickCss: boolean;
|
||||
themeLinks: string[];
|
||||
eagerPatches: boolean;
|
||||
enabledThemes: string[];
|
||||
enableReactDevtools: boolean;
|
||||
themeLinks: string[];
|
||||
enabledThemes: string[];
|
||||
frameless: boolean;
|
||||
transparent: boolean;
|
||||
winCtrlQ: boolean;
|
||||
|
@ -82,7 +81,6 @@ const DefaultSettings: Settings = {
|
|||
autoUpdateNotification: true,
|
||||
useQuickCss: true,
|
||||
themeLinks: [],
|
||||
eagerPatches: IS_REPORTER,
|
||||
enabledThemes: [],
|
||||
enableReactDevtools: false,
|
||||
frameless: false,
|
||||
|
@ -118,6 +116,7 @@ const saveSettingsOnFrequentAction = debounce(async () => {
|
|||
}
|
||||
}, 60_000);
|
||||
|
||||
|
||||
export const SettingsStore = new SettingsStoreClass(settings, {
|
||||
readOnly: true,
|
||||
getDefaultValue({
|
||||
|
|
|
@ -89,8 +89,8 @@ export const isStyleEnabled = (name: string) => requireStyle(name).dom?.isConnec
|
|||
* // -- plugin.ts --
|
||||
* import pluginStyle from "./plugin.css?managed";
|
||||
* import { setStyleVars } from "@api/Styles";
|
||||
* import { findByProps } from "@webpack";
|
||||
* const classNames = findByProps("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... }
|
||||
* import { findByPropsLazy } from "@webpack";
|
||||
* const classNames = findByPropsLazy("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... }
|
||||
*
|
||||
* // Inside some plugin method like "start()"
|
||||
* setStyleClassNames(pluginStyle, classNames);
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
*/
|
||||
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { findByFactoryCode } from "@webpack";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { findModuleId, proxyLazyWebpack, wreq } from "@webpack";
|
||||
|
||||
interface UserSettingDefinition<T> {
|
||||
/**
|
||||
|
@ -42,7 +43,12 @@ interface UserSettingDefinition<T> {
|
|||
userSettingsAPIName: string;
|
||||
}
|
||||
|
||||
export const UserSettings = findByFactoryCode<Record<PropertyKey, UserSettingDefinition<any>>>('"textAndImages","renderSpoilers"');
|
||||
export const UserSettings: Record<PropertyKey, UserSettingDefinition<any>> | undefined = proxyLazyWebpack(() => {
|
||||
const modId = findModuleId('"textAndImages","renderSpoilers"');
|
||||
if (modId == null) return new Logger("UserSettingsAPI ").error("Didn't find settings module.");
|
||||
|
||||
return wreq(modId as any);
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the setting with the given setting group and name.
|
||||
|
@ -50,7 +56,7 @@ export const UserSettings = findByFactoryCode<Record<PropertyKey, UserSettingDef
|
|||
* @param group The setting group
|
||||
* @param name The name of the setting
|
||||
*/
|
||||
export function getUserSetting<T = any>(group: string, name: string): UserSettingDefinition<T> {
|
||||
export function getUserSetting<T = any>(group: string, name: string): UserSettingDefinition<T> | undefined {
|
||||
if (!Vencord.Plugins.isPluginEnabled("UserSettingsAPI")) throw new Error("Cannot use UserSettingsAPI without setting as dependency.");
|
||||
|
||||
for (const key in UserSettings) {
|
||||
|
@ -60,12 +66,10 @@ export function getUserSetting<T = any>(group: string, name: string): UserSettin
|
|||
return userSetting;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`UserSettingsAPI: Setting ${group}.${name} not found.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy version of {@link getUserSetting}
|
||||
* {@link getUserSettingDefinition}, lazy.
|
||||
*
|
||||
* Get the setting with the given setting group and name.
|
||||
*
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findByProps } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Parser } from "@webpack/common";
|
||||
|
||||
const CodeContainerClasses = findByProps("markup", "codeContainer");
|
||||
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer");
|
||||
|
||||
/**
|
||||
* Renders code in a Discord codeblock
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
|
||||
import "./iconStyles.css";
|
||||
|
||||
import { getTheme, Theme } from "@utils/discord";
|
||||
import { getIntlMessage, getTheme, Theme } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import { i18n } from "@webpack/common";
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
interface BaseIconProps extends IconProps {
|
||||
|
@ -65,8 +64,7 @@ export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Discord's copy icon, as seen in the user popout right of the username when clicking
|
||||
* your own username in the bottom left user panel
|
||||
* Discord's copy icon, as seen in the user panel popout on the right of the username and in large code blocks
|
||||
*/
|
||||
export function CopyIcon(props: IconProps) {
|
||||
return (
|
||||
|
@ -76,8 +74,9 @@ export function CopyIcon(props: IconProps) {
|
|||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="currentColor">
|
||||
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1z" />
|
||||
<path d="M15 5H8c-1.1 0-1.99.9-1.99 2L6 21c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V11l-6-6zM8 21V7h6v5h5v9H8z" />
|
||||
<path d="M3 16a1 1 0 0 1-1-1v-5a8 8 0 0 1 8-8h5a1 1 0 0 1 1 1v.5a.5.5 0 0 1-.5.5H10a6 6 0 0 0-6 6v5.5a.5.5 0 0 1-.5.5H3Z" />
|
||||
<path d="M6 18a4 4 0 0 0 4 4h8a4 4 0 0 0 4-4v-4h-3a5 5 0 0 1-5-5V6h-4a4 4 0 0 0-4 4v8Z" />
|
||||
<path d="M21.73 12a3 3 0 0 0-.6-.88l-4.25-4.24a3 3 0 0 0-.88-.61V9a3 3 0 0 0 3 3h2.73Z" />
|
||||
</g>
|
||||
</Icon>
|
||||
);
|
||||
|
@ -133,7 +132,7 @@ export function InfoIcon(props: IconProps) {
|
|||
export function OwnerCrownIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
aria-label={i18n.Messages.GUILD_OWNER}
|
||||
aria-label={getIntlMessage("GUILD_OWNER")}
|
||||
{...props}
|
||||
className={classes(props.className, "vc-owner-crown-icon")}
|
||||
role="img"
|
||||
|
|
|
@ -24,12 +24,13 @@ import { classNameFactory } from "@api/Styles";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { gitRemote } from "@shared/vencordUserAgent";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { OptionType, Plugin } from "@utils/types";
|
||||
import { find, findByProps, findComponentByCode } from "@webpack";
|
||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserUtils } from "@webpack/common";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import { Constructor } from "type-fest";
|
||||
|
||||
|
@ -49,9 +50,9 @@ import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
|||
|
||||
const cl = classNameFactory("vc-plugin-modal-");
|
||||
|
||||
const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const AvatarStyles = findByProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||
const UserRecord = find<Constructor<Partial<User>>>(m => m?.prototype?.getAvatarURL && m?.prototype?.hasHadPremium);
|
||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
|
||||
|
||||
interface PluginModalProps extends ModalProps {
|
||||
plugin: Plugin;
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { OptionType, PluginOptionNumber } from "@utils/types";
|
||||
import { Forms, React, TextInput } from "@webpack/common";
|
||||
|
||||
|
@ -54,7 +56,8 @@ export function SettingNumericComponent({ option, pluginSettings, definedSetting
|
|||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{option.description}</Forms.FormTitle>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<TextInput
|
||||
type="number"
|
||||
pattern="-?[0-9]+"
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionSelect } from "@utils/types";
|
||||
import { Forms, React, Select } from "@webpack/common";
|
||||
|
||||
|
@ -44,7 +46,8 @@ export function SettingSelectComponent({ option, pluginSettings, definedSettings
|
|||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{option.description}</Forms.FormTitle>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom16} type="description">{option.description}</Forms.FormText>
|
||||
<Select
|
||||
isDisabled={option.disabled?.call(definedSettings) ?? false}
|
||||
options={option.options}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionSlider } from "@utils/types";
|
||||
import { Forms, React, Slider } from "@webpack/common";
|
||||
|
||||
|
@ -50,7 +52,8 @@ export function SettingSliderComponent({ option, pluginSettings, definedSettings
|
|||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{option.description}</Forms.FormTitle>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<Slider
|
||||
disabled={option.disabled?.call(definedSettings) ?? false}
|
||||
markers={option.markers}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionString } from "@utils/types";
|
||||
import { Forms, React, TextInput } from "@webpack/common";
|
||||
|
||||
|
@ -41,7 +43,8 @@ export function SettingTextComponent({ option, pluginSettings, definedSettings,
|
|||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{option.description}</Forms.FormTitle>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<TextInput
|
||||
type="text"
|
||||
value={state}
|
||||
|
|
|
@ -33,19 +33,19 @@ import { Margins } from "@utils/margins";
|
|||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { Plugin } from "@utils/types";
|
||||
import { findByProps } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo } from "@webpack/common";
|
||||
|
||||
import Plugins, { ExcludedPlugins } from "~plugins";
|
||||
|
||||
// Avoid circular dependency
|
||||
const PluginManager = proxyLazy(() => require("../../plugins")) as typeof import("../../plugins");
|
||||
const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins"));
|
||||
|
||||
const cl = classNameFactory("vc-plugins-");
|
||||
const logger = new Logger("PluginSettings", "#a6d189");
|
||||
|
||||
const InputStyles = findByProps("inputWrapper", "inputDefault", "error");
|
||||
const ButtonClasses = findByProps("button", "disabled", "enabled");
|
||||
const InputStyles = findByPropsLazy("inputWrapper", "inputDefault", "error");
|
||||
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
||||
|
||||
|
||||
function showErrorToast(message: string) {
|
||||
|
@ -93,14 +93,14 @@ interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
|
|||
export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
|
||||
const settings = Settings.plugins[plugin.name];
|
||||
|
||||
const isEnabled = () => settings.enabled ?? false;
|
||||
const isEnabled = () => Vencord.Plugins.isPluginEnabled(plugin.name);
|
||||
|
||||
function toggleEnabled() {
|
||||
const wasEnabled = isEnabled();
|
||||
|
||||
// If we're enabling a plugin, make sure all deps are enabled recursively.
|
||||
if (!wasEnabled) {
|
||||
const { restartNeeded, failures } = PluginManager.startDependenciesRecursive(plugin);
|
||||
const { restartNeeded, failures } = startDependenciesRecursive(plugin);
|
||||
if (failures.length) {
|
||||
logger.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`);
|
||||
showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null);
|
||||
|
@ -126,7 +126,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
|
|||
return;
|
||||
}
|
||||
|
||||
const result = wasEnabled ? PluginManager.stopPlugin(plugin) : PluginManager.startPlugin(plugin);
|
||||
const result = wasEnabled ? stopPlugin(plugin) : startPlugin(plugin);
|
||||
|
||||
if (!result) {
|
||||
settings.enabled = false;
|
||||
|
@ -292,10 +292,10 @@ export default function PluginSettings() {
|
|||
|
||||
if (!pluginFilter(p)) continue;
|
||||
|
||||
const isRequired = p.required || depMap[p.name]?.some(d => settings.plugins[d].enabled);
|
||||
const isRequired = p.required || p.isDependency || depMap[p.name]?.some(d => settings.plugins[d].enabled);
|
||||
|
||||
if (isRequired) {
|
||||
const tooltipText = p.required
|
||||
const tooltipText = p.required || !depMap[p.name]
|
||||
? "This plugin is required for Vencord to function."
|
||||
: makeDependencyList(depMap[p.name]?.filter(d => settings.plugins[d].enabled));
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import "./Switch.css";
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { findByProps } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
|
||||
interface SwitchProps {
|
||||
checked: boolean;
|
||||
|
@ -29,7 +29,7 @@ interface SwitchProps {
|
|||
|
||||
const SWITCH_ON = "var(--green-360)";
|
||||
const SWITCH_OFF = "var(--primary-400)";
|
||||
const SwitchClasses = findByProps("slider", "input", "container");
|
||||
const SwitchClasses = findByPropsLazy("slider", "input", "container");
|
||||
|
||||
export function Switch({ checked, onChange, disabled }: SwitchProps) {
|
||||
return (
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Margins } from "@utils/margins";
|
|||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||
import { makeCodeblock } from "@utils/text";
|
||||
import { Patch, ReplaceFn } from "@utils/types";
|
||||
import { searchFactories } from "@webpack";
|
||||
import { search } from "@webpack";
|
||||
import { Button, Clipboard, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common";
|
||||
|
||||
import { SettingsTab, wrapTab } from "./shared";
|
||||
|
@ -33,7 +33,7 @@ if (IS_DEV) {
|
|||
}
|
||||
|
||||
const findCandidates = debounce(function ({ find, setModule, setError }) {
|
||||
const candidates = searchFactories(find);
|
||||
const candidates = search(find);
|
||||
const keys = Object.keys(candidates);
|
||||
const len = keys.length;
|
||||
if (len === 0)
|
||||
|
@ -56,7 +56,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
|
|||
const [compileResult, setCompileResult] = React.useState<[boolean, string]>();
|
||||
|
||||
const [patchedCode, matchResult, diff] = React.useMemo(() => {
|
||||
const src = String(fact).replaceAll("\n", "");
|
||||
const src: string = fact.toString().replaceAll("\n", "");
|
||||
|
||||
try {
|
||||
new RegExp(match);
|
||||
|
@ -382,6 +382,7 @@ function PatchHelper() {
|
|||
<Forms.FormTitle className={Margins.top20}>Code</Forms.FormTitle>
|
||||
<CodeBlock lang="js" content={code} />
|
||||
<Button onClick={() => Clipboard.copy(code)}>Copy to Clipboard</Button>
|
||||
<Button className={Margins.top8} onClick={() => Clipboard.copy("```ts\n" + code + "\n```")}>Copy as Codeblock</Button>
|
||||
</>
|
||||
)}
|
||||
</SettingsTab>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { Settings, useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { DeleteIcon, FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
|
||||
|
@ -27,9 +27,9 @@ import { openInviteModal } from "@utils/discord";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { showItemInFolder } from "@utils/native";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { findComponentByFields } from "@webpack";
|
||||
import { findLazy } from "@webpack";
|
||||
import { Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
|
||||
import type { Ref, SyntheticEvent } from "react";
|
||||
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
|
@ -37,14 +37,14 @@ import { AddonCard } from "./AddonCard";
|
|||
import { QuickAction, QuickActionCard } from "./quickActions";
|
||||
import { SettingsTab, wrapTab } from "./shared";
|
||||
|
||||
type FileInputProps = {
|
||||
type FileInput = ComponentType<{
|
||||
ref: Ref<HTMLInputElement>;
|
||||
onChange: (e: SyntheticEvent<HTMLInputElement>) => void;
|
||||
multiple?: boolean;
|
||||
filters?: { name?: string; extensions: string[]; }[];
|
||||
};
|
||||
}>;
|
||||
|
||||
const FileInput = findComponentByFields<FileInputProps>("activateUploadDialogue", "setRef");
|
||||
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef);
|
||||
|
||||
const cl = classNameFactory("vc-settings-theme-");
|
||||
|
||||
|
@ -77,8 +77,16 @@ function Validators({ themeLinks }: { themeLinks: string[]; }) {
|
|||
<Forms.FormTitle className={Margins.top20} tag="h5">Validator</Forms.FormTitle>
|
||||
<Forms.FormText>This section will tell you whether your themes can successfully be loaded</Forms.FormText>
|
||||
<div>
|
||||
{themeLinks.map(link => (
|
||||
<Card style={{
|
||||
{themeLinks.map(rawLink => {
|
||||
const { label, link } = (() => {
|
||||
const match = /^@(light|dark) (.*)/.exec(rawLink);
|
||||
if (!match) return { label: rawLink, link: rawLink };
|
||||
|
||||
const [, mode, link] = match;
|
||||
return { label: `[${mode} mode only] ${link}`, link };
|
||||
})();
|
||||
|
||||
return <Card style={{
|
||||
padding: ".5em",
|
||||
marginBottom: ".5em",
|
||||
marginTop: ".5em"
|
||||
|
@ -86,11 +94,11 @@ function Validators({ themeLinks }: { themeLinks: string[]; }) {
|
|||
<Forms.FormTitle tag="h5" style={{
|
||||
overflowWrap: "break-word"
|
||||
}}>
|
||||
{link}
|
||||
{label}
|
||||
</Forms.FormTitle>
|
||||
<Validator link={link} />
|
||||
</Card>
|
||||
))}
|
||||
</Card>;
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -249,7 +257,7 @@ function ThemesTab() {
|
|||
Icon={PaintbrushIcon}
|
||||
/>
|
||||
|
||||
{Vencord.Plugins.isPluginEnabled("ClientTheme") && (
|
||||
{Settings.plugins.ClientTheme.enabled && (
|
||||
<QuickAction
|
||||
text="Edit ClientTheme"
|
||||
action={() => openPluginModal(Plugins.ClientTheme)}
|
||||
|
@ -296,6 +304,7 @@ function ThemesTab() {
|
|||
<Card className="vc-settings-card vc-text-selectable">
|
||||
<Forms.FormTitle tag="h5">Paste links to css files here</Forms.FormTitle>
|
||||
<Forms.FormText>One link per line</Forms.FormText>
|
||||
<Forms.FormText>You can prefix lines with @light or @dark to toggle them based on your Discord theme</Forms.FormText>
|
||||
<Forms.FormText>Make sure to use direct links to files (raw or github.io)!</Forms.FormText>
|
||||
</Card>
|
||||
|
||||
|
|
|
@ -23,61 +23,35 @@ if (IS_DEV || IS_REPORTER) {
|
|||
var logger = new Logger("Tracer", "#FFD166");
|
||||
}
|
||||
|
||||
export const beginTrace = !(IS_DEV || IS_REPORTER) ? () => { } :
|
||||
const noop = function () { };
|
||||
|
||||
export const beginTrace = !(IS_DEV || IS_REPORTER) ? noop :
|
||||
function beginTrace(name: string, ...args: any[]) {
|
||||
if (name in traces) {
|
||||
if (name in traces)
|
||||
throw new Error(`Trace ${name} already exists!`);
|
||||
}
|
||||
|
||||
traces[name] = [performance.now(), args];
|
||||
};
|
||||
|
||||
export const finishTrace = !(IS_DEV || IS_REPORTER) ? () => 0 :
|
||||
function finishTrace(name: string) {
|
||||
const end = performance.now();
|
||||
export const finishTrace = !(IS_DEV || IS_REPORTER) ? noop : function finishTrace(name: string) {
|
||||
const end = performance.now();
|
||||
|
||||
const [start, args] = traces[name];
|
||||
delete traces[name];
|
||||
const [start, args] = traces[name];
|
||||
delete traces[name];
|
||||
|
||||
const totalTime = end - start;
|
||||
logger.debug(`${name} took ${totalTime}ms`, args);
|
||||
|
||||
return totalTime;
|
||||
};
|
||||
logger.debug(`${name} took ${end - start}ms`, args);
|
||||
};
|
||||
|
||||
type Func = (...args: any[]) => any;
|
||||
type TraceNameMapper<F extends Func> = (...args: Parameters<F>) => string;
|
||||
|
||||
function noopTracerWithResults<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) {
|
||||
return function (this: unknown, ...args: Parameters<F>): [ReturnType<F>, number] {
|
||||
return [f.apply(this, args), 0];
|
||||
};
|
||||
}
|
||||
|
||||
function noopTracer<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) {
|
||||
return f;
|
||||
}
|
||||
|
||||
export const traceFunctionWithResults = !(IS_DEV || IS_REPORTER)
|
||||
? noopTracerWithResults
|
||||
: function traceFunctionWithResults<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): (this: unknown, ...args: Parameters<F>) => [ReturnType<F>, number] {
|
||||
return function (this: unknown, ...args: Parameters<F>) {
|
||||
const traceName = mapper?.(...args) ?? name;
|
||||
|
||||
beginTrace(traceName, ...arguments);
|
||||
try {
|
||||
return [f.apply(this, args), finishTrace(traceName)];
|
||||
} catch (e) {
|
||||
finishTrace(traceName);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
||||
const noopTracer =
|
||||
<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) => f;
|
||||
|
||||
export const traceFunction = !(IS_DEV || IS_REPORTER)
|
||||
? noopTracer
|
||||
: function traceFunction<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): F {
|
||||
return function (this: unknown, ...args: Parameters<F>) {
|
||||
return function (this: any, ...args: Parameters<F>) {
|
||||
const traceName = mapper?.(...args) ?? name;
|
||||
|
||||
beginTrace(traceName, ...arguments);
|
||||
|
|
|
@ -8,11 +8,10 @@ import { Logger } from "@utils/Logger";
|
|||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import * as Webpack from "@webpack";
|
||||
import { wreq } from "@webpack";
|
||||
import { AnyModuleFactory, ModuleFactory } from "webpack";
|
||||
|
||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
||||
|
||||
export async function loadLazyChunks() {
|
||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
||||
|
||||
try {
|
||||
LazyChunkLoaderLogger.log("Loading all chunks...");
|
||||
|
||||
|
@ -26,12 +25,14 @@ export async function loadLazyChunks() {
|
|||
// True if resolved, false otherwise
|
||||
const chunksSearchPromises = [] as Array<() => boolean>;
|
||||
|
||||
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i(?:\.\i)?\.bind\(\i,"?([^)]+?)"?(?:,[^)]+?)?\)\)/g);
|
||||
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g);
|
||||
|
||||
async function searchAndLoadLazyChunks(factoryCode: string) {
|
||||
const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
|
||||
const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>();
|
||||
|
||||
const shouldForceDefer = false;
|
||||
|
||||
await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => {
|
||||
const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => Number(m[1])) : [];
|
||||
|
||||
|
@ -66,14 +67,19 @@ export async function loadLazyChunks() {
|
|||
await Promise.all(
|
||||
Array.from(validChunkGroups)
|
||||
.map(([chunkIds]) =>
|
||||
Promise.all(chunkIds.map(id => wreq.e(id)))
|
||||
Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { })))
|
||||
)
|
||||
);
|
||||
|
||||
// Requires the entry points for all valid chunk groups
|
||||
for (const [, entryPoint] of validChunkGroups) {
|
||||
try {
|
||||
if (wreq.m[entryPoint]) wreq(entryPoint);
|
||||
if (shouldForceDefer) {
|
||||
deferredRequires.add(entryPoint);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wreq.m[entryPoint]) wreq(entryPoint as any);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
@ -101,33 +107,32 @@ export async function loadLazyChunks() {
|
|||
}, 0);
|
||||
}
|
||||
|
||||
function factoryListener(factory: AnyModuleFactory | ModuleFactory) {
|
||||
Webpack.factoryListeners.add(factory => {
|
||||
let isResolved = false;
|
||||
searchAndLoadLazyChunks(String(factory))
|
||||
.then(() => isResolved = true)
|
||||
.catch(() => isResolved = true);
|
||||
searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true);
|
||||
|
||||
chunksSearchPromises.push(() => isResolved);
|
||||
});
|
||||
|
||||
for (const factoryId in wreq.m) {
|
||||
let isResolved = false;
|
||||
searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true);
|
||||
|
||||
chunksSearchPromises.push(() => isResolved);
|
||||
}
|
||||
|
||||
Webpack.factoryListeners.add(factoryListener);
|
||||
for (const factoryId in wreq.m) {
|
||||
factoryListener(wreq.m[factoryId]);
|
||||
}
|
||||
|
||||
await chunksSearchingDone;
|
||||
Webpack.factoryListeners.delete(factoryListener);
|
||||
|
||||
// Require deferred entry points
|
||||
for (const deferredRequire of deferredRequires) {
|
||||
wreq(deferredRequire);
|
||||
wreq!(deferredRequire as any);
|
||||
}
|
||||
|
||||
// All chunks Discord has mapped to asset files, even if they are not used anymore
|
||||
const allChunks = [] as number[];
|
||||
|
||||
// Matches "id" or id:
|
||||
for (const currentMatch of String(wreq.u).matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) {
|
||||
for (const currentMatch of wreq!.u.toString().matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) {
|
||||
const id = currentMatch[1] ?? currentMatch[2];
|
||||
if (id == null) continue;
|
||||
|
||||
|
@ -136,8 +141,7 @@ export async function loadLazyChunks() {
|
|||
|
||||
if (allChunks.length === 0) throw new Error("Failed to get all chunks");
|
||||
|
||||
// Chunks which our regex could not catch to load
|
||||
// It will always contain WebWorker assets, and also currently contains some language packs which are loaded differently
|
||||
// Chunks that are not loaded (not used) by Discord code anymore
|
||||
const chunksLeft = allChunks.filter(id => {
|
||||
return !(validChunks.has(id) || invalidChunks.has(id));
|
||||
});
|
||||
|
@ -147,9 +151,12 @@ export async function loadLazyChunks() {
|
|||
.then(r => r.text())
|
||||
.then(t => t.includes("importScripts("));
|
||||
|
||||
// Loads the chunk. Currently this only happens with the language packs which are loaded differently
|
||||
// Loads and requires a chunk
|
||||
if (!isWorkerAsset) {
|
||||
await wreq.e(id);
|
||||
await wreq.e(id as any);
|
||||
// Technically, the id of the chunk does not match the entry point
|
||||
// But, still try it because we have no way to get the actual entry point
|
||||
if (wreq.m[id]) wreq(id as any);
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
@ -4,39 +4,22 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
|
||||
import * as Webpack from "@webpack";
|
||||
import { addPatch, patches } from "plugins";
|
||||
import { patches } from "plugins";
|
||||
|
||||
import { loadLazyChunks } from "./loadLazyChunks";
|
||||
|
||||
async function runReporter() {
|
||||
const ReporterLogger = new Logger("Reporter");
|
||||
const ReporterLogger = new Logger("Reporter");
|
||||
|
||||
async function runReporter() {
|
||||
try {
|
||||
ReporterLogger.log("Starting test...");
|
||||
|
||||
let loadLazyChunksResolve: (value: void) => void;
|
||||
let loadLazyChunksResolve: (value: void | PromiseLike<void>) => void;
|
||||
const loadLazyChunksDone = new Promise<void>(r => loadLazyChunksResolve = r);
|
||||
|
||||
// The main patch for starting the reporter chunk loading
|
||||
addPatch({
|
||||
find: '"Could not find app-mount"',
|
||||
replacement: {
|
||||
match: /(?<="use strict";)/,
|
||||
replace: "Vencord.Webpack._initReporter();"
|
||||
}
|
||||
}, "Vencord Reporter");
|
||||
|
||||
// @ts-ignore
|
||||
Vencord.Webpack._initReporter = function () {
|
||||
// initReporter is called in the patched entry point of Discord
|
||||
// setImmediate to only start searching for lazy chunks after Discord initialized the app
|
||||
setTimeout(() => loadLazyChunks().then(loadLazyChunksResolve), 0);
|
||||
};
|
||||
|
||||
Webpack.beforeInitListeners.add(() => loadLazyChunks().then((loadLazyChunksResolve)));
|
||||
await loadLazyChunksDone;
|
||||
|
||||
for (const patch of patches) {
|
||||
|
@ -45,158 +28,52 @@ async function runReporter() {
|
|||
}
|
||||
}
|
||||
|
||||
for (const [plugin, moduleId, match, totalTime] of Vencord.WebpackPatcher.patchTimings) {
|
||||
if (totalTime > 3) {
|
||||
new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${totalTime}ms (Module id is ${String(moduleId)}): ${match}`);
|
||||
for (const [searchType, args] of Webpack.lazyWebpackSearchHistory) {
|
||||
let method = searchType;
|
||||
|
||||
if (searchType === "findComponent") method = "find";
|
||||
if (searchType === "findExportedComponent") method = "findByProps";
|
||||
if (searchType === "waitFor" || searchType === "waitForComponent") {
|
||||
if (typeof args[0] === "string") method = "findByProps";
|
||||
else method = "find";
|
||||
}
|
||||
}
|
||||
if (searchType === "waitForStore") method = "findStore";
|
||||
|
||||
await Promise.all(Webpack.webpackSearchHistory.map(async ([searchType, args]) => {
|
||||
args = [...args];
|
||||
|
||||
let result = null as any;
|
||||
let result: any;
|
||||
try {
|
||||
switch (searchType) {
|
||||
case "webpackDependantLazy":
|
||||
case "webpackDependantLazyComponent": {
|
||||
const [factory] = args;
|
||||
result = factory();
|
||||
break;
|
||||
}
|
||||
case "extractAndLoadChunks": {
|
||||
const extractAndLoadChunks = args.shift();
|
||||
if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") {
|
||||
const [factory] = args;
|
||||
result = factory();
|
||||
} else if (method === "extractAndLoadChunks") {
|
||||
const [code, matcher] = args;
|
||||
|
||||
result = await extractAndLoadChunks();
|
||||
if (result === false) {
|
||||
result = null;
|
||||
}
|
||||
result = await Webpack.extractAndLoadChunks(code, matcher);
|
||||
if (result === false) result = null;
|
||||
} else if (method === "mapMangledModule") {
|
||||
const [code, mapper] = args;
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const findResult = args.shift();
|
||||
|
||||
if (findResult != null) {
|
||||
if (findResult.$$vencordCallbackCalled != null && findResult.$$vencordCallbackCalled()) {
|
||||
result = findResult;
|
||||
break;
|
||||
}
|
||||
|
||||
if (findResult[SYM_PROXY_INNER_GET] != null) {
|
||||
result = findResult[SYM_PROXY_INNER_VALUE];
|
||||
break;
|
||||
}
|
||||
|
||||
if (findResult[SYM_LAZY_COMPONENT_INNER] != null) {
|
||||
result = findResult[SYM_LAZY_COMPONENT_INNER]();
|
||||
break;
|
||||
}
|
||||
|
||||
if (searchType === "mapMangledModule") {
|
||||
result = findResult;
|
||||
|
||||
for (const innerMap in result) {
|
||||
if (
|
||||
(result[innerMap][SYM_PROXY_INNER_GET] != null && result[innerMap][SYM_PROXY_INNER_VALUE] == null) ||
|
||||
(result[innerMap][SYM_LAZY_COMPONENT_INNER] != null && result[innerMap][SYM_LAZY_COMPONENT_INNER]() == null)
|
||||
) {
|
||||
throw new Error("Webpack Find Fail");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// This can happen if a `find` was immediately found
|
||||
result = findResult;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
result = Webpack.mapMangledModule(code, mapper);
|
||||
if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail");
|
||||
} else {
|
||||
// @ts-ignore
|
||||
result = Webpack[method](...args);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new Error("Webpack Find Fail");
|
||||
}
|
||||
if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail");
|
||||
} catch (e) {
|
||||
let logMessage = searchType;
|
||||
if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`;
|
||||
else if (method === "extractAndLoadChunks") logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`;
|
||||
else if (method === "mapMangledModule") {
|
||||
const failedMappings = Object.keys(args[1]).filter(key => result?.[key] == null);
|
||||
|
||||
let filterName = "";
|
||||
let parsedArgs = args;
|
||||
|
||||
if (args[0].$$vencordProps != null) {
|
||||
if (["find", "findComponent", "waitFor"].includes(searchType)) {
|
||||
filterName = args[0].$$vencordProps[0];
|
||||
parsedArgs = args[0].$$vencordProps.slice(1);
|
||||
} else {
|
||||
parsedArgs = args[0].$$vencordProps;
|
||||
}
|
||||
}
|
||||
|
||||
function stringifyFilter(code: Webpack.CodeFilterWithSingle) {
|
||||
if (Array.isArray(code)) {
|
||||
return `[${code.map(arg => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")}]`;
|
||||
}
|
||||
|
||||
return code instanceof RegExp ? String(code) : JSON.stringify(code);
|
||||
}
|
||||
|
||||
// if parsedArgs is the same as args, it means vencordProps of the filter was not available (like in normal filter functions),
|
||||
// so log the filter function instead
|
||||
if (
|
||||
parsedArgs === args &&
|
||||
["waitFor", "find", "findComponent", "webpackDependantLazy", "webpackDependantLazyComponent"].includes(searchType)
|
||||
) {
|
||||
let filter = String(parsedArgs[0]);
|
||||
if (filter.length > 150) {
|
||||
filter = filter.slice(0, 147) + "...";
|
||||
}
|
||||
|
||||
logMessage += `(${filter})`;
|
||||
} else if (searchType === "extractAndLoadChunks") {
|
||||
const [code, matcher] = parsedArgs;
|
||||
|
||||
let regexStr: string;
|
||||
if (matcher === Webpack.DefaultExtractAndLoadChunksRegex) {
|
||||
regexStr = "DefaultExtractAndLoadChunksRegex";
|
||||
} else {
|
||||
regexStr = String(matcher);
|
||||
}
|
||||
|
||||
logMessage += `(${stringifyFilter(code)}, ${regexStr})`;
|
||||
} else if (searchType === "mapMangledModule") {
|
||||
const [code, mappers] = parsedArgs;
|
||||
|
||||
const parsedFailedMappers = Object.entries<any>(mappers)
|
||||
.filter(([key]) =>
|
||||
result == null ||
|
||||
(result[key]?.[SYM_PROXY_INNER_GET] != null && result[key][SYM_PROXY_INNER_VALUE] == null) ||
|
||||
(result[key]?.[SYM_LAZY_COMPONENT_INNER] != null && result[key][SYM_LAZY_COMPONENT_INNER]() == null)
|
||||
)
|
||||
.map(([key, filter]) => {
|
||||
let parsedFilter: string;
|
||||
|
||||
if (filter.$$vencordProps != null) {
|
||||
const filterName = filter.$$vencordProps[0];
|
||||
parsedFilter = `${filterName}(${filter.$$vencordProps.slice(1).map((arg: any) => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")})`;
|
||||
} else {
|
||||
parsedFilter = String(filter);
|
||||
if (parsedFilter.length > 150) {
|
||||
parsedFilter = parsedFilter.slice(0, 147) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
return [key, parsedFilter];
|
||||
});
|
||||
|
||||
logMessage += `(${stringifyFilter(code)}, {\n${parsedFailedMappers.map(([key, parsedFilter]) => `\t${key}: ${parsedFilter}`).join(",\n")}\n})`;
|
||||
} else {
|
||||
logMessage += `(${filterName.length ? `${filterName}(` : ""}${parsedArgs.map(stringifyFilter).join(", ")})${filterName.length ? ")" : ""}`;
|
||||
logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`;
|
||||
}
|
||||
else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`;
|
||||
|
||||
ReporterLogger.log("Webpack Find Fail:", logMessage);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
ReporterLogger.log("Finished test");
|
||||
} catch (e) {
|
||||
|
@ -204,5 +81,4 @@ async function runReporter() {
|
|||
}
|
||||
}
|
||||
|
||||
// Run after the Vencord object has been created
|
||||
setTimeout(runReporter, 0);
|
||||
runReporter();
|
||||
|
|
7
src/globals.d.ts
vendored
7
src/globals.d.ts
vendored
|
@ -64,8 +64,13 @@ declare global {
|
|||
export var Vesktop: any;
|
||||
export var VesktopNative: any;
|
||||
|
||||
interface Window extends Record<PropertyKey, any> {
|
||||
interface Window {
|
||||
webpackChunkdiscord_app: {
|
||||
push(chunk: any): any;
|
||||
pop(): any;
|
||||
};
|
||||
_: LoDashStatic;
|
||||
[k: string]: any;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import { onceDefined } from "@shared/onceDefined";
|
||||
import electron, { app, BrowserWindowConstructorOptions, Menu } from "electron";
|
||||
import electron, { app, BrowserWindowConstructorOptions, Menu, nativeTheme } from "electron";
|
||||
import { dirname, join } from "path";
|
||||
|
||||
import { initIpc } from "./ipcMain";
|
||||
|
@ -100,6 +100,19 @@ if (!IS_VANILLA) {
|
|||
|
||||
super(options);
|
||||
initIpc(this);
|
||||
|
||||
// Workaround for https://github.com/electron/electron/issues/43367. Vesktop also has its own workaround
|
||||
// @TODO: Remove this when the issue is fixed
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
this.webContents.on("devtools-opened", () => {
|
||||
if (!nativeTheme.shouldUseDarkColors) return;
|
||||
|
||||
nativeTheme.themeSource = "light";
|
||||
setTimeout(() => {
|
||||
nativeTheme.themeSource = "dark";
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
} else super(options);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[class*="profileBadges"] {
|
||||
flex: none;
|
||||
}
|
|
@ -16,8 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./fixBadgeOverflow.css";
|
||||
|
||||
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
|
||||
import DonateButton from "@components/DonateButton";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
|
@ -79,7 +77,7 @@ export default definePlugin({
|
|||
replace: "...$1.props,$& $1.image??"
|
||||
},
|
||||
{
|
||||
match: /(?<=text:(\i)\.description,.{0,50})children:/,
|
||||
match: /(?<=text:(\i)\.description,.{0,200})children:/,
|
||||
replace: "children:$1.component ? $self.renderBadgeComponent({ ...$1 }) :"
|
||||
},
|
||||
// conditionally override their onClick with badge.onClick if it exists
|
||||
|
|
24
src/plugins/_api/dynamicImageModalApi.ts
Normal file
24
src/plugins/_api/dynamicImageModalApi.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
|
||||
export default definePlugin({
|
||||
name: "DynamicImageModalAPI",
|
||||
authors: [Devs.sadan, Devs.Nuckyz],
|
||||
description: "Allows you to omit either width or height when opening an image modal",
|
||||
patches: [
|
||||
{
|
||||
find: "SCALE_DOWN:",
|
||||
replacement: {
|
||||
match: /!\(null==(\i)\|\|0===\i\|\|null==(\i)\|\|0===\i\)/,
|
||||
replace: (_, width, height) => `!((null == ${width} || 0 === ${width}) && (null == ${height} || 0 === ${height}))`
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
|
@ -31,7 +31,7 @@ export default definePlugin({
|
|||
match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/,
|
||||
replace: "$&vencordProps=$1,"
|
||||
}, {
|
||||
match: /\.Messages\.GUILD_OWNER(?=.+?decorators:(\i)\(\)).+?\1=?\(\)=>.+?children:\[/,
|
||||
match: /#{intl::GUILD_OWNER}(?=.+?decorators:(\i)\(\)).+?\1=?\(\)=>.+?children:\[/,
|
||||
replace: "$&...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)),"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -25,7 +25,7 @@ export default definePlugin({
|
|||
authors: [Devs.Cyn],
|
||||
patches: [
|
||||
{
|
||||
find: ".Messages.REMOVE_ATTACHMENT_BODY",
|
||||
find: "#{intl::REMOVE_ATTACHMENT_BODY}",
|
||||
replacement: {
|
||||
match: /(?<=.container\)?,children:)(\[.+?\])/,
|
||||
replace: "Vencord.Api.MessageAccessories._modifyAccessories($1,this.props)",
|
||||
|
|
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||
{
|
||||
find: '"Message Username"',
|
||||
replacement: {
|
||||
match: /\.Messages\.GUILD_COMMUNICATION_DISABLED_BOTTOM_SHEET_TITLE.+?}\),\i(?=\])/,
|
||||
match: /#{intl::GUILD_COMMUNICATION_DISABLED_BOTTOM_SHEET_TITLE}.+?}\),\i(?=\])/,
|
||||
replace: "$&,...Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0])"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ export default definePlugin({
|
|||
authors: [Devs.Arjix, Devs.hunt, Devs.Ven],
|
||||
patches: [
|
||||
{
|
||||
find: ".Messages.EDIT_TEXTAREA_HELP",
|
||||
find: "#{intl::EDIT_TEXTAREA_HELP}",
|
||||
replacement: {
|
||||
match: /(?<=,channel:\i\}\)\.then\().+?(?=return \i\.content!==this\.props\.message\.content&&\i\((.+?)\))/,
|
||||
replace: (match, args) => "" +
|
||||
|
|
|
@ -24,9 +24,9 @@ export default definePlugin({
|
|||
description: "API to add buttons to message popovers.",
|
||||
authors: [Devs.KingFish, Devs.Ven, Devs.Nuckyz],
|
||||
patches: [{
|
||||
find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
|
||||
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
|
||||
replacement: {
|
||||
match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.Messages\.MESSAGE_ACTION_REPLY.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/,
|
||||
match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.string\(\i\.\i#{intl::MESSAGE_ACTION_REPLY}.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/,
|
||||
replace: "$&,Vencord.Api.MessagePopover._buildPopoverElements($1,$2)"
|
||||
}
|
||||
}],
|
||||
|
|
|
@ -25,16 +25,16 @@ export default definePlugin({
|
|||
description: "Api required for plugins that modify the server list",
|
||||
patches: [
|
||||
{
|
||||
find: "Messages.DISCODO_DISABLED",
|
||||
find: "#{intl::DISCODO_DISABLED}",
|
||||
replacement: {
|
||||
match: /(?<=Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
||||
match: /(?<=#{intl::DISCODO_DISABLED}.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
||||
replace: "[$1].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "Messages.SERVERS,children",
|
||||
find: "#{intl::SERVERS}),children",
|
||||
replacement: {
|
||||
match: /(?<=Messages\.SERVERS,children:).+?default:return null\}\}\)/,
|
||||
match: /(?<=#{intl::SERVERS}\),children:)\i\.map\(\i\)/,
|
||||
replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
import { WebpackRequire } from "webpack";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
disableAnalytics: {
|
||||
|
@ -49,7 +48,7 @@ export default definePlugin({
|
|||
},
|
||||
},
|
||||
{
|
||||
find: ".METRICS,",
|
||||
find: ".METRICS",
|
||||
replacement: [
|
||||
{
|
||||
match: /this\._intervalId=/,
|
||||
|
@ -82,9 +81,9 @@ export default definePlugin({
|
|||
Object.defineProperty(Function.prototype, "g", {
|
||||
configurable: true,
|
||||
|
||||
set(this: WebpackRequire, globalObj: WebpackRequire["g"]) {
|
||||
set(v: any) {
|
||||
Object.defineProperty(this, "g", {
|
||||
value: globalObj,
|
||||
value: v,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
|
@ -93,11 +92,11 @@ export default definePlugin({
|
|||
// Ensure this is most likely the Sentry WebpackInstance.
|
||||
// Function.g is a very generic property and is not uncommon for another WebpackInstance (or even a React component: <g></g>) to include it
|
||||
const { stack } = new Error();
|
||||
if (this.c != null || !stack?.includes("http") || !String(this).includes("exports:{}")) {
|
||||
if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || !String(this).includes("exports:{}") || this.c != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const assetPath = stack.match(/http.+?(?=:\d+?:\d+?$)/m)?.[0];
|
||||
const assetPath = stack?.match(/\/assets\/.+?\.js/)?.[0];
|
||||
if (!assetPath) {
|
||||
return;
|
||||
}
|
||||
|
@ -107,8 +106,7 @@ export default definePlugin({
|
|||
srcRequest.send();
|
||||
|
||||
// Final condition to see if this is the Sentry WebpackInstance
|
||||
// This is matching window.DiscordSentry=, but without `window` to avoid issues on some proxies
|
||||
if (!srcRequest.responseText.includes(".DiscordSentry=")) {
|
||||
if (!srcRequest.responseText.includes("window.DiscordSentry=")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Settings } from "@api/Settings";
|
||||
import BackupAndRestoreTab from "@components/VencordSettings/BackupAndRestoreTab";
|
||||
import CloudTab from "@components/VencordSettings/CloudTab";
|
||||
import PatchHelperTab from "@components/VencordSettings/PatchHelperTab";
|
||||
|
@ -25,35 +25,20 @@ import ThemesTab from "@components/VencordSettings/ThemesTab";
|
|||
import UpdaterTab from "@components/VencordSettings/UpdaterTab";
|
||||
import VencordTab from "@components/VencordSettings/VencordTab";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { i18n, React } from "@webpack/common";
|
||||
import { React } from "@webpack/common";
|
||||
|
||||
import gitHash from "~git-hash";
|
||||
|
||||
type SectionType = "HEADER" | "DIVIDER" | "CUSTOM";
|
||||
type SectionTypes = Record<SectionType, SectionType>;
|
||||
|
||||
const settings = definePluginSettings({
|
||||
settingsLocation: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Where to put the Vencord settings section",
|
||||
options: [
|
||||
{ label: "At the very top", value: "top" },
|
||||
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
|
||||
{ label: "Below the Nitro section", value: "belowNitro" },
|
||||
{ label: "Above Activity Settings", value: "aboveActivity" },
|
||||
{ label: "Below Activity Settings", value: "belowActivity" },
|
||||
{ label: "At the very bottom", value: "bottom" },
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "Settings",
|
||||
description: "Adds Settings UI and debug info",
|
||||
authors: [Devs.Ven, Devs.Megu],
|
||||
required: true,
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
|
@ -73,20 +58,20 @@ export default definePlugin({
|
|||
]
|
||||
},
|
||||
{
|
||||
find: "Messages.ACTIVITY_SETTINGS",
|
||||
find: ".SEARCH_NO_RESULTS&&0===",
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/,
|
||||
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
|
||||
},
|
||||
{
|
||||
match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,30}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
|
||||
match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,60}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
|
||||
replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})`
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL",
|
||||
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
||||
replacement: {
|
||||
match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/,
|
||||
replace: "$2.open($1);return;"
|
||||
|
@ -152,25 +137,30 @@ export default definePlugin({
|
|||
].filter(Boolean);
|
||||
},
|
||||
|
||||
isRightSpot({ header, settingsChilds }: { header?: string; settingsChilds?: string[]; }) {
|
||||
const firstChild = settingsChilds?.[0];
|
||||
isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) {
|
||||
const firstChild = settings?.[0];
|
||||
// lowest two elements... sanity backup
|
||||
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true;
|
||||
|
||||
const { settingsLocation } = settings.store;
|
||||
const { settingsLocation } = Settings.plugins.Settings;
|
||||
|
||||
if (settingsLocation === "bottom") return firstChild === "LOGOUT";
|
||||
if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG";
|
||||
|
||||
if (!header) return;
|
||||
|
||||
const names = {
|
||||
top: i18n.Messages.USER_SETTINGS,
|
||||
aboveNitro: i18n.Messages.BILLING_SETTINGS,
|
||||
belowNitro: i18n.Messages.APP_SETTINGS,
|
||||
aboveActivity: i18n.Messages.ACTIVITY_SETTINGS
|
||||
};
|
||||
return header === names[settingsLocation];
|
||||
try {
|
||||
const names = {
|
||||
top: getIntlMessage("USER_SETTINGS"),
|
||||
aboveNitro: getIntlMessage("BILLING_SETTINGS"),
|
||||
belowNitro: getIntlMessage("APP_SETTINGS"),
|
||||
aboveActivity: getIntlMessage("ACTIVITY_SETTINGS")
|
||||
};
|
||||
|
||||
return header === names[settingsLocation];
|
||||
} catch {
|
||||
return firstChild === "PREMIUM";
|
||||
}
|
||||
},
|
||||
|
||||
patchedSettings: new WeakSet(),
|
||||
|
@ -197,8 +187,23 @@ export default definePlugin({
|
|||
};
|
||||
},
|
||||
|
||||
options: {
|
||||
settingsLocation: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Where to put the Vencord settings section",
|
||||
options: [
|
||||
{ label: "At the very top", value: "top" },
|
||||
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
|
||||
{ label: "Below the Nitro section", value: "belowNitro" },
|
||||
{ label: "Above Activity Settings", value: "aboveActivity" },
|
||||
{ label: "Below Activity Settings", value: "belowActivity" },
|
||||
{ label: "At the very bottom", value: "bottom" },
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
get electronVersion() {
|
||||
return VencordNative.native.getVersions().electron || window.armcord?.electron || null;
|
||||
return VencordNative.native.getVersions().electron || window.legcord?.electron || null;
|
||||
},
|
||||
|
||||
get chromiumVersion() {
|
||||
|
|
|
@ -59,7 +59,7 @@ const TrustedRolesIds = [
|
|||
|
||||
const AsyncFunction = async function () { }.constructor;
|
||||
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||
|
||||
async function forceUpdate() {
|
||||
const outdated = await checkForUpdates();
|
||||
|
@ -77,7 +77,7 @@ async function generateDebugInfoMessage() {
|
|||
const client = (() => {
|
||||
if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`;
|
||||
if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`;
|
||||
if ("armcord" in window) return `ArmCord v${window.armcord.version}`;
|
||||
if ("legcord" in window) return `Legcord v${window.legcord.version}`;
|
||||
|
||||
// @ts-expect-error
|
||||
const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web";
|
||||
|
@ -142,15 +142,15 @@ export default definePlugin({
|
|||
required: true,
|
||||
description: "Helps us provide support to you",
|
||||
authors: [Devs.Ven],
|
||||
dependencies: ["CommandsAPI", "UserSettingsAPI", "MessageAccessoriesAPI"],
|
||||
dependencies: ["UserSettingsAPI", "MessageAccessoriesAPI"],
|
||||
|
||||
settings,
|
||||
|
||||
patches: [{
|
||||
find: ".BEGINNING_DM.format",
|
||||
find: "#{intl::BEGINNING_DM}",
|
||||
replacement: {
|
||||
match: /BEGINNING_DM\.format\(\{.+?\}\),(?=.{0,100}userId:(\i\.getRecipientId\(\)))/,
|
||||
replace: "$& $self.ContributorDmWarningCard({ userId: $1 }),"
|
||||
match: /#{intl::BEGINNING_DM},{.+?}\),(?=.{0,300}(\i)\.isMultiUserDM)/,
|
||||
replace: "$& $self.renderContributorDmWarningCard({ channel: $1 }),"
|
||||
}
|
||||
}],
|
||||
|
||||
|
@ -235,7 +235,8 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
|
||||
ContributorDmWarningCard: ErrorBoundary.wrap(({ userId }) => {
|
||||
renderContributorDmWarningCard: ErrorBoundary.wrap(({ channel }) => {
|
||||
const userId = channel.getRecipientId();
|
||||
if (!isPluginDev(userId)) return null;
|
||||
if (RelationshipStore.isFriend(userId) || isPluginDev(UserStore.getCurrentUser()?.id)) return null;
|
||||
|
||||
|
|
7
src/plugins/accountPanelServerProfile/README.md
Normal file
7
src/plugins/accountPanelServerProfile/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# AccountPanelServerProfile
|
||||
|
||||
Right click your account panel in the bottom left to view your profile in the current server
|
||||
|
||||
![](https://github.com/user-attachments/assets/3228497d-488f-479c-93d2-a32ccdb08f0f)
|
||||
|
||||
![](https://github.com/user-attachments/assets/6fc45363-d95f-4810-812f-2f9fb28b41b5)
|
134
src/plugins/accountPanelServerProfile/index.tsx
Normal file
134
src/plugins/accountPanelServerProfile/index.tsx
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentChannel } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
interface UserProfileProps {
|
||||
popoutProps: Record<string, any>;
|
||||
currentUser: User;
|
||||
originalPopout: () => React.ReactNode;
|
||||
}
|
||||
|
||||
const UserProfile = findComponentByCodeLazy("UserProfilePopoutWrapper: user cannot be undefined");
|
||||
const styles = findByPropsLazy("accountProfilePopoutWrapper");
|
||||
|
||||
let openAlternatePopout = false;
|
||||
let accountPanelRef: React.MutableRefObject<Record<PropertyKey, any> | null> = { current: null };
|
||||
|
||||
const AccountPanelContextMenu = ErrorBoundary.wrap(() => {
|
||||
const { prioritizeServerProfile } = settings.use(["prioritizeServerProfile"]);
|
||||
|
||||
return (
|
||||
<Menu.Menu
|
||||
navId="vc-ap-server-profile"
|
||||
onClose={ContextMenuApi.closeContextMenu}
|
||||
>
|
||||
<Menu.MenuItem
|
||||
id="vc-ap-view-alternate-popout"
|
||||
label={prioritizeServerProfile ? "View Account Profile" : "View Server Profile"}
|
||||
disabled={getCurrentChannel()?.getGuildId() == null}
|
||||
action={e => {
|
||||
openAlternatePopout = true;
|
||||
accountPanelRef.current?.props.onMouseDown();
|
||||
accountPanelRef.current?.props.onClick(e);
|
||||
}}
|
||||
/>
|
||||
<Menu.MenuCheckboxItem
|
||||
id="vc-ap-prioritize-server-profile"
|
||||
label="Prioritize Server Profile"
|
||||
checked={prioritizeServerProfile}
|
||||
action={() => settings.store.prioritizeServerProfile = !prioritizeServerProfile}
|
||||
/>
|
||||
</Menu.Menu>
|
||||
);
|
||||
}, { noop: true });
|
||||
|
||||
const settings = definePluginSettings({
|
||||
prioritizeServerProfile: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Prioritize Server Profile when left clicking your account panel",
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "AccountPanelServerProfile",
|
||||
description: "Right click your account panel in the bottom left to view your profile in the current server",
|
||||
authors: [Devs.Nuckyz, Devs.relitrix],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=\.SIZE_32\)}\);)/,
|
||||
replace: "$self.useAccountPanelRef();"
|
||||
},
|
||||
{
|
||||
match: /(\.AVATAR,children:.+?renderPopout:(\i)=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/,
|
||||
replace: (_, rest, popoutProps, originalPopout, currentUser) => `${rest}$self.UserProfile({popoutProps:${popoutProps},currentUser:${currentUser},originalPopout:()=>{${originalPopout}}})`
|
||||
},
|
||||
{
|
||||
match: /\.AVATAR,children:.+?(?=renderPopout:)/,
|
||||
replace: "$&onRequestClose:$self.onPopoutClose,"
|
||||
},
|
||||
{
|
||||
match: /(?<=.avatarWrapper,)/,
|
||||
replace: "ref:$self.accountPanelRef,onContextMenu:$self.openAccountPanelContextMenu,"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
get accountPanelRef() {
|
||||
return accountPanelRef;
|
||||
},
|
||||
|
||||
useAccountPanelRef() {
|
||||
useEffect(() => () => {
|
||||
accountPanelRef.current = null;
|
||||
}, []);
|
||||
|
||||
return (accountPanelRef = useRef(null));
|
||||
},
|
||||
|
||||
openAccountPanelContextMenu(event: React.UIEvent) {
|
||||
ContextMenuApi.openContextMenu(event, AccountPanelContextMenu);
|
||||
},
|
||||
|
||||
onPopoutClose() {
|
||||
openAlternatePopout = false;
|
||||
},
|
||||
|
||||
UserProfile: ErrorBoundary.wrap(({ popoutProps, currentUser, originalPopout }: UserProfileProps) => {
|
||||
if (
|
||||
(settings.store.prioritizeServerProfile && openAlternatePopout) ||
|
||||
(!settings.store.prioritizeServerProfile && !openAlternatePopout)
|
||||
) {
|
||||
return originalPopout();
|
||||
}
|
||||
|
||||
const currentChannel = getCurrentChannel();
|
||||
if (currentChannel?.getGuildId() == null) {
|
||||
return originalPopout();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.accountProfilePopoutWrapper}>
|
||||
<UserProfile {...popoutProps} userId={currentUser.id} guildId={currentChannel.getGuildId()} channelId={currentChannel.id} />
|
||||
</div>
|
||||
);
|
||||
}, { noop: true })
|
||||
});
|
|
@ -41,7 +41,7 @@ export default definePlugin({
|
|||
},
|
||||
{
|
||||
// Status emojis
|
||||
find: ".Messages.GUILD_OWNER,",
|
||||
find: "#{intl::GUILD_OWNER}",
|
||||
replacement: {
|
||||
match: /(?<=\.activityEmoji,.+?animate:)\i/,
|
||||
replace: "!0"
|
||||
|
|
3
src/plugins/alwaysExpandRoles/README.md
Normal file
3
src/plugins/alwaysExpandRoles/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Always Expand Roles
|
||||
|
||||
Always expands the role list in profile popouts
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -16,20 +16,22 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
migratePluginSettings("AlwaysExpandRoles", "ShowAllRoles");
|
||||
export default definePlugin({
|
||||
name: "TimeBarAllActivities",
|
||||
description: "Adds the Spotify time bar to all activities if they have start and end timestamps",
|
||||
authors: [Devs.fawn],
|
||||
name: "AlwaysExpandRoles",
|
||||
description: "Always expands the role list in profile popouts",
|
||||
authors: [Devs.surgedevs],
|
||||
patches: [
|
||||
{
|
||||
find: "}renderTimeBar(",
|
||||
find: 'action:"EXPAND_ROLES"',
|
||||
replacement: {
|
||||
match: /renderTimeBar\((.{1,3})\){.{0,50}?let/,
|
||||
replace: "renderTimeBar($1){let"
|
||||
match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/,
|
||||
replace: (_, rest, setExpandedRoles) => `${rest}!0)`
|
||||
}
|
||||
}
|
||||
],
|
||||
]
|
||||
});
|
|
@ -21,12 +21,12 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByProps, findComponentByCode } from "@webpack";
|
||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||
|
||||
type AnonUpload = Upload & { anonymise?: boolean; };
|
||||
|
||||
const ActionBarIcon = findComponentByCode(".actionBarIcon)");
|
||||
const UploadDraft = findByProps("popFirstFile", "update");
|
||||
const ActionBarIcon = findByCodeLazy(".actionBarIcon)");
|
||||
const UploadDraft = findByPropsLazy("popFirstFile", "update");
|
||||
|
||||
const enum Methods {
|
||||
Random,
|
||||
|
@ -71,7 +71,7 @@ export default definePlugin({
|
|||
description: "Anonymise uploaded file names",
|
||||
patches: [
|
||||
{
|
||||
find: "instantBatchUpload:function",
|
||||
find: "instantBatchUpload:",
|
||||
replacement: {
|
||||
match: /uploadFiles:(\i),/,
|
||||
replace:
|
||||
|
@ -86,9 +86,9 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: ".Messages.ATTACHMENT_UTILITIES_SPOILER",
|
||||
find: "#{intl::ATTACHMENT_UTILITIES_SPOILER}",
|
||||
replacement: {
|
||||
match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}\i\.\i\.Messages\.ATTACHMENT_UTILITIES_SPOILER)/,
|
||||
match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}#{intl::ATTACHMENT_UTILITIES_SPOILER})/,
|
||||
replace: "arguments[0].canEdit!==false?$self.renderIcon(arguments[0]):null,"
|
||||
},
|
||||
},
|
||||
|
|
|
@ -24,7 +24,7 @@ interface ActivityButton {
|
|||
}
|
||||
|
||||
interface Activity {
|
||||
state: string;
|
||||
state?: string;
|
||||
details?: string;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
|
@ -52,8 +52,8 @@ const enum ActivityFlag {
|
|||
|
||||
export interface TrackData {
|
||||
name: string;
|
||||
album: string;
|
||||
artist: string;
|
||||
album?: string;
|
||||
artist?: string;
|
||||
|
||||
appleMusicLink?: string;
|
||||
songLink?: string;
|
||||
|
@ -61,8 +61,8 @@ export interface TrackData {
|
|||
albumArtwork?: string;
|
||||
artistArtwork?: string;
|
||||
|
||||
playerPosition: number;
|
||||
duration: number;
|
||||
playerPosition?: number;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
const enum AssetImageType {
|
||||
|
@ -120,7 +120,7 @@ const settings = definePluginSettings({
|
|||
stateString: {
|
||||
type: OptionType.STRING,
|
||||
description: "Activity state format string",
|
||||
default: "{artist}"
|
||||
default: "{artist} · {album}"
|
||||
},
|
||||
largeImageType: {
|
||||
type: OptionType.SELECT,
|
||||
|
@ -155,8 +155,8 @@ const settings = definePluginSettings({
|
|||
function customFormat(formatStr: string, data: TrackData) {
|
||||
return formatStr
|
||||
.replaceAll("{name}", data.name)
|
||||
.replaceAll("{album}", data.album)
|
||||
.replaceAll("{artist}", data.artist);
|
||||
.replaceAll("{album}", data.album ?? "")
|
||||
.replaceAll("{artist}", data.artist ?? "");
|
||||
}
|
||||
|
||||
function getImageAsset(type: AssetImageType, data: TrackData) {
|
||||
|
@ -212,14 +212,16 @@ export default definePlugin({
|
|||
|
||||
const assets: ActivityAssets = {};
|
||||
|
||||
const isRadio = Number.isNaN(trackData.duration) && (trackData.playerPosition === 0);
|
||||
|
||||
if (settings.store.largeImageType !== AssetImageType.Disabled) {
|
||||
assets.large_image = largeImageAsset;
|
||||
assets.large_text = customFormat(settings.store.largeTextString, trackData);
|
||||
if (!isRadio) assets.large_text = customFormat(settings.store.largeTextString, trackData);
|
||||
}
|
||||
|
||||
if (settings.store.smallImageType !== AssetImageType.Disabled) {
|
||||
assets.small_image = smallImageAsset;
|
||||
assets.small_text = customFormat(settings.store.smallTextString, trackData);
|
||||
if (!isRadio) assets.small_text = customFormat(settings.store.smallTextString, trackData);
|
||||
}
|
||||
|
||||
const buttons: ActivityButton[] = [];
|
||||
|
@ -243,17 +245,17 @@ export default definePlugin({
|
|||
|
||||
name: customFormat(settings.store.nameString, trackData),
|
||||
details: customFormat(settings.store.detailsString, trackData),
|
||||
state: customFormat(settings.store.stateString, trackData),
|
||||
state: isRadio ? undefined : customFormat(settings.store.stateString, trackData),
|
||||
|
||||
timestamps: (settings.store.enableTimestamps ? {
|
||||
timestamps: (trackData.playerPosition && trackData.duration && settings.store.enableTimestamps) ? {
|
||||
start: Date.now() - (trackData.playerPosition * 1000),
|
||||
end: Date.now() - (trackData.playerPosition * 1000) + (trackData.duration * 1000),
|
||||
} : undefined),
|
||||
} : undefined,
|
||||
|
||||
assets,
|
||||
|
||||
buttons: buttons.length ? buttons.map(v => v.label) : undefined,
|
||||
metadata: { button_urls: buttons.map(v => v.url) || undefined, },
|
||||
buttons: !isRadio && buttons.length ? buttons.map(v => v.label) : undefined,
|
||||
metadata: !isRadio && buttons.length ? { button_urls: buttons.map(v => v.url) } : undefined,
|
||||
|
||||
type: settings.store.activityType,
|
||||
flags: ActivityFlag.INSTANCE,
|
||||
|
|
|
@ -11,37 +11,11 @@ import type { TrackData } from ".";
|
|||
|
||||
const exec = promisify(execFile);
|
||||
|
||||
// function exec(file: string, args: string[] = []) {
|
||||
// return new Promise<{ code: number | null, stdout: string | null, stderr: string | null; }>((resolve, reject) => {
|
||||
// const process = spawn(file, args, { stdio: [null, "pipe", "pipe"] });
|
||||
|
||||
// let stdout: string | null = null;
|
||||
// process.stdout.on("data", (chunk: string) => { stdout ??= ""; stdout += chunk; });
|
||||
// let stderr: string | null = null;
|
||||
// process.stderr.on("data", (chunk: string) => { stdout ??= ""; stderr += chunk; });
|
||||
|
||||
// process.on("exit", code => { resolve({ code, stdout, stderr }); });
|
||||
// process.on("error", err => reject(err));
|
||||
// });
|
||||
// }
|
||||
|
||||
async function applescript(cmds: string[]) {
|
||||
const { stdout } = await exec("osascript", cmds.map(c => ["-e", c]).flat());
|
||||
return stdout;
|
||||
}
|
||||
|
||||
function makeSearchUrl(type: string, query: string) {
|
||||
const url = new URL("https://tools.applemediaservices.com/api/apple-media/music/US/search.json");
|
||||
url.searchParams.set("types", type);
|
||||
url.searchParams.set("limit", "1");
|
||||
url.searchParams.set("term", query);
|
||||
return url;
|
||||
}
|
||||
|
||||
const requestOptions: RequestInit = {
|
||||
headers: { "user-agent": "Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0" },
|
||||
};
|
||||
|
||||
interface RemoteData {
|
||||
appleMusicLink?: string,
|
||||
songLink?: string,
|
||||
|
@ -51,6 +25,24 @@ interface RemoteData {
|
|||
|
||||
let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null;
|
||||
|
||||
const APPLE_MUSIC_BUNDLE_REGEX = /<script type="module" crossorigin src="([a-zA-Z0-9.\-/]+)"><\/script>/;
|
||||
const APPLE_MUSIC_TOKEN_REGEX = /\w+="([A-Za-z0-9-_]*\.[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*)",\w+="x-apple-jingle-correlation-key"/;
|
||||
|
||||
let cachedToken: string | undefined = undefined;
|
||||
|
||||
const getToken = async () => {
|
||||
if (cachedToken) return cachedToken;
|
||||
|
||||
const html = await fetch("https://music.apple.com/").then(r => r.text());
|
||||
const bundleUrl = new URL(html.match(APPLE_MUSIC_BUNDLE_REGEX)![1], "https://music.apple.com/");
|
||||
|
||||
const bundle = await fetch(bundleUrl).then(r => r.text());
|
||||
const token = bundle.match(APPLE_MUSIC_TOKEN_REGEX)![1];
|
||||
|
||||
cachedToken = token;
|
||||
return token;
|
||||
};
|
||||
|
||||
async function fetchRemoteData({ id, name, artist, album }: { id: string, name: string, artist: string, album: string; }) {
|
||||
if (id === cachedRemoteData?.id) {
|
||||
if ("data" in cachedRemoteData) return cachedRemoteData.data;
|
||||
|
@ -58,21 +50,39 @@ async function fetchRemoteData({ id, name, artist, album }: { id: string, name:
|
|||
}
|
||||
|
||||
try {
|
||||
const [songData, artistData] = await Promise.all([
|
||||
fetch(makeSearchUrl("songs", artist + " " + album + " " + name), requestOptions).then(r => r.json()),
|
||||
fetch(makeSearchUrl("artists", artist.split(/ *[,&] */)[0]), requestOptions).then(r => r.json())
|
||||
]);
|
||||
const dataUrl = new URL("https://amp-api-edge.music.apple.com/v1/catalog/us/search");
|
||||
dataUrl.searchParams.set("platform", "web");
|
||||
dataUrl.searchParams.set("l", "en-US");
|
||||
dataUrl.searchParams.set("limit", "1");
|
||||
dataUrl.searchParams.set("with", "serverBubbles");
|
||||
dataUrl.searchParams.set("types", "songs");
|
||||
dataUrl.searchParams.set("term", `${name} ${artist} ${album}`);
|
||||
dataUrl.searchParams.set("include[songs]", "artists");
|
||||
|
||||
const appleMusicLink = songData?.songs?.data[0]?.attributes.url;
|
||||
const songLink = songData?.songs?.data[0]?.id ? `https://song.link/i/${songData?.songs?.data[0]?.id}` : undefined;
|
||||
const token = await getToken();
|
||||
|
||||
const albumArtwork = songData?.songs?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512");
|
||||
const artistArtwork = artistData?.artists?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512");
|
||||
const songData = await fetch(dataUrl, {
|
||||
headers: {
|
||||
"accept": "*/*",
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
"authorization": `Bearer ${token}`,
|
||||
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
|
||||
"origin": "https://music.apple.com",
|
||||
},
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => data.results.song.data[0]);
|
||||
|
||||
cachedRemoteData = {
|
||||
id,
|
||||
data: { appleMusicLink, songLink, albumArtwork, artistArtwork }
|
||||
data: {
|
||||
appleMusicLink: songData.attributes.url,
|
||||
songLink: `https://song.link/i/${songData.id}`,
|
||||
albumArtwork: songData.attributes.artwork.url.replace("{w}x{h}", "512x512"),
|
||||
artistArtwork: songData.relationships.artists.data[0].attributes.artwork.url.replace("{w}x{h}", "512x512"),
|
||||
}
|
||||
};
|
||||
|
||||
return cachedRemoteData.data;
|
||||
} catch (e) {
|
||||
console.error("[AppleMusicRichPresence] Failed to fetch remote data:", e);
|
||||
|
|
|
@ -20,10 +20,10 @@ import { popNotice, showNotice } from "@api/Notices";
|
|||
import { Link } from "@components/Link";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { ReporterTestable } from "@utils/types";
|
||||
import { findByCode } from "@webpack";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common";
|
||||
|
||||
const fetchApplicationsRPC = findByCode("APPLICATION_RPC(", "Client ID");
|
||||
const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID");
|
||||
|
||||
async function lookupAsset(applicationId: string, key: string): Promise<string> {
|
||||
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
||||
|
@ -73,8 +73,8 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
async start() {
|
||||
// ArmCord comes with its own arRPC implementation, so this plugin just confuses users
|
||||
if ("armcord" in window) return;
|
||||
// Legcord comes with its own arRPC implementation, so this plugin just confuses users
|
||||
if ("legcord" in window) return;
|
||||
|
||||
if (ws) ws.close();
|
||||
ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket
|
||||
|
|
|
@ -36,7 +36,7 @@ export default definePlugin({
|
|||
settings,
|
||||
patches: [
|
||||
{
|
||||
find: "BAN_CONFIRM_TITLE.",
|
||||
find: "#{intl::BAN_CONFIRM_TITLE}",
|
||||
replacement: {
|
||||
match: /src:\i\("?\d+"?\)/g,
|
||||
replace: "src:$self.source"
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { findByProps, findComponentByCode, findStore } from "@webpack";
|
||||
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { useStateFromStores } from "@webpack/common";
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
import { ExpandedGuildFolderStore, settings } from ".";
|
||||
|
||||
const ChannelRTCStore = findStore("ChannelRTCStore");
|
||||
const Animations = findByProps("a", "animated", "useTransition");
|
||||
const GuildsBar = findComponentByCode('("guildsnav")');
|
||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
||||
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
||||
const GuildsBar = findComponentByCodeLazy('("guildsnav")');
|
||||
|
||||
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
||||
|
|
11
src/plugins/betterFolders/README.md
Normal file
11
src/plugins/betterFolders/README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Better Folders
|
||||
|
||||
Better Folders offers a variety of options to improve your folder experience
|
||||
|
||||
Always show the folder icon, regardless of if the folder is open or not
|
||||
|
||||
Only have one folder open at a time
|
||||
|
||||
Open folders in a sidebar:
|
||||
|
||||
![A folder open in a separate sidebar](https://github.com/user-attachments/assets/432d3146-8091-4bae-9c1e-c19046c72947)
|
|
@ -18,9 +18,10 @@
|
|||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { find, findByProps, findStore } from "@webpack";
|
||||
import { FluxDispatcher, i18n, useMemo } from "@webpack/common";
|
||||
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
||||
import { FluxDispatcher, useMemo } from "@webpack/common";
|
||||
|
||||
import FolderSideBar from "./FolderSideBar";
|
||||
|
||||
|
@ -30,10 +31,10 @@ enum FolderIconDisplay {
|
|||
MoreThanOneFolderExpanded
|
||||
}
|
||||
|
||||
const GuildsTree = find(m => m.prototype?.moveNextTo);
|
||||
const SortedGuildStore = findStore("SortedGuildStore");
|
||||
export const ExpandedGuildFolderStore = findStore("ExpandedGuildFolderStore");
|
||||
const FolderUtils = findByProps("move", "toggleGuildFolderExpand");
|
||||
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
||||
const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
||||
const GuildsTree = findLazy(m => m.prototype?.moveNextTo);
|
||||
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
||||
|
||||
let lastGuildId = null as string | null;
|
||||
let dispatchingFoldersClose = false;
|
||||
|
@ -118,22 +119,22 @@ export default definePlugin({
|
|||
// If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders
|
||||
{
|
||||
match: /\[(\i)\]=(\(0,\i\.\i\).{0,40}getGuildsTree\(\).+?}\))(?=,)/,
|
||||
replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0].isBetterFolders,betterFoldersOriginalTree,arguments[0].betterFoldersExpandedIds)`
|
||||
replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0]?.isBetterFolders,betterFoldersOriginalTree,arguments[0]?.betterFoldersExpandedIds)`
|
||||
},
|
||||
// If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children
|
||||
{
|
||||
match: /lastTargetNode:\i\[\i\.length-1\].+?Fragment.+?\]}\)\]/,
|
||||
replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0].isBetterFolders))"
|
||||
replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0]?.isBetterFolders))"
|
||||
},
|
||||
// If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children
|
||||
{
|
||||
match: /unreadMentionsIndicatorBottom,.+?}\)\]/,
|
||||
replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0].isBetterFolders))"
|
||||
replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0]?.isBetterFolders))"
|
||||
},
|
||||
// Export the isBetterFolders variable to the folders component
|
||||
{
|
||||
match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,)/,
|
||||
replace: 'isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
|
||||
match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/,
|
||||
replace: '$&isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -167,31 +168,31 @@ export default definePlugin({
|
|||
{
|
||||
predicate: () => settings.store.keepIcons,
|
||||
match: /(?<=let{folderNode:\i,setNodeRef:\i,.+?expanded:(\i),.+?;)(?=let)/,
|
||||
replace: (_, isExpanded) => `${isExpanded}=!!arguments[0].isBetterFolders&&${isExpanded};`
|
||||
replace: (_, isExpanded) => `${isExpanded}=!!arguments[0]?.isBetterFolders&&${isExpanded};`
|
||||
},
|
||||
// Disable expanding and collapsing folders transition in the normal GuildsBar sidebar
|
||||
{
|
||||
predicate: () => !settings.store.keepIcons,
|
||||
match: /(?<=\.Messages\.SERVER_FOLDER_PLACEHOLDER.+?useTransition\)\()/,
|
||||
replace: "!!arguments[0].isBetterFolders&&"
|
||||
match: /(?<=#{intl::SERVER_FOLDER_PLACEHOLDER}.+?useTransition\)\()/,
|
||||
replace: "$self.shouldShowTransition(arguments[0])&&"
|
||||
},
|
||||
// If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded
|
||||
{
|
||||
predicate: () => !settings.store.keepIcons,
|
||||
match: /expandedFolderBackground,.+?,(?=\i\(\(\i,\i,\i\)=>{let{key.{0,45}ul)(?<=selected:\i,expanded:(\i),.+?)/,
|
||||
replace: (m, isExpanded) => `${m}!arguments[0].isBetterFolders&&${isExpanded}?null:`
|
||||
replace: (m, isExpanded) => `${m}$self.shouldRenderContents(arguments[0],${isExpanded})?null:`
|
||||
},
|
||||
{
|
||||
// Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar
|
||||
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
||||
match: /(?<=\.wrapper,children:\[)/,
|
||||
replace: "$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)&&"
|
||||
replace: "$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)&&"
|
||||
},
|
||||
{
|
||||
// Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar
|
||||
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
||||
match: /(?<=\.expandedFolderBackground.+?}\),)(?=\i,)/,
|
||||
replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)?null:"
|
||||
replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)?null:"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -200,12 +201,12 @@ export default definePlugin({
|
|||
predicate: () => settings.store.sidebar,
|
||||
replacement: {
|
||||
// Render the Better Folders sidebar
|
||||
match: /(?<=({className:\i\.guilds,themeOverride:\i})\))/,
|
||||
replace: ",$self.FolderSideBar($1)"
|
||||
match: /(container.{0,50}({className:\i\.guilds,themeOverride:\i})\))/,
|
||||
replace: "$1,$self.FolderSideBar({...$2})"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: ".Messages.DISCODO_DISABLED",
|
||||
find: "#{intl::DISCODO_DISABLED}",
|
||||
predicate: () => settings.store.closeAllHomeButton,
|
||||
replacement: {
|
||||
// Close all folders when clicking the home button
|
||||
|
@ -274,12 +275,16 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
|
||||
return child => {
|
||||
if (isBetterFolders) {
|
||||
return child?.props?.["aria-label"] === i18n.Messages.SERVERS;
|
||||
}
|
||||
try {
|
||||
return child => {
|
||||
if (isBetterFolders) {
|
||||
return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
} catch {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
|
||||
|
@ -306,7 +311,20 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
|
||||
FolderSideBar: guildsBarProps => <FolderSideBar {...guildsBarProps} />,
|
||||
shouldShowTransition(props: any) {
|
||||
// Pending guilds
|
||||
if (props?.folderNode?.id === 1) return true;
|
||||
|
||||
closeFolders
|
||||
return !!props?.isBetterFolders;
|
||||
},
|
||||
|
||||
shouldRenderContents(props: any, isExpanded: boolean) {
|
||||
// Pending guilds
|
||||
if (props?.folderNode?.id === 1) return false;
|
||||
|
||||
return !props?.isBetterFolders && isExpanded;
|
||||
},
|
||||
|
||||
FolderSideBar,
|
||||
closeFolders,
|
||||
});
|
||||
|
|
|
@ -34,9 +34,9 @@ export default definePlugin({
|
|||
},
|
||||
},
|
||||
{
|
||||
find: ".Messages.GIF,",
|
||||
find: "#{intl::GIF}",
|
||||
replacement: {
|
||||
match: /alt:(\i)=(\i\.\i\.Messages\.GIF)(?=,[^}]*\}=(\i))/,
|
||||
match: /alt:(\i)=(\i\.\i\.string\(\i\.\i#{intl::GIF}\))(?=,[^}]*\}=(\i))/,
|
||||
replace:
|
||||
// rename prop so we can always use default value
|
||||
"alt_$$:$1=$self.altify($3)||$2",
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
@ -31,7 +31,7 @@ const settings = definePluginSettings({
|
|||
noSpellCheck: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Disable spellcheck in notes",
|
||||
disabled: () => settings.store.hide,
|
||||
disabled: () => Settings.plugins.BetterNotesBox.hide,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
@ -63,9 +63,9 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: "Messages.NOTE_PLACEHOLDER",
|
||||
find: "#{intl::NOTE_PLACEHOLDER}",
|
||||
replacement: {
|
||||
match: /\.NOTE_PLACEHOLDER,/,
|
||||
match: /#{intl::NOTE_PLACEHOLDER}\),/,
|
||||
replace: "$&spellCheck:!$self.noSpellCheck,"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@ import { ImageIcon } from "@components/Icons";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentGuild, openImageModal } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByProps } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common";
|
||||
|
||||
const GuildSettingsActions = findByProps("open", "selectRole", "updateGuild");
|
||||
const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild");
|
||||
|
||||
const DeveloperMode = getUserSettingLazy("appearance", "developerMode");
|
||||
const DeveloperMode = getUserSettingLazy("appearance", "developerMode")!;
|
||||
|
||||
function PencilIcon() {
|
||||
return (
|
||||
|
@ -99,7 +99,11 @@ export default definePlugin({
|
|||
id="vc-view-role-icon"
|
||||
label="View Role Icon"
|
||||
action={() => {
|
||||
openImageModal(`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${role.id}/${role.icon}.${settings.store.roleIconFileFormat}`);
|
||||
openImageModal({
|
||||
url: `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${role.id}/${role.icon}.${settings.store.roleIconFileFormat}`,
|
||||
height: 128,
|
||||
width: 128
|
||||
});
|
||||
}}
|
||||
icon={ImageIcon}
|
||||
/>
|
||||
|
|
|
@ -16,32 +16,16 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Settings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Clipboard, Toasts } from "@webpack/common";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
bothStyles: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show both role dot and coloured names",
|
||||
restartNeeded: true,
|
||||
default: false,
|
||||
},
|
||||
copyRoleColorInProfilePopout: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Allow click on role dot in profile popout to copy role color",
|
||||
restartNeeded: true,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "BetterRoleDot",
|
||||
authors: [Devs.Ven, Devs.AutumnVN],
|
||||
description:
|
||||
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
|
@ -55,7 +39,7 @@ export default definePlugin({
|
|||
find: '"dot"===',
|
||||
all: true,
|
||||
noWarn: true,
|
||||
predicate: () => settings.store.bothStyles,
|
||||
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
|
||||
replacement: {
|
||||
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
||||
replace: "true",
|
||||
|
@ -63,9 +47,9 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
{
|
||||
find: ".ADD_ROLE_A11Y_LABEL",
|
||||
find: "#{intl::ADD_ROLE_A11Y_LABEL}",
|
||||
all: true,
|
||||
predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
|
||||
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||
noWarn: true,
|
||||
replacement: {
|
||||
match: /"dot"===\i/,
|
||||
|
@ -75,7 +59,7 @@ export default definePlugin({
|
|||
{
|
||||
find: ".roleVerifiedIcon",
|
||||
all: true,
|
||||
predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
|
||||
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||
noWarn: true,
|
||||
replacement: {
|
||||
match: /"dot"===\i/,
|
||||
|
@ -84,6 +68,21 @@ export default definePlugin({
|
|||
}
|
||||
],
|
||||
|
||||
options: {
|
||||
bothStyles: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show both role dot and coloured names",
|
||||
restartNeeded: true,
|
||||
default: false,
|
||||
},
|
||||
copyRoleColorInProfilePopout: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Allow click on role dot in profile popout to copy role color",
|
||||
restartNeeded: true,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
copyToClipBoard(color: string) {
|
||||
Clipboard.copy(color);
|
||||
Toasts.show({
|
||||
|
|
|
@ -21,20 +21,20 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByProps, findExportedComponent, findStore } from "@webpack";
|
||||
import { findByPropsLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
|
||||
import { Constants, React, RestAPI, Tooltip } from "@webpack/common";
|
||||
|
||||
import { RenameButton } from "./components/RenameButton";
|
||||
import { Session, SessionInfo } from "./types";
|
||||
import { fetchNamesFromDataStore, getDefaultName, GetOsColor, GetPlatformIcon, savedSessionsCache, saveSessionsToDataStore } from "./utils";
|
||||
|
||||
const AuthSessionsStore = findStore("AuthSessionsStore");
|
||||
const UserSettingsModal = findByProps("saveAccountChanges", "open");
|
||||
const AuthSessionsStore = findStoreLazy("AuthSessionsStore");
|
||||
const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open");
|
||||
|
||||
const TimestampClasses = findByProps("timestampTooltip", "blockquoteContainer");
|
||||
const SessionIconClasses = findByProps("sessionIcon");
|
||||
const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer");
|
||||
const SessionIconClasses = findByPropsLazy("sessionIcon");
|
||||
|
||||
const BlobMask = findExportedComponent("BlobMask");
|
||||
const BlobMask = findExportedComponentLazy("BlobMask");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
backgroundCheck: {
|
||||
|
@ -60,7 +60,7 @@ export default definePlugin({
|
|||
|
||||
patches: [
|
||||
{
|
||||
find: "Messages.AUTH_SESSIONS_SESSION_LOG_OUT",
|
||||
find: "#{intl::AUTH_SESSIONS_SESSION_LOG_OUT}",
|
||||
replacement: [
|
||||
// Replace children with a single label with state
|
||||
{
|
||||
|
|
69
src/plugins/betterSettings/PluginsSubmenu.tsx
Normal file
69
src/plugins/betterSettings/PluginsSubmenu.tsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { openPluginModal } from "@components/PluginSettings/PluginModal";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { isObjectEmpty } from "@utils/misc";
|
||||
import { Alerts, Menu, useMemo, useState } from "@webpack/common";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
function onRestartNeeded() {
|
||||
Alerts.show({
|
||||
title: "Restart required",
|
||||
body: <p>You have changed settings that require a restart.</p>,
|
||||
confirmText: "Restart now",
|
||||
cancelText: "Later!",
|
||||
onConfirm: () => location.reload()
|
||||
});
|
||||
}
|
||||
|
||||
export default function PluginsSubmenu() {
|
||||
const sortedPlugins = useMemo(() => Object.values(Plugins)
|
||||
.sort((a, b) => a.name.localeCompare(b.name)), []);
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
const search = query.toLowerCase();
|
||||
const include = (p: typeof Plugins[keyof typeof Plugins]) => (
|
||||
Vencord.Plugins.isPluginEnabled(p.name)
|
||||
&& p.options && !isObjectEmpty(p.options)
|
||||
&& (
|
||||
p.name.toLowerCase().includes(search)
|
||||
|| p.description.toLowerCase().includes(search)
|
||||
|| p.tags?.some(t => t.toLowerCase().includes(search))
|
||||
)
|
||||
);
|
||||
|
||||
const plugins = sortedPlugins.filter(include);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu.MenuControlItem
|
||||
id="vc-plugins-search"
|
||||
control={(props, ref) => (
|
||||
<Menu.MenuSearchControl
|
||||
{...props}
|
||||
query={query}
|
||||
onChange={setQuery}
|
||||
ref={ref}
|
||||
placeholder={getIntlMessage("SEARCH")}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{!!plugins.length && <Menu.MenuSeparator />}
|
||||
|
||||
{plugins.map(p => (
|
||||
<Menu.MenuItem
|
||||
key={p.name}
|
||||
id={p.name}
|
||||
label={p.name}
|
||||
action={() => openPluginModal(p, onRestartNeeded)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -7,18 +7,20 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
|
||||
import { NoopComponent } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByProps } from "@webpack";
|
||||
import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import { waitFor } from "@webpack";
|
||||
import { ComponentDispatch, FocusLock, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import type { HTMLAttributes, ReactElement } from "react";
|
||||
|
||||
import PluginsSubmenu from "./PluginsSubmenu";
|
||||
|
||||
type SettingsEntry = { section: string, label: string; };
|
||||
|
||||
const cl = classNameFactory("");
|
||||
const Classes = findByProps("animating", "baseLayer", "bg", "layer", "layers");
|
||||
let Classes: Record<string, string>;
|
||||
waitFor(["animating", "baseLayer", "bg", "layer", "layers"], m => Classes = m);
|
||||
|
||||
const settings = definePluginSettings({
|
||||
disableFade: {
|
||||
|
@ -110,7 +112,7 @@ export default definePlugin({
|
|||
predicate: () => settings.store.disableFade
|
||||
},
|
||||
{ // Load menu TOC eagerly
|
||||
find: "Messages.USER_SETTINGS_WITH_BUILD_OVERRIDE.format",
|
||||
find: "#{intl::USER_SETTINGS_WITH_BUILD_OVERRIDE}",
|
||||
replacement: {
|
||||
match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?null!=\i&&.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/,
|
||||
replace: "$&(async ()=>$2)(),"
|
||||
|
@ -118,14 +120,22 @@ export default definePlugin({
|
|||
predicate: () => settings.store.eagerLoad
|
||||
},
|
||||
{ // Settings cog context menu
|
||||
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL",
|
||||
replacement: {
|
||||
match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
|
||||
replace: "$1$self.wrapMenu($2)"
|
||||
}
|
||||
}
|
||||
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
||||
replacement: [
|
||||
{
|
||||
match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
|
||||
replace: "$1$self.wrapMenu($2)"
|
||||
},
|
||||
{
|
||||
match: /case \i\.\i\.DEVELOPER_OPTIONS:return \i;/,
|
||||
replace: "$&case 'VencordPlugins':return $self.PluginsSubmenu();"
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
|
||||
PluginsSubmenu,
|
||||
|
||||
// This is the very outer layer of the entire ui, so we can't wrap this in an ErrorBoundary
|
||||
// without possibly also catching unrelated errors of children.
|
||||
//
|
||||
|
@ -133,7 +143,7 @@ export default definePlugin({
|
|||
// try catch will only catch errors in the Layer function (hence why it's called as a plain function rather than a component), but
|
||||
// not in children
|
||||
Layer(props: LayerProps) {
|
||||
if (FocusLock === NoopComponent || ComponentDispatch[SYM_PROXY_INNER_VALUE] == null || Classes[SYM_PROXY_INNER_VALUE] == null) {
|
||||
if (!FocusLock || !ComponentDispatch || !Classes) {
|
||||
new Logger("BetterSettings").error("Failed to find some components");
|
||||
return props.children;
|
||||
}
|
||||
|
@ -150,7 +160,7 @@ export default definePlugin({
|
|||
if (item.section === "HEADER") {
|
||||
items.push({ label: item.label, items: [] });
|
||||
} else if (item.section === "DIVIDER") {
|
||||
items.push({ label: i18n.Messages.OTHER_OPTIONS, items: [] });
|
||||
items.push({ label: getIntlMessage("OTHER_OPTIONS"), items: [] });
|
||||
} else {
|
||||
items.at(-1)!.items.push(item);
|
||||
}
|
||||
|
|
|
@ -57,7 +57,11 @@ export const handleViewPreview = async ({ guildId, channelId, ownerId }: Applica
|
|||
const previewUrl = await ApplicationStreamPreviewStore.getPreviewURL(guildId, channelId, ownerId);
|
||||
if (!previewUrl) return;
|
||||
|
||||
openImageModal(previewUrl);
|
||||
openImageModal({
|
||||
url: previewUrl,
|
||||
height: 720,
|
||||
width: 1280
|
||||
});
|
||||
};
|
||||
|
||||
export const addViewStreamContext: NavContextMenuPatchCallback = (children, { userId }: { userId: string | bigint; }) => {
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { findStore } from "@webpack";
|
||||
import { findStoreLazy } from "@webpack";
|
||||
|
||||
import * as t from "./types/stores";
|
||||
|
||||
export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStore("ApplicationStreamPreviewStore");
|
||||
export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStore("ApplicationStreamingStore");
|
||||
export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStoreLazy("ApplicationStreamPreviewStore");
|
||||
export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore");
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Settings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
|
@ -26,7 +26,7 @@ function setCss() {
|
|||
style.textContent = `
|
||||
.vc-nsfw-img [class^=imageWrapper] img,
|
||||
.vc-nsfw-img [class^=wrapperPaused] video {
|
||||
filter: blur(${settings.store.blurAmount}px);
|
||||
filter: blur(${Settings.plugins.BlurNSFW.blurAmount}px);
|
||||
transition: filter 0.2s;
|
||||
}
|
||||
.vc-nsfw-img [class^=imageWrapper]:hover img,
|
||||
|
@ -36,31 +36,30 @@ function setCss() {
|
|||
`;
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
blurAmount: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Blur Amount",
|
||||
default: 10,
|
||||
onChange: setCss
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "BlurNSFW",
|
||||
description: "Blur attachments in NSFW channels until hovered",
|
||||
authors: [Devs.Ven],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: ".embedWrapper,embed",
|
||||
replacement: [{
|
||||
match: /\.embedWrapper(?=.+?channel_id:(\i)\.id)/g,
|
||||
replace: "$&+($1.nsfw?' vc-nsfw-img':'')"
|
||||
match: /\.container/,
|
||||
replace: "$&+(this.props.channel.nsfw? ' vc-nsfw-img': '')"
|
||||
}]
|
||||
}
|
||||
],
|
||||
|
||||
options: {
|
||||
blurAmount: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Blur Amount",
|
||||
default: 10,
|
||||
onChange: setCss
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
style = document.createElement("style");
|
||||
style.id = "VcBlurNsfw";
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Settings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { useTimer } from "@utils/react";
|
||||
|
@ -25,7 +25,7 @@ import { React } from "@webpack/common";
|
|||
|
||||
function formatDuration(ms: number) {
|
||||
// here be dragons (moment fucking sucks)
|
||||
const human = settings.store.format === "human";
|
||||
const human = Settings.plugins.CallTimer.format === "human";
|
||||
|
||||
const format = (n: number) => human ? n : n.toString().padStart(2, "0");
|
||||
const unit = (s: string) => human ? s : "";
|
||||
|
@ -46,33 +46,32 @@ function formatDuration(ms: number) {
|
|||
return res;
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
format: {
|
||||
type: OptionType.SELECT,
|
||||
description: "The timer format. This can be any valid moment.js format",
|
||||
options: [
|
||||
{
|
||||
label: "30d 23:00:42",
|
||||
value: "stopwatch",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "30d 23h 00m 42s",
|
||||
value: "human"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "CallTimer",
|
||||
description: "Adds a timer to vcs",
|
||||
authors: [Devs.Ven],
|
||||
settings,
|
||||
|
||||
startTime: 0,
|
||||
interval: void 0 as NodeJS.Timeout | undefined,
|
||||
|
||||
options: {
|
||||
format: {
|
||||
type: OptionType.SELECT,
|
||||
description: "The timer format. This can be any valid moment.js format",
|
||||
options: [
|
||||
{
|
||||
label: "30d 23:00:42",
|
||||
value: "stopwatch",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "30d 23h 00m 42s",
|
||||
value: "human"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
patches: [{
|
||||
find: "renderConnectionStatus(){",
|
||||
replacement: {
|
||||
|
|
|
@ -155,4 +155,5 @@ export const defaultRules = [
|
|||
"igshid",
|
||||
"igsh",
|
||||
"share_id@reddit.com",
|
||||
"si@soundcloud.com",
|
||||
];
|
||||
|
|
|
@ -11,10 +11,10 @@ import { Devs } from "@utils/constants";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
import { findByCode, findComponentByCode, findStore } from "@webpack";
|
||||
import { Button, Forms, useStateFromStores } from "@webpack/common";
|
||||
import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common";
|
||||
|
||||
const ColorPicker = findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
|
||||
const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
|
||||
|
||||
const colorPresets = [
|
||||
"#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D",
|
||||
|
@ -30,21 +30,20 @@ function onPickColor(color: number) {
|
|||
updateColorVars(hexColor);
|
||||
}
|
||||
|
||||
const saveClientTheme = findByCode('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"===');
|
||||
const saveClientTheme = findByCodeLazy('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"===');
|
||||
|
||||
function setTheme(theme: string) {
|
||||
saveClientTheme({ theme });
|
||||
}
|
||||
|
||||
const ThemeStore = findStore("ThemeStore");
|
||||
const ClientThemesBackgroundStore = findStore("ClientThemesBackgroundStore");
|
||||
const NitroThemeStore = findStoreLazy("ClientThemesBackgroundStore");
|
||||
|
||||
function ThemeSettings() {
|
||||
const theme = useStateFromStores([ThemeStore], () => ThemeStore.theme);
|
||||
const isLightTheme = theme === "light";
|
||||
const oppositeTheme = isLightTheme ? "dark" : "light";
|
||||
|
||||
const nitroTheme = useStateFromStores([ClientThemesBackgroundStore], () => ClientThemesBackgroundStore.gradientPreset);
|
||||
const nitroTheme = useStateFromStores([NitroThemeStore], () => NitroThemeStore.gradientPreset);
|
||||
const nitroThemeEnabled = nitroTheme !== undefined;
|
||||
|
||||
const selectedLuminance = relativeLuminance(settings.store.color);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# ConsoleJanitor
|
||||
|
||||
Disables annoying console messages/errors. This plugin mainly removes errors/warnings that happen all the time and noisy/spammy logging messages.
|
||||
Disables annoying console messages/errors. This plugin mainly removes errors/warnings that happen all the time and Discord logger messages.
|
||||
|
||||
Some of the disabled messages include the "notosans-400-normalitalic" error and MessageActionCreators, Routing/Utils loggers.
|
||||
One of the disabled messages is the "Window state not initialized" warning, for example.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
|
||||
const Noop = () => { };
|
||||
const NoopLogger = {
|
||||
|
@ -22,10 +22,12 @@ const NoopLogger = {
|
|||
fileOnly: Noop
|
||||
};
|
||||
|
||||
const logAllow = new Set();
|
||||
|
||||
const settings = definePluginSettings({
|
||||
disableNoisyLoggers: {
|
||||
disableLoggers: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Disable noisy loggers like the MessageActionCreators",
|
||||
description: "Disables Discords loggers",
|
||||
default: false,
|
||||
restartNeeded: true
|
||||
},
|
||||
|
@ -34,18 +36,43 @@ const settings = definePluginSettings({
|
|||
description: "Disable the Spotify logger, which leaks account information and access token",
|
||||
default: true,
|
||||
restartNeeded: true
|
||||
},
|
||||
whitelistedLoggers: {
|
||||
type: OptionType.STRING,
|
||||
description: "Semi colon separated list of loggers to allow even if others are hidden",
|
||||
default: "GatewaySocket; Routing/Utils",
|
||||
onChange(newVal: string) {
|
||||
logAllow.clear();
|
||||
newVal.split(";").map(x => x.trim()).forEach(logAllow.add.bind(logAllow));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "ConsoleJanitor",
|
||||
description: "Disables annoying console messages/errors",
|
||||
authors: [Devs.Nuckyz],
|
||||
authors: [Devs.Nuckyz, Devs.sadan],
|
||||
settings,
|
||||
|
||||
startAt: StartAt.Init,
|
||||
start() {
|
||||
logAllow.clear();
|
||||
this.settings.store.whitelistedLoggers?.split(";").map(x => x.trim()).forEach(logAllow.add.bind(logAllow));
|
||||
},
|
||||
|
||||
NoopLogger: () => NoopLogger,
|
||||
shouldLog(logger: string) {
|
||||
return logAllow.has(logger);
|
||||
},
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: 'react-spring: The "interpolate" function',
|
||||
replacement: {
|
||||
match: /,console.warn\('react-spring: The "interpolate" function is deprecated in v10 \(use "to" instead\)'\)/,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
find: 'console.warn("Window state not initialized"',
|
||||
replacement: {
|
||||
|
@ -60,13 +87,6 @@ export default definePlugin({
|
|||
replace: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "notosans-400-normalitalic",
|
||||
replacement: {
|
||||
match: /,"notosans-.+?"/g,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
find: 'console.warn("[DEPRECATED] Please use `subscribeWithSelector` middleware");',
|
||||
all: true,
|
||||
|
@ -110,34 +130,13 @@ export default definePlugin({
|
|||
replace: ""
|
||||
}
|
||||
},
|
||||
...[
|
||||
'("MessageActionCreators")', '("ChannelMessages")',
|
||||
'("Routing/Utils")', '("RTCControlSocket")',
|
||||
'("ConnectionEventFramerateReducer")', '("RTCLatencyTestManager")',
|
||||
'("OverlayBridgeStore")', '("RPCServer:WSS")', '("RPCServer:IPC")'
|
||||
].map(logger => ({
|
||||
find: logger,
|
||||
predicate: () => settings.store.disableNoisyLoggers,
|
||||
all: true,
|
||||
replacement: {
|
||||
match: new RegExp(String.raw`new \i\.\i${logger.replace(/([()])/g, "\\$1")}`),
|
||||
replace: `$self.NoopLogger${logger}`
|
||||
}
|
||||
})),
|
||||
// Patches discords generic logger function
|
||||
{
|
||||
find: '"Experimental codecs: "',
|
||||
predicate: () => settings.store.disableNoisyLoggers,
|
||||
find: "Σ:",
|
||||
predicate: () => settings.store.disableLoggers,
|
||||
replacement: {
|
||||
match: /new \i\.\i\("Connection\("\.concat\(\i,"\)"\)\)/,
|
||||
replace: "$self.NoopLogger()"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: '"Handling ping: "',
|
||||
predicate: () => settings.store.disableNoisyLoggers,
|
||||
replacement: {
|
||||
match: /new \i\.\i\("RTCConnection\("\.concat.+?\)\)(?=,)/,
|
||||
replace: "$self.NoopLogger()"
|
||||
match: /(?<=&&)(?=console)/,
|
||||
replace: "$self.shouldLog(arguments[0])&&"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -148,5 +147,5 @@ export default definePlugin({
|
|||
replace: "$self.NoopLogger()"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
});
|
||||
|
|
|
@ -18,14 +18,13 @@
|
|||
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentChannel, getCurrentGuild } from "@utils/discord";
|
||||
import { runtimeHashMessageKey } from "@utils/intlHash";
|
||||
import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy";
|
||||
import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
|
||||
import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
|
||||
import definePlugin, { PluginNative, StartAt } from "@utils/types";
|
||||
import * as Webpack from "@webpack";
|
||||
import { cacheFindAll, cacheFindModuleId, extract, filters, searchFactories } from "@webpack";
|
||||
import { extract, filters, findAll, findModuleId, search } from "@webpack";
|
||||
import * as Common from "@webpack/common";
|
||||
import { loadLazyChunks } from "debug/loadLazyChunks";
|
||||
import type { ComponentType } from "react";
|
||||
|
@ -34,29 +33,27 @@ const DESKTOP_ONLY = (f: string) => () => {
|
|||
throw new Error(`'${f}' is Discord Desktop only.`);
|
||||
};
|
||||
|
||||
const define: typeof Object.defineProperty =
|
||||
(obj, prop, desc) => {
|
||||
if (Object.hasOwn(desc, "value"))
|
||||
desc.writable = true;
|
||||
|
||||
type Define = typeof Object.defineProperty;
|
||||
const define: Define = (target, p, attributes) => {
|
||||
if (Object.hasOwn(attributes, "value")) {
|
||||
attributes.writable = true;
|
||||
}
|
||||
|
||||
return Object.defineProperty(target, p, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
...attributes
|
||||
});
|
||||
};
|
||||
return Object.defineProperty(obj, prop, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
...desc
|
||||
});
|
||||
};
|
||||
|
||||
function makeShortcuts() {
|
||||
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn, shouldReturnFactory = false) {
|
||||
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) {
|
||||
const cache = new Map<string, unknown>();
|
||||
|
||||
return function (...filterProps: unknown[]) {
|
||||
const cacheKey = String(filterProps);
|
||||
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
||||
|
||||
const matches = cacheFindAll(filterFactory(...filterProps), shouldReturnFactory);
|
||||
const matches = findAll(filterFactory(...filterProps));
|
||||
|
||||
const result = (() => {
|
||||
switch (matches.length) {
|
||||
|
@ -64,71 +61,51 @@ function makeShortcuts() {
|
|||
case 1: return matches[0];
|
||||
default:
|
||||
const uniqueMatches = [...new Set(matches)];
|
||||
if (uniqueMatches.length > 1) {
|
||||
if (uniqueMatches.length > 1)
|
||||
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
|
||||
}
|
||||
|
||||
return uniqueMatches[0];
|
||||
return matches[0];
|
||||
}
|
||||
})();
|
||||
|
||||
if (result && cacheKey) cache.set(cacheKey, result);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
let fakeRenderWin: WeakRef<Window> | undefined;
|
||||
|
||||
const find = newFindWrapper(f => f);
|
||||
const findByProps = newFindWrapper(filters.byProps);
|
||||
|
||||
return {
|
||||
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
|
||||
wp: Webpack,
|
||||
wpc: { getter: () => Webpack.cache },
|
||||
wreq: { getter: () => Webpack.wreq },
|
||||
|
||||
WebpackInstances: { getter: () => Vencord.WebpackPatcher.allWebpackInstances },
|
||||
loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); },
|
||||
|
||||
wpsearch: searchFactories,
|
||||
wpsearch: search,
|
||||
wpex: extract,
|
||||
wpexs: (...code: Webpack.CodeFilter) => extract(cacheFindModuleId(...code)!),
|
||||
|
||||
filters,
|
||||
wpexs: (code: string) => extract(findModuleId(code)!),
|
||||
loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); },
|
||||
find,
|
||||
findAll: cacheFindAll,
|
||||
findComponent: find,
|
||||
findAllComponents: cacheFindAll,
|
||||
findExportedComponent: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
|
||||
findComponentByCode: newFindWrapper(filters.componentByCode),
|
||||
findAllComponentsByCode: (...code: Webpack.PropsFilter) => cacheFindAll(filters.componentByCode(...code)),
|
||||
findComponentByFields: newFindWrapper(filters.componentByFields),
|
||||
findAllComponentsByFields: (...fields: Webpack.PropsFilter) => cacheFindAll(filters.componentByFields(...fields)),
|
||||
findAll: findAll,
|
||||
findByProps,
|
||||
findAllByProps: (...props: Webpack.PropsFilter) => cacheFindAll(filters.byProps(...props)),
|
||||
findProp: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
|
||||
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
|
||||
findByCode: newFindWrapper(filters.byCode),
|
||||
findAllByCode: (code: Webpack.CodeFilter) => cacheFindAll(filters.byCode(...code)),
|
||||
findAllByCode: (code: string) => findAll(filters.byCode(code)),
|
||||
findComponentByCode: newFindWrapper(filters.componentByCode),
|
||||
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
|
||||
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
|
||||
findStore: newFindWrapper(filters.byStoreName),
|
||||
findByFactoryCode: newFindWrapper(filters.byFactoryCode),
|
||||
findAllByFactoryCode: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code)),
|
||||
findModuleFactory: newFindWrapper(filters.byFactoryCode, true),
|
||||
findAllModuleFactories: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code), true),
|
||||
|
||||
plugins: { getter: () => Vencord.Plugins.plugins },
|
||||
PluginsApi: { getter: () => Vencord.Plugins },
|
||||
plugins: { getter: () => Vencord.Plugins.plugins },
|
||||
Settings: { getter: () => Vencord.Settings },
|
||||
Api: { getter: () => Vencord.Api },
|
||||
Util: { getter: () => Vencord.Util },
|
||||
|
||||
reload: () => location.reload(),
|
||||
restart: IS_WEB ? DESKTOP_ONLY("restart") : relaunch,
|
||||
|
||||
canonicalizeMatch,
|
||||
canonicalizeReplace,
|
||||
canonicalizeReplacement,
|
||||
|
||||
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
|
||||
runtimeHashMessageKey,
|
||||
fakeRender: (component: ComponentType, props: any) => {
|
||||
const prevWin = fakeRenderWin?.deref();
|
||||
const win = prevWin?.closed === false
|
||||
|
@ -158,6 +135,8 @@ function makeShortcuts() {
|
|||
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
|
||||
},
|
||||
|
||||
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
|
||||
|
||||
channel: { getter: () => getCurrentChannel(), preload: false },
|
||||
channelId: { getter: () => Common.SelectedChannelStore.getChannelId(), preload: false },
|
||||
guild: { getter: () => getCurrentGuild(), preload: false },
|
||||
|
@ -166,7 +145,6 @@ function makeShortcuts() {
|
|||
meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false },
|
||||
messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false },
|
||||
|
||||
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
|
||||
Stores: {
|
||||
getter: () => Object.fromEntries(
|
||||
Common.Flux.Store.getAll()
|
||||
|
@ -181,39 +159,11 @@ function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) {
|
|||
const currentVal = val.getter();
|
||||
if (!currentVal || val.preload === false) return currentVal;
|
||||
|
||||
function unwrapProxy(value: any) {
|
||||
if (value[SYM_LAZY_GET]) {
|
||||
return forceLoad ? value[SYM_LAZY_GET]() : value[SYM_LAZY_CACHED];
|
||||
} else if (value[SYM_PROXY_INNER_GET]) {
|
||||
return forceLoad ? value[SYM_PROXY_INNER_GET]() : value[SYM_PROXY_INNER_VALUE];
|
||||
} else if (value[SYM_LAZY_COMPONENT_INNER]) {
|
||||
return value[SYM_LAZY_COMPONENT_INNER]() != null ? value[SYM_LAZY_COMPONENT_INNER]() : value;
|
||||
}
|
||||
const value = currentVal[SYM_LAZY_GET]
|
||||
? forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED]
|
||||
: currentVal;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
const value = unwrapProxy(currentVal);
|
||||
if (value != null && typeof value === "object") {
|
||||
const descriptors = Object.getOwnPropertyDescriptors(value);
|
||||
|
||||
for (const propKey in descriptors) {
|
||||
if (value[propKey] == null) continue;
|
||||
|
||||
const descriptor = descriptors[propKey];
|
||||
if (descriptor.writable === true || descriptor.set != null) {
|
||||
const currentValue = value[propKey];
|
||||
const newValue = unwrapProxy(currentValue);
|
||||
if (newValue != null && currentValue !== newValue) {
|
||||
value[propKey] = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
define(window.shortcutList, key, { value });
|
||||
}
|
||||
if (value) define(window.shortcutList, key, { value });
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -228,10 +178,8 @@ export default definePlugin({
|
|||
const shortcuts = makeShortcuts();
|
||||
window.shortcutList = {};
|
||||
|
||||
for (const key in shortcuts) {
|
||||
const val = shortcuts[key];
|
||||
|
||||
if (Object.hasOwn(val, "getter")) {
|
||||
for (const [key, val] of Object.entries(shortcuts)) {
|
||||
if ("getter" in val) {
|
||||
define(window.shortcutList, key, {
|
||||
get: () => loadAndCacheShortcut(key, val, true)
|
||||
});
|
||||
|
@ -245,8 +193,8 @@ export default definePlugin({
|
|||
}
|
||||
}
|
||||
|
||||
// Unproxy loaded modules
|
||||
Webpack.onceDiscordLoaded.then(() => {
|
||||
// unproxy loaded modules
|
||||
Webpack.onceReady.then(() => {
|
||||
setTimeout(() => this.eagerLoad(false), 1000);
|
||||
|
||||
if (!IS_WEB) {
|
||||
|
@ -257,13 +205,13 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
async eagerLoad(forceLoad: boolean) {
|
||||
await Webpack.onceDiscordLoaded;
|
||||
await Webpack.onceReady;
|
||||
|
||||
const shortcuts = makeShortcuts();
|
||||
for (const key in shortcuts) {
|
||||
const val = shortcuts[key];
|
||||
|
||||
if (!Object.hasOwn(val, "getter") || val.preload === false) continue;
|
||||
for (const [key, val] of Object.entries(shortcuts)) {
|
||||
if (!Object.hasOwn(val, "getter") || (val as any).preload === false) continue;
|
||||
|
||||
try {
|
||||
loadAndCacheShortcut(key, val, forceLoad);
|
||||
} catch { } // swallow not found errors in DEV
|
||||
|
|
|
@ -8,10 +8,10 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { copyWithToast } from "@utils/misc";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findProp } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Menu } from "@webpack/common";
|
||||
|
||||
const convertNameToSurrogate = findProp("convertNameToSurrogate");
|
||||
const { convertNameToSurrogate } = findByPropsLazy("convertNameToSurrogate");
|
||||
|
||||
interface Emoji {
|
||||
type: string;
|
||||
|
@ -55,7 +55,7 @@ export default definePlugin({
|
|||
settings,
|
||||
|
||||
contextMenus: {
|
||||
"expression-picker"(children, { target }: { target: Target; }) {
|
||||
"expression-picker"(children, { target }: { target: Target }) {
|
||||
if (target.dataset.type !== "emoji") return;
|
||||
|
||||
children.push(
|
||||
|
|
5
src/plugins/copyFileContents/README.md
Normal file
5
src/plugins/copyFileContents/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# CopyFileContents
|
||||
|
||||
Adds a button to text file attachments to copy their contents.
|
||||
|
||||
![](https://github.com/user-attachments/assets/b1a0f6f4-106f-4953-94d9-4c5ef5810bca)
|
60
src/plugins/copyFileContents/index.tsx
Normal file
60
src/plugins/copyFileContents/index.tsx
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./style.css";
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { CopyIcon, NoEntrySignIcon } from "@components/Icons";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { copyWithToast } from "@utils/misc";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Tooltip, useState } from "@webpack/common";
|
||||
|
||||
const CheckMarkIcon = () => {
|
||||
return <svg width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M21.7 5.3a1 1 0 0 1 0 1.4l-12 12a1 1 0 0 1-1.4 0l-6-6a1 1 0 1 1 1.4-1.4L9 16.58l11.3-11.3a1 1 0 0 1 1.4 0Z"></path>
|
||||
</svg>;
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "CopyFileContents",
|
||||
description: "Adds a button to text file attachments to copy their contents",
|
||||
authors: [Devs.Obsidian, Devs.Nuckyz],
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::PREVIEW_BYTES_LEFT}",
|
||||
replacement: {
|
||||
match: /\.footerGap.+?url:\i,fileName:\i,fileSize:\i}\),(?<=fileContents:(\i),bytesLeft:(\i).+?)/g,
|
||||
replace: "$&$self.addCopyButton({fileContents:$1,bytesLeft:$2}),"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
addCopyButton: ErrorBoundary.wrap(({ fileContents, bytesLeft }: { fileContents: string, bytesLeft: number; }) => {
|
||||
const [recentlyCopied, setRecentlyCopied] = useState(false);
|
||||
|
||||
return (
|
||||
<Tooltip text={recentlyCopied ? "Copied!" : bytesLeft > 0 ? "File too large to copy" : "Copy File Contents"}>
|
||||
{tooltipProps => (
|
||||
<div
|
||||
{...tooltipProps}
|
||||
className="vc-cfc-button"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
if (!recentlyCopied && bytesLeft <= 0) {
|
||||
copyWithToast(fileContents);
|
||||
setRecentlyCopied(true);
|
||||
setTimeout(() => setRecentlyCopied(false), 2000);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{recentlyCopied ? <CheckMarkIcon /> : bytesLeft > 0 ? <NoEntrySignIcon color="var(--channel-icon)" /> : <CopyIcon />}
|
||||
</div>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
}, { noop: true }),
|
||||
});
|
9
src/plugins/copyFileContents/style.css
Normal file
9
src/plugins/copyFileContents/style.css
Normal file
|
@ -0,0 +1,9 @@
|
|||
.vc-cfc-button {
|
||||
color: var(--interactive-normal);
|
||||
cursor: pointer;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.vc-cfc-button:hover {
|
||||
color: var(--interactive-hover);
|
||||
}
|
|
@ -23,12 +23,22 @@ import { Logger } from "@utils/Logger";
|
|||
import { closeAllModals } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { maybePromptToUpdate } from "@utils/updater";
|
||||
import { findByProps } from "@webpack";
|
||||
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
|
||||
import { DraftType, ExpressionPickerStore, FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common";
|
||||
|
||||
const CrashHandlerLogger = new Logger("CrashHandler");
|
||||
const ModalStack = findByProps("pushLazy", "popAll");
|
||||
const DraftManager = findByProps("clearDraft", "saveDraft");
|
||||
|
||||
const { ModalStack, DraftManager } = proxyLazyWebpack(() => {
|
||||
const [ModalStack, DraftManager] = findBulk(
|
||||
filters.byProps("pushLazy", "popAll"),
|
||||
filters.byProps("clearDraft", "saveDraft"),
|
||||
);
|
||||
|
||||
return {
|
||||
ModalStack,
|
||||
DraftManager
|
||||
};
|
||||
});
|
||||
|
||||
const settings = definePluginSettings({
|
||||
attemptToPreventCrashes: {
|
||||
|
@ -57,7 +67,7 @@ export default definePlugin({
|
|||
|
||||
patches: [
|
||||
{
|
||||
find: ".Messages.ERRORS_UNEXPECTED_CRASH",
|
||||
find: "#{intl::ERRORS_UNEXPECTED_CRASH}",
|
||||
replacement: {
|
||||
match: /this\.setState\((.+?)\)/,
|
||||
replace: "$self.handleCrash(this,$1);"
|
||||
|
@ -165,7 +175,7 @@ export default definePlugin({
|
|||
}
|
||||
if (settings.store.attemptToNavigateToHome) {
|
||||
try {
|
||||
NavigationRouter.transitionTo("/channels/@me");
|
||||
NavigationRouter.transitionToGuild("@me");
|
||||
} catch (err) {
|
||||
CrashHandlerLogger.debug("Failed to navigate to home", err);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { getUserSettingLazy } from "@api/UserSettings";
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { Link } from "@components/Link";
|
||||
|
@ -26,14 +26,13 @@ import { Margins } from "@utils/margins";
|
|||
import { classes } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCode, findByProps, findComponentByCode } from "@webpack";
|
||||
import { findByCodeLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
||||
|
||||
const useProfileThemeStyle = findByCode("profileThemeStyle:", "--profile-gradient-primary-color");
|
||||
const ActivityComponent = findComponentByCode("onOpenGameProfile");
|
||||
const ActivityClassName = findByProps("activity", "buttonColor");
|
||||
const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color");
|
||||
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
|
||||
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||
|
||||
async function getApplicationAsset(key: string): Promise<string> {
|
||||
if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, "");
|
||||
|
@ -261,7 +260,7 @@ const settings = definePluginSettings({
|
|||
|
||||
function onChange() {
|
||||
setRpc(true);
|
||||
if (Vencord.Plugins.isPluginEnabled("CustomRPC")) setRpc();
|
||||
if (Settings.plugins.CustomRPC.enabled) setRpc();
|
||||
}
|
||||
|
||||
function isStreamLinkDisabled() {
|
||||
|
@ -436,8 +435,8 @@ export default definePlugin({
|
|||
|
||||
<Forms.FormDivider className={Margins.top8} />
|
||||
|
||||
<div style={{ width: "284px", ...profileThemeStyle }}>
|
||||
{activity[0] && <ActivityComponent activity={activity[0]} className={ActivityClassName.activity} channelId={SelectedChannelStore.getChannelId()}
|
||||
<div style={{ width: "284px", ...profileThemeStyle, padding: 8, marginTop: 8, borderRadius: 8, background: "var(--bg-mod-faint)" }}>
|
||||
{activity[0] && <ActivityComponent activity={activity[0]} channelId={SelectedChannelStore.getChannelId()}
|
||||
guild={GuildStore.getGuild(SelectedGuildStore.getLastSelectedGuildId())}
|
||||
application={{ id: settings.store.appID }}
|
||||
user={UserStore.getCurrentUser()} />}
|
||||
|
|
|
@ -37,8 +37,8 @@ export default definePlugin({
|
|||
find: 'type:"IDLE",idle:',
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=Date\.now\(\)-\i>)\i\.\i/,
|
||||
replace: "$self.getIdleTimeout()"
|
||||
match: /(?<=Date\.now\(\)-\i>)\i\.\i\|\|/,
|
||||
replace: "$self.getIdleTimeout()||"
|
||||
},
|
||||
{
|
||||
match: /Math\.min\((\i\.\i\.getSetting\(\)\*\i\.\i\.\i\.SECOND),\i\.\i\)/,
|
||||
|
|
|
@ -46,7 +46,7 @@ const embedUrlRe = /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/;
|
|||
async function embedDidMount(this: Component<Props>) {
|
||||
try {
|
||||
const { embed } = this.props;
|
||||
const { replaceElements } = settings.store;
|
||||
const { replaceElements, dearrowByDefault } = settings.store;
|
||||
|
||||
if (!embed || embed.dearrow || embed.provider?.name !== "YouTube" || !embed.video?.url) return;
|
||||
|
||||
|
@ -63,18 +63,22 @@ async function embedDidMount(this: Component<Props>) {
|
|||
|
||||
if (!hasTitle && !hasThumb) return;
|
||||
|
||||
|
||||
embed.dearrow = {
|
||||
enabled: true
|
||||
enabled: dearrowByDefault
|
||||
};
|
||||
|
||||
if (hasTitle && replaceElements !== ReplaceElements.ReplaceThumbnailsOnly) {
|
||||
embed.dearrow.oldTitle = embed.rawTitle;
|
||||
embed.rawTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2");
|
||||
}
|
||||
const replacementTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2");
|
||||
|
||||
embed.dearrow.oldTitle = dearrowByDefault ? embed.rawTitle : replacementTitle;
|
||||
if (dearrowByDefault) embed.rawTitle = replacementTitle;
|
||||
}
|
||||
if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) {
|
||||
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
|
||||
embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
||||
const replacementProxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
||||
|
||||
embed.dearrow.oldThumb = dearrowByDefault ? embed.thumbnail.proxyURL : replacementProxyURL;
|
||||
if (dearrowByDefault) embed.thumbnail.proxyURL = replacementProxyURL;
|
||||
}
|
||||
|
||||
this.forceUpdate();
|
||||
|
@ -96,6 +100,7 @@ function DearrowButton({ component }: { component: Component<Props>; }) {
|
|||
className={"vc-dearrow-toggle-" + (embed.dearrow.enabled ? "on" : "off")}
|
||||
onClick={() => {
|
||||
const { enabled, oldThumb, oldTitle } = embed.dearrow;
|
||||
settings.store.dearrowByDefault = !enabled;
|
||||
embed.dearrow.enabled = !enabled;
|
||||
if (oldTitle) {
|
||||
embed.dearrow.oldTitle = embed.rawTitle;
|
||||
|
@ -153,6 +158,12 @@ const settings = definePluginSettings({
|
|||
{ label: "Titles", value: ReplaceElements.ReplaceTitlesOnly },
|
||||
{ label: "Thumbnails", value: ReplaceElements.ReplaceThumbnailsOnly },
|
||||
],
|
||||
},
|
||||
dearrowByDefault: {
|
||||
description: "Dearrow videos automatically",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
restartNeeded: false
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "DefaultCustomizationSections",
|
||||
replacement: {
|
||||
match: /(?<=USER_SETTINGS_AVATAR_DECORATION},"decoration"\),)/,
|
||||
match: /(?<=#{intl::USER_SETTINGS_AVATAR_DECORATION}\)},"decoration"\),)/,
|
||||
replace: "$self.DecorSection(),"
|
||||
}
|
||||
},
|
||||
|
@ -54,7 +54,7 @@ export default definePlugin({
|
|||
replace: "$self.DecorationGridItem=$&"
|
||||
},
|
||||
{
|
||||
match: /(?<==)\i=>{let{user:\i,avatarDecoration.{300,600}decorationGridItemChurned/,
|
||||
match: /(?<==)\i=>{let{user:\i,avatarDecoration/,
|
||||
replace: "$self.DecorationGridDecoration=$&"
|
||||
},
|
||||
// Remove NEW label from decor avatar decorations
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { Flex } from "@components/Flex";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { Button, useEffect } from "@webpack/common";
|
||||
|
||||
import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore";
|
||||
|
@ -13,7 +13,7 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco
|
|||
import { cl } from "../";
|
||||
import { openChangeDecorationModal } from "../modals/ChangeDecorationModal";
|
||||
|
||||
const CustomizationSection = findComponentByCode(".customizationSectionBackground");
|
||||
const CustomizationSection = findByCodeLazy(".customizationSectionBackground");
|
||||
|
||||
export interface DecorSectionProps {
|
||||
hideTitle?: boolean;
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*/
|
||||
|
||||
import { PlusIcon } from "@components/Icons";
|
||||
import { i18n, Text } from "@webpack/common";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { Text } from "@webpack/common";
|
||||
import { HTMLProps } from "react";
|
||||
|
||||
import { DecorationGridItem } from ".";
|
||||
|
@ -24,7 +25,7 @@ export default function DecorationGridCreate(props: DecorationGridCreateProps) {
|
|||
variant="text-xs/normal"
|
||||
color="header-primary"
|
||||
>
|
||||
{i18n.Messages.CREATE}
|
||||
{getIntlMessage("CREATE")}
|
||||
</Text>
|
||||
</DecorationGridItem >;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*/
|
||||
|
||||
import { NoEntrySignIcon } from "@components/Icons";
|
||||
import { i18n, Text } from "@webpack/common";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { Text } from "@webpack/common";
|
||||
import { HTMLProps } from "react";
|
||||
|
||||
import { DecorationGridItem } from ".";
|
||||
|
@ -24,7 +25,7 @@ export default function DecorationGridNone(props: DecorationGridNoneProps) {
|
|||
variant="text-xs/normal"
|
||||
color="header-primary"
|
||||
>
|
||||
{i18n.Messages.NONE}
|
||||
{getIntlMessage("NONE")}
|
||||
</Text>
|
||||
</DecorationGridItem >;
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { findByProps } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { React } from "@webpack/common";
|
||||
|
||||
import { cl } from "../";
|
||||
import Grid, { GridProps } from "./Grid";
|
||||
|
||||
const ScrollerClasses = findByProps("managedReactiveScroller");
|
||||
const ScrollerClasses = findByPropsLazy("managedReactiveScroller");
|
||||
|
||||
type Section<SectionT, ItemT> = SectionT & {
|
||||
items: Array<ItemT>;
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { NoopComponent } from "@utils/react";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
import { findComponentByCode, LazyComponentWebpack } from "@webpack";
|
||||
import { React } from "@webpack/common";
|
||||
import type { ComponentType, HTMLProps, PropsWithChildren } from "react";
|
||||
|
||||
|
@ -16,10 +15,13 @@ type DecorationGridItemComponent = ComponentType<PropsWithChildren<HTMLProps<HTM
|
|||
isSelected: boolean,
|
||||
}>;
|
||||
|
||||
export let DecorationGridItem: DecorationGridItemComponent = NoopComponent;
|
||||
export let DecorationGridItem: DecorationGridItemComponent;
|
||||
export const setDecorationGridItem = v => DecorationGridItem = v;
|
||||
|
||||
export const AvatarDecorationModalPreview = findComponentByCode<any>(".shopPreviewBanner", component => React.memo(component));
|
||||
export const AvatarDecorationModalPreview = LazyComponentWebpack(() => {
|
||||
const component = findComponentByCode(".shopPreviewBanner");
|
||||
return React.memo(component);
|
||||
});
|
||||
|
||||
type DecorationGridDecorationComponent = React.ComponentType<HTMLProps<HTMLDivElement> & {
|
||||
avatarDecoration: AvatarDecoration;
|
||||
|
@ -27,5 +29,5 @@ type DecorationGridDecorationComponent = React.ComponentType<HTMLProps<HTMLDivEl
|
|||
isSelected: boolean,
|
||||
}>;
|
||||
|
||||
export let DecorationGridDecoration: DecorationGridDecorationComponent = NoopComponent;
|
||||
export let DecorationGridDecoration: DecorationGridDecorationComponent;
|
||||
export const setDecorationGridDecoration = v => DecorationGridDecoration = v;
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { extractAndLoadChunksLazy, findByProps } from "@webpack";
|
||||
import { extractAndLoadChunksLazy, findByPropsLazy } from "@webpack";
|
||||
|
||||
export const cl = classNameFactory("vc-decor-");
|
||||
export const DecorationModalStyles = findByProps("modalFooterShopButton");
|
||||
export const DecorationModalStyles = findByPropsLazy("modalFooterShopButton");
|
||||
|
||||
export const requireAvatarDecorationModal = extractAndLoadChunksLazy(".COLLECTIBLES_SHOP_FULLSCREEN&&");
|
||||
export const requireCreateStickerModal = extractAndLoadChunksLazy("stickerInspected]:");
|
||||
export const requireAvatarDecorationModal = extractAndLoadChunksLazy([".COLLECTIBLES_SHOP_FULLSCREEN&&"]);
|
||||
export const requireCreateStickerModal = extractAndLoadChunksLazy(["stickerInspected]:"]);
|
||||
|
|
|
@ -10,7 +10,7 @@ import { openInviteModal } from "@utils/discord";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { classes, copyWithToast } from "@utils/misc";
|
||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
|
@ -29,7 +29,7 @@ import SectionedGridList from "../components/SectionedGridList";
|
|||
import { openCreateDecorationModal } from "./CreateDecorationModal";
|
||||
import { openGuidelinesModal } from "./GuidelinesModal";
|
||||
|
||||
const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
|
||||
function usePresets() {
|
||||
const [presets, setPresets] = useState<Preset[]>([]);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Link } from "@components/Link";
|
|||
import { openInviteModal } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { filters, findComponentByCode, mapMangledModule } from "@webpack";
|
||||
import { filters, findComponentByCodeLazy, mapMangledModuleLazy } from "@webpack";
|
||||
import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Text, TextInput, useEffect, useMemo, UserStore, useState } from "@webpack/common";
|
||||
|
||||
import { GUILD_ID, INVITE_KEY, RAW_SKU_ID } from "../../lib/constants";
|
||||
|
@ -17,11 +17,11 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco
|
|||
import { cl, DecorationModalStyles, requireAvatarDecorationModal, requireCreateStickerModal } from "../";
|
||||
import { AvatarDecorationModalPreview } from "../components";
|
||||
|
||||
const FileUpload = findComponentByCode("fileUploadInput,");
|
||||
const FileUpload = findComponentByCodeLazy("fileUploadInput,");
|
||||
|
||||
const { HelpMessage, HelpMessageTypes } = mapMangledModule('POSITIVE=3]="POSITIVE', {
|
||||
HelpMessage: filters.componentByCode(".iconDiv,", "messageType"),
|
||||
const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE=3]="POSITIVE', {
|
||||
HelpMessageTypes: filters.byProps("POSITIVE", "WARNING"),
|
||||
HelpMessage: filters.byCode(".iconDiv")
|
||||
});
|
||||
|
||||
function useObjectURL(object: Blob | MediaSource | null) {
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Devs } from "@utils/constants";
|
|||
import { Logger } from "@utils/Logger";
|
||||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
||||
import { cacheFindAll, filters, searchFactories } from "@webpack";
|
||||
import { filters, findAll, search } from "@webpack";
|
||||
|
||||
const PORT = 8485;
|
||||
const NAV_ID = "dev-companion-reconnect";
|
||||
|
@ -154,13 +154,13 @@ function initWs(isManual = false) {
|
|||
case "testPatch": {
|
||||
const { find, replacement } = data as PatchData;
|
||||
|
||||
const candidates = searchFactories(find);
|
||||
const candidates = search(find);
|
||||
const keys = Object.keys(candidates);
|
||||
if (keys.length !== 1)
|
||||
return reply("Expected exactly one 'find' matches, found " + keys.length);
|
||||
|
||||
const mod = candidates[keys[0]];
|
||||
let src = String(mod).replaceAll("\n", "");
|
||||
let src = String(mod.original ?? mod).replaceAll("\n", "");
|
||||
|
||||
if (src.startsWith("function(")) {
|
||||
src = "0," + src;
|
||||
|
@ -201,22 +201,22 @@ function initWs(isManual = false) {
|
|||
let results: any[];
|
||||
switch (type.replace("find", "").replace("Lazy", "")) {
|
||||
case "":
|
||||
results = cacheFindAll(parsedArgs[0]);
|
||||
results = findAll(parsedArgs[0]);
|
||||
break;
|
||||
case "ByProps":
|
||||
results = cacheFindAll(filters.byProps(...parsedArgs));
|
||||
results = findAll(filters.byProps(...parsedArgs));
|
||||
break;
|
||||
case "Store":
|
||||
results = cacheFindAll(filters.byStoreName(parsedArgs[0]));
|
||||
results = findAll(filters.byStoreName(parsedArgs[0]));
|
||||
break;
|
||||
case "ByCode":
|
||||
results = cacheFindAll(filters.byCode(...parsedArgs));
|
||||
results = findAll(filters.byCode(...parsedArgs));
|
||||
break;
|
||||
case "ModuleId":
|
||||
results = Object.keys(searchFactories(parsedArgs[0]));
|
||||
results = Object.keys(search(parsedArgs[0]));
|
||||
break;
|
||||
case "ComponentByCode":
|
||||
results = cacheFindAll(filters.componentByCode(...parsedArgs));
|
||||
results = findAll(filters.componentByCode(...parsedArgs));
|
||||
break;
|
||||
default:
|
||||
return reply("Unknown Find Type " + type);
|
||||
|
|
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||
authors: [Devs.Nuckyz],
|
||||
patches: [
|
||||
{
|
||||
find: ".Messages.BOT_CALL_IDLE_DISCONNECT",
|
||||
find: "#{intl::BOT_CALL_IDLE_DISCONNECT_2}",
|
||||
replacement: {
|
||||
match: /,?(?=\i\(this,"idleTimeout",new \i\.\i\))/,
|
||||
replace: ";return;"
|
||||
|
|
|
@ -23,12 +23,12 @@ import { Logger } from "@utils/Logger";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findByCode, findStore } from "@webpack";
|
||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
||||
import { Promisable } from "type-fest";
|
||||
|
||||
const StickersStore = findStore("StickersStore");
|
||||
const uploadEmoji = findByCode(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START");
|
||||
const StickersStore = findStoreLazy("StickersStore");
|
||||
const uploadEmoji = findByCodeLazy(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START");
|
||||
|
||||
interface Sticker {
|
||||
t: "Sticker";
|
||||
|
|
|
@ -23,12 +23,13 @@ import { ErrorCard } from "@components/ErrorCard";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Margins } from "@utils/margins";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByProps } from "@webpack";
|
||||
import { findByPropsLazy, findLazy } from "@webpack";
|
||||
import { Forms, React } from "@webpack/common";
|
||||
|
||||
import hideBugReport from "./hideBugReport.css?managed";
|
||||
|
||||
const KbdStyles = findByProps("key", "combo");
|
||||
const KbdStyles = findByPropsLazy("key", "combo");
|
||||
const BugReporterExperiment = findLazy(m => m?.definition?.id === "2024-09_bug_reporter");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
toolbarDevMenu: {
|
||||
|
@ -78,8 +79,8 @@ export default definePlugin({
|
|||
{
|
||||
find: "toolbar:function",
|
||||
replacement: {
|
||||
match: /\i\.isStaff\(\)/,
|
||||
replace: "true"
|
||||
match: /hasBugReporterAccess:(\i)/,
|
||||
replace: "_hasBugReporterAccess:$1=true"
|
||||
},
|
||||
predicate: () => settings.store.toolbarDevMenu
|
||||
},
|
||||
|
@ -91,10 +92,18 @@ export default definePlugin({
|
|||
match: /\i\.isDM\(\)\|\|\i\.isThread\(\)/,
|
||||
replace: "false",
|
||||
}
|
||||
},
|
||||
// enable option to always record clips even if you are not streaming
|
||||
{
|
||||
find: "isDecoupledGameClippingEnabled(){",
|
||||
replacement: {
|
||||
match: /\i\.isStaff\(\)/,
|
||||
replace: "true"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
start: () => enableStyle(hideBugReport),
|
||||
start: () => !BugReporterExperiment.getCurrentConfig().hasBugReporterAccess && enableStyle(hideBugReport),
|
||||
stop: () => disableStyle(hideBugReport),
|
||||
|
||||
settingsAboutComponent: () => {
|
||||
|
|
|
@ -22,37 +22,37 @@ import { Devs } from "@utils/constants";
|
|||
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
|
||||
import { getCurrentGuild } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCode, findByProps, findStore, webpackDependantLazy } from "@webpack";
|
||||
import definePlugin, { OptionType, Patch } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
|
||||
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||
import type { Emoji } from "@webpack/types";
|
||||
import type { Message } from "discord-types/general";
|
||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
|
||||
const StickersStore = findStore("StickersStore") as {
|
||||
const StickerStore = findStoreLazy("StickersStore") as {
|
||||
getPremiumPacks(): StickerPack[];
|
||||
getAllGuildStickers(): Map<string, Sticker[]>;
|
||||
getStickerById(id: string): Sticker | undefined;
|
||||
};
|
||||
|
||||
const UserSettingsProtoStore = findStore("UserSettingsProtoStore");
|
||||
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
||||
|
||||
const BINARY_READ_OPTIONS = findByProps("readerFactory");
|
||||
const BINARY_READ_OPTIONS = findByPropsLazy("readerFactory");
|
||||
|
||||
function searchProtoClassField(localName: string, protoClass: any) {
|
||||
const field = protoClass?.fields?.find((field: any) => field.localName === localName);
|
||||
if (!field) return;
|
||||
|
||||
const fieldGetter = Object.values<any>(field).find(value => typeof value === "function");
|
||||
const fieldGetter = Object.values(field).find(value => typeof value === "function") as any;
|
||||
return fieldGetter?.();
|
||||
}
|
||||
|
||||
const PreloadedUserSettingsActionCreators = webpackDependantLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
||||
const AppearanceSettingsActionCreators = webpackDependantLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
||||
const ClientThemeSettingsActionsCreators = webpackDependantLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
||||
const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
||||
const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
||||
const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
||||
|
||||
const isUnusableRoleSubscriptionEmoji = findByCode(".getUserIsAdmin(");
|
||||
const isUnusableRoleSubscriptionEmoji = findByCodeLazy(".getUserIsAdmin(");
|
||||
|
||||
const enum EmojiIntentions {
|
||||
REACTION,
|
||||
|
@ -194,6 +194,26 @@ const hasExternalStickerPerms = (channelId: string) => hasPermission(channelId,
|
|||
const hasEmbedPerms = (channelId: string) => hasPermission(channelId, PermissionsBits.EMBED_LINKS);
|
||||
const hasAttachmentPerms = (channelId: string) => hasPermission(channelId, PermissionsBits.ATTACH_FILES);
|
||||
|
||||
function makeBypassPatches(): Omit<Patch, "plugin"> {
|
||||
const mapping: Array<{ func: string, predicate?: () => boolean; }> = [
|
||||
{ func: "canUseCustomStickersEverywhere", predicate: () => settings.store.enableStickerBypass },
|
||||
{ func: "canUseHighVideoUploadQuality", predicate: () => settings.store.enableStreamQualityBypass },
|
||||
{ func: "canStreamQuality", predicate: () => settings.store.enableStreamQualityBypass },
|
||||
{ func: "canUseClientThemes" },
|
||||
{ func: "canUseCustomNotificationSounds" },
|
||||
{ func: "canUsePremiumAppIcons" }
|
||||
];
|
||||
|
||||
return {
|
||||
find: "canUseCustomStickersEverywhere:",
|
||||
replacement: mapping.map(({ func, predicate }) => ({
|
||||
match: new RegExp(String.raw`(?<=${func}:function\(\i(?:,\i)?\){)`),
|
||||
replace: "return true;",
|
||||
predicate
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "FakeNitro",
|
||||
authors: [Devs.Arjix, Devs.D3SOX, Devs.Ven, Devs.fawn, Devs.captain, Devs.Nuckyz, Devs.AutumnVN],
|
||||
|
@ -203,6 +223,17 @@ export default definePlugin({
|
|||
settings,
|
||||
|
||||
patches: [
|
||||
// General bypass patches
|
||||
makeBypassPatches(),
|
||||
// Patch the emoji picker in voice calls to not be bypassed by fake nitro
|
||||
{
|
||||
find: "emojiItemDisabled]",
|
||||
predicate: () => settings.store.enableEmojiBypass,
|
||||
replacement: {
|
||||
match: /CHAT/,
|
||||
replace: "STATUS"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: ".PREMIUM_LOCKED;",
|
||||
group: true,
|
||||
|
@ -243,15 +274,6 @@ export default definePlugin({
|
|||
replace: (_, rest1, rest2) => `${rest1},fakeNitroOriginal){if(!fakeNitroOriginal)return false;${rest2}`
|
||||
}
|
||||
},
|
||||
// Allow stickers to be sent everywhere
|
||||
{
|
||||
find: "canUseCustomStickersEverywhere:function",
|
||||
predicate: () => settings.store.enableStickerBypass,
|
||||
replacement: {
|
||||
match: /canUseCustomStickersEverywhere:function\(\i\){/,
|
||||
replace: "$&return true;"
|
||||
},
|
||||
},
|
||||
// Make stickers always available
|
||||
{
|
||||
find: '"SENDABLE"',
|
||||
|
@ -261,37 +283,15 @@ export default definePlugin({
|
|||
replace: "true?"
|
||||
}
|
||||
},
|
||||
// Allow streaming with high quality
|
||||
{
|
||||
find: "canUseHighVideoUploadQuality:function",
|
||||
predicate: () => settings.store.enableStreamQualityBypass,
|
||||
replacement: [
|
||||
"canUseHighVideoUploadQuality",
|
||||
"canStreamQuality",
|
||||
].map(func => {
|
||||
return {
|
||||
match: new RegExp(`${func}:function\\(\\i(?:,\\i)?\\){`, "g"),
|
||||
replace: "$&return true;"
|
||||
};
|
||||
})
|
||||
},
|
||||
// Remove boost requirements to stream with high quality
|
||||
{
|
||||
find: "STREAM_FPS_OPTION.format",
|
||||
find: "#{intl::STREAM_FPS_OPTION}",
|
||||
predicate: () => settings.store.enableStreamQualityBypass,
|
||||
replacement: {
|
||||
match: /guildPremiumTier:\i\.\i\.TIER_\d,?/g,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
// Allow client themes to be changeable
|
||||
{
|
||||
find: "canUseClientThemes:function",
|
||||
replacement: {
|
||||
match: /canUseClientThemes:function\(\i\){/,
|
||||
replace: "$&return true;"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: '"UserSettingsProtoStore"',
|
||||
replacement: [
|
||||
|
@ -350,13 +350,13 @@ export default definePlugin({
|
|||
{
|
||||
// Filter attachments to remove fake nitro stickers or emojis
|
||||
predicate: () => settings.store.transformStickers,
|
||||
match: /renderAttachments\(\i\){let{attachments:(\i).+?;/,
|
||||
match: /renderAttachments\(\i\){.+?{attachments:(\i).+?;/,
|
||||
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: ".Messages.STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION.format",
|
||||
find: "#{intl::STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION}",
|
||||
predicate: () => settings.store.transformStickers,
|
||||
replacement: [
|
||||
{
|
||||
|
@ -381,7 +381,7 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION",
|
||||
find: "#{intl::EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION}",
|
||||
predicate: () => settings.store.transformEmojis,
|
||||
replacement: {
|
||||
// Add the fake nitro emoji notice
|
||||
|
@ -389,14 +389,6 @@ export default definePlugin({
|
|||
replace: (_, reactNode, props) => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!${props}?.fakeNitroNode?.fake)`
|
||||
}
|
||||
},
|
||||
// Allow using custom app icons
|
||||
{
|
||||
find: "canUsePremiumAppIcons:function",
|
||||
replacement: {
|
||||
match: /canUsePremiumAppIcons:function\(\i\){/,
|
||||
replace: "$&return true;"
|
||||
}
|
||||
},
|
||||
// Separate patch for allowing using custom app icons
|
||||
{
|
||||
find: /\.getCurrentDesktopIcon.{0,25}\.isPremium/,
|
||||
|
@ -412,14 +404,6 @@ export default definePlugin({
|
|||
match: /(?<=type:"(?:SOUNDBOARD_SOUNDS_RECEIVED|GUILD_SOUNDBOARD_SOUND_CREATE|GUILD_SOUNDBOARD_SOUND_UPDATE|GUILD_SOUNDBOARD_SOUNDS_UPDATE)".+?available:)\i\.available/g,
|
||||
replace: "true"
|
||||
}
|
||||
},
|
||||
// Allow using custom notification sounds
|
||||
{
|
||||
find: "canUseCustomNotificationSounds:function",
|
||||
replacement: {
|
||||
match: /canUseCustomNotificationSounds:function\(\i\){/,
|
||||
replace: "$&return true;"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -566,8 +550,8 @@ export default definePlugin({
|
|||
|
||||
const gifMatch = child.props.href.match(fakeNitroGifStickerRegex);
|
||||
if (gifMatch) {
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
|
||||
if (StickersStore.getStickerById(gifMatch[1])) return null;
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||
if (StickerStore.getStickerById(gifMatch[1])) return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -655,7 +639,7 @@ export default definePlugin({
|
|||
url = new URL(item);
|
||||
} catch { }
|
||||
|
||||
const stickerName = StickersStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker";
|
||||
const stickerName = StickerStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker";
|
||||
stickers.push({
|
||||
format_type: 1,
|
||||
id: imgMatch[1],
|
||||
|
@ -668,9 +652,9 @@ export default definePlugin({
|
|||
|
||||
const gifMatch = item.match(fakeNitroGifStickerRegex);
|
||||
if (gifMatch) {
|
||||
if (!StickersStore.getStickerById(gifMatch[1])) continue;
|
||||
if (!StickerStore.getStickerById(gifMatch[1])) continue;
|
||||
|
||||
const stickerName = StickersStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker";
|
||||
const stickerName = StickerStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker";
|
||||
stickers.push({
|
||||
format_type: 2,
|
||||
id: gifMatch[1],
|
||||
|
@ -703,8 +687,8 @@ export default definePlugin({
|
|||
|
||||
const gifMatch = embed.url!.match(fakeNitroGifStickerRegex);
|
||||
if (gifMatch) {
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
|
||||
if (StickersStore.getStickerById(gifMatch[1])) return true;
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||
if (StickerStore.getStickerById(gifMatch[1])) return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -721,8 +705,8 @@ export default definePlugin({
|
|||
|
||||
const match = attachment.url.match(fakeNitroGifStickerRegex);
|
||||
if (match) {
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
|
||||
if (StickersStore.getStickerById(match[1])) return false;
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||
if (StickerStore.getStickerById(match[1])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -878,7 +862,7 @@ export default definePlugin({
|
|||
if (!s.enableStickerBypass)
|
||||
break stickerBypass;
|
||||
|
||||
const sticker = StickersStore.getStickerById(extra.stickers?.[0]!);
|
||||
const sticker = StickerStore.getStickerById(extra.stickers?.[0]!);
|
||||
if (!sticker)
|
||||
break stickerBypass;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import { Margins } from "@utils/margins";
|
|||
import { classes, copyWithToast } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { extractAndLoadChunksLazy, findComponentByCode } from "@webpack";
|
||||
import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Flex, Forms, React, Text, UserProfileStore, UserStore, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import virtualMerge from "virtual-merge";
|
||||
|
@ -108,10 +108,10 @@ interface ProfileModalProps {
|
|||
isTryItOutFlow: boolean;
|
||||
}
|
||||
|
||||
const ColorPicker = findComponentByCode<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
|
||||
const ProfileModal = findComponentByCode<ProfileModalProps>("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER");
|
||||
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
|
||||
const ProfileModal = findComponentByCodeLazy<ProfileModalProps>("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER");
|
||||
|
||||
const requireColorPicker = extractAndLoadChunksLazy("USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format");
|
||||
const requireColorPicker = extractAndLoadChunksLazy(["#{intl::USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON}"], /createPromise:\(\)=>\i\.\i(\("?.+?"?\)).then\(\i\.bind\(\i,"?(.+?)"?\)\)/);
|
||||
|
||||
export default definePlugin({
|
||||
name: "FakeProfileThemes",
|
||||
|
@ -126,9 +126,9 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: ".USER_SETTINGS_RESET_PROFILE_THEME",
|
||||
find: "#{intl::USER_SETTINGS_RESET_PROFILE_THEME}",
|
||||
replacement: {
|
||||
match: /RESET_PROFILE_THEME}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/,
|
||||
match: /#{intl::USER_SETTINGS_RESET_PROFILE_THEME}\)}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/,
|
||||
replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByProps } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { useCallback, useEffect, useRef, useState } from "@webpack/common";
|
||||
|
||||
interface SearchBarComponentProps {
|
||||
|
@ -35,7 +35,7 @@ interface SearchBarComponentProps {
|
|||
}
|
||||
|
||||
type TSearchBarComponent =
|
||||
React.ComponentType<SearchBarComponentProps> & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; };
|
||||
React.FC<SearchBarComponentProps> & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; };
|
||||
|
||||
interface Gif {
|
||||
format: number;
|
||||
|
@ -60,7 +60,7 @@ interface Instance {
|
|||
}
|
||||
|
||||
|
||||
const containerClasses: { searchBar: string; } = findByProps("searchBar", "searchBarFullRow");
|
||||
const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchBarFullRow");
|
||||
|
||||
export const settings = definePluginSettings({
|
||||
searchOption: {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue