From 41015b4e79b9ca56e740fdf9a2b6af9def74ba83 Mon Sep 17 00:00:00 2001 From: Inrixia Date: Sun, 25 Jan 2026 03:40:59 +1300 Subject: [PATCH 1/5] Fix ListenBrainz settings url --- plugins/ListenBrainz/src/Settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ListenBrainz/src/Settings.tsx b/plugins/ListenBrainz/src/Settings.tsx index dfa4c2d6..de4ed906 100644 --- a/plugins/ListenBrainz/src/Settings.tsx +++ b/plugins/ListenBrainz/src/Settings.tsx @@ -27,7 +27,7 @@ export const Settings = () => { desc={ <> User token from{" "} - + listenbrainz.org/settings From a42f6da7da3e2d9baf1ad36875c14fed624be286 Mon Sep 17 00:00:00 2001 From: Brskt Date: Wed, 4 Feb 2026 16:26:55 +0100 Subject: [PATCH 2/5] Add debounce to Discord RPC activity updates Prevents rate-limit errors when rapidly changing tracks by: - Debouncing calls (300ms delay) - Queueing updates if one is already in progress --- plugins/DiscordRPC/src/index.ts | 8 +--- plugins/DiscordRPC/src/updateActivity.ts | 54 +++++++++++++++++++++++- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/plugins/DiscordRPC/src/index.ts b/plugins/DiscordRPC/src/index.ts index 06ca368b..684867c9 100644 --- a/plugins/DiscordRPC/src/index.ts +++ b/plugins/DiscordRPC/src/index.ts @@ -9,14 +9,10 @@ export const { trace, errSignal } = Tracer("[DiscordRPC]"); export { Settings } from "./Settings"; redux.intercept(["playbackControls/SEEK", "playbackControls/SET_PLAYBACK_STATE"], unloads, () => { - updateActivity() - .then(() => (errSignal!._ = undefined)) - .catch(trace.err.withContext("Failed to set activity")); + updateActivity(); }); MediaItem.onMediaTransition(unloads, (mediaItem) => { - updateActivity(mediaItem) - .then(() => (errSignal!._ = undefined)) - .catch(trace.err.withContext("Failed to set activity")); + updateActivity(mediaItem); }); unloads.add(cleanupRPC.bind(cleanupRPC)); diff --git a/plugins/DiscordRPC/src/updateActivity.ts b/plugins/DiscordRPC/src/updateActivity.ts index e688a028..d4ab7afb 100644 --- a/plugins/DiscordRPC/src/updateActivity.ts +++ b/plugins/DiscordRPC/src/updateActivity.ts @@ -4,11 +4,63 @@ import type { SetActivity } from "@xhayper/discord-rpc"; import { fmtStr, getStatusText } from "./activityTextHelpers"; import { setActivity, StatusDisplayTypeEnum } from "./discord.native"; import { settings } from "./Settings"; +import { trace, errSignal } from "./index"; // Proxy this so we dont try import a node native module const StatusDisplayType = await StatusDisplayTypeEnum(); -export const updateActivity = async (mediaItem?: MediaItem) => { +// Debounce state +const DEBOUNCE_MS = 300; +let debounceTimer: ReturnType | null = null; +let isUpdating = false; +let pendingUpdate: MediaItem | undefined | null = null; // null = no pending, undefined = pending with no mediaItem + +/** + * Debounced wrapper for _updateActivity + * - Waits DEBOUNCE_MS after last call before executing + * - If already updating, queues the next update + */ +export const updateActivity = (mediaItem?: MediaItem) => { + // If currently updating, mark that we need another update after + if (isUpdating) { + pendingUpdate = mediaItem; + return; + } + + // Clear existing timer + if (debounceTimer) clearTimeout(debounceTimer); + + // Set new debounce timer + debounceTimer = setTimeout(async () => { + debounceTimer = null; + await executeUpdate(mediaItem); + }, DEBOUNCE_MS); +}; + +/** + * Execute the actual update with mutex protection + */ +const executeUpdate = async (mediaItem?: MediaItem) => { + isUpdating = true; + try { + await _updateActivity(mediaItem); + errSignal!._ = undefined; + } catch (e) { + trace.err.withContext("Failed to set activity")(e); + } finally { + isUpdating = false; + if (pendingUpdate !== null) { + const pending = pendingUpdate; + pendingUpdate = null; + updateActivity(pending); + } + } +}; + +/** + * Internal update implementation (no debounce/mutex) + */ +const _updateActivity = async (mediaItem?: MediaItem) => { if (!PlayState.playing && !settings.displayOnPause) return await setActivity(); mediaItem ??= await MediaItem.fromPlaybackContext(); From 96141dada5df3cfa3a3889b0afcadd86f9eaf78c Mon Sep 17 00:00:00 2001 From: Brskt Date: Wed, 4 Feb 2026 16:37:26 +0100 Subject: [PATCH 3/5] Fix CSS editor not responding after window resize Use ResizeObserver instead of window resize listener for more reliable editor layout updates --- plugins/Themer/src/editor.html | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/plugins/Themer/src/editor.html b/plugins/Themer/src/editor.html index 04e59d7f..e057a9b8 100644 --- a/plugins/Themer/src/editor.html +++ b/plugins/Themer/src/editor.html @@ -40,17 +40,15 @@ }); require(["vs/editor/editor.main"], () => { - const editor = window.editor = monaco.editor.create( - document.getElementById("container"), - { - value: window.themerCSS, - language: "css", - theme: "vs-dark", - smoothScrolling: true, - } - ); + const container = document.getElementById("container"); + const editor = window.editor = monaco.editor.create(container, { + value: window.themerCSS, + language: "css", + theme: "vs-dark", + smoothScrolling: true, + }); editor.onDidChangeModelContent(() => ipcRenderer.setCSS(editor.getValue())); - window.addEventListener("resize", editor.layout.bind(editor)); + new ResizeObserver(() => editor.layout()).observe(container); }); From 2060d09f136f9f30e7dd0023423ef4e4b8b15afb Mon Sep 17 00:00:00 2001 From: Brskt Date: Wed, 4 Feb 2026 20:07:30 +0100 Subject: [PATCH 4/5] Fix hideTopBar not applying on startup Use observePromise to wait for the top bar element to exist before hiding it --- plugins/NativeFullscreen/src/index.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/NativeFullscreen/src/index.ts b/plugins/NativeFullscreen/src/index.ts index 4f00a852..a08090d0 100644 --- a/plugins/NativeFullscreen/src/index.ts +++ b/plugins/NativeFullscreen/src/index.ts @@ -1,5 +1,5 @@ import type { LunaUnload } from "@luna/core"; -import { redux } from "@luna/lib"; +import { observePromise, redux } from "@luna/lib"; import { storage } from "./Settings"; export { Settings } from "./Settings"; @@ -16,7 +16,12 @@ export const setTopBarVisibility = (visible: boolean) => { const bar = document.querySelector("div[class^='_bar']"); if (bar) bar.style.display = visible ? "" : "none"; }; -if (storage.hideTopBar) setTopBarVisibility(false); +// Apply hideTopBar setting on load +if (storage.hideTopBar) { + observePromise(unloads, "div[class^='_bar']").then((bar) => { + if (bar) setTopBarVisibility(false); + }); +} const onKeyDown = (event: KeyboardEvent) => { if (event.key === "F11") { From 82f0e05150abf6f80feae49cd3d5e24b141f0fff Mon Sep 17 00:00:00 2001 From: Brskt Date: Wed, 4 Feb 2026 20:07:42 +0100 Subject: [PATCH 5/5] Fix exitFullscreen error in Tidal windowed mode Only call exitFullscreen when in native fullscreen mode --- plugins/NativeFullscreen/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/NativeFullscreen/src/index.ts b/plugins/NativeFullscreen/src/index.ts index a08090d0..6d8c5070 100644 --- a/plugins/NativeFullscreen/src/index.ts +++ b/plugins/NativeFullscreen/src/index.ts @@ -32,7 +32,7 @@ const onKeyDown = (event: KeyboardEvent) => { if (document.fullscreenElement || wimp?.classList.contains("is-fullscreen")) { // Exiting fullscreen - document.exitFullscreen(); + if (document.fullscreenElement) document.exitFullscreen(); if (wimp) wimp.classList.remove("is-fullscreen"); if (!storage.hideTopBar) setTopBarVisibility(true); if (contentContainer) contentContainer.style.maxHeight = "";