Skip to content

feat(desktop): keep Windows host in system tray - #7493

Open
Ivorisnoob wants to merge 22 commits into
pingdotgg:mainfrom
Ivorisnoob:feat/windows-desktop-tray
Open

Ivorisnoob wants to merge 22 commits into
pingdotgg:mainfrom
Ivorisnoob:feat/windows-desktop-tray

Conversation

@Ivorisnoob

@Ivorisnoob Ivorisnoob commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

What Changed

  • Add a Windows system tray with Open and Quit actions.
  • Hide the desktop window on close while keeping the backend and remote connections running.
  • Preserve the graceful shutdown path for explicit Quit and updater-driven exits.
  • Enable close-to-background only after tray creation succeeds, preventing an inaccessible hidden app.
  • Add focused tray, window-close, and lifecycle coverage.
  • Document the Windows background behavior in the remote-access guide.

The independent Windows startup performance work is tracked in #7492.

Why

Closing the final T3 Code window on Windows previously exited the desktop host, stopping its backend and disconnecting paired remote clients. A tray-backed background mode keeps the host reachable while retaining an explicit, reliable way to quit completely.

UI Changes

T3 Code Windows system tray menu with Open T3 Code and Quit T3 Code actions

Related Issues

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Implemented with GPT-5.6-sol through the Codex harness in T3 Code.


Note

Medium Risk
Changes Windows quit/close semantics and couples lifecycle, tray, and update install recovery; mistakes could leave a hidden app running or fail to restore backends after a bad update install.

Overview
On Windows, closing the main window no longer exits the app when the system tray is active: a new DesktopTray service registers Open/Quit actions, enables DesktopWindow background mode only after tray setup succeeds, and DesktopLifecycle skips app.quit on window-all-closed while that mode is on.

DesktopWindow intercepts close to hide the window (including the connecting splash), calls prepareForQuit on real shutdown paths, and uses a creation mutex so backend-ready and activation cannot open duplicate main windows.

DesktopUpdates records backends that were running before install, and on failed or interrupted quit-and-install runs recoverInstallAction (restart those instances, wait for primary readiness, re-activate the window) while resetQuitPreparation restores close-to-background behavior. Updater before-quit permission is consumed once per event.

User docs for remote access note tray behavior on Windows.

Reviewed by Cursor Bugbot for commit c585d17. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Windows system tray support with background mode and update install recovery

  • Adds the DesktopTray Context service, which on Windows creates a native tray with Open and Quit menu entries and enables DesktopWindow background mode while the tray exists; closing the window hides it instead of quitting, and tray click re-activates the window
  • Extends DesktopWindow with setBackgroundModeEnabled, isBackgroundModeEnabled, prepareForQuit, and resetQuitPreparation; closes on Windows are intercepted to hide the window when background mode is on, and a semaphore serializes main window creation to prevent duplicates under concurrent activation/readiness signals
  • Updates DesktopLifecycle so window-all-closed does not quit on non-mac platforms when background mode is enabled, and handleBeforeQuit now calls prepareForQuit synchronously and treats allowQuit as a one-time boolean
  • Adds recoverInstallAction in DesktopUpdates that restarts previously running backends, waits up to 60s for primary readiness, and re-activates the window after a failed or interrupted quit-and-install; resetInstallAction now clears quitting state and calls resetQuitPreparation
  • Risk: on Windows the close button no longer quits the app when the tray is active; handleBeforeQuit signature changed from a callback allowQuit: () => boolean to a plain boolean, and DesktopWindow interface gains four new methods that all stubs must implement

Macroscope summarized c585d17.

Summary by CodeRabbit

  • New Features
    • On Windows, closing the desktop window keeps T3 Code and its server running in the system tray. Select Open to reopen the window or Quit to exit the app and stop the server.
  • Bug Fixes
    • Prevented duplicate desktop windows from opening during simultaneous startup and activation.
    • Improved quit and update-recovery behavior so the app can close or resume correctly.

@github-actions github-actions Bot added the vouch:unvouched PR author is not yet trusted in the VOUCHED list. label Aug 19, 2026
@coderabbitai

coderabbitai Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: d6cef67d-2bde-4e2b-9663-273b684d114f

📥 Commits

