diff --git a/src/equicordplugins/chatButtonsPlus/index.tsx b/src/equicordplugins/chatButtonsPlus/index.tsx index 9ea64a434..2366a1450 100644 --- a/src/equicordplugins/chatButtonsPlus/index.tsx +++ b/src/equicordplugins/chatButtonsPlus/index.tsx @@ -27,7 +27,7 @@ let buttonEntries: ButtonEntry[] = []; const BUTTON_ENTRIES_KEY = "ChatButtonsPlus_buttonEntries"; function generateId(): string { - return Date.now().toString(36) + Math.random().toString(36).substr(2); + return Date.now().toString(36) + Math.random().toString(36).substring(2); } async function handleButtonClick(context: string) { @@ -67,9 +67,32 @@ async function removeButtonEntry(id: string, forceUpdate: () => void) { } } +async function resetAllButtons(forceUpdate: () => void) { + try { + buttonEntries.length = 0; + await DataStore.set(BUTTON_ENTRIES_KEY, []); + forceUpdate(); + } catch (error) { + console.error("ChatButtonsPlus: Failed to reset buttons:", error); + } +} + function ButtonEntries() { const update = useForceUpdater(); + React.useEffect(() => { + const loadEntries = async () => { + try { + const storedEntries = await DataStore.get(BUTTON_ENTRIES_KEY) ?? []; + buttonEntries = storedEntries; + update(); + } catch (error) { + console.error("ChatButtonsPlus: Failed to load entries:", error); + } + }; + loadEntries(); + }, []); + async function setLabel(id: string, value: string) { try { const index = buttonEntries.findIndex(entry => entry.id === id); @@ -87,7 +110,7 @@ function ButtonEntries() { try { const index = buttonEntries.findIndex(entry => entry.id === id); if (index !== -1) { - buttonEntries[index].message = value.trim() || "Hello!"; + buttonEntries[index].message = value; await DataStore.set(BUTTON_ENTRIES_KEY, buttonEntries); update(); } @@ -99,10 +122,12 @@ function ButtonEntries() { async function setSvg(id: string, value: string) { try { const index = buttonEntries.findIndex(entry => entry.id === id); - if (index !== -1 && isValidSvg(value.trim())) { - buttonEntries[index].svg = value.trim(); - await DataStore.set(BUTTON_ENTRIES_KEY, buttonEntries); - update(); + if (index !== -1) { + if (value.trim() === "" || isValidSvg(value.trim())) { + buttonEntries[index].svg = value.trim(); + await DataStore.set(BUTTON_ENTRIES_KEY, buttonEntries); + update(); + } } } catch (error) { console.error("ChatButtonsPlus: Failed to update SVG:", error); @@ -185,19 +210,23 @@ function ButtonEntries() {
Message to Send - setMessage(entry.id, e)} + onChange={e => setMessage(entry.id, e.target.value)} + rows={2} />
Custom SVG Path (24x24 viewBox) - setSvg(entry.id, e)} + onChange={e => setSvg(entry.id, e.target.value)} + rows={3} /> Enter SVG path elements for a 24x24 viewBox. Use "currentColor" for the fill to match Discord's theme. @@ -210,7 +239,16 @@ function ButtonEntries() { return ( <> {elements} - +
+ + +
); } diff --git a/src/equicordplugins/chatButtonsPlus/styles.css b/src/equicordplugins/chatButtonsPlus/styles.css index e3b15338a..d896da97e 100644 --- a/src/equicordplugins/chatButtonsPlus/styles.css +++ b/src/equicordplugins/chatButtonsPlus/styles.css @@ -49,4 +49,30 @@ font-size: 12px; color: var(--text-muted); margin-top: 3px; +} + +.chatButtonsPlus-textarea { + width: 100%; + padding: 8px 12px; + border: 2px solid var(--text-muted); + border-radius: 4px; + background-color: var(--background-secondary); + color: var(--header-primary); + font-family: var(--font-primary); + font-size: 14px; + resize: vertical; + min-height: 60px; + box-sizing: border-box; +} + +.chatButtonsPlus-textarea:focus { + outline: none; + border-color: var(--brand-experiment); + box-shadow: 0 0 0 1px var(--brand-experiment); +} + +.chatButtonsPlus-actions { + display: flex; + gap: 10px; + margin-top: 15px; } \ No newline at end of file diff --git a/src/equicordplugins/fixFileExtensions/index.tsx b/src/equicordplugins/fixFileExtensions/index.tsx index 4d1ee6ba8..c1dfc3a93 100644 --- a/src/equicordplugins/fixFileExtensions/index.tsx +++ b/src/equicordplugins/fixFileExtensions/index.tsx @@ -11,12 +11,13 @@ import definePlugin, { ReporterTestable } from "@utils/types"; import { CloudUpload } from "@vencord/discord-types"; const extensionMap = { - "ogg": [".ogv", ".oga", ".ogx", ".ogm", ".spx", ".opus"], - "jpg": [".jpg", ".jpeg", ".jfif", ".jpe", ".jif", ".jfi", ".pjpeg", ".pjp"], - "svg": [".svgz"], - "mp4": [".m4v", ".m4r", ".m4p"], - "m4a": [".m4b"], - "mov": [".movie", ".qt"], + "ogg": [".ogv", ".oga", ".ogx", ".ogm", ".spx", ".opus", ".flac", ".aac", ".wma"], + "jpg": [".jpeg", ".jfif", ".jpe", ".jif", ".jfi", ".pjpeg", ".pjp", ".bmp", ".tiff", ".tif", ".webp"], + "svg": [".svgz", ".ai", ".eps"], + "mp4": [".m4v", ".m4r", ".m4p", ".avi", ".mkv", ".wmv", ".flv", ".3gp", ".webm"], + "m4a": [".m4b", ".aiff", ".wav"], + "mov": [".movie", ".qt", ".asf", ".rm", ".rmvb"], + "png": [".ico", ".cur"], }; export const reverseExtensionMap = Object.entries(extensionMap).reduce((acc, [target, exts]) => { @@ -50,6 +51,6 @@ export default definePlugin({ const ext = extIdx !== -1 ? file.slice(extIdx) : ""; const newExt = reverseExtensionMap[ext] || ext; - return fileName + newExt; + upload.filename = fileName + newExt; }, }); diff --git a/src/equicordplugins/guildPickerDumper/index.tsx b/src/equicordplugins/guildPickerDumper/index.tsx index 8fa7c34de..0607864d2 100644 --- a/src/equicordplugins/guildPickerDumper/index.tsx +++ b/src/equicordplugins/guildPickerDumper/index.tsx @@ -12,6 +12,8 @@ import type { Guild } from "@vencord/discord-types"; import { EmojiStore, Menu, StickersStore } from "@webpack/common"; import { zipSync } from "fflate"; +const StickerExt = [, "png", "apng", "json", "gif"] as const; + const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { // Assuming "privacy" is the correct ID for the group you want to modify. const group = findGroupChildrenByChildId("privacy", children); @@ -36,16 +38,44 @@ async function zipGuildAssets(guild: Guild, type: "emojis" | "stickers") { return console.log("Server not found!"); } - const fetchAsset = async e => { - const ext = e.animated ? ".gif" : ".png"; - const filename = e.id + ext; - const url = isEmojis - ? `https://${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/emojis/${filename}?size=512&quality=lossless` - : `https://${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${filename}?size=4096&lossless=true`; + const getProxyEndpoint = () => { + const rawEndpoint = window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT; + return rawEndpoint.startsWith("//") ? rawEndpoint.slice(2) : rawEndpoint; + }; + + const fetchAsset = async (e: any) => { + let ext: string; + let url: string; + const endpoint = getProxyEndpoint(); + + if (isEmojis) { + ext = e.animated ? ".gif" : ".png"; + url = `https://${endpoint}/emojis/${e.id}${ext}?size=512&quality=lossless`; + } else { + ext = "." + StickerExt[e.format_type]; + const urlExt = e.format_type === 2 ? ".png" : ext; + url = `https://${endpoint}/stickers/${e.id}${urlExt}?size=4096&lossless=true`; + } + + const sanitizedName = e.name.replace(/[<>:"/\\|?*]/g, "_"); + let filename = `${sanitizedName}_${e.id}${ext}`; + + let response = await fetch(url); + + if (!isEmojis && e.format_type === 2 && (!response.ok || response.headers.get("content-type")?.includes("text"))) { + const gifUrl = `https://${endpoint}/stickers/${e.id}.gif?size=4096&lossless=true`; + const gifResponse = await fetch(gifUrl); + + if (gifResponse.ok && !gifResponse.headers.get("content-type")?.includes("text")) { + response = gifResponse; + ext = ".gif"; + filename = `${sanitizedName}_${e.id}${ext}`; + } + } - const response = await fetch(url); const blob = await response.blob(); - return { file: new Uint8Array(await blob.arrayBuffer()), filename }; + const arrayBuffer = await blob.arrayBuffer(); + return { file: new Uint8Array(arrayBuffer), filename }; }; const assetPromises = items.map(e => fetchAsset(e));