Skip to content

fix(windows): unique AppUserModelID for proper taskbar grouping#181

Open
adam2am wants to merge 5 commits intoblackboardsh:mainfrom
adam2am:fix/windows-appusermodelid
Open

fix(windows): unique AppUserModelID for proper taskbar grouping#181
adam2am wants to merge 5 commits intoblackboardsh:mainfrom
adam2am:fix/windows-appusermodelid

Conversation

@adam2am
Copy link

@adam2am adam2am commented Feb 21, 2026

Issue

Hey, so windows taskbar creates duplicate icons for Electrobun apps:

  1. Pin app to taskbar
  2. Launch -> 2 separate icons appear (launcher.exe + bun.exe)
  3. Pinned icon doesn't group with running process
  4. Pinning bun.exe directly fails to launch app
    Root cause: launcher.exe and bun.exe use different AppUserModelID values (or none), so Windows treats them as separate applications.

Solution

Set unique AppUserModelID per app using app.identifier from config:
launcher.exe -> reads identifier -> sets AppUserModelID
->
passes via env var (ELECTROBUN_APP_IDENTIFIER)
->
bun.exe -> reads env var -> sets same AppUserModelID
Shortcuts also include AppUserModelID via COM IPropertyStore API.

Changes

Core Fix

  • src/launcher/main.zig - Read identifier, set AppUserModelID, pass to bun
  • src/native/win/nativeWrapper.cpp - Read env var, set AppUserModelID
  • src/extractor/shortcut_helper.cpp - NEW: COM helper for shortcuts

Build

  • src/extractor/build.zig - Link COM libraries
  • src/launcher/build.zig - Console subsystem (fixes spinning cursor, no more deadlocks/threads during development))

Bug Fixes

  • src/extractor/main.zig - Remove spinner thread (Debug deadlock), add progress dots
  • src/launcher/main.zig - Fix dev console visibility

Testing

Environment: Windows 10 Pro
Results:
Pinning works correctly - single unified taskbar icon, no duplicates, unique ID (from logs) = each Electrobun app has its own identity
✅ Debug mode no deadlock
✅ Dev console shows correctly in dev mode
✅ No breaking changes (Linux/macOS should be unaffected)

beforePR.mp4
afterPR.mp4

…ning

Fixes issue where all Electrobun apps shared the same AppUserModelID,
causing Windows taskbar to confuse different apps and break pinning.

Changes:
- Read app identifier from version.json (DRY approach)
- Set unique AppUserModelID on both launcher.exe and bun.exe
- Pass identifier from launcher to bun via environment variable
- Create shortcuts with AppUserModelID using COM APIs
- Hide launcher console with ShowWindow instead of GUI subsystem

Technical details:
- AppUserModelID format: use identifier directly (e.g., 'svelteapp.electrobun.dev1')
- Environment block: use Zig's proven pattern from std/process.zig
- Timing: AppUserModelID set BEFORE any UI (critical for Windows)
- Shortcuts: IShellLink + IPropertyStore COM APIs
- Logging: %TEMP% logs for production diagnostics
- Backwards compatible: fallback to 'com.electrobun.app' if identifier missing

Tested:
- Release mode: User confirmed pinning works perfectly
  * unpin → pin → close → open → synchronized ✅
  * Logs show correct identifier in launcher, bun, and shortcuts
- Debug mode: Known issue - deadlock in extractor spinner thread
  * Release builds unaffected, production-ready
  * Debug deadlock requires investigation (spinner thread + console I/O)
- No breaking changes to Linux (LD_LIBRARY_PATH preserved)
- macOS: env_map refactoring is DRY, functionally equivalent

Files changed:
- src/launcher/main.zig: DRY version reading, env block, AppUserModelID
- src/launcher/build.zig: Console subsystem (no GUI)
- src/native/win/nativeWrapper.cpp: Read env var, set AppUserModelID
- src/extractor/main.zig: COM shortcut creation
- src/extractor/build.zig: Link C++ helper + COM libraries
- src/extractor/shortcut_helper.cpp: NEW FILE - COM helper

Known Issues:
- Debug mode has deadlock in extractor (spinner thread)
- Release mode works perfectly, production-ready
Removes threaded spinner that caused deadlock in Debug mode due to
console I/O lock contention. Replaces with real progress dots during
decompression (every 10MB) and milestone dots after major operations.

No threading, no deadlock, real progress feedback.
Makes console visibility conditional on channel field. Dev channel
builds keep console visible for debugging, production builds hide it.

Removes AttachConsole logic (no longer needed since dev console isn't
hidden at startup). Robust solution that works for both double-click
and cmd launch.
Restores original maintainer comments that don't contradict the new
implementation. Makes future upstream merges cleaner by preserving
the maintainer's documentation intent.

Restored comments:
- Version.json reading and parsing flow
- CEF library handling on Linux
- Environment setup for Windows/macOS
- Console attachment for dev mode
- CreateProcessW parameter documentation

Minor adjustment: Updated 'reading version.json' to 'reading version_info.channel'
to match the new implementation (version.json is now parsed once upfront).
Removes leftover milestone dots from previous approach. Progress is now
shown via decompression dots (every 10MB), making these redundant.
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