@omercnet/paseo-shared-browser 1.2.2 → 2.0.0-next.249.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/OVERVIEW.md +2 -2
- package/README.md +316 -27
- package/REMOTE_BROWSER_RESEARCH.md +395 -0
- package/STREAMING_DESIGN.md +196 -0
- package/client/browser-auto-capture-density.test.ts +102 -0
- package/client/browser-auto-capture-density.ts +30 -0
- package/client/browser-canvas-input.test.ts +198 -0
- package/client/browser-canvas-input.ts +202 -0
- package/client/browser-canvas-keyboard-web.test.ts +113 -0
- package/client/browser-canvas-keyboard-web.ts +230 -0
- package/client/browser-canvas-keyboard.test.ts +123 -0
- package/client/browser-canvas-keyboard.ts +126 -0
- package/client/browser-canvas-layout.test.ts +109 -0
- package/client/browser-canvas-layout.ts +51 -0
- package/client/browser-canvas-viewport-web.test.ts +77 -0
- package/client/browser-canvas-viewport-web.ts +63 -0
- package/client/browser-canvas-viewport.tsx +152 -0
- package/client/browser-capture-cadence.test.ts +9 -0
- package/client/browser-capture-cadence.ts +6 -0
- package/client/browser-capture-density-controls.tsx +75 -0
- package/client/browser-chrome.tsx +234 -0
- package/client/browser-client-presence.ts +184 -0
- package/client/browser-compose-controls.test.tsx +275 -0
- package/client/browser-compose-controls.tsx +174 -0
- package/client/browser-control-button.tsx +109 -0
- package/client/browser-control-dialog.tsx +71 -0
- package/client/browser-control-request.test.ts +27 -0
- package/client/browser-control-request.ts +57 -0
- package/client/browser-control-tooltip-web.ts +10 -0
- package/client/browser-dialog-body.tsx +22 -0
- package/client/browser-emulation-mode.test.ts +75 -0
- package/client/browser-emulation-mode.ts +98 -0
- package/client/browser-fill-viewport.test.ts +45 -0
- package/client/browser-fill-viewport.ts +28 -0
- package/client/browser-frame-buffer.test.ts +157 -0
- package/client/browser-frame-buffer.ts +148 -0
- package/client/browser-frame-image.tsx +62 -0
- package/client/browser-image-load.test.ts +23 -0
- package/client/browser-image-load.ts +26 -0
- package/client/browser-input-queue.test.ts +1254 -0
- package/client/browser-input-queue.ts +519 -0
- package/client/browser-menu-placement.test.ts +90 -0
- package/client/browser-menu-placement.ts +78 -0
- package/client/browser-panel-styles.ts +384 -0
- package/client/browser-quality-controls.test.ts +26 -0
- package/client/browser-quality-controls.tsx +139 -0
- package/client/browser-resolution-picker.tsx +189 -0
- package/client/browser-tabs.tsx +117 -0
- package/client/browser-toolbar-menu-web.test.ts +185 -0
- package/client/browser-toolbar-menu-web.ts +159 -0
- package/client/browser-toolbar-menu.tsx +255 -0
- package/client/browser-toolbar-submenu.tsx +93 -0
- package/client/browser-video-bytes.test.ts +44 -0
- package/client/browser-video-currency.ts +45 -0
- package/client/browser-video-decoder.test.ts +473 -0
- package/client/browser-video-decoder.ts +311 -0
- package/client/browser-video-surface.tsx +21 -0
- package/client/browser-video-visibility.test.ts +45 -0
- package/client/browser-viewer-recovery.test.ts +111 -0
- package/client/browser-viewer-recovery.ts +78 -0
- package/client/browser.tsx +1650 -1579
- package/client/favorite-star.tsx +23 -0
- package/client/frame-lifecycle.ts +62 -59
- package/client/sidebar.tsx +1 -2
- package/client/use-browser-auto-capture-density.test.ts +104 -0
- package/client/use-browser-auto-capture-density.ts +43 -0
- package/client/use-browser-canvas-input.ts +375 -0
- package/client/use-browser-capture-density.ts +87 -0
- package/client/use-browser-emulation-mode.ts +56 -0
- package/client/use-browser-fill-viewport.test.ts +108 -0
- package/client/use-browser-fill-viewport.ts +62 -0
- package/client/use-browser-frame-buffer.ts +66 -0
- package/client/use-browser-image-capture.ts +139 -0
- package/client/use-browser-scoped-mutation.test.ts +118 -0
- package/client/use-browser-scoped-mutation.ts +59 -0
- package/client/use-browser-video.test.ts +1060 -0
- package/client/use-browser-video.ts +481 -0
- package/client/use-browser-viewer-recovery.ts +26 -0
- package/client/use-resolution-favorites.ts +90 -0
- package/client/web.test.ts +125 -0
- package/client/web.ts +405 -0
- package/index.client.tsx +0 -10
- package/index.server.ts +32 -4
- package/npm-shrinkwrap.json +9 -9
- package/package.json +10 -5
- package/paseo-plugin.json +1 -1
- package/scripts/prepare-runtime.mjs +1 -1
- package/server/agent-browser-runtime.ts +1601 -314
- package/server/browser-cursor.test.ts +64 -0
- package/server/browser-cursor.ts +69 -0
- package/server/browser-frame-context.ts +22 -0
- package/server/browser-frame-epoch.test.ts +95 -0
- package/server/browser-frame-rejection.ts +17 -0
- package/server/browser-gesture.ts +120 -0
- package/server/browser-operation-diagnostics.test.ts +79 -0
- package/server/browser-operation-diagnostics.ts +103 -0
- package/server/browser-policy.ts +1316 -151
- package/server/browser-video-input-authority.test.ts +211 -0
- package/server/browser-video-policy.test.ts +564 -0
- package/server/browser-video-socket.test.ts +368 -0
- package/server/browser.ts +151 -16
- package/server/capture-transport-policy.test.ts +105 -0
- package/server/capture-transport-policy.ts +154 -0
- package/server/capture-transport-runtime.test.ts +213 -0
- package/server/daemon-launch-chain.test.ts +70 -0
- package/server/daemon-shutdown.test.ts +289 -0
- package/server/discrete-input-freshness.test.ts +364 -0
- package/server/discrete-input-native-lifetime.test.ts +150 -0
- package/server/display-loss-recovery.test.ts +294 -0
- package/server/emulation-restoration.test.ts +262 -0
- package/server/input-cleanup.ts +23 -0
- package/server/input-generation.test.ts +31 -0
- package/server/input-generation.ts +36 -0
- package/server/jpeg-capture-demand.test.ts +76 -0
- package/server/jpeg-capture-demand.ts +98 -0
- package/server/jpeg-frame.ts +29 -0
- package/server/keyboard-input.test.ts +61 -0
- package/server/keyboard-input.ts +104 -0
- package/server/live-input-policy.test.ts +958 -0
- package/server/live-input-runtime.test.ts +732 -0
- package/server/live-input-supervisor.test.ts +147 -0
- package/server/mcp-entry.ts +44 -2
- package/server/native-video-backpressure.test.ts +228 -0
- package/server/native-video-buffer.test.ts +141 -0
- package/server/native-video-buffer.ts +114 -0
- package/server/native-video-capture.test.ts +599 -0
- package/server/native-video-capture.ts +497 -0
- package/server/native-video-extension-source.test.ts +201 -0
- package/server/native-video-extension-source.ts +364 -0
- package/server/native-video-extension.test.ts +41 -0
- package/server/native-video-extension.ts +83 -0
- package/server/native-video-packet.ts +9 -0
- package/server/native-video-settings.test.ts +245 -0
- package/server/navigation-budget.ts +8 -0
- package/server/navigation-commit.ts +125 -0
- package/server/navigation-policy.test.ts +153 -0
- package/server/process-identity.ts +58 -0
- package/server/reattach-held-input.test.ts +143 -0
- package/server/runtime-frame-reuse.test.ts +295 -0
- package/server/runtime-jpeg-demand.test.ts +200 -0
- package/server/runtime-jpeg-shutdown.test.ts +210 -0
- package/server/runtime-jpeg-source-order.test.ts +201 -0
- package/server/runtime-launch-environment.test.ts +111 -0
- package/server/runtime-navigation.test.ts +327 -0
- package/server/runtime-owner-display.test.ts +152 -0
- package/server/runtime-owner.ts +340 -31
- package/server/runtime-page-state.test.ts +249 -0
- package/server/runtime-protocol.ts +32 -2
- package/server/runtime-target-retention.test.ts +181 -0
- package/server/runtime-video-opt-in.test.ts +82 -0
- package/server/runtime-video.test.ts +444 -0
- package/server/screencast-source-time.ts +46 -0
- package/server/stale-wheel-admission.test.ts +136 -0
- package/server/supervisor-client.ts +28 -4
- package/server/supervisor-heartbeat.test.ts +80 -0
- package/server/supervisor.ts +553 -55
- package/server/tab-close-confirmation.test.ts +97 -0
- package/server/video-source-recovery.test.ts +20 -0
- package/server/video-source-recovery.ts +64 -0
- package/server/viewer-capture-lifetime.ts +51 -0
- package/server/virtual-display.test.ts +184 -0
- package/server/virtual-display.ts +236 -0
- package/shared/browser-display-preferences.ts +46 -0
- package/shared/browser-video.ts +83 -0
- package/shared/browser.ts +286 -65
- package/shared/capture-density.ts +16 -0
- package/shared/capture-settings.ts +17 -0
- package/shared/device-presets.ts +90 -0
- package/shared/resolution-menu.test.ts +120 -0
- package/shared/resolution-menu.ts +72 -0
- package/shared/video-settings.test.ts +38 -0
- package/shared/video-settings.ts +34 -0
- package/shared/viewport-limits.ts +5 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# Shared Browser: established remote-control designs and dropped-click research
|
|
2
|
+
|
|
3
|
+
Research date: 2026-10-03 (America/Toronto).
|
|
4
|
+
Status: continuous human input implemented. Network and native-phone latency require separate measurement.
|
|
5
|
+
|
|
6
|
+
## Recommendation
|
|
7
|
+
|
|
8
|
+
Use the established remote-control input model for the human pane: an ordered
|
|
9
|
+
input channel owned by the current controller and browser geometry, independent
|
|
10
|
+
of individual video frames. Retain explicit screenshot/frame targeting for
|
|
11
|
+
agent commands. Fix the demonstrated visual-frame/input-frame waiter mismatch
|
|
12
|
+
as part of that separation, rather than adding longer capture waits.
|
|
13
|
+
|
|
14
|
+
Evaluate native browser WebRTC media from the existing tab-capture track next.
|
|
15
|
+
Compare it against a binary stream carried through Paseo's authenticated relay.
|
|
16
|
+
Select by measured interaction latency, clarity and relay compatibility, not
|
|
17
|
+
by assuming WebRTC solves input policy or guarantees sharper text.
|
|
18
|
+
|
|
19
|
+
Continuous human input intentionally allows normal presses without requiring
|
|
20
|
+
pixels captured after the preceding press. A remote display can
|
|
21
|
+
lag the page, as in ordinary remote desktop. Keep controller, exact target,
|
|
22
|
+
geometry, visibility, replacement-page and disconnect fences. Do not silently
|
|
23
|
+
change agent screenshot-targeting semantics or replay clicks after timeouts.
|
|
24
|
+
|
|
25
|
+
## Demonstrated failure path matching the reported error
|
|
26
|
+
|
|
27
|
+
The preceding human channel inherited frame-target rules from discrete browser
|
|
28
|
+
automation. Every new mouse/touch press needs a decoded targeting receipt.
|
|
29
|
+
After non-hover input the server clears recent receipts and imposes a
|
|
30
|
+
post-input capture-time floor. The client also invalidates its input revision.
|
|
31
|
+
This creates a required trip through capture, transport, decode and React
|
|
32
|
+
presentation between successive independent presses.
|
|
33
|
+
|
|
34
|
+
A second, narrower mismatch is demonstrated in the production panel:
|
|
35
|
+
|
|
36
|
+
1. The rapid-input fix allows a delayed read to paint if its source, document
|
|
37
|
+
and geometry remain current. This preserves video continuity.
|
|
38
|
+
2. That paint retains its old input revision and correctly cannot authorize a
|
|
39
|
+
new press.
|
|
40
|
+
3. The canvas input hook's waiter watches a changed *visible* frame ID instead
|
|
41
|
+
of a changed actionable targeting receipt.
|
|
42
|
+
4. It wakes, the queue finds no current targeting authority, and emits
|
|
43
|
+
`A current decoded frame is required for a new press.`
|
|
44
|
+
|
|
45
|
+
A disposable mounted React/Query reproduction used the real panel and a DOM
|
|
46
|
+
mouse-down. It produced that exact error, sent zero gesture updates, and kept
|
|
47
|
+
Video visible. This confirms a rejected unsent press, not proof of a lost
|
|
48
|
+
published native click. The reproduction did not access the live browser.
|
|
49
|
+
Fixing only its waiter would avoid premature rejection but retain deliberate
|
|
50
|
+
frame-per-press delays. Increasing the four-second wait would not remove that
|
|
51
|
+
coupling.
|
|
52
|
+
|
|
53
|
+
Relevant local owners:
|
|
54
|
+
|
|
55
|
+
- `client/browser.tsx`: passes visible decoded frame identity to the canvas hook.
|
|
56
|
+
- `client/use-browser-video.ts`: permits same-source visual continuity but
|
|
57
|
+
retains the original read revision for input authority.
|
|
58
|
+
- `client/use-browser-canvas-input.ts`: resolves waiters when visible frame ID changes.
|
|
59
|
+
- `client/browser-input-queue.ts`: awaits targeting receipts and each gesture RPC.
|
|
60
|
+
- `server/browser-policy.ts`: requires recent press targets, clears receipts
|
|
61
|
+
after input, and applies the source-clock floor.
|
|
62
|
+
|
|
63
|
+
The failure description records the behavior before continuous admission was
|
|
64
|
+
implemented. The current implementation separates presentation from initial
|
|
65
|
+
input admission and is covered by the mounted panel fixtures.
|
|
66
|
+
|
|
67
|
+
## Established implementations
|
|
68
|
+
|
|
69
|
+
These are primary-source findings. They describe the reviewed paths, not a
|
|
70
|
+
claim that every project is free of race conditions or input bugs.
|
|
71
|
+
|
|
72
|
+
| System | Input and display relationship | What is reusable | Cost of adopting the whole system |
|
|
73
|
+
| --- | --- | --- | --- |
|
|
74
|
+
| Chrome DevTools screencast | CDP events use decoded screen scale/offset; ordinary input does not wait for a new image after each event. | Closest source reference for our CDP coordinate mapping, modifiers, click count and drag geometry. | Frontend depends on DevTools SDK and UI, so importing the whole panel is inappropriate. |
|
|
75
|
+
| noVNC | Connection/view-only gates and framebuffer coordinates govern pointer sends; render flushing is separate. | Mature keyboard, cursor, gesture and RFB client code. | Requires an RFB/VNC server and normally a WebSocket gateway, replacing our tab-capture/CDP backend. |
|
|
76
|
+
| KasmVNC | Connected/view-only input is separate from its bounded asynchronous display queue. | Browser fork and media queue patterns. | Full use adds an Xvnc backend and protocol extensions; browser core is MPL-2.0, server GPLv2. |
|
|
77
|
+
| Apache Guacamole | Input sends are tunnel/session based; display flush and sync acknowledge rendering separately. | Standalone JavaScript client and input/display primitives. | Full use adds guacd plus a supported remote desktop backend and authenticated tunnel integration. |
|
|
78
|
+
| Selkies | Current design defaults to WebSocket/WebCodecs; WebRTC is optional. Human input is separately sequenced and gated by controller/token state. | Closest architectural reference for bounded streaming, input ordering, workers and WebCodecs. | Full runtime adds Linux display capture/input, Python and Rust media components. |
|
|
79
|
+
| Neko | WebRTC browser streaming with current host ownership and overlay-to-remote coordinate mapping. | Strong reference for shared control, native cursor and media/data separation. | Complete system adds a containerized desktop, Go/GStreamer/X11 and its room/session model. |
|
|
80
|
+
| Epic Pixel Streaming | Mouse handlers check video readiness, then send structured events; no decoded-frame ID on each press. | MIT TypeScript input/coordinate and WebRTC player references. | Complete player and server speak Unreal-specific signaling/messages, not our CDP protocol. |
|
|
81
|
+
| Chromium remoting | Host event dispatch validates incoming event fields and injects them, with event timing separate from display rendering. | Reference for input ordering, held-state cleanup and transport backpressure. | Chromium remoting is a substantial native subsystem, not a small embeddable Node library. |
|
|
82
|
+
|
|
83
|
+
### Closest reference: Chrome DevTools
|
|
84
|
+
|
|
85
|
+
In the inspected screencast, image load updates screen scale/offset. Mouse and
|
|
86
|
+
wheel handlers forward through `InputModel`; input does not invalidate that
|
|
87
|
+
mapping until another image arrives. A drag retains its starting vertical
|
|
88
|
+
mapping until release. Profiling/inactive-target overlays can block input.
|
|
89
|
+
This is readiness and mapping, not an after-every-input freshness protocol.
|
|
90
|
+
|
|
91
|
+
Reviewed pinned source:
|
|
92
|
+
[InputModel](https://github.com/ChromeDevTools/devtools-frontend/blob/ae714aac9f0a7c8683fa4dff5a7dcaf55c233ad4/front_end/panels/screencast/InputModel.ts),
|
|
93
|
+
[ScreencastView](https://github.com/ChromeDevTools/devtools-frontend/blob/ae714aac9f0a7c8683fa4dff5a7dcaf55c233ad4/front_end/panels/screencast/ScreencastView.ts).
|
|
94
|
+
Both carry Chromium's BSD-style source notice. Treat them as a reference or
|
|
95
|
+
carefully attributed small extraction, not an interchangeable frontend package.
|
|
96
|
+
|
|
97
|
+
### Rendering acknowledgements are not input admission
|
|
98
|
+
|
|
99
|
+
In released noVNC 1.7.0, pointer sends check connection and view-only state;
|
|
100
|
+
button handlers flush pending motion before a transition. The inbound render
|
|
101
|
+
flush path is separate. In Guacamole 1.6.0, mouse/key/touch sends use session
|
|
102
|
+
readiness and display scaling, while display flush/sync acknowledges rendered
|
|
103
|
+
operations. Neither reviewed send path demands a decoded frame token per press.
|
|
104
|
+
Guacamole touch availability still depends on the remote backend.
|
|
105
|
+
|
|
106
|
+
Primary sources:
|
|
107
|
+
[noVNC 1.7.0 RFB](https://github.com/novnc/noVNC/blob/63107bd06d9e1f6136ff21aeda8cd62cbf0d433e/core/rfb.js#L1172-L1225),
|
|
108
|
+
[Guacamole 1.6.0 client](https://github.com/apache/guacamole-client/blob/0537c89cd783681b986ff8f8c4e0b97ec6873371/guacamole-common-js/src/main/webapp/modules/Client.js#L328-L430),
|
|
109
|
+
[Guacamole JavaScript integration](https://guacamole.apache.org/doc/gug/guacamole-common-js.html).
|
|
110
|
+
An RFB fence or display sync is not our exact browser-document receipt policy;
|
|
111
|
+
copying a protocol component would not preserve that policy automatically.
|
|
112
|
+
|
|
113
|
+
KasmVNC's exact browser submodule similarly separates mouse sends from its
|
|
114
|
+
three-slot asynchronous frame queue, which can discard incomplete older display
|
|
115
|
+
frames. Its [input path](https://github.com/kasmtech/noVNC/blob/1746dfe9ed8084aee57b55c720ef00d4d7c26513/core/rfb.js#L2484-L2508)
|
|
116
|
+
and [display drop handling](https://github.com/kasmtech/noVNC/blob/1746dfe9ed8084aee57b55c720ef00d4d7c26513/core/display.js#L1146-L1223)
|
|
117
|
+
are useful references. The browser [MPL-2.0 license](https://github.com/kasmtech/noVNC/blob/1746dfe9ed8084aee57b55c720ef00d4d7c26513/LICENSE.txt)
|
|
118
|
+
and server [GPLv2 license](https://github.com/kasmtech/KasmVNC/blob/39b94e68ddc302dd896008b04a71e6115eb62d06/LICENSE.TXT)
|
|
119
|
+
are separate; adopting the full stack is a packaging/licensing choice.
|
|
120
|
+
|
|
121
|
+
### Input ordering is a separate contract
|
|
122
|
+
|
|
123
|
+
Selkies' reviewed WebRTC code uses an ordered input channel and a separate
|
|
124
|
+
unordered pointer channel, plus sequence checks. Its bounded 512-message input
|
|
125
|
+
queue can discard messages when overloaded. Neko's reviewed client sends
|
|
126
|
+
ordered reliable binary events, positions before button transitions, and checks
|
|
127
|
+
host ownership on the server. Neither path carries a per-click decoded-frame
|
|
128
|
+
ID. These are useful designs, not unconditional no-loss guarantees or proof of
|
|
129
|
+
our stronger document/geometry fencing.
|
|
130
|
+
|
|
131
|
+
Pinned primary sources:
|
|
132
|
+
[Selkies channels](https://github.com/selkies-project/selkies/blob/5927ca77b1fc31d43ebca096b2aa59e35bbb128f/src/selkies/webrtc_engine.py#L2536-L2564),
|
|
133
|
+
[Selkies input queue](https://github.com/selkies-project/selkies/blob/5927ca77b1fc31d43ebca096b2aa59e35bbb128f/src/selkies/webrtc_engine.py#L2030-L2075),
|
|
134
|
+
[Neko client transport](https://github.com/m1k1o/neko/blob/bdd0428a607ccfd22eebabc74b85ae17a4cb42d5/client/src/neko/base.ts#L187-L245),
|
|
135
|
+
[Neko controller gate](https://github.com/m1k1o/neko/blob/bdd0428a607ccfd22eebabc74b85ae17a4cb42d5/server/internal/webrtc/handler.go#L16-L100).
|
|
136
|
+
|
|
137
|
+
### Video readiness is not press freshness
|
|
138
|
+
|
|
139
|
+
Pixel Streaming's `isVideoReady()` checks the media element's `readyState > 0`.
|
|
140
|
+
Mouse-down/up/wheel then send normalized messages, without a last-decoded-frame
|
|
141
|
+
token. Its default data channel is ordered. This supports an established stream
|
|
142
|
+
being interactive while rendering catches up, not the stronger promise that a
|
|
143
|
+
press targets the pixels most recently captured after another press.
|
|
144
|
+
|
|
145
|
+
Reviewed pinned source:
|
|
146
|
+
[VideoPlayer](https://github.com/EpicGames/PixelStreamingInfrastructure/blob/85f15cb354c8ccbfa1e8f20d9600e4d75dbce492/Frontend/library/src/VideoPlayer/VideoPlayer.ts),
|
|
147
|
+
[hovering mouse controller](https://github.com/EpicGames/PixelStreamingInfrastructure/blob/85f15cb354c8ccbfa1e8f20d9600e4d75dbce492/Frontend/library/src/Inputs/MouseControllerHovering.ts),
|
|
148
|
+
[data channel](https://github.com/EpicGames/PixelStreamingInfrastructure/blob/85f15cb354c8ccbfa1e8f20d9600e4d75dbce492/Frontend/library/src/DataChannel/DataChannelController.ts).
|
|
149
|
+
Its [MIT infrastructure license](https://github.com/EpicGames/PixelStreamingInfrastructure/blob/85f15cb354c8ccbfa1e8f20d9600e4d75dbce492/LICENSE.md)
|
|
150
|
+
is distinct from licensing the Unreal Engine application.
|
|
151
|
+
|
|
152
|
+
## Design changes worth adopting
|
|
153
|
+
|
|
154
|
+
### Separate control readiness from frame targeting
|
|
155
|
+
|
|
156
|
+
Initial human channel admission should require a decoded current source and
|
|
157
|
+
geometry basis, as it does today. The channel should establish exact workspace/viewer/controller,
|
|
158
|
+
browser incarnation, target, document policy and CSS viewport mapping once.
|
|
159
|
+
Subsequent pointer/key transitions use ordered sequence numbers and that control
|
|
160
|
+
context. Video decoding may lag without revoking every independent press.
|
|
161
|
+
A viewer without control still cannot inject events.
|
|
162
|
+
|
|
163
|
+
Agent click tools retain their explicit frame receipts. Server authorization
|
|
164
|
+
must enforce the distinction; do not add a caller-selected flag that turns an
|
|
165
|
+
agent request into unchecked human input. A human session can require a
|
|
166
|
+
controller-owned admission token issued for the pane and its displayed geometry.
|
|
167
|
+
|
|
168
|
+
Maintain separate values for visible frame, actionable agent receipt and
|
|
169
|
+
human channel readiness. A visible old-read frame must neither invent an agent
|
|
170
|
+
receipt nor falsely resolve a wait for one. Renderer state should report video
|
|
171
|
+
stall separately from control ownership. A genuinely disconnected/frozen source
|
|
172
|
+
can disable new input under an explicit session policy; an ordinary view update
|
|
173
|
+
must not be treated as a disconnect.
|
|
174
|
+
|
|
175
|
+
### Preserve transitions; coalesce motion
|
|
176
|
+
|
|
177
|
+
Mouse down/up, key down/up, text commits, touch start/end and cancellation are
|
|
178
|
+
ordered transitions. Never drop one because the next video frame is pending.
|
|
179
|
+
Coalesce only compatible moves or wheel deltas within the same control context,
|
|
180
|
+
without crossing a press/release or document/viewport boundary. Flush preceding
|
|
181
|
+
motion before a transition so the press lands at the intended coordinates.
|
|
182
|
+
|
|
183
|
+
Held input needs pointer capture and a release path even when the pointer leaves
|
|
184
|
+
the pane. Blur, controller loss, teardown and rejected sequences release held
|
|
185
|
+
state. Double-click forwarding must not synthesize a duplicate press from both
|
|
186
|
+
DOM down/up and the `dblclick` callback.
|
|
187
|
+
|
|
188
|
+
### Do not turn input into a chain of network round trips
|
|
189
|
+
|
|
190
|
+
Our queue awaits each update RPC before sending the next. That is an additional
|
|
191
|
+
throughput limit even after frame coupling is removed. For an event stream whose
|
|
192
|
+
individual request/response round trip is R, that loop cannot exceed roughly
|
|
193
|
+
1/R acknowledged updates per second. This is a design inference, not a measured
|
|
194
|
+
relay RTT or observed throughput result.
|
|
195
|
+
|
|
196
|
+
Use a bounded ordered stream or bounded event batches, with sequence
|
|
197
|
+
acknowledgements, instead of waiting for a separate round trip after every key
|
|
198
|
+
and mouse transition. The server still serializes native injection. Disconnect
|
|
199
|
+
or uncertain publication terminates the channel; it never replays presses.
|
|
200
|
+
Queue pressure must release/cancel cleanly rather than accumulating seconds of
|
|
201
|
+
old clicks that later fire on a changed page.
|
|
202
|
+
|
|
203
|
+
## Streaming choices and actual reuse candidates
|
|
204
|
+
|
|
205
|
+
### A. Native WebRTC from the existing Chromium tab capture
|
|
206
|
+
|
|
207
|
+
The extension already owns an exact-tab `MediaStream`. A prototype can pass its
|
|
208
|
+
video track to `RTCPeerConnection`, display the receiver's media track in a
|
|
209
|
+
web-only adapter, and use existing authenticated Paseo RPC for signaling.
|
|
210
|
+
This would let the browser own media packetization, congestion response,
|
|
211
|
+
keyframe recovery and playout instead of our custom encoded packet reader.
|
|
212
|
+
This is a proposed fit, not a proved drop-in or a measured latency improvement.
|
|
213
|
+
|
|
214
|
+
The [tabCapture API](https://developer.chrome.com/docs/extensions/reference/api/tabCapture)
|
|
215
|
+
provides the capture primitive. WebRTC still needs authenticated negotiation,
|
|
216
|
+
viewer/controller fencing and source identity. Direct ICE paths may work on
|
|
217
|
+
LANs; remote relay users require TURN coverage for networks that cannot connect
|
|
218
|
+
peers. Paseo's current application relay is not automatically a TURN relay.
|
|
219
|
+
[WebRTC TURN guidance](https://webrtc.org/getting-started/turn-server).
|
|
220
|
+
|
|
221
|
+
Start with browser-to-browser peers so no Node media engine is needed merely
|
|
222
|
+
for forwarding an existing browser track. Keep native phone JPEG fallback as
|
|
223
|
+
previously agreed. Test the current capture quality ceiling: RTP cannot recover
|
|
224
|
+
colour detail already lost before transmission.
|
|
225
|
+
|
|
226
|
+
### B. Binary WebSocket or SDK stream with WebCodecs
|
|
227
|
+
|
|
228
|
+
Selkies shows this is a viable primary design, not inherently a lesser fallback.
|
|
229
|
+
Its current [design](https://github.com/selkies-project/selkies/blob/5927ca77b1fc31d43ebca096b2aa59e35bbb128f/docs/design.md)
|
|
230
|
+
uses WebSockets by default and WebRTC optionally. Its [web client](https://docs.selkies.io/latest/components/web-client)
|
|
231
|
+
separates streaming/input and uses worker components. We should compare a binary
|
|
232
|
+
stream and bounded decode pipeline with native WebRTC under identical load.
|
|
233
|
+
|
|
234
|
+
Our current media is base64 in JSON RPC replies. Base64 represents three bytes
|
|
235
|
+
as four characters before JSON/envelope overhead. Removing that conversion and
|
|
236
|
+
request/response scheduling is a concrete optimization candidate; how much it
|
|
237
|
+
helps requires profiling. A binary stream needs Paseo authentication and remote
|
|
238
|
+
relay support, not an anonymously exposed localhost socket.
|
|
239
|
+
|
|
240
|
+
### C. Existing whole remote-desktop stack
|
|
241
|
+
|
|
242
|
+
Adopting noVNC, Guacamole, Selkies or Neko can replace substantial custom code.
|
|
243
|
+
They also replace substantial backend behavior: OS-level desktop capture/input,
|
|
244
|
+
new server dependencies, authentication wiring and browser lifecycle ownership.
|
|
245
|
+
They do not transparently preserve our exact-tab CDP operations, independent
|
|
246
|
+
native emulation, or agent screenshot contracts.
|
|
247
|
+
|
|
248
|
+
Benchmark Selkies and Neko in disposable containers if considering this route.
|
|
249
|
+
Do not migrate the user's profile into a new backend as a research shortcut.
|
|
250
|
+
An isolated integration would need to show it can retain workspace ownership,
|
|
251
|
+
agent tools and mobile fallback before choosing the whole-stack option.
|
|
252
|
+
|
|
253
|
+
Selkies' [licensing inventory](https://github.com/selkies-project/selkies/blob/5927ca77b1fc31d43ebca096b2aa59e35bbb128f/docs/licensing.md)
|
|
254
|
+
identifies MPL-2.0 project code and encoder-dependent components. Neko's
|
|
255
|
+
[license](https://github.com/m1k1o/neko/blob/bdd0428a607ccfd22eebabc74b85ae17a4cb42d5/LICENSE)
|
|
256
|
+
is Apache-2.0. Dependency and distribution obligations need separate review.
|
|
257
|
+
|
|
258
|
+
### Library shortlist
|
|
259
|
+
|
|
260
|
+
Current stable versions were read from the npm registry on the research date;
|
|
261
|
+
no dependency was installed. Recheck metadata before implementation.
|
|
262
|
+
|
|
263
|
+
| Candidate | Registry stable / license | Use and limitation |
|
|
264
|
+
| --- | --- | --- |
|
|
265
|
+
| `@novnc/novnc` | 1.7.0 / MPL-2.0 | Actual reusable RFB client; requires an RFB backend, not a CDP or WebRTC adapter. |
|
|
266
|
+
| Guacamole JavaScript client | Source module / Apache-2.0 | Reusable input/display/tunnel modules; full remote protocol needs a guacd/backend integration. |
|
|
267
|
+
| `simple-peer` | 9.11.1 / MIT | Browser peer/signaling convenience; does not implement remote input, capture, TURN deployment or our authorization. Its Node-style dependencies need SDK/bundling review. |
|
|
268
|
+
| `werift` | 0.24.4 / MIT | TypeScript Node WebRTC if a server terminates media; unnecessary for initial browser-to-browser capture. Published metadata says Node >=16; development README says >=22, so do not conflate them. |
|
|
269
|
+
| `node-datachannel` | 0.33.4 / MPL 2.0 | Native Node/libdatachannel media/data transport; adds native binary packaging and platform support work. |
|
|
270
|
+
| `@epicgames-ps/lib-pixelstreamingfrontend-ue5.8` | 0.1.2 / MIT | A real streaming frontend library, but its Unreal-specific message/signaling model makes it a reference rather than a natural whole import. |
|
|
271
|
+
| Pixelflux | Source/native component | Reusable Linux capture/encoder pipeline behind Selkies; Rust/Python and display capture differ from our current tab track. Encoder license depends on the build. |
|
|
272
|
+
|
|
273
|
+
Primary library sources:
|
|
274
|
+
[noVNC](https://github.com/novnc/noVNC),
|
|
275
|
+
[Guacamole client](https://github.com/apache/guacamole-client),
|
|
276
|
+
[simple-peer](https://github.com/feross/simple-peer),
|
|
277
|
+
[werift](https://github.com/shinyoshiaki/werift-webrtc),
|
|
278
|
+
[node-datachannel](https://github.com/murat-dogan/node-datachannel),
|
|
279
|
+
[Pixel Streaming](https://github.com/EpicGames/PixelStreamingInfrastructure),
|
|
280
|
+
[Pixelflux](https://github.com/selkies-project/pixelflux).
|
|
281
|
+
|
|
282
|
+
For Pixelflux, the default x264-enabled build carries GPL components; an
|
|
283
|
+
OpenH264 build can avoid that encoder dependency. Check the entire distribution
|
|
284
|
+
and license notices before redistribution, not just a top-level library name.
|
|
285
|
+
|
|
286
|
+
## Gotchas the references make concrete
|
|
287
|
+
|
|
288
|
+
- **Ordered input is separate from video.** Use reliable ordering for
|
|
289
|
+
presses/releases and committed text. An unordered move channel would need
|
|
290
|
+
sequence-aware reconciliation before any transition; do not introduce one
|
|
291
|
+
merely because WebRTC permits it.
|
|
292
|
+
- **WebRTC still buffers.** `bufferedAmount`, channel size limits, ICE failure,
|
|
293
|
+
playout/jitter and codec support remain real. Chromium's
|
|
294
|
+
[data-stream adapter](https://chromium.googlesource.com/chromium/src/+/main/remoting/protocol/webrtc_data_stream_adapter.cc)
|
|
295
|
+
uses its own send queue to avoid overrunning lower-layer buffering.
|
|
296
|
+
- **Resize mapping can race.** Use current CSS geometry, not JPEG pixel size or
|
|
297
|
+
device pixel density. Reject old geometry on replacement, retain admitted
|
|
298
|
+
drag coordinates through release, and qualify the first new mapping.
|
|
299
|
+
- **Stateful input must be cleaned up.** The pinned Pixel Streaming hovering
|
|
300
|
+
controller retains window-level move/up handlers during drags, and documents
|
|
301
|
+
a double-click release fix. These are evidence against assuming a library
|
|
302
|
+
automatically makes all drag/press edge cases disappear.
|
|
303
|
+
- **Touch libraries differ.** noVNC's
|
|
304
|
+
[pinch path](https://github.com/novnc/noVNC/blob/63107bd06d9e1f6136ff21aeda8cd62cbf0d433e/core/rfb.js#L1421-L1441)
|
|
305
|
+
emulates Ctrl/wheel, rather than genuine CDP multi-contact touch. Guacamole
|
|
306
|
+
1.6.0 has a [Touch module](https://github.com/apache/guacamole-client/blob/0537c89cd783681b986ff8f8c4e0b97ec6873371/guacamole-common-js/src/main/webapp/modules/Touch.js#L98-L185),
|
|
307
|
+
but backend support and explicit cancellation still need checks. Reuse must
|
|
308
|
+
preserve our required physical touch behavior, not substitute mouse emulation.
|
|
309
|
+
- **Phone text is not desktop keycodes.** Pixel Streaming's
|
|
310
|
+
[release notes](https://github.com/EpicGames/PixelStreamingInfrastructure/releases)
|
|
311
|
+
describe separate text-input/IME support. Preserve our agreed basic live text
|
|
312
|
+
relay and commit complex IME with Done; do not replace it with keycode-only
|
|
313
|
+
forwarding.
|
|
314
|
+
- **Native Paseo is a distinct client.** The current
|
|
315
|
+
[plugin reference](https://github.com/getpaseo/paseo/blob/main/public-docs/plugins/reference.md)
|
|
316
|
+
documents React Native primitives and host-provided imports, not a portable
|
|
317
|
+
native WebRTC/video surface. A new native media API would require host work;
|
|
318
|
+
web-only libraries must remain isolated from phone bundle evaluation.
|
|
319
|
+
- **A protocol is not an authorization policy.** Continuous human input accepts
|
|
320
|
+
visual latency. Frame-targeted automation has a different targeting contract.
|
|
321
|
+
Keep both explicit and test that neither can acquire the other's privileges.
|
|
322
|
+
|
|
323
|
+
## Implementation and follow-up plan
|
|
324
|
+
|
|
325
|
+
1. **Define input contracts.** Separate human controller-owned channels from
|
|
326
|
+
frame-targeted agent commands. Specify document/geometry changes, suspension,
|
|
327
|
+
queued transitions and cleanup in one shared policy, covering JPEG and video.
|
|
328
|
+
2. **Fix the demonstrated mismatch.** Frame waiters watch actionable admission,
|
|
329
|
+
not merely newly painted pixels. Pin the production-panel reproduction even
|
|
330
|
+
if human mode no longer normally needs those waits.
|
|
331
|
+
3. **Remove ordinary human press/frame coupling.** Establish geometry readiness
|
|
332
|
+
on admission; retain native exact-target and controller checks. Normal
|
|
333
|
+
same-document dialog/list changes and scrolling do not revoke the channel.
|
|
334
|
+
Real document/viewport replacement cancels unsent old-context actions rather
|
|
335
|
+
than deferring them onto new content.
|
|
336
|
+
4. **Measure current transport.** Record event-to-native acknowledgement and
|
|
337
|
+
event-to-observed-pixel latency, queue depth, source/encode/read/decode/paint
|
|
338
|
+
timings, resets, dropped frames and bytes. Keep clocks labelled by domain;
|
|
339
|
+
one-way delay cannot be computed from unsynchronized host/client clocks.
|
|
340
|
+
5. **Prototype WebRTC and binary streaming in isolation.** Same tab, sizes,
|
|
341
|
+
density, bitrate and interaction workload; include real large-view changes,
|
|
342
|
+
cameras, loss, jitter, multiple viewers, LAN and actual Paseo relay paths.
|
|
343
|
+
6. **Choose reuse based on results.** Prefer native browser media APIs for the
|
|
344
|
+
smallest initial WebRTC prototype; use libraries where they remove a real
|
|
345
|
+
responsibility. Choose a whole remote-desktop stack only if the additional
|
|
346
|
+
backend/packaging work beats retaining exact-tab capture and CDP control.
|
|
347
|
+
7. **Review and activate exact source.** Independent policy/ordering review,
|
|
348
|
+
mounted DOM plus real browser tests, actual SDK/Hermes compile, and human
|
|
349
|
+
desktop/phone acceptance. Leave changes uncommitted until requested.
|
|
350
|
+
|
|
351
|
+
### Required acceptance evidence
|
|
352
|
+
|
|
353
|
+
- Rapid clicks during a large same-document refresh publish each admitted
|
|
354
|
+
down/up once, in order, without a decoded-frame error or duplicates.
|
|
355
|
+
- Typing, modifier combinations, double/right click, wheel, scrollbar drags and
|
|
356
|
+
touch continue while video catches up.
|
|
357
|
+
- Controller loss, new target, actual document/viewport replacement, hidden
|
|
358
|
+
client and disconnect release held state and fence old queued events.
|
|
359
|
+
- A denied/uncertain native press is never automatically retried on a new page.
|
|
360
|
+
- Agent stale-frame rejection still holds; agent requests cannot mint human
|
|
361
|
+
admission authority.
|
|
362
|
+
- Video and native JPEG fallback use the same human control semantics.
|
|
363
|
+
- Actual pane measurements include latency percentiles and byte/CPU load;
|
|
364
|
+
server captures alone are not evidence of host rendering or relay performance.
|
|
365
|
+
- Existing sign-in/workspace/profile ownership and current UI conventions remain
|
|
366
|
+
unchanged by a transport selection.
|
|
367
|
+
|
|
368
|
+
## Research limits and handoff
|
|
369
|
+
|
|
370
|
+
Source review and a local production-panel reproduction were completed.
|
|
371
|
+
No candidate replacement system was installed, no live browser inputs were
|
|
372
|
+
sent, and no current streaming runtime was changed by this investigation.
|
|
373
|
+
The transport performance comparison remains work to perform, not a benchmark
|
|
374
|
+
result inferred from project descriptions. The current live improvement has
|
|
375
|
+
human confirmation, but the reported click rejection remains unresolved until
|
|
376
|
+
the approved input contract is implemented and verified.
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
## Implemented follow-up
|
|
380
|
+
|
|
381
|
+
The approved human-input change admits decoded geometry once and continues ordered presses,
|
|
382
|
+
keyboard, touch and motion independently of replacement video frames. Normal
|
|
383
|
+
idle closure can reopen the exact acknowledged geometry grant. Native admission
|
|
384
|
+
compares expected attachment/document generation after asynchronous cleanup
|
|
385
|
+
and page attachment, before binding, so an intervening same-URL reload refuses
|
|
386
|
+
input. Controller, viewer, page, viewport and uncertain-publication changes still
|
|
387
|
+
cancel the channel. Agent input retains fresh frame receipt checks.
|
|
388
|
+
|
|
389
|
+
The mounted production panel verifies five clicks with video decoding blocked,
|
|
390
|
+
then another after idle closure using the same acknowledged geometry. A separate
|
|
391
|
+
fixture proves that visual-only delayed frames cannot wake initial admission,
|
|
392
|
+
while actually actionable decoded publication can. Native tests cover page and
|
|
393
|
+
attachment replacement during admission awaits. These checks do not measure
|
|
394
|
+
human-pane relay latency. No WebRTC, RFB, guacd or new dependency was added in
|
|
395
|
+
this policy change; transport batching and media replacement remain follow-ups.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Shared Browser streaming design
|
|
2
|
+
|
|
3
|
+
This document describes the media and input contracts maintained by the plugin.
|
|
4
|
+
User-facing controls and installation are documented in [README](README.md).
|
|
5
|
+
[Remote-control research](REMOTE_BROWSER_RESEARCH.md) records the primary-source
|
|
6
|
+
comparisons behind the input design.
|
|
7
|
+
|
|
8
|
+
## Transport and ownership
|
|
9
|
+
|
|
10
|
+
Encoded video is opt-in: the daemon must set `PASEO_SHARED_BROWSER_VIDEO=1`. Without it
|
|
11
|
+
the capture helper is never loaded and `video.read` returns a typed `video-disabled` reply,
|
|
12
|
+
after which the client stops reading and uses JPEG. When enabled, desktop and web clients
|
|
13
|
+
use Chromium tab capture, a realtime WebCodecs encoder, and bounded encoded-packet reads
|
|
14
|
+
over authenticated Paseo RPC. The receiver decodes into a retained canvas. Native clients
|
|
15
|
+
and clients without a usable decoder use JPEG; a native surface needs a host SDK
|
|
16
|
+
export that the published plugin SDK does not yet provide. This is encoded video, not WebRTC or a push subscription; it
|
|
17
|
+
shares the reliable connection and relay used by Paseo.
|
|
18
|
+
|
|
19
|
+
`server/native-video-extension.ts` materializes the immutable bundled capture
|
|
20
|
+
helper in the runtime's private IPC directory. The helper has tabCapture and
|
|
21
|
+
debugger permissions, but no content scripts, web-accessible resources, site
|
|
22
|
+
message handlers, or network host permissions. It captures the exact retained
|
|
23
|
+
workspace tab, never a title-selected window or arbitrary desktop.
|
|
24
|
+
|
|
25
|
+
`server/native-video-capture.ts` owns the helper target, track, encoder cohorts
|
|
26
|
+
and encoded buffers. `server/agent-browser-runtime.ts` binds that ownership to
|
|
27
|
+
the exact native page attachment and emulation. `server/browser-policy.ts`
|
|
28
|
+
checks viewer lifetime and the browser generations before admitting packets.
|
|
29
|
+
`client/use-browser-video.ts` owns viewing retries and presentation authority;
|
|
30
|
+
`client/browser-video-decoder.ts` owns codec queues and frame disposal.
|
|
31
|
+
|
|
32
|
+
Every acquisition waits for the complete target or viewport transition and any
|
|
33
|
+
pending native cleanup. Expired, detached or hidden viewers cannot revive media
|
|
34
|
+
ownership through a late reply. Encoder cohorts retire after two seconds without
|
|
35
|
+
active reads. JPEG capture follows actual image demand and retires after three
|
|
36
|
+
seconds without demand; attaching a viewer or reading video does not renew it.
|
|
37
|
+
|
|
38
|
+
## Resource limits and backpressure
|
|
39
|
+
|
|
40
|
+
Encoded packets are limited to 2 MiB. A reply contains at most 32 packets and
|
|
41
|
+
4 MiB of encoded payload. Source and decoder queues are independently bounded.
|
|
42
|
+
Video waits are at most 500 milliseconds and run outside ordered input and
|
|
43
|
+
workspace mutation lanes. Socket media admission leaves capacity for controls;
|
|
44
|
+
heartbeat uses a separately bounded lane so a slow page command cannot expire
|
|
45
|
+
the bridge. Buffered socket lines resume draining after admitted work completes.
|
|
46
|
+
|
|
47
|
+
At most three bitrate/frame-rate cohorts share a track. A new profile cannot
|
|
48
|
+
evict a healthy peer encoder. Capacity exhaustion returns an explicit reason
|
|
49
|
+
and uses JPEG while a bounded viewing retry waits for capacity. Source-image
|
|
50
|
+
skips do not break already encoded delta dependencies. Actual encoded-output
|
|
51
|
+
loss suppresses dependent deltas until a keyframe restores the chain.
|
|
52
|
+
|
|
53
|
+
The receiver feeds a bounded FIFO as the native decoder makes progress, rather
|
|
54
|
+
than resetting it because a normal reply contains more than four pictures. A
|
|
55
|
+
new retained keyframe allows a delayed viewer to skip obsolete encoded backlog.
|
|
56
|
+
Old decoder callbacks are generation-fenced and their frames closed. The last
|
|
57
|
+
paint remains visible during catch-up. Feature-detected native byte/base64
|
|
58
|
+
conversion avoids intermediate binary strings; exact fallback conversion is
|
|
59
|
+
retained for older clients.
|
|
60
|
+
|
|
61
|
+
## Presentation and input are separate contracts
|
|
62
|
+
|
|
63
|
+
Human input requires decoded and painted geometry. A channel binds the controller,
|
|
64
|
+
viewer, workspace, runtime, bridge, native attachment, document and viewport. Every
|
|
65
|
+
independent press (mouse down, first touch contact) carries its own decoded frame
|
|
66
|
+
receipt, valid for five seconds and spent by acknowledged input; the next press waits
|
|
67
|
+
a bounded time for a newer decoded frame, and a stalled presentation therefore cannot
|
|
68
|
+
admit clicks indefinitely. Motion, releases and keys of a held gesture use the ordered
|
|
69
|
+
channel without waiting for new pixels. Agent commands retain strict fresh-frame
|
|
70
|
+
targeting.
|
|
71
|
+
|
|
72
|
+
A visible frame is not automatically an actionable receipt. The host flags each
|
|
73
|
+
video packet `actionable` or not (a capture older than the last acknowledged input
|
|
74
|
+
is painted but never a receipt), and the viewer starts a new epoch when the host
|
|
75
|
+
acknowledges a receipt-revoking input, so reads answered before that revocation
|
|
76
|
+
cannot authorize the next press. After an acknowledged press the viewer has no
|
|
77
|
+
actionable video receipt until a newer packet paints; a following press waits for it. Delayed reads may
|
|
78
|
+
continue painting within the same source and geometry while retaining their
|
|
79
|
+
original input revision. Initial-admission waiters wake only for actionable
|
|
80
|
+
pixels. A document or viewport transition, controller loss, cancellation,
|
|
81
|
+
disconnect, hidden panel or uncertain mutation invalidates admission. Native
|
|
82
|
+
binding checks its expected generation after attachment and cleanup awaits.
|
|
83
|
+
|
|
84
|
+
Press/release, key and text transitions remain ordered; only compatible motion
|
|
85
|
+
can coalesce without crossing a transition. Pointer capture and teardown release
|
|
86
|
+
held state. The four-second idle channel closure releases native keys/buttons; a reopen
|
|
87
|
+
needs a fresh decoded receipt. No published or uncertain input is replayed after
|
|
88
|
+
a timeout.
|
|
89
|
+
|
|
90
|
+
Video can retain its last painted canvas across an ordinary input or document
|
|
91
|
+
transition without retaining input authority. Viewer, runtime, bridge and
|
|
92
|
+
viewport replacement clear that source. The 2.5-second stalled-paint watchdog
|
|
93
|
+
requests image fallback but holds the canvas until a current JPEG actually
|
|
94
|
+
decodes. A revision guards that handoff against late images and React commits.
|
|
95
|
+
|
|
96
|
+
Source timestamps remain Chromium capture timestamps. Arrival time never makes
|
|
97
|
+
queued, duplicate, backwards or missing source timestamps fresh. JPEG admission
|
|
98
|
+
uses a one-second source age and bounded native clock tolerance. Navigation
|
|
99
|
+
clears cached JPEGs and queued video before admitting replacement pixels.
|
|
100
|
+
|
|
101
|
+
## Quality and geometry
|
|
102
|
+
|
|
103
|
+
The menu separates JPEG quality (40, 65, 85 or 100), video bitrate (2, 5, 12 or
|
|
104
|
+
24 Mbps), frame rate (15, 30 or 60 FPS), source density and local Fit/Actual size.
|
|
105
|
+
These are encoder targets, not measured bandwidth or guaranteed frame rates.
|
|
106
|
+
JPEG 100 remains lossy and its byte budget can lower actual quality. Video
|
|
107
|
+
keyframes repair decoder dependencies; they do not restore RGB detail reduced
|
|
108
|
+
by the native capture's colour sampling. No lossless idle overlay or static-page
|
|
109
|
+
comparison loop is used.
|
|
110
|
+
|
|
111
|
+
Capture density is independently controller-authorized at 1x or 2x. It preserves
|
|
112
|
+
CSS dimensions, input mode, user agent and preset identity. Physical dimensions
|
|
113
|
+
must each remain at most 3840, so 2x is unavailable above 1920 on either CSS axis.
|
|
114
|
+
Each viewer panel defaults to Auto, choosing the smallest supported density for
|
|
115
|
+
its displayed frame size and screen pixel ratio. Fit uses the contained frame;
|
|
116
|
+
Actual uses CSS dimensions. Manual selections disable automatic adjustment for
|
|
117
|
+
the panel. Adjustments are debounced, require visible/idle controller authority,
|
|
118
|
+
and pause on failure. Fill viewport carries the chosen density in its resize RPC
|
|
119
|
+
so growing beyond the 2x limit can reduce density and resize in one transition.
|
|
120
|
+
Rendering DPR is raised to at least the selected density while a genuine phone
|
|
121
|
+
DPR is retained. Explicit preset changes retain their established density reset
|
|
122
|
+
behavior; mode-only changes retain it. Returned state reports actual density.
|
|
123
|
+
|
|
124
|
+
Encoded and decoded dimensions match the requested viewport and density. Chromium
|
|
125
|
+
YUV capture rounds odd physical dimensions down to even sizes. Only that exact
|
|
126
|
+
one-pixel alignment is resampled by the encoder to the requested size, preserving
|
|
127
|
+
page layout, capture density and source timing. Other dimension mismatches remain
|
|
128
|
+
terminal. Odd-sized 1x captures can use VP8 when H.264 rejects those dimensions;
|
|
129
|
+
2x Pixel 7 capture remains 824 by 1678.
|
|
130
|
+
|
|
131
|
+
## Failure and navigation recovery
|
|
132
|
+
|
|
133
|
+
Source-wide startup, stopped-track and reset failures retire through the native
|
|
134
|
+
stop barrier even while reads continue. Acquisition has a three-second monotonic
|
|
135
|
+
cooldown starting when failure is admitted, and cleanup independently blocks
|
|
136
|
+
replacement. An encoder-cohort failure does not retire healthy peers. Dimension
|
|
137
|
+
failure is permanent for the current attachment and viewport configuration;
|
|
138
|
+
polling or navigation cannot repeatedly restart it. A configuration change can
|
|
139
|
+
recover without weakening exact geometry.
|
|
140
|
+
|
|
141
|
+
Navigation observes its matching main-frame commit before dispatch, then waits
|
|
142
|
+
for the command acknowledgement and document commit, not DOMContentLoaded. Full
|
|
143
|
+
navigation matches its loader; same-document navigation matches root frame and
|
|
144
|
+
URL. A download acknowledgement retains the document. One bounded metadata read
|
|
145
|
+
follows commit. Attachment recovery and held-state cleanup have separate budgets.
|
|
146
|
+
An uncertain navigation is not replayed.
|
|
147
|
+
|
|
148
|
+
Transient video read failures revoke input authority and use bounded viewing
|
|
149
|
+
backoff. Exhaustion stops reads. Ordinary pointer/image refresh cannot rearm that
|
|
150
|
+
retry; successful navigation may queue one recovery after cooldown. Decoder
|
|
151
|
+
failures are terminal for that playback attempt. Expired viewers reattach through
|
|
152
|
+
the existing viewing recovery, never by replaying a mutation or taking control.
|
|
153
|
+
|
|
154
|
+
Diagnostics record redacted operation categories, elapsed duration and safe CDP
|
|
155
|
+
method names, with rate limits. They must not record URLs, text, credentials,
|
|
156
|
+
viewer/control tokens, media payloads, SDP or per-frame successes. A screenshot
|
|
157
|
+
failure together with navigation-history/layout timeouts indicates a broader
|
|
158
|
+
runtime failure; successful cached metadata is not proof of page preservation.
|
|
159
|
+
|
|
160
|
+
## Verification
|
|
161
|
+
|
|
162
|
+
Follow the isolated workflow in [README](README.md). Unit regressions cover
|
|
163
|
+
packet bounds, frame authority, viewer expiry, transition races, socket admission,
|
|
164
|
+
source cleanup, codec queues and geometry. The opt-in native video gate captures
|
|
165
|
+
an actual tab and decodes through the production receiver at desktop, sharp phone
|
|
166
|
+
and large square sizes, checking all four corners and batched final-frame paint.
|
|
167
|
+
It also distinguishes real 2x source rendering from rescaling a 1x image.
|
|
168
|
+
|
|
169
|
+
Mounted panel checks require the explicitly supplied tooling workspace described
|
|
170
|
+
in README. Handoff, rapid-input, continuous-input and admission fixtures exercise
|
|
171
|
+
React/React Query settlement and DOM events. They do not prove phone IME behavior,
|
|
172
|
+
physical-input latency, human-pane rendering or performance through a live relay.
|
|
173
|
+
A successful MCP image capture establishes server image delivery only.
|
|
174
|
+
|
|
175
|
+
## WebRTC follow-up
|
|
176
|
+
|
|
177
|
+
WebRTC could move media off the ordered RPC connection and avoid base64 expansion.
|
|
178
|
+
Authenticated RPC can carry bounded offer/answer/ICE signaling. Native-phone image
|
|
179
|
+
fallback would remain. Local proof alone cannot qualify remote networking: Paseo's
|
|
180
|
+
relay is not a TURN service, and WSL/NAT clients may require configured TURN,
|
|
181
|
+
short-lived credentials, traffic limits and operational ownership.
|
|
182
|
+
|
|
183
|
+
An owned native-track proof produced correct full-frame geometry and local ICE
|
|
184
|
+
playback. Exact correlation between a presented RTP frame and its authenticated
|
|
185
|
+
source timestamp remains unqualified. Arrival time must not stand in for capture
|
|
186
|
+
time. An alternative data channel can retain existing source-stamped packets but
|
|
187
|
+
needs bounded fragmentation, incomplete-packet expiry, backpressure and keyframe
|
|
188
|
+
recovery. Neither alternative is shipped.
|
|
189
|
+
|
|
190
|
+
Measure RPC and candidate WebRTC paths over the actual LAN and relay topology:
|
|
191
|
+
time to first frame, source-to-presentation latency, text readability, scrolling,
|
|
192
|
+
CPU, bandwidth, recovery and stalled-network behavior. See the
|
|
193
|
+
[WebRTC specification](https://www.w3.org/TR/webrtc/),
|
|
194
|
+
[TURN guidance](https://webrtc.org/getting-started/turn-server),
|
|
195
|
+
[video presentation callbacks](https://wicg.github.io/video-rvfc/) and
|
|
196
|
+
[encoded transforms](https://www.w3.org/TR/webrtc-encoded-transform/).
|