From 45fa6c430bc874f2642555915dd55602b7792d5c Mon Sep 17 00:00:00 2001 From: rowkav09 Date: Sat, 3 Oct 2026 03:58:10 +0100 Subject: [PATCH] fix(linux): route tray pages to Settings until first-run setup completes --- scripts/nowplaying.js | 2 ++ src/linux-tray.js | 8 ++++++-- test/linux-tray.test.js | 12 ++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/scripts/nowplaying.js b/scripts/nowplaying.js index 60d17e08..5c927958 100755 --- a/scripts/nowplaying.js +++ b/scripts/nowplaying.js @@ -108,6 +108,8 @@ async function start() { if (!args.includes("--no-tray") && linuxTrayAvailable()) { tray = createLinuxTray({ url: app.url, + isFirstRun: () => Boolean(liveApp?.firstRun), + getUrl: () => liveApp.url, script: fileURLToPath(new URL("./linux-tray.py", import.meta.url)), icon: fileURLToPath(new URL("../assets/brand/png/icon-512.png", import.meta.url)), onQuit: () => { void close(); }, diff --git a/src/linux-tray.js b/src/linux-tray.js index 6609a7a8..b7e8457b 100644 --- a/src/linux-tray.js +++ b/src/linux-tray.js @@ -22,7 +22,7 @@ export function linuxTrayAvailable({ platform = process.platform, env = process. // The helper sees only the icon path. Menu events are a small allowlist, not // commands or URLs supplied by the helper. Missing desktop support leaves the // WebUI/server running rather than turning Quit into a startup failure. -export function createLinuxTray({ url, script, icon, spawnProcess = spawn, openUrl = openLinuxWebUiUrl, onQuit = () => {}, onUnavailable = () => {}, readyTimeoutMs = 5000, killTimeoutMs = 1000, env = process.env } = {}) { +export function createLinuxTray({ url, script, icon, spawnProcess = spawn, openUrl = openLinuxWebUiUrl, onQuit = () => {}, onUnavailable = () => {}, readyTimeoutMs = 5000, killTimeoutMs = 1000, isFirstRun = () => false, getUrl = () => url, env = process.env } = {}) { const target = new URL(url); if (target.protocol !== "http:" || target.hostname !== "127.0.0.1" || target.username || target.password || target.pathname !== "/" || target.search || target.hash) throw new TypeError("tray URL must be a loopback app origin"); @@ -60,7 +60,11 @@ export function createLinuxTray({ url, script, icon, spawnProcess = spawn, openU if (event === "ready") settle(true); else if (readyDone && event === "quit") { didQuit = true; onQuit(); break; } else if (readyDone && ["open", "settings", "logs"].includes(event)) - try { openUrl(event === "open" ? target.origin : `${target.origin}/${event}`); } + try { + const live = new URL(getUrl()); + if (live.protocol !== "http:" || live.hostname !== "127.0.0.1" || live.username || live.password || live.pathname !== "/" || live.search || live.hash) throw new TypeError("Invalid live origin"); + openUrl(isFirstRun() ? `${live.origin}/settings` : event === "open" ? live.origin : `${live.origin}/${event}`); + } catch { /* A browser failure must not terminate the media app. */ } } }); diff --git a/test/linux-tray.test.js b/test/linux-tray.test.js index dc0b4536..31ee5c0f 100644 --- a/test/linux-tray.test.js +++ b/test/linux-tray.test.js @@ -116,3 +116,15 @@ test("real helper ignoring TERM is killed within the bounded shutdown window", { assert.equal(signal, "SIGKILL"); } finally { child.kill("SIGKILL"); } }); + +test("first-run menu opens setup until the live app is configured", async () => { + const child = fakeChild(); const opened = []; let firstRun = true; let url = "http://127.0.0.1:47832"; + const tray = createLinuxTray(options(child, { isFirstRun: () => firstRun, getUrl: () => url, openUrl: (url) => opened.push(url) })); + child.stdout.write("ready\nopen\nlogs\n"); + await tray.ready; + firstRun = false; + url = "http://127.0.0.1:47833"; + child.stdout.write("logs\n"); + assert.deepEqual(opened, ["http://127.0.0.1:47832/settings", "http://127.0.0.1:47832/settings", "http://127.0.0.1:47833/logs"]); + tray.close(); +});