9 Commits

Author SHA1 Message Date
yan5xu 403f29592f fix(view): allow tab switching when stream is disconnected
- Remove pointer-events:none from dimmed tab/url bar so tabs remain
  clickable even when not connected to a stream.
- Update activeTabId and re-render immediately on click for responsive
  UI, then async invoke stream.switch only if connected.
- Skip stream.switch call when disconnected to avoid errors.
2026-05-25 22:03:31 +08:00
Yanwu e60412f5fd feat(view): heartbeat ping + idle timeout + visibility-based disconnect (#226)
* feat(view): heartbeat ping + idle timeout + visibility-based disconnect

- Send OpPing (0x08) heartbeat every 5s (active) / 10s (idle)
- Visibility change: hidden starts 30s idle countdown, visible cancels
- cleanup(reason): distinct overlay messages for user/idle/network disconnect
- Start heartbeat on DataChannel open, stop on cleanup

* fix(view): use polling instead of setTimeout for idle timeout in hidden tabs

Browsers throttle setTimeout in hidden tabs to >=1 minute (Chrome).
The 30s idle timeout was never firing because the timer was delayed.

Fix: record hiddenSince timestamp on visibilitychange, check elapsed
time on each heartbeat interval tick instead of relying on setTimeout.

* fix(view): fire-and-forget stream.close to prevent disconnect from hanging

disconnect() was awaiting invoke('stream.close') which could hang if
the server is slow or the connection is degraded. This blocked cleanup()
from running, so the UI stayed in 'connected' state and heartbeat
continued indefinitely.

Fix: fire stream.close without awaiting, then immediately cleanup.
2026-05-24 13:40:00 +08:00
yan5xu 822d0788a4 refactor: rename view.* to stream.*, update Clip Web UI
Hub registration:
- view.start → stream.start
- view.answer → stream.answer
- view.close → stream.close
- Add stream.switch (switch streamer to different tab)

Clip Web UI (view.html):
- Tab management: tab_list, tab_new, close (standard daemon commands)
- Tab switching: stream.switch with tab short ID
- Navigation: open/back/forward/reload with --tab
- All nav commands pass activeTabId explicitly

New: getTabCdpUrl() resolves short tab ID → CDP WebSocket URL
2026-05-22 19:46:00 +08:00
yan5xu 6f7aeefa9c fix(view): correct mouse button mapping and clean up dead code
Fix left-click being sent as middle-click: mapButton was converting
Web API button 0 (left) to 1, but bb-viewer uses 0-indexed buttons
(0=left, 1=middle, 2=right). Middle-click on links opens new tabs
in Chrome, which was the root cause of the 'links open in new tab'
bug.

Also remove unused URL bar composition tracking variables and
debug-only url_keydown logging.
2026-05-21 22:54:15 +08:00
yan5xu bf6ab49cf9 fix(view): prevent stuck modifier keys on remote Chrome
Three fixes for modifier keys getting stuck:
- URL input capture-phase keyup no longer stops propagation for
  modifier keys (Shift/Ctrl/Alt/Meta), allowing them to reach the
  document-level onKeyUp handler
- Clicking the video overlay sends keyUp for all modifiers to clear
  any stuck state from URL bar interactions
- Window focus event resets all modifiers to handle Cmd+Tab scenarios

Also add version indicator (v9) for debugging cache issues.
2026-05-21 22:45:20 +08:00
yan5xu 03f7604ae3 feat(view): route tab/nav commands through bb-viewer
Route tab_list, tab_switch, tab_new, tab_close, navigate, back,
forward, reload through bb-viewer API instead of daemon. This ensures
tab operations and video stream operate on the same Chrome instance
and CDP session.

Also fix URL bar: skip polling overwrite when input is focused,
and simplify Enter key handling to avoid IME false suppression.
2026-05-21 20:34:16 +08:00
Yanwu bad09debe1 fix(web): polish remote viewer UI (#223)
- Green connect button, red disconnect state
- Video overlay with centered status text when disconnected
- Input overlay with crosshair cursor for mouse capture
- Tab/URL bar dimmed when not connected
- Slimmer toolbar (32px), webkit scrollbar styling
- Status resolution aligned right
2026-05-21 18:30:43 +08:00
Yanwu 80e4e37056 feat(web): add tab bar, URL bar, and debug panel to remote viewer (#222)
- Tab bar: list/switch/create/close tabs via invoke, 5s polling
- URL bar: address input, back/forward/reload, IME-aware Enter
- Debug panel: event log with timestamps, toggle Ctrl+Shift+D, copy/clear
- Status bar: connection dot, resolution display, debug toggle
- Matches bb-viewer index.html dark theme styling
2026-05-21 18:24:48 +08:00
Yanwu 4815d776c1 feat: remote browser viewing via Clip Web UI (#220)
* feat(daemon): expose CDP host/port in daemon.json and /status

- Add cdpHost, cdpPort to DaemonInfo (optional in shared, required in daemon)
- Write cdpHost/cdpPort from discovered cdpEndpoint to daemon.json
- Return cdpHost/cdpPort in GET /status response
- Enables external tools (bb-viewer) to discover the Chrome CDP connection

* feat(provider): add view.start/answer/close commands + bb-viewer sidecar management

- Register view.start, view.answer, view.close commands in browser clip
- Viewer sidecar: auto-spawn bb-viewer on first view.start, kill on exit
- Read CDP info from daemon.json (cdpHost/cdpPort) for viewer config
- TURN config from env vars: TURN_URL, TURN_USER, TURN_CRED
- bb-viewer binary lookup: ~/.bb-browser/bin/bb-viewer or PATH fallback
- Sidecar health check with 10s startup timeout

* feat(provider): add Clip Web UI for remote browser viewing

- web/view.html: remote viewer page using @pinixai/core/web invoke()
  - WebRTC video with playoutDelayHint=0 for low latency
  - DataChannel binary input: mouse, keyboard, scroll, IME
  - X11 keysym mapping, composition handling
  - Connect/Disconnect with auto-cleanup
- provider: set hasWeb=true for browser clip
- provider: handle GetClipWebCommand to serve web/ files
  - MIME type detection, ETag, range request support
  - Path security validation

* feat(provider): TURN ephemeral credentials via TURN REST API

- Generate time-limited TURN credentials using HMAC-SHA1 shared secret
- Credentials valid for 24h, auto-generated at viewer sidecar spawn
- Config: TURN_URL + TURN_SECRET env vars (no user-facing config needed)
- Replaces static TURN_USER/TURN_CRED with secure ephemeral credentials

* fix(web): replace esm.sh import with direct /api/ fetch

@pinixai/core is a private package, not available on esm.sh.
Use direct fetch to /api/{command} which the Hub's subdomain
handler proxies to provider invoke.
2026-05-21 18:01:49 +08:00