Repository navigation
Description
- I have verified this feature I'm about to request hasn't been suggested before.
Correction: I initially ticked this box by mistake. This has been requested before — see "Existing work" below. The earlier requests were auto-closed for inactivity, so I opened a fresh one as the bot suggested in #27463 / #42349.
Describe the enhancement you want to request
The desktop app has no close-to-tray behaviour: closing the window quits the whole UI process. There is no setting to keep the window alive in the background, and Tray is never used in the main process. Clicking x therefore throws away the UI state (open tabs, unsent drafts, an in-flight streaming response) even though the background service keeps running.
Existing work
- feat(desktop): close to tray instead of exiting #41520 — a v1 PR that implements exactly this (
close-behavior.ts+tray.ts+window-registry.ts, with tests). It was closed unmerged with the note that V1 PRs are no longer accepted, and that it should be ported to v2 if still relevant. I can do that port. - feat(desktop): add menu-bar tray companion #52365 — an open PR adding a menu-bar / system-tray companion (session list, quick actions). It overlaps on the tray icon. If it lands first, this request only needs the close interception plus the setting, reusing that tray.
- feature(desktop): system tray icon with clean shutdown of the background opencode-cli service (Windows) #50633 — open request for a tray that cleanly stops the background
opencode-cliservice. Different goal; a tray icon would be a natural place for it later. - [FEATURE]: Desktop: Close button should minimize to system tray instead of exiting #18134, [FEATURE]: Add close confirmation or minimize-to-tray support for Desktop app #27463, Feature Request: Minimize to system tray on close #35775, Desktop: option to minimize to tray / keep running when window is closed #42349 — earlier requests for the same behaviour, all closed without a resolution (the last two by the 60-day inactivity rule).
Current behaviour (verified on @opencode/desktop 2.1.2, Electron 44.9.1, Windows 11)
window-all-closedcallsapp.quit()on every non-darwin platform.- No
Tray/setSkipTaskbarusage anywhere in the main or renderer bundles. - The renderer close control goes through the
window.closeRPC →BrowserWindow.close(). Windows only listen forready-to-show,closed,focusandsession-end— nothing interceptsclose. - The v2 background service (
opencode-cli serve --service) is an independent, detached process, so the UI is the only thing lost when the window closes.
Proposed behaviour
An opt-in setting (e.g. closeToTray in the desktop settings, default off so existing behaviour is unchanged):
- Closing the window hides it and keeps the process running; a tray icon restores it.
- Tray: left click toggles show/hide, context menu has Show / Hide / Quit OpenCode.
- Explicit quit paths still exit normally: tray Quit,
before-quit, OS session end, updater restart. - Re-launching the executable restores the existing hidden window through the already implemented
second-instancehandler, instead of starting a second process.
Suggested implementation
This can be done entirely with a small addition to the main process, without touching existing logic:
let quitting = false;
app.on("before-quit", () => { quitting = true; });
app.on("browser-window-created", (_event, win) => {
win.on("session-end", () => { quitting = true; }); // OS shutdown / logoff
win.on("close", (event) => {
if (quitting || !settings.closeToTray) return;
event.preventDefault();
win.hide();
});
});
app.whenReady().then(() => {
const tray = new Tray(icon);
tray.setToolTip("OpenCode");
tray.on("click", () => {
const win = BrowserWindow.getAllWindows()[0];
if (win.isVisible() && win.isFocused()) win.hide();
else { win.show(); win.focus(); }
});
tray.setContextMenu(Menu.buildFromTemplate([
{ label: "Show OpenCode", click: showWindow },
{ label: "Hide to tray", click: hideWindow },
{ type: "separator" },
{ label: "Quit OpenCode", click: () => { quitting = true; app.quit(); } },
]));
});hide() removes the taskbar entry as well, and because the windows still exist, the existing window-all-closed → app.quit() path is never reached while the app is resident — so no changes to the lifecycle code are needed.
Verified locally
I prototyped exactly this patch against the extracted 2.1.2 bundle on Windows 11 and confirmed:
- closing the window logs
close intercepted -> hidden, the process stays alive and disappears from the taskbar; - launching the executable again raises the existing window through
second-instance(no second process); - the tray icon is created successfully and a graceful quit still goes through the normal shutdown path.
Happy to turn this into a PR (setting + tray + tests) if the shape of the setting is acceptable to the maintainers.
Additional context
- The TUI and web clients talk to the same background service, so a resident desktop window behaves consistently with them.
- A tray icon is also a natural place for the service actions requested in feature(desktop): system tray icon with clean shutdown of the background opencode-cli service (Windows) #50633 later on.
Activity
opencode-agent commentedon Oct 22, 2026
Thanks for the detailed write-up and for prototyping it on 2.1.2!
To restate the request: you'd like an opt-in setting in the desktop app that hides the window to a system tray icon when you close it, instead of quitting the UI process. That way open tabs, drafts and streaming responses are kept, and you can still quit from the tray menu, at OS session end or when the updater restarts the app. The maintainers will consider it. Since you offered a PR, it may help to agree on the setting's shape with them here first.
Related issues and PRs:
opencode-cliservice.This can't be done as an OpenCode plugin for now, because plugins can't reach the desktop app's main process, which handles the window and the tray.
Qianqian-chun commentedon Oct 22, 2026
Thanks — and good catch on the checklist. I've edited the issue: that box is a factual claim and I shouldn't have ticked it, since this was requested before (#18134, #27463, #35775, #42349, all closed without a resolution). The body now lists the earlier requests and the two related PRs.
On the two PRs:
resolveCloseAction+createTray, with unit tests). If porting it to v2 is the preferred path, I'm happy to do it and keep the same decisions it already encoded: only hide when the last window is closed, only on Windows/Linux, leave macOS close-to-Dock untouched, and let real quit paths (before-quit, tray Quit, signals) close normally. I'd add the setting on top so the behaviour stays opt-in.So the open question for maintainers: should I port #41520 to v2 now, or wait for #52365 and adapt on top of it? Either way I'm fine doing the work.