@memberjunction/ng-conversations 5.40.2 → 5.41.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/README.md +57 -0
- package/dist/__tests__/channel-optional-surface.test.d.ts +2 -0
- package/dist/__tests__/channel-optional-surface.test.d.ts.map +1 -0
- package/dist/__tests__/channel-optional-surface.test.js +53 -0
- package/dist/__tests__/channel-optional-surface.test.js.map +1 -0
- package/dist/__tests__/chat-events.test.d.ts +14 -0
- package/dist/__tests__/chat-events.test.d.ts.map +1 -0
- package/dist/__tests__/chat-events.test.js +109 -0
- package/dist/__tests__/chat-events.test.js.map +1 -0
- package/dist/__tests__/conversation-naming.test.d.ts +2 -0
- package/dist/__tests__/conversation-naming.test.d.ts.map +1 -0
- package/dist/__tests__/conversation-naming.test.js +110 -0
- package/dist/__tests__/conversation-naming.test.js.map +1 -0
- package/dist/__tests__/delegation-result-parser.test.d.ts +2 -0
- package/dist/__tests__/delegation-result-parser.test.d.ts.map +1 -0
- package/dist/__tests__/delegation-result-parser.test.js +107 -0
- package/dist/__tests__/delegation-result-parser.test.js.map +1 -0
- package/dist/__tests__/event-wiring.test.d.ts +15 -0
- package/dist/__tests__/event-wiring.test.d.ts.map +1 -0
- package/dist/__tests__/event-wiring.test.js +100 -0
- package/dist/__tests__/event-wiring.test.js.map +1 -0
- package/dist/__tests__/narration-template.test.d.ts +2 -0
- package/dist/__tests__/narration-template.test.d.ts.map +1 -0
- package/dist/__tests__/narration-template.test.js +76 -0
- package/dist/__tests__/narration-template.test.js.map +1 -0
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts +2 -0
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-agent-picker-models.test.js +49 -0
- package/dist/__tests__/realtime-agent-picker-models.test.js.map +1 -0
- package/dist/__tests__/realtime-audio-visuals.test.d.ts +2 -0
- package/dist/__tests__/realtime-audio-visuals.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-audio-visuals.test.js +123 -0
- package/dist/__tests__/realtime-audio-visuals.test.js.map +1 -0
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts +2 -0
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-delegation-card-cancel.test.js +48 -0
- package/dist/__tests__/realtime-delegation-card-cancel.test.js.map +1 -0
- package/dist/__tests__/realtime-disclosure.test.d.ts +2 -0
- package/dist/__tests__/realtime-disclosure.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-disclosure.test.js +164 -0
- package/dist/__tests__/realtime-disclosure.test.js.map +1 -0
- package/dist/__tests__/realtime-pairing.test.d.ts +2 -0
- package/dist/__tests__/realtime-pairing.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-pairing.test.js +207 -0
- package/dist/__tests__/realtime-pairing.test.js.map +1 -0
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts +2 -0
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-review-lifecycle.test.js +154 -0
- package/dist/__tests__/realtime-review-lifecycle.test.js.map +1 -0
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-cancel-usage.test.js +230 -0
- package/dist/__tests__/realtime-session-cancel-usage.test.js.map +1 -0
- package/dist/__tests__/realtime-session-channels.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-channels.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-channels.test.js +252 -0
- package/dist/__tests__/realtime-session-channels.test.js.map +1 -0
- package/dist/__tests__/realtime-session-client-tools.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-client-tools.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-client-tools.test.js +103 -0
- package/dist/__tests__/realtime-session-client-tools.test.js.map +1 -0
- package/dist/__tests__/realtime-session-minimized.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-minimized.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-minimized.test.js +32 -0
- package/dist/__tests__/realtime-session-minimized.test.js.map +1 -0
- package/dist/__tests__/realtime-session-mint.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-mint.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-mint.test.js +69 -0
- package/dist/__tests__/realtime-session-mint.test.js.map +1 -0
- package/dist/__tests__/realtime-session-policy.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-policy.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-policy.test.js +303 -0
- package/dist/__tests__/realtime-session-policy.test.js.map +1 -0
- package/dist/__tests__/realtime-session-review.service.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-review.service.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-review.service.test.js +743 -0
- package/dist/__tests__/realtime-session-review.service.test.js.map +1 -0
- package/dist/__tests__/realtime-session-state.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-state.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-state.test.js +83 -0
- package/dist/__tests__/realtime-session-state.test.js.map +1 -0
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-timeline-card.test.js +106 -0
- package/dist/__tests__/realtime-session-timeline-card.test.js.map +1 -0
- package/dist/__tests__/realtime-session-timeline.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-timeline.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-timeline.test.js +142 -0
- package/dist/__tests__/realtime-session-timeline.test.js.map +1 -0
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts +19 -0
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-sessions-adapter.test.js +188 -0
- package/dist/__tests__/realtime-sessions-adapter.test.js.map +1 -0
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts +2 -0
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-surface-panel-prefs.test.js +100 -0
- package/dist/__tests__/realtime-surface-panel-prefs.test.js.map +1 -0
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts +2 -0
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-surface-tabs-model.test.js +193 -0
- package/dist/__tests__/realtime-surface-tabs-model.test.js.map +1 -0
- package/dist/__tests__/remote-browser-audio-player.test.d.ts +2 -0
- package/dist/__tests__/remote-browser-audio-player.test.d.ts.map +1 -0
- package/dist/__tests__/remote-browser-audio-player.test.js +137 -0
- package/dist/__tests__/remote-browser-audio-player.test.js.map +1 -0
- package/dist/__tests__/remote-browser-channel.test.d.ts +2 -0
- package/dist/__tests__/remote-browser-channel.test.d.ts.map +1 -0
- package/dist/__tests__/remote-browser-channel.test.js +423 -0
- package/dist/__tests__/remote-browser-channel.test.js.map +1 -0
- package/dist/__tests__/slot-defaults.test.d.ts +24 -0
- package/dist/__tests__/slot-defaults.test.d.ts.map +1 -0
- package/dist/__tests__/slot-defaults.test.js +63 -0
- package/dist/__tests__/slot-defaults.test.js.map +1 -0
- package/dist/__tests__/user-authorization.test.d.ts +2 -0
- package/dist/__tests__/user-authorization.test.d.ts.map +1 -0
- package/dist/__tests__/user-authorization.test.js +97 -0
- package/dist/__tests__/user-authorization.test.js.map +1 -0
- package/dist/__tests__/voice-session-narration.test.d.ts +2 -0
- package/dist/__tests__/voice-session-narration.test.d.ts.map +1 -0
- package/dist/__tests__/voice-session-narration.test.js +609 -0
- package/dist/__tests__/voice-session-narration.test.js.map +1 -0
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts +2 -0
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts.map +1 -0
- package/dist/__tests__/whiteboard-artifact-viewer.test.js +101 -0
- package/dist/__tests__/whiteboard-artifact-viewer.test.js.map +1 -0
- package/dist/__tests__/whiteboard-channel.test.d.ts +2 -0
- package/dist/__tests__/whiteboard-channel.test.d.ts.map +1 -0
- package/dist/__tests__/whiteboard-channel.test.js +260 -0
- package/dist/__tests__/whiteboard-channel.test.js.map +1 -0
- package/dist/__tests__/whiteboard-restore-state.test.d.ts +2 -0
- package/dist/__tests__/whiteboard-restore-state.test.d.ts.map +1 -0
- package/dist/__tests__/whiteboard-restore-state.test.js +108 -0
- package/dist/__tests__/whiteboard-restore-state.test.js.map +1 -0
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +205 -3
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +911 -342
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/mention/mention-dropdown.component.js +35 -17
- package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.d.ts +4 -0
- package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.js +43 -19
- package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
- package/dist/lib/components/message/message-input-box.component.d.ts +17 -1
- package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input-box.component.js +73 -15
- package/dist/lib/components/message/message-input-box.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +142 -6
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +328 -82
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +28 -3
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +180 -108
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +81 -2
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +252 -87
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +282 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +158 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -0
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts +25 -0
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js +140 -0
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js.map +1 -0
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts +35 -0
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js +58 -0
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +63 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.js +260 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +117 -0
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-banner.component.js +504 -0
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +168 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.js +556 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts +97 -0
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-audio-visuals.js +139 -0
- package/dist/lib/components/realtime/realtime-audio-visuals.js.map +1 -0
- package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts +29 -0
- package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-channel-strip.component.js +69 -0
- package/dist/lib/components/realtime/realtime-channel-strip.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-composer.component.d.ts +65 -0
- package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-composer.component.js +256 -0
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +71 -0
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-delegation-card.component.js +324 -0
- package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-disclosure.d.ts +135 -0
- package/dist/lib/components/realtime/realtime-disclosure.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-disclosure.js +188 -0
- package/dist/lib/components/realtime/realtime-disclosure.js.map +1 -0
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +491 -0
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +1274 -0
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-session-state.d.ts +191 -0
- package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-session-state.js +244 -0
- package/dist/lib/components/realtime/realtime-session-state.js.map +1 -0
- package/dist/lib/components/realtime/realtime-session-thread.component.d.ts +56 -0
- package/dist/lib/components/realtime/realtime-session-thread.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-session-thread.component.js +246 -0
- package/dist/lib/components/realtime/realtime-session-thread.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts +51 -0
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.js +193 -0
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts +77 -0
- package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-panel-prefs.js +114 -0
- package/dist/lib/components/realtime/realtime-surface-panel-prefs.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +173 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js +496 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +181 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js +223 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts +163 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js +309 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +168 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +524 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts +346 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js +851 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts +86 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js +210 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts +48 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js +180 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +119 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +274 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts +11 -0
- package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js +98 -0
- package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts +9 -0
- package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js +35 -0
- package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts +28 -0
- package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-empty-state-default.component.js +104 -0
- package/dist/lib/components/slots/mj-chat-empty-state-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-header-default.component.d.ts +11 -0
- package/dist/lib/components/slots/mj-chat-header-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-header-default.component.js +103 -0
- package/dist/lib/components/slots/mj-chat-header-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts +15 -0
- package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js +73 -0
- package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts +9 -0
- package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-message-extra-default.component.js +34 -0
- package/dist/lib/components/slots/mj-chat-message-extra-default.component.js.map +1 -0
- package/dist/lib/components/slots/slot-interfaces.d.ts +95 -0
- package/dist/lib/components/slots/slot-interfaces.d.ts.map +1 -0
- package/dist/lib/components/slots/slot-interfaces.js +18 -0
- package/dist/lib/components/slots/slot-interfaces.js.map +1 -0
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +11 -0
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +28 -4
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +12 -1
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +93 -5
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/directives/chat-slot.directive.d.ts +44 -0
- package/dist/lib/directives/chat-slot.directive.d.ts.map +1 -0
- package/dist/lib/directives/chat-slot.directive.js +54 -0
- package/dist/lib/directives/chat-slot.directive.js.map +1 -0
- package/dist/lib/events/chat-events.d.ts +137 -0
- package/dist/lib/events/chat-events.d.ts.map +1 -0
- package/dist/lib/events/chat-events.js +189 -0
- package/dist/lib/events/chat-events.js.map +1 -0
- package/dist/lib/models/conversation-state.model.d.ts +2 -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.map +1 -1
- package/dist/lib/services/artifact-state.service.js +23 -6
- package/dist/lib/services/artifact-state.service.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +60 -74
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +100 -313
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-bridge.service.d.ts +11 -70
- package/dist/lib/services/conversation-bridge.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-bridge.service.js +51 -85
- package/dist/lib/services/conversation-bridge.service.js.map +1 -1
- package/dist/lib/services/conversation-naming.d.ts +63 -0
- package/dist/lib/services/conversation-naming.d.ts.map +1 -0
- package/dist/lib/services/conversation-naming.js +58 -0
- package/dist/lib/services/conversation-naming.js.map +1 -0
- package/dist/lib/services/conversation-streaming.service.d.ts +24 -154
- package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-streaming.service.js +39 -361
- package/dist/lib/services/conversation-streaming.service.js.map +1 -1
- package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts +10 -0
- package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts.map +1 -0
- package/dist/lib/services/conversations-runtime-bootstrap.service.js +104 -0
- package/dist/lib/services/conversations-runtime-bootstrap.service.js.map +1 -0
- package/dist/lib/services/delegation-result-parser.d.ts +45 -0
- package/dist/lib/services/delegation-result-parser.d.ts.map +1 -0
- package/dist/lib/services/delegation-result-parser.js +48 -0
- package/dist/lib/services/delegation-result-parser.js.map +1 -0
- package/dist/lib/services/mention-autocomplete.service.d.ts +19 -4
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +65 -4
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/mention-parser.service.d.ts +8 -53
- package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
- package/dist/lib/services/mention-parser.service.js +32 -243
- package/dist/lib/services/mention-parser.service.js.map +1 -1
- package/dist/lib/services/narration-template.d.ts +42 -0
- package/dist/lib/services/narration-template.d.ts.map +1 -0
- package/dist/lib/services/narration-template.js +73 -0
- package/dist/lib/services/narration-template.js.map +1 -0
- package/dist/lib/services/realtime-pairing.d.ts +120 -0
- package/dist/lib/services/realtime-pairing.d.ts.map +1 -0
- package/dist/lib/services/realtime-pairing.js +150 -0
- package/dist/lib/services/realtime-pairing.js.map +1 -0
- package/dist/lib/services/realtime-session-review.service.d.ts +233 -0
- package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -0
- package/dist/lib/services/realtime-session-review.service.js +417 -0
- package/dist/lib/services/realtime-session-review.service.js.map +1 -0
- package/dist/lib/services/realtime-session.service.d.ts +739 -0
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -0
- package/dist/lib/services/realtime-session.service.js +1647 -0
- package/dist/lib/services/realtime-session.service.js.map +1 -0
- package/dist/lib/services/realtime-sessions-adapter.d.ts +54 -0
- package/dist/lib/services/realtime-sessions-adapter.d.ts.map +1 -0
- package/dist/lib/services/realtime-sessions-adapter.js +154 -0
- package/dist/lib/services/realtime-sessions-adapter.js.map +1 -0
- package/dist/lib/services/user-authorization.d.ts +67 -0
- package/dist/lib/services/user-authorization.d.ts.map +1 -0
- package/dist/lib/services/user-authorization.js +66 -0
- package/dist/lib/services/user-authorization.js.map +1 -0
- package/dist/lib/utils/realtime-session-timeline.d.ts +84 -0
- package/dist/lib/utils/realtime-session-timeline.d.ts.map +1 -0
- package/dist/lib/utils/realtime-session-timeline.js +94 -0
- package/dist/lib/utils/realtime-session-timeline.js.map +1 -0
- package/dist/public-api.d.ts +41 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +50 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +27 -24
- package/dist/__tests__/conversation-bridge.service.test.d.ts +0 -2
- package/dist/__tests__/conversation-bridge.service.test.d.ts.map +0 -1
- package/dist/__tests__/conversation-bridge.service.test.js +0 -98
- package/dist/__tests__/conversation-bridge.service.test.js.map +0 -1
- package/dist/__tests__/mention-parser.test.d.ts +0 -2
- package/dist/__tests__/mention-parser.test.d.ts.map +0 -1
- package/dist/__tests__/mention-parser.test.js +0 -154
- package/dist/__tests__/mention-parser.test.js.map +0 -1
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, NgZone, Output, ViewChild, inject } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
import * as i1 from "@memberjunction/ng-shared-generic";
|
|
6
|
+
const _c0 = ["frameCanvas"];
|
|
7
|
+
const _c1 = ["cursorCanvas"];
|
|
8
|
+
function RemoteBrowserSurfaceComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
9
|
+
i0.ɵɵelementStart(0, "span", 7);
|
|
10
|
+
i0.ɵɵelement(1, "i", 12);
|
|
11
|
+
i0.ɵɵtext(2, " You're driving ");
|
|
12
|
+
i0.ɵɵelementEnd();
|
|
13
|
+
} }
|
|
14
|
+
function RemoteBrowserSurfaceComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
15
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
16
|
+
i0.ɵɵelementStart(0, "button", 13);
|
|
17
|
+
i0.ɵɵlistener("click", function RemoteBrowserSurfaceComponent_Conditional_8_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ToggleAudioMuted()); });
|
|
18
|
+
i0.ɵɵelement(1, "i", 14);
|
|
19
|
+
i0.ɵɵelementEnd();
|
|
20
|
+
} if (rf & 2) {
|
|
21
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
22
|
+
i0.ɵɵclassProp("rb-speaker--muted", ctx_r1.AudioMuted);
|
|
23
|
+
i0.ɵɵproperty("title", ctx_r1.AudioMuted ? "Unmute browser audio" : "Mute browser audio");
|
|
24
|
+
i0.ɵɵattribute("aria-pressed", !ctx_r1.AudioMuted)("aria-label", ctx_r1.AudioMuted ? "Unmute browser audio" : "Mute browser audio");
|
|
25
|
+
i0.ɵɵadvance();
|
|
26
|
+
i0.ɵɵclassProp("fa-volume-high", !ctx_r1.AudioMuted)("fa-volume-xmark", ctx_r1.AudioMuted);
|
|
27
|
+
} }
|
|
28
|
+
function RemoteBrowserSurfaceComponent_Conditional_10_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
29
|
+
i0.ɵɵelement(0, "canvas", 17, 1);
|
|
30
|
+
} }
|
|
31
|
+
function RemoteBrowserSurfaceComponent_Conditional_10_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
32
|
+
i0.ɵɵelementStart(0, "div", 11);
|
|
33
|
+
i0.ɵɵelement(1, "mj-loading", 18);
|
|
34
|
+
i0.ɵɵelementEnd();
|
|
35
|
+
} }
|
|
36
|
+
function RemoteBrowserSurfaceComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
37
|
+
i0.ɵɵelementStart(0, "div", 15);
|
|
38
|
+
i0.ɵɵelement(1, "canvas", 16, 0);
|
|
39
|
+
i0.ɵɵconditionalCreate(3, RemoteBrowserSurfaceComponent_Conditional_10_Conditional_3_Template, 2, 0, "canvas", 17);
|
|
40
|
+
i0.ɵɵelementEnd();
|
|
41
|
+
i0.ɵɵconditionalCreate(4, RemoteBrowserSurfaceComponent_Conditional_10_Conditional_4_Template, 2, 0, "div", 11);
|
|
42
|
+
} if (rf & 2) {
|
|
43
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
44
|
+
i0.ɵɵclassProp("rb-hidden", !ctx_r1.HasFrame);
|
|
45
|
+
i0.ɵɵadvance();
|
|
46
|
+
i0.ɵɵclassProp("rb-screenshot--interactive", ctx_r1.CanTakeOver);
|
|
47
|
+
i0.ɵɵadvance(2);
|
|
48
|
+
i0.ɵɵconditional(ctx_r1.CanTakeOver ? 3 : -1);
|
|
49
|
+
i0.ɵɵadvance();
|
|
50
|
+
i0.ɵɵconditional(!ctx_r1.HasFrame ? 4 : -1);
|
|
51
|
+
} }
|
|
52
|
+
function RemoteBrowserSurfaceComponent_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
53
|
+
i0.ɵɵelement(0, "img", 10);
|
|
54
|
+
} if (rf & 2) {
|
|
55
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
56
|
+
i0.ɵɵproperty("src", ctx_r1.ScreenshotDataUrl, i0.ɵɵsanitizeUrl);
|
|
57
|
+
} }
|
|
58
|
+
function RemoteBrowserSurfaceComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
59
|
+
i0.ɵɵelementStart(0, "div", 11);
|
|
60
|
+
i0.ɵɵelement(1, "mj-loading", 18);
|
|
61
|
+
i0.ɵɵelementEnd();
|
|
62
|
+
} }
|
|
63
|
+
/** How often the surface polls the server for a fresh page screenshot while bound (~1.4 fps). */
|
|
64
|
+
const SNAPSHOT_POLL_MS = 700;
|
|
65
|
+
/** Min interval between forwarded pointer-move samples during takeover (~20 moves/sec). */
|
|
66
|
+
const POINTER_MOVE_THROTTLE_MS = 50;
|
|
67
|
+
/** Min interval between forwarded scroll samples during takeover; the deltas coalesce between sends (~25/sec). */
|
|
68
|
+
const SCROLL_THROTTLE_MS = 40;
|
|
69
|
+
/** Radius (px) of the synthetic cursor ring drawn on the overlay so the user can see their pointer. */
|
|
70
|
+
const SYNTHETIC_CURSOR_RADIUS = 6;
|
|
71
|
+
/**
|
|
72
|
+
* Keys forwarded into the page during takeover even though they aren't single printable characters —
|
|
73
|
+
* navigation/editing keys the page (not the host app) should receive. `preventDefault` is applied only to
|
|
74
|
+
* forwarded keys so the host app's own shortcuts on un-forwarded keys still work.
|
|
75
|
+
*/
|
|
76
|
+
const FORWARDED_CONTROL_KEYS = new Set([
|
|
77
|
+
'Enter', 'Tab', 'Backspace', 'Delete', 'Escape',
|
|
78
|
+
'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight',
|
|
79
|
+
'Home', 'End', 'PageUp', 'PageDown',
|
|
80
|
+
]);
|
|
81
|
+
/**
|
|
82
|
+
* The modifier keys themselves — pressing one alone is never forwarded as a key (it only rides as a
|
|
83
|
+
* modifier on the NEXT non-modifier key / click), so the page never receives a lone `'Shift'` keypress.
|
|
84
|
+
*/
|
|
85
|
+
const MODIFIER_KEYS = new Set(['Shift', 'Control', 'Alt', 'Meta', 'CapsLock']);
|
|
86
|
+
/**
|
|
87
|
+
* Maps a display-space pointer position on the live canvas to the server browser's VIEWPORT pixel space.
|
|
88
|
+
*
|
|
89
|
+
* The canvas's internal resolution (`canvasWidth`/`canvasHeight`, set from each pushed frame) IS the
|
|
90
|
+
* viewport pixel space; the canvas is displayed scaled to its bounding rect. So a display point maps as
|
|
91
|
+
* `vx = (clientX - rect.left) / rect.width * canvasWidth` (and likewise for y), rounded to ints. Returns
|
|
92
|
+
* `null` for a zero-size rect or un-sized canvas (divide-by-zero / not-ready guard).
|
|
93
|
+
*
|
|
94
|
+
* Pure + framework-free so it's unit-testable without a DOM.
|
|
95
|
+
*
|
|
96
|
+
* @param clientX The pointer's viewport X in the DOM (`event.clientX`).
|
|
97
|
+
* @param clientY The pointer's viewport Y in the DOM (`event.clientY`).
|
|
98
|
+
* @param rect The canvas's bounding rect (`getBoundingClientRect()`).
|
|
99
|
+
* @param canvasWidth The canvas's internal pixel width (= frame/viewport width).
|
|
100
|
+
* @param canvasHeight The canvas's internal pixel height (= frame/viewport height).
|
|
101
|
+
* @returns Integer viewport coordinates, or `null` when mapping isn't possible.
|
|
102
|
+
*/
|
|
103
|
+
export function MapToViewportCoords(clientX, clientY, rect, canvasWidth, canvasHeight) {
|
|
104
|
+
if (rect.width <= 0 || rect.height <= 0 || canvasWidth <= 0 || canvasHeight <= 0) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const x = Math.round((clientX - rect.left) / rect.width * canvasWidth);
|
|
108
|
+
const y = Math.round((clientY - rect.top) / rect.height * canvasHeight);
|
|
109
|
+
return { x, y };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* LIVE REMOTE-BROWSER surface (`mj-realtime-remote-browser-surface`) — the Browser channel
|
|
113
|
+
* tab's pane. It renders the SERVER-hosted browser the agent drives: a refreshing screenshot
|
|
114
|
+
* `<img>` with the current URL above it and a small "live" indicator. The agent's
|
|
115
|
+
* `browser_*` tools mutate the page through the channel plugin; this surface only PERCEIVES
|
|
116
|
+
* it, polling its {@link Fetch} callback every {@link SNAPSHOT_POLL_MS} ms while bound.
|
|
117
|
+
*
|
|
118
|
+
* The surface is transport-agnostic — it never touches GraphQL directly. The channel plugin
|
|
119
|
+
* wires the {@link Fetch} callback (closing over the session id + provider) in `BindSurface`
|
|
120
|
+
* before the surface's first change detection, so the `ngOnInit` poll has it. Polling stops
|
|
121
|
+
* in `ngOnDestroy` (pane collapsed / overlay torn down) so no traffic continues after unbind.
|
|
122
|
+
* View-only in v1 — there is no takeover input.
|
|
123
|
+
*
|
|
124
|
+
* ### Two render paths: pushed screencast (preferred) vs. snapshot poll (fallback)
|
|
125
|
+
* When the backend advertises the `ScreenStreaming` capability, the server PUSHES encoded CDP
|
|
126
|
+
* frames and the channel plugin sets {@link Streaming} = `true`. In that mode the surface paints
|
|
127
|
+
* each frame onto a `<canvas>` via {@link RenderFrame} and DOES NOT start the poll. When streaming
|
|
128
|
+
* is off (capability absent or start failed) the original behavior is unchanged: the surface polls
|
|
129
|
+
* {@link Fetch} every {@link SNAPSHOT_POLL_MS} ms and renders the screenshot `<img>`.
|
|
130
|
+
*/
|
|
131
|
+
export class RemoteBrowserSurfaceComponent {
|
|
132
|
+
/** Snapshot fetcher supplied by the channel plugin (closes over the session id + provider). */
|
|
133
|
+
Fetch = null;
|
|
134
|
+
/**
|
|
135
|
+
* Whether the server is PUSHING live screencast frames for this session (the backend advertised the
|
|
136
|
+
* `ScreenStreaming` capability and the start succeeded). When `true` the surface paints pushed frames
|
|
137
|
+
* onto its `<canvas>` via {@link RenderFrame} and does NOT poll; when `false` it uses the snapshot
|
|
138
|
+
* `<img>` poll fallback. Set by the channel plugin in `BindSurface` from the start-screencast result.
|
|
139
|
+
*
|
|
140
|
+
* The start-screencast result is async, so this may flip to `true` AFTER `ngOnInit` has already begun
|
|
141
|
+
* polling — the setter tears the poll down in that case so the two render paths never run at once.
|
|
142
|
+
*/
|
|
143
|
+
set Streaming(value) {
|
|
144
|
+
if (value === this._streaming) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
this._streaming = value;
|
|
148
|
+
if (value) {
|
|
149
|
+
// Late flip after the poll-fallback already started: stop polling, switch to canvas frames.
|
|
150
|
+
this.stopPolling();
|
|
151
|
+
this.zone.run(() => this.cdr.markForCheck());
|
|
152
|
+
}
|
|
153
|
+
// Takeover only attaches on the canvas path — (de)attach as streaming flips.
|
|
154
|
+
this.syncTakeoverListeners();
|
|
155
|
+
}
|
|
156
|
+
get Streaming() {
|
|
157
|
+
return this._streaming;
|
|
158
|
+
}
|
|
159
|
+
_streaming = false;
|
|
160
|
+
/**
|
|
161
|
+
* Whether HUMAN TAKEOVER is enabled — the user watching the live view can click/type into the page and
|
|
162
|
+
* those events are relayed into the server browser (Collaborative control). Takeover only attaches to the
|
|
163
|
+
* canvas render path (pushed screencast); the `<img>` poll fallback stays view-only. When enabled while
|
|
164
|
+
* already streaming the setter attaches listeners; flipping it off (or streaming off) detaches them.
|
|
165
|
+
*/
|
|
166
|
+
set Interactive(value) {
|
|
167
|
+
if (value === this._interactive) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this._interactive = value;
|
|
171
|
+
this.syncTakeoverListeners();
|
|
172
|
+
this.zone.run(() => this.cdr.markForCheck());
|
|
173
|
+
}
|
|
174
|
+
get Interactive() {
|
|
175
|
+
return this._interactive;
|
|
176
|
+
}
|
|
177
|
+
_interactive = false;
|
|
178
|
+
/** Emits each human-takeover input (pointer move/click, key) the user performs on the live canvas. */
|
|
179
|
+
HumanInput = new EventEmitter();
|
|
180
|
+
/**
|
|
181
|
+
* Whether the live tab-audio stream is available — the channel sets this `true` when the server confirms
|
|
182
|
+
* it is pushing audio. Drives whether the speaker toggle renders in the live-view bar.
|
|
183
|
+
*/
|
|
184
|
+
AudioAvailable = false;
|
|
185
|
+
/**
|
|
186
|
+
* Whether tab audio is currently muted. Two-way: the channel sets the initial value (un-muted when audio
|
|
187
|
+
* starts) and the toggle updates it; {@link AudioMutedChange} relays each user change to the channel,
|
|
188
|
+
* which mutes/unmutes the player.
|
|
189
|
+
*/
|
|
190
|
+
AudioMuted = false;
|
|
191
|
+
/** Emits the new muted state each time the user toggles the speaker (two-way `AudioMuted`). */
|
|
192
|
+
AudioMutedChange = new EventEmitter();
|
|
193
|
+
/** Toggles the speaker mute state and relays it to the channel (which mutes/unmutes the audio player). */
|
|
194
|
+
ToggleAudioMuted() {
|
|
195
|
+
this.AudioMuted = !this.AudioMuted;
|
|
196
|
+
this.AudioMutedChange.emit(this.AudioMuted);
|
|
197
|
+
}
|
|
198
|
+
/** True when takeover is both enabled AND on the canvas path — drives the cursor + "driving" pill. */
|
|
199
|
+
get CanTakeOver() {
|
|
200
|
+
return this._interactive && this._streaming;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* True while the user is actively driving the remote browser with the keyboard — the takeover canvas
|
|
204
|
+
* holds focus. The host overlay reads this to stand down its own global keyboard shortcuts (e.g.
|
|
205
|
+
* T-to-type opening the local composer) so keystrokes go to the remote page, not the local input.
|
|
206
|
+
*/
|
|
207
|
+
get IsCapturingInput() {
|
|
208
|
+
return this.surfaceFocused && this.CanTakeOver;
|
|
209
|
+
}
|
|
210
|
+
/** The current screenshot as a `data:` URL, or `null` before the first snapshot arrives. */
|
|
211
|
+
ScreenshotDataUrl = null;
|
|
212
|
+
/** The current page URL reported by the server, or `null` when none. */
|
|
213
|
+
CurrentUrl = null;
|
|
214
|
+
/** Whether at least one pushed screencast frame has been painted (drives the canvas placeholder). */
|
|
215
|
+
HasFrame = false;
|
|
216
|
+
/** The canvas the pushed screencast frames are painted onto (present only while {@link Streaming}). */
|
|
217
|
+
frameCanvas;
|
|
218
|
+
/** The transparent overlay the synthetic cursor is drawn onto (present only while {@link CanTakeOver}). */
|
|
219
|
+
cursorCanvas;
|
|
220
|
+
/** Whether at least one screenshot OR frame has been rendered (drives the "Live" indicator). */
|
|
221
|
+
get HasSnapshot() {
|
|
222
|
+
return this.ScreenshotDataUrl !== null || this.HasFrame;
|
|
223
|
+
}
|
|
224
|
+
cdr = inject(ChangeDetectorRef);
|
|
225
|
+
zone = inject(NgZone);
|
|
226
|
+
/** Active poll timer; `null` when polling is stopped. */
|
|
227
|
+
pollTimer = null;
|
|
228
|
+
/** Guards against overlapping polls when a request runs longer than the interval. */
|
|
229
|
+
polling = false;
|
|
230
|
+
/** Set on destroy so an in-flight poll's late resolution doesn't touch a torn-down view. */
|
|
231
|
+
destroyed = false;
|
|
232
|
+
/** Reused decode target for pushed frames — avoids allocating an `Image` per frame. */
|
|
233
|
+
frameImage = new Image();
|
|
234
|
+
/** The most recent un-painted frame data URL, drained on the next animation frame (drop-old coalescing). */
|
|
235
|
+
pendingFrameDataUrl = null;
|
|
236
|
+
/** True while a paint is scheduled, so rapid frames coalesce to one `requestAnimationFrame`. */
|
|
237
|
+
framePaintScheduled = false;
|
|
238
|
+
/** The canvas element takeover listeners are currently bound to, or `null` when detached. */
|
|
239
|
+
takeoverCanvas = null;
|
|
240
|
+
/** Timestamp (ms) of the last forwarded pointer-move, for throttling the high-frequency stream. */
|
|
241
|
+
lastPointerMoveAt = 0;
|
|
242
|
+
/** Timestamp (ms) of the last forwarded scroll, for throttling the high-frequency wheel stream. */
|
|
243
|
+
lastScrollAt = 0;
|
|
244
|
+
/** Horizontal wheel delta accumulated since the last forwarded scroll (coalesced across throttled events). */
|
|
245
|
+
pendingScrollDeltaX = 0;
|
|
246
|
+
/** Vertical wheel delta accumulated since the last forwarded scroll (coalesced across throttled events). */
|
|
247
|
+
pendingScrollDeltaY = 0;
|
|
248
|
+
/** Last canvas-relative mouse position in VIEWPORT pixel space, or `null` when the pointer is off-canvas. */
|
|
249
|
+
cursorViewportPoint = null;
|
|
250
|
+
/** True while a synthetic-cursor repaint is queued, so rapid moves coalesce to one `requestAnimationFrame`. */
|
|
251
|
+
cursorPaintScheduled = false;
|
|
252
|
+
/**
|
|
253
|
+
* True while the user is mid-drag on the canvas (a `mousedown` not yet released) — drives forwarding
|
|
254
|
+
* intermediate moves as a drag and emitting the closing `pointer-up`. Click-drag text selection relies on
|
|
255
|
+
* this so the drag relays as down → moves → up rather than a single discrete click.
|
|
256
|
+
*/
|
|
257
|
+
dragging = false;
|
|
258
|
+
/**
|
|
259
|
+
* True when the most recent `mousedown`→`mouseup` actually MOVED (a real drag), so the synthetic `click`
|
|
260
|
+
* the browser fires right after should be suppressed — the drag's down/move/up already conveyed the intent,
|
|
261
|
+
* and a trailing click would collapse a just-made text selection.
|
|
262
|
+
*/
|
|
263
|
+
suppressNextClick = false;
|
|
264
|
+
/** Viewport point of the active drag's mousedown, used to tell a real drag from a stationary click. */
|
|
265
|
+
dragStartPoint = null;
|
|
266
|
+
/**
|
|
267
|
+
* True while the takeover canvas currently HOLDS keyboard focus. The host overlay reads this (via
|
|
268
|
+
* {@link IsCapturingInput}) to suppress its own global key shortcuts (e.g. T-to-type) so the user's
|
|
269
|
+
* keystrokes land in the remote browser, not the local composer — fixing the "greedy textbox" focus theft.
|
|
270
|
+
*/
|
|
271
|
+
surfaceFocused = false;
|
|
272
|
+
/** Bound handlers — stored so the exact same references can be removed on detach. */
|
|
273
|
+
onCanvasMouseMove = (e) => this.handlePointerMove(e);
|
|
274
|
+
onCanvasMouseDown = (e) => this.handlePointerDown(e);
|
|
275
|
+
onCanvasMouseUp = (e) => this.handlePointerUp(e);
|
|
276
|
+
onCanvasClick = (e) => this.handlePointerClick(e);
|
|
277
|
+
onCanvasKeyDown = (e) => this.handleKeyDown(e);
|
|
278
|
+
onCanvasMouseLeave = () => this.handleMouseLeave();
|
|
279
|
+
onCanvasWheel = (e) => this.handleWheel(e);
|
|
280
|
+
onCanvasFocus = () => { this.surfaceFocused = true; };
|
|
281
|
+
onCanvasBlur = () => { this.surfaceFocused = false; };
|
|
282
|
+
ngOnInit() {
|
|
283
|
+
// In streaming mode the server pushes frames — never start the poll (it would be redundant traffic).
|
|
284
|
+
if (this.Streaming) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
void this.pollOnce();
|
|
288
|
+
this.startPolling();
|
|
289
|
+
}
|
|
290
|
+
ngOnDestroy() {
|
|
291
|
+
this.destroyed = true;
|
|
292
|
+
this.stopPolling();
|
|
293
|
+
this.detachTakeoverListeners();
|
|
294
|
+
}
|
|
295
|
+
/** Starts the interval poll OUTSIDE Angular's zone so it doesn't trigger CD on every tick. */
|
|
296
|
+
startPolling() {
|
|
297
|
+
if (this.pollTimer !== null) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
this.zone.runOutsideAngular(() => {
|
|
301
|
+
this.pollTimer = setInterval(() => void this.pollOnce(), SNAPSHOT_POLL_MS);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
/** Stops the interval poll (no further snapshot requests are issued). */
|
|
305
|
+
stopPolling() {
|
|
306
|
+
if (this.pollTimer !== null) {
|
|
307
|
+
clearInterval(this.pollTimer);
|
|
308
|
+
this.pollTimer = null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Fetches one snapshot and applies it to the view. Best-effort: the fetcher resolves to
|
|
313
|
+
* `null` on failure (the prior frame stays on screen) so a single failed poll never breaks
|
|
314
|
+
* the live view. Skips when a prior poll is still in flight, the fetcher isn't wired, or
|
|
315
|
+
* after destroy.
|
|
316
|
+
*/
|
|
317
|
+
async pollOnce() {
|
|
318
|
+
if (this.polling || this.destroyed || !this.Fetch) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
this.polling = true;
|
|
322
|
+
try {
|
|
323
|
+
const snapshot = await this.Fetch();
|
|
324
|
+
this.applySnapshot(snapshot);
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
// Defensive — the fetcher is contractually non-throwing, but never let a poll break the view.
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
this.polling = false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/** Applies a fetched snapshot to the view fields and triggers OnPush change detection. */
|
|
334
|
+
applySnapshot(snapshot) {
|
|
335
|
+
if (this.destroyed) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const base64 = snapshot?.ScreenshotBase64 ?? null;
|
|
339
|
+
const nextUrl = snapshot?.CurrentUrl ?? null;
|
|
340
|
+
const nextDataUrl = base64 ? `data:image/png;base64,${base64}` : this.ScreenshotDataUrl;
|
|
341
|
+
if (nextDataUrl === this.ScreenshotDataUrl && nextUrl === this.CurrentUrl) {
|
|
342
|
+
return; // nothing changed — skip the CD pass
|
|
343
|
+
}
|
|
344
|
+
this.ScreenshotDataUrl = nextDataUrl;
|
|
345
|
+
this.CurrentUrl = nextUrl;
|
|
346
|
+
// Re-enter the zone for the OnPush update (the poll runs outside Angular).
|
|
347
|
+
this.zone.run(() => this.cdr.markForCheck());
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Paints one PUSHED screencast frame (base64 JPEG) onto the canvas. Called by the channel plugin for
|
|
351
|
+
* each frame the server pushes while {@link Streaming}. Coalesces a burst of frames to one paint per
|
|
352
|
+
* animation frame (the newest wins) so a fast stream never floods the main thread, and reuses a single
|
|
353
|
+
* `Image` decode target to avoid per-frame allocation. No-op after destroy or when not in streaming mode.
|
|
354
|
+
*
|
|
355
|
+
* @param dataBase64 The frame image as raw base64 JPEG (no `data:` prefix).
|
|
356
|
+
*/
|
|
357
|
+
RenderFrame(dataBase64) {
|
|
358
|
+
if (this.destroyed || !this.Streaming || !dataBase64) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
this.pendingFrameDataUrl = `data:image/jpeg;base64,${dataBase64}`;
|
|
362
|
+
if (this.framePaintScheduled) {
|
|
363
|
+
return; // a paint is already queued — it will pick up this newest frame
|
|
364
|
+
}
|
|
365
|
+
this.framePaintScheduled = true;
|
|
366
|
+
this.zone.runOutsideAngular(() => requestAnimationFrame(() => this.paintPendingFrame()));
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Updates the URL shown above the live view. In streaming (canvas) mode the snapshot poll is stopped, so
|
|
370
|
+
* the channel pushes the current URL here after a navigation/action reports one — otherwise the bar would
|
|
371
|
+
* stay stuck on "No page loaded yet" even though the page is live. No-op for an unchanged / empty value.
|
|
372
|
+
*
|
|
373
|
+
* @param url The current page URL, or null/empty to leave the bar unchanged.
|
|
374
|
+
*/
|
|
375
|
+
SetCurrentUrl(url) {
|
|
376
|
+
if (this.destroyed || !url || url === this.CurrentUrl) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
this.CurrentUrl = url;
|
|
380
|
+
this.zone.run(() => this.cdr.markForCheck());
|
|
381
|
+
}
|
|
382
|
+
/** Drains the most recent pending frame onto the canvas (drop-old coalescing target). */
|
|
383
|
+
paintPendingFrame() {
|
|
384
|
+
this.framePaintScheduled = false;
|
|
385
|
+
const dataUrl = this.pendingFrameDataUrl;
|
|
386
|
+
this.pendingFrameDataUrl = null;
|
|
387
|
+
if (this.destroyed || !dataUrl) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
this.frameImage.onload = () => this.drawFrameImage();
|
|
391
|
+
this.frameImage.src = dataUrl;
|
|
392
|
+
}
|
|
393
|
+
/** Draws the decoded frame image onto the canvas, sizing the canvas to the frame on the first paint. */
|
|
394
|
+
drawFrameImage() {
|
|
395
|
+
const canvas = this.frameCanvas?.nativeElement;
|
|
396
|
+
if (this.destroyed || !canvas) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const ctx = canvas.getContext('2d');
|
|
400
|
+
if (!ctx) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const { naturalWidth: w, naturalHeight: h } = this.frameImage;
|
|
404
|
+
if (w > 0 && h > 0 && (canvas.width !== w || canvas.height !== h)) {
|
|
405
|
+
canvas.width = w;
|
|
406
|
+
canvas.height = h;
|
|
407
|
+
}
|
|
408
|
+
ctx.drawImage(this.frameImage, 0, 0);
|
|
409
|
+
if (!this.HasFrame) {
|
|
410
|
+
// First frame arrived — re-enter the zone to clear the placeholder + flip the "Live" indicator,
|
|
411
|
+
// and attach takeover listeners now that the canvas element exists in the DOM.
|
|
412
|
+
this.zone.run(() => {
|
|
413
|
+
this.HasFrame = true;
|
|
414
|
+
this.cdr.markForCheck();
|
|
415
|
+
});
|
|
416
|
+
this.syncTakeoverListeners();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// ----- human takeover (Collaborative control) --------------------------------------------
|
|
420
|
+
/**
|
|
421
|
+
* Attaches or detaches the canvas takeover listeners so they're bound exactly when takeover is live —
|
|
422
|
+
* {@link CanTakeOver} is true AND the canvas element exists. Idempotent: re-binds only when the target
|
|
423
|
+
* canvas changed, detaches when the conditions no longer hold. Safe to call from any state-flip path
|
|
424
|
+
* (the `Interactive`/`Streaming` setters, the first-frame paint, destroy).
|
|
425
|
+
*/
|
|
426
|
+
syncTakeoverListeners() {
|
|
427
|
+
const canvas = this.CanTakeOver && !this.destroyed ? (this.frameCanvas?.nativeElement ?? null) : null;
|
|
428
|
+
if (canvas === this.takeoverCanvas) {
|
|
429
|
+
return; // already in the desired state
|
|
430
|
+
}
|
|
431
|
+
this.detachTakeoverListeners();
|
|
432
|
+
if (canvas) {
|
|
433
|
+
this.attachTakeoverListeners(canvas);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
/** Binds the pointer/keyboard/wheel listeners to the canvas OUTSIDE the zone (mousemove/wheel are high-frequency). */
|
|
437
|
+
attachTakeoverListeners(canvas) {
|
|
438
|
+
canvas.tabIndex = 0; // focusable so it can receive keydown
|
|
439
|
+
// Marks this canvas as a keyboard-capturing surface so the host overlay's global shortcuts (e.g.
|
|
440
|
+
// T-to-type) stand down while it holds focus — see the overlay's isKeyboardCapturingSurfaceFocused().
|
|
441
|
+
canvas.setAttribute('data-mj-capture-keys', '');
|
|
442
|
+
this.zone.runOutsideAngular(() => {
|
|
443
|
+
canvas.addEventListener('mousemove', this.onCanvasMouseMove);
|
|
444
|
+
canvas.addEventListener('mousedown', this.onCanvasMouseDown);
|
|
445
|
+
canvas.addEventListener('mouseup', this.onCanvasMouseUp);
|
|
446
|
+
canvas.addEventListener('click', this.onCanvasClick);
|
|
447
|
+
canvas.addEventListener('keydown', this.onCanvasKeyDown);
|
|
448
|
+
canvas.addEventListener('mouseleave', this.onCanvasMouseLeave);
|
|
449
|
+
canvas.addEventListener('focus', this.onCanvasFocus);
|
|
450
|
+
canvas.addEventListener('blur', this.onCanvasBlur);
|
|
451
|
+
// `passive: false` so preventDefault stops the host page from scrolling while the user drives the page.
|
|
452
|
+
canvas.addEventListener('wheel', this.onCanvasWheel, { passive: false });
|
|
453
|
+
});
|
|
454
|
+
this.takeoverCanvas = canvas;
|
|
455
|
+
}
|
|
456
|
+
/** Removes the listeners from the currently-bound canvas (no-op when none are bound) and clears the cursor. */
|
|
457
|
+
detachTakeoverListeners() {
|
|
458
|
+
const canvas = this.takeoverCanvas;
|
|
459
|
+
if (!canvas) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
canvas.removeEventListener('mousemove', this.onCanvasMouseMove);
|
|
463
|
+
canvas.removeEventListener('mousedown', this.onCanvasMouseDown);
|
|
464
|
+
canvas.removeEventListener('mouseup', this.onCanvasMouseUp);
|
|
465
|
+
canvas.removeEventListener('click', this.onCanvasClick);
|
|
466
|
+
canvas.removeEventListener('keydown', this.onCanvasKeyDown);
|
|
467
|
+
canvas.removeEventListener('mouseleave', this.onCanvasMouseLeave);
|
|
468
|
+
canvas.removeEventListener('focus', this.onCanvasFocus);
|
|
469
|
+
canvas.removeEventListener('blur', this.onCanvasBlur);
|
|
470
|
+
canvas.removeEventListener('wheel', this.onCanvasWheel);
|
|
471
|
+
canvas.removeAttribute('data-mj-capture-keys');
|
|
472
|
+
this.takeoverCanvas = null;
|
|
473
|
+
this.cursorViewportPoint = null;
|
|
474
|
+
this.dragging = false;
|
|
475
|
+
this.suppressNextClick = false;
|
|
476
|
+
this.dragStartPoint = null;
|
|
477
|
+
this.surfaceFocused = false;
|
|
478
|
+
this.clearSyntheticCursor();
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Pointer-move → tracks the cursor for the synthetic-cursor overlay (every move, immediate, no
|
|
482
|
+
* round-trip) AND emits a throttled viewport-mapped move to the server. Runs outside the zone (no CD
|
|
483
|
+
* per move).
|
|
484
|
+
*/
|
|
485
|
+
handlePointerMove(event) {
|
|
486
|
+
const point = this.toViewportCoords(event);
|
|
487
|
+
if (point) {
|
|
488
|
+
// Always update the local cursor for instant feedback — independent of the server-relay throttle.
|
|
489
|
+
this.cursorViewportPoint = point;
|
|
490
|
+
this.scheduleCursorPaint();
|
|
491
|
+
}
|
|
492
|
+
const now = Date.now();
|
|
493
|
+
if (now - this.lastPointerMoveAt < POINTER_MOVE_THROTTLE_MS) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
this.lastPointerMoveAt = now;
|
|
497
|
+
if (point) {
|
|
498
|
+
this.emitInput({ kind: 'pointer-move', x: point.x, y: point.y });
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
/** Mouse leaves the canvas → hide the synthetic cursor (nothing to relay). */
|
|
502
|
+
handleMouseLeave() {
|
|
503
|
+
this.cursorViewportPoint = null;
|
|
504
|
+
this.scheduleCursorPaint();
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Wheel/trackpad/Magic-Mouse scroll over the canvas → forwards a viewport-mapped scroll to the server.
|
|
508
|
+
* Calls `preventDefault` so the host page doesn't scroll while the user drives the live page. Throttled
|
|
509
|
+
* to {@link SCROLL_THROTTLE_MS}, coalescing the skipped deltas so no scroll distance is lost.
|
|
510
|
+
*/
|
|
511
|
+
handleWheel(event) {
|
|
512
|
+
event.preventDefault();
|
|
513
|
+
this.pendingScrollDeltaX += event.deltaX;
|
|
514
|
+
this.pendingScrollDeltaY += event.deltaY;
|
|
515
|
+
const point = this.toViewportCoords(event);
|
|
516
|
+
if (point) {
|
|
517
|
+
this.cursorViewportPoint = point;
|
|
518
|
+
this.scheduleCursorPaint();
|
|
519
|
+
}
|
|
520
|
+
const now = Date.now();
|
|
521
|
+
if (now - this.lastScrollAt < SCROLL_THROTTLE_MS) {
|
|
522
|
+
return; // accumulate into pendingScrollDelta* until the throttle window opens
|
|
523
|
+
}
|
|
524
|
+
this.lastScrollAt = now;
|
|
525
|
+
const target = point ?? this.cursorViewportPoint;
|
|
526
|
+
if (target && (this.pendingScrollDeltaX !== 0 || this.pendingScrollDeltaY !== 0)) {
|
|
527
|
+
this.emitInput({ kind: 'scroll', x: target.x, y: target.y, deltaX: this.pendingScrollDeltaX, deltaY: this.pendingScrollDeltaY });
|
|
528
|
+
this.pendingScrollDeltaX = 0;
|
|
529
|
+
this.pendingScrollDeltaY = 0;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Mouse-down on the canvas → focuses the canvas (so subsequent keys are captured) and BEGINS a drag:
|
|
534
|
+
* emits a `pointer-down` and arms drag mode so the following moves relay as drag motion and the matching
|
|
535
|
+
* `pointer-up` closes it. This is what makes click-drag text selection work (rather than a discrete click).
|
|
536
|
+
*/
|
|
537
|
+
handlePointerDown(event) {
|
|
538
|
+
this.takeoverCanvas?.focus();
|
|
539
|
+
const point = this.toViewportCoords(event);
|
|
540
|
+
if (!point) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
this.dragging = true;
|
|
544
|
+
this.dragStartPoint = point;
|
|
545
|
+
this.suppressNextClick = false;
|
|
546
|
+
this.cursorViewportPoint = point;
|
|
547
|
+
this.scheduleCursorPaint();
|
|
548
|
+
this.emitInput({ kind: 'pointer-down', x: point.x, y: point.y, button: this.mapButton(event.button), modifiers: this.collectModifiers(event) });
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Mouse-up on the canvas → closes a drag with a `pointer-up` at the release point, disarming drag mode.
|
|
552
|
+
* If the pointer actually moved between down and up, marks the following synthetic `click` to be suppressed
|
|
553
|
+
* (so the drag selection isn't immediately collapsed by a trailing click).
|
|
554
|
+
*/
|
|
555
|
+
handlePointerUp(event) {
|
|
556
|
+
if (!this.dragging) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
this.dragging = false;
|
|
560
|
+
const point = this.toViewportCoords(event);
|
|
561
|
+
if (point) {
|
|
562
|
+
const start = this.dragStartPoint;
|
|
563
|
+
this.suppressNextClick = !!start && (start.x !== point.x || start.y !== point.y);
|
|
564
|
+
this.emitInput({ kind: 'pointer-up', x: point.x, y: point.y, button: this.mapButton(event.button), modifiers: this.collectModifiers(event) });
|
|
565
|
+
}
|
|
566
|
+
this.dragStartPoint = null;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Click → emits a viewport-mapped click WITH any held modifiers (e.g. Shift-click text selection). The
|
|
570
|
+
* server browser receives the press/release of a drag via {@link handlePointerDown}/{@link handlePointerUp};
|
|
571
|
+
* a plain click (down+up with no motion) still fires this `click` event, so a simple click relays as one
|
|
572
|
+
* click action — but we skip emitting it when it terminates a real drag (where down/up already covered it).
|
|
573
|
+
*/
|
|
574
|
+
handlePointerClick(event) {
|
|
575
|
+
this.takeoverCanvas?.focus();
|
|
576
|
+
if (this.suppressNextClick) {
|
|
577
|
+
// This click terminates a real drag (selection) — the down/move/up already conveyed it.
|
|
578
|
+
this.suppressNextClick = false;
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
const point = this.toViewportCoords(event);
|
|
582
|
+
if (point) {
|
|
583
|
+
this.emitInput({ kind: 'pointer-click', x: point.x, y: point.y, button: this.mapButton(event.button), modifiers: this.collectModifiers(event) });
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Keydown → forwards printable keys, a curated set of control keys ({@link FORWARDED_CONTROL_KEYS}), AND
|
|
588
|
+
* any key combined with a Ctrl/Cmd/Alt modifier (so combos like Ctrl/Cmd+A select-all, Cmd+C/Cmd+V relay).
|
|
589
|
+
* Calls `preventDefault` + `stopPropagation` ONLY on forwarded keys, so (a) the host app keeps its own
|
|
590
|
+
* shortcuts on un-forwarded keys and (b) forwarded keys never bubble to the host overlay's global shortcuts
|
|
591
|
+
* (the T-to-type focus-steal). Lone modifier keys are never forwarded — they ride as `modifiers` on the
|
|
592
|
+
* next key/click.
|
|
593
|
+
*/
|
|
594
|
+
handleKeyDown(event) {
|
|
595
|
+
const key = event.key;
|
|
596
|
+
if (MODIFIER_KEYS.has(key)) {
|
|
597
|
+
return; // a modifier on its own is not a keypress — it rides on the next key/click
|
|
598
|
+
}
|
|
599
|
+
const isPrintable = key.length === 1;
|
|
600
|
+
const hasComboModifier = event.ctrlKey || event.metaKey || event.altKey;
|
|
601
|
+
const isForwardable = isPrintable || FORWARDED_CONTROL_KEYS.has(key) || hasComboModifier;
|
|
602
|
+
if (!isForwardable) {
|
|
603
|
+
return; // leave non-forwarded keys to the host app
|
|
604
|
+
}
|
|
605
|
+
event.preventDefault();
|
|
606
|
+
// Stop the keystroke from reaching document-level handlers (e.g. the overlay's T-to-type) so the user's
|
|
607
|
+
// typing lands in the remote browser, not the local composer.
|
|
608
|
+
event.stopPropagation();
|
|
609
|
+
this.emitInput({ kind: 'key', key, modifiers: this.collectModifiers(event) });
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Collects the modifier keys currently held during a DOM mouse/keyboard event into the relayed
|
|
613
|
+
* {@link RemoteBrowserModifier} list (empty when none are held).
|
|
614
|
+
*
|
|
615
|
+
* @param event The DOM event whose modifier flags to read.
|
|
616
|
+
* @returns The held modifiers, in a stable order.
|
|
617
|
+
*/
|
|
618
|
+
collectModifiers(event) {
|
|
619
|
+
const modifiers = [];
|
|
620
|
+
if (event.shiftKey) {
|
|
621
|
+
modifiers.push('Shift');
|
|
622
|
+
}
|
|
623
|
+
if (event.ctrlKey) {
|
|
624
|
+
modifiers.push('Control');
|
|
625
|
+
}
|
|
626
|
+
if (event.altKey) {
|
|
627
|
+
modifiers.push('Alt');
|
|
628
|
+
}
|
|
629
|
+
if (event.metaKey) {
|
|
630
|
+
modifiers.push('Meta');
|
|
631
|
+
}
|
|
632
|
+
return modifiers;
|
|
633
|
+
}
|
|
634
|
+
/** Re-enters the zone to emit one human input (so subscribers see it inside Angular). */
|
|
635
|
+
emitInput(input) {
|
|
636
|
+
this.zone.run(() => this.HumanInput.emit(input));
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Maps a DOM pointer event on the live canvas to VIEWPORT coordinates via {@link MapToViewportCoords}
|
|
640
|
+
* (the pure, unit-tested mapping). Returns `null` when no canvas is bound or mapping isn't possible yet.
|
|
641
|
+
*
|
|
642
|
+
* @param event The pointer event on the canvas.
|
|
643
|
+
* @returns The integer viewport coordinates, or `null` when mapping isn't possible yet.
|
|
644
|
+
*/
|
|
645
|
+
toViewportCoords(event) {
|
|
646
|
+
const canvas = this.takeoverCanvas;
|
|
647
|
+
if (!canvas) {
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
return MapToViewportCoords(event.clientX, event.clientY, canvas.getBoundingClientRect(), canvas.width, canvas.height);
|
|
651
|
+
}
|
|
652
|
+
// ----- synthetic cursor (local feedback — CDP frames don't include the OS cursor) -----------
|
|
653
|
+
/**
|
|
654
|
+
* Schedules a synthetic-cursor repaint on the next animation frame, coalescing a burst of moves to one
|
|
655
|
+
* paint (the newest position wins). Runs outside the zone — drawing the cursor never triggers Angular CD.
|
|
656
|
+
*/
|
|
657
|
+
scheduleCursorPaint() {
|
|
658
|
+
if (this.cursorPaintScheduled) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
this.cursorPaintScheduled = true;
|
|
662
|
+
this.zone.runOutsideAngular(() => requestAnimationFrame(() => this.paintSyntheticCursor()));
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Draws (or clears) the synthetic cursor on the overlay canvas at {@link cursorViewportPoint}. The overlay
|
|
666
|
+
* shares the frame canvas's internal resolution (sized here to match), so a point drawn at viewport coords
|
|
667
|
+
* aligns exactly with the page underneath. A small brand-tinted ring with a center dot reads as a pointer
|
|
668
|
+
* without obscuring what it's over. No-op after destroy or when the overlay isn't mounted.
|
|
669
|
+
*/
|
|
670
|
+
paintSyntheticCursor() {
|
|
671
|
+
this.cursorPaintScheduled = false;
|
|
672
|
+
const overlay = this.cursorCanvas?.nativeElement;
|
|
673
|
+
const frame = this.frameCanvas?.nativeElement;
|
|
674
|
+
if (this.destroyed || !overlay || !frame) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
// Keep the overlay's internal resolution locked to the frame canvas so coordinate spaces match.
|
|
678
|
+
if (overlay.width !== frame.width || overlay.height !== frame.height) {
|
|
679
|
+
overlay.width = frame.width;
|
|
680
|
+
overlay.height = frame.height;
|
|
681
|
+
}
|
|
682
|
+
const ctx = overlay.getContext('2d');
|
|
683
|
+
if (!ctx) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
ctx.clearRect(0, 0, overlay.width, overlay.height);
|
|
687
|
+
const point = this.cursorViewportPoint;
|
|
688
|
+
if (!point) {
|
|
689
|
+
return; // pointer left the canvas — leave the overlay cleared
|
|
690
|
+
}
|
|
691
|
+
const ring = this.resolveCssColor('--mj-brand-primary', '#264FAF');
|
|
692
|
+
ctx.beginPath();
|
|
693
|
+
ctx.arc(point.x, point.y, SYNTHETIC_CURSOR_RADIUS, 0, Math.PI * 2);
|
|
694
|
+
ctx.lineWidth = 2;
|
|
695
|
+
ctx.strokeStyle = ring;
|
|
696
|
+
ctx.stroke();
|
|
697
|
+
ctx.beginPath();
|
|
698
|
+
ctx.arc(point.x, point.y, 1.5, 0, Math.PI * 2);
|
|
699
|
+
ctx.fillStyle = ring;
|
|
700
|
+
ctx.fill();
|
|
701
|
+
}
|
|
702
|
+
/** Clears the synthetic-cursor overlay (e.g. on detach). No-op when the overlay isn't mounted. */
|
|
703
|
+
clearSyntheticCursor() {
|
|
704
|
+
const overlay = this.cursorCanvas?.nativeElement;
|
|
705
|
+
const ctx = overlay?.getContext('2d');
|
|
706
|
+
if (overlay && ctx) {
|
|
707
|
+
ctx.clearRect(0, 0, overlay.width, overlay.height);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Resolves a CSS custom property (design token) to its computed color value off the host element, so the
|
|
712
|
+
* canvas-drawn cursor honors theming. Falls back to the supplied default when the token is unset or the
|
|
713
|
+
* host isn't measurable.
|
|
714
|
+
*
|
|
715
|
+
* @param token The CSS custom property name (e.g. `'--mj-brand-primary'`).
|
|
716
|
+
* @param fallback The color to use when the token resolves empty.
|
|
717
|
+
* @returns The resolved color string.
|
|
718
|
+
*/
|
|
719
|
+
resolveCssColor(token, fallback) {
|
|
720
|
+
const host = this.frameCanvas?.nativeElement;
|
|
721
|
+
if (!host) {
|
|
722
|
+
return fallback;
|
|
723
|
+
}
|
|
724
|
+
const value = getComputedStyle(host).getPropertyValue(token).trim();
|
|
725
|
+
return value.length > 0 ? value : fallback;
|
|
726
|
+
}
|
|
727
|
+
/** Maps a DOM `MouseEvent.button` (0/1/2) to the relayed button union, defaulting to `'left'`. */
|
|
728
|
+
mapButton(button) {
|
|
729
|
+
if (button === 1) {
|
|
730
|
+
return 'middle';
|
|
731
|
+
}
|
|
732
|
+
if (button === 2) {
|
|
733
|
+
return 'right';
|
|
734
|
+
}
|
|
735
|
+
return 'left';
|
|
736
|
+
}
|
|
737
|
+
static ɵfac = function RemoteBrowserSurfaceComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || RemoteBrowserSurfaceComponent)(); };
|
|
738
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RemoteBrowserSurfaceComponent, selectors: [["mj-realtime-remote-browser-surface"]], viewQuery: function RemoteBrowserSurfaceComponent_Query(rf, ctx) { if (rf & 1) {
|
|
739
|
+
i0.ɵɵviewQuery(_c0, 5)(_c1, 5);
|
|
740
|
+
} if (rf & 2) {
|
|
741
|
+
let _t;
|
|
742
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.frameCanvas = _t.first);
|
|
743
|
+
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) {
|
|
745
|
+
i0.ɵɵelementStart(0, "div", 2)(1, "div", 3);
|
|
746
|
+
i0.ɵɵelement(2, "span", 4);
|
|
747
|
+
i0.ɵɵelementStart(3, "span", 5);
|
|
748
|
+
i0.ɵɵtext(4);
|
|
749
|
+
i0.ɵɵelementEnd();
|
|
750
|
+
i0.ɵɵelementStart(5, "span", 6);
|
|
751
|
+
i0.ɵɵtext(6);
|
|
752
|
+
i0.ɵɵelementEnd();
|
|
753
|
+
i0.ɵɵconditionalCreate(7, RemoteBrowserSurfaceComponent_Conditional_7_Template, 3, 0, "span", 7);
|
|
754
|
+
i0.ɵɵconditionalCreate(8, RemoteBrowserSurfaceComponent_Conditional_8_Template, 2, 9, "button", 8);
|
|
755
|
+
i0.ɵɵelementEnd();
|
|
756
|
+
i0.ɵɵelementStart(9, "div", 9);
|
|
757
|
+
i0.ɵɵconditionalCreate(10, RemoteBrowserSurfaceComponent_Conditional_10_Template, 5, 6)(11, RemoteBrowserSurfaceComponent_Conditional_11_Template, 1, 1, "img", 10)(12, RemoteBrowserSurfaceComponent_Conditional_12_Template, 2, 0, "div", 11);
|
|
758
|
+
i0.ɵɵelementEnd()();
|
|
759
|
+
} if (rf & 2) {
|
|
760
|
+
i0.ɵɵadvance(2);
|
|
761
|
+
i0.ɵɵclassProp("rb-live--on", ctx.HasSnapshot);
|
|
762
|
+
i0.ɵɵadvance(2);
|
|
763
|
+
i0.ɵɵtextInterpolate(ctx.HasSnapshot ? "Live" : "Connecting\u2026");
|
|
764
|
+
i0.ɵɵadvance();
|
|
765
|
+
i0.ɵɵproperty("title", ctx.CurrentUrl || "");
|
|
766
|
+
i0.ɵɵadvance();
|
|
767
|
+
i0.ɵɵtextInterpolate(ctx.CurrentUrl || "No page loaded yet");
|
|
768
|
+
i0.ɵɵadvance();
|
|
769
|
+
i0.ɵɵconditional(ctx.CanTakeOver ? 7 : -1);
|
|
770
|
+
i0.ɵɵadvance();
|
|
771
|
+
i0.ɵɵconditional(ctx.AudioAvailable ? 8 : -1);
|
|
772
|
+
i0.ɵɵadvance(2);
|
|
773
|
+
i0.ɵɵconditional(ctx.Streaming ? 10 : ctx.ScreenshotDataUrl ? 11 : 12);
|
|
774
|
+
} }, dependencies: [CommonModule, SharedGenericModule, i1.LoadingComponent], styles: ["[_nghost-%COMP%] { display: block; height: 100%; }\n .rb-surface[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n }\n .rb-bar[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n .rb-url[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n \n\n\n cursor: none;\n outline: none;\n }\n .rb-screenshot--interactive[_ngcontent-%COMP%]: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 \n\n\n .rb-canvas-stack[_ngcontent-%COMP%] {\n position: relative;\n display: inline-flex;\n max-width: 100%;\n max-height: 100%;\n min-height: 0;\n }\n .rb-cursor-overlay[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n \n\n\n border: none;\n box-shadow: none;\n background: transparent;\n pointer-events: none;\n }\n .rb-driving[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .rb-hidden[_ngcontent-%COMP%] {\n display: none;\n }\n .rb-speaker[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n }\n .rb-speaker[_ngcontent-%COMP%]: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[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n }"], changeDetection: 0 });
|
|
775
|
+
}
|
|
776
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RemoteBrowserSurfaceComponent, [{
|
|
777
|
+
type: Component,
|
|
778
|
+
args: [{ standalone: true, selector: 'mj-realtime-remote-browser-surface', imports: [CommonModule, SharedGenericModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
779
|
+
<div class="rb-surface">
|
|
780
|
+
<div class="rb-bar">
|
|
781
|
+
<span class="rb-live" [class.rb-live--on]="HasSnapshot" aria-hidden="true"></span>
|
|
782
|
+
<span class="rb-live-label">{{ HasSnapshot ? 'Live' : 'Connecting…' }}</span>
|
|
783
|
+
<span class="rb-url" [title]="CurrentUrl || ''">{{ CurrentUrl || 'No page loaded yet' }}</span>
|
|
784
|
+
@if (CanTakeOver) {
|
|
785
|
+
<span class="rb-driving" title="Click and type to control the live page">
|
|
786
|
+
<i class="fa-solid fa-hand-pointer" aria-hidden="true"></i> You're driving
|
|
787
|
+
</span>
|
|
788
|
+
}
|
|
789
|
+
@if (AudioAvailable) {
|
|
790
|
+
<button
|
|
791
|
+
type="button"
|
|
792
|
+
class="rb-speaker"
|
|
793
|
+
[class.rb-speaker--muted]="AudioMuted"
|
|
794
|
+
[attr.aria-pressed]="!AudioMuted"
|
|
795
|
+
[attr.aria-label]="AudioMuted ? 'Unmute browser audio' : 'Mute browser audio'"
|
|
796
|
+
[title]="AudioMuted ? 'Unmute browser audio' : 'Mute browser audio'"
|
|
797
|
+
(click)="ToggleAudioMuted()"
|
|
798
|
+
>
|
|
799
|
+
<i class="fa-solid" [class.fa-volume-high]="!AudioMuted" [class.fa-volume-xmark]="AudioMuted" aria-hidden="true"></i>
|
|
800
|
+
</button>
|
|
801
|
+
}
|
|
802
|
+
</div>
|
|
803
|
+
<div class="rb-viewport">
|
|
804
|
+
@if (Streaming) {
|
|
805
|
+
<div class="rb-canvas-stack" [class.rb-hidden]="!HasFrame">
|
|
806
|
+
<canvas #frameCanvas class="rb-screenshot" [class.rb-screenshot--interactive]="CanTakeOver"></canvas>
|
|
807
|
+
@if (CanTakeOver) {
|
|
808
|
+
<!-- Synthetic cursor overlay: same internal resolution + CSS box as the frame canvas, so a
|
|
809
|
+
point drawn at viewport coords aligns exactly. CDP screencast frames don't include the OS
|
|
810
|
+
cursor, so we render the user's pointer locally for immediate, round-trip-free feedback. -->
|
|
811
|
+
<canvas #cursorCanvas class="rb-screenshot rb-cursor-overlay"></canvas>
|
|
812
|
+
}
|
|
813
|
+
</div>
|
|
814
|
+
@if (!HasFrame) {
|
|
815
|
+
<div class="rb-placeholder">
|
|
816
|
+
<mj-loading text="Waiting for the browser…" size="medium"></mj-loading>
|
|
817
|
+
</div>
|
|
818
|
+
}
|
|
819
|
+
} @else if (ScreenshotDataUrl) {
|
|
820
|
+
<img class="rb-screenshot" [src]="ScreenshotDataUrl" alt="Live view of the shared browser" />
|
|
821
|
+
} @else {
|
|
822
|
+
<div class="rb-placeholder">
|
|
823
|
+
<mj-loading text="Waiting for the browser…" size="medium"></mj-loading>
|
|
824
|
+
</div>
|
|
825
|
+
}
|
|
826
|
+
</div>
|
|
827
|
+
</div>
|
|
828
|
+
`, 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
|
+
}], null, { Fetch: [{
|
|
830
|
+
type: Input
|
|
831
|
+
}], Streaming: [{
|
|
832
|
+
type: Input
|
|
833
|
+
}], Interactive: [{
|
|
834
|
+
type: Input
|
|
835
|
+
}], HumanInput: [{
|
|
836
|
+
type: Output
|
|
837
|
+
}], AudioAvailable: [{
|
|
838
|
+
type: Input
|
|
839
|
+
}], AudioMuted: [{
|
|
840
|
+
type: Input
|
|
841
|
+
}], AudioMutedChange: [{
|
|
842
|
+
type: Output
|
|
843
|
+
}], frameCanvas: [{
|
|
844
|
+
type: ViewChild,
|
|
845
|
+
args: ['frameCanvas']
|
|
846
|
+
}], cursorCanvas: [{
|
|
847
|
+
type: ViewChild,
|
|
848
|
+
args: ['cursorCanvas']
|
|
849
|
+
}] }); })();
|
|
850
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RemoteBrowserSurfaceComponent, { className: "RemoteBrowserSurfaceComponent", filePath: "src/lib/components/realtime/remote-browser/remote-browser-surface.component.ts", lineNumber: 331 }); })();
|
|
851
|
+
//# sourceMappingURL=remote-browser-surface.component.js.map
|