Skip to content

OR-326 Open the dashboard in its own window on Windows - #439

Merged
myles332 merged 9 commits into
mainfrom
myles/desktop-app-windows
Sep 28, 2026
Merged

myles332 merged 9 commits into
mainfrom
myles/desktop-app-windows

Conversation

@myles332

Copy link
Copy Markdown
Contributor

Phase 2 of the desktop app, stacked on #438 (macOS). Retarget to main once #438 merges.

What changes

  • Launcher: OpenResearch.exe (windows/launcher, its own tiny crate) is a windowed program with the app icon. It starts the orx.exe next to it as orx app with CREATE_NO_WINDOW. orx and every git, shell and agent process it starts share that one hidden console, so none of them flashes a console window. A single windowed orx.exe would have needed CREATE_NO_WINDOW at dozens of spawn sites.
  • orx app reuses the window code from Open the macOS app's dashboard in its own window #438 (src/commands/app.rs): pop-ups go to the browser (http/https/mailto only), downloads use a Save panel, the window appears once the dashboard has loaded, and the port is 4792. On Windows specifically:
    • Closing the window quits: it flushes workspace state, then shuts the server down.
    • A second launch brings the running window forward and exits, via a named mutex and event.
    • The process sets the Start-menu shortcut's AppUserModelID, so the taskbar groups and pins the app correctly.
    • The window and orx.exe get their icon from a resource that embed-resource embeds.
  • Quit: macOS and Windows now quit through up::request_shutdown(), a Notify that shutdown_signal also waits on, instead of the app sending itself SIGTERM. Windows has no SIGTERM, and the stored permit also covers a quit that arrives before the server is ready.
  • Updates: they reuse the CLI's self-update. An orx.exe in %LOCALAPPDATA%\Programs\OpenResearch is a Portable install and swaps itself in place. After a restart the app comes back as orx app on the same port; the new process waits for the old one before claiming the single-instance lock.
  • Installer (windows/OpenResearch.iss): a per-user Inno Setup installer with no admin prompt, a Start-menu entry, an optional desktop shortcut, and a WebView2 bootstrap if the runtime is missing. Uninstall also removes WebView2's data folder.
  • CI: the Windows job now lints the launcher, builds OpenResearch-Setup.exe from that run's orx.exe, and uploads it as the openresearch-windows-installer artifact.
  • Release: release-windows-app.yml attaches an installer built from each release's published orx.exe. It does nothing until the repo variable WINDOWS_APP_ENABLED is true.
  • Docs: docs/windows.md covers the app.

Not included

  • Code signing: the installer gets SmartScreen's "Run anyway" prompt, like orx.exe does today.
  • Launcher updates: the launcher only changes when the app is reinstalled.

Test plan

  • macOS: cargo fmt --check, cargo clippy --all-targets -D warnings, cargo test --locked (923 passed); launcher builds and passes clippy
  • Windows CI: clippy, build and tests pass, and the installer artifact builds
  • Installing from the artifact on a clean Windows 11 user: no admin prompt, Start-menu entry, the app opens in its own window with the right icon
  • Opening a chat and running an experiment shows no console windows
  • Links open in the browser; downloads show the Save dialog; window.confirm prompts work
  • Launching again while it's running brings the window forward, with no second instance
  • Closing the window stops the agents and exits the process
  • An update restart relaunches into a new window on the same port
  • Uninstall removes the app files and the Start-menu entry
  • macOS: Cmd+Q still stops the agents and exits, now through request_shutdown

🤖 Generated with Claude Code

myles332 and others added 4 commits September 24, 2026 16:21
The OpenResearch.app now hosts the dashboard in a native window (tao + wry
WKWebView) instead of handing it to the user's browser. It adds a standard
menu bar, save panels for downloads, a native window.confirm panel, and a
Cmd+Q that flushes workspace state and shuts the server down cleanly.
Pop-ups open in the system browser (http/https/mailto only).

The app now prefers port 4792 so the window's localStorage survives
relaunches. The Dock-click tab-focus script, its Apple-events entitlement,
and the SSE client counter it relied on are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the Windows desktop app. A small GUI-subsystem OpenResearch.exe
(windows/launcher) starts the orx.exe beside it as `orx app` in a hidden
console, so orx and the git, shell, and agent processes it runs share one
invisible console instead of each flashing a window.

