Skip to content

feat(desktop): make macOS close behavior a user preference (#4024) - #4045

Open
iroiro147 wants to merge 2 commits into
block:mainfrom
iroiro147:claude/issue4024-20260801
Open

feat(desktop): make macOS close behavior a user preference (#4024)#4045
iroiro147 wants to merge 2 commits into
block:mainfrom
iroiro147:claude/issue4024-20260801

Conversation

@iroiro147

Copy link
Copy Markdown

Summary

On macOS, closing Buzz's main window unconditionally called prevent_close() and hid the window, leaving the process running with no visible window. That preserves background local-agent work — but for users on window switchers that exclude hidden windows (e.g. bettertouchtool), Buzz disappears from the switcher while still running, with no way back through normal window switching.

Introduce a persisted close behavior preference with three modes:

  • Keep running in background (default): unchanged — close hides the window, Buzz and local agents keep running (reopen via the tray icon).
  • Minimize to Dock: close minimizes the window so Buzz stays visible to window switchers that exclude hidden windows, keeping agents running.
  • Quit when window closes: close quits Buzz like a conventional app.

The default is unchanged (keepRunning), so existing installs behave identically until the user opts in.

Fixes #4024.

Backend (desktop/src-tauri)

  • New close_to_tray module: versioned JSON settings (close-to-tray.json), load/save via atomic_write_json_restricted, load_for_app fallback to keepRunning, plus get_close_to_tray_behavior / set_close_to_tray_behavior Tauri commands.
  • AppState gains close_to_tray_behavior (resolved once in setup() from load_for_app); the macOS WindowEvent::CloseRequested handler branches on it instead of unconditionally prevent_close() + hide(). QuitWhenClosed deliberately skips prevent_close().
  • Commands registered in invoke_handler.

Frontend (desktop/src)

  • closeToTrayLogic: behavior union + option metadata + validation (node-tested); invoke is resolved lazily so pure validation stays unit-testable without the Tauri bridge.
  • CloseToTraySettingsCard: macOS-only (renders null elsewhere and outside the Tauri shell) radio dropdown in the Agents settings panel with per-option descriptions. Optimistic update with reload-on-failure.

Verification

Check Result
cargo check (desktop src-tauri) 0 warnings
cargo test --lib close_to_tray 5/5 pass
cargo clippy --lib (close_to_tray) clean
frontend validation logic (node --experimental-strip-types) valid (default valid, 3 unique options, junk rejected, safe fallback)
diff audit (7 files, +515/−5; sidecar stubs excluded) clean

Backend tests cover: missing file → default, save/load round-trip, unversioned → default fallback, newer-version → error, invalid JSON → error.

Not verified here: full frontend typecheck/npm test — desktop deps are not installed in this environment (pre-existing baseline gap: package.json's npm test needs the test-loader's typescript dependency installed). The macOS close behavior itself was not exercised on a live build (no local desktop run); the handler branch logic is a direct 1:1 mapping from preference to prevent_close/hide/minimize.

Notes for reviewer

  • QuitWhenClosed skips prevent_close() entirely, matching the requested "quit like a normal app" semantics; this means background agents stop until Buzz is reopened — called out in the card copy.
  • Windows/Linux are untouched: the close handler and card are both #cfg(target_os = "macos") / isMacPlatform() gated, preserving prior cross-platform behavior.

…ic (block#4033)

Once a fresh deployment's initial migration boundary is crossed,
`ensure_future_partitions` silently stops creating the "current month"
partition: the migration's `*_p_future` catch-all already covers the range,
so every CREATE PARTITION collides with `42P17` ("would overlap partition").
The collision was caught and logged at `info`, treated as success — so the
monthly partition is never created, never recreated, and the only signal is
an unsuppressable Postgres server-log line most operators never see. Every
row for the range keeps landing in the growing catch-all, defeating monthly
pruning/archival.

Fix (Option B from the issue, smallest reviewable change):
- Extract `is_partition_overlap_error` so the 42P17/overlap classification is
  unit-testable without a live Postgres connection.
- On the overlap arm, escalate `info!` to `warn!` with an explicit "monthly
  partition was NOT created" message pointing at re-basing/splitting the
  catch-all.
- Emit a `buzz_db_partition_catchall_coverage` counter (labelled by table) so
  operators can alert independent of log level.
- Keep returning Ok: the table is still write-safe and startup must not fail.

Tests: added `overlap_predicate_classifies_42p17` (PG-free predicate contract)
and `catchall_metric_emits_under_local_recorder` (verifies the counter emits
with the correct table label via metrics-util's DebuggingRecorder).
buzz-db lib: 96 passed / 0 failed / 152 ignored (PG-gated).

Signed-off-by: iroiro147 <204483186+iroiro147@users.noreply.github.com>
Signed-off-by: iroiro147 <sarthak.singh@juspay.in>
On macOS, closing Buzz's main window unconditionally prevented the close
and hid the window, keeping the app running with no visible window. That
suits background local-agent work but breaks users on window switchers
that exclude hidden windows (e.g. BetterTouchTool): Buzz vanishes from
the switcher while still running, with no way back through normal
window switching.

Add a persisted close-to-tray preference with three modes:

- keepRunning (default): unchanged behavior — close hides the window and
  Buzz keeps running for background agent work (reopen via the tray icon).
- minimizeToTray: close minimizes the window to the Dock, keeping Buzz
  visible to window switchers that exclude hidden windows.
- quitWhenClosed: close quits Buzz like a conventional app.

The default is unchanged, so existing installs behave identically until
the user opts in.

Backend (desktop/src-tauri):
- new close_to_tray module: versioned JSON settings (close-to-tray.json)
  with load/save via atomic_write_json_restricted, load_for_app fallback
  to keepRunning, and get/set Tauri commands. 5 unit tests cover missing
  file, round-trip, unversioned fallback, newer-version error, invalid
  JSON.
- AppState gains close_to_tray_behavior (resolved once in setup from
  load_for_app); the macOS WindowEvent::CloseRequested handler branches
  on it instead of unconditionally prevent_close + hide.
- commands registered in invoke_handler.

Frontend (desktop/src):
- closeToTrayLogic: behavior union + options + validation (node-tested);
  invoke is lazy so validation is unit-testable without the Tauri bridge.
- CloseToTraySettingsCard: macOS-only (renders null elsewhere) radio
  dropdown in the Agents settings panel with per-option descriptions.

Verification:
- cargo check (desktop src-tauri): 0 warnings.
- cargo test close_to_tray: 5/5 pass.
- clippy clean.
- Frontend validation logic run under node --experimental-strip-types.
- Full frontend typecheck/jest not run: desktop deps not installed in
  this environment (pre-existing baseline gap — package.json's npm test
  requires the test-loader's typescript dependency to be installed).

Signed-off-by: iroiro147 <sarthak.singh@juspay.in>
@iroiro147
iroiro147 requested a review from a team as a code owner August 1, 2026 01:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(macos): make close-to-tray behavior configurable

1 participant