Compare commits

..

6 commits

Author SHA1 Message Date
7907bf7120
offline edition 2024-11-06 16:32:07 +09:00
4deb52028a
katex 2 katex harder 2024-11-06 16:32:07 +09:00
f713398070
katex left align 2024-11-06 16:32:07 +09:00
056a1b0e1e
katex 2024-11-06 16:32:07 +09:00
66492344a9
asdf 2024-11-06 16:32:07 +09:00
fb940489b0
owo 2024-11-06 16:32:03 +09:00
36 changed files with 211 additions and 343 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "vencord", "name": "vencord",
"private": "true", "private": "true",
"version": "1.10.7", "version": "1.10.6",
"description": "The cutest Discord client mod", "description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme", "homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": { "bugs": {

View file

@ -225,7 +225,7 @@ page.on("console", async e => {
plugin, plugin,
type, type,
id, id,
match: regex.replace(/\(\?:\[A-Za-z_\$\]\[\\w\$\]\*\)/g, "\\i"), match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"),
error: await maybeGetError(e.args()[3]) error: await maybeGetError(e.args()[3])
}); });

View file

@ -18,7 +18,7 @@
import "./iconStyles.css"; import "./iconStyles.css";
import { getIntlMessage } from "@utils/discord"; import { getIntlMessage, getTheme, Theme } from "@utils/discord";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import type { PropsWithChildren } from "react"; import type { PropsWithChildren } from "react";
@ -122,8 +122,8 @@ export function InfoIcon(props: IconProps) {
> >
<path <path
fill="currentColor" fill="currentColor"
fill-rule="evenodd" transform="translate(2 2)"
d="M23 12a11 11 0 1 1-22 0 11 11 0 0 1 22 0Zm-9.5-4.75a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Zm-.77 3.96a1 1 0 1 0-1.96-.42l-1.04 4.86a2.77 2.77 0 0 0 4.31 2.83l.24-.17a1 1 0 1 0-1.16-1.62l-.24.17a.77.77 0 0 1-1.2-.79l1.05-4.86Z" clip-rule="evenodd" d="M9,7 L11,7 L11,5 L9,5 L9,7 Z M10,18 C5.59,18 2,14.41 2,10 C2,5.59 5.59,2 10,2 C14.41,2 18,5.59 18,10 C18,14.41 14.41,18 10,18 L10,18 Z M10,4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16,4.4771525 0,10 C-1.33226763e-15,12.6521649 1.0535684,15.195704 2.92893219,17.0710678 C4.80429597,18.9464316 7.3478351,20 10,20 C12.6521649,20 15.195704,18.9464316 17.0710678,17.0710678 C18.9464316,15.195704 20,12.6521649 20,10 C20,7.3478351 18.9464316,4.80429597 17.0710678,2.92893219 C15.195704,1.0535684 12.6521649,2.22044605e-16 10,0 L10,4.4408921e-16 Z M9,15 L11,15 L11,9 L9,9 L9,15 L9,15 Z"
/> />
</Icon> </Icon>
); );
@ -211,10 +211,9 @@ export function CogWheel(props: IconProps) {
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
<path <path
clipRule="evenodd"
fill="currentColor" fill="currentColor"
fill-rule="evenodd" d="M19.738 10H22V14H19.739C19.498 14.931 19.1 15.798 18.565 16.564L20 18L18 20L16.565 18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069 19.498 8.203 19.099 7.436 18.564L6 20L4 18L5.436 16.564C4.901 15.799 4.502 14.932 4.262 14H2V10H4.262C4.502 9.068 4.9 8.202 5.436 7.436L4 6L6 4L7.436 5.436C8.202 4.9 9.068 4.502 10 4.262V2H14V4.261C14.932 4.502 15.797 4.9 16.565 5.435L18 3.999L20 5.999L18.564 7.436C19.099 8.202 19.498 9.069 19.738 10ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z"
d="M10.56 1.1c-.46.05-.7.53-.64.98.18 1.16-.19 2.2-.98 2.53-.8.33-1.79-.15-2.49-1.1-.27-.36-.78-.52-1.14-.24-.77.59-1.45 1.27-2.04 2.04-.28.36-.12.87.24 1.14.96.7 1.43 1.7 1.1 2.49-.33.8-1.37 1.16-2.53.98-.45-.07-.93.18-.99.64a11.1 11.1 0 0 0 0 2.88c.06.46.54.7.99.64 1.16-.18 2.2.19 2.53.98.33.8-.14 1.79-1.1 2.49-.36.27-.52.78-.24 1.14.59.77 1.27 1.45 2.04 2.04.36.28.87.12 1.14-.24.7-.95 1.7-1.43 2.49-1.1.8.33 1.16 1.37.98 2.53-.07.45.18.93.64.99a11.1 11.1 0 0 0 2.88 0c.46-.06.7-.54.64-.99-.18-1.16.19-2.2.98-2.53.8-.33 1.79.14 2.49 1.1.27.36.78.52 1.14.24.77-.59 1.45-1.27 2.04-2.04.28-.36.12-.87-.24-1.14-.96-.7-1.43-1.7-1.1-2.49.33-.8 1.37-1.16 2.53-.98.45.07.93-.18.99-.64a11.1 11.1 0 0 0 0-2.88c-.06-.46-.54-.7-.99-.64-1.16.18-2.2-.19-2.53-.98-.33-.8.14-1.79 1.1-2.49.36-.27.52-.78.24-1.14a11.07 11.07 0 0 0-2.04-2.04c-.36-.28-.87-.12-1.14.24-.7.96-1.7 1.43-2.49 1.1-.8-.33-1.16-1.37-.98-2.53.07-.45-.18-.93-.64-.99a11.1 11.1 0 0 0-2.88 0ZM16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"
clip-rule="evenodd"
/> />
</Icon> </Icon>
); );
@ -407,30 +406,23 @@ export function PencilIcon(props: IconProps) {
); );
} }
export function GithubIcon(props: IconProps) { const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
return ( const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
<Icon const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
{...props} const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
viewBox="-3 -3 30 30"
> export function GithubIcon(props: ImageProps) {
<path const src = getTheme() === Theme.Light
fill={props.fill || "currentColor"} ? GithubIconLight
d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.438 9.8 8.205 11.385.6.11.82-.26.82-.577v-2.17c-3.338.726-4.042-1.61-4.042-1.61-.546-1.387-1.333-1.757-1.333-1.757-1.09-.745.083-.73.083-.73 1.205.084 1.84 1.237 1.84 1.237 1.07 1.835 2.807 1.305 3.492.998.108-.775.42-1.305.763-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.467-2.38 1.235-3.22-.123-.303-.535-1.523.117-3.176 0 0 1.008-.322 3.3 1.23.957-.266 1.98-.398 3-.403 1.02.005 2.043.137 3 .403 2.29-1.552 3.297-1.23 3.297-1.23.653 1.653.24 2.873.118 3.176.77.84 1.233 1.91 1.233 3.22 0 4.61-2.803 5.625-5.475 5.92.43.37.823 1.102.823 2.222v3.293c0 .32.218.694.825.577C20.565 21.797 24 17.298 24 12c0-6.63-5.37-12-12-12z" : GithubIconDark;
/>
</Icon> return <img {...props} src={src} />;
);
} }
export function WebsiteIcon(props: IconProps) { export function WebsiteIcon(props: ImageProps) {
return ( const src = getTheme() === Theme.Light
<Icon ? WebsiteIconLight
{...props} : WebsiteIconDark;
viewBox="0 0 24 24"
> return <img {...props} src={src} />;
<path
fill={props.fill || "currentColor"}
d="M12 2C6.486 2 2 6.486 2 12s4.486 10 10 10 10-4.486 10-10S17.514 2 12 2zM4 12c0-.899.156-1.762.431-2.569L6 11l2 2v2l2 2 1 1v1.931C7.061 19.436 4 16.072 4 12zm14.33 4.873C17.677 16.347 16.687 16 16 16v-1a2 2 0 0 0-2-2h-4v-3a2 2 0 0 0 2-2V7h1a2 2 0 0 0 2-2v-.411C17.928 5.778 20 8.65 20 12a7.947 7.947 0 0 1-1.67 4.873z"
/>
</Icon>
);
} }

