Desktop: background store reads from the tray (no document.hidden skip, backoff), notifications by kind (rejected, approved, live, halted, crash, reviews) with switches, quiet hours and mute, click-through on next show; tray menu with apps and their states, read now, mute, updates, settings, tooltip summary and attention dot; tray_update command; tests

This commit is contained in:
CRTkafa 2026-10-22 22:24:05 +08:00
parent 5d8a0f5e9a
commit 03ef999dac
27 changed files with 1050 additions and 55 deletions

View file

@ -34,6 +34,18 @@ export function parseStart(v: string, apps: string[]): Route | null {
return tab ? { screen: screen as WorkspaceScreen, section: tab } : { screen: screen as WorkspaceScreen };
}
/** A route sent as JSON by the host (tray menu, notification target); anything that is not a known route is null. */
export function parseRouteJson(v: string): Route | null {
let o: unknown;
try { o = JSON.parse(v); } catch { return null; }
if (!o || typeof o !== 'object') return null;
const r = o as Record<string, unknown>;
if (r.screen === 'portfolio') return { screen: 'portfolio' };
if (r.screen === 'app') return typeof r.appId === 'string' && r.appId ? { screen: 'app', appId: r.appId, tab: APP_TABS.includes(r.tab as AppTab) ? r.tab as AppTab : 'releases' } : null;
if (typeof r.screen === 'string' && (WORKSPACE as readonly string[]).includes(r.screen)) return typeof r.section === 'string' ? { screen: r.screen as WorkspaceScreen, section: r.section } : { screen: r.screen as WorkspaceScreen };
return null;
}
export const sameRoute = (a: Route, b: Route): boolean => JSON.stringify(a) === JSON.stringify(b);
export interface Nav { route: Route; go: (r: Route) => void; openPalette: () => void; openWelcome: () => void }