Reviewing files that changed from the base of the PR and between 95030dc and 17bf27e.

📒 Files selected for processing (15)
  • apps/desktop/src/app/DesktopApp.ts
  • apps/desktop/src/app/DesktopLifecycle.test.ts
  • apps/desktop/src/app/DesktopLifecycle.ts
  • apps/desktop/src/backend/DesktopBackendPool.test.ts
  • apps/desktop/src/ipc/methods/localEnvironment.test.ts
  • apps/desktop/src/main.ts
  • apps/desktop/src/updates/DesktopUpdates.test.ts
  • apps/desktop/src/updates/DesktopUpdates.ts
  • apps/desktop/src/updates/updatesTestHarness.ts
  • apps/desktop/src/window/DesktopApplicationMenu.test.ts
  • apps/desktop/src/window/DesktopTray.test.ts
  • apps/desktop/src/window/DesktopTray.ts
  • apps/desktop/src/window/DesktopWindow.test.ts
  • apps/desktop/src/window/DesktopWindow.ts
  • docs/user/remote-access.md

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The desktop app adds Windows tray controls, background-mode window handling, and serialized main-window creation. Quit handling prepares windows and consumes updater quit permission. Update recovery resets quit preparation. Tests and remote-access guidance cover these changes.

Changes

Windows tray lifecycle

Layer / File(s) Summary
Window background handling and creation
apps/desktop/src/window/DesktopWindow.ts, apps/desktop/src/window/DesktopWindow.test.ts, apps/desktop/src/backend/DesktopBackendPool.test.ts, apps/desktop/src/ipc/methods/localEnvironment.test.ts, apps/desktop/src/window/DesktopApplicationMenu.test.ts
DesktopWindow adds Windows background-mode and quit-preparation controls. It hides windows on close when background mode is enabled and serializes main-window creation. Tests cover close handling, splash behavior, and concurrent creation.
Tray setup and app wiring
apps/desktop/src/window/DesktopTray.ts, apps/desktop/src/window/DesktopTray.test.ts, apps/desktop/src/app/DesktopApp.ts, apps/desktop/src/main.ts, docs/user/remote-access.md
DesktopTray creates a Windows tray when an .ico icon is available, with Open and Quit actions. It enables background mode and cleans up the tray on release. Startup configures the tray, and the remote-access guide describes the Windows behavior.
Quit lifecycle and updater recovery
apps/desktop/src/app/DesktopLifecycle.ts, apps/desktop/src/app/DesktopLifecycle.test.ts, apps/desktop/src/updates/DesktopUpdates.ts, apps/desktop/src/updates/DesktopUpdates.test.ts, apps/desktop/src/updates/updatesTestHarness.ts
Quit handling prepares windows and consumes updater quit permission once. The app does not quit when all windows close in background mode. Update cleanup and recovery reset quit preparation.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant DesktopApp
  participant DesktopTray
  participant ElectronTray
  participant DesktopWindow
  participant ElectronApp
  DesktopApp->>DesktopTray: configure tray
  DesktopTray->>ElectronTray: create tray and menu
  DesktopTray->>DesktopWindow: enable background mode
  ElectronTray->>DesktopTray: deliver Open or Quit action
  DesktopTray->>DesktopWindow: activate window for Open
  DesktopTray->>ElectronApp: run quit effect for Quit
Loading

Possibly related PRs

  • pingdotgg/t3code#10163: Implements similar Windows tray actions and keeps the desktop process running after the last window closes.

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 17bf2

The Windows tray asset and quit recovery paths support the intended close-to-background behavior. No actionable merge blocker remains after normal checks.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 17bf2

The tray preserves remote access when the window closes, but an interrupted update can leave the app running in the tray after its backends have stopped. The remote-access controls were not shown to change; the concern is recovery from that stopped state.

Retained concerns

  • Medium · reliability · inferred: After an update is interrupted following backend shutdown, cleanup restores tray-close behavior without restarting the stopped backends. Closing the window can consequently leave an apparently available background host with remote connections unavailable; before this PR, closing the final Windows window exited the host.
Security review details

Security Blast Radius

  • inferred — The changed lifetime affects the local Windows host and whichever backend instances and remote connections that host has configured. Closing its window no longer terminates that exposure; no new tenant, privilege, or deployment boundary was established by the reviewed changes.