`orx app` shares the macOS window code in src/commands/app.rs: WebView2
window, pop-ups to the system browser, save panel, page-load reveal, and
port 4792. On Windows, closing the window quits, a second launch focuses
the running window (named mutex + event), and the taskbar groups the
window with the Start menu shortcut. An update restart relaunches as the
app on the same port. Quit on both platforms now goes through
up::request_shutdown instead of a self-sent SIGTERM.

An Inno Setup script builds a per-user OpenResearch-Setup.exe with a Start
menu entry and a WebView2 bootstrap. CI builds it as an artifact, and
release-windows-app.yml attaches it to releases once WINDOWS_APP_ENABLED
is set. The icon is embedded via embed-resource.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The focus listener captured its bare HANDLE (edition 2021 disjoint
  capture) instead of the Send wrapper, which failed to compile on Windows.
- An orx.exe away from the CLI installer's prefix, like the app's, is now
  Portable even when the installer's receipt exists, so it can update itself.
- Single instance creates its event before the mutex; the launcher hands its
  foreground rights to orx.exe.
- Focus requests and server readiness are ignored while quitting, and focus
  waits for the first load. The save dialog is owned by the window.
- Telemetry counts an app start only after the instance claim.
- The installer reports a failed WebView2 bootstrap and shows progress.
- Icon embedding now fails the build if no resource compiler is found.
- CI format-checks the launcher; the release job drops an unpinned action.
- Docs: maintainer notes for the release gate, two known gaps, and wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Keep the macOS save panel free-floating: only Windows gets the window as
  its owner, since a parent makes rfd show a sheet on macOS.
- Treat the WebView2 bootstrap as failed unless the runtime is then present.
- Move the UTF-16 helper out of the single-instance module, and bind the
  kernel object names before the mutex call that GetLastError follows.
- Docs and a dead_code reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	Cargo.lock
#	ui/dist/assets/index-DLDRL7rV.js
#	ui/dist/assets/index-UAhw--Xc.js
#	ui/dist/assets/index-yT9oNk64.js
#	ui/dist/index.html
@myles332
myles332 changed the base branch from myles/desktop-app to main September 28, 2026 18:41
# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	src/commands/app.rs
#	src/updates.rs
@myles332
myles332 marked this pull request as ready for review September 28, 2026 18:44
@myles332
myles332 requested a review from sox8502 as a code owner September 28, 2026 18:44
@myles332 myles332 changed the title Open the dashboard in its own window on Windows OR-326 Open the dashboard in its own window on Windows Sep 28, 2026
@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 2/5

[High risk] Adds Windows desktop app launcher and build infrastructure.

The PR is not yet safe to merge: Windows child processes can show consoles, and window-loading and download failures can leave users without a usable window or their original file.

Findings

  1. P1 Child consoles can appear ▶
  2. P1 Download deletes existing file ▶
  3. P2 Launch offered without WebView2 ▶
Fix with agent prompt
### Issue 1
windows/launcher/src/main.rs:18
**Child consoles can appear.** On Windows, `CREATE_NO_WINDOW` starts `orx.exe` without an attached console; it does not create a hidden console for its children to share. When the app starts a console program such as `git` or an agent without the same flag, that program can open a visible console window. The update-restart spawn has the same exposure.

### Issue 2
src/commands/app.rs:612-616
**Download deletes existing file.** On Windows, choosing an existing destination removes that file before the WebView saves the replacement. If the download fails or is interrupted, the original is gone too, even though the user only confirmed replacing it. Keep the original until the new download succeeds.

### Issue 3
windows/OpenResearch.iss:102-106
**Launch offered without WebView2.** If the WebView2 bootstrapper cannot be downloaded or installed, setup shows an error but still offers to launch OpenResearch. That launch cannot open the dashboard view, creating a confusing first-run failure. Suppressing the post-install launch when the runtime is missing would give users a clearer outcome.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR adds a per-user Windows desktop installer and launcher, extends the dashboard window and shutdown paths to Windows, and adds CI and release packaging. The launcher’s console behavior, initial window visibility, and download replacement handling need correction before release.

Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A["Start-menu shortcut"] --> B["OpenResearch.exe"]
  B --> C["orx.exe app"]
  C --> D["Dashboard server"]
  C --> E["WebView2 window"]
  D --> F["Git and agent processes"]
  E --> G["Download save dialog"]
  E --> H["Close window"]
  H --> I["Shutdown notification"]
  I --> D
