@memberjunction/ng-conversations 6.2.0-edge.3 → 6.2.0-edge.5
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 +33 -7
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +1 -0
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/composer/ai-composer.component.d.ts +14 -2
- package/dist/lib/components/composer/ai-composer.component.d.ts.map +1 -1
- package/dist/lib/components/composer/ai-composer.component.js +35 -4
- package/dist/lib/components/composer/ai-composer.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts +1 -1
- package/dist/lib/components/conversation/conversation-agent-picker.component.js +3 -3
- package/dist/lib/components/conversation/conversation-agent-picker.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +651 -53
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +3002 -661
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/forks-popover.component.d.ts +37 -0
- package/dist/lib/components/conversation/forks-popover.component.d.ts.map +1 -0
- package/dist/lib/components/conversation/forks-popover.component.js +164 -0
- package/dist/lib/components/conversation/forks-popover.component.js.map +1 -0
- package/dist/lib/components/export/export-modal.component.d.ts +7 -2
- package/dist/lib/components/export/export-modal.component.d.ts.map +1 -1
- package/dist/lib/components/export/export-modal.component.js +16 -3
- package/dist/lib/components/export/export-modal.component.js.map +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.d.ts.map +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.js +11 -9
- package/dist/lib/components/message/conversation-message-rating.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +96 -11
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +253 -112
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +304 -16
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +2029 -363
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +101 -22
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +326 -55
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/realtime/avatar/realtime-avatar-channel.d.ts +34 -0
- package/dist/lib/components/realtime/avatar/realtime-avatar-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/avatar/realtime-avatar-channel.js +75 -0
- package/dist/lib/components/realtime/avatar/realtime-avatar-channel.js.map +1 -0
- package/dist/lib/components/realtime/avatar/realtime-avatar-surface.component.d.ts +80 -0
- package/dist/lib/components/realtime/avatar/realtime-avatar-surface.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/avatar/realtime-avatar-surface.component.js +202 -0
- package/dist/lib/components/realtime/avatar/realtime-avatar-surface.component.js.map +1 -0
- package/dist/lib/components/realtime/capture/realtime-camera-check-card.component.d.ts +45 -0
- package/dist/lib/components/realtime/capture/realtime-camera-check-card.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/capture/realtime-camera-check-card.component.js +123 -0
- package/dist/lib/components/realtime/capture/realtime-camera-check-card.component.js.map +1 -0
- package/dist/lib/components/realtime/capture/realtime-capture-channel.d.ts +90 -0
- package/dist/lib/components/realtime/capture/realtime-capture-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/capture/realtime-capture-channel.js +223 -0
- package/dist/lib/components/realtime/capture/realtime-capture-channel.js.map +1 -0
- package/dist/lib/components/realtime/capture/realtime-capture-model.d.ts +98 -0
- package/dist/lib/components/realtime/capture/realtime-capture-model.d.ts.map +1 -0
- package/dist/lib/components/realtime/capture/realtime-capture-model.js +161 -0
- package/dist/lib/components/realtime/capture/realtime-capture-model.js.map +1 -0
- package/dist/lib/components/realtime/capture/realtime-capture-surface.component.d.ts +48 -0
- package/dist/lib/components/realtime/capture/realtime-capture-surface.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/capture/realtime-capture-surface.component.js +205 -0
- package/dist/lib/components/realtime/capture/realtime-capture-surface.component.js.map +1 -0
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts +19 -2
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/client-context-channel.js +83 -9
- package/dist/lib/components/realtime/channels/client-context-channel.js.map +1 -1
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts +30 -9
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js +78 -14
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js.map +1 -1
- package/dist/lib/components/realtime/identity-verification/identity-verification-channel.d.ts +71 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-channel.js +259 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-channel.js.map +1 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-model.d.ts +129 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-model.d.ts.map +1 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-model.js +373 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-model.js.map +1 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-surface.component.d.ts +77 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-surface.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-surface.component.js +420 -0
- package/dist/lib/components/realtime/identity-verification/identity-verification-surface.component.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/component-contract.d.ts +83 -0
- package/dist/lib/components/realtime/interactive-component/component-contract.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/component-contract.js +292 -0
- package/dist/lib/components/realtime/interactive-component/component-contract.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/component-data-state.d.ts +39 -0
- package/dist/lib/components/realtime/interactive-component/component-data-state.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/component-data-state.js +152 -0
- package/dist/lib/components/realtime/interactive-component/component-data-state.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/component-instance-engine.d.ts +109 -0
- package/dist/lib/components/realtime/interactive-component/component-instance-engine.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/component-instance-engine.js +146 -0
- package/dist/lib/components/realtime/interactive-component/component-instance-engine.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/component-spec-content.d.ts +19 -0
- package/dist/lib/components/realtime/interactive-component/component-spec-content.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/component-spec-content.js +39 -0
- package/dist/lib/components/realtime/interactive-component/component-spec-content.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-channel.d.ts +183 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-channel.js +761 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-channel.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-config.d.ts +25 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-config.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-config.js +57 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-config.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-host.component.d.ts +40 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-host.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-host.component.js +97 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-host.component.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-types.d.ts +144 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-types.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-types.js +57 -0
- package/dist/lib/components/realtime/interactive-component/interactive-component-types.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/react-component-handle.d.ts +36 -0
- package/dist/lib/components/realtime/interactive-component/react-component-handle.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/react-component-handle.js +55 -0
- package/dist/lib/components/realtime/interactive-component/react-component-handle.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/realtime-interactive-component-surface.component.d.ts +55 -0
- package/dist/lib/components/realtime/interactive-component/realtime-interactive-component-surface.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/realtime-interactive-component-surface.component.js +210 -0
- package/dist/lib/components/realtime/interactive-component/realtime-interactive-component-surface.component.js.map +1 -0
- package/dist/lib/components/realtime/interactive-component/run-view-component-artifact-source.d.ts +32 -0
- package/dist/lib/components/realtime/interactive-component/run-view-component-artifact-source.d.ts.map +1 -0
- package/dist/lib/components/realtime/interactive-component/run-view-component-artifact-source.js +90 -0
- package/dist/lib/components/realtime/interactive-component/run-view-component-artifact-source.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel.d.ts +7 -0
- package/dist/lib/components/realtime/media/media-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/media/media-channel.js +28 -1
- package/dist/lib/components/realtime/media/media-channel.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +8 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.js +89 -71
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-orb.component.d.ts +21 -0
- package/dist/lib/components/realtime/realtime-agent-orb.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-orb.component.js +49 -0
- package/dist/lib/components/realtime/realtime-agent-orb.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +44 -11
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.js +162 -50
- package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts +8 -2
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-audio-visuals.js +14 -18
- package/dist/lib/components/realtime/realtime-audio-visuals.js.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.d.ts +42 -2
- package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.js +137 -102
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-perception-chip.component.d.ts +71 -0
- package/dist/lib/components/realtime/realtime-perception-chip.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-perception-chip.component.js +221 -0
- package/dist/lib/components/realtime/realtime-perception-chip.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +264 -22
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +851 -135
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-placement-prefs.d.ts +14 -0
- package/dist/lib/components/realtime/realtime-surface-placement-prefs.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-placement-prefs.js +14 -0
- package/dist/lib/components/realtime/realtime-surface-placement-prefs.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-stage.model.d.ts +74 -0
- package/dist/lib/components/realtime/realtime-surface-stage.model.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-stage.model.js +171 -0
- package/dist/lib/components/realtime/realtime-surface-stage.model.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +71 -13
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js +232 -73
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +5 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js +8 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.d.ts +12 -2
- package/dist/lib/components/realtime/realtime-ui-config.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.js +5 -1
- package/dist/lib/components/realtime/realtime-ui-config.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +10 -1
- 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 +42 -2
- 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 +20 -3
- 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 +44 -10
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +28 -44
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +76 -162
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -1
- package/dist/lib/components/search/search-panel.component.d.ts +2 -0
- package/dist/lib/components/search/search-panel.component.d.ts.map +1 -1
- package/dist/lib/components/search/search-panel.component.js +25 -12
- package/dist/lib/components/search/search-panel.component.js.map +1 -1
- package/dist/lib/components/sidebar/routines-section.component.js +1 -1
- package/dist/lib/components/sidebar/routines-section.component.js.map +1 -1
- package/dist/lib/components/task/tasks-full-view.component.d.ts +152 -31
- package/dist/lib/components/task/tasks-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/task/tasks-full-view.component.js +2227 -214
- package/dist/lib/components/task/tasks-full-view.component.js.map +1 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.js +4 -4
- package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +33 -32
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +72 -88
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +30 -29
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +27 -5
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/events/chat-events.d.ts +25 -0
- package/dist/lib/events/chat-events.d.ts.map +1 -1
- package/dist/lib/events/chat-events.js +36 -0
- package/dist/lib/events/chat-events.js.map +1 -1
- package/dist/lib/models/agent-turn.model.d.ts +5 -0
- package/dist/lib/models/agent-turn.model.d.ts.map +1 -1
- package/dist/lib/models/agent-turn.model.js.map +1 -1
- package/dist/lib/services/agent-state.service.d.ts +34 -8
- package/dist/lib/services/agent-state.service.d.ts.map +1 -1
- package/dist/lib/services/agent-state.service.js +90 -37
- package/dist/lib/services/agent-state.service.js.map +1 -1
- package/dist/lib/services/artifact-state.service.d.ts +0 -9
- package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-state.service.js +1 -32
- package/dist/lib/services/artifact-state.service.js.map +1 -1
- package/dist/lib/services/browser-realtime-media-host.d.ts +19 -7
- package/dist/lib/services/browser-realtime-media-host.d.ts.map +1 -1
- package/dist/lib/services/browser-realtime-media-host.js +21 -72
- package/dist/lib/services/browser-realtime-media-host.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +48 -16
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +110 -35
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-detail-window.store.d.ts +22 -4
- package/dist/lib/services/conversation-detail-window.store.d.ts.map +1 -1
- package/dist/lib/services/conversation-detail-window.store.js +50 -9
- package/dist/lib/services/conversation-detail-window.store.js.map +1 -1
- package/dist/lib/services/conversation-naming.d.ts +72 -22
- package/dist/lib/services/conversation-naming.d.ts.map +1 -1
- package/dist/lib/services/conversation-naming.js +93 -12
- package/dist/lib/services/conversation-naming.js.map +1 -1
- package/dist/lib/services/conversation-tasks-filter.d.ts +15 -0
- package/dist/lib/services/conversation-tasks-filter.d.ts.map +1 -0
- package/dist/lib/services/conversation-tasks-filter.js +69 -0
- package/dist/lib/services/conversation-tasks-filter.js.map +1 -0
- package/dist/lib/services/data-cache.service.d.ts +13 -5
- package/dist/lib/services/data-cache.service.d.ts.map +1 -1
- package/dist/lib/services/data-cache.service.js +39 -19
- package/dist/lib/services/data-cache.service.js.map +1 -1
- package/dist/lib/services/export.service.d.ts +26 -9
- package/dist/lib/services/export.service.d.ts.map +1 -1
- package/dist/lib/services/export.service.js +61 -18
- package/dist/lib/services/export.service.js.map +1 -1
- package/dist/lib/services/realtime-audio-recorder.d.ts +115 -79
- package/dist/lib/services/realtime-audio-recorder.d.ts.map +1 -1
- package/dist/lib/services/realtime-audio-recorder.js +273 -186
- package/dist/lib/services/realtime-audio-recorder.js.map +1 -1
- package/dist/lib/services/realtime-pairing.d.ts +10 -5
- package/dist/lib/services/realtime-pairing.d.ts.map +1 -1
- package/dist/lib/services/realtime-pairing.js +15 -6
- package/dist/lib/services/realtime-pairing.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts +31 -2
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +34 -2
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/dist/lib/services/recording/libopus-wasm-binary.d.ts +3 -0
- package/dist/lib/services/recording/libopus-wasm-binary.d.ts.map +1 -0
- package/dist/lib/services/recording/libopus-wasm-binary.js +489 -0
- package/dist/lib/services/recording/libopus-wasm-binary.js.map +1 -0
- package/dist/lib/services/recording/libopus-worker-source.d.ts +48 -0
- package/dist/lib/services/recording/libopus-worker-source.d.ts.map +1 -0
- package/dist/lib/services/recording/libopus-worker-source.js +170 -0
- package/dist/lib/services/recording/libopus-worker-source.js.map +1 -0
- package/dist/lib/services/recording/linear-resampler.d.ts +45 -0
- package/dist/lib/services/recording/linear-resampler.d.ts.map +1 -0
- package/dist/lib/services/recording/linear-resampler.js +85 -0
- package/dist/lib/services/recording/linear-resampler.js.map +1 -0
- package/dist/lib/services/recording/opus-packet-encoder.d.ts +50 -0
- package/dist/lib/services/recording/opus-packet-encoder.d.ts.map +1 -0
- package/dist/lib/services/recording/opus-packet-encoder.js +41 -0
- package/dist/lib/services/recording/opus-packet-encoder.js.map +1 -0
- package/dist/lib/services/recording/wasm-opus-encoder.d.ts +86 -0
- package/dist/lib/services/recording/wasm-opus-encoder.d.ts.map +1 -0
- package/dist/lib/services/recording/wasm-opus-encoder.js +232 -0
- package/dist/lib/services/recording/wasm-opus-encoder.js.map +1 -0
- package/dist/lib/services/recording/webcodecs-opus-encoder.d.ts +39 -0
- package/dist/lib/services/recording/webcodecs-opus-encoder.d.ts.map +1 -0
- package/dist/lib/services/recording/webcodecs-opus-encoder.js +101 -0
- package/dist/lib/services/recording/webcodecs-opus-encoder.js.map +1 -0
- package/dist/lib/services/search.service.d.ts +7 -2
- package/dist/lib/services/search.service.d.ts.map +1 -1
- package/dist/lib/services/search.service.js +16 -20
- package/dist/lib/services/search.service.js.map +1 -1
- package/dist/lib/utils/conversation-branching.d.ts +4 -0
- package/dist/lib/utils/conversation-branching.d.ts.map +1 -0
- package/dist/lib/utils/conversation-branching.js +5 -0
- package/dist/lib/utils/conversation-branching.js.map +1 -0
- package/dist/lib/utils/conversation-forks.d.ts +315 -0
- package/dist/lib/utils/conversation-forks.d.ts.map +1 -0
- package/dist/lib/utils/conversation-forks.js +456 -0
- package/dist/lib/utils/conversation-forks.js.map +1 -0
- package/dist/lib/utils/distinct-artifacts.d.ts +9 -3
- package/dist/lib/utils/distinct-artifacts.d.ts.map +1 -1
- package/dist/lib/utils/distinct-artifacts.js +24 -5
- package/dist/lib/utils/distinct-artifacts.js.map +1 -1
- package/dist/public-api.d.ts +23 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +30 -1
- package/dist/public-api.js.map +1 -1
- package/package.json +37 -33
- package/dist/lib/components/thread/thread-panel.component.d.ts +0 -126
- package/dist/lib/components/thread/thread-panel.component.d.ts.map +0 -1
- package/dist/lib/components/thread/thread-panel.component.js +0 -440
- package/dist/lib/components/thread/thread-panel.component.js.map +0 -1
- package/dist/lib/services/realtime-pcm-wav.d.ts +0 -79
- package/dist/lib/services/realtime-pcm-wav.d.ts.map +0 -1
- package/dist/lib/services/realtime-pcm-wav.js +0 -213
- package/dist/lib/services/realtime-pcm-wav.js.map +0 -1
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { LIBOPUS_WORKER_SOURCE } from './libopus-worker-source';
|
|
2
|
+
/**
|
|
3
|
+
* How long {@link WasmOpusEncoder.Flush} waits for the worker's `flushed` reply. The runtime bounds
|
|
4
|
+
* the whole recorder Stop at 2 s, so this stays below that and leaves room for closing up.
|
|
5
|
+
*/
|
|
6
|
+
export const WASM_OPUS_FLUSH_TIMEOUT_MS = 1500;
|
|
7
|
+
/**
|
|
8
|
+
* How long {@link WasmOpusEncoder.Start} waits for the worker to instantiate libopus. Instantiating
|
|
9
|
+
* the ~330 KB module takes milliseconds; a worker that says nothing for this long is not running
|
|
10
|
+
* (a Content-Security-Policy without `worker-src blob:` or `'wasm-unsafe-eval'`, for instance).
|
|
11
|
+
*/
|
|
12
|
+
export const WASM_OPUS_READY_TIMEOUT_MS = 5000;
|
|
13
|
+
/** Memoized load of the embedded wasm module, so a page only parses the 440 KB string once. */
|
|
14
|
+
let wasmBase64Load = null;
|
|
15
|
+
function loadLibopusWasmBase64() {
|
|
16
|
+
if (!wasmBase64Load) {
|
|
17
|
+
// Dynamic import, category 3 (genuine bundle-size deferral, .claude/rules/typescript-style.md):
|
|
18
|
+
// the embedded libopus build is ~440 KB of base64 and is needed only by browsers whose WebCodecs
|
|
19
|
+
// cannot encode Opus. A static import would put it in every Explorer bundle that loads this
|
|
20
|
+
// library. The module is local to this package, so no dependency is hidden from package.json.
|
|
21
|
+
wasmBase64Load = import('./libopus-wasm-binary').then((module) => module.LIBOPUS_WASM_BASE64);
|
|
22
|
+
wasmBase64Load.catch(() => {
|
|
23
|
+
// Let a later call retry (e.g. a chunk load that failed on a flaky network); the caller logs.
|
|
24
|
+
wasmBase64Load = null;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return wasmBase64Load;
|
|
28
|
+
}
|
|
29
|
+
/** Decodes base64 into a fresh ArrayBuffer (one the worker can take ownership of). */
|
|
30
|
+
export function DecodeBase64ToArrayBuffer(base64) {
|
|
31
|
+
const binary = atob(base64);
|
|
32
|
+
const bytes = new Uint8Array(binary.length);
|
|
33
|
+
for (let i = 0; i < binary.length; i++) {
|
|
34
|
+
bytes[i] = binary.charCodeAt(i);
|
|
35
|
+
}
|
|
36
|
+
return bytes.buffer;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Opus encoding through libopus compiled to WebAssembly, running in a dedicated Worker so encoding
|
|
40
|
+
* never competes with the call's UI or audio on the main thread. The fallback for browsers whose
|
|
41
|
+
* WebCodecs cannot encode Opus.
|
|
42
|
+
*
|
|
43
|
+
* The worker does the 960-sample framing itself (see {@link LIBOPUS_WORKER_SOURCE}); this class only
|
|
44
|
+
* forwards audio, collects the packets the worker posts back, and turns `flush` into a promise.
|
|
45
|
+
*/
|
|
46
|
+
export class WasmOpusEncoder {
|
|
47
|
+
port;
|
|
48
|
+
onClose;
|
|
49
|
+
Kind = 'wasm';
|
|
50
|
+
packets = [];
|
|
51
|
+
flushWaiters = new Map();
|
|
52
|
+
nextFlushId = 1;
|
|
53
|
+
closed = false;
|
|
54
|
+
/** Whether the worker has reported `ready`, or the error it reported instead. */
|
|
55
|
+
startState = 'starting';
|
|
56
|
+
/** Notified once when {@link startState} leaves `starting` (null = ready, else the error). */
|
|
57
|
+
startWaiter = null;
|
|
58
|
+
/**
|
|
59
|
+
* @param port channel to a worker running {@link LIBOPUS_WORKER_SOURCE}
|
|
60
|
+
* @param wasmBytes the libopus module; ownership moves to the worker
|
|
61
|
+
* @param onClose extra cleanup after the worker stops (the Blob URL revoke)
|
|
62
|
+
*/
|
|
63
|
+
constructor(port, wasmBytes, onClose = () => undefined) {
|
|
64
|
+
this.port = port;
|
|
65
|
+
this.onClose = onClose;
|
|
66
|
+
port.OnReply = (reply) => this.onReply(reply);
|
|
67
|
+
port.Post({ Type: 'init', Wasm: wasmBytes }, [wasmBytes]);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Loads the embedded wasm and starts the worker from a Blob URL. Rejects if either fails, or if the
|
|
71
|
+
* worker never gets libopus running (see {@link Start}).
|
|
72
|
+
*/
|
|
73
|
+
static async Create() {
|
|
74
|
+
const wasmBytes = DecodeBase64ToArrayBuffer(await loadLibopusWasmBase64());
|
|
75
|
+
const url = URL.createObjectURL(new Blob([LIBOPUS_WORKER_SOURCE], { type: 'application/javascript' }));
|
|
76
|
+
let worker;
|
|
77
|
+
try {
|
|
78
|
+
worker = new Worker(url);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
URL.revokeObjectURL(url);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
return WasmOpusEncoder.Start(new BrowserWorkerPort(worker), wasmBytes, () => URL.revokeObjectURL(url));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Creates the encoder on `port` and resolves once the worker reports libopus ready. Rejects — after
|
|
88
|
+
* closing the encoder — when the worker reports an init error or says nothing for
|
|
89
|
+
* {@link WASM_OPUS_READY_TIMEOUT_MS}: an encoder that can never produce a packet must fail selection,
|
|
90
|
+
* so the recorder turns recording off visibly instead of "recording" while producing nothing.
|
|
91
|
+
*/
|
|
92
|
+
static async Start(port, wasmBytes, onClose = () => undefined) {
|
|
93
|
+
const encoder = new WasmOpusEncoder(port, wasmBytes, onClose);
|
|
94
|
+
try {
|
|
95
|
+
await encoder.waitUntilStarted();
|
|
96
|
+
return encoder;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
encoder.Close();
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
Encode(pcm48k) {
|
|
104
|
+
if (this.closed || pcm48k.length === 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const copy = pcm48k.slice();
|
|
108
|
+
this.port.Post({ Type: 'encode', Pcm: copy }, [copy.buffer]);
|
|
109
|
+
}
|
|
110
|
+
Flush() {
|
|
111
|
+
if (this.closed) {
|
|
112
|
+
return Promise.resolve();
|
|
113
|
+
}
|
|
114
|
+
const id = this.nextFlushId++;
|
|
115
|
+
return new Promise((resolve) => {
|
|
116
|
+
const timer = setTimeout(() => {
|
|
117
|
+
this.flushWaiters.delete(id);
|
|
118
|
+
console.warn(`[WasmOpusEncoder] Worker did not confirm flush within ${WASM_OPUS_FLUSH_TIMEOUT_MS} ms — the final packets may be missing.`);
|
|
119
|
+
resolve();
|
|
120
|
+
}, WASM_OPUS_FLUSH_TIMEOUT_MS);
|
|
121
|
+
this.flushWaiters.set(id, () => {
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
resolve();
|
|
124
|
+
});
|
|
125
|
+
this.port.Post({ Type: 'flush', ID: id }, []);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
TakePackets() {
|
|
129
|
+
const packets = this.packets;
|
|
130
|
+
this.packets = [];
|
|
131
|
+
return packets;
|
|
132
|
+
}
|
|
133
|
+
Close() {
|
|
134
|
+
if (this.closed) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.closed = true;
|
|
138
|
+
this.port.OnReply = null;
|
|
139
|
+
this.port.Terminate();
|
|
140
|
+
this.onClose();
|
|
141
|
+
// A flush still waiting can never be answered now; release it instead of leaving it to the timeout.
|
|
142
|
+
for (const release of this.flushWaiters.values()) {
|
|
143
|
+
release();
|
|
144
|
+
}
|
|
145
|
+
this.flushWaiters.clear();
|
|
146
|
+
}
|
|
147
|
+
/** Records the worker's start outcome (only the first one counts) and wakes {@link waitUntilStarted}. */
|
|
148
|
+
settleStart(error) {
|
|
149
|
+
if (this.startState !== 'starting') {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.startState = error === null ? 'ready' : { Error: error };
|
|
153
|
+
const waiter = this.startWaiter;
|
|
154
|
+
this.startWaiter = null;
|
|
155
|
+
waiter?.(error);
|
|
156
|
+
}
|
|
157
|
+
/** Resolves once the worker is ready; rejects with its init error or after the start timeout. */
|
|
158
|
+
waitUntilStarted() {
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
const state = this.startState;
|
|
161
|
+
if (state === 'ready') {
|
|
162
|
+
resolve();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (state !== 'starting') {
|
|
166
|
+
reject(new Error(state.Error));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const timer = setTimeout(() => {
|
|
170
|
+
this.startWaiter = null;
|
|
171
|
+
reject(new Error(`libopus worker did not start within ${WASM_OPUS_READY_TIMEOUT_MS} ms`));
|
|
172
|
+
}, WASM_OPUS_READY_TIMEOUT_MS);
|
|
173
|
+
this.startWaiter = (error) => {
|
|
174
|
+
clearTimeout(timer);
|
|
175
|
+
if (error === null) {
|
|
176
|
+
resolve();
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
reject(new Error(error));
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
onReply(reply) {
|
|
185
|
+
switch (reply.Type) {
|
|
186
|
+
case 'packets':
|
|
187
|
+
for (const buffer of reply.Packets) {
|
|
188
|
+
this.packets.push(new Uint8Array(buffer));
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
case 'flushed': {
|
|
192
|
+
const release = this.flushWaiters.get(reply.ID);
|
|
193
|
+
this.flushWaiters.delete(reply.ID);
|
|
194
|
+
release?.();
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
case 'error':
|
|
198
|
+
console.warn(`[WasmOpusEncoder] ${reply.Message} — the rest of this call is not recorded.`);
|
|
199
|
+
this.settleStart(reply.Message);
|
|
200
|
+
break;
|
|
201
|
+
case 'ready':
|
|
202
|
+
this.settleStart(null);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/** Adapts a DOM `Worker` to {@link OpusWorkerPort}. */
|
|
208
|
+
export class BrowserWorkerPort {
|
|
209
|
+
worker;
|
|
210
|
+
OnReply = null;
|
|
211
|
+
constructor(worker) {
|
|
212
|
+
this.worker = worker;
|
|
213
|
+
worker.onmessage = (event) => this.OnReply?.(event.data);
|
|
214
|
+
// A worker that fails to load or throws (a Content-Security-Policy without `worker-src blob:`, for
|
|
215
|
+
// instance) reports it here, not through a message. Surface it as an error reply, so a starting
|
|
216
|
+
// encoder fails at once instead of after its start timeout.
|
|
217
|
+
worker.onerror = (event) => {
|
|
218
|
+
const message = `Encoder worker error: ${event.message}`;
|
|
219
|
+
console.warn(`[WasmOpusEncoder] ${message}`);
|
|
220
|
+
this.OnReply?.({ Type: 'error', Message: message });
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
Post(message, transfer) {
|
|
224
|
+
this.worker.postMessage(message, transfer);
|
|
225
|
+
}
|
|
226
|
+
Terminate() {
|
|
227
|
+
this.worker.onmessage = null;
|
|
228
|
+
this.worker.onerror = null;
|
|
229
|
+
this.worker.terminate();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=wasm-opus-encoder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm-opus-encoder.js","sourceRoot":"","sources":["../../../../src/lib/services/recording/wasm-opus-encoder.ts"],"names":[],"mappings":"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","sourcesContent":["import { LIBOPUS_WORKER_SOURCE, OpusWorkerReply, OpusWorkerRequest } from './libopus-worker-source';\nimport type { IOpusPacketEncoder } from './opus-packet-encoder';\n\n/**\n * How long {@link WasmOpusEncoder.Flush} waits for the worker's `flushed` reply. The runtime bounds\n * the whole recorder Stop at 2 s, so this stays below that and leaves room for closing up.\n */\nexport const WASM_OPUS_FLUSH_TIMEOUT_MS = 1500;\n\n/**\n * How long {@link WasmOpusEncoder.Start} waits for the worker to instantiate libopus. Instantiating\n * the ~330 KB module takes milliseconds; a worker that says nothing for this long is not running\n * (a Content-Security-Policy without `worker-src blob:` or `'wasm-unsafe-eval'`, for instance).\n */\nexport const WASM_OPUS_READY_TIMEOUT_MS = 5000;\n\n/** The two-way channel to the encoder worker. A real `Worker` is adapted to it; tests run the worker source in-process. */\nexport interface OpusWorkerPort {\n /** Posts a request, transferring the listed buffers. */\n Post(message: OpusWorkerRequest, transfer: Transferable[]): void;\n /** Receives every reply the worker posts. */\n OnReply: ((reply: OpusWorkerReply) => void) | null;\n /** Stops the worker. */\n Terminate(): void;\n}\n\n/** Memoized load of the embedded wasm module, so a page only parses the 440 KB string once. */\nlet wasmBase64Load: Promise<string> | null = null;\n\nfunction loadLibopusWasmBase64(): Promise<string> {\n if (!wasmBase64Load) {\n // Dynamic import, category 3 (genuine bundle-size deferral, .claude/rules/typescript-style.md):\n // the embedded libopus build is ~440 KB of base64 and is needed only by browsers whose WebCodecs\n // cannot encode Opus. A static import would put it in every Explorer bundle that loads this\n // library. The module is local to this package, so no dependency is hidden from package.json.\n wasmBase64Load = import('./libopus-wasm-binary').then((module) => module.LIBOPUS_WASM_BASE64);\n wasmBase64Load.catch(() => {\n // Let a later call retry (e.g. a chunk load that failed on a flaky network); the caller logs.\n wasmBase64Load = null;\n });\n }\n return wasmBase64Load;\n}\n\n/** Decodes base64 into a fresh ArrayBuffer (one the worker can take ownership of). */\nexport function DecodeBase64ToArrayBuffer(base64: string): ArrayBuffer {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes.buffer;\n}\n\n/**\n * Opus encoding through libopus compiled to WebAssembly, running in a dedicated Worker so encoding\n * never competes with the call's UI or audio on the main thread. The fallback for browsers whose\n * WebCodecs cannot encode Opus.\n *\n * The worker does the 960-sample framing itself (see {@link LIBOPUS_WORKER_SOURCE}); this class only\n * forwards audio, collects the packets the worker posts back, and turns `flush` into a promise.\n */\nexport class WasmOpusEncoder implements IOpusPacketEncoder {\n public readonly Kind = 'wasm';\n private packets: Uint8Array[] = [];\n private readonly flushWaiters = new Map<number, () => void>();\n private nextFlushId = 1;\n private closed = false;\n /** Whether the worker has reported `ready`, or the error it reported instead. */\n private startState: 'starting' | 'ready' | { Error: string } = 'starting';\n /** Notified once when {@link startState} leaves `starting` (null = ready, else the error). */\n private startWaiter: ((error: string | null) => void) | null = null;\n\n /**\n * @param port channel to a worker running {@link LIBOPUS_WORKER_SOURCE}\n * @param wasmBytes the libopus module; ownership moves to the worker\n * @param onClose extra cleanup after the worker stops (the Blob URL revoke)\n */\n constructor(private readonly port: OpusWorkerPort, wasmBytes: ArrayBuffer, private readonly onClose: () => void = () => undefined) {\n port.OnReply = (reply) => this.onReply(reply);\n port.Post({ Type: 'init', Wasm: wasmBytes }, [wasmBytes]);\n }\n\n /**\n * Loads the embedded wasm and starts the worker from a Blob URL. Rejects if either fails, or if the\n * worker never gets libopus running (see {@link Start}).\n */\n public static async Create(): Promise<WasmOpusEncoder> {\n const wasmBytes = DecodeBase64ToArrayBuffer(await loadLibopusWasmBase64());\n const url = URL.createObjectURL(new Blob([LIBOPUS_WORKER_SOURCE], { type: 'application/javascript' }));\n let worker: Worker;\n try {\n worker = new Worker(url);\n } catch (error) {\n URL.revokeObjectURL(url);\n throw error;\n }\n return WasmOpusEncoder.Start(new BrowserWorkerPort(worker), wasmBytes, () => URL.revokeObjectURL(url));\n }\n\n /**\n * Creates the encoder on `port` and resolves once the worker reports libopus ready. Rejects — after\n * closing the encoder — when the worker reports an init error or says nothing for\n * {@link WASM_OPUS_READY_TIMEOUT_MS}: an encoder that can never produce a packet must fail selection,\n * so the recorder turns recording off visibly instead of \"recording\" while producing nothing.\n */\n public static async Start(port: OpusWorkerPort, wasmBytes: ArrayBuffer, onClose: () => void = () => undefined): Promise<WasmOpusEncoder> {\n const encoder = new WasmOpusEncoder(port, wasmBytes, onClose);\n try {\n await encoder.waitUntilStarted();\n return encoder;\n } catch (error) {\n encoder.Close();\n throw error;\n }\n }\n\n public Encode(pcm48k: Float32Array): void {\n if (this.closed || pcm48k.length === 0) {\n return;\n }\n const copy = pcm48k.slice();\n this.port.Post({ Type: 'encode', Pcm: copy }, [copy.buffer]);\n }\n\n public Flush(): Promise<void> {\n if (this.closed) {\n return Promise.resolve();\n }\n const id = this.nextFlushId++;\n return new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n this.flushWaiters.delete(id);\n console.warn(`[WasmOpusEncoder] Worker did not confirm flush within ${WASM_OPUS_FLUSH_TIMEOUT_MS} ms — the final packets may be missing.`);\n resolve();\n }, WASM_OPUS_FLUSH_TIMEOUT_MS);\n this.flushWaiters.set(id, () => {\n clearTimeout(timer);\n resolve();\n });\n this.port.Post({ Type: 'flush', ID: id }, []);\n });\n }\n\n public TakePackets(): Uint8Array[] {\n const packets = this.packets;\n this.packets = [];\n return packets;\n }\n\n public Close(): void {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.port.OnReply = null;\n this.port.Terminate();\n this.onClose();\n // A flush still waiting can never be answered now; release it instead of leaving it to the timeout.\n for (const release of this.flushWaiters.values()) {\n release();\n }\n this.flushWaiters.clear();\n }\n\n /** Records the worker's start outcome (only the first one counts) and wakes {@link waitUntilStarted}. */\n private settleStart(error: string | null): void {\n if (this.startState !== 'starting') {\n return;\n }\n this.startState = error === null ? 'ready' : { Error: error };\n const waiter = this.startWaiter;\n this.startWaiter = null;\n waiter?.(error);\n }\n\n /** Resolves once the worker is ready; rejects with its init error or after the start timeout. */\n private waitUntilStarted(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const state = this.startState;\n if (state === 'ready') {\n resolve();\n return;\n }\n if (state !== 'starting') {\n reject(new Error(state.Error));\n return;\n }\n const timer = setTimeout(() => {\n this.startWaiter = null;\n reject(new Error(`libopus worker did not start within ${WASM_OPUS_READY_TIMEOUT_MS} ms`));\n }, WASM_OPUS_READY_TIMEOUT_MS);\n this.startWaiter = (error) => {\n clearTimeout(timer);\n if (error === null) {\n resolve();\n } else {\n reject(new Error(error));\n }\n };\n });\n }\n\n private onReply(reply: OpusWorkerReply): void {\n switch (reply.Type) {\n case 'packets':\n for (const buffer of reply.Packets) {\n this.packets.push(new Uint8Array(buffer));\n }\n break;\n case 'flushed': {\n const release = this.flushWaiters.get(reply.ID);\n this.flushWaiters.delete(reply.ID);\n release?.();\n break;\n }\n case 'error':\n console.warn(`[WasmOpusEncoder] ${reply.Message} — the rest of this call is not recorded.`);\n this.settleStart(reply.Message);\n break;\n case 'ready':\n this.settleStart(null);\n break;\n }\n }\n}\n\n/** The slice of a DOM `Worker` {@link BrowserWorkerPort} drives (the real `Worker` satisfies it). */\nexport type WorkerLike = Pick<Worker, 'postMessage' | 'terminate'> & {\n // case-violation-ok-legacy-back-compat: mirrors the DOM Worker handler properties so a real Worker satisfies this type\n onmessage: ((event: MessageEvent<OpusWorkerReply>) => void) | null;\n // case-violation-ok-legacy-back-compat: mirrors the DOM Worker handler properties so a real Worker satisfies this type\n onerror: ((event: ErrorEvent) => void) | null;\n};\n\n/** Adapts a DOM `Worker` to {@link OpusWorkerPort}. */\nexport class BrowserWorkerPort implements OpusWorkerPort {\n public OnReply: ((reply: OpusWorkerReply) => void) | null = null;\n\n constructor(private readonly worker: WorkerLike) {\n worker.onmessage = (event: MessageEvent<OpusWorkerReply>) => this.OnReply?.(event.data);\n // A worker that fails to load or throws (a Content-Security-Policy without `worker-src blob:`, for\n // instance) reports it here, not through a message. Surface it as an error reply, so a starting\n // encoder fails at once instead of after its start timeout.\n worker.onerror = (event: ErrorEvent) => {\n const message = `Encoder worker error: ${event.message}`;\n console.warn(`[WasmOpusEncoder] ${message}`);\n this.OnReply?.({ Type: 'error', Message: message });\n };\n }\n\n public Post(message: OpusWorkerRequest, transfer: Transferable[]): void {\n this.worker.postMessage(message, transfer);\n }\n\n public Terminate(): void {\n this.worker.onmessage = null;\n this.worker.onerror = null;\n this.worker.terminate();\n }\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IOpusPacketEncoder } from './opus-packet-encoder';
|
|
2
|
+
/** The recording's Opus configuration for WebCodecs: mono 48 kHz, 32 kbps, 20 ms frames. */
|
|
3
|
+
export declare const WEBCODECS_OPUS_CONFIG: AudioEncoderConfig;
|
|
4
|
+
/** The slice of a WebCodecs `AudioEncoder` instance this wrapper drives. */
|
|
5
|
+
export type AudioEncoderLike = Pick<AudioEncoder, 'configure' | 'encode' | 'flush' | 'close' | 'state'>;
|
|
6
|
+
/** The WebCodecs `AudioEncoder` constructor shape (the real global satisfies it). */
|
|
7
|
+
export interface AudioEncoderConstructor {
|
|
8
|
+
new (init: AudioEncoderInit): AudioEncoderLike;
|
|
9
|
+
isConfigSupported(config: AudioEncoderConfig): Promise<AudioEncoderSupport>;
|
|
10
|
+
}
|
|
11
|
+
/** The WebCodecs `AudioData` constructor shape (the real global satisfies it). */
|
|
12
|
+
export interface AudioDataConstructor {
|
|
13
|
+
new (init: AudioDataInit): AudioData;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Opus encoding through the browser's native WebCodecs `AudioEncoder`.
|
|
17
|
+
*
|
|
18
|
+
* Each 48 kHz block becomes one `AudioData` whose timestamp comes from the running sample count
|
|
19
|
+
* (never the wall clock), so the encoder sees one gap-free stream. The encoder emits one
|
|
20
|
+
* `EncodedAudioChunk` per 20 ms packet; each is copied out immediately because a chunk's bytes are
|
|
21
|
+
* only guaranteed while the callback runs.
|
|
22
|
+
*/
|
|
23
|
+
export declare class WebCodecsOpusEncoder implements IOpusPacketEncoder {
|
|
24
|
+
private readonly audioData;
|
|
25
|
+
readonly Kind = "webcodecs";
|
|
26
|
+
private readonly encoder;
|
|
27
|
+
private packets;
|
|
28
|
+
private samplesFed;
|
|
29
|
+
/** Set by the encoder's error callback or a throwing encode: later input is dropped, packets kept. */
|
|
30
|
+
private failed;
|
|
31
|
+
private closed;
|
|
32
|
+
/** Creates and configures the encoder. Throws if `configure` does (the selector falls back to WASM). */
|
|
33
|
+
constructor(audioEncoder: AudioEncoderConstructor, audioData: AudioDataConstructor);
|
|
34
|
+
Encode(pcm48k: Float32Array): void;
|
|
35
|
+
Flush(): Promise<void>;
|
|
36
|
+
TakePackets(): Uint8Array[];
|
|
37
|
+
Close(): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=webcodecs-opus-encoder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webcodecs-opus-encoder.d.ts","sourceRoot":"","sources":["../../../../src/lib/services/recording/webcodecs-opus-encoder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,4FAA4F;AAC5F,eAAO,MAAM,qBAAqB,EAAE,kBAMnC,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,CAAC;AAExG,qFAAqF;AACrF,MAAM,WAAW,uBAAuB;IACpC,KAAK,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IAE/C,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC/E;AAED,kFAAkF;AAClF,MAAM,WAAW,oBAAoB;IACjC,KAAK,IAAI,EAAE,aAAa,GAAG,SAAS,CAAC;CACxC;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,kBAAkB;IAUR,OAAO,CAAC,QAAQ,CAAC,SAAS;IAT7E,SAAgB,IAAI,eAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,UAAU,CAAK;IACvB,sGAAsG;IACtG,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IAEvB,wGAAwG;gBAC5F,YAAY,EAAE,uBAAuB,EAAmB,SAAS,EAAE,oBAAoB;IAgB5F,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAwB5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,WAAW,IAAI,UAAU,EAAE;IAM3B,KAAK,IAAI,IAAI;CAcvB"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { RECORDING_SAMPLE_RATE } from '@memberjunction/ai-core-plus';
|
|
2
|
+
/** The recording's Opus configuration for WebCodecs: mono 48 kHz, 32 kbps, 20 ms frames. */
|
|
3
|
+
export const WEBCODECS_OPUS_CONFIG = {
|
|
4
|
+
codec: 'opus',
|
|
5
|
+
sampleRate: RECORDING_SAMPLE_RATE,
|
|
6
|
+
numberOfChannels: 1,
|
|
7
|
+
bitrate: 32000,
|
|
8
|
+
opus: { frameDuration: 20000 },
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Opus encoding through the browser's native WebCodecs `AudioEncoder`.
|
|
12
|
+
*
|
|
13
|
+
* Each 48 kHz block becomes one `AudioData` whose timestamp comes from the running sample count
|
|
14
|
+
* (never the wall clock), so the encoder sees one gap-free stream. The encoder emits one
|
|
15
|
+
* `EncodedAudioChunk` per 20 ms packet; each is copied out immediately because a chunk's bytes are
|
|
16
|
+
* only guaranteed while the callback runs.
|
|
17
|
+
*/
|
|
18
|
+
export class WebCodecsOpusEncoder {
|
|
19
|
+
audioData;
|
|
20
|
+
Kind = 'webcodecs';
|
|
21
|
+
encoder;
|
|
22
|
+
packets = [];
|
|
23
|
+
samplesFed = 0;
|
|
24
|
+
/** Set by the encoder's error callback or a throwing encode: later input is dropped, packets kept. */
|
|
25
|
+
failed = false;
|
|
26
|
+
closed = false;
|
|
27
|
+
/** Creates and configures the encoder. Throws if `configure` does (the selector falls back to WASM). */
|
|
28
|
+
constructor(audioEncoder, audioData) {
|
|
29
|
+
this.audioData = audioData;
|
|
30
|
+
this.encoder = new audioEncoder({
|
|
31
|
+
output: (chunk) => {
|
|
32
|
+
const bytes = new Uint8Array(chunk.byteLength);
|
|
33
|
+
chunk.copyTo(bytes);
|
|
34
|
+
this.packets.push(bytes);
|
|
35
|
+
},
|
|
36
|
+
error: (error) => {
|
|
37
|
+
// The recorder keeps the packets it already has; a broken encoder must never end the call.
|
|
38
|
+
console.warn(`[WebCodecsOpusEncoder] Encoder failed after ${this.samplesFed} samples — the rest of this call is not recorded:`, error);
|
|
39
|
+
this.failed = true;
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
this.encoder.configure(WEBCODECS_OPUS_CONFIG);
|
|
43
|
+
}
|
|
44
|
+
Encode(pcm48k) {
|
|
45
|
+
if (this.failed || this.closed || pcm48k.length === 0) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const data = new this.audioData({
|
|
49
|
+
format: 'f32',
|
|
50
|
+
sampleRate: RECORDING_SAMPLE_RATE,
|
|
51
|
+
numberOfFrames: pcm48k.length,
|
|
52
|
+
numberOfChannels: 1,
|
|
53
|
+
timestamp: Math.round((this.samplesFed * 1_000_000) / RECORDING_SAMPLE_RATE),
|
|
54
|
+
// AudioData needs an ArrayBuffer-backed view (not a SharedArrayBuffer one); it copies anyway.
|
|
55
|
+
data: pcm48k.buffer instanceof ArrayBuffer ? pcm48k : new Float32Array(pcm48k),
|
|
56
|
+
});
|
|
57
|
+
this.samplesFed += pcm48k.length;
|
|
58
|
+
try {
|
|
59
|
+
this.encoder.encode(data);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.warn(`[WebCodecsOpusEncoder] encode() threw at sample ${this.samplesFed} — the rest of this call is not recorded:`, error);
|
|
63
|
+
this.failed = true;
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
data.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async Flush() {
|
|
70
|
+
if (this.failed || this.closed) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
await this.encoder.flush();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.warn(`[WebCodecsOpusEncoder] flush() failed after ${this.samplesFed} samples — the final packets may be missing:`, error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
TakePackets() {
|
|
81
|
+
const packets = this.packets;
|
|
82
|
+
this.packets = [];
|
|
83
|
+
return packets;
|
|
84
|
+
}
|
|
85
|
+
Close() {
|
|
86
|
+
if (this.closed) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.closed = true;
|
|
90
|
+
if (this.encoder.state === 'closed') {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
this.encoder.close();
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.warn('[WebCodecsOpusEncoder] close() failed:', error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=webcodecs-opus-encoder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webcodecs-opus-encoder.js","sourceRoot":"","sources":["../../../../src/lib/services/recording/webcodecs-opus-encoder.ts"],"names":[],"mappings":"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","sourcesContent":["import { RECORDING_SAMPLE_RATE } from '@memberjunction/ai-core-plus';\nimport type { IOpusPacketEncoder } from './opus-packet-encoder';\n\n/** The recording's Opus configuration for WebCodecs: mono 48 kHz, 32 kbps, 20 ms frames. */\nexport const WEBCODECS_OPUS_CONFIG: AudioEncoderConfig = {\n codec: 'opus',\n sampleRate: RECORDING_SAMPLE_RATE,\n numberOfChannels: 1,\n bitrate: 32000,\n opus: { frameDuration: 20000 },\n};\n\n/** The slice of a WebCodecs `AudioEncoder` instance this wrapper drives. */\nexport type AudioEncoderLike = Pick<AudioEncoder, 'configure' | 'encode' | 'flush' | 'close' | 'state'>;\n\n/** The WebCodecs `AudioEncoder` constructor shape (the real global satisfies it). */\nexport interface AudioEncoderConstructor {\n new (init: AudioEncoderInit): AudioEncoderLike;\n // case-violation-ok-legacy-back-compat: mirrors the WebCodecs static so the real `AudioEncoder` global satisfies this interface\n isConfigSupported(config: AudioEncoderConfig): Promise<AudioEncoderSupport>;\n}\n\n/** The WebCodecs `AudioData` constructor shape (the real global satisfies it). */\nexport interface AudioDataConstructor {\n new (init: AudioDataInit): AudioData;\n}\n\n/**\n * Opus encoding through the browser's native WebCodecs `AudioEncoder`.\n *\n * Each 48 kHz block becomes one `AudioData` whose timestamp comes from the running sample count\n * (never the wall clock), so the encoder sees one gap-free stream. The encoder emits one\n * `EncodedAudioChunk` per 20 ms packet; each is copied out immediately because a chunk's bytes are\n * only guaranteed while the callback runs.\n */\nexport class WebCodecsOpusEncoder implements IOpusPacketEncoder {\n public readonly Kind = 'webcodecs';\n private readonly encoder: AudioEncoderLike;\n private packets: Uint8Array[] = [];\n private samplesFed = 0;\n /** Set by the encoder's error callback or a throwing encode: later input is dropped, packets kept. */\n private failed = false;\n private closed = false;\n\n /** Creates and configures the encoder. Throws if `configure` does (the selector falls back to WASM). */\n constructor(audioEncoder: AudioEncoderConstructor, private readonly audioData: AudioDataConstructor) {\n this.encoder = new audioEncoder({\n output: (chunk: EncodedAudioChunk) => {\n const bytes = new Uint8Array(chunk.byteLength);\n chunk.copyTo(bytes);\n this.packets.push(bytes);\n },\n error: (error: DOMException) => {\n // The recorder keeps the packets it already has; a broken encoder must never end the call.\n console.warn(`[WebCodecsOpusEncoder] Encoder failed after ${this.samplesFed} samples — the rest of this call is not recorded:`, error);\n this.failed = true;\n },\n });\n this.encoder.configure(WEBCODECS_OPUS_CONFIG);\n }\n\n public Encode(pcm48k: Float32Array): void {\n if (this.failed || this.closed || pcm48k.length === 0) {\n return;\n }\n const data = new this.audioData({\n format: 'f32',\n sampleRate: RECORDING_SAMPLE_RATE,\n numberOfFrames: pcm48k.length,\n numberOfChannels: 1,\n timestamp: Math.round((this.samplesFed * 1_000_000) / RECORDING_SAMPLE_RATE),\n // AudioData needs an ArrayBuffer-backed view (not a SharedArrayBuffer one); it copies anyway.\n data: pcm48k.buffer instanceof ArrayBuffer ? (pcm48k as Float32Array<ArrayBuffer>) : new Float32Array(pcm48k),\n });\n this.samplesFed += pcm48k.length;\n try {\n this.encoder.encode(data);\n } catch (error) {\n console.warn(`[WebCodecsOpusEncoder] encode() threw at sample ${this.samplesFed} — the rest of this call is not recorded:`, error);\n this.failed = true;\n } finally {\n data.close();\n }\n }\n\n public async Flush(): Promise<void> {\n if (this.failed || this.closed) {\n return;\n }\n try {\n await this.encoder.flush();\n } catch (error) {\n console.warn(`[WebCodecsOpusEncoder] flush() failed after ${this.samplesFed} samples — the final packets may be missing:`, error);\n }\n }\n\n public TakePackets(): Uint8Array[] {\n const packets = this.packets;\n this.packets = [];\n return packets;\n }\n\n public Close(): void {\n if (this.closed) {\n return;\n }\n this.closed = true;\n if (this.encoder.state === 'closed') {\n return;\n }\n try {\n this.encoder.close();\n } catch (error) {\n console.warn('[WebCodecsOpusEncoder] close() failed:', error);\n }\n }\n}\n"]}
|
|
@@ -26,6 +26,10 @@ export interface SearchResult {
|
|
|
26
26
|
collectionName?: string;
|
|
27
27
|
createdAt: Date;
|
|
28
28
|
relevanceScore: number;
|
|
29
|
+
/** A message hit's branch; null is the trunk. */
|
|
30
|
+
branchId?: string | null;
|
|
31
|
+
/** A message hit's position in its conversation. */
|
|
32
|
+
sequence?: number;
|
|
29
33
|
}
|
|
30
34
|
/**
|
|
31
35
|
* Search results grouped by type
|
|
@@ -107,7 +111,8 @@ export declare class SearchService {
|
|
|
107
111
|
*/
|
|
108
112
|
private searchConversations;
|
|
109
113
|
/**
|
|
110
|
-
* Search message content inside the conversations the conversation list shows the user
|
|
114
|
+
* Search message content inside the conversations the conversation list shows the user,
|
|
115
|
+
* on every branch of each conversation
|
|
111
116
|
*/
|
|
112
117
|
private searchMessages;
|
|
113
118
|
/**
|
|
@@ -135,7 +140,7 @@ export declare class SearchService {
|
|
|
135
140
|
*/
|
|
136
141
|
private mapConversationToSearchResult;
|
|
137
142
|
/**
|
|
138
|
-
* Map message
|
|
143
|
+
* Map a message row to a search result, with the branch and sequence that locate it
|
|
139
144
|
*/
|
|
140
145
|
private mapMessageToSearchResult;
|
|
141
146
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/search.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,UAAU,EAAe,MAAM,MAAM,CAAC;AAYhE,OAAO,EAAW,QAAQ,EAAY,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEtF,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;;
|
|
1
|
+
{"version":3,"file":"search.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/search.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,8 +4,13 @@ import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
|
|
4
4
|
import { ConversationEngine } from '@memberjunction/core-entities';
|
|
5
5
|
import { RunView, Metadata } from '@memberjunction/core';
|
|
6
6
|
import { EscapeSQLString } from '@memberjunction/global';
|
|
7
|
+
import { BuildConversationTasksFilter } from './conversation-tasks-filter';
|
|
7
8
|
import * as i0 from "@angular/core";
|
|
8
9
|
import * as i1 from "./artifact-permission.service";
|
|
10
|
+
/** Columns of {@link MessageSearchRow}, as the message query's `Fields`. */
|
|
11
|
+
const MESSAGE_SEARCH_FIELDS = [
|
|
12
|
+
'ID', 'ConversationID', 'Conversation', 'Message', 'BranchID', 'Sequence', '__mj_CreatedAt'
|
|
13
|
+
];
|
|
9
14
|
/**
|
|
10
15
|
* Service for searching across conversations, messages, and artifacts
|
|
11
16
|
* Provides debounced search with result ranking and filtering
|
|
@@ -179,7 +184,8 @@ export class SearchService {
|
|
|
179
184
|
return result.Results.map(conv => this.mapConversationToSearchResult(conv, query));
|
|
180
185
|
}
|
|
181
186
|
/**
|
|
182
|
-
* Search message content inside the conversations the conversation list shows the user
|
|
187
|
+
* Search message content inside the conversations the conversation list shows the user,
|
|
188
|
+
* on every branch of each conversation
|
|
183
189
|
*/
|
|
184
190
|
async searchMessages(query, environmentId, currentUser, dateRange) {
|
|
185
191
|
const conversationIds = await this.getVisibleConversationIds(environmentId, currentUser);
|
|
@@ -199,12 +205,14 @@ export class SearchService {
|
|
|
199
205
|
if (dateRange.end) {
|
|
200
206
|
filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;
|
|
201
207
|
}
|
|
208
|
+
// conversation-scope: search spans every path on purpose; opening a hit switches to its branch
|
|
202
209
|
const result = await rv.RunView({
|
|
203
210
|
EntityName: 'MJ: Conversation Details',
|
|
204
211
|
ExtraFilter: filter,
|
|
205
212
|
OrderBy: '__mj_CreatedAt DESC',
|
|
206
213
|
MaxRows: 100,
|
|
207
|
-
|
|
214
|
+
Fields: [...MESSAGE_SEARCH_FIELDS],
|
|
215
|
+
ResultType: 'simple'
|
|
208
216
|
}, currentUser);
|
|
209
217
|
if (!result.Success || !result.Results) {
|
|
210
218
|
console.error('Failed to search messages:', result.ErrorMessage);
|
|
@@ -322,22 +330,8 @@ export class SearchService {
|
|
|
322
330
|
async searchTasks(query, environmentId, currentUser, dateRange) {
|
|
323
331
|
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
324
332
|
const lowerQuery = query.toLowerCase();
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
const cd = md.EntityByName('MJ: Conversation Details');
|
|
328
|
-
const c = md.EntityByName('MJ: Conversations');
|
|
329
|
-
if (!cd || !c) {
|
|
330
|
-
console.warn('⚠️ Missing metadata for Conversations or Conversation Details');
|
|
331
|
-
return [];
|
|
332
|
-
}
|
|
333
|
-
let filter = `ParentID IS NULL AND (UserID = '${currentUser.ID}' OR ConversationDetailID IN (
|
|
334
|
-
SELECT ID FROM [${cd.SchemaName}].[${cd.BaseView}]
|
|
335
|
-
WHERE
|
|
336
|
-
UserID ='${currentUser.ID}' OR
|
|
337
|
-
ConversationID IN (
|
|
338
|
-
SELECT ID FROM [${c.SchemaName}].[${c.BaseView}] WHERE UserID='${currentUser.ID}'
|
|
339
|
-
)
|
|
340
|
-
))`;
|
|
333
|
+
// The same tasks the conversation workspace's tasks view shows.
|
|
334
|
+
let filter = await BuildConversationTasksFilter(this.Provider, currentUser);
|
|
341
335
|
filter += ` AND (LOWER(Name) LIKE '%${EscapeSQLString(lowerQuery)}%' OR LOWER(Description) LIKE '%${EscapeSQLString(lowerQuery)}%')`;
|
|
342
336
|
if (dateRange.start) {
|
|
343
337
|
filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;
|
|
@@ -386,7 +380,7 @@ export class SearchService {
|
|
|
386
380
|
};
|
|
387
381
|
}
|
|
388
382
|
/**
|
|
389
|
-
* Map message
|
|
383
|
+
* Map a message row to a search result, with the branch and sequence that locate it
|
|
390
384
|
*/
|
|
391
385
|
mapMessageToSearchResult(message, query) {
|
|
392
386
|
const messageText = message.Message || '';
|
|
@@ -400,7 +394,9 @@ export class SearchService {
|
|
|
400
394
|
conversationId: message.ConversationID,
|
|
401
395
|
conversationName: message.Conversation || undefined,
|
|
402
396
|
createdAt: message.__mj_CreatedAt,
|
|
403
|
-
relevanceScore: 5
|
|
397
|
+
relevanceScore: 5,
|
|
398
|
+
branchId: message.BranchID ?? null,
|
|
399
|
+
sequence: message.Sequence
|
|
404
400
|
};
|
|
405
401
|
}
|
|
406
402
|
/**
|