View file

@ -6,19 +6,16 @@
import "./LinkIconButton.css"; import "./LinkIconButton.css";
import { getTheme, Theme } from "@utils/discord";
import { MaskedLink, Tooltip } from "@webpack/common"; import { MaskedLink, Tooltip } from "@webpack/common";
import { GithubIcon, WebsiteIcon } from ".."; import { GithubIcon, WebsiteIcon } from "..";
export function GithubLinkIcon() { export function GithubLinkIcon() {
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF"; return <GithubIcon aria-hidden className={"vc-settings-modal-link-icon"} />;
return <GithubIcon aria-hidden fill={theme} className={"vc-settings-modal-link-icon"} />;
} }
export function WebsiteLinkIcon() { export function WebsiteLinkIcon() {
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF"; return <WebsiteIcon aria-hidden className={"vc-settings-modal-link-icon"} />;
return <WebsiteIcon aria-hidden fill={theme} className={"vc-settings-modal-link-icon"} />;
} }
interface Props { interface Props {

View file

@ -247,7 +247,7 @@ function FullPatchInput({ setFind, setParsedFind, setMatch, setReplacement }: Fu
} }
try { try {
const parsed = (0, eval)(`([${fullPatch}][0])`) as Patch; const parsed = (0, eval)(`(${fullPatch})`) as Patch;
if (!parsed.find) throw new Error("No 'find' field"); if (!parsed.find) throw new Error("No 'find' field");
if (!parsed.replacement) throw new Error("No 'replacement' field"); if (!parsed.replacement) throw new Error("No 'replacement' field");

View file

@ -27,12 +27,7 @@ export async function loadLazyChunks() {
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g); const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g);
let foundCssDebuggingLoad = false;
async function searchAndLoadLazyChunks(factoryCode: string) { async function searchAndLoadLazyChunks(factoryCode: string) {
// Workaround to avoid loading the CSS debugging chunk which turns the app pink
const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&"));
const lazyChunks = factoryCode.matchAll(LazyChunkRegex); const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>(); const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>();
@ -48,16 +43,6 @@ export async function loadLazyChunks() {
let invalidChunkGroup = false; let invalidChunkGroup = false;
for (const id of chunkIds) { for (const id of chunkIds) {
if (hasCssDebuggingLoad) {
if (chunkIds.length > 1) {
throw new Error("Found multiple chunks in factory that loads the CSS debugging chunk");
}
invalidChunks.add(id);
invalidChunkGroup = true;
break;
}
if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue;
const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) const isWorkerAsset = await fetch(wreq.p + wreq.u(id))

View file

@ -59,7 +59,15 @@ export default definePlugin({
replace: "$&return;" replace: "$&return;"
} }
] ]
} },
{
find: ".installedLogHooks)",
replacement: {
// if getDebugLogging() returns false, the hooks don't get installed.
match: "getDebugLogging(){",
replace: "getDebugLogging(){return false;"
}
},
], ],
startAt: StartAt.Init, startAt: StartAt.Init,

