@memberjunction/ng-conversations 5.41.0 → 5.43.0
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/dist/__tests__/remote-browser-channel.test.js +163 -1
- package/dist/__tests__/remote-browser-channel.test.js.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.d.ts +105 -2
- package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.js +1378 -489
- package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +4 -0
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +13 -8
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.d.ts +98 -5
- package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.js +1102 -401
- package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
- package/dist/lib/components/mention/mention-dropdown.component.js +37 -19
- package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.js +10 -3
- package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.js +1 -1
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +5 -0
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +26 -3
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/project/project-form-modal.component.d.ts +5 -1
- package/dist/lib/components/project/project-form-modal.component.d.ts.map +1 -1
- package/dist/lib/components/project/project-form-modal.component.js +104 -85
- package/dist/lib/components/project/project-form-modal.component.js.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +22 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +40 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +12 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.js +97 -19
- package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +33 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +164 -7
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts +35 -2
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js +67 -4
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js +13 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +4 -0
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +13 -8
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/models/conversation-state.model.d.ts +1 -1
- package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
- package/dist/lib/models/conversation-state.model.js.map +1 -1
- package/dist/lib/services/artifact-state.service.d.ts +1 -1
- package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-state.service.js +7 -2
- package/dist/lib/services/artifact-state.service.js.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.d.ts +25 -5
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +86 -18
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/realtime-pairing.d.ts +8 -7
- package/dist/lib/services/realtime-pairing.d.ts.map +1 -1
- package/dist/lib/services/realtime-pairing.js +19 -9
- package/dist/lib/services/realtime-pairing.js.map +1 -1
- package/package.json +26 -26
package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-browser-surface.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-surface.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAC8D,YAAY,EAAiB,SAAS,EACzG,MAAM,EACP,MAAM,eAAe,CAAC;;AAgBvB,0GAA0G;AAC1G,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,4BAA4B;IAC3C,4BAA4B;IAC5B,IAAI,EAAE,cAAc,GAAG,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,KAAK,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"remote-browser-surface.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-surface.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -131,6 +131,13 @@ export function MapToViewportCoords(clientX, clientY, rect, canvasWidth, canvasH
|
|
|
131
131
|
export class RemoteBrowserSurfaceComponent {
|
|
132
132
|
/** Snapshot fetcher supplied by the channel plugin (closes over the session id + provider). */
|
|
133
133
|
Fetch = null;
|
|
134
|
+
/**
|
|
135
|
+
* Selection fetcher for copy-out, supplied by the channel plugin (closes over the session id + provider).
|
|
136
|
+
* On a local `copy` / Cmd+C over the live canvas the surface calls this to read the remote page's
|
|
137
|
+
* selection, then writes it to the local clipboard. Best-effort: when unset or it resolves to `''`, the
|
|
138
|
+
* copy is a no-op (the local clipboard is left untouched).
|
|
139
|
+
*/
|
|
140
|
+
FetchSelection = null;
|
|
134
141
|
/**
|
|
135
142
|
* Whether the server is PUSHING live screencast frames for this session (the backend advertised the
|
|
136
143
|
* `ScreenStreaming` capability and the start succeeded). When `true` the surface paints pushed frames
|
|
@@ -275,6 +282,8 @@ export class RemoteBrowserSurfaceComponent {
|
|
|
275
282
|
onCanvasMouseUp = (e) => this.handlePointerUp(e);
|
|
276
283
|
onCanvasClick = (e) => this.handlePointerClick(e);
|
|
277
284
|
onCanvasKeyDown = (e) => this.handleKeyDown(e);
|
|
285
|
+
onCanvasPaste = (e) => this.handlePaste(e);
|
|
286
|
+
onCanvasCopy = (e) => this.handleCopy(e);
|
|
278
287
|
onCanvasMouseLeave = () => this.handleMouseLeave();
|
|
279
288
|
onCanvasWheel = (e) => this.handleWheel(e);
|
|
280
289
|
onCanvasFocus = () => { this.surfaceFocused = true; };
|
|
@@ -445,6 +454,10 @@ export class RemoteBrowserSurfaceComponent {
|
|
|
445
454
|
canvas.addEventListener('mouseup', this.onCanvasMouseUp);
|
|
446
455
|
canvas.addEventListener('click', this.onCanvasClick);
|
|
447
456
|
canvas.addEventListener('keydown', this.onCanvasKeyDown);
|
|
457
|
+
// Paste-in / copy-out: the focused canvas receives the local clipboard `paste` / `copy` events; we
|
|
458
|
+
// relay paste text into the remote page and write the remote selection to the local clipboard.
|
|
459
|
+
canvas.addEventListener('paste', this.onCanvasPaste);
|
|
460
|
+
canvas.addEventListener('copy', this.onCanvasCopy);
|
|
448
461
|
canvas.addEventListener('mouseleave', this.onCanvasMouseLeave);
|
|
449
462
|
canvas.addEventListener('focus', this.onCanvasFocus);
|
|
450
463
|
canvas.addEventListener('blur', this.onCanvasBlur);
|
|
@@ -464,6 +477,8 @@ export class RemoteBrowserSurfaceComponent {
|
|
|
464
477
|
canvas.removeEventListener('mouseup', this.onCanvasMouseUp);
|
|
465
478
|
canvas.removeEventListener('click', this.onCanvasClick);
|
|
466
479
|
canvas.removeEventListener('keydown', this.onCanvasKeyDown);
|
|
480
|
+
canvas.removeEventListener('paste', this.onCanvasPaste);
|
|
481
|
+
canvas.removeEventListener('copy', this.onCanvasCopy);
|
|
467
482
|
canvas.removeEventListener('mouseleave', this.onCanvasMouseLeave);
|
|
468
483
|
canvas.removeEventListener('focus', this.onCanvasFocus);
|
|
469
484
|
canvas.removeEventListener('blur', this.onCanvasBlur);
|
|
@@ -598,9 +613,13 @@ export class RemoteBrowserSurfaceComponent {
|
|
|
598
613
|
}
|
|
599
614
|
const isPrintable = key.length === 1;
|
|
600
615
|
const hasComboModifier = event.ctrlKey || event.metaKey || event.altKey;
|
|
601
|
-
|
|
616
|
+
// Cmd/Ctrl+C and Cmd/Ctrl+V are handled by the dedicated `copy` / `paste` listeners (which read/write the
|
|
617
|
+
// LOCAL clipboard) — don't ALSO relay them as a keypress, or paste would double-fire and copy would send a
|
|
618
|
+
// useless Ctrl+C to the remote page. The browser fires the matching clipboard event for these chords.
|
|
619
|
+
const isClipboardChord = (event.ctrlKey || event.metaKey) && (key === 'c' || key === 'v' || key === 'C' || key === 'V');
|
|
620
|
+
const isForwardable = !isClipboardChord && (isPrintable || FORWARDED_CONTROL_KEYS.has(key) || hasComboModifier);
|
|
602
621
|
if (!isForwardable) {
|
|
603
|
-
return; // leave non-forwarded keys to the host app
|
|
622
|
+
return; // leave non-forwarded keys to the host app (and clipboard chords to copy/paste handlers)
|
|
604
623
|
}
|
|
605
624
|
event.preventDefault();
|
|
606
625
|
// Stop the keystroke from reaching document-level handlers (e.g. the overlay's T-to-type) so the user's
|
|
@@ -608,6 +627,48 @@ export class RemoteBrowserSurfaceComponent {
|
|
|
608
627
|
event.stopPropagation();
|
|
609
628
|
this.emitInput({ kind: 'key', key, modifiers: this.collectModifiers(event) });
|
|
610
629
|
}
|
|
630
|
+
/**
|
|
631
|
+
* Paste-in: a local `paste` (Cmd/Ctrl+V or context-menu paste) over the canvas → reads the LOCAL clipboard
|
|
632
|
+
* text and relays it as a `'text'` human input, which the server inserts into the remote page's focused
|
|
633
|
+
* element (the VNC/remote-desktop paste model — no remote-clipboard sync). `preventDefault` keeps the host
|
|
634
|
+
* app from also acting on the paste; empty/non-text clipboards are a no-op.
|
|
635
|
+
*/
|
|
636
|
+
handlePaste(event) {
|
|
637
|
+
event.preventDefault();
|
|
638
|
+
event.stopPropagation();
|
|
639
|
+
const text = event.clipboardData?.getData('text') ?? '';
|
|
640
|
+
if (text.length === 0) {
|
|
641
|
+
return; // nothing (or no text) on the clipboard — paste nothing
|
|
642
|
+
}
|
|
643
|
+
this.emitInput({ kind: 'text', text });
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Copy-out: a local `copy` (Cmd/Ctrl+C or context-menu copy) over the canvas → reads the REMOTE page's
|
|
647
|
+
* current selection via {@link FetchSelection} and writes it to the LOCAL clipboard (the mirror of paste-in).
|
|
648
|
+
* `preventDefault` stops the host app from copying its own empty selection. Best-effort throughout: when the
|
|
649
|
+
* fetcher is unwired, resolves to `''`, or `navigator.clipboard` is unavailable / denies permission, the copy
|
|
650
|
+
* is silently a no-op — it never throws and never breaks the live view.
|
|
651
|
+
*/
|
|
652
|
+
handleCopy(event) {
|
|
653
|
+
event.preventDefault();
|
|
654
|
+
event.stopPropagation();
|
|
655
|
+
const fetchSelection = this.FetchSelection;
|
|
656
|
+
if (!fetchSelection) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
void (async () => {
|
|
660
|
+
try {
|
|
661
|
+
const text = await fetchSelection();
|
|
662
|
+
if (text.length > 0 && navigator.clipboard?.writeText) {
|
|
663
|
+
await navigator.clipboard.writeText(text);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
catch {
|
|
667
|
+
// Best-effort: a denied clipboard permission or a failed selection read leaves the local clipboard
|
|
668
|
+
// untouched rather than surfacing an error during takeover.
|
|
669
|
+
}
|
|
670
|
+
})();
|
|
671
|
+
}
|
|
611
672
|
/**
|
|
612
673
|
* Collects the modifier keys currently held during a DOM mouse/keyboard event into the relayed
|
|
613
674
|
* {@link RemoteBrowserModifier} list (empty when none are held).
|
|
@@ -741,7 +802,7 @@ export class RemoteBrowserSurfaceComponent {
|
|
|
741
802
|
let _t;
|
|
742
803
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.frameCanvas = _t.first);
|
|
743
804
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.cursorCanvas = _t.first);
|
|
744
|
-
} }, inputs: { Fetch: "Fetch", Streaming: "Streaming", Interactive: "Interactive", AudioAvailable: "AudioAvailable", AudioMuted: "AudioMuted" }, outputs: { HumanInput: "HumanInput", AudioMutedChange: "AudioMutedChange" }, decls: 13, vars: 8, consts: [["frameCanvas", ""], ["cursorCanvas", ""], [1, "rb-surface"], [1, "rb-bar"], ["aria-hidden", "true", 1, "rb-live"], [1, "rb-live-label"], [1, "rb-url", 3, "title"], ["title", "Click and type to control the live page", 1, "rb-driving"], ["type", "button", 1, "rb-speaker", 3, "rb-speaker--muted", "title"], [1, "rb-viewport"], ["alt", "Live view of the shared browser", 1, "rb-screenshot", 3, "src"], [1, "rb-placeholder"], ["aria-hidden", "true", 1, "fa-solid", "fa-hand-pointer"], ["type", "button", 1, "rb-speaker", 3, "click", "title"], ["aria-hidden", "true", 1, "fa-solid"], [1, "rb-canvas-stack"], [1, "rb-screenshot"], [1, "rb-screenshot", "rb-cursor-overlay"], ["text", "Waiting for the browser\u2026", "size", "medium"]], template: function RemoteBrowserSurfaceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
805
|
+
} }, inputs: { Fetch: "Fetch", FetchSelection: "FetchSelection", Streaming: "Streaming", Interactive: "Interactive", AudioAvailable: "AudioAvailable", AudioMuted: "AudioMuted" }, outputs: { HumanInput: "HumanInput", AudioMutedChange: "AudioMutedChange" }, decls: 13, vars: 8, consts: [["frameCanvas", ""], ["cursorCanvas", ""], [1, "rb-surface"], [1, "rb-bar"], ["aria-hidden", "true", 1, "rb-live"], [1, "rb-live-label"], [1, "rb-url", 3, "title"], ["title", "Click and type to control the live page", 1, "rb-driving"], ["type", "button", 1, "rb-speaker", 3, "rb-speaker--muted", "title"], [1, "rb-viewport"], ["alt", "Live view of the shared browser", 1, "rb-screenshot", 3, "src"], [1, "rb-placeholder"], ["aria-hidden", "true", 1, "fa-solid", "fa-hand-pointer"], ["type", "button", 1, "rb-speaker", 3, "click", "title"], ["aria-hidden", "true", 1, "fa-solid"], [1, "rb-canvas-stack"], [1, "rb-screenshot"], [1, "rb-screenshot", "rb-cursor-overlay"], ["text", "Waiting for the browser\u2026", "size", "medium"]], template: function RemoteBrowserSurfaceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
745
806
|
i0.ɵɵelementStart(0, "div", 2)(1, "div", 3);
|
|
746
807
|
i0.ɵɵelement(2, "span", 4);
|
|
747
808
|
i0.ɵɵelementStart(3, "span", 5);
|
|
@@ -828,6 +889,8 @@ export class RemoteBrowserSurfaceComponent {
|
|
|
828
889
|
`, styles: ["\n :host { display: block; height: 100%; }\n .rb-surface {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n }\n .rb-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n }\n .rb-live {\n width: 8px;\n height: 8px;\n border-radius: var(--mj-radius-full, 50%);\n background: var(--mj-text-disabled);\n flex: 0 0 auto;\n }\n .rb-live--on {\n background: var(--mj-status-success);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-status-success) 25%, transparent);\n }\n .rb-live-label {\n flex: 0 0 auto;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n .rb-url {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--mj-font-mono, monospace);\n }\n .rb-viewport {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: auto;\n padding: 12px;\n }\n .rb-screenshot {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 6px);\n box-shadow: var(--mj-shadow-sm, 0 1px 3px color-mix(in srgb, var(--mj-text-primary) 12%, transparent));\n background: var(--mj-bg-surface);\n }\n .rb-screenshot--interactive {\n /* Hide the OS cursor \u2014 we render a synthetic cursor on the overlay so there's exactly one pointer\n and it reads naturally for clicking on a web page (not the old crosshair). */\n cursor: none;\n outline: none;\n }\n .rb-screenshot--interactive:focus-visible {\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n }\n /* Frame canvas + synthetic-cursor overlay share one box; the stack shrinks to the frame canvas so the\n absolutely-positioned overlay (inset:0, same object-fit) lines up pixel-for-pixel with it. */\n .rb-canvas-stack {\n position: relative;\n display: inline-flex;\n max-width: 100%;\n max-height: 100%;\n min-height: 0;\n }\n .rb-cursor-overlay {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n /* Transparent passthrough layer \u2014 no chrome of its own, and never steals pointer/wheel events from\n the frame canvas underneath (those listeners drive takeover). */\n border: none;\n box-shadow: none;\n background: transparent;\n pointer-events: none;\n }\n .rb-driving {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 2px 8px;\n border-radius: var(--mj-radius-full, 999px);\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n }\n .rb-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .rb-hidden {\n display: none;\n }\n .rb-speaker {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 6px);\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n cursor: pointer;\n font-size: 0.8125rem;\n transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;\n }\n .rb-speaker:hover {\n background: var(--mj-bg-surface-hover);\n }\n .rb-speaker:focus-visible {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n }\n .rb-speaker--muted {\n color: var(--mj-text-muted);\n }\n "] }]
|
|
829
890
|
}], null, { Fetch: [{
|
|
830
891
|
type: Input
|
|
892
|
+
}], FetchSelection: [{
|
|
893
|
+
type: Input
|
|
831
894
|
}], Streaming: [{
|
|
832
895
|
type: Input
|
|
833
896
|
}], Interactive: [{
|
|
@@ -847,5 +910,5 @@ export class RemoteBrowserSurfaceComponent {
|
|
|
847
910
|
type: ViewChild,
|
|
848
911
|
args: ['cursorCanvas']
|
|
849
912
|
}] }); })();
|
|
850
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RemoteBrowserSurfaceComponent, { className: "RemoteBrowserSurfaceComponent", filePath: "src/lib/components/realtime/remote-browser/remote-browser-surface.component.ts", lineNumber:
|
|
913
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RemoteBrowserSurfaceComponent, { className: "RemoteBrowserSurfaceComponent", filePath: "src/lib/components/realtime/remote-browser/remote-browser-surface.component.ts", lineNumber: 341 }); })();
|
|
851
914
|
//# sourceMappingURL=remote-browser-surface.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-browser-surface.component.js","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-surface.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, NgZone, OnDestroy,\n OnInit, Output, ViewChild, inject\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { SharedGenericModule } from '@memberjunction/ng-shared-generic';\n\n/** How often the surface polls the server for a fresh page screenshot while bound (~1.4 fps). */\nconst SNAPSHOT_POLL_MS = 700;\n\n/** Min interval between forwarded pointer-move samples during takeover (~20 moves/sec). */\nconst POINTER_MOVE_THROTTLE_MS = 50;\n\n/** Min interval between forwarded scroll samples during takeover; the deltas coalesce between sends (~25/sec). */\nconst SCROLL_THROTTLE_MS = 40;\n\n/** Radius (px) of the synthetic cursor ring drawn on the overlay so the user can see their pointer. */\nconst SYNTHETIC_CURSOR_RADIUS = 6;\n\n/** A keyboard modifier held during a relayed human input (Shift-click selection, Ctrl/Cmd+key chords). */\nexport type RemoteBrowserModifier = 'Shift' | 'Control' | 'Alt' | 'Meta';\n\n/**\n * One human-takeover input the surface emits to the channel while the user \"drives\" the live view.\n * Mirrors the server's `RemoteBrowserHumanInput` union (flattened for transport): pointer\n * moves/clicks/down/up and scrolls carry VIEWPORT coordinates, a scroll also carries wheel deltas, a key\n * carries the pressed key string. Pointer clicks/down/up and keys also carry any held `modifiers`.\n */\nexport interface RemoteBrowserHumanInputEvent {\n /** Which input occurred. */\n kind: 'pointer-move' | 'pointer-click' | 'pointer-down' | 'pointer-up' | 'key' | 'scroll';\n /** Viewport X (pointer + scroll kinds only). */\n x?: number;\n /** Viewport Y (pointer + scroll kinds only). */\n y?: number;\n /** Mouse button (pointer-click / -down / -up only). */\n button?: 'left' | 'middle' | 'right';\n /** The pressed key (key kind only). */\n key?: string;\n /** Horizontal wheel delta in pixels (scroll kind only; positive = right). */\n deltaX?: number;\n /** Vertical wheel delta in pixels (scroll kind only; positive = down). */\n deltaY?: number;\n /** Modifier keys held during the input (pointer-click / -down / -up and key kinds). */\n modifiers?: RemoteBrowserModifier[];\n}\n\n/**\n * Keys forwarded into the page during takeover even though they aren't single printable characters —\n * navigation/editing keys the page (not the host app) should receive. `preventDefault` is applied only to\n * forwarded keys so the host app's own shortcuts on un-forwarded keys still work.\n */\nconst FORWARDED_CONTROL_KEYS = new Set<string>([\n 'Enter', 'Tab', 'Backspace', 'Delete', 'Escape',\n 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight',\n 'Home', 'End', 'PageUp', 'PageDown',\n]);\n\n/**\n * The modifier keys themselves — pressing one alone is never forwarded as a key (it only rides as a\n * modifier on the NEXT non-modifier key / click), so the page never receives a lone `'Shift'` keypress.\n */\nconst MODIFIER_KEYS = new Set<string>(['Shift', 'Control', 'Alt', 'Meta', 'CapsLock']);\n\n/**\n * Maps a display-space pointer position on the live canvas to the server browser's VIEWPORT pixel space.\n *\n * The canvas's internal resolution (`canvasWidth`/`canvasHeight`, set from each pushed frame) IS the\n * viewport pixel space; the canvas is displayed scaled to its bounding rect. So a display point maps as\n * `vx = (clientX - rect.left) / rect.width * canvasWidth` (and likewise for y), rounded to ints. Returns\n * `null` for a zero-size rect or un-sized canvas (divide-by-zero / not-ready guard).\n *\n * Pure + framework-free so it's unit-testable without a DOM.\n *\n * @param clientX The pointer's viewport X in the DOM (`event.clientX`).\n * @param clientY The pointer's viewport Y in the DOM (`event.clientY`).\n * @param rect The canvas's bounding rect (`getBoundingClientRect()`).\n * @param canvasWidth The canvas's internal pixel width (= frame/viewport width).\n * @param canvasHeight The canvas's internal pixel height (= frame/viewport height).\n * @returns Integer viewport coordinates, or `null` when mapping isn't possible.\n */\nexport function MapToViewportCoords(\n clientX: number,\n clientY: number,\n rect: { left: number; top: number; width: number; height: number },\n canvasWidth: number,\n canvasHeight: number,\n): { x: number; y: number } | null {\n if (rect.width <= 0 || rect.height <= 0 || canvasWidth <= 0 || canvasHeight <= 0) {\n return null;\n }\n const x = Math.round((clientX - rect.left) / rect.width * canvasWidth);\n const y = Math.round((clientY - rect.top) / rect.height * canvasHeight);\n return { x, y };\n}\n\n/**\n * One snapshot of the server-hosted browser — the shape the surface's {@link RemoteBrowserSurfaceComponent.Fetch}\n * callback resolves to. Mirrors the server's `RemoteBrowserSnapshot` query payload.\n */\nexport interface RemoteBrowserSnapshotView {\n /** The current page screenshot as raw base64 PNG, or `null` when none is available yet. */\n ScreenshotBase64: string | null;\n /** The current page URL, or `null` when no page is loaded. */\n CurrentUrl: string | null;\n}\n\n/**\n * Fetches one {@link RemoteBrowserSnapshotView} from the server. Supplied by the channel\n * plugin (it owns the session id + GraphQL provider). Best-effort by contract: resolves to\n * `null` on any failure rather than throwing, so the surface keeps the last good frame.\n */\nexport type RemoteBrowserSnapshotFetcher = () => Promise<RemoteBrowserSnapshotView | null>;\n\n/**\n * LIVE REMOTE-BROWSER surface (`mj-realtime-remote-browser-surface`) — the Browser channel\n * tab's pane. It renders the SERVER-hosted browser the agent drives: a refreshing screenshot\n * `<img>` with the current URL above it and a small \"live\" indicator. The agent's\n * `browser_*` tools mutate the page through the channel plugin; this surface only PERCEIVES\n * it, polling its {@link Fetch} callback every {@link SNAPSHOT_POLL_MS} ms while bound.\n *\n * The surface is transport-agnostic — it never touches GraphQL directly. The channel plugin\n * wires the {@link Fetch} callback (closing over the session id + provider) in `BindSurface`\n * before the surface's first change detection, so the `ngOnInit` poll has it. Polling stops\n * in `ngOnDestroy` (pane collapsed / overlay torn down) so no traffic continues after unbind.\n * View-only in v1 — there is no takeover input.\n *\n * ### Two render paths: pushed screencast (preferred) vs. snapshot poll (fallback)\n * When the backend advertises the `ScreenStreaming` capability, the server PUSHES encoded CDP\n * frames and the channel plugin sets {@link Streaming} = `true`. In that mode the surface paints\n * each frame onto a `<canvas>` via {@link RenderFrame} and DOES NOT start the poll. When streaming\n * is off (capability absent or start failed) the original behavior is unchanged: the surface polls\n * {@link Fetch} every {@link SNAPSHOT_POLL_MS} ms and renders the screenshot `<img>`.\n */\n@Component({\n standalone: true,\n selector: 'mj-realtime-remote-browser-surface',\n imports: [CommonModule, SharedGenericModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"rb-surface\">\n <div class=\"rb-bar\">\n <span class=\"rb-live\" [class.rb-live--on]=\"HasSnapshot\" aria-hidden=\"true\"></span>\n <span class=\"rb-live-label\">{{ HasSnapshot ? 'Live' : 'Connecting…' }}</span>\n <span class=\"rb-url\" [title]=\"CurrentUrl || ''\">{{ CurrentUrl || 'No page loaded yet' }}</span>\n @if (CanTakeOver) {\n <span class=\"rb-driving\" title=\"Click and type to control the live page\">\n <i class=\"fa-solid fa-hand-pointer\" aria-hidden=\"true\"></i> You're driving\n </span>\n }\n @if (AudioAvailable) {\n <button\n type=\"button\"\n class=\"rb-speaker\"\n [class.rb-speaker--muted]=\"AudioMuted\"\n [attr.aria-pressed]=\"!AudioMuted\"\n [attr.aria-label]=\"AudioMuted ? 'Unmute browser audio' : 'Mute browser audio'\"\n [title]=\"AudioMuted ? 'Unmute browser audio' : 'Mute browser audio'\"\n (click)=\"ToggleAudioMuted()\"\n >\n <i class=\"fa-solid\" [class.fa-volume-high]=\"!AudioMuted\" [class.fa-volume-xmark]=\"AudioMuted\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n <div class=\"rb-viewport\">\n @if (Streaming) {\n <div class=\"rb-canvas-stack\" [class.rb-hidden]=\"!HasFrame\">\n <canvas #frameCanvas class=\"rb-screenshot\" [class.rb-screenshot--interactive]=\"CanTakeOver\"></canvas>\n @if (CanTakeOver) {\n <!-- Synthetic cursor overlay: same internal resolution + CSS box as the frame canvas, so a\n point drawn at viewport coords aligns exactly. CDP screencast frames don't include the OS\n cursor, so we render the user's pointer locally for immediate, round-trip-free feedback. -->\n <canvas #cursorCanvas class=\"rb-screenshot rb-cursor-overlay\"></canvas>\n }\n </div>\n @if (!HasFrame) {\n <div class=\"rb-placeholder\">\n <mj-loading text=\"Waiting for the browser…\" size=\"medium\"></mj-loading>\n </div>\n }\n } @else if (ScreenshotDataUrl) {\n <img class=\"rb-screenshot\" [src]=\"ScreenshotDataUrl\" alt=\"Live view of the shared browser\" />\n } @else {\n <div class=\"rb-placeholder\">\n <mj-loading text=\"Waiting for the browser…\" size=\"medium\"></mj-loading>\n </div>\n }\n </div>\n </div>\n `,\n styles: [`\n :host { display: block; height: 100%; }\n .rb-surface {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n }\n .rb-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n }\n .rb-live {\n width: 8px;\n height: 8px;\n border-radius: var(--mj-radius-full, 50%);\n background: var(--mj-text-disabled);\n flex: 0 0 auto;\n }\n .rb-live--on {\n background: var(--mj-status-success);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-status-success) 25%, transparent);\n }\n .rb-live-label {\n flex: 0 0 auto;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n .rb-url {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--mj-font-mono, monospace);\n }\n .rb-viewport {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: auto;\n padding: 12px;\n }\n .rb-screenshot {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 6px);\n box-shadow: var(--mj-shadow-sm, 0 1px 3px color-mix(in srgb, var(--mj-text-primary) 12%, transparent));\n background: var(--mj-bg-surface);\n }\n .rb-screenshot--interactive {\n /* Hide the OS cursor — we render a synthetic cursor on the overlay so there's exactly one pointer\n and it reads naturally for clicking on a web page (not the old crosshair). */\n cursor: none;\n outline: none;\n }\n .rb-screenshot--interactive:focus-visible {\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n }\n /* Frame canvas + synthetic-cursor overlay share one box; the stack shrinks to the frame canvas so the\n absolutely-positioned overlay (inset:0, same object-fit) lines up pixel-for-pixel with it. */\n .rb-canvas-stack {\n position: relative;\n display: inline-flex;\n max-width: 100%;\n max-height: 100%;\n min-height: 0;\n }\n .rb-cursor-overlay {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n /* Transparent passthrough layer — no chrome of its own, and never steals pointer/wheel events from\n the frame canvas underneath (those listeners drive takeover). */\n border: none;\n box-shadow: none;\n background: transparent;\n pointer-events: none;\n }\n .rb-driving {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 2px 8px;\n border-radius: var(--mj-radius-full, 999px);\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n }\n .rb-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .rb-hidden {\n display: none;\n }\n .rb-speaker {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 6px);\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n cursor: pointer;\n font-size: 0.8125rem;\n transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;\n }\n .rb-speaker:hover {\n background: var(--mj-bg-surface-hover);\n }\n .rb-speaker:focus-visible {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n }\n .rb-speaker--muted {\n color: var(--mj-text-muted);\n }\n `]\n})\nexport class RemoteBrowserSurfaceComponent implements OnInit, OnDestroy {\n /** Snapshot fetcher supplied by the channel plugin (closes over the session id + provider). */\n @Input() Fetch: RemoteBrowserSnapshotFetcher | null = null;\n\n /**\n * Whether the server is PUSHING live screencast frames for this session (the backend advertised the\n * `ScreenStreaming` capability and the start succeeded). When `true` the surface paints pushed frames\n * onto its `<canvas>` via {@link RenderFrame} and does NOT poll; when `false` it uses the snapshot\n * `<img>` poll fallback. Set by the channel plugin in `BindSurface` from the start-screencast result.\n *\n * The start-screencast result is async, so this may flip to `true` AFTER `ngOnInit` has already begun\n * polling — the setter tears the poll down in that case so the two render paths never run at once.\n */\n @Input()\n set Streaming(value: boolean) {\n if (value === this._streaming) {\n return;\n }\n this._streaming = value;\n if (value) {\n // Late flip after the poll-fallback already started: stop polling, switch to canvas frames.\n this.stopPolling();\n this.zone.run(() => this.cdr.markForCheck());\n }\n // Takeover only attaches on the canvas path — (de)attach as streaming flips.\n this.syncTakeoverListeners();\n }\n get Streaming(): boolean {\n return this._streaming;\n }\n private _streaming = false;\n\n /**\n * Whether HUMAN TAKEOVER is enabled — the user watching the live view can click/type into the page and\n * those events are relayed into the server browser (Collaborative control). Takeover only attaches to the\n * canvas render path (pushed screencast); the `<img>` poll fallback stays view-only. When enabled while\n * already streaming the setter attaches listeners; flipping it off (or streaming off) detaches them.\n */\n @Input()\n set Interactive(value: boolean) {\n if (value === this._interactive) {\n return;\n }\n this._interactive = value;\n this.syncTakeoverListeners();\n this.zone.run(() => this.cdr.markForCheck());\n }\n get Interactive(): boolean {\n return this._interactive;\n }\n private _interactive = false;\n\n /** Emits each human-takeover input (pointer move/click, key) the user performs on the live canvas. */\n @Output() HumanInput = new EventEmitter<RemoteBrowserHumanInputEvent>();\n\n /**\n * Whether the live tab-audio stream is available — the channel sets this `true` when the server confirms\n * it is pushing audio. Drives whether the speaker toggle renders in the live-view bar.\n */\n @Input() AudioAvailable = false;\n\n /**\n * Whether tab audio is currently muted. Two-way: the channel sets the initial value (un-muted when audio\n * starts) and the toggle updates it; {@link AudioMutedChange} relays each user change to the channel,\n * which mutes/unmutes the player.\n */\n @Input() AudioMuted = false;\n\n /** Emits the new muted state each time the user toggles the speaker (two-way `AudioMuted`). */\n @Output() AudioMutedChange = new EventEmitter<boolean>();\n\n /** Toggles the speaker mute state and relays it to the channel (which mutes/unmutes the audio player). */\n public ToggleAudioMuted(): void {\n this.AudioMuted = !this.AudioMuted;\n this.AudioMutedChange.emit(this.AudioMuted);\n }\n\n /** True when takeover is both enabled AND on the canvas path — drives the cursor + \"driving\" pill. */\n public get CanTakeOver(): boolean {\n return this._interactive && this._streaming;\n }\n\n /**\n * True while the user is actively driving the remote browser with the keyboard — the takeover canvas\n * holds focus. The host overlay reads this to stand down its own global keyboard shortcuts (e.g.\n * T-to-type opening the local composer) so keystrokes go to the remote page, not the local input.\n */\n public get IsCapturingInput(): boolean {\n return this.surfaceFocused && this.CanTakeOver;\n }\n\n /** The current screenshot as a `data:` URL, or `null` before the first snapshot arrives. */\n public ScreenshotDataUrl: string | null = null;\n /** The current page URL reported by the server, or `null` when none. */\n public CurrentUrl: string | null = null;\n /** Whether at least one pushed screencast frame has been painted (drives the canvas placeholder). */\n public HasFrame = false;\n\n /** The canvas the pushed screencast frames are painted onto (present only while {@link Streaming}). */\n @ViewChild('frameCanvas') private frameCanvas?: ElementRef<HTMLCanvasElement>;\n\n /** The transparent overlay the synthetic cursor is drawn onto (present only while {@link CanTakeOver}). */\n @ViewChild('cursorCanvas') private cursorCanvas?: ElementRef<HTMLCanvasElement>;\n\n /** Whether at least one screenshot OR frame has been rendered (drives the \"Live\" indicator). */\n public get HasSnapshot(): boolean {\n return this.ScreenshotDataUrl !== null || this.HasFrame;\n }\n\n private readonly cdr = inject(ChangeDetectorRef);\n private readonly zone = inject(NgZone);\n /** Active poll timer; `null` when polling is stopped. */\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n /** Guards against overlapping polls when a request runs longer than the interval. */\n private polling = false;\n /** Set on destroy so an in-flight poll's late resolution doesn't touch a torn-down view. */\n private destroyed = false;\n /** Reused decode target for pushed frames — avoids allocating an `Image` per frame. */\n private readonly frameImage = new Image();\n /** The most recent un-painted frame data URL, drained on the next animation frame (drop-old coalescing). */\n private pendingFrameDataUrl: string | null = null;\n /** True while a paint is scheduled, so rapid frames coalesce to one `requestAnimationFrame`. */\n private framePaintScheduled = false;\n\n /** The canvas element takeover listeners are currently bound to, or `null` when detached. */\n private takeoverCanvas: HTMLCanvasElement | null = null;\n /** Timestamp (ms) of the last forwarded pointer-move, for throttling the high-frequency stream. */\n private lastPointerMoveAt = 0;\n /** Timestamp (ms) of the last forwarded scroll, for throttling the high-frequency wheel stream. */\n private lastScrollAt = 0;\n /** Horizontal wheel delta accumulated since the last forwarded scroll (coalesced across throttled events). */\n private pendingScrollDeltaX = 0;\n /** Vertical wheel delta accumulated since the last forwarded scroll (coalesced across throttled events). */\n private pendingScrollDeltaY = 0;\n\n /** Last canvas-relative mouse position in VIEWPORT pixel space, or `null` when the pointer is off-canvas. */\n private cursorViewportPoint: { x: number; y: number } | null = null;\n /** True while a synthetic-cursor repaint is queued, so rapid moves coalesce to one `requestAnimationFrame`. */\n private cursorPaintScheduled = false;\n\n /**\n * True while the user is mid-drag on the canvas (a `mousedown` not yet released) — drives forwarding\n * intermediate moves as a drag and emitting the closing `pointer-up`. Click-drag text selection relies on\n * this so the drag relays as down → moves → up rather than a single discrete click.\n */\n private dragging = false;\n\n /**\n * True when the most recent `mousedown`→`mouseup` actually MOVED (a real drag), so the synthetic `click`\n * the browser fires right after should be suppressed — the drag's down/move/up already conveyed the intent,\n * and a trailing click would collapse a just-made text selection.\n */\n private suppressNextClick = false;\n\n /** Viewport point of the active drag's mousedown, used to tell a real drag from a stationary click. */\n private dragStartPoint: { x: number; y: number } | null = null;\n\n /**\n * True while the takeover canvas currently HOLDS keyboard focus. The host overlay reads this (via\n * {@link IsCapturingInput}) to suppress its own global key shortcuts (e.g. T-to-type) so the user's\n * keystrokes land in the remote browser, not the local composer — fixing the \"greedy textbox\" focus theft.\n */\n private surfaceFocused = false;\n\n /** Bound handlers — stored so the exact same references can be removed on detach. */\n private readonly onCanvasMouseMove = (e: MouseEvent): void => this.handlePointerMove(e);\n private readonly onCanvasMouseDown = (e: MouseEvent): void => this.handlePointerDown(e);\n private readonly onCanvasMouseUp = (e: MouseEvent): void => this.handlePointerUp(e);\n private readonly onCanvasClick = (e: MouseEvent): void => this.handlePointerClick(e);\n private readonly onCanvasKeyDown = (e: KeyboardEvent): void => this.handleKeyDown(e);\n private readonly onCanvasMouseLeave = (): void => this.handleMouseLeave();\n private readonly onCanvasWheel = (e: WheelEvent): void => this.handleWheel(e);\n private readonly onCanvasFocus = (): void => { this.surfaceFocused = true; };\n private readonly onCanvasBlur = (): void => { this.surfaceFocused = false; };\n\n ngOnInit(): void {\n // In streaming mode the server pushes frames — never start the poll (it would be redundant traffic).\n if (this.Streaming) {\n return;\n }\n void this.pollOnce();\n this.startPolling();\n }\n\n ngOnDestroy(): void {\n this.destroyed = true;\n this.stopPolling();\n this.detachTakeoverListeners();\n }\n\n /** Starts the interval poll OUTSIDE Angular's zone so it doesn't trigger CD on every tick. */\n private startPolling(): void {\n if (this.pollTimer !== null) {\n return;\n }\n this.zone.runOutsideAngular(() => {\n this.pollTimer = setInterval(() => void this.pollOnce(), SNAPSHOT_POLL_MS);\n });\n }\n\n /** Stops the interval poll (no further snapshot requests are issued). */\n private stopPolling(): void {\n if (this.pollTimer !== null) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Fetches one snapshot and applies it to the view. Best-effort: the fetcher resolves to\n * `null` on failure (the prior frame stays on screen) so a single failed poll never breaks\n * the live view. Skips when a prior poll is still in flight, the fetcher isn't wired, or\n * after destroy.\n */\n private async pollOnce(): Promise<void> {\n if (this.polling || this.destroyed || !this.Fetch) {\n return;\n }\n this.polling = true;\n try {\n const snapshot = await this.Fetch();\n this.applySnapshot(snapshot);\n } catch {\n // Defensive — the fetcher is contractually non-throwing, but never let a poll break the view.\n } finally {\n this.polling = false;\n }\n }\n\n /** Applies a fetched snapshot to the view fields and triggers OnPush change detection. */\n private applySnapshot(snapshot: RemoteBrowserSnapshotView | null): void {\n if (this.destroyed) {\n return;\n }\n const base64 = snapshot?.ScreenshotBase64 ?? null;\n const nextUrl = snapshot?.CurrentUrl ?? null;\n const nextDataUrl = base64 ? `data:image/png;base64,${base64}` : this.ScreenshotDataUrl;\n if (nextDataUrl === this.ScreenshotDataUrl && nextUrl === this.CurrentUrl) {\n return; // nothing changed — skip the CD pass\n }\n this.ScreenshotDataUrl = nextDataUrl;\n this.CurrentUrl = nextUrl;\n // Re-enter the zone for the OnPush update (the poll runs outside Angular).\n this.zone.run(() => this.cdr.markForCheck());\n }\n\n /**\n * Paints one PUSHED screencast frame (base64 JPEG) onto the canvas. Called by the channel plugin for\n * each frame the server pushes while {@link Streaming}. Coalesces a burst of frames to one paint per\n * animation frame (the newest wins) so a fast stream never floods the main thread, and reuses a single\n * `Image` decode target to avoid per-frame allocation. No-op after destroy or when not in streaming mode.\n *\n * @param dataBase64 The frame image as raw base64 JPEG (no `data:` prefix).\n */\n public RenderFrame(dataBase64: string): void {\n if (this.destroyed || !this.Streaming || !dataBase64) {\n return;\n }\n this.pendingFrameDataUrl = `data:image/jpeg;base64,${dataBase64}`;\n if (this.framePaintScheduled) {\n return; // a paint is already queued — it will pick up this newest frame\n }\n this.framePaintScheduled = true;\n this.zone.runOutsideAngular(() => requestAnimationFrame(() => this.paintPendingFrame()));\n }\n\n /**\n * Updates the URL shown above the live view. In streaming (canvas) mode the snapshot poll is stopped, so\n * the channel pushes the current URL here after a navigation/action reports one — otherwise the bar would\n * stay stuck on \"No page loaded yet\" even though the page is live. No-op for an unchanged / empty value.\n *\n * @param url The current page URL, or null/empty to leave the bar unchanged.\n */\n public SetCurrentUrl(url: string | null | undefined): void {\n if (this.destroyed || !url || url === this.CurrentUrl) {\n return;\n }\n this.CurrentUrl = url;\n this.zone.run(() => this.cdr.markForCheck());\n }\n\n /** Drains the most recent pending frame onto the canvas (drop-old coalescing target). */\n private paintPendingFrame(): void {\n this.framePaintScheduled = false;\n const dataUrl = this.pendingFrameDataUrl;\n this.pendingFrameDataUrl = null;\n if (this.destroyed || !dataUrl) {\n return;\n }\n this.frameImage.onload = () => this.drawFrameImage();\n this.frameImage.src = dataUrl;\n }\n\n /** Draws the decoded frame image onto the canvas, sizing the canvas to the frame on the first paint. */\n private drawFrameImage(): void {\n const canvas = this.frameCanvas?.nativeElement;\n if (this.destroyed || !canvas) {\n return;\n }\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return;\n }\n const { naturalWidth: w, naturalHeight: h } = this.frameImage;\n if (w > 0 && h > 0 && (canvas.width !== w || canvas.height !== h)) {\n canvas.width = w;\n canvas.height = h;\n }\n ctx.drawImage(this.frameImage, 0, 0);\n if (!this.HasFrame) {\n // First frame arrived — re-enter the zone to clear the placeholder + flip the \"Live\" indicator,\n // and attach takeover listeners now that the canvas element exists in the DOM.\n this.zone.run(() => {\n this.HasFrame = true;\n this.cdr.markForCheck();\n });\n this.syncTakeoverListeners();\n }\n }\n\n // ----- human takeover (Collaborative control) --------------------------------------------\n\n /**\n * Attaches or detaches the canvas takeover listeners so they're bound exactly when takeover is live —\n * {@link CanTakeOver} is true AND the canvas element exists. Idempotent: re-binds only when the target\n * canvas changed, detaches when the conditions no longer hold. Safe to call from any state-flip path\n * (the `Interactive`/`Streaming` setters, the first-frame paint, destroy).\n */\n private syncTakeoverListeners(): void {\n const canvas = this.CanTakeOver && !this.destroyed ? (this.frameCanvas?.nativeElement ?? null) : null;\n if (canvas === this.takeoverCanvas) {\n return; // already in the desired state\n }\n this.detachTakeoverListeners();\n if (canvas) {\n this.attachTakeoverListeners(canvas);\n }\n }\n\n /** Binds the pointer/keyboard/wheel listeners to the canvas OUTSIDE the zone (mousemove/wheel are high-frequency). */\n private attachTakeoverListeners(canvas: HTMLCanvasElement): void {\n canvas.tabIndex = 0; // focusable so it can receive keydown\n // Marks this canvas as a keyboard-capturing surface so the host overlay's global shortcuts (e.g.\n // T-to-type) stand down while it holds focus — see the overlay's isKeyboardCapturingSurfaceFocused().\n canvas.setAttribute('data-mj-capture-keys', '');\n this.zone.runOutsideAngular(() => {\n canvas.addEventListener('mousemove', this.onCanvasMouseMove);\n canvas.addEventListener('mousedown', this.onCanvasMouseDown);\n canvas.addEventListener('mouseup', this.onCanvasMouseUp);\n canvas.addEventListener('click', this.onCanvasClick);\n canvas.addEventListener('keydown', this.onCanvasKeyDown);\n canvas.addEventListener('mouseleave', this.onCanvasMouseLeave);\n canvas.addEventListener('focus', this.onCanvasFocus);\n canvas.addEventListener('blur', this.onCanvasBlur);\n // `passive: false` so preventDefault stops the host page from scrolling while the user drives the page.\n canvas.addEventListener('wheel', this.onCanvasWheel, { passive: false });\n });\n this.takeoverCanvas = canvas;\n }\n\n /** Removes the listeners from the currently-bound canvas (no-op when none are bound) and clears the cursor. */\n private detachTakeoverListeners(): void {\n const canvas = this.takeoverCanvas;\n if (!canvas) {\n return;\n }\n canvas.removeEventListener('mousemove', this.onCanvasMouseMove);\n canvas.removeEventListener('mousedown', this.onCanvasMouseDown);\n canvas.removeEventListener('mouseup', this.onCanvasMouseUp);\n canvas.removeEventListener('click', this.onCanvasClick);\n canvas.removeEventListener('keydown', this.onCanvasKeyDown);\n canvas.removeEventListener('mouseleave', this.onCanvasMouseLeave);\n canvas.removeEventListener('focus', this.onCanvasFocus);\n canvas.removeEventListener('blur', this.onCanvasBlur);\n canvas.removeEventListener('wheel', this.onCanvasWheel);\n canvas.removeAttribute('data-mj-capture-keys');\n this.takeoverCanvas = null;\n this.cursorViewportPoint = null;\n this.dragging = false;\n this.suppressNextClick = false;\n this.dragStartPoint = null;\n this.surfaceFocused = false;\n this.clearSyntheticCursor();\n }\n\n /**\n * Pointer-move → tracks the cursor for the synthetic-cursor overlay (every move, immediate, no\n * round-trip) AND emits a throttled viewport-mapped move to the server. Runs outside the zone (no CD\n * per move).\n */\n private handlePointerMove(event: MouseEvent): void {\n const point = this.toViewportCoords(event);\n if (point) {\n // Always update the local cursor for instant feedback — independent of the server-relay throttle.\n this.cursorViewportPoint = point;\n this.scheduleCursorPaint();\n }\n const now = Date.now();\n if (now - this.lastPointerMoveAt < POINTER_MOVE_THROTTLE_MS) {\n return;\n }\n this.lastPointerMoveAt = now;\n if (point) {\n this.emitInput({ kind: 'pointer-move', x: point.x, y: point.y });\n }\n }\n\n /** Mouse leaves the canvas → hide the synthetic cursor (nothing to relay). */\n private handleMouseLeave(): void {\n this.cursorViewportPoint = null;\n this.scheduleCursorPaint();\n }\n\n /**\n * Wheel/trackpad/Magic-Mouse scroll over the canvas → forwards a viewport-mapped scroll to the server.\n * Calls `preventDefault` so the host page doesn't scroll while the user drives the live page. Throttled\n * to {@link SCROLL_THROTTLE_MS}, coalescing the skipped deltas so no scroll distance is lost.\n */\n private handleWheel(event: WheelEvent): void {\n event.preventDefault();\n this.pendingScrollDeltaX += event.deltaX;\n this.pendingScrollDeltaY += event.deltaY;\n const point = this.toViewportCoords(event);\n if (point) {\n this.cursorViewportPoint = point;\n this.scheduleCursorPaint();\n }\n const now = Date.now();\n if (now - this.lastScrollAt < SCROLL_THROTTLE_MS) {\n return; // accumulate into pendingScrollDelta* until the throttle window opens\n }\n this.lastScrollAt = now;\n const target = point ?? this.cursorViewportPoint;\n if (target && (this.pendingScrollDeltaX !== 0 || this.pendingScrollDeltaY !== 0)) {\n this.emitInput({ kind: 'scroll', x: target.x, y: target.y, deltaX: this.pendingScrollDeltaX, deltaY: this.pendingScrollDeltaY });\n this.pendingScrollDeltaX = 0;\n this.pendingScrollDeltaY = 0;\n }\n }\n\n /**\n * Mouse-down on the canvas → focuses the canvas (so subsequent keys are captured) and BEGINS a drag:\n * emits a `pointer-down` and arms drag mode so the following moves relay as drag motion and the matching\n * `pointer-up` closes it. This is what makes click-drag text selection work (rather than a discrete click).\n */\n private handlePointerDown(event: MouseEvent): void {\n this.takeoverCanvas?.focus();\n const point = this.toViewportCoords(event);\n if (!point) {\n return;\n }\n this.dragging = true;\n this.dragStartPoint = point;\n this.suppressNextClick = false;\n this.cursorViewportPoint = point;\n this.scheduleCursorPaint();\n this.emitInput({ kind: 'pointer-down', x: point.x, y: point.y, button: this.mapButton(event.button), modifiers: this.collectModifiers(event) });\n }\n\n /**\n * Mouse-up on the canvas → closes a drag with a `pointer-up` at the release point, disarming drag mode.\n * If the pointer actually moved between down and up, marks the following synthetic `click` to be suppressed\n * (so the drag selection isn't immediately collapsed by a trailing click).\n */\n private handlePointerUp(event: MouseEvent): void {\n if (!this.dragging) {\n return;\n }\n this.dragging = false;\n const point = this.toViewportCoords(event);\n if (point) {\n const start = this.dragStartPoint;\n this.suppressNextClick = !!start && (start.x !== point.x || start.y !== point.y);\n this.emitInput({ kind: 'pointer-up', x: point.x, y: point.y, button: this.mapButton(event.button), modifiers: this.collectModifiers(event) });\n }\n this.dragStartPoint = null;\n }\n\n /**\n * Click → emits a viewport-mapped click WITH any held modifiers (e.g. Shift-click text selection). The\n * server browser receives the press/release of a drag via {@link handlePointerDown}/{@link handlePointerUp};\n * a plain click (down+up with no motion) still fires this `click` event, so a simple click relays as one\n * click action — but we skip emitting it when it terminates a real drag (where down/up already covered it).\n */\n private handlePointerClick(event: MouseEvent): void {\n this.takeoverCanvas?.focus();\n if (this.suppressNextClick) {\n // This click terminates a real drag (selection) — the down/move/up already conveyed it.\n this.suppressNextClick = false;\n return;\n }\n const point = this.toViewportCoords(event);\n if (point) {\n this.emitInput({ kind: 'pointer-click', x: point.x, y: point.y, button: this.mapButton(event.button), modifiers: this.collectModifiers(event) });\n }\n }\n\n /**\n * Keydown → forwards printable keys, a curated set of control keys ({@link FORWARDED_CONTROL_KEYS}), AND\n * any key combined with a Ctrl/Cmd/Alt modifier (so combos like Ctrl/Cmd+A select-all, Cmd+C/Cmd+V relay).\n * Calls `preventDefault` + `stopPropagation` ONLY on forwarded keys, so (a) the host app keeps its own\n * shortcuts on un-forwarded keys and (b) forwarded keys never bubble to the host overlay's global shortcuts\n * (the T-to-type focus-steal). Lone modifier keys are never forwarded — they ride as `modifiers` on the\n * next key/click.\n */\n private handleKeyDown(event: KeyboardEvent): void {\n const key = event.key;\n if (MODIFIER_KEYS.has(key)) {\n return; // a modifier on its own is not a keypress — it rides on the next key/click\n }\n const isPrintable = key.length === 1;\n const hasComboModifier = event.ctrlKey || event.metaKey || event.altKey;\n const isForwardable = isPrintable || FORWARDED_CONTROL_KEYS.has(key) || hasComboModifier;\n if (!isForwardable) {\n return; // leave non-forwarded keys to the host app\n }\n event.preventDefault();\n // Stop the keystroke from reaching document-level handlers (e.g. the overlay's T-to-type) so the user's\n // typing lands in the remote browser, not the local composer.\n event.stopPropagation();\n this.emitInput({ kind: 'key', key, modifiers: this.collectModifiers(event) });\n }\n\n /**\n * Collects the modifier keys currently held during a DOM mouse/keyboard event into the relayed\n * {@link RemoteBrowserModifier} list (empty when none are held).\n *\n * @param event The DOM event whose modifier flags to read.\n * @returns The held modifiers, in a stable order.\n */\n private collectModifiers(event: MouseEvent | KeyboardEvent): RemoteBrowserModifier[] {\n const modifiers: RemoteBrowserModifier[] = [];\n if (event.shiftKey) { modifiers.push('Shift'); }\n if (event.ctrlKey) { modifiers.push('Control'); }\n if (event.altKey) { modifiers.push('Alt'); }\n if (event.metaKey) { modifiers.push('Meta'); }\n return modifiers;\n }\n\n /** Re-enters the zone to emit one human input (so subscribers see it inside Angular). */\n private emitInput(input: RemoteBrowserHumanInputEvent): void {\n this.zone.run(() => this.HumanInput.emit(input));\n }\n\n /**\n * Maps a DOM pointer event on the live canvas to VIEWPORT coordinates via {@link MapToViewportCoords}\n * (the pure, unit-tested mapping). Returns `null` when no canvas is bound or mapping isn't possible yet.\n *\n * @param event The pointer event on the canvas.\n * @returns The integer viewport coordinates, or `null` when mapping isn't possible yet.\n */\n private toViewportCoords(event: MouseEvent): { x: number; y: number } | null {\n const canvas = this.takeoverCanvas;\n if (!canvas) {\n return null;\n }\n return MapToViewportCoords(event.clientX, event.clientY, canvas.getBoundingClientRect(), canvas.width, canvas.height);\n }\n\n // ----- synthetic cursor (local feedback — CDP frames don't include the OS cursor) -----------\n\n /**\n * Schedules a synthetic-cursor repaint on the next animation frame, coalescing a burst of moves to one\n * paint (the newest position wins). Runs outside the zone — drawing the cursor never triggers Angular CD.\n */\n private scheduleCursorPaint(): void {\n if (this.cursorPaintScheduled) {\n return;\n }\n this.cursorPaintScheduled = true;\n this.zone.runOutsideAngular(() => requestAnimationFrame(() => this.paintSyntheticCursor()));\n }\n\n /**\n * Draws (or clears) the synthetic cursor on the overlay canvas at {@link cursorViewportPoint}. The overlay\n * shares the frame canvas's internal resolution (sized here to match), so a point drawn at viewport coords\n * aligns exactly with the page underneath. A small brand-tinted ring with a center dot reads as a pointer\n * without obscuring what it's over. No-op after destroy or when the overlay isn't mounted.\n */\n private paintSyntheticCursor(): void {\n this.cursorPaintScheduled = false;\n const overlay = this.cursorCanvas?.nativeElement;\n const frame = this.frameCanvas?.nativeElement;\n if (this.destroyed || !overlay || !frame) {\n return;\n }\n // Keep the overlay's internal resolution locked to the frame canvas so coordinate spaces match.\n if (overlay.width !== frame.width || overlay.height !== frame.height) {\n overlay.width = frame.width;\n overlay.height = frame.height;\n }\n const ctx = overlay.getContext('2d');\n if (!ctx) {\n return;\n }\n ctx.clearRect(0, 0, overlay.width, overlay.height);\n const point = this.cursorViewportPoint;\n if (!point) {\n return; // pointer left the canvas — leave the overlay cleared\n }\n const ring = this.resolveCssColor('--mj-brand-primary', '#264FAF');\n ctx.beginPath();\n ctx.arc(point.x, point.y, SYNTHETIC_CURSOR_RADIUS, 0, Math.PI * 2);\n ctx.lineWidth = 2;\n ctx.strokeStyle = ring;\n ctx.stroke();\n ctx.beginPath();\n ctx.arc(point.x, point.y, 1.5, 0, Math.PI * 2);\n ctx.fillStyle = ring;\n ctx.fill();\n }\n\n /** Clears the synthetic-cursor overlay (e.g. on detach). No-op when the overlay isn't mounted. */\n private clearSyntheticCursor(): void {\n const overlay = this.cursorCanvas?.nativeElement;\n const ctx = overlay?.getContext('2d');\n if (overlay && ctx) {\n ctx.clearRect(0, 0, overlay.width, overlay.height);\n }\n }\n\n /**\n * Resolves a CSS custom property (design token) to its computed color value off the host element, so the\n * canvas-drawn cursor honors theming. Falls back to the supplied default when the token is unset or the\n * host isn't measurable.\n *\n * @param token The CSS custom property name (e.g. `'--mj-brand-primary'`).\n * @param fallback The color to use when the token resolves empty.\n * @returns The resolved color string.\n */\n private resolveCssColor(token: string, fallback: string): string {\n const host = this.frameCanvas?.nativeElement;\n if (!host) {\n return fallback;\n }\n const value = getComputedStyle(host).getPropertyValue(token).trim();\n return value.length > 0 ? value : fallback;\n }\n\n /** Maps a DOM `MouseEvent.button` (0/1/2) to the relayed button union, defaulting to `'left'`. */\n private mapButton(button: number): 'left' | 'middle' | 'right' {\n if (button === 1) {\n return 'middle';\n }\n if (button === 2) {\n return 'right';\n }\n return 'left';\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remote-browser-surface.component.js","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-surface.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, NgZone, OnDestroy,\n OnInit, Output, ViewChild, inject\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { SharedGenericModule } from '@memberjunction/ng-shared-generic';\n\n/** How often the surface polls the server for a fresh page screenshot while bound (~1.4 fps). */\nconst SNAPSHOT_POLL_MS = 700;\n\n/** Min interval between forwarded pointer-move samples during takeover (~20 moves/sec). */\nconst POINTER_MOVE_THROTTLE_MS = 50;\n\n/** Min interval between forwarded scroll samples during takeover; the deltas coalesce between sends (~25/sec). */\nconst SCROLL_THROTTLE_MS = 40;\n\n/** Radius (px) of the synthetic cursor ring drawn on the overlay so the user can see their pointer. */\nconst SYNTHETIC_CURSOR_RADIUS = 6;\n\n/** A keyboard modifier held during a relayed human input (Shift-click selection, Ctrl/Cmd+key chords). */\nexport type RemoteBrowserModifier = 'Shift' | 'Control' | 'Alt' | 'Meta';\n\n/**\n * One human-takeover input the surface emits to the channel while the user \"drives\" the live view.\n * Mirrors the server's `RemoteBrowserHumanInput` union (flattened for transport): pointer\n * moves/clicks/down/up and scrolls carry VIEWPORT coordinates, a scroll also carries wheel deltas, a key\n * carries the pressed key string. Pointer clicks/down/up and keys also carry any held `modifiers`.\n */\nexport interface RemoteBrowserHumanInputEvent {\n /** Which input occurred. */\n kind: 'pointer-move' | 'pointer-click' | 'pointer-down' | 'pointer-up' | 'key' | 'text' | 'scroll';\n /** Viewport X (pointer + scroll kinds only). */\n x?: number;\n /** Viewport Y (pointer + scroll kinds only). */\n y?: number;\n /** Mouse button (pointer-click / -down / -up only). */\n button?: 'left' | 'middle' | 'right';\n /** The pressed key (key kind only). */\n key?: string;\n /** The pasted clipboard text (text kind only) — inserted into the remote page's focused element. */\n text?: string;\n /** Horizontal wheel delta in pixels (scroll kind only; positive = right). */\n deltaX?: number;\n /** Vertical wheel delta in pixels (scroll kind only; positive = down). */\n deltaY?: number;\n /** Modifier keys held during the input (pointer-click / -down / -up and key kinds). */\n modifiers?: RemoteBrowserModifier[];\n}\n\n/**\n * Keys forwarded into the page during takeover even though they aren't single printable characters —\n * navigation/editing keys the page (not the host app) should receive. `preventDefault` is applied only to\n * forwarded keys so the host app's own shortcuts on un-forwarded keys still work.\n */\nconst FORWARDED_CONTROL_KEYS = new Set<string>([\n 'Enter', 'Tab', 'Backspace', 'Delete', 'Escape',\n 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight',\n 'Home', 'End', 'PageUp', 'PageDown',\n]);\n\n/**\n * The modifier keys themselves — pressing one alone is never forwarded as a key (it only rides as a\n * modifier on the NEXT non-modifier key / click), so the page never receives a lone `'Shift'` keypress.\n */\nconst MODIFIER_KEYS = new Set<string>(['Shift', 'Control', 'Alt', 'Meta', 'CapsLock']);\n\n/**\n * Maps a display-space pointer position on the live canvas to the server browser's VIEWPORT pixel space.\n *\n * The canvas's internal resolution (`canvasWidth`/`canvasHeight`, set from each pushed frame) IS the\n * viewport pixel space; the canvas is displayed scaled to its bounding rect. So a display point maps as\n * `vx = (clientX - rect.left) / rect.width * canvasWidth` (and likewise for y), rounded to ints. Returns\n * `null` for a zero-size rect or un-sized canvas (divide-by-zero / not-ready guard).\n *\n * Pure + framework-free so it's unit-testable without a DOM.\n *\n * @param clientX The pointer's viewport X in the DOM (`event.clientX`).\n * @param clientY The pointer's viewport Y in the DOM (`event.clientY`).\n * @param rect The canvas's bounding rect (`getBoundingClientRect()`).\n * @param canvasWidth The canvas's internal pixel width (= frame/viewport width).\n * @param canvasHeight The canvas's internal pixel height (= frame/viewport height).\n * @returns Integer viewport coordinates, or `null` when mapping isn't possible.\n */\nexport function MapToViewportCoords(\n clientX: number,\n clientY: number,\n rect: { left: number; top: number; width: number; height: number },\n canvasWidth: number,\n canvasHeight: number,\n): { x: number; y: number } | null {\n if (rect.width <= 0 || rect.height <= 0 || canvasWidth <= 0 || canvasHeight <= 0) {\n return null;\n }\n const x = Math.round((clientX - rect.left) / rect.width * canvasWidth);\n const y = Math.round((clientY - rect.top) / rect.height * canvasHeight);\n return { x, y };\n}\n\n/**\n * One snapshot of the server-hosted browser — the shape the surface's {@link RemoteBrowserSurfaceComponent.Fetch}\n * callback resolves to. Mirrors the server's `RemoteBrowserSnapshot` query payload.\n */\nexport interface RemoteBrowserSnapshotView {\n /** The current page screenshot as raw base64 PNG, or `null` when none is available yet. */\n ScreenshotBase64: string | null;\n /** The current page URL, or `null` when no page is loaded. */\n CurrentUrl: string | null;\n}\n\n/**\n * Fetches one {@link RemoteBrowserSnapshotView} from the server. Supplied by the channel\n * plugin (it owns the session id + GraphQL provider). Best-effort by contract: resolves to\n * `null` on any failure rather than throwing, so the surface keeps the last good frame.\n */\nexport type RemoteBrowserSnapshotFetcher = () => Promise<RemoteBrowserSnapshotView | null>;\n\n/**\n * Reads the remote page's CURRENT text selection for copy-out (the mirror of the `'text'` paste-in path).\n * Supplied by the channel plugin (it owns the session id + GraphQL provider). Best-effort by contract:\n * resolves to `''` on any failure / when nothing is selected rather than throwing, so a best-effort copy\n * never breaks the live view.\n */\nexport type RemoteBrowserSelectionFetcher = () => Promise<string>;\n\n/**\n * LIVE REMOTE-BROWSER surface (`mj-realtime-remote-browser-surface`) — the Browser channel\n * tab's pane. It renders the SERVER-hosted browser the agent drives: a refreshing screenshot\n * `<img>` with the current URL above it and a small \"live\" indicator. The agent's\n * `browser_*` tools mutate the page through the channel plugin; this surface only PERCEIVES\n * it, polling its {@link Fetch} callback every {@link SNAPSHOT_POLL_MS} ms while bound.\n *\n * The surface is transport-agnostic — it never touches GraphQL directly. The channel plugin\n * wires the {@link Fetch} callback (closing over the session id + provider) in `BindSurface`\n * before the surface's first change detection, so the `ngOnInit` poll has it. Polling stops\n * in `ngOnDestroy` (pane collapsed / overlay torn down) so no traffic continues after unbind.\n * View-only in v1 — there is no takeover input.\n *\n * ### Two render paths: pushed screencast (preferred) vs. snapshot poll (fallback)\n * When the backend advertises the `ScreenStreaming` capability, the server PUSHES encoded CDP\n * frames and the channel plugin sets {@link Streaming} = `true`. In that mode the surface paints\n * each frame onto a `<canvas>` via {@link RenderFrame} and DOES NOT start the poll. When streaming\n * is off (capability absent or start failed) the original behavior is unchanged: the surface polls\n * {@link Fetch} every {@link SNAPSHOT_POLL_MS} ms and renders the screenshot `<img>`.\n */\n@Component({\n standalone: true,\n selector: 'mj-realtime-remote-browser-surface',\n imports: [CommonModule, SharedGenericModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"rb-surface\">\n <div class=\"rb-bar\">\n <span class=\"rb-live\" [class.rb-live--on]=\"HasSnapshot\" aria-hidden=\"true\"></span>\n <span class=\"rb-live-label\">{{ HasSnapshot ? 'Live' : 'Connecting…' }}</span>\n <span class=\"rb-url\" [title]=\"CurrentUrl || ''\">{{ CurrentUrl || 'No page loaded yet' }}</span>\n @if (CanTakeOver) {\n <span class=\"rb-driving\" title=\"Click and type to control the live page\">\n <i class=\"fa-solid fa-hand-pointer\" aria-hidden=\"true\"></i> You're driving\n </span>\n }\n @if (AudioAvailable) {\n <button\n type=\"button\"\n class=\"rb-speaker\"\n [class.rb-speaker--muted]=\"AudioMuted\"\n [attr.aria-pressed]=\"!AudioMuted\"\n [attr.aria-label]=\"AudioMuted ? 'Unmute browser audio' : 'Mute browser audio'\"\n [title]=\"AudioMuted ? 'Unmute browser audio' : 'Mute browser audio'\"\n (click)=\"ToggleAudioMuted()\"\n >\n <i class=\"fa-solid\" [class.fa-volume-high]=\"!AudioMuted\" [class.fa-volume-xmark]=\"AudioMuted\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n <div class=\"rb-viewport\">\n @if (Streaming) {\n <div class=\"rb-canvas-stack\" [class.rb-hidden]=\"!HasFrame\">\n <canvas #frameCanvas class=\"rb-screenshot\" [class.rb-screenshot--interactive]=\"CanTakeOver\"></canvas>\n @if (CanTakeOver) {\n <!-- Synthetic cursor overlay: same internal resolution + CSS box as the frame canvas, so a\n point drawn at viewport coords aligns exactly. CDP screencast frames don't include the OS\n cursor, so we render the user's pointer locally for immediate, round-trip-free feedback. -->\n <canvas #cursorCanvas class=\"rb-screenshot rb-cursor-overlay\"></canvas>\n }\n </div>\n @if (!HasFrame) {\n <div class=\"rb-placeholder\">\n <mj-loading text=\"Waiting for the browser…\" size=\"medium\"></mj-loading>\n </div>\n }\n } @else if (ScreenshotDataUrl) {\n <img class=\"rb-screenshot\" [src]=\"ScreenshotDataUrl\" alt=\"Live view of the shared browser\" />\n } @else {\n <div class=\"rb-placeholder\">\n <mj-loading text=\"Waiting for the browser…\" size=\"medium\"></mj-loading>\n </div>\n }\n </div>\n </div>\n `,\n styles: [`\n :host { display: block; height: 100%; }\n .rb-surface {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n }\n .rb-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n }\n .rb-live {\n width: 8px;\n height: 8px;\n border-radius: var(--mj-radius-full, 50%);\n background: var(--mj-text-disabled);\n flex: 0 0 auto;\n }\n .rb-live--on {\n background: var(--mj-status-success);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-status-success) 25%, transparent);\n }\n .rb-live-label {\n flex: 0 0 auto;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n .rb-url {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--mj-font-mono, monospace);\n }\n .rb-viewport {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: auto;\n padding: 12px;\n }\n .rb-screenshot {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 6px);\n box-shadow: var(--mj-shadow-sm, 0 1px 3px color-mix(in srgb, var(--mj-text-primary) 12%, transparent));\n background: var(--mj-bg-surface);\n }\n .rb-screenshot--interactive {\n /* Hide the OS cursor — we render a synthetic cursor on the overlay so there's exactly one pointer\n and it reads naturally for clicking on a web page (not the old crosshair). */\n cursor: none;\n outline: none;\n }\n .rb-screenshot--interactive:focus-visible {\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n }\n /* Frame canvas + synthetic-cursor overlay share one box; the stack shrinks to the frame canvas so the\n absolutely-positioned overlay (inset:0, same object-fit) lines up pixel-for-pixel with it. */\n .rb-canvas-stack {\n position: relative;\n display: inline-flex;\n max-width: 100%;\n max-height: 100%;\n min-height: 0;\n }\n .rb-cursor-overlay {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n /* Transparent passthrough layer — no chrome of its own, and never steals pointer/wheel events from\n the frame canvas underneath (those listeners drive takeover). */\n border: none;\n box-shadow: none;\n background: transparent;\n pointer-events: none;\n }\n .rb-driving {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 2px 8px;\n border-radius: var(--mj-radius-full, 999px);\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n }\n .rb-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .rb-hidden {\n display: none;\n }\n .rb-speaker {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 6px);\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n cursor: pointer;\n font-size: 0.8125rem;\n transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;\n }\n .rb-speaker:hover {\n background: var(--mj-bg-surface-hover);\n }\n .rb-speaker:focus-visible {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n }\n .rb-speaker--muted {\n color: var(--mj-text-muted);\n }\n `]\n})\nexport class RemoteBrowserSurfaceComponent implements OnInit, OnDestroy {\n /** Snapshot fetcher supplied by the channel plugin (closes over the session id + provider). */\n @Input() Fetch: RemoteBrowserSnapshotFetcher | null = null;\n\n /**\n * Selection fetcher for copy-out, supplied by the channel plugin (closes over the session id + provider).\n * On a local `copy` / Cmd+C over the live canvas the surface calls this to read the remote page's\n * selection, then writes it to the local clipboard. Best-effort: when unset or it resolves to `''`, the\n * copy is a no-op (the local clipboard is left untouched).\n */\n @Input() FetchSelection: RemoteBrowserSelectionFetcher | null = null;\n\n /**\n * Whether the server is PUSHING live screencast frames for this session (the backend advertised the\n * `ScreenStreaming` capability and the start succeeded). When `true` the surface paints pushed frames\n * onto its `<canvas>` via {@link RenderFrame} and does NOT poll; when `false` it uses the snapshot\n * `<img>` poll fallback. Set by the channel plugin in `BindSurface` from the start-screencast result.\n *\n * The start-screencast result is async, so this may flip to `true` AFTER `ngOnInit` has already begun\n * polling — the setter tears the poll down in that case so the two render paths never run at once.\n */\n @Input()\n set Streaming(value: boolean) {\n if (value === this._streaming) {\n return;\n }\n this._streaming = value;\n if (value) {\n // Late flip after the poll-fallback already started: stop polling, switch to canvas frames.\n this.stopPolling();\n this.zone.run(() => this.cdr.markForCheck());\n }\n // Takeover only attaches on the canvas path — (de)attach as streaming flips.\n this.syncTakeoverListeners();\n }\n get Streaming(): boolean {\n return this._streaming;\n }\n private _streaming = false;\n\n /**\n * Whether HUMAN TAKEOVER is enabled — the user watching the live view can click/type into the page and\n * those events are relayed into the server browser (Collaborative control). Takeover only attaches to the\n * canvas render path (pushed screencast); the `<img>` poll fallback stays view-only. When enabled while\n * already streaming the setter attaches listeners; flipping it off (or streaming off) detaches them.\n */\n @Input()\n set Interactive(value: boolean) {\n if (value === this._interactive) {\n return;\n }\n this._interactive = value;\n this.syncTakeoverListeners();\n this.zone.run(() => this.cdr.markForCheck());\n }\n get Interactive(): boolean {\n return this._interactive;\n }\n private _interactive = false;\n\n /** Emits each human-takeover input (pointer move/click, key) the user performs on the live canvas. */\n @Output() HumanInput = new EventEmitter<RemoteBrowserHumanInputEvent>();\n\n /**\n * Whether the live tab-audio stream is available — the channel sets this `true` when the server confirms\n * it is pushing audio. Drives whether the speaker toggle renders in the live-view bar.\n */\n @Input() AudioAvailable = false;\n\n /**\n * Whether tab audio is currently muted. Two-way: the channel sets the initial value (un-muted when audio\n * starts) and the toggle updates it; {@link AudioMutedChange} relays each user change to the channel,\n * which mutes/unmutes the player.\n */\n @Input() AudioMuted = false;\n\n /** Emits the new muted state each time the user toggles the speaker (two-way `AudioMuted`). */\n @Output() AudioMutedChange = new EventEmitter<boolean>();\n\n /** Toggles the speaker mute state and relays it to the channel (which mutes/unmutes the audio player). */\n public ToggleAudioMuted(): void {\n this.AudioMuted = !this.AudioMuted;\n this.AudioMutedChange.emit(this.AudioMuted);\n }\n\n /** True when takeover is both enabled AND on the canvas path — drives the cursor + \"driving\" pill. */\n public get CanTakeOver(): boolean {\n return this._interactive && this._streaming;\n }\n\n /**\n * True while the user is actively driving the remote browser with the keyboard — the takeover canvas\n * holds focus. The host overlay reads this to stand down its own global keyboard shortcuts (e.g.\n * T-to-type opening the local composer) so keystrokes go to the remote page, not the local input.\n */\n public get IsCapturingInput(): boolean {\n return this.surfaceFocused && this.CanTakeOver;\n }\n\n /** The current screenshot as a `data:` URL, or `null` before the first snapshot arrives. */\n public ScreenshotDataUrl: string | null = null;\n /** The current page URL reported by the server, or `null` when none. */\n public CurrentUrl: string | null = null;\n /** Whether at least one pushed screencast frame has been painted (drives the canvas placeholder). */\n public HasFrame = false;\n\n /** The canvas the pushed screencast frames are painted onto (present only while {@link Streaming}). */\n @ViewChild('frameCanvas') private frameCanvas?: ElementRef<HTMLCanvasElement>;\n\n /** The transparent overlay the synthetic cursor is drawn onto (present only while {@link CanTakeOver}). */\n @ViewChild('cursorCanvas') private cursorCanvas?: ElementRef<HTMLCanvasElement>;\n\n /** Whether at least one screenshot OR frame has been rendered (drives the \"Live\" indicator). */\n public get HasSnapshot(): boolean {\n return this.ScreenshotDataUrl !== null || this.HasFrame;\n }\n\n private readonly cdr = inject(ChangeDetectorRef);\n private readonly zone = inject(NgZone);\n /** Active poll timer; `null` when polling is stopped. */\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n /** Guards against overlapping polls when a request runs longer than the interval. */\n private polling = false;\n /** Set on destroy so an in-flight poll's late resolution doesn't touch a torn-down view. */\n private destroyed = false;\n /** Reused decode target for pushed frames — avoids allocating an `Image` per frame. */\n private readonly frameImage = new Image();\n /** The most recent un-painted frame data URL, drained on the next animation frame (drop-old coalescing). */\n private pendingFrameDataUrl: string | null = null;\n /** True while a paint is scheduled, so rapid frames coalesce to one `requestAnimationFrame`. */\n private framePaintScheduled = false;\n\n /** The canvas element takeover listeners are currently bound to, or `null` when detached. */\n private takeoverCanvas: HTMLCanvasElement | null = null;\n /** Timestamp (ms) of the last forwarded pointer-move, for throttling the high-frequency stream. */\n private lastPointerMoveAt = 0;\n /** Timestamp (ms) of the last forwarded scroll, for throttling the high-frequency wheel stream. */\n private lastScrollAt = 0;\n /** Horizontal wheel delta accumulated since the last forwarded scroll (coalesced across throttled events). */\n private pendingScrollDeltaX = 0;\n /** Vertical wheel delta accumulated since the last forwarded scroll (coalesced across throttled events). */\n private pendingScrollDeltaY = 0;\n\n /** Last canvas-relative mouse position in VIEWPORT pixel space, or `null` when the pointer is off-canvas. */\n private cursorViewportPoint: { x: number; y: number } | null = null;\n /** True while a synthetic-cursor repaint is queued, so rapid moves coalesce to one `requestAnimationFrame`. */\n private cursorPaintScheduled = false;\n\n /**\n * True while the user is mid-drag on the canvas (a `mousedown` not yet released) — drives forwarding\n * intermediate moves as a drag and emitting the closing `pointer-up`. Click-drag text selection relies on\n * this so the drag relays as down → moves → up rather than a single discrete click.\n */\n private dragging = false;\n\n /**\n * True when the most recent `mousedown`→`mouseup` actually MOVED (a real drag), so the synthetic `click`\n * the browser fires right after should be suppressed — the drag's down/move/up already conveyed the intent,\n * and a trailing click would collapse a just-made text selection.\n */\n private suppressNextClick = false;\n\n /** Viewport point of the active drag's mousedown, used to tell a real drag from a stationary click. */\n private dragStartPoint: { x: number; y: number } | null = null;\n\n /**\n * True while the takeover canvas currently HOLDS keyboard focus. The host overlay reads this (via\n * {@link IsCapturingInput}) to suppress its own global key shortcuts (e.g. T-to-type) so the user's\n * keystrokes land in the remote browser, not the local composer — fixing the \"greedy textbox\" focus theft.\n */\n private surfaceFocused = false;\n\n /** Bound handlers — stored so the exact same references can be removed on detach. */\n private readonly onCanvasMouseMove = (e: MouseEvent): void => this.handlePointerMove(e);\n private readonly onCanvasMouseDown = (e: MouseEvent): void => this.handlePointerDown(e);\n private readonly onCanvasMouseUp = (e: MouseEvent): void => this.handlePointerUp(e);\n private readonly onCanvasClick = (e: MouseEvent): void => this.handlePointerClick(e);\n private readonly onCanvasKeyDown = (e: KeyboardEvent): void => this.handleKeyDown(e);\n private readonly onCanvasPaste = (e: ClipboardEvent): void => this.handlePaste(e);\n private readonly onCanvasCopy = (e: ClipboardEvent): void => this.handleCopy(e);\n private readonly onCanvasMouseLeave = (): void => this.handleMouseLeave();\n private readonly onCanvasWheel = (e: WheelEvent): void => this.handleWheel(e);\n private readonly onCanvasFocus = (): void => { this.surfaceFocused = true; };\n private readonly onCanvasBlur = (): void => { this.surfaceFocused = false; };\n\n ngOnInit(): void {\n // In streaming mode the server pushes frames — never start the poll (it would be redundant traffic).\n if (this.Streaming) {\n return;\n }\n void this.pollOnce();\n this.startPolling();\n }\n\n ngOnDestroy(): void {\n this.destroyed = true;\n this.stopPolling();\n this.detachTakeoverListeners();\n }\n\n /** Starts the interval poll OUTSIDE Angular's zone so it doesn't trigger CD on every tick. */\n private startPolling(): void {\n if (this.pollTimer !== null) {\n return;\n }\n this.zone.runOutsideAngular(() => {\n this.pollTimer = setInterval(() => void this.pollOnce(), SNAPSHOT_POLL_MS);\n });\n }\n\n /** Stops the interval poll (no further snapshot requests are issued). */\n private stopPolling(): void {\n if (this.pollTimer !== null) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Fetches one snapshot and applies it to the view. Best-effort: the fetcher resolves to\n * `null` on failure (the prior frame stays on screen) so a single failed poll never breaks\n * the live view. Skips when a prior poll is still in flight, the fetcher isn't wired, or\n * after destroy.\n */\n private async pollOnce(): Promise<void> {\n if (this.polling || this.destroyed || !this.Fetch) {\n return;\n }\n this.polling = true;\n try {\n const snapshot = await this.Fetch();\n this.applySnapshot(snapshot);\n } catch {\n // Defensive — the fetcher is contractually non-throwing, but never let a poll break the view.\n } finally {\n this.polling = false;\n }\n }\n\n /** Applies a fetched snapshot to the view fields and triggers OnPush change detection. */\n private applySnapshot(snapshot: RemoteBrowserSnapshotView | null): void {\n if (this.destroyed) {\n return;\n }\n const base64 = snapshot?.ScreenshotBase64 ?? null;\n const nextUrl = snapshot?.CurrentUrl ?? null;\n const nextDataUrl = base64 ? `data:image/png;base64,${base64}` : this.ScreenshotDataUrl;\n if (nextDataUrl === this.ScreenshotDataUrl && nextUrl === this.CurrentUrl) {\n return; // nothing changed — skip the CD pass\n }\n this.ScreenshotDataUrl = nextDataUrl;\n this.CurrentUrl = nextUrl;\n // Re-enter the zone for the OnPush update (the poll runs outside Angular).\n this.zone.run(() => this.cdr.markForCheck());\n }\n\n /**\n * Paints one PUSHED screencast frame (base64 JPEG) onto the canvas. Called by the channel plugin for\n * each frame the server pushes while {@link Streaming}. Coalesces a burst of frames to one paint per\n * animation frame (the newest wins) so a fast stream never floods the main thread, and reuses a single\n * `Image` decode target to avoid per-frame allocation. No-op after destroy or when not in streaming mode.\n *\n * @param dataBase64 The frame image as raw base64 JPEG (no `data:` prefix).\n */\n public RenderFrame(dataBase64: string): void {\n if (this.destroyed || !this.Streaming || !dataBase64) {\n return;\n }\n this.pendingFrameDataUrl = `data:image/jpeg;base64,${dataBase64}`;\n if (this.framePaintScheduled) {\n return; // a paint is already queued — it will pick up this newest frame\n }\n this.framePaintScheduled = true;\n this.zone.runOutsideAngular(() => requestAnimationFrame(() => this.paintPendingFrame()));\n }\n\n /**\n * Updates the URL shown above the live view. In streaming (canvas) mode the snapshot poll is stopped, so\n * the channel pushes the current URL here after a navigation/action reports one — otherwise the bar would\n * stay stuck on \"No page loaded yet\" even though the page is live. No-op for an unchanged / empty value.\n *\n * @param url The current page URL, or null/empty to leave the bar unchanged.\n */\n public SetCurrentUrl(url: string | null | undefined): void {\n if (this.destroyed || !url || url === this.CurrentUrl) {\n return;\n }\n this.CurrentUrl = url;\n this.zone.run(() => this.cdr.markForCheck());\n }\n\n /** Drains the most recent pending frame onto the canvas (drop-old coalescing target). */\n private paintPendingFrame(): void {\n this.framePaintScheduled = false;\n const dataUrl = this.pendingFrameDataUrl;\n this.pendingFrameDataUrl = null;\n if (this.destroyed || !dataUrl) {\n return;\n }\n this.frameImage.onload = () => this.drawFrameImage();\n this.frameImage.src = dataUrl;\n }\n\n /** Draws the decoded frame image onto the canvas, sizing the canvas to the frame on the first paint. */\n private drawFrameImage(): void {\n const canvas = this.frameCanvas?.nativeElement;\n if (this.destroyed || !canvas) {\n return;\n }\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return;\n }\n const { naturalWidth: w, naturalHeight: h } = this.frameImage;\n if (w > 0 && h > 0 && (canvas.width !== w || canvas.height !== h)) {\n canvas.width = w;\n canvas.height = h;\n }\n ctx.drawImage(this.frameImage, 0, 0);\n if (!this.HasFrame) {\n // First frame arrived — re-enter the zone to clear the placeholder + flip the \"Live\" indicator,\n // and attach takeover listeners now that the canvas element exists in the DOM.\n this.zone.run(() => {\n this.HasFrame = true;\n this.cdr.markForCheck();\n });\n this.syncTakeoverListeners();\n }\n }\n\n // ----- human takeover (Collaborative control) --------------------------------------------\n\n /**\n * Attaches or detaches the canvas takeover listeners so they're bound exactly when takeover is live —\n * {@link CanTakeOver} is true AND the canvas element exists. Idempotent: re-binds only when the target\n * canvas changed, detaches when the conditions no longer hold. Safe to call from any state-flip path\n * (the `Interactive`/`Streaming` setters, the first-frame paint, destroy).\n */\n private syncTakeoverListeners(): void {\n const canvas = this.CanTakeOver && !this.destroyed ? (this.frameCanvas?.nativeElement ?? null) : null;\n if (canvas === this.takeoverCanvas) {\n return; // already in the desired state\n }\n this.detachTakeoverListeners();\n if (canvas) {\n this.attachTakeoverListeners(canvas);\n }\n }\n\n /** Binds the pointer/keyboard/wheel listeners to the canvas OUTSIDE the zone (mousemove/wheel are high-frequency). */\n private attachTakeoverListeners(canvas: HTMLCanvasElement): void {\n canvas.tabIndex = 0; // focusable so it can receive keydown\n // Marks this canvas as a keyboard-capturing surface so the host overlay's global shortcuts (e.g.\n // T-to-type) stand down while it holds focus — see the overlay's isKeyboardCapturingSurfaceFocused().\n canvas.setAttribute('data-mj-capture-keys', '');\n this.zone.runOutsideAngular(() => {\n canvas.addEventListener('mousemove', this.onCanvasMouseMove);\n canvas.addEventListener('mousedown', this.onCanvasMouseDown);\n canvas.addEventListener('mouseup', this.onCanvasMouseUp);\n canvas.addEventListener('click', this.onCanvasClick);\n canvas.addEventListener('keydown', this.onCanvasKeyDown);\n // Paste-in / copy-out: the focused canvas receives the local clipboard `paste` / `copy` events; we\n // relay paste text into the remote page and write the remote selection to the local clipboard.\n canvas.addEventListener('paste', this.onCanvasPaste);\n canvas.addEventListener('copy', this.onCanvasCopy);\n canvas.addEventListener('mouseleave', this.onCanvasMouseLeave);\n canvas.addEventListener('focus', this.onCanvasFocus);\n canvas.addEventListener('blur', this.onCanvasBlur);\n // `passive: false` so preventDefault stops the host page from scrolling while the user drives the page.\n canvas.addEventListener('wheel', this.onCanvasWheel, { passive: false });\n });\n this.takeoverCanvas = canvas;\n }\n\n /** Removes the listeners from the currently-bound canvas (no-op when none are bound) and clears the cursor. */\n private detachTakeoverListeners(): void {\n const canvas = this.takeoverCanvas;\n if (!canvas) {\n return;\n }\n canvas.removeEventListener('mousemove', this.onCanvasMouseMove);\n canvas.removeEventListener('mousedown', this.onCanvasMouseDown);\n canvas.removeEventListener('mouseup', this.onCanvasMouseUp);\n canvas.removeEventListener('click', this.onCanvasClick);\n canvas.removeEventListener('keydown', this.onCanvasKeyDown);\n canvas.removeEventListener('paste', this.onCanvasPaste);\n canvas.removeEventListener('copy', this.onCanvasCopy);\n canvas.removeEventListener('mouseleave', this.onCanvasMouseLeave);\n canvas.removeEventListener('focus', this.onCanvasFocus);\n canvas.removeEventListener('blur', this.onCanvasBlur);\n canvas.removeEventListener('wheel', this.onCanvasWheel);\n canvas.removeAttribute('data-mj-capture-keys');\n this.takeoverCanvas = null;\n this.cursorViewportPoint = null;\n this.dragging = false;\n this.suppressNextClick = false;\n this.dragStartPoint = null;\n this.surfaceFocused = false;\n this.clearSyntheticCursor();\n }\n\n /**\n * Pointer-move → tracks the cursor for the synthetic-cursor overlay (every move, immediate, no\n * round-trip) AND emits a throttled viewport-mapped move to the server. Runs outside the zone (no CD\n * per move).\n */\n private handlePointerMove(event: MouseEvent): void {\n const point = this.toViewportCoords(event);\n if (point) {\n // Always update the local cursor for instant feedback — independent of the server-relay throttle.\n this.cursorViewportPoint = point;\n this.scheduleCursorPaint();\n }\n const now = Date.now();\n if (now - this.lastPointerMoveAt < POINTER_MOVE_THROTTLE_MS) {\n return;\n }\n this.lastPointerMoveAt = now;\n if (point) {\n this.emitInput({ kind: 'pointer-move', x: point.x, y: point.y });\n }\n }\n\n /** Mouse leaves the canvas → hide the synthetic cursor (nothing to relay). */\n private handleMouseLeave(): void {\n this.cursorViewportPoint = null;\n this.scheduleCursorPaint();\n }\n\n /**\n * Wheel/trackpad/Magic-Mouse scroll over the canvas → forwards a viewport-mapped scroll to the server.\n * Calls `preventDefault` so the host page doesn't scroll while the user drives the live page. Throttled\n * to {@link SCROLL_THROTTLE_MS}, coalescing the skipped deltas so no scroll distance is lost.\n */\n private handleWheel(event: WheelEvent): void {\n event.preventDefault();\n this.pendingScrollDeltaX += event.deltaX;\n this.pendingScrollDeltaY += event.deltaY;\n const point = this.toViewportCoords(event);\n if (point) {\n this.cursorViewportPoint = point;\n this.scheduleCursorPaint();\n }\n const now = Date.now();\n if (now - this.lastScrollAt < SCROLL_THROTTLE_MS) {\n return; // accumulate into pendingScrollDelta* until the throttle window opens\n }\n this.lastScrollAt = now;\n const target = point ?? this.cursorViewportPoint;\n if (target && (this.pendingScrollDeltaX !== 0 || this.pendingScrollDeltaY !== 0)) {\n this.emitInput({ kind: 'scroll', x: target.x, y: target.y, deltaX: this.pendingScrollDeltaX, deltaY: this.pendingScrollDeltaY });\n this.pendingScrollDeltaX = 0;\n this.pendingScrollDeltaY = 0;\n }\n }\n\n /**\n * Mouse-down on the canvas → focuses the canvas (so subsequent keys are captured) and BEGINS a drag:\n * emits a `pointer-down` and arms drag mode so the following moves relay as drag motion and the matching\n * `pointer-up` closes it. This is what makes click-drag text selection work (rather than a discrete click).\n */\n private handlePointerDown(event: MouseEvent): void {\n this.takeoverCanvas?.focus();\n const point = this.toViewportCoords(event);\n if (!point) {\n return;\n }\n this.dragging = true;\n this.dragStartPoint = point;\n this.suppressNextClick = false;\n this.cursorViewportPoint = point;\n this.scheduleCursorPaint();\n this.emitInput({ kind: 'pointer-down', x: point.x, y: point.y, button: this.mapButton(event.button), modifiers: this.collectModifiers(event) });\n }\n\n /**\n * Mouse-up on the canvas → closes a drag with a `pointer-up` at the release point, disarming drag mode.\n * If the pointer actually moved between down and up, marks the following synthetic `click` to be suppressed\n * (so the drag selection isn't immediately collapsed by a trailing click).\n */\n private handlePointerUp(event: MouseEvent): void {\n if (!this.dragging) {\n return;\n }\n this.dragging = false;\n const point = this.toViewportCoords(event);\n if (point) {\n const start = this.dragStartPoint;\n this.suppressNextClick = !!start && (start.x !== point.x || start.y !== point.y);\n this.emitInput({ kind: 'pointer-up', x: point.x, y: point.y, button: this.mapButton(event.button), modifiers: this.collectModifiers(event) });\n }\n this.dragStartPoint = null;\n }\n\n /**\n * Click → emits a viewport-mapped click WITH any held modifiers (e.g. Shift-click text selection). The\n * server browser receives the press/release of a drag via {@link handlePointerDown}/{@link handlePointerUp};\n * a plain click (down+up with no motion) still fires this `click` event, so a simple click relays as one\n * click action — but we skip emitting it when it terminates a real drag (where down/up already covered it).\n */\n private handlePointerClick(event: MouseEvent): void {\n this.takeoverCanvas?.focus();\n if (this.suppressNextClick) {\n // This click terminates a real drag (selection) — the down/move/up already conveyed it.\n this.suppressNextClick = false;\n return;\n }\n const point = this.toViewportCoords(event);\n if (point) {\n this.emitInput({ kind: 'pointer-click', x: point.x, y: point.y, button: this.mapButton(event.button), modifiers: this.collectModifiers(event) });\n }\n }\n\n /**\n * Keydown → forwards printable keys, a curated set of control keys ({@link FORWARDED_CONTROL_KEYS}), AND\n * any key combined with a Ctrl/Cmd/Alt modifier (so combos like Ctrl/Cmd+A select-all, Cmd+C/Cmd+V relay).\n * Calls `preventDefault` + `stopPropagation` ONLY on forwarded keys, so (a) the host app keeps its own\n * shortcuts on un-forwarded keys and (b) forwarded keys never bubble to the host overlay's global shortcuts\n * (the T-to-type focus-steal). Lone modifier keys are never forwarded — they ride as `modifiers` on the\n * next key/click.\n */\n private handleKeyDown(event: KeyboardEvent): void {\n const key = event.key;\n if (MODIFIER_KEYS.has(key)) {\n return; // a modifier on its own is not a keypress — it rides on the next key/click\n }\n const isPrintable = key.length === 1;\n const hasComboModifier = event.ctrlKey || event.metaKey || event.altKey;\n // Cmd/Ctrl+C and Cmd/Ctrl+V are handled by the dedicated `copy` / `paste` listeners (which read/write the\n // LOCAL clipboard) — don't ALSO relay them as a keypress, or paste would double-fire and copy would send a\n // useless Ctrl+C to the remote page. The browser fires the matching clipboard event for these chords.\n const isClipboardChord = (event.ctrlKey || event.metaKey) && (key === 'c' || key === 'v' || key === 'C' || key === 'V');\n const isForwardable = !isClipboardChord && (isPrintable || FORWARDED_CONTROL_KEYS.has(key) || hasComboModifier);\n if (!isForwardable) {\n return; // leave non-forwarded keys to the host app (and clipboard chords to copy/paste handlers)\n }\n event.preventDefault();\n // Stop the keystroke from reaching document-level handlers (e.g. the overlay's T-to-type) so the user's\n // typing lands in the remote browser, not the local composer.\n event.stopPropagation();\n this.emitInput({ kind: 'key', key, modifiers: this.collectModifiers(event) });\n }\n\n /**\n * Paste-in: a local `paste` (Cmd/Ctrl+V or context-menu paste) over the canvas → reads the LOCAL clipboard\n * text and relays it as a `'text'` human input, which the server inserts into the remote page's focused\n * element (the VNC/remote-desktop paste model — no remote-clipboard sync). `preventDefault` keeps the host\n * app from also acting on the paste; empty/non-text clipboards are a no-op.\n */\n private handlePaste(event: ClipboardEvent): void {\n event.preventDefault();\n event.stopPropagation();\n const text = event.clipboardData?.getData('text') ?? '';\n if (text.length === 0) {\n return; // nothing (or no text) on the clipboard — paste nothing\n }\n this.emitInput({ kind: 'text', text });\n }\n\n /**\n * Copy-out: a local `copy` (Cmd/Ctrl+C or context-menu copy) over the canvas → reads the REMOTE page's\n * current selection via {@link FetchSelection} and writes it to the LOCAL clipboard (the mirror of paste-in).\n * `preventDefault` stops the host app from copying its own empty selection. Best-effort throughout: when the\n * fetcher is unwired, resolves to `''`, or `navigator.clipboard` is unavailable / denies permission, the copy\n * is silently a no-op — it never throws and never breaks the live view.\n */\n private handleCopy(event: ClipboardEvent): void {\n event.preventDefault();\n event.stopPropagation();\n const fetchSelection = this.FetchSelection;\n if (!fetchSelection) {\n return;\n }\n void (async (): Promise<void> => {\n try {\n const text = await fetchSelection();\n if (text.length > 0 && navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n }\n } catch {\n // Best-effort: a denied clipboard permission or a failed selection read leaves the local clipboard\n // untouched rather than surfacing an error during takeover.\n }\n })();\n }\n\n /**\n * Collects the modifier keys currently held during a DOM mouse/keyboard event into the relayed\n * {@link RemoteBrowserModifier} list (empty when none are held).\n *\n * @param event The DOM event whose modifier flags to read.\n * @returns The held modifiers, in a stable order.\n */\n private collectModifiers(event: MouseEvent | KeyboardEvent): RemoteBrowserModifier[] {\n const modifiers: RemoteBrowserModifier[] = [];\n if (event.shiftKey) { modifiers.push('Shift'); }\n if (event.ctrlKey) { modifiers.push('Control'); }\n if (event.altKey) { modifiers.push('Alt'); }\n if (event.metaKey) { modifiers.push('Meta'); }\n return modifiers;\n }\n\n /** Re-enters the zone to emit one human input (so subscribers see it inside Angular). */\n private emitInput(input: RemoteBrowserHumanInputEvent): void {\n this.zone.run(() => this.HumanInput.emit(input));\n }\n\n /**\n * Maps a DOM pointer event on the live canvas to VIEWPORT coordinates via {@link MapToViewportCoords}\n * (the pure, unit-tested mapping). Returns `null` when no canvas is bound or mapping isn't possible yet.\n *\n * @param event The pointer event on the canvas.\n * @returns The integer viewport coordinates, or `null` when mapping isn't possible yet.\n */\n private toViewportCoords(event: MouseEvent): { x: number; y: number } | null {\n const canvas = this.takeoverCanvas;\n if (!canvas) {\n return null;\n }\n return MapToViewportCoords(event.clientX, event.clientY, canvas.getBoundingClientRect(), canvas.width, canvas.height);\n }\n\n // ----- synthetic cursor (local feedback — CDP frames don't include the OS cursor) -----------\n\n /**\n * Schedules a synthetic-cursor repaint on the next animation frame, coalescing a burst of moves to one\n * paint (the newest position wins). Runs outside the zone — drawing the cursor never triggers Angular CD.\n */\n private scheduleCursorPaint(): void {\n if (this.cursorPaintScheduled) {\n return;\n }\n this.cursorPaintScheduled = true;\n this.zone.runOutsideAngular(() => requestAnimationFrame(() => this.paintSyntheticCursor()));\n }\n\n /**\n * Draws (or clears) the synthetic cursor on the overlay canvas at {@link cursorViewportPoint}. The overlay\n * shares the frame canvas's internal resolution (sized here to match), so a point drawn at viewport coords\n * aligns exactly with the page underneath. A small brand-tinted ring with a center dot reads as a pointer\n * without obscuring what it's over. No-op after destroy or when the overlay isn't mounted.\n */\n private paintSyntheticCursor(): void {\n this.cursorPaintScheduled = false;\n const overlay = this.cursorCanvas?.nativeElement;\n const frame = this.frameCanvas?.nativeElement;\n if (this.destroyed || !overlay || !frame) {\n return;\n }\n // Keep the overlay's internal resolution locked to the frame canvas so coordinate spaces match.\n if (overlay.width !== frame.width || overlay.height !== frame.height) {\n overlay.width = frame.width;\n overlay.height = frame.height;\n }\n const ctx = overlay.getContext('2d');\n if (!ctx) {\n return;\n }\n ctx.clearRect(0, 0, overlay.width, overlay.height);\n const point = this.cursorViewportPoint;\n if (!point) {\n return; // pointer left the canvas — leave the overlay cleared\n }\n const ring = this.resolveCssColor('--mj-brand-primary', '#264FAF');\n ctx.beginPath();\n ctx.arc(point.x, point.y, SYNTHETIC_CURSOR_RADIUS, 0, Math.PI * 2);\n ctx.lineWidth = 2;\n ctx.strokeStyle = ring;\n ctx.stroke();\n ctx.beginPath();\n ctx.arc(point.x, point.y, 1.5, 0, Math.PI * 2);\n ctx.fillStyle = ring;\n ctx.fill();\n }\n\n /** Clears the synthetic-cursor overlay (e.g. on detach). No-op when the overlay isn't mounted. */\n private clearSyntheticCursor(): void {\n const overlay = this.cursorCanvas?.nativeElement;\n const ctx = overlay?.getContext('2d');\n if (overlay && ctx) {\n ctx.clearRect(0, 0, overlay.width, overlay.height);\n }\n }\n\n /**\n * Resolves a CSS custom property (design token) to its computed color value off the host element, so the\n * canvas-drawn cursor honors theming. Falls back to the supplied default when the token is unset or the\n * host isn't measurable.\n *\n * @param token The CSS custom property name (e.g. `'--mj-brand-primary'`).\n * @param fallback The color to use when the token resolves empty.\n * @returns The resolved color string.\n */\n private resolveCssColor(token: string, fallback: string): string {\n const host = this.frameCanvas?.nativeElement;\n if (!host) {\n return fallback;\n }\n const value = getComputedStyle(host).getPropertyValue(token).trim();\n return value.length > 0 ? value : fallback;\n }\n\n /** Maps a DOM `MouseEvent.button` (0/1/2) to the relayed button union, defaulting to `'left'`. */\n private mapButton(button: number): 'left' | 'middle' | 'right' {\n if (button === 1) {\n return 'middle';\n }\n if (button === 2) {\n return 'right';\n }\n return 'left';\n }\n}\n"]}
|
|
@@ -19,6 +19,7 @@ export declare const REMOTE_BROWSER_TOOL_NAMES: {
|
|
|
19
19
|
readonly GetPageText: "browser_GetPageText";
|
|
20
20
|
readonly DescribePage: "browser_DescribePage";
|
|
21
21
|
readonly LocateElement: "browser_LocateElement";
|
|
22
|
+
readonly AchieveGoal: "browser_AchieveGoal";
|
|
22
23
|
};
|
|
23
24
|
/**
|
|
24
25
|
* The discriminated `kind` the server's `ExecuteRemoteBrowserAction` mutation switches on.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-browser-tools.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,aAAa,CAAC;AAErD,gFAAgF;AAChF,eAAO,MAAM,yBAAyB
|
|
1
|
+
{"version":3,"file":"remote-browser-tools.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,aAAa,CAAC;AAErD,gFAAgF;AAChF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;CAa5B,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,aAAa,CAAC;AAErI;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,IAAI,EAAE,uBAAuB,CAAC;IAC9B,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,mFAAmF;AACnF,KAAK,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,YAAY,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,CAAC;AAYxG;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;CAAG;AAEvD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,mBAAmB,CAsDzG;AAED;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,EAAE,sBAAsB,EAqHnE,CAAC"}
|
|
@@ -18,6 +18,7 @@ export const REMOTE_BROWSER_TOOL_NAMES = {
|
|
|
18
18
|
GetPageText: 'browser_GetPageText',
|
|
19
19
|
DescribePage: 'browser_DescribePage',
|
|
20
20
|
LocateElement: 'browser_LocateElement',
|
|
21
|
+
AchieveGoal: 'browser_AchieveGoal',
|
|
21
22
|
};
|
|
22
23
|
/** Coerces a parsed arg to a non-empty string, or `undefined` when absent / wrong-typed. */
|
|
23
24
|
function asString(value) {
|
|
@@ -206,5 +207,17 @@ export const REMOTE_BROWSER_TOOL_DEFINITIONS = [
|
|
|
206
207
|
required: ['description'],
|
|
207
208
|
},
|
|
208
209
|
},
|
|
210
|
+
{
|
|
211
|
+
Name: REMOTE_BROWSER_TOOL_NAMES.AchieveGoal,
|
|
212
|
+
Description: 'Hand a high-level GOAL to the autonomous browser agent (a stronger vision/action model) and let it plan and execute all the steps itself — e.g. "log in and open the latest invoice", "search for X and add the first result to the cart". Prefer this over issuing many granular browser_* steps when the task is multi-step. Returns when the goal is done (or could not be completed).',
|
|
213
|
+
ParametersSchema: {
|
|
214
|
+
type: 'object',
|
|
215
|
+
properties: {
|
|
216
|
+
goal: { type: 'string', description: 'The natural-language goal to accomplish in the shared browser.' },
|
|
217
|
+
startUrl: { type: 'string', description: 'Optional absolute URL to open before starting.' },
|
|
218
|
+
},
|
|
219
|
+
required: ['goal'],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
209
222
|
];
|
|
210
223
|
//# sourceMappingURL=remote-browser-tools.js.map
|