P1 — add true tray toggle and local zara --toggle-desktop control IPC #357

Closed
opened 2026-09-08 02:03:38 +00:00 by nsaspy · 0 comments
Owner

Parent: #323
Integrates with: #87 global shortcuts, #133 daemon/client migration
Research: docs/research/002-unified-copilot-voice-visual-ci.md

Goal

Make Zara's one desktop Copilot reliably toggleable from the system tray, global shortcuts, shell/dotfiles, and launchers without spawning duplicate desktop/runtime instances.

Current defect

QuickCopilotWindow already implements toggle_visibility(), but tray primary activation emits quick_requested, and DesktopController wires that to show_quick_copilot() rather than a visibility toggle. A second tray click therefore does not hide the Copilot.

This issue should target the unified Copilot from #324 when available; do not deepen the legacy two-window architecture.

Required tray behavior

Primary tray activation:

if Copilot visible:
    hide
else:
    show + raise + activate + focus composer

Also:

  • window close hides rather than terminates Zara;
  • Escape hides in compact mode;
  • explicit Quit Zara performs actual client/process cleanup;
  • repeated rapid activation does not create duplicate windows;
  • minimized state is normalized correctly on show;
  • geometry recovery remains multi-monitor safe.

External command

Expose:

zara --toggle-desktop

Semantics:

no desktop owner      -> start desktop Zara and show Copilot
owner + hidden        -> request show/raise/focus
owner + visible       -> request hide

Optional companion commands are acceptable if they simplify testing/integration:

zara --show-desktop
zara --hide-desktop

but --toggle-desktop is required.

Architecture decision

Do not send client-local window visibility over remote ZARA/1 assistant semantics. The server/daemon should not be required merely to hide/show a local Qt window.

Implement a narrow per-user local desktop-control boundary owned by the desktop process. Linux-first candidates include QLocalServer/QLocalSocket or an equivalently bounded Unix-domain socket under XDG_RUNTIME_DIR.

Research final mechanism adversarially, but preserve these invariants:

  • one desktop owner;
  • per-user permissions/runtime directory;
  • tiny closed vocabulary (toggle, show, hide, optional status);
  • no arbitrary eval/shell;
  • bounded connect/read/write timeouts;
  • stale socket recovery after crash;
  • duplicate process race handling;
  • CLI failure is explicit and does not hang;
  • local control works even when remote daemon is reconnecting/unavailable.

Startup behavior

If zara --toggle-desktop observes no owner, it may launch the canonical desktop entrypoint and arrange initial visibility. Do not accidentally start a second mic-owning standalone runtime when daemon/client mode is configured.

Coordinate with current #133 behavior and exact current desktop entrypoint.

Global shortcut integration

#87 should ultimately invoke the same control/summon behavior rather than creating a separate window ownership path.

Tests

TDD at minimum:

  • tray click visible -> hidden;
  • tray click hidden -> visible + focused;
  • minimized -> normal + focused;
  • close -> hidden, process remains;
  • Escape compact -> hidden;
  • --toggle-desktop with existing hidden owner;
  • --toggle-desktop with existing visible owner;
  • no owner -> exactly one owner starts;
  • two simultaneous toggle/start invocations do not create two desktop processes;
  • stale local socket after crash recovers;
  • wrong-user/world-writable endpoint rejected according to chosen IPC design;
  • malformed/oversized command rejected;
  • local toggle works during daemon reconnect/unavailability;
  • monitor topology change while hidden recovers visible geometry.

TDD / RAGE

Follow repository Auto-RAGE and exact-head gates.

Acceptance

Tray, shortcuts and shell integrations all control the same single Copilot instance; a second tray click hides it; zara --toggle-desktop is a stable bounded local interface suitable for the user's Qtile/dotfiles; and no path creates duplicate desktop/runtime ownership.

Parent: #323 Integrates with: #87 global shortcuts, #133 daemon/client migration Research: `docs/research/002-unified-copilot-voice-visual-ci.md` ## Goal Make Zara's one desktop Copilot reliably toggleable from the system tray, global shortcuts, shell/dotfiles, and launchers without spawning duplicate desktop/runtime instances. ## Current defect `QuickCopilotWindow` already implements `toggle_visibility()`, but tray primary activation emits `quick_requested`, and `DesktopController` wires that to `show_quick_copilot()` rather than a visibility toggle. A second tray click therefore does not hide the Copilot. This issue should target the unified Copilot from #324 when available; do not deepen the legacy two-window architecture. ## Required tray behavior Primary tray activation: ```text if Copilot visible: hide else: show + raise + activate + focus composer ``` Also: - window close hides rather than terminates Zara; - Escape hides in compact mode; - explicit `Quit Zara` performs actual client/process cleanup; - repeated rapid activation does not create duplicate windows; - minimized state is normalized correctly on show; - geometry recovery remains multi-monitor safe. ## External command Expose: ```text zara --toggle-desktop ``` Semantics: ```text no desktop owner -> start desktop Zara and show Copilot owner + hidden -> request show/raise/focus owner + visible -> request hide ``` Optional companion commands are acceptable if they simplify testing/integration: ```text zara --show-desktop zara --hide-desktop ``` but `--toggle-desktop` is required. ## Architecture decision Do **not** send client-local window visibility over remote `ZARA/1` assistant semantics. The server/daemon should not be required merely to hide/show a local Qt window. Implement a narrow per-user local desktop-control boundary owned by the desktop process. Linux-first candidates include `QLocalServer`/`QLocalSocket` or an equivalently bounded Unix-domain socket under `XDG_RUNTIME_DIR`. Research final mechanism adversarially, but preserve these invariants: - one desktop owner; - per-user permissions/runtime directory; - tiny closed vocabulary (`toggle`, `show`, `hide`, optional `status`); - no arbitrary eval/shell; - bounded connect/read/write timeouts; - stale socket recovery after crash; - duplicate process race handling; - CLI failure is explicit and does not hang; - local control works even when remote daemon is reconnecting/unavailable. ## Startup behavior If `zara --toggle-desktop` observes no owner, it may launch the canonical desktop entrypoint and arrange initial visibility. Do not accidentally start a second mic-owning standalone runtime when daemon/client mode is configured. Coordinate with current #133 behavior and exact current desktop entrypoint. ## Global shortcut integration #87 should ultimately invoke the same control/summon behavior rather than creating a separate window ownership path. ## Tests TDD at minimum: - tray click visible -> hidden; - tray click hidden -> visible + focused; - minimized -> normal + focused; - close -> hidden, process remains; - Escape compact -> hidden; - `--toggle-desktop` with existing hidden owner; - `--toggle-desktop` with existing visible owner; - no owner -> exactly one owner starts; - two simultaneous toggle/start invocations do not create two desktop processes; - stale local socket after crash recovers; - wrong-user/world-writable endpoint rejected according to chosen IPC design; - malformed/oversized command rejected; - local toggle works during daemon reconnect/unavailability; - monitor topology change while hidden recovers visible geometry. ## TDD / RAGE Follow repository Auto-RAGE and exact-head gates. ## Acceptance Tray, shortcuts and shell integrations all control the same single Copilot instance; a second tray click hides it; `zara --toggle-desktop` is a stable bounded local interface suitable for the user's Qtile/dotfiles; and no path creates duplicate desktop/runtime ownership.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/zara#357
No description provided.