View file

@ -65,7 +65,7 @@ export default definePlugin({
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
}, },
{ {
match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}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})` replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})`
} }
] ]

View file

@ -28,18 +28,10 @@ export default definePlugin({
patches: [ patches: [
{ {
find: 'action:"EXPAND_ROLES"', find: 'action:"EXPAND_ROLES"',
replacement: [ replacement: {
{ match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/,
match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/, replace: (_, rest, setExpandedRoles) => `${rest}!0)`
replace: (_, rest, setExpandedRoles) => `${rest}!0)` }
},
{
// Fix not calculating non-expanded roles because the above patch makes the default "expanded",
// which makes the collapse button never show up and calculation never occur
match: /(?<=useLayoutEffect\(\(\)=>{if\()\i/,
replace: isExpanded => "false"
}
]
} }
] ]
}); });

View file

@ -275,16 +275,16 @@ export default definePlugin({
}, },
makeGuildsBarGuildListFilter(isBetterFolders: boolean) { makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
try {
return child => { return child => {
if (isBetterFolders) { if (isBetterFolders) {
try { return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
} catch (e) {
console.error(e);
}
} }
return true; return true;
}; };
} catch {
return true;
}
}, },
makeGuildsBarTreeFilter(isBetterFolders: boolean) { makeGuildsBarTreeFilter(isBetterFolders: boolean) {

View file

@ -110,7 +110,7 @@ const settings = definePluginSettings({
export default definePlugin({ export default definePlugin({
name: "ClientTheme", name: "ClientTheme",
authors: [Devs.Nuckyz], authors: [Devs.F53, Devs.Nuckyz],
description: "Recreation of the old client theme experiment. Add a color to your Discord client theme", description: "Recreation of the old client theme experiment. Add a color to your Discord client theme",
settings, settings,

View file

@ -147,5 +147,5 @@ export default definePlugin({
replace: "$self.NoopLogger()" replace: "$self.NoopLogger()"
} }
} }
] ],
}); });

View file

@ -93,7 +93,7 @@ export const useAuthorizationStore = proxyLazy(() => zustandCreate(
} as AuthorizationState), } as AuthorizationState),
{ {
name: "decor-auth", name: "decor-auth",
storage: indexedDBStorage, getStorage: () => indexedDBStorage,
partialize: state => ({ tokens: state.tokens }), partialize: state => ({ tokens: state.tokens }),
onRehydrateStorage: () => state => state?.init() onRehydrateStorage: () => state => state?.init()
} }

View file

@ -95,39 +95,24 @@ export const useUsersDecorationsStore = proxyLazy(() => zustandCreate((set: any,
} as UsersDecorationsState))); } as UsersDecorationsState)));
export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | null | undefined { export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | null | undefined {
try { const [decorAvatarDecoration, setDecorAvatarDecoration] = useState<string | null>(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null);
const [decorAvatarDecoration, setDecorAvatarDecoration] = useState<string | null>(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null);
useEffect(() => { useEffect(() => {
const destructor = (() => { const destructor = useUsersDecorationsStore.subscribe(
try { state => {
return useUsersDecorationsStore.subscribe( if (!user) return;
state => { const newDecorAvatarDecoration = state.getAsset(user.id);
if (!user) return; if (!newDecorAvatarDecoration) return;
const newDecorAvatarDecoration = state.getAsset(user.id); if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration);
if (!newDecorAvatarDecoration) return; }
if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration); );
}
);
} catch {
return () => { };
}
})();
try { if (user) {
if (user) { const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState();
const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState(); fetchUserDecorAvatarDecoration(user.id);
fetchUserDecorAvatarDecoration(user.id); }
} return destructor;
} catch { } }, []);
return destructor; return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null;
}, []);
return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null;
} catch (e) {
console.error(e);
}
return null;
} }

View file

