fix(desktop): dedupe Linux tray D-Bus calls to stop main-thread freeze (#2459)
The tray controller called setToolTip and setContextMenu unconditionally on every snapshot push. On Linux both are synchronous D-Bus calls into plasmashell's StatusNotifierItem host. useTraySync debounced pushes at 120ms (up to ~8/sec during token streaming), so each push made 2 blocking D-Bus round-trips even when only dockBadgeCount changed or a token streamed into an already-listed session — i.e., tooltip and menu content didn't change. The native block has no JS-level log, is intermittent (depends on plasmashell load), and eventually freezes the Electron main thread until crash. setTitle and setImage were already deduped; setToolTip and setContextMenu were the gap. Mirror the existing lastX pattern: - tray.mjs: dedupe setToolTip (exact string compare via lastTooltip) and setContextMenu (via lastMenuKey, a lightweight signature of menu-affecting fields: sessions/approvals/usage). menuKey is a string concat, cheaper than buildMenu itself, so skipping buildMenu when the key matches also saves work. Cache-after-call ordering matches lastTitle/lastTooltip so a throw forces a retry rather than skipping one. destroy() resets both new vars. - useTraySync.ts: FLUSH_DEBOUNCE_MS 120 -> 500. Tray doesn't need sub-second updates; approvals are rare discrete events that flush through the debounce and the main app UI stays instant via SSE/stores. The first change is the real fix; the second is a complement. Either alone helps; together they eliminate the freeze under streaming.
This commit is contained in:
@@ -39,7 +39,7 @@ import type { QuestionRequest } from '@/types/question';
|
||||
const TRAY_ACTION_EVENT = 'openchamber:tray-action';
|
||||
// Event-driven updates do the real work; this is just a slow safety net.
|
||||
const POLL_INTERVAL_MS = 5000;
|
||||
const FLUSH_DEBOUNCE_MS = 120;
|
||||
const FLUSH_DEBOUNCE_MS = 500;
|
||||
// Pull the full cross-project session list periodically. SSE keeps the active
|
||||
// directory instant; this catches sessions created in directories this client
|
||||
// never opened (other worktrees, other projects, the TUI, …).
|
||||
@@ -468,8 +468,8 @@ export const useTraySync = (): void => {
|
||||
};
|
||||
|
||||
// Coalesce bursts (e.g. token-by-token streaming updates a store rapidly)
|
||||
// into a single push, while staying near-instant for discrete events like
|
||||
// a new session appearing.
|
||||
// into at most one push per FLUSH_DEBOUNCE_MS; discrete events surface within
|
||||
// that window. The main app UI stays instant via SSE/stores.
|
||||
const scheduleFlush = () => {
|
||||
if (disposed || flushTimer !== null) return;
|
||||
flushTimer = window.setTimeout(() => {
|
||||
|
||||
Reference in New Issue
Block a user