Skip to content

Fix toolbar eye freezing after idle (stale Port) + bump to 0.2.1 - #2

Merged
kkomelin merged 2 commits into
mainfrom
fix/icon-freeze-stale-port
Jun 24, 2026
Merged

kkomelin merged 2 commits into
mainfrom
fix/icon-freeze-stale-port

Conversation

@kkomelin

Copy link
Copy Markdown
Owner

Summary

Fixes the bug where the toolbar eye freezes after the browser sits idle and never recovers, then ships it as 0.2.1.

Root cause was an MV3 service-worker lifecycle trap: the SW idles out after ~30s of quiet, which silently kills the long-lived Port. The content script's onDisconnect is not guaranteed to fire, so it kept holding a dead port whose postMessage neither threw nor delivered - gaze events were dropped forever. Two related stale-state issues in the background SW (a lost blink-reopen timer and a force-center on every cold wake) compounded it.

Changes

  • content.js - Treat a port that's been quiet past PORT_STALE_MS (20s) as stale and rebuild it lazily on the next real event; tear the port down on any send failure. An inbound "hello" ack refreshes the staleness clock. Deliberately no timer-based pings (that would pin the SW alive against its idle-out design).
  • background.js - Ack new connections with a hello message; stop inferring "unsupported tab" from missing-port (unreliable across SW restarts) and measure support directly from the active tab's URL; recover any stranded non-open mode on gaze and via the 1-minute blink alarm; drop the force-center on cold start so a persisted icon isn't yanked to neutral on every SW wake.
  • docs/icon-freeze-investigation.md - Investigation notes documenting the failure modes and the reasoning behind the fix.
  • manifest.json / package.json - Version bump 0.2.0 → 0.2.1 (patch: bug fix only).

Testing

  • npm test

🤖 Generated with Claude Code

kkomelin and others added 2 commits June 23, 2026 14:44
After the MV3 service worker idled out, the content script could be left
holding a non-null but dead Port: postMessage neither threw nor delivered,
and onDisconnect did not fire, so gaze was dropped silently and the eye
froze at its last pose with no recovery on cursor move (SW still active,
no console errors).

- content.js: self-healing port - rebuild when quiet past PORT_STALE_MS
  (under the ~30s SW idle) or on any send failure, instead of trusting a
  single possibly-dead port forever. No periodic ping (would pin the SW
  alive); reconnect is lazy, on the next real event.
- background.js: ack new connections with a `hello` so the content script
  can refresh its staleness clock.
- background.js: center on the active tab's URL (isUnsupportedUrl) rather
  than port presence, so it stays correct across SW restarts and the
  reconnect above does not flicker the eye to center.
- background.js: drop the unconditional force-center on every SW wake; the
  toolbar icon persists the real pose across restarts.
- background.js: recover a stranded mode - gaze resets any non-open mode,
  and the 1-minute alarm clears a lingering `blinking`, so a lost 140ms
  reopen can't leave the eye stuck CLOSED.

No new manifest permissions. Orphan re-injection (needs `scripting`) is
intentionally deferred; that case was ruled out for this report.

Investigation: docs/icon-freeze-investigation.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Patch release carrying the idle-freeze (stale Port) fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kkomelin
kkomelin merged commit 62beffb into main Jun 24, 2026
1 check passed
@kkomelin
kkomelin deleted the fix/icon-freeze-stale-port branch June 24, 2026 16:59
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