Skip to content

fix(chromium-headful): sync clipboard to viewers under implicit hosting#311

Open
breteo wants to merge 1 commit into
kernel:mainfrom
breteo:fix/implicit-hosting-clipboard-sync
Open

fix(chromium-headful): sync clipboard to viewers under implicit hosting#311
breteo wants to merge 1 commit into
kernel:mainfrom
breteo:fix/implicit-hosting-clipboard-sync

Conversation

@breteo

@breteo breteo commented Jul 10, 2026

Copy link
Copy Markdown

Description

Copying inside a remote browser session never reaches the viewer's local clipboard. The neko server only sends remote-clipboard updates to the registered host session, and under implicit hosting the vendored client never sends control/request (remote.ts request() bails because hosting is always true), so no host is ever registered and the updates are silently dropped. Full analysis and a live repro in #310.

This is a port of upstream m1k1o/neko#540 (which fixed the same bug, m1k1o/neko#499) onto the client vendored here:

  • remote.ts: add an explicit-host controlling getter; gate request() on it instead of hosting.
  • video.vue: on mouse interaction while not controlling, buffer the triggering event, send the control request, and replay the event once controlling flips true (@Watch('controlling')). Non-implicit behavior (control-attempt emit) is preserved.

Verification

  • npm run build in images/chromium-headful/client passes.
  • Protocol change validated against a live production Kernel session: manually issuing the exact control/request this patch adds made the server reply control/locked, after which a Ctrl+C in the remote browser produced the control/clipboard push, navigator.clipboard.writeText succeeded, and the local clipboard held the remote selection — end-to-end copy working (details in Copy in the live view never reaches the viewer's clipboard (implicit hosting drops clipboard sync) #310).

Note

Medium Risk
Changes remote session control and input routing in the headful client; behavior is localized but affects who receives server clipboard and when mouse events reach the remote.

Overview
Under implicit hosting, the client treated every viewer as “hosting” without ever sending control/request, so the neko server never registered a host and remote clipboard updates were dropped.

This adds a controlling getter (registered host only) and gates remote.request() on it instead of hosting. In video.vue, the first click while not controlling sends a control request, buffers the mouse down/up, and replays them when controlling becomes true; non-implicit mode still emits control-attempt on mousedown.

Reviewed by Cursor Bugbot for commit 0388493. Bugbot is set up for automated code reviews on this repo. Configure here.

The neko server only delivers remote-clipboard updates to the session
registered as host. Under implicit hosting the client never sends
control/request (the request() action bails because hosting is always
true), so no session ever registers and clipboard updates are dropped —
copying inside the remote browser never reaches the viewer's local
clipboard.

Port upstream m1k1o/neko#540: request control on the first interaction
with the screen (buffering the triggering mouse event and replaying it
once control is granted), and gate request() on an explicit-host
'controlling' getter instead of 'hosting'.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0388493. Configure here.


this.reqMouseDown = null
this.reqMouseUp = null
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete click replay stuck button

Medium Severity

Under implicit hosting, a mousedown on the overlay stores reqMouseDown and sends control/request, but reqMouseUp is only set when onMouseUp runs on the same overlay. If the pointer is released outside the overlay before control is granted, onControlChange replays only onMouseDown, sending a remote mousedown with no matching mouseup.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0388493. Configure here.

}

if (controlling && this.reqMouseUp) {
this.onMouseUp(this.reqMouseUp)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Buffered mouseup replay skipped

Medium Severity

If control is lost after a forwarded mousedown but before mouseup, the physical mouseup clears isMouseDown and only stores reqMouseUp because controlling is false. When controlling becomes true again, @Watch('controlling') replays via onMouseUp, which returns immediately when isMouseDown is false, so the remote never gets mouseup. A later implicit mousedown clears reqMouseUp without flushing that state, leaving a stuck pressed button on the remote.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0388493. Configure here.

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