Dear Claudia (Claude)

This commit is contained in:
thororen 2026-09-02 14:03:36 -04:00
parent 183920dd37
commit fae7c71fed
No known key found for this signature in database
17 changed files with 148 additions and 55 deletions

View file

@ -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) {

View file

@ -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();
};
};

View file

@ -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,

View file

@ -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;
}

View file

@ -34,12 +34,16 @@ async function loadFFmpeg(): Promise<FFmpeg> {
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");

View file

@ -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;

View file

@ -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);
}

View file

@ -242,7 +242,10 @@ export function loadFriendImage(source: File | string): Promise<HTMLImageElement
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;
});
@ -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;
});

View file

@ -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",

View file

@ -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());

View file

@ -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!");
}

View file

@ -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();
},
});

View file

@ -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<typeof setTimeout> | 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();
}

View file

@ -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;
}
}

View file

@ -45,9 +45,11 @@ export const Canvas = ({ file }: { file: File; }) => {
const canvasRef = useRef<HTMLCanvasElement>(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 (<canvas ref={canvasRef} className="vc-remix-canvas"></canvas>);
};

View file

@ -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 (
<div style={{ marginTop: "10px" }}>
@ -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 (
<div style={{ marginTop: "10px" }}>

View file

@ -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);
}