Trust Boundaries and Controls

  • inferred — The tray actions are local desktop controls; the reviewed startup path does not show a new remote caller or an authentication-control change. Authentication inside the remote listener was outside the inspected source scope.

Resilience and Maintainability Implications

  • inferred — An interruption after backend stop can clear quitting state without restoring remote-service availability. The interruption test exercises cancellation during a pending stop, so it does not establish recovery after stops complete.

Hardening Proposals

  • proposed — For an interrupted install after backend shutdown, restore stopped instances and establish readiness before returning to normal tray-close behavior; cover that ordering with a cancellation-after-stop scenario.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 14 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: keeping the Windows desktop host in the system tray.
Description check ✅ Passed The description explains what changed, why it changed, UI behavior, related issues, testing context, and checklist status. It includes a screenshot. The interaction video requested by the template is …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 14 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the size:L 100-499 changed lines (additions + deletions). label Aug 19, 2026
Comment thread apps/desktop/src/window/DesktopTray.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new Windows tray/background workflow and changes close, quit, startup, and updater recovery semantics across several production services. The cross-cutting native lifecycle and backend restart behavior warrants human validation despite focused test coverage.

You can add or adjust custom eligibility rules. Learn more.

@Ivorisnoob

Copy link
Copy Markdown
Contributor Author

On it

Comment thread apps/desktop/src/window/DesktopTray.ts Outdated
Comment thread apps/desktop/src/app/DesktopLifecycle.test.ts
Comment thread apps/desktop/src/window/DesktopTray.ts
@Ivorisnoob

Copy link
Copy Markdown
Contributor Author

PR does touch a lot of files here in my testing on windows in Dev and other modes it is working fine but its upto you guys to decide if we want this feature or not

Comment thread apps/desktop/src/window/DesktopWindow.ts
Comment thread apps/desktop/src/updates/DesktopUpdates.ts
Comment thread apps/desktop/src/updates/DesktopUpdates.ts

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect service conventions review of the tray work. The new DesktopTray service itself follows the canonical shape (subpath namespace imports, inline Context.Service interface, make then layer, all dependencies acquired with yield* Foo.Foo, scoped acquireRelease for the native handle, runPromiseWith only at the Electron callback boundary, and test-only Layer.succeed seams).

Two findings, both about failure modeling: the new failure paths report causes as Cause.pretty(...) text in a log annotation instead of a structured tagged error, which the conventions call out explicitly ("Define service failures with Schema.TaggedErrorClass and structured attributes" / "Do not copy ... arbitrary defect text into ... a parallel log payload. Preserve the exact underlying value only as cause").

Posted via Macroscope — Effect Service Conventions

Comment thread apps/desktop/src/window/DesktopTray.ts Outdated
Comment thread apps/desktop/src/updates/DesktopUpdates.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding on the new tray service: DesktopTrayConfigurationError carries a single-value stage literal that duplicates the information already in the error tag. Everything else (namespace imports, Context.Service + inline interface, make/layer ordering, dependency acquisition via yield*, and the native-callback runPromiseWith boundary mirroring DesktopApplicationMenu.ts) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/desktop/src/window/DesktopTray.ts Outdated
@Ivorisnoob

Copy link
Copy Markdown
Contributor Author

only failing one is the marketing thing and im getting e mails about that LMAO

@Ivorisnoob

Copy link
Copy Markdown
Contributor Author

@StiensWout

Comment thread apps/desktop/src/updates/DesktopUpdates.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b16f4de. Configure here.

Comment thread apps/desktop/src/app/DesktopLifecycle.ts
Ivorisnoob and others added 2 commits September 26, 2026 18:27
…tray

# Conflicts:
#	apps/desktop/src/app/DesktopLifecycle.test.ts
#	apps/desktop/src/backend/DesktopBackendPool.test.ts
#	apps/desktop/src/updates/DesktopUpdates.test.ts
#	apps/desktop/src/updates/DesktopUpdates.ts
#	apps/desktop/src/window/DesktopApplicationMenu.test.ts
#	apps/desktop/src/window/DesktopWindow.test.ts
#	apps/desktop/src/window/DesktopWindow.ts
#	docs/user/remote-access.md

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant