Skip to content

Feat/windows support - #150

Open
alanpeng wants to merge 17 commits into
deer-flow:mainfrom
alanpeng:feat/windows-support
Open

Feat/windows support#150
alanpeng wants to merge 17 commits into
deer-flow:mainfrom
alanpeng:feat/windows-support

Conversation

@alanpeng

Copy link
Copy Markdown

What

Windows 11 support for the desktop app: the app now builds, runs, and installs on
Windows, packaged as a standard GUI wizard installer (Inno Setup). macOS behavior
is unchanged — every change is guarded by process.platform === "win32".

Highlights

  • Window options per platform — no hiddenInset title bar / traffic lights on
    Windows; native application menu only on macOS; WebView2 default context menu
    suppressed on Windows
  • Auto-update off on Windows — upstream channel builds don't exist yet, so
    auto-update is disabled and the manual check surfaces an explicit
    "updates not supported" error instead of failing silently
  • App icon — Windows icon added to electrobun.config.ts and embedded into
    the exes at build time via rcedit (no new runtime dependencies)
  • Fix: Windows installer tar was invalid — GNU tar's LongLink (L/K)
    entries broke the electrobun extractor; rewritten in pure ustar format
  • Fix: window-state persistence could brick the window on Windows — a
    minimized window saves the Windows minimized-rect (-32000,-32000,<icon>) as
    its frame; the next launch restores it verbatim → off-screen 1px window →
    WebView2 container attach fails ("flash quit"). Saving minimized/invalid
    frames is now skipped, and restored frames are validated against sane bounds
  • Inno Setup wizard installermise run pack:win produces a standard
    Windows GUI installer (PrivilegesRequired=lowest, per-user install;
    uninstall removes the app but keeps ~/.llm-space user data)

Notes

  • No changes to CI workflows or the release pipeline; the macOS build path is
    untouched (this PR's macOS diff is limited to the shared app-code guards)
  • png-to-ico / rcedit are devDependencies used only by the Windows build
    hooks; the wizard installer itself (Inno Setup / ISCC) is a machine-level
    tool detected at build time
  • Local Windows builds are unsigned (SmartScreen warning on first run is
    expected)
  • Base: v4.14.1 (eb4fb64) — happy to rebase if upstream has moved

alanpeng and others added 17 commits August 21, 2026 22:15
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
electrobun 1.18.1's rcedit resolution is broken on Windows (its Bun
bundle records a build-machine path for require.resolve("rcedit/package.json")),
so build.win.icon converts the PNG to ICO but never embeds it into the
launcher, bun runtime, or Setup exe. Work around it with two hooks that
use the rcedit + png-to-ico devDependencies:

- postBuild (embed-windows-icon.ts): embeds the icon into every exe in
  build/<channel>-win-x64/<App>/bin/ before electrobun compresses the
  bundle into the installer archive, so the installed app carries it.
- postPackage (post-package.ts): embeds the icon into the Setup exe,
  re-creates the distribution zip with electrobun's layout, and refreshes
  the artifact copy.

Both no-op off Windows and are removable when upstream fixes the
resolution.

Co-Authored-By: Claude <noreply@anthropic.com>
electrobun compresses the Windows bundle with the system `tar` (GNU tar
on Windows), which emits GNU LongLink entries for paths over 100 chars —
Vite's geist-mono font asset is 102 chars. Its own zig extractor rejects
LongLink with TarUnsupportedFileType, so install fails. The extractor
does support the ustar prefix field, so the postWrap hook
(fix-windows-tar.ts) rewrites both the build-dir archive and the wrapper
bundle's inner archive in ustar format, splitting long names across
name[100] + prefix[155]. postWrap is now a dispatcher (post-wrap.ts)
running fix-x64-headerpad.ts then fix-windows-tar.ts. Windows-only;
removable when upstream fixes the writer or extractor.

Verified: local install simulation with the rebuilt archive completes
"Installation completed successfully!" (extraction, shortcuts, updater
tar).

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
electrobun deletes the app bundle folder after tarring (buildEnvironment
!== "dev"), leaving only the self-extracting wrapper (extractor stub +
inner tar.zst) at build/<channel>-win-x64/LLMSpace-<channel>/; the wizard
hook compiled ISCC from the wrapper, so the installer shipped a bundle
without bin/launcher.exe and failed to launch. At postPackage the only
complete copy is the channel tar.zst: decompress it (helpers moved to a
shared win-tar module), strip the tar's bundle-folder root component so
{app}\bin\launcher.exe lands flat, compile ISCC from a dot-prefixed
staging dir, and clean up after. Channels without a tar.zst (dev) skip.

Co-Authored-By: Claude <noreply@anthropic.com>
…rames

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…blur

The app processes were DPI-unaware, so at any system DPI != 96 Windows
rendered the whole window at 96 DPI and bitmap-scaled it up (e.g. 1280x800
logical -> 1920x1200 physical at 150% scaling), blurring every glyph.
Declare PER_MONITOR_AWARE_V2 via bun:ffi as the first statement of the bun
entry, before any window or WebView exists, so the WebView2 renders at the
real device scale factor. Windows-only; macOS handles Retina natively.

Verified on a 150%-scaled RDP session: process reports DPI-aware, text
renders crisp at 144 DPI, layout size unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
@alanpeng

Copy link
Copy Markdown
Author

Friendly ping for review — happy to iterate on anything.

Quick context on how this was validated beyond CI: it's in daily use on real Windows 11 hardware (WebView2 renderer, per-monitor V2 DPI awareness, window-state restore, deep-link cold-start launcher, trash/reveal, GitHub auth through the system proxy). Packaging runs through the in-repo hooks (icon embed → ustar-safe tar rewrite → Inno Setup wizard with en/zh-CN installer UI, per-user, no UAC), and the auto-update path stays off on Windows until the update feed can serve it.

This is one of three open PRs from us (#151 SearXNG, #152 i18n) — independent of each other, so they can be reviewed and merged in any order. If merging #152 first causes textual conflicts here (both touch menu.ts / updates/index.ts lightly), I'll rebase immediately.

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