@ -10,7 +10,6 @@ import { openInviteModal } from "@utils/discord";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { classes, copyWithToast } from "@utils/misc"; import { classes, copyWithToast } from "@utils/misc";
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { Queue } from "@utils/Queue";
import { findComponentByCodeLazy } from "@webpack"; import { findComponentByCodeLazy } from "@webpack";
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common"; import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
@ -50,8 +49,6 @@ interface SectionHeaderProps {
section: Section; section: Section;
} }
const fetchAuthorsQueue = new Queue();
function SectionHeader({ section }: SectionHeaderProps) { function SectionHeader({ section }: SectionHeaderProps) {
const hasSubtitle = typeof section.subtitle !== "undefined"; const hasSubtitle = typeof section.subtitle !== "undefined";
const hasAuthorIds = typeof section.authorIds !== "undefined"; const hasAuthorIds = typeof section.authorIds !== "undefined";
@ -59,18 +56,17 @@ function SectionHeader({ section }: SectionHeaderProps) {
const [authors, setAuthors] = useState<User[]>([]); const [authors, setAuthors] = useState<User[]>([]);
useEffect(() => { useEffect(() => {
fetchAuthorsQueue.push(async () => { (async () => {
if (!section.authorIds) return; if (!section.authorIds) return;
for (const authorId of section.authorIds) { for (const authorId of section.authorIds) {
const author = UserStore.getUser(authorId) ?? await UserUtils.getUser(authorId).catch(() => null); const author = UserStore.getUser(authorId) ?? await UserUtils.getUser(authorId);
if (author == null) continue;
setAuthors(authors => [...authors, author]); setAuthors(authors => [...authors, author]);
} }
}); })();
}, [section.authorIds]); }, [section.authorIds]);
return <div> return <div>
<Flex> <Flex>
<Forms.FormTitle style={{ flexGrow: 1 }}>{section.title}</Forms.FormTitle> <Forms.FormTitle style={{ flexGrow: 1 }}>{section.title}</Forms.FormTitle>

View file

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

View file

@ -310,8 +310,7 @@ function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Om
} }
function isGifUrl(url: string) { function isGifUrl(url: string) {
const u = new URL(url); return new URL(url).pathname.endsWith(".gif");
return u.pathname.endsWith(".gif") || u.searchParams.get("animated") === "true";
} }
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {

View file

@ -20,11 +20,11 @@ import { addPreEditListener, addPreSendListener, removePreEditListener, removePr
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies"; import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
import { getCurrentGuild, getEmojiURL } from "@utils/discord"; import { getCurrentGuild } from "@utils/discord";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType, Patch } from "@utils/types"; import definePlugin, { OptionType, Patch } from "@utils/types";
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; 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 { Emoji } from "@webpack/types";
import type { Message } from "discord-types/general"; import type { Message } from "discord-types/general";
import { applyPalette, GIFEncoder, quantize } from "gifenc"; import { applyPalette, GIFEncoder, quantize } from "gifenc";
@ -920,7 +920,7 @@ export default definePlugin({
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`; const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize)); const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize }));
url.searchParams.set("size", s.emojiSize.toString()); url.searchParams.set("size", s.emojiSize.toString());
url.searchParams.set("name", emoji.name); url.searchParams.set("name", emoji.name);
@ -953,7 +953,7 @@ export default definePlugin({
hasBypass = true; hasBypass = true;
const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize)); const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize }));
url.searchParams.set("size", s.emojiSize.toString()); url.searchParams.set("size", s.emojiSize.toString());
url.searchParams.set("name", emoji.name); url.searchParams.set("name", emoji.name);

View file

@ -28,7 +28,7 @@ import { getIntlMessage } from "@utils/discord";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByCodeLazy, findByPropsLazy } from "@webpack";
import { ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; import { ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
@ -43,6 +43,7 @@ interface MLMessage extends Message {
} }
const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage"); const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage");
const getMessage = findByCodeLazy('replace(/^\\n+|\\n+$/g,"")');
function addDeleteStyle() { function addDeleteStyle() {
if (Settings.plugins.MessageLogger.deleteStyle === "text") { if (Settings.plugins.MessageLogger.deleteStyle === "text") {
@ -311,33 +312,35 @@ export default definePlugin({
); );
}, },
// DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}") Messages: {
// TODO: Find a better way to generate intl messages // DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}")
DELETED_MESSAGE_COUNT: () => ({ // TODO: find a better way to generate intl messages
ast: [[ DELETED_MESSAGE_COUNT: () => ({
6, ast: [[
"count", 6,
{ "count",
"=0": ["No deleted messages"], {
one: [ "=0": ["No deleted messages"],
[ one: [
1, [
"count" 1,
"count"
],
" deleted message"
], ],
" deleted message" other: [
], [
other: [ 1,
[ "count"
1, ],
"count" " deleted messages"
], ]
" deleted messages" },
] 0,
}, "cardinal"
0, ]]
"cardinal" })
]] },
}),
patches: [ patches: [
{ {
@ -528,7 +531,7 @@ export default definePlugin({
}, },
{ {
match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\?.*?:/, match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\?.*?:/,
replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.DELETED_MESSAGE_COUNT:', replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.Messages.DELETED_MESSAGE_COUNT:',
}, },
], ],
predicate: () => Settings.plugins.MessageLogger.collapseDeleted predicate: () => Settings.plugins.MessageLogger.collapseDeleted

View file

@ -28,8 +28,8 @@ import { Message } from "discord-types/general";
const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked"); const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked");
interface MessageDeleteProps { interface MessageDeleteProps {
// Internal intl message for BLOCKED_MESSAGE_COUNT // i18n message i18n.t["+FcYMz"] if deleted, with args
collapsedReason: () => any; collapsedReason: () => any
} }
export default definePlugin({ export default definePlugin({

View file

@ -20,7 +20,7 @@ const settings = definePluginSettings({
export default definePlugin({ export default definePlugin({
name: "NoMosaic", name: "NoMosaic",
authors: [Devs.AutumnVN], authors: [Devs.AutumnVN],
description: "Removes Discord image mosaic", description: "Removes Discord new image mosaic",
tags: ["image", "mosaic", "media"], tags: ["image", "mosaic", "media"],
settings, settings,
@ -29,8 +29,8 @@ export default definePlugin({
{ {
find: '=>"IMAGE"===', find: '=>"IMAGE"===',
replacement: { replacement: {
match: /=>"IMAGE"===\i\|\|"VIDEO"===\i(?:\|\|("VISUAL_PLACEHOLDER"===\i))?;/, match: /=>"IMAGE"===\i\|\|"VIDEO"===\i;/,
replace: (_, visualPlaceholderPred) => visualPlaceholderPred != null ? `=>${visualPlaceholderPred};` : "=>false;" replace: "=>false;"
} }
}, },
{ {

View file

@ -87,7 +87,7 @@ export default definePlugin({
{ {
find: "trackAnnouncementMessageLinkClicked({", find: "trackAnnouncementMessageLinkClicked({",
replacement: { replacement: {
match: /function (\i\(\i,\i\)\{)(?=.{0,150}trusted:)/, match: /function (\i\(\i,\i\)\{)(?=.{0,100}trusted:)/,
replace: "async function $1 if(await $self.handleLink(...arguments)) return;" replace: "async function $1 if(await $self.handleLink(...arguments)) return;"
} }
}, },

View file

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

View file

@ -50,8 +50,6 @@ async function runMigrations() {
export async function syncAndRunChecks() { export async function syncAndRunChecks() {
await runMigrations(); await runMigrations();
if (UserStore.getCurrentUser() == null) return;
const [oldGuilds, oldGroups, oldFriends] = await DataStore.getMany([ const [oldGuilds, oldGroups, oldFriends] = await DataStore.getMany([
guildsKey(), guildsKey(),
groupsKey(), groupsKey(),

View file

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

View file

@ -1,6 +1,6 @@
/* /*
* Vencord, a modification for Discord's desktop app * Vencord, a modification for Discord's desktop app
* Copyright (c) 2024 Vendicated and contributors * Copyright (c) 2022 Vendicated and contributors
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -18,9 +18,9 @@
import { IShikiTheme } from "@vap/shiki"; import { IShikiTheme } from "@vap/shiki";
export const SHIKI_REPO = "shikijs/textmate-grammars-themes"; export const SHIKI_REPO = "shikijs/shiki";
export const SHIKI_REPO_COMMIT = "2d87559c7601a928b9f7e0f0dda243d2fb6d4499"; export const SHIKI_REPO_COMMIT = "0b28ad8ccfbf2615f2d9d38ea8255416b8ac3043";
export const shikiRepoTheme = (name: string) => `https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/tm-themes/themes/${name}.json`; export const shikiRepoTheme = (name: string) => `https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/shiki/themes/${name}.json`;
export const themes = { export const themes = {
// Default // Default
@ -30,59 +30,33 @@ export const themes = {
MaterialCandy: "https://raw.githubusercontent.com/millsp/material-candy/master/material-candy.json", MaterialCandy: "https://raw.githubusercontent.com/millsp/material-candy/master/material-candy.json",
// More from Shiki repo // 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"), DraculaSoft: shikiRepoTheme("dracula-soft"),
Dracula: shikiRepoTheme("dracula"), Dracula: shikiRepoTheme("dracula"),
EverforestDark: shikiRepoTheme("everforest-dark"),
EverforestLight: shikiRepoTheme("everforest-light"),
GithubDarkDefault: shikiRepoTheme("github-dark-default"),
GithubDarkDimmed: shikiRepoTheme("github-dark-dimmed"), GithubDarkDimmed: shikiRepoTheme("github-dark-dimmed"),
GithubDarkHighContrast: shikiRepoTheme("github-dark-high-contrast"),
GithubDark: shikiRepoTheme("github-dark"), GithubDark: shikiRepoTheme("github-dark"),
GithubLightDefault: shikiRepoTheme("github-light-default"),
GithubLightHighContrast: shikiRepoTheme("github-light-high-contrast"),
GithubLight: shikiRepoTheme("github-light"), 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"), LightPlus: shikiRepoTheme("light-plus"),
MaterialDarker: shikiRepoTheme("material-theme-darker"), MaterialDarker: shikiRepoTheme("material-darker"),
MaterialDefault: shikiRepoTheme("material-theme"), MaterialDefault: shikiRepoTheme("material-default"),
MaterialLighter: shikiRepoTheme("material-theme-lighter"), MaterialLighter: shikiRepoTheme("material-lighter"),
MaterialOcean: shikiRepoTheme("material-theme-ocean"), MaterialOcean: shikiRepoTheme("material-ocean"),
MaterialPalenight: shikiRepoTheme("material-theme-palenight"), MaterialPalenight: shikiRepoTheme("material-palenight"),
MinDark: shikiRepoTheme("min-dark"), MinDark: shikiRepoTheme("min-dark"),
MinLight: shikiRepoTheme("min-light"), MinLight: shikiRepoTheme("min-light"),
Monokai: shikiRepoTheme("monokai"), Monokai: shikiRepoTheme("monokai"),
NightOwl: shikiRepoTheme("night-owl"),
Nord: shikiRepoTheme("nord"), Nord: shikiRepoTheme("nord"),
OneDarkPro: shikiRepoTheme("one-dark-pro"), OneDarkPro: shikiRepoTheme("one-dark-pro"),
OneLight: shikiRepoTheme("one-light"),
Plastic: shikiRepoTheme("plastic"),
Poimandres: shikiRepoTheme("poimandres"), Poimandres: shikiRepoTheme("poimandres"),
Red: shikiRepoTheme("red"),
RosePineDawn: shikiRepoTheme("rose-pine-dawn"), RosePineDawn: shikiRepoTheme("rose-pine-dawn"),
RosePineMoon: shikiRepoTheme("rose-pine-moon"), RosePineMoon: shikiRepoTheme("rose-pine-moon"),
RosePine: shikiRepoTheme("rose-pine"), RosePine: shikiRepoTheme("rose-pine"),
SlackDark: shikiRepoTheme("slack-dark"), SlackDark: shikiRepoTheme("slack-dark"),
SlackOchin: shikiRepoTheme("slack-ochin"), SlackOchin: shikiRepoTheme("slack-ochin"),
SnazzyLight: shikiRepoTheme("snazzy-light"),
SolarizedDark: shikiRepoTheme("solarized-dark"), SolarizedDark: shikiRepoTheme("solarized-dark"),
SolarizedLight: shikiRepoTheme("solarized-light"), SolarizedLight: shikiRepoTheme("solarized-light"),
Synthwave84: shikiRepoTheme("synthwave-84"),
TokyoNight: shikiRepoTheme("tokyo-night"),
Vesper: shikiRepoTheme("vesper"),
VitesseBlack: shikiRepoTheme("vitesse-black"),
VitesseDark: shikiRepoTheme("vitesse-dark"), VitesseDark: shikiRepoTheme("vitesse-dark"),
VitesseLight: shikiRepoTheme("vitesse-light"), VitesseLight: shikiRepoTheme("vitesse-light"),
CssVariables: shikiRepoTheme("css-variables"),
}; };
export const themeCache = new Map<string, IShikiTheme>(); export const themeCache = new Map<string, IShikiTheme>();

View file

@ -103,7 +103,7 @@ export default definePlugin({
replacement: [ replacement: [
{ {
// Do not show confirmation to join a voice channel when already connected to another if clicking on a hidden voice channel // Do not show confirmation to join a voice channel when already connected to another if clicking on a hidden voice channel
match: /(?<=getIgnoredUsersForVoiceChannel\((\i)\.id\);return\()/, match: /(?<=getBlockedUsersForVoiceChannel\((\i)\.id\);return\()/,
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&` replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`
}, },
{ {

View file

@ -107,7 +107,7 @@ export default definePlugin({
predicate: () => settings.store.disableDisallowedDiscoveryFilters, predicate: () => settings.store.disableDisallowedDiscoveryFilters,
all: true, all: true,
replacement: { replacement: {
match: /\i\.\i\.get\(\{url:\i\.\i\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0,rejectWithError:!1\}\)/g, match: /\i\.\i\.get\(\{url:\i\.\i\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0\}\)/g,
replace: "Promise.resolve({ body: { valid: true } })" replace: "Promise.resolve({ body: { valid: true } })"
} }
} }

View file

@ -24,7 +24,7 @@ import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord"; import { getIntlMessage } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack"; import { findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
import { GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; import { ChannelStore, GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
import { buildSeveralUsers } from "../typingTweaks"; import { buildSeveralUsers } from "../typingTweaks";
@ -44,7 +44,7 @@ function getDisplayName(guildId: string, userId: string) {
return GuildMemberStore.getNick(guildId, userId) ?? (user as any).globalName ?? user.username; return GuildMemberStore.getNick(guildId, userId) ?? (user as any).globalName ?? user.username;
} }
function TypingIndicator({ channelId, guildId }: { channelId: string; guildId: string; }) { function TypingIndicator({ channelId }: { channelId: string; }) {
const typingUsers: Record<string, number> = useStateFromStores( const typingUsers: Record<string, number> = useStateFromStores(
[TypingStore], [TypingStore],
() => ({ ...TypingStore.getTypingUsers(channelId) as Record<string, number> }), () => ({ ...TypingStore.getTypingUsers(channelId) as Record<string, number> }),
@ -57,6 +57,7 @@ function TypingIndicator({ channelId, guildId }: { channelId: string; guildId: s
} }
); );
const currentChannelId: string = useStateFromStores([SelectedChannelStore], () => SelectedChannelStore.getChannelId()); const currentChannelId: string = useStateFromStores([SelectedChannelStore], () => SelectedChannelStore.getChannelId());
const guildId = ChannelStore.getChannel(channelId).guild_id;
if (!settings.store.includeMutedChannels) { if (!settings.store.includeMutedChannels) {
const isChannelMuted = UserGuildSettingsStore.isChannelMuted(guildId, channelId); const isChannelMuted = UserGuildSettingsStore.isChannelMuted(guildId, channelId);
@ -164,7 +165,7 @@ export default definePlugin({
find: "UNREAD_IMPORTANT:", find: "UNREAD_IMPORTANT:",
replacement: { replacement: {
match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/, match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
replace: "$&,$self.TypingIndicator($1.id,$1.getGuildId())" replace: "$&,$self.TypingIndicator($1.id)"
} }
}, },
// Theads // Theads
@ -173,14 +174,14 @@ export default definePlugin({
find: "M11 9H4C2.89543 9 2 8.10457 2 7V1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1V7C0 9.20914 1.79086 11 4 11H11C11.5523 11 12 10.5523 12 10C12 9.44771 11.5523 9 11 9Z", find: "M11 9H4C2.89543 9 2 8.10457 2 7V1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1V7C0 9.20914 1.79086 11 4 11H11C11.5523 11 12 10.5523 12 10C12 9.44771 11.5523 9 11 9Z",
replacement: { replacement: {
match: /mentionsCount:\i.+?null(?<=channel:(\i).+?)/, match: /mentionsCount:\i.+?null(?<=channel:(\i).+?)/,
replace: "$&,$self.TypingIndicator($1.id,$1.getGuildId())" replace: "$&,$self.TypingIndicator($1.id)"
} }
} }
], ],
TypingIndicator: (channelId: string, guildId: string) => ( TypingIndicator: (channelId: string) => (
<ErrorBoundary noop> <ErrorBoundary noop>
<TypingIndicator channelId={channelId} guildId={guildId} /> <TypingIndicator channelId={channelId} />
</ErrorBoundary> </ErrorBoundary>
), ),
}); });

View file

@ -129,22 +129,14 @@ export default definePlugin({
buildSeveralUsers, buildSeveralUsers,
mutateChildren(props: any, users: User[], children: any) { mutateChildren(props: any, users: User[], children: any) {
try { if (!Array.isArray(children)) return children;
if (!Array.isArray(children)) {
return children;
}
let element = 0; let element = 0;
return children.map(c => return children.map(c =>
c.type === "strong" || (typeof c !== "string" && !React.isValidElement(c)) c.type === "strong"
? <TypingUser {...props} user={users[element++]} /> ? <TypingUser {...props} user={users[element++]} />
: c : c
); );
} catch (e) {
console.error(e);
}
return children;
} }
}); });

View file

@ -209,11 +209,10 @@ export default definePlugin({
}, },
// Group DMs top small & large icon // Group DMs top small & large icon
{ {
find: '["aria-hidden"],"aria-label":', find: /\.recipients\.length>=2(?!<isMultiUserDM.{0,50})/,
replacement: { replacement: {
match: /null==\i\.icon\?.+?src:(\(0,\i\.\i\).+?\))(?=[,}])/, match: /null==\i\.icon\?.+?src:(\(0,\i\.\i\).+?\))(?=[,}])/,
// We have to check that icon is not an unread GDM in the server bar replace: (m, iconUrl) => `${m},onClick:()=>$self.openAvatar(${iconUrl})`
replace: (m, iconUrl) => `${m},onClick:()=>arguments[0]?.size!=="SIZE_48"&&$self.openAvatar(${iconUrl})`
} }
}, },
// User DMs top small icon // User DMs top small icon

View file

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

View file

@ -267,6 +267,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "arHSM", name: "arHSM",
id: 841509053422632990n id: 841509053422632990n
}, },
F53: {
name: "Cassie (Code)",
id: 280411966126948353n
},
AutumnVN: { AutumnVN: {
name: "AutumnVN", name: "AutumnVN",
id: 393694671383166998n id: 393694671383166998n
@ -516,8 +520,8 @@ export const Devs = /* #__PURE__*/ Object.freeze({
id: 721717126523781240n, id: 721717126523781240n,
}, },
nyx: { nyx: {
name: "verticalsync.", name: "verticalsync",
id: 1207087393929171095n id: 328165170536775680n
}, },
nekohaxx: { nekohaxx: {
name: "nekohaxx", name: "nekohaxx",
@ -571,14 +575,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "RamziAH", name: "RamziAH",
id: 1279957227612147747n, id: 1279957227612147747n,
}, },
SomeAspy: { SomeAspy: {
name: "SomeAspy", name: "SomeAspy",
id: 516750892372852754n, id: 516750892372852754n,
}, },
jamesbt365: {
name: "jamesbt365",
id: 158567567487795200n,
},
} satisfies Record<string, Dev>); } satisfies Record<string, Dev>);
// iife so #__PURE__ works correctly // iife so #__PURE__ works correctly

View file

@ -19,7 +19,7 @@
import "./discord.css"; import "./discord.css";
import { MessageObject } from "@api/MessageEvents"; import { MessageObject } from "@api/MessageEvents";
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
import { Channel, Guild, Message, User } from "discord-types/general"; import { Channel, Guild, Message, User } from "discord-types/general";
import { Except } from "type-fest"; import { Except } from "type-fest";
@ -212,14 +212,3 @@ export async function fetchUserProfile(id: string, options?: FetchUserProfileOpt
export function getUniqueUsername(user: User) { export function getUniqueUsername(user: User) {
return user.discriminator === "0" ? user.username : user.tag; return user.discriminator === "0" ? user.username : user.tag;
} }
/**
* Get the URL for an emoji. This function always returns a gif URL for animated emojis, instead of webp
* @param id The emoji id
* @param animated Whether the emoji is animated
* @param size The size for the emoji
*/
export function getEmojiURL(id: string, animated: boolean, size: number) {
const url = IconUtils.getEmojiURL({ id, animated, size });
return animated ? url.replace(".webp", ".gif") : url;
}

View file

@ -21,8 +21,8 @@ import { Patch, PatchReplacement, ReplaceFn } from "./types";
export function canonicalizeMatch<T extends RegExp | string>(match: T): T { export function canonicalizeMatch<T extends RegExp | string>(match: T): T {
let partialCanon = typeof match === "string" ? match : match.source; let partialCanon = typeof match === "string" ? match : match.source;
partialCanon = partialCanon.replaceAll(/#{intl::([\w$+/]*)(?:::(\w+))?}/g, (_, key, modifier) => { partialCanon = partialCanon.replaceAll(/#{intl::([A-Za-z_$][\w$]*)}/g, (_, key) => {
const hashed = modifier === "raw" ? key : runtimeHashMessageKey(key); const hashed = runtimeHashMessageKey(key);
const isString = typeof match === "string"; const isString = typeof match === "string";
const hasSpecialChars = !Number.isNaN(Number(hashed[0])) || hashed.includes("+") || hashed.includes("/"); const hasSpecialChars = !Number.isNaN(Number(hashed[0])) || hashed.includes("+") || hashed.includes("/");
@ -40,7 +40,7 @@ export function canonicalizeMatch<T extends RegExp | string>(match: T): T {
return partialCanon as T; return partialCanon as T;
} }
const canonSource = partialCanon.replaceAll("\\i", String.raw`(?:[A-Za-z_$][\w$]*)`); const canonSource = partialCanon.replaceAll(String.raw`\i`, String.raw`(?:[A-Za-z_$][\w$]*)`);
return new RegExp(canonSource, match.flags) as T; return new RegExp(canonSource, match.flags) as T;
} }

View file

@ -163,13 +163,9 @@ waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint"); export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint");
export const { zustandCreate } = mapMangledModuleLazy(["useSyncExternalStoreWithSelector:", "Object.assign"], { export const zustandCreate = findByCodeLazy("will be removed in v4");
zustandCreate: filters.byCode(/=>(\i)\?\i\(\1/)
});
export const { zustandPersist } = mapMangledModuleLazy(".onRehydrateStorage)?", { export const zustandPersist = findByCodeLazy("[zustand persist middleware]");
zustandPersist: filters.byCode(/(\(\i,\i\))=>.+?\i\1/)
});
export const MessageActions = findByPropsLazy("editMessage", "sendMessage"); export const MessageActions = findByPropsLazy("editMessage", "sendMessage");
export const MessageCache = findByPropsLazy("clearCache", "_channelMessages"); export const MessageCache = findByPropsLazy("clearCache", "_channelMessages");
@ -185,7 +181,7 @@ export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModuleLa
toggleExpressionPicker: filters.byCode(/getState\(\)\.activeView===\i\?\i\(\):\i\(/), toggleExpressionPicker: filters.byCode(/getState\(\)\.activeView===\i\?\i\(\):\i\(/),
setExpressionPickerView: filters.byCode(/setState\({activeView:\i,lastActiveView:/), setExpressionPickerView: filters.byCode(/setState\({activeView:\i,lastActiveView:/),
setSearchQuery: filters.byCode("searchQuery:"), setSearchQuery: filters.byCode("searchQuery:"),
useExpressionPickerStore: filters.byCode(/\(\i,\i=\i\)=>/) useExpressionPickerStore: filters.byCode("Object.is")
}); });
export const PopoutActions: t.PopoutActions = mapMangledModuleLazy('type:"POPOUT_WINDOW_OPEN"', { export const PopoutActions: t.PopoutActions = mapMangledModuleLazy('type:"POPOUT_WINDOW_OPEN"', {