From fae7c71fed5812e8d9aa683ae411b32d874ff282 Mon Sep 17 00:00:00 2001 From: thororen <78185467+thororen1234@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:03:36 -0400 Subject: [PATCH] Dear Claudia (Claude) --- src/equicordplugins/betterCommands/index.ts | 2 +- src/equicordplugins/cursorBuddy/fathorse.js | 14 +++-- src/equicordplugins/cursorBuddy/index.tsx | 8 ++- src/equicordplugins/cursorBuddy/oneko.js | 52 ++++++++++++------- .../fileUpload/utils/apngToGif.ts | 16 +++--- src/equicordplugins/gifMaker/fonts.ts | 3 +- .../guildPickerDumper/index.tsx | 4 +- src/equicordplugins/moreCommands/utils.ts | 10 +++- .../moreStickers/components/misc.tsx | 4 +- src/equicordplugins/moreStickers/upload.ts | 7 ++- src/equicordplugins/moreStickers/utils.tsx | 16 +++--- src/equicordplugins/musicControls/index.tsx | 7 +++ .../musicControls/tidal/TidalStore.ts | 23 ++++++-- .../tidal/lyrics/providers/store.ts | 10 ++++ .../remix/editor/components/Canvas.tsx | 8 ++- src/equicordplugins/vcPanelSettings/index.tsx | 15 ++++-- .../voiceMessageTranscriber.desktop/utils.ts | 4 +- 17 files changed, 148 insertions(+), 55 deletions(-) diff --git a/src/equicordplugins/betterCommands/index.ts b/src/equicordplugins/betterCommands/index.ts index d2ccaf1f5..13d616fed 100644 --- a/src/equicordplugins/betterCommands/index.ts +++ b/src/equicordplugins/betterCommands/index.ts @@ -15,7 +15,7 @@ const settings = definePluginSettings({ description: "Automatically fill command with all arguments instead of just required ones.", type: OptionType.BOOLEAN, default: true, - }, a + }, }); function fetchIndex(target: object) { diff --git a/src/equicordplugins/cursorBuddy/fathorse.js b/src/equicordplugins/cursorBuddy/fathorse.js index 449b474a7..19d459b54 100644 --- a/src/equicordplugins/cursorBuddy/fathorse.js +++ b/src/equicordplugins/cursorBuddy/fathorse.js @@ -155,15 +155,23 @@ export default function fathorse(cfg) { if (config.shake) document.body.style.willChange = "transform"; - window.addEventListener("mousemove", ev => { + function handleMouseMove(ev) { mousePos.x = ev.clientX; mousePos.y = ev.clientY; nextMove = Date.now() + freeroamStart; isRoaming = false; - }); + } + window.addEventListener("mousemove", handleMouseMove); requestAnimationFrame(lifecycle); - return mousePos; + return function destroy() { + window.removeEventListener("mousemove", handleMouseMove); + if (config.shake) { + document.body.style.willChange = ""; + document.body.style.transform = ""; + } + fathorse.remove(); + }; }; diff --git a/src/equicordplugins/cursorBuddy/index.tsx b/src/equicordplugins/cursorBuddy/index.tsx index 1bfb47a5c..9329745dd 100644 --- a/src/equicordplugins/cursorBuddy/index.tsx +++ b/src/equicordplugins/cursorBuddy/index.tsx @@ -196,7 +196,11 @@ const settings = definePluginSettings({ } }); +let destroyBuddy: (() => void) | undefined; + function unload() { + destroyBuddy?.(); + destroyBuddy = undefined; document.getElementById("oneko")?.remove(); document.getElementById("fathorse")?.remove(); } @@ -207,7 +211,7 @@ function load() { switch (settings.store.buddy) { case "oneko": { - oneko({ + destroyBuddy = oneko({ speed: settings.store.speed, fps: settings.store.fps, image: ONEKO_IMAGE, @@ -218,7 +222,7 @@ function load() { break; } case "fathorse": { - fathorse({ + destroyBuddy = fathorse({ speed: settings.store.speed, fps: settings.store.fps, size: settings.store.size, diff --git a/src/equicordplugins/cursorBuddy/oneko.js b/src/equicordplugins/cursorBuddy/oneko.js index 78f81a973..eb25d0075 100644 --- a/src/equicordplugins/cursorBuddy/oneko.js +++ b/src/equicordplugins/cursorBuddy/oneko.js @@ -196,33 +196,43 @@ export default function oneko(options = {}) { document.body.appendChild(nekoEl); - document.addEventListener("mousemove", function (event) { - mousePosX = event.clientX; - mousePosY = event.clientY; - }); + document.addEventListener("mousemove", handleMouseMove); if (persistPosition) { - window.addEventListener("beforeunload", function (event) { - window.localStorage.setItem( - "oneko", - JSON.stringify({ - nekoPosX: nekoPosX, - nekoPosY: nekoPosY, - mousePosX: mousePosX, - mousePosY: mousePosY, - frameCount: frameCount, - idleTime: idleTime, - idleAnimation: idleAnimation, - idleAnimationFrame: idleAnimationFrame, - bgPos: nekoEl.style.backgroundPosition, - }) - ); - }); + window.addEventListener("beforeunload", handleBeforeUnload); } window.requestAnimationFrame(onAnimationFrame); } + function handleMouseMove(event) { + mousePosX = event.clientX; + mousePosY = event.clientY; + } + + function handleBeforeUnload(event) { + window.localStorage.setItem( + "oneko", + JSON.stringify({ + nekoPosX: nekoPosX, + nekoPosY: nekoPosY, + mousePosX: mousePosX, + mousePosY: mousePosY, + frameCount: frameCount, + idleTime: idleTime, + idleAnimation: idleAnimation, + idleAnimationFrame: idleAnimationFrame, + bgPos: nekoEl.style.backgroundPosition, + }) + ); + } + + function destroy() { + document.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("beforeunload", handleBeforeUnload); + nekoEl.remove(); + } + let lastFrameTimestamp; function onAnimationFrame(timestamp) { @@ -335,4 +345,6 @@ export default function oneko(options = {}) { } init(); + + return destroy; } diff --git a/src/equicordplugins/fileUpload/utils/apngToGif.ts b/src/equicordplugins/fileUpload/utils/apngToGif.ts index d7ce1a666..fc41bb500 100644 --- a/src/equicordplugins/fileUpload/utils/apngToGif.ts +++ b/src/equicordplugins/fileUpload/utils/apngToGif.ts @@ -34,12 +34,16 @@ async function loadFFmpeg(): Promise { const classWorkerBlob = new Blob([(new TextEncoder()).encode(classWorkerRaw)], { type: "text/javascript" }); const classWorkerUrl = URL.createObjectURL(classWorkerBlob); - await ffmpeg.load({ - coreURL: `${baseURL}/ffmpeg-core.js`, - wasmURL: `${baseURL}/ffmpeg-core.wasm`, - workerURL: `${baseURL}/ffmpeg-core.worker.js`, - classWorkerURL: classWorkerUrl, - }); + try { + await ffmpeg.load({ + coreURL: `${baseURL}/ffmpeg-core.js`, + wasmURL: `${baseURL}/ffmpeg-core.wasm`, + workerURL: `${baseURL}/ffmpeg-core.worker.js`, + classWorkerURL: classWorkerUrl, + }); + } finally { + URL.revokeObjectURL(classWorkerUrl); + } ffmpegLoaded = true; console.log("[FileUpload] FFmpeg loaded"); diff --git a/src/equicordplugins/gifMaker/fonts.ts b/src/equicordplugins/gifMaker/fonts.ts index c7d235fdb..51175f732 100644 --- a/src/equicordplugins/gifMaker/fonts.ts +++ b/src/equicordplugins/gifMaker/fonts.ts @@ -64,9 +64,10 @@ async function loadFontFace(family: string, url: string, descriptors: FontFaceDe document.fonts.add(font); return true; } catch { + return false; + } finally { URL.revokeObjectURL(objectUrl); fontObjectUrls.delete(objectUrl); - return false; } } catch { return false; diff --git a/src/equicordplugins/guildPickerDumper/index.tsx b/src/equicordplugins/guildPickerDumper/index.tsx index fe9e86b39..63d939f6f 100644 --- a/src/equicordplugins/guildPickerDumper/index.tsx +++ b/src/equicordplugins/guildPickerDumper/index.tsx @@ -83,11 +83,13 @@ async function zipGuildAssets(guild: Guild, type: "emojis" | "stickers") { .then(results => { const zipped = zipSync(Object.fromEntries(results.map(({ file, filename }) => [filename, file]))); const blob = new Blob([new Uint8Array(zipped)], { type: "application/zip" }); + const objectUrl = URL.createObjectURL(blob); const link = document.createElement("a"); - link.href = URL.createObjectURL(blob); + link.href = objectUrl; link.download = `${guild.name}-${type}.zip`; link.click(); link.remove(); + setTimeout(() => URL.revokeObjectURL(objectUrl), 1000); }) .catch(console.error); } diff --git a/src/equicordplugins/moreCommands/utils.ts b/src/equicordplugins/moreCommands/utils.ts index 6bc7492dc..82771308b 100644 --- a/src/equicordplugins/moreCommands/utils.ts +++ b/src/equicordplugins/moreCommands/utils.ts @@ -242,7 +242,10 @@ export function loadFriendImage(source: File | string): Promise reject(err || event); + img.onerror = (event, _source, _lineno, _colno, err) => { + if (isFile) URL.revokeObjectURL(url); + reject(err || event); + }; img.crossOrigin = "anonymous"; img.src = url; }); @@ -343,7 +346,10 @@ export function loadImage(source: File | string) { if (isFile) URL.revokeObjectURL(url); resolve(img); }; - img.onerror = (event, _source, _lineno, _colno, err) => reject(err || event); + img.onerror = (event, _source, _lineno, _colno, err) => { + if (isFile) URL.revokeObjectURL(url); + reject(err || event); + }; img.crossOrigin = "Anonymous"; img.src = url; }); diff --git a/src/equicordplugins/moreStickers/components/misc.tsx b/src/equicordplugins/moreStickers/components/misc.tsx index bca8f2990..4c3ac9325 100644 --- a/src/equicordplugins/moreStickers/components/misc.tsx +++ b/src/equicordplugins/moreStickers/components/misc.tsx @@ -388,10 +388,12 @@ export const Packs = () => { } } + const objectUrl = URL.createObjectURL(new Blob([JSON.stringify(result)], { type: "application/json" })); const a = document.createElement("a"); - a.href = URL.createObjectURL(new Blob([JSON.stringify(result)], { type: "application/json" })); + a.href = objectUrl; a.download = "MoreStickers.stickerpacks"; a.click(); + setTimeout(() => URL.revokeObjectURL(objectUrl), 1000); Toasts.show({ message: "Sticker Packs exported", diff --git a/src/equicordplugins/moreStickers/upload.ts b/src/equicordplugins/moreStickers/upload.ts index 5f36bcd4f..964a34d52 100644 --- a/src/equicordplugins/moreStickers/upload.ts +++ b/src/equicordplugins/moreStickers/upload.ts @@ -133,7 +133,12 @@ export async function sendSticker({ channelId, sticker, ctrlKey, shiftKey, ffmpe const res = await corsFetch(sticker.image); if (!res.ok) throw new Error("Failed to fetch sticker image"); const blobUrl = URL.createObjectURL(await res.blob()); - const processed = await resizeImage(blobUrl); + let processed: Blob; + try { + processed = await resizeImage(blobUrl); + } finally { + URL.revokeObjectURL(blobUrl); + } const filename = sticker.filename ?? new URL(sticker.image).pathname.split("/").pop()!; const mimeType = getMimeFromExtension(filename.split(".").pop()); diff --git a/src/equicordplugins/moreStickers/utils.tsx b/src/equicordplugins/moreStickers/utils.tsx index 75aa7d6fa..c8d39654a 100644 --- a/src/equicordplugins/moreStickers/utils.tsx +++ b/src/equicordplugins/moreStickers/utils.tsx @@ -54,12 +54,16 @@ export async function loadFFmpeg(ffmpeg: FFmpeg, setLoaded: () => void) { const classWorkerBlob = new Blob([(new TextEncoder()).encode(classWorkerRaw)], { type: "text/javascript" }); const classWorkerUrl = URL.createObjectURL(classWorkerBlob); - await ffmpeg.load({ - coreURL: `${baseURL}/ffmpeg-core.js`, - wasmURL: `${baseURL}/ffmpeg-core.wasm`, - workerURL: `${baseURL}/ffmpeg-core.worker.js`, - classWorkerURL: classWorkerUrl, - }); + try { + await ffmpeg.load({ + coreURL: `${baseURL}/ffmpeg-core.js`, + wasmURL: `${baseURL}/ffmpeg-core.wasm`, + workerURL: `${baseURL}/ffmpeg-core.worker.js`, + classWorkerURL: classWorkerUrl, + }); + } finally { + URL.revokeObjectURL(classWorkerUrl); + } setLoaded(); console.log("FFmpeg loaded!"); } diff --git a/src/equicordplugins/musicControls/index.tsx b/src/equicordplugins/musicControls/index.tsx index 0a153b6d2..05ec0a1ce 100644 --- a/src/equicordplugins/musicControls/index.tsx +++ b/src/equicordplugins/musicControls/index.tsx @@ -27,7 +27,9 @@ import { migrateOldLyrics } from "./spotify/lyrics/api"; import { SpotifyLyrics } from "./spotify/lyrics/components/lyrics"; import { SpotifyPlayer } from "./spotify/PlayerComponent"; import { TidalLyrics } from "./tidal/lyrics/components/lyrics"; +import { stopTidalLrcStore } from "./tidal/lyrics/providers/store"; import { TidalPlayer } from "./tidal/TidalPlayer"; +import { stopTidalStore } from "./tidal/TidalStore"; export default definePlugin({ name: "MusicControls", @@ -115,4 +117,9 @@ export default definePlugin({ await migrateOldLyrics(); toggleHoverControls(settings.store.hoverControls); }, + + stop() { + stopTidalLrcStore(); + stopTidalStore(); + }, }); diff --git a/src/equicordplugins/musicControls/tidal/TidalStore.ts b/src/equicordplugins/musicControls/tidal/TidalStore.ts index 21b5c7db5..0e3e9deef 100644 --- a/src/equicordplugins/musicControls/tidal/TidalStore.ts +++ b/src/equicordplugins/musicControls/tidal/TidalStore.ts @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { SYM_LAZY_CACHED } from "@utils/lazy"; import { Logger } from "@utils/Logger"; import { proxyLazyWebpack } from "@webpack"; import { Flux, FluxDispatcher } from "@webpack/common"; @@ -59,8 +60,10 @@ type Message = { type: "update"; all: boolean; fields?: any; field?: string; val class TidalSocket { public onChange: (e: Message) => void; public ready = false; + public destroyed = false; public socket: WebSocket | undefined; + private reconnectTimeout: ReturnType | undefined; constructor(onChange: typeof this.onChange) { this.reconnect(); @@ -68,7 +71,7 @@ class TidalSocket { } public reconnect() { - if (this.ready) return; + if (this.ready || this.destroyed) return; try { this.initWs(); } catch (e) { @@ -78,6 +81,12 @@ class TidalSocket { this.ready = true; } + public close() { + this.destroyed = true; + clearTimeout(this.reconnectTimeout); + this.socket?.close(); + } + get routes() { return { "play": () => this.socket?.send(JSON.stringify({ action: "resume" })), @@ -105,13 +114,13 @@ class TidalSocket { }); this.socket.addEventListener("error", e => { - if (!this.ready) setTimeout(() => this.reconnect(), 5_000); + if (!this.ready && !this.destroyed) this.reconnectTimeout = setTimeout(() => this.reconnect(), 5_000); this.onChange({ type: "update", all: true, fields: { playing: false, track: null, currentTime: 0, repeatMode: 0, shuffle: false, volume: 100 } }); }); this.socket.addEventListener("close", e => { this.ready = false; - if (!this.ready) setTimeout(() => this.reconnect(), 10_000); + if (!this.destroyed) this.reconnectTimeout = setTimeout(() => this.reconnect(), 10_000); this.onChange({ type: "update", all: true, fields: { playing: false, track: null, currentTime: 0, repeatMode: 0, shuffle: false, volume: 100 } }); }); @@ -242,9 +251,17 @@ export const TidalStore = proxyLazyWebpack(() => { } return true; } + + public destroy() { + this.socket.close(); + } } const store = new TidalStore(FluxDispatcher); return store; }); + +export function stopTidalStore() { + TidalStore[SYM_LAZY_CACHED]?.destroy(); +} diff --git a/src/equicordplugins/musicControls/tidal/lyrics/providers/store.ts b/src/equicordplugins/musicControls/tidal/lyrics/providers/store.ts index a51293445..8e70c1248 100644 --- a/src/equicordplugins/musicControls/tidal/lyrics/providers/store.ts +++ b/src/equicordplugins/musicControls/tidal/lyrics/providers/store.ts @@ -23,6 +23,8 @@ function showNotif(title: string, body: string) { } } +let tidalStoreChangeListener: (() => void) | undefined; + export const TidalLrcStore = proxyLazyWebpack(() => { let lyrics: EnhancedLyric[] | null = null; let lastTrackId: string | null = null; @@ -49,6 +51,14 @@ export const TidalLrcStore = proxyLazyWebpack(() => { } TidalStore.addChangeListener(handleTidalStoreChange); + tidalStoreChangeListener = handleTidalStoreChange; return store; }); + +export function stopTidalLrcStore() { + if (tidalStoreChangeListener) { + TidalStore.removeChangeListener(tidalStoreChangeListener); + tidalStoreChangeListener = undefined; + } +} diff --git a/src/equicordplugins/remix/editor/components/Canvas.tsx b/src/equicordplugins/remix/editor/components/Canvas.tsx index 3990d7b77..417e4138f 100644 --- a/src/equicordplugins/remix/editor/components/Canvas.tsx +++ b/src/equicordplugins/remix/editor/components/Canvas.tsx @@ -45,9 +45,11 @@ export const Canvas = ({ file }: { file: File; }) => { const canvasRef = useRef(null); useEffect(() => { + const objectUrl = URL.createObjectURL(file); image = new Image(); - image.src = URL.createObjectURL(file); + image.src = objectUrl; image.onload = () => { + URL.revokeObjectURL(objectUrl); canvas = canvasRef.current; if (!canvas) return; @@ -66,7 +68,9 @@ export const Canvas = ({ file }: { file: File; }) => { initInput(); }; - }); + + return () => URL.revokeObjectURL(objectUrl); + }, [file]); return (); }; diff --git a/src/equicordplugins/vcPanelSettings/index.tsx b/src/equicordplugins/vcPanelSettings/index.tsx index 1be123c4f..2d34c423e 100644 --- a/src/equicordplugins/vcPanelSettings/index.tsx +++ b/src/equicordplugins/vcPanelSettings/index.tsx @@ -96,7 +96,8 @@ function OutputVolumeComponent() { useEffect(() => { const listener = () => setOutputVolume(configModule.getOutputVolume()); FluxDispatcher.subscribe("AUDIO_SET_OUTPUT_VOLUME", listener); - }); + return () => FluxDispatcher.unsubscribe("AUDIO_SET_OUTPUT_VOLUME", listener); + }, []); return ( <> @@ -117,7 +118,8 @@ function InputVolumeComponent() { useEffect(() => { const listener = () => setInputVolume(configModule.getInputVolume()); FluxDispatcher.subscribe("AUDIO_SET_INPUT_VOLUME", listener); - }); + return () => FluxDispatcher.unsubscribe("AUDIO_SET_INPUT_VOLUME", listener); + }, []); return ( <> @@ -138,7 +140,8 @@ function OutputDeviceComponent() { useEffect(() => { const listener = () => setOutputDevice(configModule.getOutputDeviceId()); FluxDispatcher.subscribe("AUDIO_SET_OUTPUT_DEVICE", listener); - }); + return () => FluxDispatcher.unsubscribe("AUDIO_SET_OUTPUT_DEVICE", listener); + }, []); return ( <> @@ -166,7 +169,8 @@ function InputDeviceComponent() { useEffect(() => { const listener = () => setInputDevice(configModule.getInputDeviceId()); FluxDispatcher.subscribe("AUDIO_SET_INPUT_DEVICE", listener); - }); + return () => FluxDispatcher.unsubscribe("AUDIO_SET_INPUT_DEVICE", listener); + }, []); return (
@@ -194,7 +198,8 @@ function VideoDeviceComponent() { useEffect(() => { const listener = () => setVideoDevice(configModule.getVideoDeviceId()); FluxDispatcher.subscribe("MEDIA_ENGINE_SET_VIDEO_DEVICE", listener); - }); + return () => FluxDispatcher.unsubscribe("MEDIA_ENGINE_SET_VIDEO_DEVICE", listener); + }, []); return (
diff --git a/src/equicordplugins/voiceMessageTranscriber.desktop/utils.ts b/src/equicordplugins/voiceMessageTranscriber.desktop/utils.ts index cdb12ebe3..af728b854 100644 --- a/src/equicordplugins/voiceMessageTranscriber.desktop/utils.ts +++ b/src/equicordplugins/voiceMessageTranscriber.desktop/utils.ts @@ -291,7 +291,9 @@ export class TranscriptionWorker { this.onPartial = onPartial; const blob = new Blob([workerCode], { type: "text/javascript" }); - this.worker = new Worker(URL.createObjectURL(blob), { type: "module" }); + const objectUrl = URL.createObjectURL(blob); + this.worker = new Worker(objectUrl, { type: "module" }); + URL.revokeObjectURL(objectUrl); this.worker.onmessage = this.handleMessage.bind(this); }