Loading

Reviews (1) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..."

If the dashboard never finishes loading, show the window 15 seconds after the
server is up and say so on stderr; a relaunch (or a Dock click on macOS) also
shows it before the page loads. The installer and uninstaller now check the
app's single-instance mutex and ask the user to close it, rather than
replacing files under a running app and skipping its shutdown path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
std::process::Command::new(exe.with_file_name("orx.exe"))
// WINDOWS_APP_ARG in src/commands/app.rs.
.arg("app")
.creation_flags(CREATE_NO_WINDOW)

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.

P1 Child consoles can appear. On Windows, CREATE_NO_WINDOW starts orx.exe without an attached console; it does not create a hidden console for its children to share. When the app starts a console program such as git or an agent without the same flag, that program can open a visible console window. The update-restart spawn has the same exposure.

Prompt To Fix With AI
This is a comment left during a code review.
Path: windows/launcher/src/main.rs
Line: 18

Comment:
**Child consoles can appear.** On Windows, `CREATE_NO_WINDOW` starts `orx.exe` without an attached console; it does not create a hidden console for its children to share. When the app starts a console program such as `git` or an agent without the same flag, that program can open a visible console window. The update-restart spawn has the same exposure.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment thread src/commands/app.rs Outdated
Comment on lines 612 to 616
Some(chosen) => {
// WebKit fails a download onto an existing file, and the panel
// Neither webview writes over an existing file, and the panel
// has already confirmed replacing it.
if chosen.exists() && std::fs::remove_file(&chosen).is_err() {
return false;

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.

P1 Download deletes existing file. On Windows, choosing an existing destination removes that file before the WebView saves the replacement. If the download fails or is interrupted, the original is gone too, even though the user only confirmed replacing it. Keep the original until the new download succeeds.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/app.rs
Line: 612-616

Comment:
**Download deletes existing file.** On Windows, choosing an existing destination removes that file before the WebView saves the replacement. If the download fails or is interrupted, the original is gone too, even though the user only confirmed replacing it. Keep the original until the new download succeeds.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment thread windows/OpenResearch.iss
Comment on lines +102 to +106
if not Exec(ExpandConstant('{tmp}\MicrosoftEdgeWebview2Setup.exe'), '/silent /install', '',
SW_HIDE, ewWaitUntilTerminated, ResultCode) or not WebView2Installed then
ReportMissingWebView2;
except
ReportMissingWebView2;

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.

P2 Launch offered without WebView2. If the WebView2 bootstrapper cannot be downloaded or installed, setup shows an error but still offers to launch OpenResearch. That launch cannot open the dashboard view, creating a confusing first-run failure. Suppressing the post-install launch when the runtime is missing would give users a clearer outcome.

Prompt To Fix With AI
This is a comment left during a code review.
Path: windows/OpenResearch.iss
Line: 102-106

Comment:
**Launch offered without WebView2.** If the WebView2 bootstrapper cannot be downloaded or installed, setup shows an error but still offers to launch OpenResearch. That launch cannot open the dashboard view, creating a confusing first-run failure. Suppressing the post-install launch when the runtime is missing would give users a clearer outcome.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@greptile-apps

greptile-apps Bot commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

Comments Outside Diff

These findings could not be posted inline.

  • P1 Window can remain hidden src/commands/app.rs:435 ▶

    Window can remain hidden. The Windows window starts hidden and is shown only after a dashboard page reports that it finished loading. If navigation stalls or fails while the server keeps running, the window never appears and the app looks as though it did not start. A bounded load fallback would let users see the window or an error.

… launch without WebView2

Move the file a download replaces aside and restore it if the download fails
or is cancelled (WebView2's flyout can cancel), rather than deleting it up
front. The installer no longer offers to start OpenResearch when the WebView2
Runtime is still missing, since the window could not open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@myles332
myles332 merged commit 2c63f54 into main Sep 28, 2026
15 of 16 checks passed
@myles332
myles332 deleted the myles/desktop-app-windows branch September 28, 2026 21:04
@myles332 myles332 mentioned this pull request Sep 28, 2026
4 tasks
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.

1 participant