Merge branch 'dev'

This commit is contained in:
thororen1234 2024-11-17 18:08:08 -05:00
commit ad5b8c85b8
20 changed files with 499 additions and 245 deletions

View file

@ -66,7 +66,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- GodMode by Tolgchu
- GoodPerson by nin0dev & mantikafasi
- GoogleThat by Samwich
- Grammar by Samwich
- GrammarFix by unstream
- HideChatButtons by iamme
- HideMessage by Hanzy
@ -108,6 +107,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- NotificationTitle by Kyuuhachi
- OnePingPerDM by ProffDea
- PinIcon by iamme
- PolishWording (Grammar) by Samwich
- PlatformSpoofer by Drag
- PurgeMessages by bhop & nyx
- QuestionMarkReplacement by nyx
@ -143,7 +143,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- VCSupport by thororen
- VencordRPC by AutumnVN
- VideoSpeed by Samwich
- ViewRaw2 by Kyuuhachi
- ViewRawVariant (ViewRaw2) by Kyuuhachi
- VoiceChannelLog by Sqaaakoi & maintained by thororen
- VoiceChatUtilities by D3SOX
- WebpackTarball by Kyuuhachi

View file

@ -27,11 +27,11 @@ export async function loadLazyChunks() {
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g);
const foundCssDebuggingLoad = false;
let foundCssDebuggingLoad = false;
async function searchAndLoadLazyChunks(factoryCode: string) {
// Workaround to avoid loading the CSS debugging chunk which turns the app pink
const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : factoryCode.includes(".cssDebuggingEnabled&&");
const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&"));
const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>();

View file

@ -0,0 +1,150 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { migratePluginSettings } from "@api/Settings";
import { CodeBlock } from "@components/CodeBlock";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import { Margins } from "@utils/margins";
import {
closeModal,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalRoot,
ModalSize,
openModal,
} from "@utils/modal";
import definePlugin from "@utils/types";
import { Forms, Menu, Text } from "@webpack/common";
import { Message } from "discord-types/general";
migratePluginSettings("ViewRawVariant", "ViewRaw2");
type CustomMessage = Message & {
editHistory?: any;
deleted?: any;
firstEditTimestamp?: any;
};
const CopyIcon = () => {
return (
<svg
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
width="18"
height="18"
>
<path d="M12.9297 3.25007C12.7343 3.05261 12.4154 3.05226 12.2196 3.24928L11.5746 3.89824C11.3811 4.09297 11.3808 4.40733 11.5739 4.60245L16.5685 9.64824C16.7614 9.84309 16.7614 10.1569 16.5685 10.3517L11.5739 15.3975C11.3808 15.5927 11.3811 15.907 11.5746 16.1017L12.2196 16.7507C12.4154 16.9477 12.7343 16.9474 12.9297 16.7499L19.2604 10.3517C19.4532 10.1568 19.4532 9.84314 19.2604 9.64832L12.9297 3.25007Z" />
<path d="M8.42616 4.60245C8.6193 4.40733 8.61898 4.09297 8.42545 3.89824L7.78047 3.24928C7.58466 3.05226 7.26578 3.05261 7.07041 3.25007L0.739669 9.64832C0.5469 9.84314 0.546901 10.1568 0.739669 10.3517L7.07041 16.7499C7.26578 16.9474 7.58465 16.9477 7.78047 16.7507L8.42545 16.1017C8.61898 15.907 8.6193 15.5927 8.42616 15.3975L3.43155 10.3517C3.23869 10.1569 3.23869 9.84309 3.43155 9.64824L8.42616 4.60245Z" />
</svg>
);
};
function cleanMessage(msg: CustomMessage) {
const author = { ...msg.author } as any;
delete author.email;
delete author.phone;
delete author.mfaEnabled;
delete author.personalConnectionId;
delete msg.editHistory;
delete msg.deleted;
delete msg.firstEditTimestamp;
return { ...msg, author };
}
function openViewRawModal(obj: any, type: string, isMessage?: boolean) {
const key = openModal((props) => (
<ErrorBoundary>
<ModalRoot {...props} size={ModalSize.LARGE}>
<ModalHeader>
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>
View Raw {type}
</Text>
<ModalCloseButton onClick={() => closeModal(key)} />
</ModalHeader>
<ModalContent>
<div style={{ padding: "16px 0" }}>
{isMessage && (
<>
<Forms.FormTitle tag="h5">
Content
</Forms.FormTitle>
<CodeBlock
content={obj.content}
lang="markdown"
/>
<Forms.FormDivider
className={Margins.bottom20}
/>
</>
)}
<Forms.FormTitle tag="h5">{type} Data</Forms.FormTitle>
<CodeBlock
content={JSON.stringify(obj, null, 4)}
lang="json"
/>
</div>
</ModalContent>
</ModalRoot>
</ErrorBoundary>
));
}
function makeContextCallback(
name: string,
action: (any) => void,
): NavContextMenuPatchCallback {
return (children, props) => {
if (props.label === getIntlMessage("CHANNEL_ACTIONS_MENU_LABEL"))
return; // random shit like notification settings
const value = props[name];
if (!value) return;
const lastChild = children.at(-1);
if (lastChild?.key === "developer-actions") {
const p = lastChild.props;
if (!Array.isArray(p.children)) p.children = [p.children];
children = p.children;
}
children.push(
<Menu.MenuItem
id={`c98-view-${name}-raw`}
label="View Raw"
action={() => action(value)}
icon={CopyIcon}
/>,
);
};
}
export default definePlugin({
name: "ViewRawVariant",
description:
"Copy/View raw content of any message, channel, or guild, but show in the right click menu.",
authors: [Devs.KingFish, Devs.Ven, Devs.rad, Devs.ImLvna, Devs.Kyuuhachi],
contextMenus: {
"guild-context": makeContextCallback("guild", (val) =>
openViewRawModal(val, "Guild"),
),
"channel-context": makeContextCallback("channel", (val) =>
openViewRawModal(val, "Channel"),
),
"user-context": makeContextCallback("user", (val) =>
openViewRawModal(val, "User"),
),
message: makeContextCallback("message", (val) =>
openViewRawModal(cleanMessage(val), "Message", true),
),
},
});

View file

@ -8,7 +8,7 @@ const extensionMap = {
"ogg": [".ogv", ".oga", ".ogx", ".ogm", ".spx", ".opus"],
"jpg": [".jpg", ".jpeg", ".jfif", ".jpe", ".jif", ".jfi", ".pjpeg", ".pjp"],
"svg": [".svgz"],
"mp4": [".m4v", ".m4a", ".m4r", ".m4b", ".m4p"],
"mp4": [".m4v", ".m4r", ".m4b", ".m4p"],
"mov": [".movie", ".qt"],
};

View file

@ -13,57 +13,64 @@ import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
const settings = definePluginSettings(
{
autoCapitalization: {
type: OptionType.BOOLEAN,
description: "Auto Capitalization to the first letter"
},
autoPunctuation: {
type: OptionType.BOOLEAN,
description: "Auto Punctuation at the end of a sentence"
},
autoWordReplacement: {
type: OptionType.BOOLEAN,
description: "Auto Capitalizes the first letter"
}
}
);
const settings = definePluginSettings({
autoCapitalization: {
type: OptionType.BOOLEAN,
description: "Auto Capitalization to the first letter",
},
autoPunctuation: {
type: OptionType.BOOLEAN,
description: "Auto Punctuation at the end of a sentence",
},
autoWordReplacement: {
type: OptionType.BOOLEAN,
description: "Auto Capitalizes the first letter",
},
});
const getPresend = dictionary => {
const getPresend = (dictionary) => {
const presendObject: SendListener = (_, msg) => {
msg.content = msg.content.trim();
if (!msg.content.includes("```") && /\w/.test(msg.content.charAt(0))) {
if (settings.store.autoWordReplacement) {
const re = new RegExp(
`(^|(?<=[^A-Z0-9]+))(${Object.keys(dictionary)
.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
.map((k) => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
.join("|")})((?=[^A-Z0-9]+)|$)`,
"gi"
"gi",
);
if (re !== null) {
msg.content = msg.content.replace(re, match => {
msg.content = msg.content.replace(re, (match) => {
return dictionary[match.toLowerCase()] || match;
});
}
}
if (settings.store.autoPunctuation) {
if (/[A-Z0-9]/i.test(msg.content.charAt(msg.content.length - 1))) {
if (!msg.content.startsWith("http", msg.content.lastIndexOf(" ") + 1))
if (
/[A-Z0-9]/i.test(msg.content.charAt(msg.content.length - 1))
) {
if (
!msg.content.startsWith(
"http",
msg.content.lastIndexOf(" ") + 1,
)
)
msg.content += ".";
}
}
// Ensure sentences are capitalized after punctuation
if (settings.store.autoCapitalization) {
msg.content = msg.content.replace(/([.!?])\s*(\w)/g, match =>
match.toUpperCase()
msg.content = msg.content.replace(/([.!?])\s*(\w)/g, (match) =>
match.toUpperCase(),
);
// Ensure the first character of the entire message is capitalized
if (!msg.content.startsWith("http")) {
msg.content = msg.content.charAt(0).toUpperCase() + msg.content.slice(1);
msg.content =
msg.content.charAt(0).toUpperCase() +
msg.content.slice(1);
}
}
}
@ -79,7 +86,7 @@ export default definePlugin({
settings,
async start() {
let dictionary = await fetch(
"https://raw.githubusercontent.com/wont-stream/dictionary/3d52fecd9aca5dfee0fcde0df2c2af357f977df7/index.min.json"
"https://raw.githubusercontent.com/wont-stream/dictionary/3d52fecd9aca5dfee0fcde0df2c2af357f977df7/index.min.json",
);
dictionary = await dictionary.json();

View file

@ -11,7 +11,7 @@ import { Button, Forms, React, TabBar, Text, TextArea, Toasts } from "@webpack/c
import { convert as convertLineEP, getIdFromUrl as getLineEmojiPackIdFromUrl, getStickerPackById as getLineEmojiPackById, isLineEmojiPackHtml, parseHtml as getLineEPFromHtml } from "../lineEmojis";
import { convert as convertLineSP, getIdFromUrl as getLineStickerPackIdFromUrl, getStickerPackById as getLineStickerPackById, isLineStickerPackHtml, parseHtml as getLineSPFromHtml } from "../lineStickers";
import { migrate } from "../migrate-v1";
import { isV1, migrate } from "../migrate-v1";
import { deleteStickerPack, getStickerPack, getStickerPackMetas, saveStickerPack } from "../stickers";
import { SettingsTabsKey, Sticker, StickerPack, StickerPackMeta } from "../types";
import { cl, clPicker, Mutex } from "../utils";
@ -92,6 +92,7 @@ export const Settings = () => {
const [addStickerHtml, setAddStickerHtml] = React.useState<string>("");
const [tab, setTab] = React.useState<SettingsTabsKey>(SettingsTabsKey.ADD_STICKER_PACK_URL);
const [hoveredStickerPackId, setHoveredStickerPackId] = React.useState<string | null>(null);
const [_isV1, setV1] = React.useState<boolean>(false);
async function refreshStickerPackMetas() {
setstickerPackMetas(await getStickerPackMetas());
@ -99,6 +100,9 @@ export const Settings = () => {
React.useEffect(() => {
refreshStickerPackMetas();
}, []);
React.useEffect(() => {
isV1().then(setV1);
}, []);
return (
<div className={cl("settings")}>
@ -365,7 +369,7 @@ export const Settings = () => {
<Flex flexDirection="row" style={{
alignItems: "center",
justifyContent: "center"
justifyContent: "start"
}} >
<Button
size={Button.Sizes.SMALL}
@ -399,6 +403,9 @@ export const Settings = () => {
onClick={async e => {
await migrate();
}}
style={{
display: _isV1 ? "unset" : "none"
}}
>Migrate from v1</Button>
</Flex>
</div>

View file

@ -64,11 +64,11 @@ export default definePlugin({
}
},
{
find: "#{intl::EXPRESSION_PICKER_GIF}",
find: `role:"tablist","aria-label":`,
replacement: {
match: /role:"tablist",.+?#{intl::EXPRESSION_PICKER_CATEGORIES_A11Y_LABEL}\),children:(\[.*?\)\]}\)}\):null,)(.*?closePopout:\w.*?:null)/s,
match: /role:"tablist",.*?,?"aria-label":.+?\),children:(\[.*?\)\]}\)}\):null,)(.*?closePopout:\w.*?:null)/s,
replace: m => {
const stickerTabRegex = /(\w+?)\?(\([^()]+?\))\((.{1,2}),{.{0,128},isActive:(.{1,2})===.{1,150},children:(.{1,10}#{intl::EXPRESSION_PICKER_STICKER}).*?:null/s;
const stickerTabRegex = /(\w+?)\?(\([^()]+?\))\((.{1,2}),{.{0,128},isActive:(.{1,2})===.{1,6}\.STICKER.{1,140},children:(.{1,2}\.intl\.string\(.+?\)).*?:null/s;
const res = m.replace(stickerTabRegex, (_m, canUseStickers, jsx, tabHeaderComp, currentTab, stickerText) => {
const isActive = `${currentTab}==="stickers+"`;
return (

View file

@ -57,6 +57,20 @@ function migrateStickerPack(oldStickerPack: StickerPack): StickerPack {
};
}
export async function isV1() {
const newPackMetas = await getStickerPackMetas(PACKS_KEY);
if (newPackMetas.length > 0) {
return false;
}
const oldPackMetas = await getStickerPackMetas(PACKS_KEY_OLD);
if (oldPackMetas.length === 0) {
return false;
}
return true;
}
export async function migrate() {
const newPackMetas = await getStickerPackMetas(PACKS_KEY);
if (newPackMetas.length > 0) {

View file

@ -7,11 +7,12 @@
import * as DataStore from "@api/DataStore";
import { removeRecentStickerByPackId } from "./components";
import { StickerPack, StickerPackMeta } from "./types";
import { DynamicPackSetMeta, DynamicStickerPackMeta, StickerPack, StickerPackMeta } from "./types";
import { Mutex } from "./utils";
const mutex = new Mutex();
const PACKS_KEY = "MoreStickers:Packs";
const DYNAMIC_PACK_SET_METAS_KEY = "MoreStickers:DynamicPackSetMetas";
/**
* Convert StickerPack to StickerPackMeta
@ -24,7 +25,8 @@ function stickerPackToMeta(sp: StickerPack): StickerPackMeta {
id: sp.id,
title: sp.title,
author: sp.author,
logo: sp.logo
logo: sp.logo,
dynamic: sp.dynamic,
};
}
@ -43,8 +45,11 @@ export async function saveStickerPack(sp: StickerPack, packsKey: string = PACKS_
const unlock = await mutex.lock();
try {
const packs = (await DataStore.get(packsKey) ?? null) as (StickerPackMeta[] | null);
await DataStore.set(packsKey, packs === null ? [meta] : [...packs, meta]);
let packs = (await DataStore.get(packsKey) ?? null) as (StickerPackMeta[] | null);
if (packs?.some(p => p.id === sp.id)) {
packs = packs.map(p => p.id === sp.id ? meta : p);
}
await DataStore.set(packsKey, packs === null ? [meta] : packs);
} finally {
unlock();
}
@ -106,3 +111,66 @@ export async function deleteStickerPack(id: string, packsKey: string = PACKS_KEY
})()
]);
}
// ---------------------------- Dynamic Packs ----------------------------
export async function getDynamicStickerPack(dspm: DynamicStickerPackMeta): Promise<StickerPack | null> {
const dsp = await fetch(dspm.dynamic.refreshUrl, {
headers: dspm.dynamic.authHeaders,
});
if (!dsp.ok) return null;
return await dsp.json();
}
export async function getDynamicPackSetMetas(dpsmKey: string = DYNAMIC_PACK_SET_METAS_KEY): Promise<DynamicPackSetMeta[] | null> {
return (await DataStore.get(dpsmKey)) ?? null as DynamicPackSetMeta[] | null;
}
function hasDynamicPackSetMeta(dpsm: DynamicPackSetMeta, metas?: DynamicPackSetMeta[] | null): boolean {
return !!metas?.some(m => m.id === dpsm.id);
}
export async function fetchDynamicPackSetMeta(dpsm: DynamicPackSetMeta): Promise<DynamicPackSetMeta | null> {
const dpsm_ = await fetch(dpsm.refreshUrl, {
headers: dpsm.authHeaders,
});
if (!dpsm_.ok) return null;
const dpsmData = await dpsm_.json();
return dpsmData as DynamicPackSetMeta;
}
export async function refreshDynamicPackSet(old: DynamicPackSetMeta, _new: DynamicPackSetMeta): Promise<void> {
const oldPacks = old.packs.map(p => p.id);
const newPacks = _new.packs.map(p => p.id);
const toRemove = oldPacks.filter(p => !newPacks.includes(p));
const toAdd = newPacks.filter(p => !oldPacks.includes(p));
await Promise.all([
...toRemove.map(id => deleteStickerPack(id)),
...toAdd.map(id => getDynamicStickerPack(_new.packs.find(p => p.id === id)!).then(sp => sp && saveStickerPack(sp)))
]);
}
export async function saveDynamicPackSetMeta(dpsm: DynamicPackSetMeta, dpsmKey: string = DYNAMIC_PACK_SET_METAS_KEY): Promise<void> {
let metas = (await DataStore.get(dpsmKey) ?? null) as (DynamicPackSetMeta[] | null);
if (hasDynamicPackSetMeta(dpsm, metas)) {
await refreshDynamicPackSet(metas!.find(m => m.id === dpsm.id)!, dpsm);
metas = metas!.map(m => m.id === dpsm.id ? dpsm : m);
}
const unlock = await mutex.lock();
try {
await DataStore.set(dpsmKey, metas === null ? [dpsm] : metas);
} finally {
unlock();
}
}
export async function fetchAndRefreshDynamicPackSet(dpsm: DynamicPackSetMeta, dpsmKey: string = DYNAMIC_PACK_SET_METAS_KEY): Promise<void> {
const _new = await fetchDynamicPackSetMeta(dpsm);
if (!_new) return;
await saveDynamicPackSetMeta(_new, dpsmKey);
}

View file

@ -138,6 +138,16 @@ export interface StickerPackMeta {
url?: string;
};
logo: Sticker;
dynamic?: DynamicStickerPackMeta["dynamic"];
}
export interface DynamicStickerPackMeta extends StickerPackMeta {
dynamic: {
version?: string;
refreshUrl: string;
authHeaders?: Record<string, string>;
};
}
export interface StickerPack extends StickerPackMeta {
@ -148,3 +158,18 @@ export interface FFmpegState {
ffmpeg?: FFmpeg;
isLoaded: boolean;
}
export interface DynamicPackSetMeta {
id: string;
version?: string;
title?: string;
author?: {
name: string;
url?: string;
};
packs: DynamicStickerPackMeta[];
refreshUrl: string;
authHeaders?: Record<string, string>;
}

View file

@ -4,8 +4,16 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addPreSendListener, removePreSendListener, SendListener, } from "@api/MessageEvents";
import { definePluginSettings, Settings } from "@api/Settings";
import {
addPreSendListener,
removePreSendListener,
SendListener,
} from "@api/MessageEvents";
import {
definePluginSettings,
migratePluginSettings,
Settings,
} from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
@ -13,18 +21,20 @@ const presendObject: SendListener = (channelId, msg) => {
msg.content = textProcessing(msg.content);
};
const settings = definePluginSettings(
{
blockedWords: {
type: OptionType.STRING,
description: "Words that will not be capitalised",
default: ""
}
});
migratePluginSettings("PolishWording", "Grammar");
const settings = definePluginSettings({
blockedWords: {
type: OptionType.STRING,
description: "Words that will not be capitalised",
default: "",
},
});
export default definePlugin({
name: "Grammar",
description: "Tweaks your messages to make them look nicer and have better grammar",
name: "PolishWording",
description:
"Tweaks your messages to make them look nicer and have better grammar",
authors: [Devs.Samwich],
dependencies: ["MessageEventsAPI"],
start() {
@ -33,7 +43,7 @@ export default definePlugin({
stop() {
removePreSendListener(presendObject);
},
settings
settings,
});
function textProcessing(input: string) {
@ -44,14 +54,18 @@ function textProcessing(input: string) {
}
function apostrophe(textInput: string): string {
const corrected = "wasn't, can't, don't, won't, isn't, aren't, haven't, hasn't, hadn't, doesn't, didn't, shouldn't, wouldn't, couldn't, i'm, you're, he's, she's, it's, they're, that's, who's, what's, there's, here's, how's, where's, when's, why's, let's, you'll, I'll, they'll, it'll, I've, you've, we've, they've, you'd, he'd, she'd, it'd, we'd, they'd, y'all".toLowerCase();
const corrected =
"wasn't, can't, don't, won't, isn't, aren't, haven't, hasn't, hadn't, doesn't, didn't, shouldn't, wouldn't, couldn't, i'm, you're, he's, she's, it's, they're, that's, who's, what's, there's, here's, how's, where's, when's, why's, let's, you'll, I'll, they'll, it'll, I've, you've, we've, they've, you'd, he'd, she'd, it'd, we'd, they'd, y'all".toLowerCase();
const words: string[] = corrected.split(", ");
const wordsInputted = textInput.split(" ");
wordsInputted.forEach(element => {
words.forEach(wordelement => {
wordsInputted.forEach((element) => {
words.forEach((wordelement) => {
if (removeApostrophes(wordelement) === element.toLowerCase()) {
wordsInputted[wordsInputted.indexOf(element)] = restoreCap(wordelement, getCapData(element));
wordsInputted[wordsInputted.indexOf(element)] = restoreCap(
wordelement,
getCapData(element),
);
}
});
});
@ -64,7 +78,6 @@ function getCapData(str: string) {
booleanArray.push(char === char.toUpperCase());
}
return booleanArray;
}
function removeApostrophes(str: string): string {
@ -90,20 +103,22 @@ function restoreCap(str: string, data: boolean[]): string {
}
function cap(textInput: string): string {
const sentences = textInput.split(/(?<=\w\.)\s/);
const blockedWordsArray: string[] = Settings.plugins.Grammar.blockedWords.split(", ");
const blockedWordsArray: string[] =
Settings.plugins.PolishWording.blockedWords.split(", ");
return sentences.map(element => {
if (!blockedWordsArray.some(word => element.toLowerCase().startsWith(word.toLowerCase()))) {
return element.charAt(0).toUpperCase() + element.slice(1);
}
else {
return element;
}
}).join(" ");
return sentences
.map((element) => {
if (
!blockedWordsArray.some((word) =>
element.toLowerCase().startsWith(word.toLowerCase()),
)
) {
return element.charAt(0).toUpperCase() + element.slice(1);
} else {
return element;
}
})
.join(" ");
}

View file

@ -138,11 +138,12 @@ export default definePlugin({
}
},
{
find: /"aria-label":.{0,1}\.\i\.string\(\i\.\i#{intl::STATUS_MENU_LABEL}/,
find: "#{intl::STATUS_MENU_LABEL}",
replacement: {
match: /!\i&&(.{0,15}\i\.Fragment.{0,55}null==(\i).{0,200}customEmojiPlaceholder\}\),onClick:([^}]+}))/,
match: /!\i\i&&(.{0,20}\i\.Fragment.{0,100}null==(\i).{0,200}customEmojiPlaceholder\}\),onClick:(.*?}))/,
replace: "$self.render($2, $3),false&&$1"
}
},
all: true
}
],
render(status: null | { emoji: Emoji | null; }, openCustomStatusModal: () => void) {

View file

@ -1,103 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { CodeBlock } from "@components/CodeBlock";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import { Margins } from "@utils/margins";
import { closeModal, ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
import definePlugin from "@utils/types";
import { Forms, Menu, Text } from "@webpack/common";
import { Message } from "discord-types/general";
type CustomMessage = Message & { editHistory?: any; deleted?: any; firstEditTimestamp?: any; };
const CopyIcon = () => {
return <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" width="18" height="18">
<path d="M12.9297 3.25007C12.7343 3.05261 12.4154 3.05226 12.2196 3.24928L11.5746 3.89824C11.3811 4.09297 11.3808 4.40733 11.5739 4.60245L16.5685 9.64824C16.7614 9.84309 16.7614 10.1569 16.5685 10.3517L11.5739 15.3975C11.3808 15.5927 11.3811 15.907 11.5746 16.1017L12.2196 16.7507C12.4154 16.9477 12.7343 16.9474 12.9297 16.7499L19.2604 10.3517C19.4532 10.1568 19.4532 9.84314 19.2604 9.64832L12.9297 3.25007Z" />
<path d="M8.42616 4.60245C8.6193 4.40733 8.61898 4.09297 8.42545 3.89824L7.78047 3.24928C7.58466 3.05226 7.26578 3.05261 7.07041 3.25007L0.739669 9.64832C0.5469 9.84314 0.546901 10.1568 0.739669 10.3517L7.07041 16.7499C7.26578 16.9474 7.58465 16.9477 7.78047 16.7507L8.42545 16.1017C8.61898 15.907 8.6193 15.5927 8.42616 15.3975L3.43155 10.3517C3.23869 10.1569 3.23869 9.84309 3.43155 9.64824L8.42616 4.60245Z" />
</svg>;
};
function cleanMessage(msg: CustomMessage) {
const author = { ...msg.author } as any;
delete author.email;
delete author.phone;
delete author.mfaEnabled;
delete author.personalConnectionId;
delete msg.editHistory;
delete msg.deleted;
delete msg.firstEditTimestamp;
return { ...msg, author };
}
function openViewRawModal(obj: any, type: string, isMessage?: boolean) {
const key = openModal(props => (
<ErrorBoundary>
<ModalRoot {...props} size={ModalSize.LARGE}>
<ModalHeader>
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>View Raw {type}</Text>
<ModalCloseButton onClick={() => closeModal(key)} />
</ModalHeader>
<ModalContent>
<div style={{ padding: "16px 0" }}>
{isMessage && (
<>
<Forms.FormTitle tag="h5">Content</Forms.FormTitle>
<CodeBlock content={obj.content} lang="markdown" />
<Forms.FormDivider className={Margins.bottom20} />
</>
)}
<Forms.FormTitle tag="h5">{type} Data</Forms.FormTitle>
<CodeBlock content={JSON.stringify(obj, null, 4)} lang="json" />
</div>
</ModalContent >
</ModalRoot >
</ErrorBoundary >
));
}
function makeContextCallback(name: string, action: (any) => void): NavContextMenuPatchCallback {
return (children, props) => {
if (props.label === getIntlMessage("CHANNEL_ACTIONS_MENU_LABEL")) return; // random shit like notification settings
const value = props[name];
if (!value) return;
const lastChild = children.at(-1);
if (lastChild?.key === "developer-actions") {
const p = lastChild.props;
if (!Array.isArray(p.children))
p.children = [p.children];
children = p.children;
}
children.push(
<Menu.MenuItem
id={`c98-view-${name}-raw`}
label="View Raw"
action={() => action(value)}
icon={CopyIcon}
/>
);
};
}
export default definePlugin({
name: "ViewRaw2",
description: "Copy and view the raw content/data of any message, channel or guild",
authors: [Devs.KingFish, Devs.Ven, Devs.rad, Devs.ImLvna, Devs.Kyuuhachi],
contextMenus: {
"guild-context": makeContextCallback("guild", val => openViewRawModal(val, "Guild")),
"channel-context": makeContextCallback("channel", val => openViewRawModal(val, "Channel")),
"user-context": makeContextCallback("user", val => openViewRawModal(val, "User")),
"message": makeContextCallback("message", val => openViewRawModal(cleanMessage(val), "Message", true)),
}
});

View file

@ -107,7 +107,7 @@ export default definePlugin({
try {
if (!hasCrashedOnce) {
hasCrashedOnce = true;
maybePromptToUpdate("Uh oh, Discord has just crashed... but good news, there is a Vencord update available that might fix this issue! Would you like to update now?", true);
maybePromptToUpdate("Uh oh, Discord has just crashed... but good news, there is a Equicord update available that might fix this issue! Would you like to update now?", true);
}
} catch { }

View file

@ -20,7 +20,7 @@ import { AvatarDecorationModalPreview } from "../components";
const FileUpload = findComponentByCodeLazy("fileUploadInput,");
const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE=3]="POSITIVE', {
HelpMessageTypes: filters.byProps("POSITIVE", "WARNING"),
HelpMessageTypes: filters.byProps("POSITIVE", "WARNING", "INFO"),
HelpMessage: filters.byCode(".iconDiv")
});
@ -119,8 +119,8 @@ function CreateDecorationModal(props: ModalProps) {
/>
</div>
</div>
<Forms.FormText type="description" className={Margins.bottom16}>
<br />You can receive updates on your decoration's review by joining <Link
<HelpMessage messageType={HelpMessageTypes.INFO}>
To receive updates on your decoration's review, join <Link
href={`https://discord.gg/${INVITE_KEY}`}
onClick={async e => {
e.preventDefault();
@ -138,8 +138,8 @@ function CreateDecorationModal(props: ModalProps) {
}}
>
Decor's Discord server
</Link>.
</Forms.FormText>
</Link> and allow direct messages.
</HelpMessage>
</ErrorBoundary>
</ModalContent>
<ModalFooter className={cl("modal-footer")}>

View file

@ -30,10 +30,10 @@ export default definePlugin({
{
find: ".removeMosaicItemHoverButton),",
replacement: {
match: /\.nonMediaMosaicItem\]:!(\i).{0,50}?children:\[\S,(\S)/,
replace: "$&,$1&&$2&&$self.renderPiPButton(),"
},
},
match: /\.nonMediaMosaicItem\]:.{0,40}children:\[(?<=showDownload:(\i).+?isVisualMediaType:(\i).+?)/,
replace: "$&$1&&$2&&$self.renderPiPButton(),"
}
}
],
renderPiPButton: ErrorBoundary.wrap(() => {

View file

@ -20,6 +20,7 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { makeRange } from "@components/PluginSettings/components";
import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy } from "@webpack";
import { ChannelStore, GuildMemberStore, GuildStore } from "@webpack/common";
@ -51,6 +52,12 @@ const settings = definePluginSettings({
description: "Show role colors in the reactors list",
restartNeeded: true
},
pollResults: {
type: OptionType.BOOLEAN,
default: true,
description: "Show role colors in the poll results",
restartNeeded: true
},
colorChatMessages: {
type: OptionType.BOOLEAN,
default: false,
@ -62,14 +69,15 @@ const settings = definePluginSettings({
description: "Intensity of message coloring.",
markers: makeRange(0, 100, 10),
default: 30
},
}
});
export default definePlugin({
name: "RoleColorEverywhere",
authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN, Devs.Kyuuhachi],
authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN, Devs.Kyuuhachi, Devs.jamesbt365],
description: "Adds the top role color anywhere possible",
settings,
patches: [
// Chat Mentions
{
@ -77,100 +85,106 @@ export default definePlugin({
replacement: [
{
match: /onContextMenu:\i,color:\i,\.\.\.\i(?=,children:)(?<=user:(\i),channel:(\i).{0,500}?)/,
replace: "$&,color:$self.getUserColor($1?.id,{channelId:$2?.id})"
replace: "$&,color:$self.getColorInt($1?.id,$2?.id)"
}
],
predicate: () => settings.store.chatMentions,
predicate: () => settings.store.chatMentions
},
// Slate
{
find: ".userTooltip,children",
replacement: [
{
match: /let\{id:(\i),guildId:(\i)[^}]*\}.*?\.\i,{(?=children)/,
replace: "$&color:$self.getUserColor($1,{guildId:$2}),"
match: /let\{id:(\i),guildId:\i,channelId:(\i)[^}]*\}.*?\.\i,{(?=children)/,
replace: "$&color:$self.getColorInt($1,$2),"
}
],
predicate: () => settings.store.chatMentions,
predicate: () => settings.store.chatMentions
},
// Member List Role Headers
{
find: 'tutorialId:"whos-online',
replacement: [
{
match: /null,\i," — ",\i\]/,
replace: "null,$self.roleGroupColor(arguments[0])]"
replace: "null,$self.RoleGroupColor(arguments[0])]"
},
],
predicate: () => settings.store.memberList,
predicate: () => settings.store.memberList
},
{
find: "#{intl::THREAD_BROWSER_PRIVATE}",
replacement: [
{
match: /children:\[\i," — ",\i\]/,
replace: "children:[$self.roleGroupColor(arguments[0])]"
replace: "children:[$self.RoleGroupColor(arguments[0])]"
},
],
predicate: () => settings.store.memberList,
predicate: () => settings.store.memberList
},
// Voice Users
{
find: "renderPrioritySpeaker",
find: "renderPrioritySpeaker(){",
replacement: [
{
match: /renderName\(\){.+?usernameSpeaking\]:.+?(?=children)/,
replace: "$&...$self.getVoiceProps(this.props),"
replace: "$&style:$self.getColorStyle(this?.props?.user?.id,this?.props?.guildId),"
}
],
predicate: () => settings.store.voiceUsers,
predicate: () => settings.store.voiceUsers
},
// Reaction List
{
find: ".reactorDefault",
replacement: {
match: /,onContextMenu:e=>.{0,15}\((\i),(\i),(\i)\).{0,250}tag:"strong"/,
replace: "$&,style:{color:$self.getColor($2?.id,$1)}"
match: /,onContextMenu:\i=>.{0,15}\((\i),(\i),(\i)\).{0,250}tag:"strong"/,
replace: "$&,style:$self.getColorStyle($2?.id,$1?.channel?.id)"
},
predicate: () => settings.store.reactorsList,
},
// Poll Results
{
find: ",reactionVoteCounts",
replacement: {
match: /\.nickname,(?=children:)/,
replace: "$&style:$self.getColorStyle(arguments[0]?.user?.id,arguments[0]?.channel?.id),"
},
predicate: () => settings.store.pollResults
},
// Messages
{
find: "#{intl::MESSAGE_EDITED}",
replacement: {
match: /(?<=isUnsupported\]:(\i)\.isUnsupported\}\),)(?=children:\[)/,
replace: "style:{color:$self.useMessageColor($1)},"
replace: "style:$self.useMessageColorStyle($1),"
},
predicate: () => settings.store.colorChatMessages,
},
predicate: () => settings.store.colorChatMessages
}
],
settings,
getColor(userId: string, { channelId, guildId }: { channelId?: string; guildId?: string; }) {
if (!(guildId ??= ChannelStore.getChannel(channelId!)?.guild_id)) return null;
return GuildMemberStore.getMember(guildId, userId)?.colorString ?? null;
getColorString(userId: string, channelOrGuildId: string) {
try {
const guildId = ChannelStore.getChannel(channelOrGuildId)?.guild_id ?? GuildStore.getGuild(channelOrGuildId)?.id;
if (guildId == null) return null;
return GuildMemberStore.getMember(guildId, userId)?.colorString ?? null;
} catch (e) {
new Logger("RoleColorEverywhere").error("Failed to get color string", e);
}
return null;
},
getUserColor(userId: string, ids: { channelId?: string; guildId?: string; }) {
const colorString = this.getColor(userId, ids);
getColorInt(userId: string, channelOrGuildId: string) {
const colorString = this.getColorString(userId, channelOrGuildId);
return colorString && parseInt(colorString.slice(1), 16);
},
roleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => {
const role = GuildStore.getRole(guildId, id);
getColorStyle(userId: string, channelOrGuildId: string) {
const colorString = this.getColorString(userId, channelOrGuildId);
return (
<span style={{
color: role?.colorString,
fontWeight: "unset",
letterSpacing: ".05em"
}}>
{title ?? label} &mdash; {count}
</span>
);
}, { noop: true }),
getVoiceProps({ user: { id: userId }, guildId }: { user: { id: string; }; guildId: string; }) {
return {
style: {
color: this.getColor(userId, { guildId })
}
return colorString && {
color: colorString
};
},
@ -178,11 +192,36 @@ export default definePlugin({
try {
const { messageSaturation } = settings.use(["messageSaturation"]);
const author = useMessageAuthor(message);
if (author.colorString !== undefined && messageSaturation !== 0)
if (author.colorString != null && messageSaturation !== 0) {
return `color-mix(in oklab, ${author.colorString} ${messageSaturation}%, var(--text-normal))`;
}
} catch (e) {
console.error("[RCE] failed to get message color", e);
new Logger("RoleColorEverywhere").error("Failed to get message color", e);
}
return undefined;
return null;
},
useMessageColorStyle(message: any) {
const color = this.useMessageColor(message);
return color && {
color
};
},
RoleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => {
const role = GuildStore.getRole(guildId, id);
return role != null && (
<span style={{
color: role.colorString,
fontWeight: "unset",
letterSpacing: ".05em"
}}>
{title ?? label} &mdash; {count}
</span>
);
}, { noop: true })
});

View file

@ -1,6 +1,6 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors
* Copyright (c) 2024 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
@ -18,9 +18,9 @@
import { IShikiTheme } from "@vap/shiki";
export const SHIKI_REPO = "shikijs/shiki";
export const SHIKI_REPO_COMMIT = "0b28ad8ccfbf2615f2d9d38ea8255416b8ac3043";
export const shikiRepoTheme = (name: string) => `https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/shiki/themes/${name}.json`;
export const SHIKI_REPO = "shikijs/textmate-grammars-themes";
export const SHIKI_REPO_COMMIT = "2d87559c7601a928b9f7e0f0dda243d2fb6d4499";
export const shikiRepoTheme = (name: string) => `https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/tm-themes/themes/${name}.json`;
export const themes = {
// Default
@ -30,33 +30,59 @@ export const themes = {
MaterialCandy: "https://raw.githubusercontent.com/millsp/material-candy/master/material-candy.json",
// More from Shiki repo
Andromeeda: shikiRepoTheme("andromeeda"),
AuroraX: shikiRepoTheme("aurora-x"),
AyuDark: shikiRepoTheme("ayu-dark"),
CatppuccinLatte: shikiRepoTheme("catppuccin-latte"),
CatppuccinFrappe: shikiRepoTheme("catppuccin-frappe"),
CatppuccinMacchiato: shikiRepoTheme("catppuccin-macchiato"),
CatppuccinMocha: shikiRepoTheme("catppuccin-mocha"),
DraculaSoft: shikiRepoTheme("dracula-soft"),
Dracula: shikiRepoTheme("dracula"),
EverforestDark: shikiRepoTheme("everforest-dark"),
EverforestLight: shikiRepoTheme("everforest-light"),
GithubDarkDefault: shikiRepoTheme("github-dark-default"),
GithubDarkDimmed: shikiRepoTheme("github-dark-dimmed"),
GithubDarkHighContrast: shikiRepoTheme("github-dark-high-contrast"),
GithubDark: shikiRepoTheme("github-dark"),
GithubLightDefault: shikiRepoTheme("github-light-default"),
GithubLightHighContrast: shikiRepoTheme("github-light-high-contrast"),
GithubLight: shikiRepoTheme("github-light"),
Houston: shikiRepoTheme("houston"),
KanagawaDragon: shikiRepoTheme("kanagawa-dragon"),
KanagawaLotus: shikiRepoTheme("kanagawa-lotus"),
KanagawaWave: shikiRepoTheme("kanagawa-wave"),
LaserWave: shikiRepoTheme("laserwave"),
LightPlus: shikiRepoTheme("light-plus"),
MaterialDarker: shikiRepoTheme("material-darker"),
MaterialDefault: shikiRepoTheme("material-default"),
MaterialLighter: shikiRepoTheme("material-lighter"),
MaterialOcean: shikiRepoTheme("material-ocean"),
MaterialPalenight: shikiRepoTheme("material-palenight"),
MaterialDarker: shikiRepoTheme("material-theme-darker"),
MaterialDefault: shikiRepoTheme("material-theme"),
MaterialLighter: shikiRepoTheme("material-theme-lighter"),
MaterialOcean: shikiRepoTheme("material-theme-ocean"),
MaterialPalenight: shikiRepoTheme("material-theme-palenight"),
MinDark: shikiRepoTheme("min-dark"),
MinLight: shikiRepoTheme("min-light"),
Monokai: shikiRepoTheme("monokai"),
NightOwl: shikiRepoTheme("night-owl"),
Nord: shikiRepoTheme("nord"),
OneDarkPro: shikiRepoTheme("one-dark-pro"),
OneLight: shikiRepoTheme("one-light"),
Plastic: shikiRepoTheme("plastic"),
Poimandres: shikiRepoTheme("poimandres"),
Red: shikiRepoTheme("red"),
RosePineDawn: shikiRepoTheme("rose-pine-dawn"),
RosePineMoon: shikiRepoTheme("rose-pine-moon"),
RosePine: shikiRepoTheme("rose-pine"),
SlackDark: shikiRepoTheme("slack-dark"),
SlackOchin: shikiRepoTheme("slack-ochin"),
SnazzyLight: shikiRepoTheme("snazzy-light"),
SolarizedDark: shikiRepoTheme("solarized-dark"),
SolarizedLight: shikiRepoTheme("solarized-light"),
Synthwave84: shikiRepoTheme("synthwave-84"),
TokyoNight: shikiRepoTheme("tokyo-night"),
Vesper: shikiRepoTheme("vesper"),
VitesseBlack: shikiRepoTheme("vitesse-black"),
VitesseDark: shikiRepoTheme("vitesse-dark"),
VitesseLight: shikiRepoTheme("vitesse-light"),
CssVariables: shikiRepoTheme("css-variables"),
};
export const themeCache = new Map<string, IShikiTheme>();

View file

@ -155,6 +155,7 @@ export default definePlugin({
"guild-context": MakeContextCallback("Guild"),
"channel-context": MakeContextCallback("Channel"),
"thread-context": MakeContextCallback("Channel"),
"gdm-context": MakeContextCallback("Channel"),
"user-context": MakeContextCallback("User")
},

View file

@ -525,7 +525,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({
id: 721717126523781240n
},
nyx: {
name: "verticalsync",
name: "verticalsync.",
id: 1207087393929171095n
},
nekohaxx: {
@ -584,6 +584,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "SomeAspy",
id: 516750892372852754n
},
jamesbt365: {
name: "jamesbt365",
id: 158567567487795200n,
},
} satisfies Record<string, Dev>);
export const EquicordDevs = Object.freeze({