Skip to content

Fix double-launch flashing a port-in-use error - #23

Merged
MyNamesEMurray merged 2 commits into
mainfrom
claude/single-instance-focus
Jul 18, 2026
Merged

Fix double-launch flashing a port-in-use error#23
MyNamesEMurray merged 2 commits into
mainfrom
claude/single-instance-focus

Conversation

@MyNamesEMurray

Copy link
Copy Markdown
Owner

Summary

Launching Hype Ghost while it's already running no longer spawns a doomed second instance that errors with "port already in use" — it now just brings the running app to the foreground (recreating the window from tray mode, restoring it if minimized).

Changes

  • Root cause: the single-instance lock existed, but app.quit() is asynchronous — and the app.whenReady().then(...) startup block was registered at top level regardless of the lock. The losing instance therefore still reached startServer(), collided on port 3777, and showed the EADDRINUSE dialog before its queued quit ever landed. The whenReady registration is now gated on holding the lock, so a second launch exits silently.
  • Focus behavior (second-instance in the surviving app): restore if minimized, recreate the window if it was destroyed to tray, then show + focus — "open the program again" now simply means "show me the deck".

Verification

  • Suite (45) + smoke green; syntax checked. The double-launch path itself is Electron-runtime-only (not exercisable in Linux CI): worth a ten-second manual check after release — launch the installed app, launch it again from the Start menu, expect the deck to pop to the front with no error dialog, once from the tray-only state too.

Risk & rollout

  • Single-file change in the desktop shell; the smoke/CI path (HG_SMOKE) holds the lock as the only instance, unchanged.
  • Rides as a patch (no bump trailer).

Checklist

  • npm run smoke passes locally (or CI is green)
  • Follows the design language in DESIGN.md (if UI changed)
  • AI-labeling / "simulated viewers" guarantees intact (if bot output or overlay changed)
  • Docs updated (README / config reference) if behavior or config changed
  • Targets main

🤖 Generated with Claude Code

https://claude.ai/code/session_01FNRQP2h3GUQVThuE2KVBEu


Generated by Claude Code

app.quit() is asynchronous: when a second instance lost the
single-instance lock, the top-level whenReady() handler still ran,
booted a server into the occupied port, and showed the EADDRINUSE
dialog before the queued quit landed. Gate the whenReady registration
on holding the lock so the losing instance exits silently.

The second-instance handler in the surviving app now also restores a
minimized window (tray mode already recreated it) and brings it to the
foreground - relaunching the app is now just 'show me the deck'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FNRQP2h3GUQVThuE2KVBEu
@MyNamesEMurray MyNamesEMurray added the bug Something isn't working label Jul 18, 2026 — with Claude
@MyNamesEMurray
MyNamesEMurray merged commit 728305a into main Jul 18, 2026
2 checks passed
@MyNamesEMurray
MyNamesEMurray deleted the claude/single-instance-focus branch July 18, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants