@memberjunction/ng-conversations 5.43.0 → 5.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts +0 -1
- package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts.map +1 -1
- package/dist/lib/components/active-tasks/active-tasks-panel.component.js +56 -52
- package/dist/lib/components/active-tasks/active-tasks-panel.component.js.map +1 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.d.ts +3 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.js +52 -52
- package/dist/lib/components/artifact/artifact-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.js +10 -14
- package/dist/lib/components/collection/collection-form-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.d.ts +3 -1
- package/dist/lib/components/collection/collection-share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.js +56 -56
- package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-tree.component.d.ts +3 -1
- package/dist/lib/components/collection/collection-tree.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-tree.component.js +10 -7
- package/dist/lib/components/collection/collection-tree.component.js.map +1 -1
- package/dist/lib/components/collection/collection-view.component.d.ts +3 -1
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +29 -41
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.d.ts +4 -0
- package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.js +134 -166
- package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
- package/dist/lib/components/composer/ai-composer.component.d.ts +103 -0
- package/dist/lib/components/composer/ai-composer.component.d.ts.map +1 -0
- package/dist/lib/components/composer/ai-composer.component.js +263 -0
- package/dist/lib/components/composer/ai-composer.component.js.map +1 -0
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +76 -12
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +522 -158
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
- package/dist/lib/components/conversation/pinned-messages-panel.component.js +6 -9
- package/dist/lib/components/conversation/pinned-messages-panel.component.js.map +1 -1
- package/dist/lib/components/library/library-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/library/library-full-view.component.js +20 -24
- package/dist/lib/components/library/library-full-view.component.js.map +1 -1
- package/dist/lib/components/members/members-modal.component.d.ts.map +1 -1
- package/dist/lib/components/members/members-modal.component.js +10 -11
- package/dist/lib/components/members/members-modal.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +87 -23
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +245 -158
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +11 -3
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +73 -21
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.js +6 -9
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts +4 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.js +9 -4
- package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +45 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +10 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -1
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts +46 -0
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/channels/client-context-channel.js +174 -0
- package/dist/lib/components/realtime/channels/client-context-channel.js.map +1 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.d.ts +123 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.js +397 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-state.d.ts +103 -0
- package/dist/lib/components/realtime/media/media-channel-state.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-state.js +143 -0
- package/dist/lib/components/realtime/media/media-channel-state.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-tools.d.ts +24 -0
- package/dist/lib/components/realtime/media/media-channel-tools.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-tools.js +107 -0
- package/dist/lib/components/realtime/media/media-channel-tools.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel.d.ts +89 -0
- package/dist/lib/components/realtime/media/media-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/media-channel.js +253 -0
- package/dist/lib/components/realtime/media/media-channel.js.map +1 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts +141 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js +574 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +59 -14
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-activity-rail.component.js +277 -74
- package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +28 -27
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.js +144 -143
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +12 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.js +105 -43
- package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts +2 -2
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-audio-visuals.js.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.d.ts +41 -7
- package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.js +216 -102
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +0 -2
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-delegation-card.component.js +67 -71
- package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +364 -44
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +839 -153
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.d.ts +5 -5
- package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tab-style.d.ts +70 -0
- package/dist/lib/components/realtime/realtime-surface-tab-style.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tab-style.js +115 -0
- package/dist/lib/components/realtime/realtime-surface-tab-style.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +55 -57
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js +165 -143
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +80 -71
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js +83 -113
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.d.ts +270 -0
- package/dist/lib/components/realtime/realtime-ui-config.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-ui-config.js +212 -0
- package/dist/lib/components/realtime/realtime-ui-config.js.map +1 -0
- 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 +92 -101
- package/dist/lib/components/search/search-panel.component.js.map +1 -1
- package/dist/lib/components/share/share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/share/share-modal.component.js +10 -11
- package/dist/lib/components/share/share-modal.component.js.map +1 -1
- package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -1
- package/dist/lib/components/shared/user-picker.component.js +12 -15
- package/dist/lib/components/shared/user-picker.component.js.map +1 -1
- package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts +11 -3
- package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts.map +1 -1
- package/dist/lib/components/sidebar/conversation-sidebar.component.js +34 -10
- package/dist/lib/components/sidebar/conversation-sidebar.component.js.map +1 -1
- package/dist/lib/components/sidebar/routines-section.component.d.ts +73 -0
- package/dist/lib/components/sidebar/routines-section.component.d.ts.map +1 -0
- package/dist/lib/components/sidebar/routines-section.component.js +211 -0
- package/dist/lib/components/sidebar/routines-section.component.js.map +1 -0
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +12 -0
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.js +37 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +16 -5
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +51 -31
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/composer-plugins/agent-mention.provider.d.ts +16 -0
- package/dist/lib/composer-plugins/agent-mention.provider.d.ts.map +1 -0
- package/dist/lib/composer-plugins/agent-mention.provider.js +28 -0
- package/dist/lib/composer-plugins/agent-mention.provider.js.map +1 -0
- package/dist/lib/composer-plugins/base-conversation-mention.provider.d.ts +20 -0
- package/dist/lib/composer-plugins/base-conversation-mention.provider.d.ts.map +1 -0
- package/dist/lib/composer-plugins/base-conversation-mention.provider.js +32 -0
- package/dist/lib/composer-plugins/base-conversation-mention.provider.js.map +1 -0
- package/dist/lib/composer-plugins/load-composer-plugins.d.ts +9 -0
- package/dist/lib/composer-plugins/load-composer-plugins.d.ts.map +1 -0
- package/dist/lib/composer-plugins/load-composer-plugins.js +17 -0
- package/dist/lib/composer-plugins/load-composer-plugins.js.map +1 -0
- package/dist/lib/composer-plugins/record-mention.provider.d.ts +14 -0
- package/dist/lib/composer-plugins/record-mention.provider.d.ts.map +1 -0
- package/dist/lib/composer-plugins/record-mention.provider.js +26 -0
- package/dist/lib/composer-plugins/record-mention.provider.js.map +1 -0
- package/dist/lib/composer-plugins/skill-command.provider.d.ts +15 -0
- package/dist/lib/composer-plugins/skill-command.provider.d.ts.map +1 -0
- package/dist/lib/composer-plugins/skill-command.provider.js +27 -0
- package/dist/lib/composer-plugins/skill-command.provider.js.map +1 -0
- package/dist/lib/conversations.module.d.ts +68 -64
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +69 -18
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/models/conversation-state.model.d.ts +6 -3
- 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/conversation-agent.service.d.ts +9 -2
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +26 -2
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-attachment.service.d.ts +1 -1
- package/dist/lib/services/conversation-attachment.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-attachment.service.js.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.d.ts +42 -19
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +113 -18
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/realtime-audio-recorder.d.ts +143 -0
- package/dist/lib/services/realtime-audio-recorder.d.ts.map +1 -0
- package/dist/lib/services/realtime-audio-recorder.js +379 -0
- package/dist/lib/services/realtime-audio-recorder.js.map +1 -0
- package/dist/lib/services/realtime-pcm-wav.d.ts +75 -0
- package/dist/lib/services/realtime-pcm-wav.d.ts.map +1 -0
- package/dist/lib/services/realtime-pcm-wav.js +205 -0
- package/dist/lib/services/realtime-pcm-wav.js.map +1 -0
- package/dist/lib/services/realtime-session-review.service.d.ts +28 -1
- package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session-review.service.js +12 -3
- package/dist/lib/services/realtime-session-review.service.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts +210 -20
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +452 -21
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/dist/lib/services/realtime-sessions-adapter.js +1 -1
- package/dist/lib/services/realtime-sessions-adapter.js.map +1 -1
- package/dist/lib/services/ui-command-handler.service.d.ts +7 -2
- package/dist/lib/services/ui-command-handler.service.d.ts.map +1 -1
- package/dist/lib/services/ui-command-handler.service.js +6 -2
- package/dist/lib/services/ui-command-handler.service.js.map +1 -1
- package/dist/lib/utils/plan-mode-preference.d.ts +62 -0
- package/dist/lib/utils/plan-mode-preference.d.ts.map +1 -0
- package/dist/lib/utils/plan-mode-preference.js +130 -0
- package/dist/lib/utils/plan-mode-preference.js.map +1 -0
- package/dist/public-api.d.ts +29 -3
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +46 -3
- package/dist/public-api.js.map +1 -1
- package/package.json +30 -26
- package/dist/__tests__/attachment-badge.test.d.ts +0 -2
- package/dist/__tests__/attachment-badge.test.d.ts.map +0 -1
- package/dist/__tests__/attachment-badge.test.js +0 -66
- package/dist/__tests__/attachment-badge.test.js.map +0 -1
- package/dist/__tests__/channel-optional-surface.test.d.ts +0 -2
- package/dist/__tests__/channel-optional-surface.test.d.ts.map +0 -1
- package/dist/__tests__/channel-optional-surface.test.js +0 -53
- package/dist/__tests__/channel-optional-surface.test.js.map +0 -1
- package/dist/__tests__/chat-events.test.d.ts +0 -14
- package/dist/__tests__/chat-events.test.d.ts.map +0 -1
- package/dist/__tests__/chat-events.test.js +0 -109
- package/dist/__tests__/chat-events.test.js.map +0 -1
- package/dist/__tests__/collections-full-view.test.d.ts +0 -2
- package/dist/__tests__/collections-full-view.test.d.ts.map +0 -1
- package/dist/__tests__/collections-full-view.test.js +0 -225
- package/dist/__tests__/collections-full-view.test.js.map +0 -1
- package/dist/__tests__/conversation-cache.test.d.ts +0 -2
- package/dist/__tests__/conversation-cache.test.d.ts.map +0 -1
- package/dist/__tests__/conversation-cache.test.js +0 -233
- package/dist/__tests__/conversation-cache.test.js.map +0 -1
- package/dist/__tests__/conversation-naming.test.d.ts +0 -2
- package/dist/__tests__/conversation-naming.test.d.ts.map +0 -1
- package/dist/__tests__/conversation-naming.test.js +0 -110
- package/dist/__tests__/conversation-naming.test.js.map +0 -1
- package/dist/__tests__/delegation-result-parser.test.d.ts +0 -2
- package/dist/__tests__/delegation-result-parser.test.d.ts.map +0 -1
- package/dist/__tests__/delegation-result-parser.test.js +0 -107
- package/dist/__tests__/delegation-result-parser.test.js.map +0 -1
- package/dist/__tests__/distinct-artifacts.test.d.ts +0 -2
- package/dist/__tests__/distinct-artifacts.test.d.ts.map +0 -1
- package/dist/__tests__/distinct-artifacts.test.js +0 -56
- package/dist/__tests__/distinct-artifacts.test.js.map +0 -1
- package/dist/__tests__/event-wiring.test.d.ts +0 -15
- package/dist/__tests__/event-wiring.test.d.ts.map +0 -1
- package/dist/__tests__/event-wiring.test.js +0 -100
- package/dist/__tests__/event-wiring.test.js.map +0 -1
- package/dist/__tests__/index.test.d.ts +0 -2
- package/dist/__tests__/index.test.d.ts.map +0 -1
- package/dist/__tests__/index.test.js +0 -7
- package/dist/__tests__/index.test.js.map +0 -1
- package/dist/__tests__/narration-template.test.d.ts +0 -2
- package/dist/__tests__/narration-template.test.d.ts.map +0 -1
- package/dist/__tests__/narration-template.test.js +0 -76
- package/dist/__tests__/narration-template.test.js.map +0 -1
- package/dist/__tests__/notification-model.test.d.ts +0 -2
- package/dist/__tests__/notification-model.test.d.ts.map +0 -1
- package/dist/__tests__/notification-model.test.js +0 -68
- package/dist/__tests__/notification-model.test.js.map +0 -1
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts +0 -2
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-agent-picker-models.test.js +0 -49
- package/dist/__tests__/realtime-agent-picker-models.test.js.map +0 -1
- package/dist/__tests__/realtime-audio-visuals.test.d.ts +0 -2
- package/dist/__tests__/realtime-audio-visuals.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-audio-visuals.test.js +0 -123
- package/dist/__tests__/realtime-audio-visuals.test.js.map +0 -1
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts +0 -2
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-delegation-card-cancel.test.js +0 -48
- package/dist/__tests__/realtime-delegation-card-cancel.test.js.map +0 -1
- package/dist/__tests__/realtime-disclosure.test.d.ts +0 -2
- package/dist/__tests__/realtime-disclosure.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-disclosure.test.js +0 -164
- package/dist/__tests__/realtime-disclosure.test.js.map +0 -1
- package/dist/__tests__/realtime-pairing.test.d.ts +0 -2
- package/dist/__tests__/realtime-pairing.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-pairing.test.js +0 -207
- package/dist/__tests__/realtime-pairing.test.js.map +0 -1
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts +0 -2
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-review-lifecycle.test.js +0 -154
- package/dist/__tests__/realtime-review-lifecycle.test.js.map +0 -1
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-cancel-usage.test.js +0 -230
- package/dist/__tests__/realtime-session-cancel-usage.test.js.map +0 -1
- package/dist/__tests__/realtime-session-channels.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-channels.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-channels.test.js +0 -252
- package/dist/__tests__/realtime-session-channels.test.js.map +0 -1
- package/dist/__tests__/realtime-session-client-tools.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-client-tools.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-client-tools.test.js +0 -103
- package/dist/__tests__/realtime-session-client-tools.test.js.map +0 -1
- package/dist/__tests__/realtime-session-minimized.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-minimized.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-minimized.test.js +0 -32
- package/dist/__tests__/realtime-session-minimized.test.js.map +0 -1
- package/dist/__tests__/realtime-session-mint.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-mint.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-mint.test.js +0 -69
- package/dist/__tests__/realtime-session-mint.test.js.map +0 -1
- package/dist/__tests__/realtime-session-policy.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-policy.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-policy.test.js +0 -303
- package/dist/__tests__/realtime-session-policy.test.js.map +0 -1
- package/dist/__tests__/realtime-session-review.service.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-review.service.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-review.service.test.js +0 -743
- package/dist/__tests__/realtime-session-review.service.test.js.map +0 -1
- package/dist/__tests__/realtime-session-state.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-state.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-state.test.js +0 -83
- package/dist/__tests__/realtime-session-state.test.js.map +0 -1
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-timeline-card.test.js +0 -106
- package/dist/__tests__/realtime-session-timeline-card.test.js.map +0 -1
- package/dist/__tests__/realtime-session-timeline.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-timeline.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-timeline.test.js +0 -142
- package/dist/__tests__/realtime-session-timeline.test.js.map +0 -1
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts +0 -19
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-sessions-adapter.test.js +0 -188
- package/dist/__tests__/realtime-sessions-adapter.test.js.map +0 -1
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts +0 -2
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-surface-panel-prefs.test.js +0 -100
- package/dist/__tests__/realtime-surface-panel-prefs.test.js.map +0 -1
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts +0 -2
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-surface-tabs-model.test.js +0 -193
- package/dist/__tests__/realtime-surface-tabs-model.test.js.map +0 -1
- package/dist/__tests__/remote-browser-audio-player.test.d.ts +0 -2
- package/dist/__tests__/remote-browser-audio-player.test.d.ts.map +0 -1
- package/dist/__tests__/remote-browser-audio-player.test.js +0 -137
- package/dist/__tests__/remote-browser-audio-player.test.js.map +0 -1
- package/dist/__tests__/remote-browser-channel.test.d.ts +0 -2
- package/dist/__tests__/remote-browser-channel.test.d.ts.map +0 -1
- package/dist/__tests__/remote-browser-channel.test.js +0 -585
- package/dist/__tests__/remote-browser-channel.test.js.map +0 -1
- package/dist/__tests__/search-service.test.d.ts +0 -2
- package/dist/__tests__/search-service.test.d.ts.map +0 -1
- package/dist/__tests__/search-service.test.js +0 -114
- package/dist/__tests__/search-service.test.js.map +0 -1
- package/dist/__tests__/slot-defaults.test.d.ts +0 -24
- package/dist/__tests__/slot-defaults.test.d.ts.map +0 -1
- package/dist/__tests__/slot-defaults.test.js +0 -63
- package/dist/__tests__/slot-defaults.test.js.map +0 -1
- package/dist/__tests__/toast-service.test.d.ts +0 -2
- package/dist/__tests__/toast-service.test.d.ts.map +0 -1
- package/dist/__tests__/toast-service.test.js +0 -143
- package/dist/__tests__/toast-service.test.js.map +0 -1
- package/dist/__tests__/user-authorization.test.d.ts +0 -2
- package/dist/__tests__/user-authorization.test.d.ts.map +0 -1
- package/dist/__tests__/user-authorization.test.js +0 -97
- package/dist/__tests__/user-authorization.test.js.map +0 -1
- package/dist/__tests__/voice-session-narration.test.d.ts +0 -2
- package/dist/__tests__/voice-session-narration.test.d.ts.map +0 -1
- package/dist/__tests__/voice-session-narration.test.js +0 -609
- package/dist/__tests__/voice-session-narration.test.js.map +0 -1
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts +0 -2
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts.map +0 -1
- package/dist/__tests__/whiteboard-artifact-viewer.test.js +0 -101
- package/dist/__tests__/whiteboard-artifact-viewer.test.js.map +0 -1
- package/dist/__tests__/whiteboard-channel.test.d.ts +0 -2
- package/dist/__tests__/whiteboard-channel.test.d.ts.map +0 -1
- package/dist/__tests__/whiteboard-channel.test.js +0 -260
- package/dist/__tests__/whiteboard-channel.test.js.map +0 -1
- package/dist/__tests__/whiteboard-restore-state.test.d.ts +0 -2
- package/dist/__tests__/whiteboard-restore-state.test.d.ts.map +0 -1
- package/dist/__tests__/whiteboard-restore-state.test.js +0 -108
- package/dist/__tests__/whiteboard-restore-state.test.js.map +0 -1
- package/dist/lib/components/mention/mention-dropdown.component.d.ts +0 -53
- package/dist/lib/components/mention/mention-dropdown.component.d.ts.map +0 -1
- package/dist/lib/components/mention/mention-dropdown.component.js +0 -276
- package/dist/lib/components/mention/mention-dropdown.component.js.map +0 -1
- package/dist/lib/components/mention/mention-editor.component.d.ts +0 -256
- package/dist/lib/components/mention/mention-editor.component.d.ts.map +0 -1
- package/dist/lib/components/mention/mention-editor.component.js +0 -1233
- package/dist/lib/components/mention/mention-editor.component.js.map +0 -1
- package/dist/lib/components/message/message-input-box.component.d.ts +0 -137
- package/dist/lib/components/message/message-input-box.component.d.ts.map +0 -1
- package/dist/lib/components/message/message-input-box.component.js +0 -321
- package/dist/lib/components/message/message-input-box.component.js.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Component, ElementRef, EventEmitter, HostListener, Input, Output, ChangeDetectorRef, NgZone, ViewChild, inject } from '@angular/core';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import { UserInfoEngine } from '@memberjunction/core-entities';
|
|
4
|
+
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
4
5
|
import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
|
|
6
|
+
import { MJStorageMediaPlayerComponent } from '@memberjunction/ng-media-player';
|
|
5
7
|
import { RealtimeSessionService } from '../../services/realtime-session.service';
|
|
6
8
|
import { BuildReviewThreadItems } from '../../services/realtime-session-review.service';
|
|
7
9
|
import { RealtimeSessionState } from './realtime-session-state';
|
|
@@ -12,16 +14,19 @@ import { RealtimeComposerComponent } from './realtime-composer.component';
|
|
|
12
14
|
import { RealtimeSurfaceTabsComponent } from './realtime-surface-tabs.component';
|
|
13
15
|
import { ClampSurfacePanelWidth, DefaultSurfacePanelWidth, IsSurfacePanelDrag, ParseSurfacePanelPref, SerializeSurfacePanelPref, SurfacePanelDragWidth, SURFACE_PANEL_COLLAPSED_WIDTH, SURFACE_PANEL_DEFAULT_WIDTH, SURFACE_PANEL_PREF_KEY } from './realtime-surface-panel-prefs';
|
|
14
16
|
import { RealtimeDisclosureModel, SerializeUxMilestones, REALTIME_UX_PREF_KEY } from './realtime-disclosure';
|
|
17
|
+
import { resolveRealtimeUi, DEFAULT_REALTIME_UI_INPUTS, DEFAULT_REALTIME_UI_SIGNALS } from './realtime-ui-config';
|
|
15
18
|
import { RealtimeAudioVisualSmoother } from './realtime-audio-visuals';
|
|
16
19
|
import { ShouldRemoveReviewWhiteboardTab } from './realtime-surface-tabs.model';
|
|
20
|
+
import { ShouldRegisterChannelTabUpFront } from './realtime-surface-tab-style';
|
|
17
21
|
import { RealtimeWhiteboardBoardComponent, WhiteboardState } from '@memberjunction/ng-whiteboard';
|
|
18
22
|
import * as i0 from "@angular/core";
|
|
19
23
|
import * as i1 from "@memberjunction/ng-shared-generic";
|
|
20
24
|
import * as i2 from "@angular/common";
|
|
21
25
|
const _c0 = ["reviewBoardTpl"];
|
|
26
|
+
const _c1 = ["recordingTpl"];
|
|
22
27
|
function RealtimeSessionOverlayComponent_Conditional_0_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
23
|
-
i0.ɵɵelementStart(0, "div",
|
|
24
|
-
i0.ɵɵelement(1, "i",
|
|
28
|
+
i0.ɵɵelementStart(0, "div", 6);
|
|
29
|
+
i0.ɵɵelement(1, "i", 12);
|
|
25
30
|
i0.ɵɵtext(2);
|
|
26
31
|
i0.ɵɵelementEnd();
|
|
27
32
|
} if (rf & 2) {
|
|
@@ -30,27 +35,27 @@ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_4_Template(rf
|
|
|
30
35
|
i0.ɵɵtextInterpolate1(" ", ctx_r1.State.Narration, " ");
|
|
31
36
|
} }
|
|
32
37
|
function RealtimeSessionOverlayComponent_Conditional_0_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
33
|
-
i0.ɵɵelementStart(0, "div",
|
|
34
|
-
i0.ɵɵelement(1, "mj-loading",
|
|
38
|
+
i0.ɵɵelementStart(0, "div", 8);
|
|
39
|
+
i0.ɵɵelement(1, "mj-loading", 13);
|
|
35
40
|
i0.ɵɵelementEnd();
|
|
36
41
|
} }
|
|
37
42
|
function RealtimeSessionOverlayComponent_Conditional_0_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
38
43
|
const _r3 = i0.ɵɵgetCurrentView();
|
|
39
|
-
i0.ɵɵelementStart(0, "div",
|
|
40
|
-
i0.ɵɵelement(2, "span",
|
|
44
|
+
i0.ɵɵelementStart(0, "div", 9)(1, "div", 14);
|
|
45
|
+
i0.ɵɵelement(2, "span", 15)(3, "span", 16)(4, "span", 17)(5, "div", 18);
|
|
41
46
|
i0.ɵɵelementEnd();
|
|
42
|
-
i0.ɵɵelementStart(6, "div",
|
|
47
|
+
i0.ɵɵelementStart(6, "div", 19);
|
|
43
48
|
i0.ɵɵelement(7, "span")(8, "span")(9, "span")(10, "span")(11, "span")(12, "span")(13, "span")(14, "span")(15, "span");
|
|
44
49
|
i0.ɵɵelementEnd();
|
|
45
|
-
i0.ɵɵelementStart(16, "div",
|
|
50
|
+
i0.ɵɵelementStart(16, "div", 20);
|
|
46
51
|
i0.ɵɵtext(17);
|
|
47
52
|
i0.ɵɵelementEnd();
|
|
48
|
-
i0.ɵɵelementStart(18, "div",
|
|
53
|
+
i0.ɵɵelementStart(18, "div", 21);
|
|
49
54
|
i0.ɵɵtext(19);
|
|
50
55
|
i0.ɵɵelementEnd();
|
|
51
|
-
i0.ɵɵelementStart(20, "button",
|
|
56
|
+
i0.ɵɵelementStart(20, "button", 22);
|
|
52
57
|
i0.ɵɵlistener("click", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_7_Template_button_click_20_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnTextReveal()); });
|
|
53
|
-
i0.ɵɵelement(21, "i",
|
|
58
|
+
i0.ɵɵelement(21, "i", 23);
|
|
54
59
|
i0.ɵɵtext(22, " Show the conversation ");
|
|
55
60
|
i0.ɵɵelementEnd()();
|
|
56
61
|
} if (rf & 2) {
|
|
@@ -66,7 +71,7 @@ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_7_Template(rf
|
|
|
66
71
|
} }
|
|
67
72
|
function RealtimeSessionOverlayComponent_Conditional_0_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
68
73
|
const _r5 = i0.ɵɵgetCurrentView();
|
|
69
|
-
i0.ɵɵelementStart(0, "mj-realtime-session-thread",
|
|
74
|
+
i0.ɵɵelementStart(0, "mj-realtime-session-thread", 24);
|
|
70
75
|
i0.ɵɵlistener("OpenRunRequested", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_8_Template_mj_realtime_session_thread_OpenRunRequested_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnOpenRunRequested($event)); })("OpenArtifactRequested", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_8_Template_mj_realtime_session_thread_OpenArtifactRequested_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnOpenArtifactRequested($event)); })("CancelRequested", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_8_Template_mj_realtime_session_thread_CancelRequested_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnCancelDelegation($event)); });
|
|
71
76
|
i0.ɵɵelementEnd();
|
|
72
77
|
} if (rf & 2) {
|
|
@@ -79,18 +84,18 @@ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Conditional
|
|
|
79
84
|
function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
80
85
|
const _r6 = i0.ɵɵgetCurrentView();
|
|
81
86
|
i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Conditional_0_Template, 1, 0, "mj-realtime-channel-strip");
|
|
82
|
-
i0.ɵɵelementStart(1, "mj-realtime-composer",
|
|
83
|
-
i0.ɵɵlistener("OpenChanged", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template_mj_realtime_composer_OpenChanged_1_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnComposerOpenChanged($event)); })("CaptionsToggled", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template_mj_realtime_composer_CaptionsToggled_1_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnCaptionsToggled($event)); })("DetailsToggled", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template_mj_realtime_composer_DetailsToggled_1_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnDetailsToggled()); })("EndRequested", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template_mj_realtime_composer_EndRequested_1_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnEndCall()); });
|
|
87
|
+
i0.ɵɵelementStart(1, "mj-realtime-composer", 25);
|
|
88
|
+
i0.ɵɵlistener("OpenChanged", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template_mj_realtime_composer_OpenChanged_1_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnComposerOpenChanged($event)); })("CaptionsToggled", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template_mj_realtime_composer_CaptionsToggled_1_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnCaptionsToggled($event)); })("DetailsToggled", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template_mj_realtime_composer_DetailsToggled_1_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnDetailsToggled()); })("MuteChanged", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template_mj_realtime_composer_MuteChanged_1_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnComposerMuteChanged($event)); })("EndRequested", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template_mj_realtime_composer_EndRequested_1_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnEndCall()); });
|
|
84
89
|
i0.ɵɵelementEnd();
|
|
85
90
|
} if (rf & 2) {
|
|
86
91
|
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
87
|
-
i0.ɵɵconditional(ctx_r1.
|
|
92
|
+
i0.ɵɵconditional(ctx_r1.ChannelStripVisible ? 0 : -1);
|
|
88
93
|
i0.ɵɵadvance();
|
|
89
|
-
i0.ɵɵproperty("Open", ctx_r1.ComposerOpen)("CaptionsOn", ctx_r1.ShowCaptions)("ShowDetails", ctx_r1.ShowDetailsControl)("DetailsOn", ctx_r1.DetailsPeek);
|
|
94
|
+
i0.ɵɵproperty("Open", ctx_r1.ComposerOpen)("Compact", ctx_r1.Ui.compact && ctx_r1.Ui.chrome === "orb")("CaptionsOn", ctx_r1.ShowCaptions)("IsMuted", ctx_r1.Muted)("ShowDetails", ctx_r1.ShowDetailsControl)("DetailsOn", ctx_r1.DetailsPeek);
|
|
90
95
|
} }
|
|
91
96
|
function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
92
97
|
const _r8 = i0.ɵɵgetCurrentView();
|
|
93
|
-
i0.ɵɵelementStart(0, "div",
|
|
98
|
+
i0.ɵɵelementStart(0, "div", 29);
|
|
94
99
|
i0.ɵɵlistener("mousedown", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Conditional_0_Template_div_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnPanelResizeStart($event)); })("dblclick", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Conditional_0_Template_div_dblclick_0_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnPanelResizeReset()); });
|
|
95
100
|
i0.ɵɵelementEnd();
|
|
96
101
|
} if (rf & 2) {
|
|
@@ -99,8 +104,8 @@ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Conditiona
|
|
|
99
104
|
} }
|
|
100
105
|
function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
101
106
|
const _r7 = i0.ɵɵgetCurrentView();
|
|
102
|
-
i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Conditional_0_Template, 1, 2, "div",
|
|
103
|
-
i0.ɵɵelementStart(1, "div",
|
|
107
|
+
i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Conditional_0_Template, 1, 2, "div", 26);
|
|
108
|
+
i0.ɵɵelementStart(1, "div", 27)(2, "mj-realtime-surface-tabs", 28);
|
|
104
109
|
i0.ɵɵlistener("OpenRunRequested", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Template_mj_realtime_surface_tabs_OpenRunRequested_2_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnOpenRunRequested($event)); })("CollapsedChange", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Template_mj_realtime_surface_tabs_CollapsedChange_2_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnPanelCollapsedChange($event)); })("WideChanged", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Template_mj_realtime_surface_tabs_WideChanged_2_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnPanelWideChanged($event)); });
|
|
105
110
|
i0.ɵɵelementEnd()();
|
|
106
111
|
} if (rf & 2) {
|
|
@@ -109,10 +114,10 @@ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Template(r
|
|
|
109
114
|
i0.ɵɵadvance();
|
|
110
115
|
i0.ɵɵstyleProp("width", ctx_r1.ChannelFocusMode ? null : ctx_r1.PanelAreaSize, "px");
|
|
111
116
|
i0.ɵɵadvance();
|
|
112
|
-
i0.ɵɵproperty("State", ctx_r1.State)("DevMode", ctx_r1.DevMode)("CurrentUser", ctx_r1.CurrentUser)("EnvironmentID", ctx_r1.EnvironmentID)("Fill", ctx_r1.ChannelFocusMode);
|
|
117
|
+
i0.ɵɵproperty("State", ctx_r1.State)("DevMode", ctx_r1.DevMode)("CurrentUser", ctx_r1.CurrentUser)("EnvironmentID", ctx_r1.EnvironmentID)("ShowActivityTab", ctx_r1.ShowActivityTab)("ExtraArtifacts", ctx_r1.ReviewArtifacts)("Fill", ctx_r1.ChannelFocusMode);
|
|
113
118
|
} }
|
|
114
119
|
function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
115
|
-
i0.ɵɵelementStart(0, "span",
|
|
120
|
+
i0.ɵɵelementStart(0, "span", 33);
|
|
116
121
|
i0.ɵɵtext(1);
|
|
117
122
|
i0.ɵɵelementEnd();
|
|
118
123
|
} if (rf & 2) {
|
|
@@ -121,27 +126,27 @@ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Conditiona
|
|
|
121
126
|
} }
|
|
122
127
|
function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
123
128
|
const _r9 = i0.ɵɵgetCurrentView();
|
|
124
|
-
i0.ɵɵelementStart(0, "div",
|
|
125
|
-
i0.ɵɵelement(1, "span",
|
|
126
|
-
i0.ɵɵelementStart(2, "span",
|
|
129
|
+
i0.ɵɵelementStart(0, "div", 11);
|
|
130
|
+
i0.ɵɵelement(1, "span", 30);
|
|
131
|
+
i0.ɵɵelementStart(2, "span", 31)(3, "span", 32);
|
|
127
132
|
i0.ɵɵtext(4);
|
|
128
133
|
i0.ɵɵelementEnd();
|
|
129
|
-
i0.ɵɵconditionalCreate(5, RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Conditional_5_Template, 2, 1, "span",
|
|
134
|
+
i0.ɵɵconditionalCreate(5, RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Conditional_5_Template, 2, 1, "span", 33);
|
|
130
135
|
i0.ɵɵelementEnd();
|
|
131
|
-
i0.ɵɵelementStart(6, "button",
|
|
136
|
+
i0.ɵɵelementStart(6, "button", 34);
|
|
132
137
|
i0.ɵɵlistener("click", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnFocusPillMute()); });
|
|
133
|
-
i0.ɵɵelement(7, "i",
|
|
138
|
+
i0.ɵɵelement(7, "i", 35);
|
|
134
139
|
i0.ɵɵelementEnd();
|
|
135
|
-
i0.ɵɵelementStart(8, "button",
|
|
140
|
+
i0.ɵɵelementStart(8, "button", 36);
|
|
136
141
|
i0.ɵɵlistener("click", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnFocusPillExit()); });
|
|
137
|
-
i0.ɵɵelement(9, "i",
|
|
142
|
+
i0.ɵɵelement(9, "i", 37);
|
|
138
143
|
i0.ɵɵelementEnd();
|
|
139
|
-
i0.ɵɵelementStart(10, "button",
|
|
144
|
+
i0.ɵɵelementStart(10, "button", 38);
|
|
140
145
|
i0.ɵɵlistener("click", function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template_button_click_10_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnFocusPillEnd()); });
|
|
141
|
-
i0.ɵɵelement(11, "i",
|
|
146
|
+
i0.ɵɵelement(11, "i", 39);
|
|
142
147
|
i0.ɵɵelementEnd()();
|
|
143
148
|
} if (rf & 2) {
|
|
144
|
-
let
|
|
149
|
+
let tmp_7_0;
|
|
145
150
|
const state_r4 = i0.ɵɵnextContext();
|
|
146
151
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
147
152
|
i0.ɵɵadvance();
|
|
@@ -149,7 +154,7 @@ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template(r
|
|
|
149
154
|
i0.ɵɵadvance(3);
|
|
150
155
|
i0.ɵɵtextInterpolate1("Voice for ", ctx_r1.AgentName);
|
|
151
156
|
i0.ɵɵadvance();
|
|
152
|
-
i0.ɵɵconditional((
|
|
157
|
+
i0.ɵɵconditional((tmp_7_0 = ctx_r1.FocusStateLabel(state_r4)) ? 5 : -1, tmp_7_0);
|
|
153
158
|
i0.ɵɵadvance();
|
|
154
159
|
i0.ɵɵclassProp("board-focus-pill__btn--active", ctx_r1.FocusPillMuted);
|
|
155
160
|
i0.ɵɵproperty("title", ctx_r1.FocusPillMuted ? "Unmute" : "Mute");
|
|
@@ -159,18 +164,18 @@ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template(r
|
|
|
159
164
|
} }
|
|
160
165
|
function RealtimeSessionOverlayComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
161
166
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
162
|
-
i0.ɵɵelementStart(0, "div",
|
|
167
|
+
i0.ɵɵelementStart(0, "div", 3)(1, "div", 4)(2, "mj-realtime-agent-banner", 5);
|
|
163
168
|
i0.ɵɵpipe(3, "async");
|
|
164
|
-
i0.ɵɵlistener("OpenSessionRequested", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_OpenSessionRequested_2_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnOpenSessionRequested($event)); })("
|
|
169
|
+
i0.ɵɵlistener("OpenSessionRequested", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_OpenSessionRequested_2_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnOpenSessionRequested($event)); })("DevModeToggled", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_DevModeToggled_2_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDevModeToggled($event)); })("DensityChanged", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_DensityChanged_2_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDensityChanged($event)); })("MinimizeRequested", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_MinimizeRequested_2_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnMinimize()); })("StartLiveRequested", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_StartLiveRequested_2_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnStartLive()); })("CloseRequested", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_CloseRequested_2_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnReviewClose()); });
|
|
165
170
|
i0.ɵɵelementEnd();
|
|
166
|
-
i0.ɵɵconditionalCreate(4, RealtimeSessionOverlayComponent_Conditional_0_Conditional_4_Template, 3, 1, "div",
|
|
167
|
-
i0.ɵɵelementStart(5, "div",
|
|
168
|
-
i0.ɵɵconditionalCreate(6, RealtimeSessionOverlayComponent_Conditional_0_Conditional_6_Template, 2, 0, "div",
|
|
171
|
+
i0.ɵɵconditionalCreate(4, RealtimeSessionOverlayComponent_Conditional_0_Conditional_4_Template, 3, 1, "div", 6);
|
|
172
|
+
i0.ɵɵelementStart(5, "div", 7);
|
|
173
|
+
i0.ɵɵconditionalCreate(6, RealtimeSessionOverlayComponent_Conditional_0_Conditional_6_Template, 2, 0, "div", 8)(7, RealtimeSessionOverlayComponent_Conditional_0_Conditional_7_Template, 23, 4, "div", 9)(8, RealtimeSessionOverlayComponent_Conditional_0_Conditional_8_Template, 1, 5, "mj-realtime-session-thread", 10);
|
|
169
174
|
i0.ɵɵelementEnd();
|
|
170
|
-
i0.ɵɵconditionalCreate(9, RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template, 2,
|
|
175
|
+
i0.ɵɵconditionalCreate(9, RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template, 2, 7);
|
|
171
176
|
i0.ɵɵelementEnd();
|
|
172
|
-
i0.ɵɵconditionalCreate(10, RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Template, 3,
|
|
173
|
-
i0.ɵɵconditionalCreate(11, RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template, 12, 11, "div",
|
|
177
|
+
i0.ɵɵconditionalCreate(10, RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Template, 3, 10);
|
|
178
|
+
i0.ɵɵconditionalCreate(11, RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template, 12, 11, "div", 11);
|
|
174
179
|
i0.ɵɵelementEnd();
|
|
175
180
|
} if (rf & 2) {
|
|
176
181
|
const state_r4 = ctx;
|
|
@@ -178,7 +183,7 @@ function RealtimeSessionOverlayComponent_Conditional_0_Template(rf, ctx) { if (r
|
|
|
178
183
|
i0.ɵɵclassProp("call-overlay--hidden", ctx_r1.Hidden)("call-overlay--review", ctx_r1.IsReviewing)("board-focus", ctx_r1.ChannelFocusMode);
|
|
179
184
|
i0.ɵɵattribute("aria-label", ctx_r1.IsReviewing ? "Past session review" : "Live voice session");
|
|
180
185
|
i0.ɵɵadvance(2);
|
|
181
|
-
i0.ɵɵproperty("State", state_r4)("AgentName", ctx_r1.AgentName)("ModelName", ctx_r1.IsReviewing ? null : i0.ɵɵpipeBind1(3,
|
|
186
|
+
i0.ɵɵproperty("State", state_r4)("Compact", !ctx_r1.IsReviewing && ctx_r1.Ui.compact && ctx_r1.Ui.chrome === "orb")("AgentName", ctx_r1.AgentName)("ModelName", ctx_r1.IsReviewing ? null : i0.ɵɵpipeBind1(3, 28, ctx_r1.ModelName$))("DevMode", ctx_r1.DevMode)("SessionID", ctx_r1.IsReviewing ? ctx_r1.ReviewSessionID : ctx_r1.SessionID)("ReviewMode", ctx_r1.IsReviewing)("ReviewStartedAt", ctx_r1.ReviewStartedAt)("ReviewClosedAt", ctx_r1.ReviewClosedAt)("ReviewCloseReason", ctx_r1.ReviewCloseReason)("ShowGear", !ctx_r1.IsReviewing && ctx_r1.Ui.showGear)("ShowDensityPicker", ctx_r1.Ui.showDensityPicker)("ShowMinimize", !ctx_r1.IsReviewing && ctx_r1.Ui.showMinimize)("Density", ctx_r1.Disclosure.Milestones.Density);
|
|
182
187
|
i0.ɵɵadvance(2);
|
|
183
188
|
i0.ɵɵconditional(!ctx_r1.IsReviewing && ctx_r1.State.Narration && !ctx_r1.State.ActiveCallId && ctx_r1.Disclosure.ShowThread ? 4 : -1);
|
|
184
189
|
i0.ɵɵadvance();
|
|
@@ -193,8 +198,8 @@ function RealtimeSessionOverlayComponent_Conditional_0_Template(rf, ctx) { if (r
|
|
|
193
198
|
i0.ɵɵconditional(!ctx_r1.IsReviewing && ctx_r1.ChannelFocusMode ? 11 : -1);
|
|
194
199
|
} }
|
|
195
200
|
function RealtimeSessionOverlayComponent_ng_template_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
196
|
-
i0.ɵɵelementStart(0, "div",
|
|
197
|
-
i0.ɵɵelement(1, "mj-realtime-whiteboard",
|
|
201
|
+
i0.ɵɵelementStart(0, "div", 40);
|
|
202
|
+
i0.ɵɵelement(1, "mj-realtime-whiteboard", 41);
|
|
198
203
|
i0.ɵɵelementEnd();
|
|
199
204
|
} if (rf & 2) {
|
|
200
205
|
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
@@ -202,11 +207,20 @@ function RealtimeSessionOverlayComponent_ng_template_2_Conditional_0_Template(rf
|
|
|
202
207
|
i0.ɵɵproperty("State", ctx)("AgentName", ctx_r1.AgentName)("ReadOnly", true);
|
|
203
208
|
} }
|
|
204
209
|
function RealtimeSessionOverlayComponent_ng_template_2_Template(rf, ctx) { if (rf & 1) {
|
|
205
|
-
i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_ng_template_2_Conditional_0_Template, 2, 3, "div",
|
|
210
|
+
i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_ng_template_2_Conditional_0_Template, 2, 3, "div", 40);
|
|
206
211
|
} if (rf & 2) {
|
|
207
|
-
let
|
|
212
|
+
let tmp_3_0;
|
|
208
213
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
209
|
-
i0.ɵɵconditional((
|
|
214
|
+
i0.ɵɵconditional((tmp_3_0 = ctx_r1.ReviewWhiteboard) ? 0 : -1, tmp_3_0);
|
|
215
|
+
} }
|
|
216
|
+
function RealtimeSessionOverlayComponent_ng_template_4_Template(rf, ctx) { if (rf & 1) {
|
|
217
|
+
i0.ɵɵelementStart(0, "div", 42);
|
|
218
|
+
i0.ɵɵelement(1, "mj-storage-media-player", 43);
|
|
219
|
+
i0.ɵɵelementEnd();
|
|
220
|
+
} if (rf & 2) {
|
|
221
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
222
|
+
i0.ɵɵadvance();
|
|
223
|
+
i0.ɵɵproperty("FileID", ctx_r1.ReviewRecordingFileID)("Transcript", ctx_r1.ReviewCues)("Provider", ctx_r1.Provider);
|
|
210
224
|
} }
|
|
211
225
|
/**
|
|
212
226
|
* The "call mode" overlay for a live real-time voice session. Hosted by the
|
|
@@ -255,7 +269,7 @@ function RealtimeSessionOverlayComponent_ng_template_2_Template(rf, ctx) { if (r
|
|
|
255
269
|
* controls collapse to a single "Start live session" button ({@link StartLiveRequested},
|
|
256
270
|
* which resumes by chaining `lastSessionId`) and a Close button ({@link ReviewClosed}).
|
|
257
271
|
*/
|
|
258
|
-
export class RealtimeSessionOverlayComponent {
|
|
272
|
+
export class RealtimeSessionOverlayComponent extends BaseAngularComponent {
|
|
259
273
|
_agentName = 'Sage';
|
|
260
274
|
/**
|
|
261
275
|
* True while the call is MINIMIZED: the overlay hides itself (CSS, not destruction) so
|
|
@@ -276,6 +290,101 @@ export class RealtimeSessionOverlayComponent {
|
|
|
276
290
|
CurrentUser = null;
|
|
277
291
|
/** The active environment id, threaded to the surface panel's artifact viewer. */
|
|
278
292
|
EnvironmentID = '';
|
|
293
|
+
// ── Declarative UI configuration (host controls every aspect of the surface) ──
|
|
294
|
+
//
|
|
295
|
+
// Each input is a thin, PascalCase setter over a private slot in {@link _uiInputs}.
|
|
296
|
+
// When a host binds it, that field wins over the {@link UiConfig} bag (individual
|
|
297
|
+
// input precedence). Setting any of these recomputes the resolved view-model so the
|
|
298
|
+
// change takes effect on the next CD pass. Every field is optional and defaults to
|
|
299
|
+
// the historical behaviour via {@link DEFAULT_REALTIME_UI_INPUTS}, so existing call
|
|
300
|
+
// sites that bind none of them behave exactly as before.
|
|
301
|
+
//
|
|
302
|
+
// See {@link RealtimeUiInputs} / the package README for the full contract + recipes.
|
|
303
|
+
/**
|
|
304
|
+
* The chrome the widget renders: the ambient `'orb'`, the structured `'console'`, or
|
|
305
|
+
* `'auto'` (default) which starts as an orb and graduates to a console once the
|
|
306
|
+
* container is wide enough AND the user has revealed text. Mirrors
|
|
307
|
+
* {@link RealtimeUiInputs.chrome}.
|
|
308
|
+
*
|
|
309
|
+
* @example Force the rich console for a full-screen route:
|
|
310
|
+
* ```html
|
|
311
|
+
* <mj-realtime-session-overlay [Chrome]="'console'"></mj-realtime-session-overlay>
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
set Chrome(value) { this.setUiInput('chrome', value); }
|
|
315
|
+
get Chrome() { return this._uiInputs.chrome; }
|
|
316
|
+
/**
|
|
317
|
+
* In `'auto'` chrome, the container width (px) at/above which the widget may graduate
|
|
318
|
+
* to a console (still gated on text being revealed). Default `560`. Mirrors
|
|
319
|
+
* {@link RealtimeUiInputs.consoleBreakpointPx}.
|
|
320
|
+
*/
|
|
321
|
+
set ConsoleBreakpointPx(value) { this.setUiInput('consoleBreakpointPx', value); }
|
|
322
|
+
get ConsoleBreakpointPx() { return this._uiInputs.consoleBreakpointPx; }
|
|
323
|
+
/**
|
|
324
|
+
* Force dense spacing/typography regardless of width (useful for tight mobile sheets).
|
|
325
|
+
* Default `false` — compactness is otherwise inferred from the container width.
|
|
326
|
+
*/
|
|
327
|
+
set Compact(value) { this.setUiInput('compact', value); }
|
|
328
|
+
get Compact() { return this._uiInputs.compact; }
|
|
329
|
+
/** Fade non-essential controls when the session is idle (orb chrome only). Default `true`. */
|
|
330
|
+
set AutoHideControls(value) { this.setUiInput('autoHideControls', value); }
|
|
331
|
+
get AutoHideControls() { return this._uiInputs.autoHideControls; }
|
|
332
|
+
/**
|
|
333
|
+
* May the user reveal the transcript at all? `false` makes the widget a pure voice orb
|
|
334
|
+
* with no path to text (and never graduates to a console in `'auto'`). Default `true`.
|
|
335
|
+
*/
|
|
336
|
+
set AllowTextReveal(value) { this.setUiInput('allowTextReveal', value); }
|
|
337
|
+
get AllowTextReveal() { return this._uiInputs.allowTextReveal; }
|
|
338
|
+
/** Show the captions toggle. Default `true`. */
|
|
339
|
+
set ShowCaptionsControl(value) { this.setUiInput('showCaptionsControl', value); }
|
|
340
|
+
get ShowCaptionsControl() { return this._uiInputs.showCaptionsControl; }
|
|
341
|
+
/** Show the density picker inside the gear menu. Default `true`. */
|
|
342
|
+
set ShowDensityPicker(value) { this.setUiInput('showDensityPicker', value); }
|
|
343
|
+
get ShowDensityPicker() { return this._uiInputs.showDensityPicker; }
|
|
344
|
+
/** Show the minimize control (collapse the call without ending it). Default `true`. */
|
|
345
|
+
set ShowMinimize(value) { this.setUiInput('showMinimize', value); }
|
|
346
|
+
get ShowMinimize() { return this._uiInputs.showMinimize; }
|
|
347
|
+
/** Show the end-call control. Default `true` (hosts that own their own end button may hide it). */
|
|
348
|
+
set ShowEnd(value) { this.setUiInput('showEnd', value); }
|
|
349
|
+
get ShowEnd() { return this._uiInputs.showEnd; }
|
|
350
|
+
/**
|
|
351
|
+
* Show the right-hand surface panel (whiteboard / browser / interactive channels).
|
|
352
|
+
* Default `true`; only renders in console chrome and once a panel is actually earned.
|
|
353
|
+
*/
|
|
354
|
+
set ShowSurfacePanel(value) { this.setUiInput('showSurfacePanel', value); }
|
|
355
|
+
get ShowSurfacePanel() { return this._uiInputs.showSurfacePanel; }
|
|
356
|
+
/** Show the channel strip (active interactive channels). Default `true`. */
|
|
357
|
+
set ShowChannels(value) { this.setUiInput('showChannels', value); }
|
|
358
|
+
get ShowChannels() { return this._uiInputs.showChannels; }
|
|
359
|
+
/** Show the Activity rail/tab (delegations, artifacts timeline). Default `true`. */
|
|
360
|
+
set ShowActivityRail(value) { this.setUiInput('showActivityRail', value); }
|
|
361
|
+
get ShowActivityRail() { return this._uiInputs.showActivityRail; }
|
|
362
|
+
/**
|
|
363
|
+
* Show developer affordances ("open session / open run" links). Default `true`, but
|
|
364
|
+
* still additionally gated by the per-session dev-mode toggle — a hard ceiling, not a
|
|
365
|
+
* force-on.
|
|
366
|
+
*/
|
|
367
|
+
set ShowDevLinks(value) { this.setUiInput('showDevLinks', value); }
|
|
368
|
+
get ShowDevLinks() { return this._uiInputs.showDevLinks; }
|
|
369
|
+
/** Allow drag-to-resize of the surface panel. Default `true`. */
|
|
370
|
+
set AllowResize(value) { this.setUiInput('allowResize', value); }
|
|
371
|
+
get AllowResize() { return this._uiInputs.allowResize; }
|
|
372
|
+
/**
|
|
373
|
+
* A programmatic override bag, merged UNDER the individual inputs — any field also set
|
|
374
|
+
* via a dedicated `@Input()` wins over its entry here. Lets a host configure the whole
|
|
375
|
+
* surface in one binding (e.g. a saved per-context preset) without enumerating a dozen
|
|
376
|
+
* attributes.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```html
|
|
380
|
+
* <mj-realtime-session-overlay [UiConfig]="leanOverlayPreset"></mj-realtime-session-overlay>
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
set UiConfig(value) {
|
|
384
|
+
this._uiConfigBag = value ?? null;
|
|
385
|
+
this.recomputeUi();
|
|
386
|
+
}
|
|
387
|
+
get UiConfig() { return this._uiConfigBag; }
|
|
279
388
|
/**
|
|
280
389
|
* SESSION REVIEW data: when set (and no live session is active) the overlay renders
|
|
281
390
|
* the reviewed past session instead of a live call. Setting it populates the shared
|
|
@@ -287,7 +396,7 @@ export class RealtimeSessionOverlayComponent {
|
|
|
287
396
|
return;
|
|
288
397
|
}
|
|
289
398
|
this._reviewData = value;
|
|
290
|
-
if (value && !this.
|
|
399
|
+
if (value && !this.realtime.IsActive) {
|
|
291
400
|
this.enterReview(value);
|
|
292
401
|
}
|
|
293
402
|
else if (!value) {
|
|
@@ -313,8 +422,71 @@ export class RealtimeSessionOverlayComponent {
|
|
|
313
422
|
StartLiveRequested = new EventEmitter();
|
|
314
423
|
/** Review mode's Close: the host clears its review state, returning to the conversation. */
|
|
315
424
|
ReviewClosed = new EventEmitter();
|
|
316
|
-
|
|
425
|
+
// ── Declarative UI outputs (observe everything the surface does) ─────────────
|
|
426
|
+
/** Emitted when the user minimizes the call view (the call stays live). */
|
|
427
|
+
Minimized = new EventEmitter();
|
|
428
|
+
/** Emitted when the user reveals the transcript (opens text from the orb). */
|
|
429
|
+
TextRevealed = new EventEmitter();
|
|
430
|
+
/** Emitted whenever the EFFECTIVE chrome flips between `'orb'` and `'console'`. */
|
|
431
|
+
ChromeChanged = new EventEmitter();
|
|
432
|
+
/** Emitted when the connection lifecycle state changes (e.g. connecting → listening). */
|
|
433
|
+
ConnectionStateChanged = new EventEmitter();
|
|
434
|
+
/** Emitted when the mic mute is toggled; payload is the new muted state. */
|
|
435
|
+
MuteChanged = new EventEmitter();
|
|
436
|
+
/** Emitted when the interface density changes. */
|
|
437
|
+
DensityChanged = new EventEmitter();
|
|
438
|
+
/** Emitted (px) when the user finishes resizing the surface panel. */
|
|
439
|
+
SurfacePanelResized = new EventEmitter();
|
|
440
|
+
/**
|
|
441
|
+
* A generic hook fired when ANY control is invoked — even one the host hid via inputs
|
|
442
|
+
* and re-rendered itself. Lets a host observe/react to the full control surface from a
|
|
443
|
+
* single binding.
|
|
444
|
+
*/
|
|
445
|
+
ControlInvoked = new EventEmitter();
|
|
446
|
+
realtime = inject(RealtimeSessionService);
|
|
317
447
|
cdr = inject(ChangeDetectorRef);
|
|
448
|
+
/**
|
|
449
|
+
* The consumer's individually-bound UI inputs (each dedicated `@Input()` writes its own
|
|
450
|
+
* slot here). Starts empty; only fields a host actually binds appear, so the resolver's
|
|
451
|
+
* defaults fill the rest. Merged OVER {@link _uiConfigBag} (individual inputs win).
|
|
452
|
+
*/
|
|
453
|
+
_uiInputs = {};
|
|
454
|
+
/** The programmatic {@link UiConfig} override bag, merged UNDER the individual inputs. */
|
|
455
|
+
_uiConfigBag = null;
|
|
456
|
+
/**
|
|
457
|
+
* The latest measured width (px) of the rendered overlay element, fed to the resolver as
|
|
458
|
+
* {@link RealtimeUiSignals.containerWidthPx}. Maintained by a {@link ResizeObserver}; 0
|
|
459
|
+
* until the first measurement (the resolver treats 0 as "narrow", i.e. an orb in auto).
|
|
460
|
+
*/
|
|
461
|
+
containerWidthPx = 0;
|
|
462
|
+
/** Observes the rendered overlay element so chrome re-resolves as the host resizes. */
|
|
463
|
+
resizeObserver = null;
|
|
464
|
+
/** The last connection state seen on {@link RealtimeSessionService.ConnectionState$}. */
|
|
465
|
+
currentConnectionState = 'closed';
|
|
466
|
+
/**
|
|
467
|
+
* The resolved, render-ready view-model — the SINGLE source of truth the template binds
|
|
468
|
+
* to (no `&&` chains in HTML). Rebuilt by {@link recomputeUi} whenever an input, the
|
|
469
|
+
* disclosure model, the connection state, the container width, or the channel/activity
|
|
470
|
+
* set changes. Read-only to consumers via the {@link Ui} getter.
|
|
471
|
+
*/
|
|
472
|
+
// Seed from a dependency-free baseline — this runs as a field initializer, BEFORE the
|
|
473
|
+
// disclosure model / session state / ResizeObserver exist. recomputeUi() produces the real
|
|
474
|
+
// value once dependencies are ready (post-init + on every wired change source).
|
|
475
|
+
_ui = resolveRealtimeUi(DEFAULT_REALTIME_UI_INPUTS, DEFAULT_REALTIME_UI_SIGNALS);
|
|
476
|
+
/**
|
|
477
|
+
* The current resolved UI view-model. Every visibility/affordance decision the template
|
|
478
|
+
* makes reads from this — bind to its boolean fields rather than composing logic inline.
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* ```ts
|
|
482
|
+
* if (this.voice.Ui.chrome === 'console') { ... }
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
get Ui() {
|
|
486
|
+
return this._ui;
|
|
487
|
+
}
|
|
488
|
+
/** Local mic-muted state, the single source the resolver-independent controls reflect. */
|
|
489
|
+
Muted = false;
|
|
318
490
|
_reviewData = null;
|
|
319
491
|
/**
|
|
320
492
|
* Set by {@link OnStartLive} just before emitting {@link StartLiveRequested}, so the
|
|
@@ -323,8 +495,16 @@ export class RealtimeSessionOverlayComponent {
|
|
|
323
495
|
* a plain review close (clear everything).
|
|
324
496
|
*/
|
|
325
497
|
pendingLiveContinuation = false;
|
|
326
|
-
/**
|
|
498
|
+
/**
|
|
499
|
+
* The reviewed chain's history artifacts, surfaced inside the Activity tab's "Session
|
|
500
|
+
* artifacts" group (no longer their own tabs). Bound into the surface panel via
|
|
501
|
+
* {@link ReviewArtifacts}. Empty for a live session.
|
|
502
|
+
*/
|
|
327
503
|
reviewArtifacts = [];
|
|
504
|
+
/** The reviewed chain's history artifacts for the surface panel's Activity tab. */
|
|
505
|
+
get ReviewArtifacts() {
|
|
506
|
+
return this.reviewArtifacts;
|
|
507
|
+
}
|
|
328
508
|
/**
|
|
329
509
|
* True while the surface panel carries the REVIEW-registered (template-based, read-only)
|
|
330
510
|
* Whiteboard tab. Drives the review→live continuation edge: when the resumed live
|
|
@@ -335,13 +515,27 @@ export class RealtimeSessionOverlayComponent {
|
|
|
335
515
|
reviewWhiteboardTabRegistered = false;
|
|
336
516
|
/** True while the overlay renders a PAST session (review data set, no live call). */
|
|
337
517
|
get IsReviewing() {
|
|
338
|
-
return this._reviewData !== null && !this.
|
|
518
|
+
return this._reviewData !== null && !this.realtime.IsActive;
|
|
339
519
|
}
|
|
340
520
|
/**
|
|
341
521
|
* The reviewed session's rehydrated whiteboard, when it saved a parseable Whiteboard
|
|
342
522
|
* channel state — rendered read-only by the review whiteboard tab. Null = no tab.
|
|
343
523
|
*/
|
|
344
524
|
ReviewWhiteboard = null;
|
|
525
|
+
/**
|
|
526
|
+
* The `MJ: Files` id of the reviewed session's recording, or null when nothing was recorded.
|
|
527
|
+
* Bound into the storage media player, which resolves the authenticated audio itself.
|
|
528
|
+
*/
|
|
529
|
+
get ReviewRecordingFileID() {
|
|
530
|
+
return this._reviewData?.RecordingFileID ?? null;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* The reviewed session's transcript cues (built from its turns) for the recording player's
|
|
534
|
+
* transcript panel. Empty outside review.
|
|
535
|
+
*/
|
|
536
|
+
ReviewCues = [];
|
|
537
|
+
/** True while the Recording tab is registered (a recording exists for the reviewed session). */
|
|
538
|
+
reviewRecordingTabRegistered = false;
|
|
345
539
|
/** Shared session state — single source for the thread AND the activity rail. */
|
|
346
540
|
State = new RealtimeSessionState();
|
|
347
541
|
/**
|
|
@@ -365,9 +559,9 @@ export class RealtimeSessionOverlayComponent {
|
|
|
365
559
|
*/
|
|
366
560
|
ComposerOpen = false;
|
|
367
561
|
/** Live turn-state from the session service — drives the banner + connecting screen. */
|
|
368
|
-
ConnectionState$ = this.
|
|
562
|
+
ConnectionState$ = this.realtime.ConnectionState$;
|
|
369
563
|
/** Server-reported realtime model name for the active session — shown subtly in the banner. */
|
|
370
|
-
ModelName$ = this.
|
|
564
|
+
ModelName$ = this.realtime.ModelName$;
|
|
371
565
|
/**
|
|
372
566
|
* Whether the conversation renders as TEXT (the thread) or stays voice-first (the
|
|
373
567
|
* orb hero). A PERSISTED per-user preference (`mj.realtimeVoice.captions.v1`) —
|
|
@@ -383,7 +577,7 @@ export class RealtimeSessionOverlayComponent {
|
|
|
383
577
|
DevMode = false;
|
|
384
578
|
/** ID of the live server-side agent session record, for the banner's dev link. */
|
|
385
579
|
get SessionID() {
|
|
386
|
-
return this.
|
|
580
|
+
return this.realtime.CurrentAgentSessionId;
|
|
387
581
|
}
|
|
388
582
|
/**
|
|
389
583
|
* The tabbed surface panel (right panel) — channel registrations are forwarded to it.
|
|
@@ -395,11 +589,12 @@ export class RealtimeSessionOverlayComponent {
|
|
|
395
589
|
set surfaceTabsRef(ref) {
|
|
396
590
|
this.surfaceTabs = ref;
|
|
397
591
|
if (ref) {
|
|
398
|
-
// A (re)created panel starts with a FRESH tab model
|
|
399
|
-
// (
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
|
|
592
|
+
// A (re)created panel starts with a FRESH tab model. Re-register the live channel set
|
|
593
|
+
// here (gated to whiteboard + already-used channels) so hiding the panel (pure-audio
|
|
594
|
+
// return, Details off) never loses the Whiteboard or an already-used channel's tab.
|
|
595
|
+
// Artifacts live in the Activity rail now (driven by session state), so there's
|
|
596
|
+
// nothing artifact-specific to recover on the panel.
|
|
597
|
+
this.registerChannelTabs([...this.realtime.ActiveChannels]);
|
|
403
598
|
this.flushPendingChannelTabs();
|
|
404
599
|
const reveal = this.pendingRevealKey;
|
|
405
600
|
this.pendingRevealKey = null;
|
|
@@ -419,7 +614,7 @@ export class RealtimeSessionOverlayComponent {
|
|
|
419
614
|
});
|
|
420
615
|
}
|
|
421
616
|
}
|
|
422
|
-
/** The bottom dock —
|
|
617
|
+
/** The bottom dock — type-to-compose seeds + focuses its input. */
|
|
423
618
|
composer;
|
|
424
619
|
/** Channel keys already auto-revealed this session (first activity only). */
|
|
425
620
|
revealedChannelKeys = new Set();
|
|
@@ -429,6 +624,8 @@ export class RealtimeSessionOverlayComponent {
|
|
|
429
624
|
prevActive = false;
|
|
430
625
|
/** Template hosting the read-only review whiteboard (root-level, so always resolvable). */
|
|
431
626
|
reviewBoardTpl;
|
|
627
|
+
/** Template hosting the review-mode recording player (root-level, so always resolvable). */
|
|
628
|
+
recordingTpl;
|
|
432
629
|
/** Channel registrations received before the surface panel rendered (flushed in ngAfterViewInit). */
|
|
433
630
|
pendingChannelTabs = [];
|
|
434
631
|
/** True once the view (and the review-board template ref) exists. */
|
|
@@ -446,27 +643,174 @@ export class RealtimeSessionOverlayComponent {
|
|
|
446
643
|
focusChannel = null;
|
|
447
644
|
subs = [];
|
|
448
645
|
constructor() {
|
|
646
|
+
super();
|
|
449
647
|
this.loadPanelWidthPref();
|
|
450
648
|
this.loadDisclosurePref();
|
|
451
649
|
this.loadCaptionsPref();
|
|
452
|
-
this.State.Attach(this.
|
|
650
|
+
this.State.Attach(this.realtime);
|
|
453
651
|
this.subs.push(
|
|
454
652
|
// Re-render on merged-state changes; content arrival raises the disclosure level.
|
|
455
|
-
this.State.Changed$.subscribe(() => this.onSessionStateChanged()),
|
|
653
|
+
this.State.Changed$.subscribe(() => this.onSessionStateChanged()),
|
|
654
|
+
// Disclosure ratchet moved — re-resolve the view-model (thread/composer/gear gates).
|
|
655
|
+
this.Disclosure.Changed$.subscribe(() => this.recomputeUi()),
|
|
456
656
|
// One surface tab per registry-resolved channel plugin (replays the current set).
|
|
457
|
-
this.
|
|
657
|
+
this.realtime.ActiveChannels$.subscribe(channels => { this.registerChannelTabs(channels); this.recomputeUi(); }),
|
|
458
658
|
// Any channel may request the focus layout through its host context.
|
|
459
|
-
this.
|
|
659
|
+
this.realtime.ChannelFocus$.subscribe(event => this.onChannelFocus(event.Channel, event.Focused)),
|
|
460
660
|
// The agent ACTED on a channel — auto-reveal its surface tab on first activity.
|
|
461
|
-
this.
|
|
661
|
+
this.realtime.ChannelActivity$.subscribe(plugin => this.onChannelActivity(plugin)),
|
|
462
662
|
// Live/idle flips: reset/ratchet disclosure + re-evaluate the review-vs-live branch.
|
|
463
|
-
this.
|
|
663
|
+
this.realtime.Active$.subscribe(active => this.onActiveChanged(active)),
|
|
664
|
+
// Connection lifecycle drives chrome (the `connecting` loader) + the public output.
|
|
665
|
+
this.realtime.ConnectionState$.subscribe(state => this.onConnectionStateChanged(state)));
|
|
464
666
|
}
|
|
465
667
|
ngAfterViewInit() {
|
|
466
668
|
this.viewReady = true;
|
|
669
|
+
this.startResizeObserver();
|
|
467
670
|
this.flushPendingChannelTabs();
|
|
468
671
|
this.registerReviewBoardTab();
|
|
469
672
|
this.registerReviewArtifactTabs();
|
|
673
|
+
// Cues were already built by enterReview() when ReviewData was set; here we only need to
|
|
674
|
+
// (re)register the recording tab now that the #recordingTpl ref exists. The storage media
|
|
675
|
+
// player resolves the authenticated audio itself from the RecordingFileID.
|
|
676
|
+
this.registerReviewRecordingTab();
|
|
677
|
+
}
|
|
678
|
+
// ── Declarative UI config: inputs → signals → resolved view-model ────────────
|
|
679
|
+
/**
|
|
680
|
+
* Writes one individually-bound UI input into {@link _uiInputs} and re-resolves the
|
|
681
|
+
* view-model. `undefined` clears the slot (so the {@link UiConfig} bag / default applies).
|
|
682
|
+
* Called by every dedicated `@Input()` setter.
|
|
683
|
+
*
|
|
684
|
+
* @param key the {@link RealtimeUiInputs} field to set
|
|
685
|
+
* @param value the bound value, or `undefined` to clear the override
|
|
686
|
+
*/
|
|
687
|
+
setUiInput(key, value) {
|
|
688
|
+
if (value === undefined) {
|
|
689
|
+
delete this._uiInputs[key];
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
this._uiInputs[key] = value;
|
|
693
|
+
}
|
|
694
|
+
this.recomputeUi();
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* The effective consumer inputs: the {@link UiConfig} bag with the individually-bound
|
|
698
|
+
* inputs layered ON TOP, so a dedicated `@Input()` always wins over its bag entry.
|
|
699
|
+
*/
|
|
700
|
+
get mergedUiInputs() {
|
|
701
|
+
return { ...(this._uiConfigBag ?? {}), ...this._uiInputs };
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Maps the runtime {@link RealtimeConnectionState} onto the resolver's
|
|
705
|
+
* {@link RealtimeUiConnectionState}. `'closed'` (no live call) maps to `'idle'`; the
|
|
706
|
+
* other states pass through 1:1.
|
|
707
|
+
*/
|
|
708
|
+
mapConnectionState(state) {
|
|
709
|
+
return state === 'closed' ? 'idle' : state;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Builds the live {@link RealtimeUiSignals} the resolver consumes, reading current facts
|
|
713
|
+
* from the disclosure model, the session/channel state, review mode, focus mode, and the
|
|
714
|
+
* {@link ResizeObserver}-measured width. Pure read — no side effects.
|
|
715
|
+
*/
|
|
716
|
+
buildSignals() {
|
|
717
|
+
// Defensive: this can be reached before every dependency is wired (field-init / early
|
|
718
|
+
// change-detection). Fall back to the dependency-free baseline rather than throw — the
|
|
719
|
+
// next recompute, once everything exists, produces the real value.
|
|
720
|
+
const disclosure = this.Disclosure;
|
|
721
|
+
if (!disclosure) {
|
|
722
|
+
return DEFAULT_REALTIME_UI_SIGNALS;
|
|
723
|
+
}
|
|
724
|
+
const reviewing = this.IsReviewing;
|
|
725
|
+
return {
|
|
726
|
+
containerWidthPx: this.containerWidthPx ?? 0,
|
|
727
|
+
// Text intent = the user EXPLICITLY asked to see text this session — i.e. captions are on
|
|
728
|
+
// (the captions toggle, the hero's "Show the conversation", RevealText(), and SetCaptions()
|
|
729
|
+
// all route through ShowCaptions). NOT the disclosure ratchet: a power user still opens to
|
|
730
|
+
// the calm orb until they ask for text, matching the historical ShowHero = !ShowCaptions.
|
|
731
|
+
textRevealed: this.ShowCaptions,
|
|
732
|
+
disclosureShowThread: disclosure.ShowThread,
|
|
733
|
+
disclosureShowComposer: disclosure.ShowComposer,
|
|
734
|
+
disclosureShowPanel: disclosure.ShowPanel,
|
|
735
|
+
disclosureShowGear: disclosure.ShowGear,
|
|
736
|
+
// A surface to populate: the on-demand Details peek, or review (always has a surface).
|
|
737
|
+
surfacePanelEarned: this.DetailsPeek || reviewing,
|
|
738
|
+
hasChannels: (this.realtime?.ActiveChannels ?? []).some(c => c.HasSurface()),
|
|
739
|
+
hasActivity: (this.State?.Cards?.length ?? 0) > 0,
|
|
740
|
+
devMode: this.DevMode,
|
|
741
|
+
isReviewing: reviewing,
|
|
742
|
+
channelFocus: this.ChannelFocusMode,
|
|
743
|
+
connectionState: this.mapConnectionState(this.currentConnectionState)
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Re-resolves {@link Ui} from the merged inputs + current signals and emits the chrome /
|
|
748
|
+
* connection-state outputs when they actually change. Safe to call on any of the wired
|
|
749
|
+
* change sources (input write, disclosure ratchet, connection flip, resize, channel set);
|
|
750
|
+
* the resolver is pure so redundant calls are cheap. Always marks for check.
|
|
751
|
+
*/
|
|
752
|
+
recomputeUi() {
|
|
753
|
+
const next = resolveRealtimeUi(this.mergedUiInputs, this.buildSignals());
|
|
754
|
+
const prevChrome = this._ui.chrome;
|
|
755
|
+
this._ui = next;
|
|
756
|
+
if (next.chrome !== prevChrome) {
|
|
757
|
+
this.ChromeChanged.emit(next.chrome);
|
|
758
|
+
}
|
|
759
|
+
this.cdr.markForCheck();
|
|
760
|
+
}
|
|
761
|
+
/** Connection lifecycle changed: store it, re-resolve chrome, and surface the public output. */
|
|
762
|
+
onConnectionStateChanged(state) {
|
|
763
|
+
if (state === this.currentConnectionState) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
this.currentConnectionState = state;
|
|
767
|
+
this.ConnectionStateChanged.emit(this.mapConnectionState(state));
|
|
768
|
+
this.recomputeUi();
|
|
769
|
+
}
|
|
770
|
+
// ── Container-width observation (drives the auto orb ↔ console graduation) ────
|
|
771
|
+
/**
|
|
772
|
+
* Starts observing the rendered overlay element's width so the resolver can graduate
|
|
773
|
+
* `'auto'` chrome to a console when the host gives it room. Guards SSR / environments
|
|
774
|
+
* without `ResizeObserver` (plain-node tests) — there the width simply stays 0 (an orb in
|
|
775
|
+
* auto), which is the safe ambient default. Idempotent.
|
|
776
|
+
*/
|
|
777
|
+
startResizeObserver() {
|
|
778
|
+
if (this.resizeObserver || typeof ResizeObserver !== 'function') {
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
const el = this.overlayElement() ?? this.hostRef.nativeElement;
|
|
782
|
+
if (!el) {
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
// Seed an immediate measurement so the first resolve sees a real width. Deferred to a
|
|
786
|
+
// microtask (same reasoning as OnPanelWideChanged above): ngAfterViewInit runs inside the
|
|
787
|
+
// SAME change-detection pass that already checked bindings derived from Ui (e.g. the
|
|
788
|
+
// template's `@if (ShowPanelArea)` and `[Compact]`) using the field-initializer's width-0
|
|
789
|
+
// baseline. Recomputing synchronously here can flip chrome orb→console mid-pass and trip
|
|
790
|
+
// ExpressionChangedAfterItHasBeenCheckedError (NG0100). Landing the seed measurement in a
|
|
791
|
+
// fresh microtask lets it recompute in its own CD cycle instead.
|
|
792
|
+
Promise.resolve().then(() => {
|
|
793
|
+
this.applyContainerWidth(el.getBoundingClientRect ? el.getBoundingClientRect().width : 0);
|
|
794
|
+
});
|
|
795
|
+
this.resizeObserver = new ResizeObserver(entries => {
|
|
796
|
+
const width = entries[0]?.contentRect?.width ?? 0;
|
|
797
|
+
this.ngZone.run(() => Promise.resolve().then(() => this.applyContainerWidth(width)));
|
|
798
|
+
});
|
|
799
|
+
this.resizeObserver.observe(el);
|
|
800
|
+
}
|
|
801
|
+
/** Adopts a new measured width and re-resolves only when it actually changed. */
|
|
802
|
+
applyContainerWidth(width) {
|
|
803
|
+
const rounded = Math.round(width);
|
|
804
|
+
if (rounded === this.containerWidthPx) {
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
this.containerWidthPx = rounded;
|
|
808
|
+
this.recomputeUi();
|
|
809
|
+
}
|
|
810
|
+
/** Disconnects the container-width observer. */
|
|
811
|
+
stopResizeObserver() {
|
|
812
|
+
this.resizeObserver?.disconnect();
|
|
813
|
+
this.resizeObserver = null;
|
|
470
814
|
}
|
|
471
815
|
// ── Surface-panel sizing (flex layout + pointer-drag handle; width persisted per-user) ──
|
|
472
816
|
//
|
|
@@ -497,9 +841,16 @@ export class RealtimeSessionOverlayComponent {
|
|
|
497
841
|
get PanelAreaSize() {
|
|
498
842
|
return this.PanelCollapsed ? SURFACE_PANEL_COLLAPSED_WIDTH : this.PanelWidthPx;
|
|
499
843
|
}
|
|
500
|
-
/**
|
|
844
|
+
/**
|
|
845
|
+
* Resizing is meaningless while collapsed or when the surface fills (focus mode), and a
|
|
846
|
+
* host may hard-disable it via `[AllowResize]="false"` — in review the resolver's
|
|
847
|
+
* `allowResize` is false (no live surface to resize), so this also reads the host flag
|
|
848
|
+
* directly (defaulting to the historical `true`) rather than the resolved value, keeping
|
|
849
|
+
* drag available in review exactly as before.
|
|
850
|
+
*/
|
|
501
851
|
get PanelResizeDisabled() {
|
|
502
|
-
|
|
852
|
+
const hostAllows = this.mergedUiInputs.allowResize ?? DEFAULT_REALTIME_UI_INPUTS.allowResize;
|
|
853
|
+
return this.PanelCollapsed || this.ChannelFocusMode || !hostAllows;
|
|
503
854
|
}
|
|
504
855
|
OnPanelCollapsedChange(collapsed) {
|
|
505
856
|
this.PanelCollapsed = collapsed;
|
|
@@ -509,7 +860,18 @@ export class RealtimeSessionOverlayComponent {
|
|
|
509
860
|
OnPanelWideChanged(wide) {
|
|
510
861
|
this.PanelWide = wide;
|
|
511
862
|
if (this.userPanelWidth === null) {
|
|
512
|
-
|
|
863
|
+
// This can fire synchronously from the surface-tabs child during the FIRST change-detection
|
|
864
|
+
// pass, while hostWidth() transitions 0 → real across that same pass — setting the bound width
|
|
865
|
+
// inline then trips ExpressionChangedAfterItHasBeenCheckedError (380 → 308). Defer to a
|
|
866
|
+
// microtask so the recompute lands in a fresh CD cycle. (Drag/reset handlers stay synchronous —
|
|
867
|
+
// those run on real user events, not during CD.)
|
|
868
|
+
Promise.resolve().then(() => {
|
|
869
|
+
if (this.userPanelWidth === null) {
|
|
870
|
+
this.PanelWidthPx = DefaultSurfacePanelWidth(this.PanelWide, this.hostWidth());
|
|
871
|
+
this.cdr.markForCheck();
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
return;
|
|
513
875
|
}
|
|
514
876
|
this.cdr.markForCheck();
|
|
515
877
|
}
|
|
@@ -548,15 +910,21 @@ export class RealtimeSessionOverlayComponent {
|
|
|
548
910
|
return;
|
|
549
911
|
}
|
|
550
912
|
this.teardownPanelResize();
|
|
913
|
+
let resizedTo = null;
|
|
551
914
|
if (IsSurfacePanelDrag(this.panelResizeStartX, event.clientX)) {
|
|
552
915
|
this.userPanelWidth = this.PanelWidthPx;
|
|
553
916
|
this.persistPanelWidth(this.PanelWidthPx);
|
|
917
|
+
resizedTo = this.PanelWidthPx;
|
|
554
918
|
}
|
|
555
919
|
else {
|
|
556
920
|
this.PanelWidthPx = this.panelResizeStartWidth;
|
|
557
921
|
}
|
|
558
922
|
this.ngZone.run(() => {
|
|
559
923
|
this.IsPanelResizing = false;
|
|
924
|
+
// Surface the new width to the host only on a genuine drag (not a bare click).
|
|
925
|
+
if (resizedTo !== null) {
|
|
926
|
+
this.SurfacePanelResized.emit(resizedTo);
|
|
927
|
+
}
|
|
560
928
|
this.cdr.markForCheck();
|
|
561
929
|
});
|
|
562
930
|
}
|
|
@@ -610,27 +978,50 @@ export class RealtimeSessionOverlayComponent {
|
|
|
610
978
|
}
|
|
611
979
|
// ── Progressive disclosure (the console that grows with you) ────────────────
|
|
612
980
|
/**
|
|
613
|
-
* True while the PURE-AUDIO hero (the orb) owns the main column.
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
* always shows the thread.
|
|
981
|
+
* True while the PURE-AUDIO hero (the orb) owns the main column. Now reads from the
|
|
982
|
+
* resolved view-model ({@link Ui}): the resolver decides hero-vs-thread from the
|
|
983
|
+
* effective chrome + text intent + review, so host inputs (`[Chrome]`,
|
|
984
|
+
* `[AllowTextReveal]`, the breakpoint) gate it. Review still always shows the thread.
|
|
617
985
|
*/
|
|
618
986
|
get ShowHero() {
|
|
619
|
-
return
|
|
987
|
+
return this._ui.showHero;
|
|
620
988
|
}
|
|
621
989
|
/**
|
|
622
|
-
* Whether the surface-panel area renders.
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
* never surprise the user with the whole tab panel.
|
|
990
|
+
* Whether the surface-panel area renders. Driven by the resolved {@link Ui.showSurfacePanel}
|
|
991
|
+
* (console + earned + host's `[ShowSurfacePanel]`) OR review mode, which always carries the
|
|
992
|
+
* panel. The Details toggle still earns the panel on demand (it flips `surfacePanelEarned`).
|
|
626
993
|
*/
|
|
627
994
|
get ShowPanelArea() {
|
|
628
|
-
return this.
|
|
995
|
+
return this._ui.showSurfacePanel || this.IsReviewing;
|
|
629
996
|
}
|
|
630
|
-
/** The Details (tabs) toggle is
|
|
997
|
+
/** The Details (tabs) toggle is offered while live — it's the panel's one door. */
|
|
631
998
|
get ShowDetailsControl() {
|
|
632
999
|
return !this.IsReviewing;
|
|
633
1000
|
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Whether the active-channels strip renders. Preserves the historical disclosure gate
|
|
1003
|
+
* (level 2+, {@link RealtimeDisclosureModel.ShowComposer}) AND honours the host's
|
|
1004
|
+
* `[ShowChannels]` input + the resolver's focus rule: a host that sets `[ShowChannels]`
|
|
1005
|
+
* false hard-disables the strip, and channel-focus mode hides it. The strip component
|
|
1006
|
+
* itself self-hides when no channels are active, so this stays decluttered.
|
|
1007
|
+
*/
|
|
1008
|
+
get ChannelStripVisible() {
|
|
1009
|
+
// Reads the RESOLVED Ui.showChannelStrip flag — which already ANDs the host's
|
|
1010
|
+
// [ShowChannels] input, an active channel, console chrome, and the no-focus rule.
|
|
1011
|
+
// In orb/compact chrome it is false, so the overlay never shows the "CHANNELS · Voice"
|
|
1012
|
+
// strip (the lean Calm-Orb surface). The strip component itself still self-hides when
|
|
1013
|
+
// no channels are active, keeping the console decluttered too.
|
|
1014
|
+
return this._ui.showChannelStrip;
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Whether the surface panel's Activity tab should be shown. Reads the resolved
|
|
1018
|
+
* {@link Ui.showActivityTab} (host's `[ShowActivityRail]` AND activity/review AND console)
|
|
1019
|
+
* OR review mode — a past session's activity is always relevant even before the resolver's
|
|
1020
|
+
* console gate would otherwise allow it.
|
|
1021
|
+
*/
|
|
1022
|
+
get ShowActivityTab() {
|
|
1023
|
+
return this._ui.showActivityTab || this.IsReviewing;
|
|
1024
|
+
}
|
|
634
1025
|
/** Reads the persisted disclosure milestones (tolerant; defaults to day one). */
|
|
635
1026
|
loadDisclosurePref() {
|
|
636
1027
|
try {
|
|
@@ -676,7 +1067,8 @@ export class RealtimeSessionOverlayComponent {
|
|
|
676
1067
|
* A pure-audio user stays in pure audio until THEY ask for more.
|
|
677
1068
|
*/
|
|
678
1069
|
onSessionStateChanged() {
|
|
679
|
-
|
|
1070
|
+
// State.Cards drives the resolver's hasActivity signal — re-resolve (also marks for check).
|
|
1071
|
+
this.recomputeUi();
|
|
680
1072
|
}
|
|
681
1073
|
/**
|
|
682
1074
|
* The agent ACTED on a channel (a tool call routed to its local executor — e.g. the
|
|
@@ -692,16 +1084,25 @@ export class RealtimeSessionOverlayComponent {
|
|
|
692
1084
|
}
|
|
693
1085
|
this.revealedChannelKeys.add(plugin.ChannelName);
|
|
694
1086
|
this.DetailsPeek = true; // the panel shows via the same on-demand mechanism Details uses
|
|
1087
|
+
// FIRST USE: a non-whiteboard channel was tab-less until now — register its tab
|
|
1088
|
+
// SYNCHRONOUSLY (before the reveal/focus below) so the channel exists to be revealed.
|
|
1089
|
+
// The whiteboard already has its tab from session start; re-registering is idempotent.
|
|
1090
|
+
if (plugin.HasSurface()) {
|
|
1091
|
+
this.registerPluginChannelTab(plugin);
|
|
1092
|
+
}
|
|
695
1093
|
if (this.surfaceTabs) {
|
|
696
1094
|
// Stream handler (a tool call), not a change-detection pass — reveal synchronously
|
|
697
|
-
// so the board is the visible tab the instant the agent's first stroke lands.
|
|
1095
|
+
// so the board is the visible tab the instant the agent's first stroke lands. The
|
|
1096
|
+
// microtask-deferred RegisterChannelTab above is ordered before this RevealChannel,
|
|
1097
|
+
// so the tab is in place when the focus lands.
|
|
698
1098
|
this.surfaceTabs.RevealChannel(plugin.ChannelName);
|
|
699
1099
|
}
|
|
700
1100
|
else {
|
|
701
1101
|
// Panel not rendered yet (the peek just created it) — reveal once it exists.
|
|
702
1102
|
this.pendingRevealKey = plugin.ChannelName;
|
|
703
1103
|
}
|
|
704
|
-
|
|
1104
|
+
// DetailsPeek flipped — surfacePanelEarned changed; re-resolve (marks for check).
|
|
1105
|
+
this.recomputeUi();
|
|
705
1106
|
}
|
|
706
1107
|
/** Active$ edges: session start resets the volatile level; session end ratchets + persists. */
|
|
707
1108
|
onActiveChanged(active) {
|
|
@@ -734,7 +1135,7 @@ export class RealtimeSessionOverlayComponent {
|
|
|
734
1135
|
audioRafHandle = null;
|
|
735
1136
|
/** Last attribute values written (attributes are only touched on change). */
|
|
736
1137
|
lastAudioLive = null;
|
|
737
|
-
|
|
1138
|
+
lastDirection = null;
|
|
738
1139
|
/** Starts the sampling loop (idempotent). Runs outside Angular — no CD per frame. */
|
|
739
1140
|
startAudioVisualLoop() {
|
|
740
1141
|
if (this.audioRafHandle !== null || typeof requestAnimationFrame !== 'function') {
|
|
@@ -747,7 +1148,7 @@ export class RealtimeSessionOverlayComponent {
|
|
|
747
1148
|
if (this.Hidden) {
|
|
748
1149
|
return; // minimized — skip the work, keep the loop armed
|
|
749
1150
|
}
|
|
750
|
-
const frame = this.audioSmoother.Next(this.
|
|
1151
|
+
const frame = this.audioSmoother.Next(this.realtime.GetAudioActivity(), performance.now());
|
|
751
1152
|
this.applyAudioVisualFrame(frame);
|
|
752
1153
|
};
|
|
753
1154
|
this.audioRafHandle = requestAnimationFrame(tick);
|
|
@@ -761,7 +1162,7 @@ export class RealtimeSessionOverlayComponent {
|
|
|
761
1162
|
}
|
|
762
1163
|
this.audioSmoother.Reset();
|
|
763
1164
|
this.lastAudioLive = null;
|
|
764
|
-
this.
|
|
1165
|
+
this.lastDirection = null;
|
|
765
1166
|
this.overlayElement()?.setAttribute('data-audio-live', 'false');
|
|
766
1167
|
}
|
|
767
1168
|
/** The rendered `.call-overlay` element (the :host is display:contents). */
|
|
@@ -788,8 +1189,8 @@ export class RealtimeSessionOverlayComponent {
|
|
|
788
1189
|
for (let i = 0; i < frame.Bins.length; i++) {
|
|
789
1190
|
el.style.setProperty(`--eq-${i + 1}`, frame.Bins[i].toFixed(3));
|
|
790
1191
|
}
|
|
791
|
-
if (frame.Direction !== this.
|
|
792
|
-
this.
|
|
1192
|
+
if (frame.Direction !== this.lastDirection) {
|
|
1193
|
+
this.lastDirection = frame.Direction;
|
|
793
1194
|
el.setAttribute('data-voice-dir', frame.Direction);
|
|
794
1195
|
}
|
|
795
1196
|
}
|
|
@@ -800,37 +1201,45 @@ export class RealtimeSessionOverlayComponent {
|
|
|
800
1201
|
// Land on the marquee surface (channels lead the strip; Activity is pinned last).
|
|
801
1202
|
setTimeout(() => this.surfaceTabs?.FocusFirstTab());
|
|
802
1203
|
}
|
|
803
|
-
this.
|
|
1204
|
+
this.ControlInvoked.emit('surface');
|
|
1205
|
+
// DetailsPeek drives surfacePanelEarned — re-resolve (marks for check).
|
|
1206
|
+
this.recomputeUi();
|
|
804
1207
|
}
|
|
805
|
-
/**
|
|
1208
|
+
/**
|
|
1209
|
+
* The gear popover picked an interface density — apply + persist + emit. `Simple` IS the
|
|
1210
|
+
* "pure audio" surface (the standalone headphones control was folded into the gear here): it
|
|
1211
|
+
* returns to the orb-only view AND makes it stick across refresh/next call, so picking it also
|
|
1212
|
+
* closes any open Details peek so the surface is genuinely orb-only — matching the old app-bar
|
|
1213
|
+
* "Pure audio" button exactly. In-session reveals ("Show the conversation", T-to-type, Details)
|
|
1214
|
+
* stay available and ephemeral; switching density back to Standard/Pro/Auto restores the console.
|
|
1215
|
+
*/
|
|
806
1216
|
OnDensityChanged(density) {
|
|
807
1217
|
this.Disclosure.SetDensity(density);
|
|
808
1218
|
this.persistDisclosure(SerializeUxMilestones(this.Disclosure.Milestones));
|
|
1219
|
+
if (density === 'simple') {
|
|
1220
|
+
this.DetailsPeek = false; // Simple = pure audio: collapse the surface peek back to the bare orb.
|
|
1221
|
+
}
|
|
1222
|
+
this.DensityChanged.emit(density);
|
|
1223
|
+
this.ControlInvoked.emit('density');
|
|
1224
|
+
// Density (and possibly DetailsPeek) changed — re-resolve (marks for check).
|
|
1225
|
+
this.recomputeUi();
|
|
809
1226
|
}
|
|
810
1227
|
/** The hero's "Show the conversation" affordance — turns the text preference on. */
|
|
811
1228
|
OnTextReveal() {
|
|
812
1229
|
this.OnCaptionsToggled(true);
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
* The app-bar's "Pure audio": returns to the orb-only surface AND makes it stick —
|
|
816
|
-
* it sets the persisted interface density to `simple` (the same setting the gear
|
|
817
|
-
* writes), so a refresh / next call still opens pure audio. In-session reveals
|
|
818
|
-
* ("Show the conversation", T-to-type, Details) remain available and stay ephemeral;
|
|
819
|
-
* the gear's density control switches back to Standard/Pro/Auto whenever wanted.
|
|
820
|
-
*/
|
|
821
|
-
OnPureAudio() {
|
|
822
|
-
this.Disclosure.SetDensity('simple');
|
|
823
|
-
this.persistDisclosure(SerializeUxMilestones(this.Disclosure.Milestones));
|
|
824
|
-
this.DetailsPeek = false;
|
|
825
|
-
this.cdr.markForCheck();
|
|
1230
|
+
this.TextRevealed.emit();
|
|
1231
|
+
this.ControlInvoked.emit('reveal-text');
|
|
826
1232
|
}
|
|
827
1233
|
/** App-bar Minimize: hide the call view (CSS) — the call stays fully live. */
|
|
828
1234
|
OnMinimize() {
|
|
829
|
-
this.
|
|
1235
|
+
this.realtime.SetMinimized(true);
|
|
1236
|
+
this.Minimized.emit();
|
|
1237
|
+
this.ControlInvoked.emit('minimize');
|
|
830
1238
|
}
|
|
831
1239
|
/** App-bar / strip End: tear the session down, then notify the host. */
|
|
832
1240
|
async OnEndCall() {
|
|
833
|
-
|
|
1241
|
+
this.ControlInvoked.emit('end');
|
|
1242
|
+
await this.realtime.EndRealtimeSession();
|
|
834
1243
|
this.Ended.emit();
|
|
835
1244
|
}
|
|
836
1245
|
/** Maps the realtime state onto the hero orb's `data-state` (active turn-states only). */
|
|
@@ -853,29 +1262,38 @@ export class RealtimeSessionOverlayComponent {
|
|
|
853
1262
|
}
|
|
854
1263
|
}
|
|
855
1264
|
/**
|
|
856
|
-
*
|
|
857
|
-
*
|
|
858
|
-
*
|
|
859
|
-
*
|
|
1265
|
+
* TYPE-TO-COMPOSE: during a live call, pressing any single printable character (with nothing
|
|
1266
|
+
* focused) reveals the composer, raises disclosure to the engaged level, AND seeds that very
|
|
1267
|
+
* character into the draft — the user just starts typing and their first key is captured (the
|
|
1268
|
+
* old dedicated "T" key is gone; "t"/"T" now types like any other letter, and the visible
|
|
1269
|
+
* "press T to type" hint was removed in favour of this natural behaviour). Ignored while
|
|
1270
|
+
* review/minimized/inactive, with Ctrl/Cmd/Alt modifiers (don't hijack shortcuts), for
|
|
1271
|
+
* non-printable named keys (Enter/Tab/Arrow/Escape/F-keys → `key.length > 1`), for a bare
|
|
1272
|
+
* Space (ambiguous — no message starts with a space), or when an editable element / a
|
|
1273
|
+
* keyboard-capturing surface (the remote-browser canvas) already holds focus.
|
|
860
1274
|
*/
|
|
861
1275
|
OnDocumentKeydown(event) {
|
|
862
|
-
if (this.Hidden || this.IsReviewing || !this.
|
|
1276
|
+
if (this.Hidden || this.IsReviewing || !this.realtime.IsActive) {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
if (event.metaKey || event.ctrlKey || event.altKey) {
|
|
863
1280
|
return;
|
|
864
1281
|
}
|
|
865
|
-
if (
|
|
1282
|
+
if (event.key.length !== 1 || event.key === ' ') {
|
|
866
1283
|
return;
|
|
867
1284
|
}
|
|
868
1285
|
if (this.isEditableTarget(event.target) || this.isKeyboardCapturingSurfaceFocused()) {
|
|
869
1286
|
return;
|
|
870
1287
|
}
|
|
871
1288
|
event.preventDefault();
|
|
1289
|
+
const typedChar = event.key;
|
|
872
1290
|
this.OnComposerOpenChanged(true);
|
|
873
|
-
// The dock may have just been created — focus after this CD pass.
|
|
874
|
-
setTimeout(() => this.composer?.
|
|
1291
|
+
// The dock may have just been created — seed the typed character + focus after this CD pass.
|
|
1292
|
+
setTimeout(() => this.composer?.AppendAndFocus(typedChar));
|
|
875
1293
|
}
|
|
876
1294
|
/**
|
|
877
1295
|
* True when the event target is a native text-editing element (an input, textarea, or contentEditable),
|
|
878
|
-
* where a bare letter should type — not trigger
|
|
1296
|
+
* where a bare letter should type into that element — not trigger type-to-compose.
|
|
879
1297
|
*
|
|
880
1298
|
* @param eventTarget The keydown event's target.
|
|
881
1299
|
* @returns Whether the target is an editable element.
|
|
@@ -898,20 +1316,22 @@ export class RealtimeSessionOverlayComponent {
|
|
|
898
1316
|
return !!active && active.hasAttribute('data-mj-capture-keys');
|
|
899
1317
|
}
|
|
900
1318
|
/**
|
|
901
|
-
* The composer dock opened (strip Type control /
|
|
902
|
-
* control). Opening raises the 'engaged' milestone for the cross-session ratchet —
|
|
1319
|
+
* The composer dock opened (strip Type control / type-to-compose keystroke) or closed (the
|
|
1320
|
+
* dock's hide control). Opening raises the 'engaged' milestone for the cross-session ratchet —
|
|
903
1321
|
* but the dock itself stays a per-session, user-owned toggle either way.
|
|
904
1322
|
*/
|
|
905
1323
|
OnComposerOpenChanged(open) {
|
|
906
1324
|
this.ComposerOpen = open;
|
|
907
1325
|
if (open) {
|
|
908
|
-
this.Disclosure.Raise('engaged');
|
|
1326
|
+
this.Disclosure.Raise('engaged'); // emits Changed$ → recomputeUi()
|
|
1327
|
+
this.ControlInvoked.emit('type');
|
|
909
1328
|
setTimeout(() => this.composer?.FocusInput());
|
|
910
1329
|
}
|
|
911
1330
|
this.cdr.markForCheck();
|
|
912
1331
|
}
|
|
913
1332
|
ngOnDestroy() {
|
|
914
1333
|
this.stopAudioVisualLoop();
|
|
1334
|
+
this.stopResizeObserver();
|
|
915
1335
|
if (this.IsPanelResizing) {
|
|
916
1336
|
this.teardownPanelResize();
|
|
917
1337
|
this.IsPanelResizing = false;
|
|
@@ -952,10 +1372,14 @@ export class RealtimeSessionOverlayComponent {
|
|
|
952
1372
|
setTimeout(() => this.surfaceTabs?.FocusArtifact(artifact));
|
|
953
1373
|
}
|
|
954
1374
|
/**
|
|
955
|
-
* Registers
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
*
|
|
1375
|
+
* Registers a surface tab per active channel plugin THAT HAS A SURFACE — but ONLY for
|
|
1376
|
+
* channels already in play: the WHITEBOARD tabs immediately (a user may be the first to
|
|
1377
|
+
* draw), every OTHER channel stays tab-less until the agent first USES it (its first
|
|
1378
|
+
* {@link onChannelActivity}). This keeps the strip decluttered to surfaces actually in
|
|
1379
|
+
* play instead of pre-listing every registry-resolved channel.
|
|
1380
|
+
*
|
|
1381
|
+
* Server-only channels ({@link BaseRealtimeChannelClient.HasSurface} === `false`) render no
|
|
1382
|
+
* tab — their tools + perception are wired by the session service regardless.
|
|
959
1383
|
*/
|
|
960
1384
|
registerChannelTabs(channels) {
|
|
961
1385
|
this.cleanupStaleReviewBoardTab(channels);
|
|
@@ -963,15 +1387,23 @@ export class RealtimeSessionOverlayComponent {
|
|
|
963
1387
|
if (!plugin.HasSurface()) {
|
|
964
1388
|
continue; // server-only channel — no surface to tab.
|
|
965
1389
|
}
|
|
966
|
-
this.
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
Plugin: plugin
|
|
971
|
-
});
|
|
1390
|
+
if (!ShouldRegisterChannelTabUpFront(plugin.ChannelName, this.realtime.HasChannelBeenUsed(plugin.ChannelName))) {
|
|
1391
|
+
continue; // not the whiteboard and not used yet — it earns its tab on first activity.
|
|
1392
|
+
}
|
|
1393
|
+
this.registerPluginChannelTab(plugin);
|
|
972
1394
|
}
|
|
973
1395
|
this.cdr.markForCheck();
|
|
974
1396
|
}
|
|
1397
|
+
/** Registers (or upgrades) one channel plugin's surface tab on the panel. */
|
|
1398
|
+
registerPluginChannelTab(plugin) {
|
|
1399
|
+
this.RegisterChannelTab({
|
|
1400
|
+
Key: plugin.ChannelName,
|
|
1401
|
+
Title: plugin.TabTitle,
|
|
1402
|
+
Icon: plugin.TabIcon,
|
|
1403
|
+
Color: plugin.TabColor,
|
|
1404
|
+
Plugin: plugin
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
975
1407
|
/**
|
|
976
1408
|
* REVIEW→LIVE continuation edge: when the resumed live session resolves its channel set
|
|
977
1409
|
* WITHOUT a Whiteboard channel, the review-registered read-only board tab is now a dead
|
|
@@ -984,7 +1416,7 @@ export class RealtimeSessionOverlayComponent {
|
|
|
984
1416
|
if (!this.reviewWhiteboardTabRegistered) {
|
|
985
1417
|
return;
|
|
986
1418
|
}
|
|
987
|
-
if (ShouldRemoveReviewWhiteboardTab(this.
|
|
1419
|
+
if (ShouldRemoveReviewWhiteboardTab(this.realtime.IsActive, this.reviewWhiteboardTabRegistered, channels)) {
|
|
988
1420
|
this.surfaceTabs?.RemoveTab('Whiteboard');
|
|
989
1421
|
// Also drop a still-queued review registration (panel not rendered yet — rare but possible).
|
|
990
1422
|
this.pendingChannelTabs = this.pendingChannelTabs.filter(r => !(r.Key === 'Whiteboard' && r.Content));
|
|
@@ -1011,14 +1443,24 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1011
1443
|
OnCaptionsToggled(on) {
|
|
1012
1444
|
this.ShowCaptions = on;
|
|
1013
1445
|
if (on) {
|
|
1014
|
-
this.Disclosure.Raise('text');
|
|
1446
|
+
this.Disclosure.Raise('text'); // emits Changed$ → recomputeUi()
|
|
1015
1447
|
}
|
|
1016
1448
|
this.persistCaptionsPref();
|
|
1017
|
-
this.
|
|
1449
|
+
this.ControlInvoked.emit('captions');
|
|
1450
|
+
// ShowCaptions feeds the resolver's `textRevealed` signal (buildSignals), so BOTH directions must
|
|
1451
|
+
// re-resolve the view-model. Turning ON already recomputes via Disclosure.Raise → Changed$, but
|
|
1452
|
+
// turning OFF raises nothing — a bare markForCheck() would re-render against a STALE _ui (still
|
|
1453
|
+
// showThread=true / showHero=false), so the hero orb wouldn't return until the next unrelated
|
|
1454
|
+
// interaction recomputed. recomputeUi() rebuilds _ui from the current ShowCaptions and marks for
|
|
1455
|
+
// check itself; it's pure/idempotent, so the redundant call in the ON case is harmless.
|
|
1456
|
+
this.recomputeUi();
|
|
1018
1457
|
}
|
|
1019
1458
|
/** Reflect the gear toggle from the controls into the dev affordances. */
|
|
1020
1459
|
OnDevModeToggled(on) {
|
|
1021
1460
|
this.DevMode = on;
|
|
1461
|
+
this.ControlInvoked.emit('gear');
|
|
1462
|
+
// devMode drives the resolver's showDevLinks signal — re-resolve (marks for check).
|
|
1463
|
+
this.recomputeUi();
|
|
1022
1464
|
}
|
|
1023
1465
|
/** A dev link asked to open a delegated agent run record. */
|
|
1024
1466
|
OnOpenRunRequested(runId) {
|
|
@@ -1075,6 +1517,7 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1075
1517
|
this.State.LoadHistoricalItems(BuildReviewThreadItems(review));
|
|
1076
1518
|
this.ReviewWhiteboard = this.parseReviewWhiteboard(review);
|
|
1077
1519
|
this.reviewArtifacts = review.Artifacts ?? [];
|
|
1520
|
+
this.ReviewCues = this.buildReviewCues(review);
|
|
1078
1521
|
if (this.viewReady) {
|
|
1079
1522
|
// Let this CD pass create/refresh the surface panel before registering the tabs.
|
|
1080
1523
|
// ngZone.run is REQUIRED: review often opens through the deep-link/query-param
|
|
@@ -1084,9 +1527,45 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1084
1527
|
setTimeout(() => this.ngZone.run(() => {
|
|
1085
1528
|
this.registerReviewBoardTab();
|
|
1086
1529
|
this.registerReviewArtifactTabs();
|
|
1530
|
+
this.registerReviewRecordingTab();
|
|
1087
1531
|
}), 0);
|
|
1088
1532
|
}
|
|
1089
|
-
|
|
1533
|
+
// isReviewing flipped on — review always wants the console (marks for check).
|
|
1534
|
+
this.recomputeUi();
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Builds time-aligned {@link MediaTranscriptCue}s from the reviewed session's turns, for the
|
|
1538
|
+
* storage media player's transcript panel. Skips turns with no text; derives each cue's start from
|
|
1539
|
+
* `UtteranceStartMs`, falling back to (turn-created − recording-start) ms, else 0. Sorts by start.
|
|
1540
|
+
*/
|
|
1541
|
+
buildReviewCues(review) {
|
|
1542
|
+
const startedAt = review.RecordingStartedAt;
|
|
1543
|
+
const cues = [];
|
|
1544
|
+
(review.Turns ?? []).forEach((turn, index) => {
|
|
1545
|
+
const text = (turn.Text ?? turn.Message ?? '').trim();
|
|
1546
|
+
if (text.length === 0) {
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
cues.push({
|
|
1550
|
+
Id: turn.ID || `turn-${index}`,
|
|
1551
|
+
StartMs: this.reviewCueStartMs(turn, startedAt),
|
|
1552
|
+
EndMs: turn.UtteranceEndMs ?? undefined,
|
|
1553
|
+
SpeakerLabel: turn.Role === 'User' ? 'You' : review.AgentName,
|
|
1554
|
+
Text: text
|
|
1555
|
+
});
|
|
1556
|
+
});
|
|
1557
|
+
cues.sort((a, b) => a.StartMs - b.StartMs);
|
|
1558
|
+
return cues;
|
|
1559
|
+
}
|
|
1560
|
+
/** A review turn's media-relative start (ms): precise offset, else derived from t0, else 0. */
|
|
1561
|
+
reviewCueStartMs(turn, startedAt) {
|
|
1562
|
+
if (turn.UtteranceStartMs != null) {
|
|
1563
|
+
return turn.UtteranceStartMs;
|
|
1564
|
+
}
|
|
1565
|
+
if (startedAt && turn.__mj_CreatedAt) {
|
|
1566
|
+
return Math.max(0, turn.__mj_CreatedAt.getTime() - startedAt.getTime());
|
|
1567
|
+
}
|
|
1568
|
+
return 0;
|
|
1090
1569
|
}
|
|
1091
1570
|
/**
|
|
1092
1571
|
* Leaves review. Two distinct exits:
|
|
@@ -1098,6 +1577,12 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1098
1577
|
*/
|
|
1099
1578
|
exitReview() {
|
|
1100
1579
|
this.ReviewWhiteboard = null;
|
|
1580
|
+
this.ReviewCues = [];
|
|
1581
|
+
if (this.reviewRecordingTabRegistered) {
|
|
1582
|
+
this.surfaceTabs?.RemoveTab('Recording');
|
|
1583
|
+
this.pendingChannelTabs = this.pendingChannelTabs.filter(r => r.Key !== 'Recording');
|
|
1584
|
+
this.reviewRecordingTabRegistered = false;
|
|
1585
|
+
}
|
|
1101
1586
|
if (this.pendingLiveContinuation) {
|
|
1102
1587
|
this.pendingLiveContinuation = false;
|
|
1103
1588
|
this.State.StartLiveContinuation();
|
|
@@ -1106,16 +1591,22 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1106
1591
|
this.reviewArtifacts = [];
|
|
1107
1592
|
this.State.Clear();
|
|
1108
1593
|
}
|
|
1109
|
-
|
|
1594
|
+
// isReviewing flipped off — re-resolve the chrome/gates (marks for check).
|
|
1595
|
+
this.recomputeUi();
|
|
1110
1596
|
}
|
|
1111
|
-
/**
|
|
1597
|
+
/**
|
|
1598
|
+
* Surfaces the reviewed chain's history artifacts inside the Activity tab. In the redesign
|
|
1599
|
+
* there are no per-artifact tabs — the artifacts ride the {@link ReviewArtifacts} input into
|
|
1600
|
+
* the rail's "Session artifacts" group, so this only needs to ensure the Activity tab is
|
|
1601
|
+
* shown (it carries the carryover artifacts). Idempotent; no-op without any review artifacts.
|
|
1602
|
+
*/
|
|
1112
1603
|
registerReviewArtifactTabs() {
|
|
1113
1604
|
if (!this.surfaceTabs || this.reviewArtifacts.length === 0) {
|
|
1114
1605
|
return;
|
|
1115
1606
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1607
|
+
// Show the Activity tab (where the carryover artifacts now live); RegisterArtifactTab
|
|
1608
|
+
// flips the gate on without opening any tab of its own.
|
|
1609
|
+
this.surfaceTabs.RegisterArtifactTab(this.reviewArtifacts[0], false);
|
|
1119
1610
|
this.cdr.markForCheck();
|
|
1120
1611
|
}
|
|
1121
1612
|
/**
|
|
@@ -1154,17 +1645,37 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1154
1645
|
this.reviewWhiteboardTabRegistered = true;
|
|
1155
1646
|
this.cdr.markForCheck();
|
|
1156
1647
|
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Registers the read-only review RECORDING tab (the time-aligned storage media player) — ONLY when
|
|
1650
|
+
* the reviewed session carried a `RecordingFileID`. Idempotent (re-registering the same key updates
|
|
1651
|
+
* it in place). The player resolves the authenticated audio itself from the `RecordingFileID` and
|
|
1652
|
+
* shows its own no-access / loading states; the click-to-seek transcript is usable immediately.
|
|
1653
|
+
* NOT focused — the channel surface / Activity rail keeps the default focus.
|
|
1654
|
+
*/
|
|
1655
|
+
registerReviewRecordingTab() {
|
|
1656
|
+
if (!this._reviewData?.RecordingFileID || !this.recordingTpl) {
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
this.RegisterChannelTab({
|
|
1660
|
+
Key: 'Recording',
|
|
1661
|
+
Title: 'Recording',
|
|
1662
|
+
Icon: 'fa-solid fa-circle-play',
|
|
1663
|
+
Content: this.recordingTpl
|
|
1664
|
+
});
|
|
1665
|
+
this.reviewRecordingTabRegistered = true;
|
|
1666
|
+
this.cdr.markForCheck();
|
|
1667
|
+
}
|
|
1157
1668
|
/**
|
|
1158
1669
|
* A working delegation card's ✕ asked to cancel that delegated run — EXPLICIT user
|
|
1159
1670
|
* intent (barge-in never cancels work, by deliberate policy). The service calls the
|
|
1160
1671
|
* server cancel channel and flips the card to a failed "Cancelled by user" result.
|
|
1161
1672
|
*/
|
|
1162
1673
|
OnCancelDelegation(callId) {
|
|
1163
|
-
void this.
|
|
1674
|
+
void this.realtime.CancelDelegation(callId);
|
|
1164
1675
|
}
|
|
1165
1676
|
/** Minimizes the live call (it stays running) and asks the host to open the record. */
|
|
1166
1677
|
requestNavigate(entityName, recordId) {
|
|
1167
|
-
this.
|
|
1678
|
+
this.realtime.SetMinimized(true);
|
|
1168
1679
|
this.NavigateRequest.emit({ EntityName: entityName, RecordID: recordId });
|
|
1169
1680
|
}
|
|
1170
1681
|
// ── Focus layout + pill ────────────────────────────────────────────────────
|
|
@@ -1172,11 +1683,12 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1172
1683
|
onChannelFocus(channel, focused) {
|
|
1173
1684
|
this.ChannelFocusMode = focused;
|
|
1174
1685
|
this.focusChannel = focused ? channel : null;
|
|
1175
|
-
|
|
1686
|
+
// channelFocus drives the resolver (hides panel + strip) — re-resolve (marks for check).
|
|
1687
|
+
this.recomputeUi();
|
|
1176
1688
|
}
|
|
1177
|
-
/** Focus pill: toggle the mic mute. */
|
|
1689
|
+
/** Focus pill: toggle the mic mute (routes through the overlay's single mute path). */
|
|
1178
1690
|
OnFocusPillMute() {
|
|
1179
|
-
this.
|
|
1691
|
+
this.ToggleMute();
|
|
1180
1692
|
}
|
|
1181
1693
|
/** Focus pill: leave focus mode (show the thread column again). */
|
|
1182
1694
|
OnFocusPillExit() {
|
|
@@ -1189,7 +1701,7 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1189
1701
|
}
|
|
1190
1702
|
/** Focus pill: end the call (mirrors the controls row's End button). */
|
|
1191
1703
|
async OnFocusPillEnd() {
|
|
1192
|
-
await this.
|
|
1704
|
+
await this.realtime.EndRealtimeSession();
|
|
1193
1705
|
this.Ended.emit();
|
|
1194
1706
|
}
|
|
1195
1707
|
/** Short status line for the focus pill (first person — the co-agent owns the work). */
|
|
@@ -1202,30 +1714,154 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1202
1714
|
default: return '';
|
|
1203
1715
|
}
|
|
1204
1716
|
}
|
|
1717
|
+
/**
|
|
1718
|
+
* The composer dock toggled the mic. Syncs the overlay's single {@link Muted} source (and
|
|
1719
|
+
* the focus pill) and surfaces the {@link MuteChanged} / {@link ControlInvoked} outputs.
|
|
1720
|
+
*/
|
|
1721
|
+
OnComposerMuteChanged(muted) {
|
|
1722
|
+
if (muted !== this.Muted) {
|
|
1723
|
+
this.Muted = muted;
|
|
1724
|
+
this.FocusPillMuted = muted;
|
|
1725
|
+
this.cdr.markForCheck();
|
|
1726
|
+
}
|
|
1727
|
+
this.MuteChanged.emit(muted);
|
|
1728
|
+
this.ControlInvoked.emit('mute');
|
|
1729
|
+
}
|
|
1730
|
+
// ── Imperative API — drive the surface from a host via @ViewChild ────────────
|
|
1731
|
+
//
|
|
1732
|
+
// Each method maps to an existing internal handler/service so behaviour matches the
|
|
1733
|
+
// user clicking the corresponding control. See the package README's "Methods" table.
|
|
1734
|
+
/**
|
|
1735
|
+
* Override the chrome at runtime (equivalent to binding `[Chrome]`). Pass `'auto'` to
|
|
1736
|
+
* return to the size/intent-driven default.
|
|
1737
|
+
*
|
|
1738
|
+
* @param mode the chrome to force.
|
|
1739
|
+
* @example `voice.SetChrome('console');`
|
|
1740
|
+
*/
|
|
1741
|
+
SetChrome(mode) {
|
|
1742
|
+
this.setUiInput('chrome', mode);
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Reveal the transcript programmatically — the imperative twin of the orb's "Show the
|
|
1746
|
+
* conversation" affordance. Raises the text disclosure level, turns captions on, and
|
|
1747
|
+
* emits {@link TextRevealed}.
|
|
1748
|
+
*/
|
|
1749
|
+
RevealText() {
|
|
1750
|
+
this.OnTextReveal();
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Set the mic mute to an explicit state (no-op when already there). Drives the session
|
|
1754
|
+
* service, syncs every mute affordance, and emits {@link MuteChanged}.
|
|
1755
|
+
*
|
|
1756
|
+
* @param muted the desired muted state.
|
|
1757
|
+
*/
|
|
1758
|
+
SetMuted(muted) {
|
|
1759
|
+
if (muted === this.Muted) {
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
this.ToggleMute();
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Toggle the mic mute. Drives the session service, syncs the overlay's {@link Muted}
|
|
1766
|
+
* source + the composer/focus-pill affordances, and emits {@link MuteChanged} +
|
|
1767
|
+
* {@link ControlInvoked}.
|
|
1768
|
+
*/
|
|
1769
|
+
ToggleMute() {
|
|
1770
|
+
this.Muted = this.realtime.ToggleMute();
|
|
1771
|
+
this.FocusPillMuted = this.Muted;
|
|
1772
|
+
this.MuteChanged.emit(this.Muted);
|
|
1773
|
+
this.ControlInvoked.emit('mute');
|
|
1774
|
+
this.cdr.markForCheck();
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Set captions (the text-vs-orb preference) to an explicit state. Persisted per-user;
|
|
1778
|
+
* turning them on also raises the text disclosure level.
|
|
1779
|
+
*
|
|
1780
|
+
* @param on whether captions/text should be shown.
|
|
1781
|
+
*/
|
|
1782
|
+
SetCaptions(on) {
|
|
1783
|
+
if (on === this.ShowCaptions) {
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
this.OnCaptionsToggled(on);
|
|
1787
|
+
}
|
|
1788
|
+
/** Toggle captions (the text-vs-orb preference). */
|
|
1789
|
+
ToggleCaptions() {
|
|
1790
|
+
this.OnCaptionsToggled(!this.ShowCaptions);
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Set the UX interface density (the gear's escape hatch). Applied + persisted immediately;
|
|
1794
|
+
* emits {@link DensityChanged}.
|
|
1795
|
+
*
|
|
1796
|
+
* @param density the density to apply.
|
|
1797
|
+
*/
|
|
1798
|
+
SetDensity(density) {
|
|
1799
|
+
this.OnDensityChanged(density);
|
|
1800
|
+
}
|
|
1801
|
+
/**
|
|
1802
|
+
* Open the surface panel (the on-demand Details peek), optionally focusing a specific
|
|
1803
|
+
* channel's tab. Equivalent to the strip's Details control plus a channel reveal.
|
|
1804
|
+
*
|
|
1805
|
+
* @param channelId when supplied, the channel tab to reveal once the panel is shown.
|
|
1806
|
+
*/
|
|
1807
|
+
OpenSurfacePanel(channelId) {
|
|
1808
|
+
if (!this.DetailsPeek) {
|
|
1809
|
+
this.DetailsPeek = true;
|
|
1810
|
+
this.ControlInvoked.emit('surface');
|
|
1811
|
+
this.recomputeUi();
|
|
1812
|
+
}
|
|
1813
|
+
setTimeout(() => {
|
|
1814
|
+
if (channelId) {
|
|
1815
|
+
this.surfaceTabs?.RevealChannel(channelId);
|
|
1816
|
+
}
|
|
1817
|
+
else {
|
|
1818
|
+
this.surfaceTabs?.FocusFirstTab();
|
|
1819
|
+
}
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
/** Collapse the surface panel (hide the Details peek). No-op in review (always shown). */
|
|
1823
|
+
CollapseSurfacePanel() {
|
|
1824
|
+
if (this.IsReviewing || !this.DetailsPeek) {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
this.DetailsPeek = false;
|
|
1828
|
+
this.ControlInvoked.emit('surface');
|
|
1829
|
+
this.recomputeUi();
|
|
1830
|
+
}
|
|
1831
|
+
/** Minimize the call (it stays live) — the imperative twin of the app-bar Minimize. */
|
|
1832
|
+
Minimize() {
|
|
1833
|
+
this.OnMinimize();
|
|
1834
|
+
}
|
|
1835
|
+
/** End the session (tear down the call) — the imperative twin of the End control. */
|
|
1836
|
+
EndSession() {
|
|
1837
|
+
void this.OnEndCall();
|
|
1838
|
+
}
|
|
1205
1839
|
static ɵfac = function RealtimeSessionOverlayComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || RealtimeSessionOverlayComponent)(); };
|
|
1206
1840
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RealtimeSessionOverlayComponent, selectors: [["mj-realtime-session-overlay"]], viewQuery: function RealtimeSessionOverlayComponent_Query(rf, ctx) { if (rf & 1) {
|
|
1207
|
-
i0.ɵɵviewQuery(RealtimeSurfaceTabsComponent, 5)(RealtimeComposerComponent, 5)(_c0, 5);
|
|
1841
|
+
i0.ɵɵviewQuery(RealtimeSurfaceTabsComponent, 5)(RealtimeComposerComponent, 5)(_c0, 5)(_c1, 5);
|
|
1208
1842
|
} if (rf & 2) {
|
|
1209
1843
|
let _t;
|
|
1210
1844
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.surfaceTabsRef = _t.first);
|
|
1211
1845
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.composer = _t.first);
|
|
1212
1846
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.reviewBoardTpl = _t.first);
|
|
1847
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.recordingTpl = _t.first);
|
|
1213
1848
|
} }, hostBindings: function RealtimeSessionOverlayComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
1214
1849
|
i0.ɵɵlistener("keydown", function RealtimeSessionOverlayComponent_keydown_HostBindingHandler($event) { return ctx.OnDocumentKeydown($event); }, i0.ɵɵresolveDocument);
|
|
1215
|
-
} }, inputs: { Hidden: "Hidden", AgentName: "AgentName", CurrentUser: "CurrentUser", EnvironmentID: "EnvironmentID", ReviewData: "ReviewData" }, outputs: { Ended: "Ended", NavigateRequest: "NavigateRequest", StartLiveRequested: "StartLiveRequested", ReviewClosed: "ReviewClosed" }, decls:
|
|
1216
|
-
i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_Conditional_0_Template, 12,
|
|
1850
|
+
} }, inputs: { Hidden: "Hidden", AgentName: "AgentName", CurrentUser: "CurrentUser", EnvironmentID: "EnvironmentID", Chrome: "Chrome", ConsoleBreakpointPx: "ConsoleBreakpointPx", Compact: "Compact", AutoHideControls: "AutoHideControls", AllowTextReveal: "AllowTextReveal", ShowCaptionsControl: "ShowCaptionsControl", ShowDensityPicker: "ShowDensityPicker", ShowMinimize: "ShowMinimize", ShowEnd: "ShowEnd", ShowSurfacePanel: "ShowSurfacePanel", ShowChannels: "ShowChannels", ShowActivityRail: "ShowActivityRail", ShowDevLinks: "ShowDevLinks", AllowResize: "AllowResize", UiConfig: "UiConfig", ReviewData: "ReviewData" }, outputs: { Ended: "Ended", NavigateRequest: "NavigateRequest", StartLiveRequested: "StartLiveRequested", ReviewClosed: "ReviewClosed", Minimized: "Minimized", TextRevealed: "TextRevealed", ChromeChanged: "ChromeChanged", ConnectionStateChanged: "ConnectionStateChanged", MuteChanged: "MuteChanged", DensityChanged: "DensityChanged", SurfacePanelResized: "SurfacePanelResized", ControlInvoked: "ControlInvoked" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 3, consts: [["reviewBoardTpl", ""], ["recordingTpl", ""], ["role", "region", 1, "call-overlay", 3, "call-overlay--hidden", "call-overlay--review", "board-focus"], ["role", "region", 1, "call-overlay"], [1, "call-main"], [3, "OpenSessionRequested", "DevModeToggled", "DensityChanged", "MinimizeRequested", "StartLiveRequested", "CloseRequested", "State", "Compact", "AgentName", "ModelName", "DevMode", "SessionID", "ReviewMode", "ReviewStartedAt", "ReviewClosedAt", "ReviewCloseReason", "ShowGear", "ShowDensityPicker", "ShowMinimize", "Density"], ["role", "status", "aria-live", "polite", 1, "banner-live-note"], [1, "call-body"], [1, "call-connecting"], ["role", "status", "aria-live", "polite", 1, "hero"], [3, "State", "AgentName", "UserName", "ShowCaptions", "DevMode"], ["role", "status", "aria-label", "Live call (board focus)", 1, "board-focus-pill"], ["aria-hidden", "true", 1, "fa-solid", "fa-comment-dots"], ["text", "Connecting to the live session\u2026", "size", "medium"], [1, "hero__stage"], ["aria-hidden", "true", 1, "hero__ring"], ["aria-hidden", "true", 1, "hero__ring", "hero__ring--2"], ["aria-hidden", "true", 1, "hero__ring", "hero__ring--3"], ["aria-hidden", "true", 1, "hero__orb"], ["aria-hidden", "true", 1, "hero__eq"], [1, "hero__name"], [1, "hero__sub"], ["type", "button", "title", "Show the conversation as text", 1, "hero__reveal", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-closed-captioning"], [3, "OpenRunRequested", "OpenArtifactRequested", "CancelRequested", "State", "AgentName", "UserName", "ShowCaptions", "DevMode"], [3, "OpenChanged", "CaptionsToggled", "DetailsToggled", "MuteChanged", "EndRequested", "Open", "Compact", "CaptionsOn", "IsMuted", "ShowDetails", "DetailsOn"], ["role", "separator", "aria-orientation", "vertical", "aria-label", "Resize the surface panel (double-click to reset)", "title", "Drag to resize \u2022 double-click to reset", 1, "call-resizer", 3, "call-resizer--active"], [1, "call-panel"], [3, "OpenRunRequested", "CollapsedChange", "WideChanged", "State", "DevMode", "CurrentUser", "EnvironmentID", "ShowActivityTab", "ExtraArtifacts", "Fill"], ["role", "separator", "aria-orientation", "vertical", "aria-label", "Resize the surface panel (double-click to reset)", "title", "Drag to resize \u2022 double-click to reset", 1, "call-resizer", 3, "mousedown", "dblclick"], ["aria-hidden", "true", 1, "board-focus-pill__orb"], [1, "board-focus-pill__text"], [1, "board-focus-pill__name"], [1, "board-focus-pill__state"], ["type", "button", 1, "board-focus-pill__btn", 3, "click", "title"], ["aria-hidden", "true", 1, "fa-solid"], ["type", "button", "title", "Show thread", "aria-label", "Show the conversation thread", 1, "board-focus-pill__btn", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-table-columns"], ["type", "button", "title", "End call", "aria-label", "End the call", 1, "board-focus-pill__btn", "board-focus-pill__btn--end", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-phone-slash"], [1, "review-board-host"], [3, "State", "AgentName", "ReadOnly"], [1, "review-recording-host"], [3, "FileID", "Transcript", "Provider"]], template: function RealtimeSessionOverlayComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1851
|
+
i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_Conditional_0_Template, 12, 30, "div", 2);
|
|
1217
1852
|
i0.ɵɵpipe(1, "async");
|
|
1218
|
-
i0.ɵɵtemplate(2, RealtimeSessionOverlayComponent_ng_template_2_Template, 1, 1, "ng-template", null, 0, i0.ɵɵtemplateRefExtractor);
|
|
1853
|
+
i0.ɵɵtemplate(2, RealtimeSessionOverlayComponent_ng_template_2_Template, 1, 1, "ng-template", null, 0, i0.ɵɵtemplateRefExtractor)(4, RealtimeSessionOverlayComponent_ng_template_4_Template, 2, 3, "ng-template", null, 1, i0.ɵɵtemplateRefExtractor);
|
|
1219
1854
|
} if (rf & 2) {
|
|
1220
|
-
let
|
|
1221
|
-
i0.ɵɵconditional((
|
|
1855
|
+
let tmp_2_0;
|
|
1856
|
+
i0.ɵɵconditional((tmp_2_0 = i0.ɵɵpipeBind1(1, 1, ctx.ConnectionState$)) ? 0 : -1, tmp_2_0);
|
|
1222
1857
|
} }, dependencies: [CommonModule,
|
|
1223
1858
|
SharedGenericModule, i1.LoadingComponent, RealtimeAgentBannerComponent,
|
|
1224
1859
|
RealtimeSessionThreadComponent,
|
|
1225
1860
|
RealtimeChannelStripComponent,
|
|
1226
1861
|
RealtimeComposerComponent,
|
|
1227
1862
|
RealtimeSurfaceTabsComponent,
|
|
1228
|
-
RealtimeWhiteboardBoardComponent, i2.AsyncPipe], styles: ["[_nghost-%COMP%] {\n display: contents;\n}\n\n\n\n\n\n\n.call-overlay[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n z-index: 50;\n display: flex;\n background: color-mix(in srgb, var(--mj-bg-page) 94%, transparent);\n backdrop-filter: blur(8px);\n overflow: hidden;\n}\n\n\n\n\n.call-overlay--hidden[_ngcontent-%COMP%] {\n display: none;\n}\n\n\n\n\n\n.call-main[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 420px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.call-body[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 16px 22px;\n min-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--mj-border-strong) transparent;\n}\n.call-body[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 6px;\n}\n.call-body[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\n.call-connecting[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 160px;\n}\n\n\n\n\n\n\n.call-body--hero[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n}\n.hero[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n animation: _ngcontent-%COMP%_hero-in 420ms cubic-bezier(0.2, 0.8, 0.3, 1);\n}\n@keyframes _ngcontent-%COMP%_hero-in {\n from { opacity: 0; transform: scale(0.94); }\n to { opacity: 1; transform: scale(1); }\n}\n.hero__stage[_ngcontent-%COMP%] {\n position: relative;\n width: 220px;\n height: 220px;\n display: grid;\n place-items: center;\n margin-bottom: 4px;\n}\n\n\n.hero__orb[_ngcontent-%COMP%] {\n width: 120px;\n height: 120px;\n border-radius: 50%;\n position: relative;\n background: radial-gradient(circle at 35% 30%,\n var(--mj-brand-accent, var(--mj-brand-primary)),\n var(--mj-brand-primary) 60%,\n var(--mj-brand-primary-active, var(--mj-brand-primary)));\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-brand-primary) 45%, transparent);\n}\n.hero__orb[data-state='speaking'][_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_hero-orb-bob 0.9s ease-in-out infinite;\n}\n.hero__orb[data-state='listening'][_ngcontent-%COMP%] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-success) 16%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-status-success) 40%, transparent);\n background: radial-gradient(circle at 35% 30%,\n color-mix(in srgb, var(--mj-status-success) 60%, white), var(--mj-brand-primary) 65%);\n}\n.hero__orb[data-state='thinking'][_ngcontent-%COMP%] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-warning) 14%, transparent),\n 0 0 40px color-mix(in srgb, var(--mj-status-warning) 35%, transparent);\n}\n@keyframes _ngcontent-%COMP%_hero-orb-bob {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-4px); }\n}\n\n\n.hero__ring[_ngcontent-%COMP%] {\n position: absolute;\n width: 120px;\n height: 120px;\n border-radius: 50%;\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 55%, transparent);\n opacity: 0;\n animation: _ngcontent-%COMP%_hero-wave 2.7s cubic-bezier(0.2, 0.6, 0.4, 1) infinite;\n}\n.hero__ring--2[_ngcontent-%COMP%] { animation-delay: 0.9s; }\n.hero__ring--3[_ngcontent-%COMP%] { animation-delay: 1.8s; }\n@keyframes _ngcontent-%COMP%_hero-wave {\n 0% { transform: scale(1); opacity: 0.5; }\n 70% { opacity: 0.1; }\n 100% { transform: scale(1.85); opacity: 0; }\n}\n\n\n.hero__eq[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 30px;\n margin-bottom: 6px;\n}\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n width: 4px;\n height: 8px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-brand-accent, var(--mj-brand-primary));\n opacity: 0.55;\n}\n.hero[data-state='speaking'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_hero-eq 1.05s ease-in-out infinite;\n}\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(1) { animation-delay: 0s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(2) { animation-delay: 0.14s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(3) { animation-delay: 0.28s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(4) { animation-delay: 0.07s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(5) { animation-delay: 0.21s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(6) { animation-delay: 0.35s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(7) { animation-delay: 0.1s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(8) { animation-delay: 0.24s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(9) { animation-delay: 0.03s; }\n@keyframes _ngcontent-%COMP%_hero-eq {\n 0%, 100% { height: 7px; opacity: 0.6; }\n 50% { height: 28px; opacity: 1; }\n}\n.hero__name[_ngcontent-%COMP%] {\n font-size: 19px;\n font-weight: 800;\n letter-spacing: -0.01em;\n color: var(--mj-text-primary);\n}\n.hero__sub[_ngcontent-%COMP%] {\n font-size: 12.5px;\n color: var(--mj-text-muted);\n}\n\n\n\n\n\n\n\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__orb[_ngcontent-%COMP%] {\n animation: none;\n transform: scale(calc(1 + var(--voice-out, 0) * 0.18));\n transition: transform 70ms linear;\n}\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__ring[_ngcontent-%COMP%] {\n opacity: calc(var(--voice-out, 0) * 0.55);\n animation-duration: 2.1s;\n}\n.call-overlay[data-audio-live='true'][data-voice-dir='user'][_ngcontent-%COMP%] .hero__orb[_ngcontent-%COMP%] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-success) 16%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-status-success) 45%, transparent);\n background: radial-gradient(circle at 35% 30%,\n color-mix(in srgb, var(--mj-status-success) 60%, white), var(--mj-brand-primary) 65%);\n}\n.call-overlay[data-audio-live='true'][data-voice-dir='user'][_ngcontent-%COMP%] .hero__ring[_ngcontent-%COMP%] {\n border-color: color-mix(in srgb, var(--mj-status-success) 55%, transparent);\n}\n\n\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n animation: none;\n transition: height 70ms linear, opacity 120ms linear;\n}\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(1) { height: calc(6px + var(--eq-1, 0) * 24px); opacity: calc(0.45 + var(--eq-1, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(2) { height: calc(6px + var(--eq-2, 0) * 24px); opacity: calc(0.45 + var(--eq-2, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(3) { height: calc(6px + var(--eq-3, 0) * 24px); opacity: calc(0.45 + var(--eq-3, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(4) { height: calc(6px + var(--eq-4, 0) * 24px); opacity: calc(0.45 + var(--eq-4, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(5) { height: calc(6px + var(--eq-5, 0) * 24px); opacity: calc(0.45 + var(--eq-5, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(6) { height: calc(6px + var(--eq-6, 0) * 24px); opacity: calc(0.45 + var(--eq-6, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(7) { height: calc(6px + var(--eq-7, 0) * 24px); opacity: calc(0.45 + var(--eq-7, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(8) { height: calc(6px + var(--eq-8, 0) * 24px); opacity: calc(0.45 + var(--eq-8, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(9) { height: calc(6px + var(--eq-9, 0) * 24px); opacity: calc(0.45 + var(--eq-9, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][data-voice-dir='user'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n}\n\n\n\n.hero__reveal[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-top: 18px;\n padding: 6px 14px;\n border-radius: var(--mj-radius-full, 9999px);\n border: 1px dashed var(--mj-border-strong);\n background: transparent;\n color: var(--mj-text-muted);\n font-family: inherit;\n font-size: 11.5px;\n font-weight: 600;\n cursor: pointer;\n transition: color 140ms ease, border-color 140ms ease, background 140ms ease;\n}\n.hero__reveal[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-secondary);\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n.hero__reveal[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n}\n\n\n\n.banner-live-note[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 7px;\n padding: 8px 22px 0;\n font-size: 12px;\n font-style: italic;\n color: var(--mj-text-muted);\n animation: _ngcontent-%COMP%_overlay-note-in 220ms ease;\n}\n.banner-live-note[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n margin-top: 3px;\n flex-shrink: 0;\n}\n@keyframes _ngcontent-%COMP%_overlay-note-in {\n from { opacity: 0; transform: translateY(2px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n\n\n\n\n.call-overlay.board-focus[_ngcontent-%COMP%] .call-main[_ngcontent-%COMP%] {\n display: none;\n}\n.call-overlay.board-focus[_ngcontent-%COMP%] mj-realtime-surface-tabs[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n\n\n.board-focus-pill[_ngcontent-%COMP%] {\n position: absolute;\n left: 64px;\n top: 12px;\n z-index: 60;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 7px 9px 7px 12px;\n border-radius: var(--mj-radius-full, 999px);\n background: color-mix(in srgb, var(--mj-bg-surface-card) 92%, transparent);\n backdrop-filter: blur(8px);\n border: 1px solid var(--mj-border-strong);\n box-shadow: var(--mj-shadow-xl, 0 12px 32px rgba(0, 0, 0, 0.18));\n animation: _ngcontent-%COMP%_overlay-note-in 220ms ease;\n}\n.board-focus-pill__orb[_ngcontent-%COMP%] {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n flex-shrink: 0;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.board-focus-pill__orb[data-state='speaking'][_ngcontent-%COMP%], \n.board-focus-pill__orb[data-state='listening'][_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_board-pill-pulse 1.6s ease-in-out infinite;\n}\n.board-focus-pill__orb[data-state='error'][_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-status-error) 18%, transparent);\n}\n@keyframes _ngcontent-%COMP%_board-pill-pulse {\n 0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent); }\n 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--mj-brand-primary) 8%, transparent); }\n}\n.board-focus-pill__text[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n.board-focus-pill__name[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n line-height: 1.2;\n white-space: nowrap;\n color: var(--mj-text-primary);\n}\n.board-focus-pill__state[_ngcontent-%COMP%] {\n font-size: 10.5px;\n font-weight: 600;\n white-space: nowrap;\n color: var(--mj-brand-primary);\n}\n.board-focus-pill__btn[_ngcontent-%COMP%] {\n width: 30px;\n height: 30px;\n border-radius: 50%;\n border: 1px solid var(--mj-border-strong);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n font-size: 11px;\n display: grid;\n place-items: center;\n font-family: inherit;\n flex-shrink: 0;\n}\n.board-focus-pill__btn[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n.board-focus-pill__btn--active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n.board-focus-pill__btn--end[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n color: var(--mj-text-inverse, #fff);\n}\n\n\n\n\n\n.call-overlay--review[_ngcontent-%COMP%] {\n background:\n linear-gradient(180deg, color-mix(in srgb, var(--mj-text-muted) 4%, transparent), transparent 120px),\n var(--mj-bg-surface);\n}\n\n\n\n\n.review-board-host[_ngcontent-%COMP%] {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n\n\n\n\n\n.call-resizer[_ngcontent-%COMP%] {\n flex: 0 0 7px;\n width: 7px;\n cursor: col-resize;\n background: transparent;\n transition: background 140ms ease;\n}\n.call-resizer[_ngcontent-%COMP%]:hover, \n.call-resizer--active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 26%, transparent);\n}\n\n\n\n\n\n.call-panel[_ngcontent-%COMP%] {\n flex: 0 1 auto;\n min-width: 0;\n display: flex;\n overflow: hidden;\n \n\n\n animation: _ngcontent-%COMP%_panel-in 420ms cubic-bezier(0.25, 0.8, 0.3, 1);\n}\n@keyframes _ngcontent-%COMP%_panel-in {\n from { opacity: 0; transform: translateX(48px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.call-panel[_ngcontent-%COMP%] > *[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n min-height: 0;\n}\n\n\n.call-overlay.board-focus[_ngcontent-%COMP%] .call-panel[_ngcontent-%COMP%] {\n flex: 1;\n}"] });
|
|
1863
|
+
RealtimeWhiteboardBoardComponent,
|
|
1864
|
+
MJStorageMediaPlayerComponent, i2.AsyncPipe], styles: ["[_nghost-%COMP%] {\n display: contents;\n}\n\n\n\n\n\n\n.call-overlay[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n z-index: 50;\n display: flex;\n background: color-mix(in srgb, var(--mj-bg-page) 94%, transparent);\n backdrop-filter: blur(8px);\n overflow: hidden;\n}\n\n\n\n\n.call-overlay--hidden[_ngcontent-%COMP%] {\n display: none;\n}\n\n\n\n\n\n.call-main[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 420px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.call-body[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 16px 22px;\n min-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--mj-border-strong) transparent;\n}\n.call-body[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 6px;\n}\n.call-body[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\n.call-connecting[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 160px;\n}\n\n\n\n\n\n\n.call-body--hero[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n}\n.hero[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n animation: _ngcontent-%COMP%_hero-in 420ms cubic-bezier(0.2, 0.8, 0.3, 1);\n}\n@keyframes _ngcontent-%COMP%_hero-in {\n from { opacity: 0; transform: scale(0.94); }\n to { opacity: 1; transform: scale(1); }\n}\n.hero__stage[_ngcontent-%COMP%] {\n position: relative;\n width: 220px;\n height: 220px;\n display: grid;\n place-items: center;\n margin-bottom: 4px;\n}\n\n\n.hero__orb[_ngcontent-%COMP%] {\n width: 120px;\n height: 120px;\n border-radius: 50%;\n position: relative;\n background: radial-gradient(circle at 35% 30%,\n var(--mj-brand-accent, var(--mj-brand-primary)),\n var(--mj-brand-primary) 60%,\n var(--mj-brand-primary-active, var(--mj-brand-primary)));\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-brand-primary) 45%, transparent);\n}\n.hero__orb[data-state='speaking'][_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_hero-orb-bob 0.9s ease-in-out infinite;\n}\n.hero__orb[data-state='listening'][_ngcontent-%COMP%] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-success) 16%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-status-success) 40%, transparent);\n background: radial-gradient(circle at 35% 30%,\n color-mix(in srgb, var(--mj-status-success) 60%, white), var(--mj-brand-primary) 65%);\n}\n.hero__orb[data-state='thinking'][_ngcontent-%COMP%] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-warning) 14%, transparent),\n 0 0 40px color-mix(in srgb, var(--mj-status-warning) 35%, transparent);\n}\n@keyframes _ngcontent-%COMP%_hero-orb-bob {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-4px); }\n}\n\n\n.hero__ring[_ngcontent-%COMP%] {\n position: absolute;\n width: 120px;\n height: 120px;\n border-radius: 50%;\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 55%, transparent);\n opacity: 0;\n animation: _ngcontent-%COMP%_hero-wave 2.7s cubic-bezier(0.2, 0.6, 0.4, 1) infinite;\n}\n.hero__ring--2[_ngcontent-%COMP%] { animation-delay: 0.9s; }\n.hero__ring--3[_ngcontent-%COMP%] { animation-delay: 1.8s; }\n@keyframes _ngcontent-%COMP%_hero-wave {\n 0% { transform: scale(1); opacity: 0.5; }\n 70% { opacity: 0.1; }\n 100% { transform: scale(1.85); opacity: 0; }\n}\n\n\n.hero__eq[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 30px;\n margin-bottom: 6px;\n}\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n width: 4px;\n height: 8px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-brand-accent, var(--mj-brand-primary));\n opacity: 0.55;\n}\n.hero[data-state='speaking'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_hero-eq 1.05s ease-in-out infinite;\n}\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(1) { animation-delay: 0s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(2) { animation-delay: 0.14s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(3) { animation-delay: 0.28s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(4) { animation-delay: 0.07s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(5) { animation-delay: 0.21s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(6) { animation-delay: 0.35s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(7) { animation-delay: 0.1s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(8) { animation-delay: 0.24s; }\n.hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(9) { animation-delay: 0.03s; }\n@keyframes _ngcontent-%COMP%_hero-eq {\n 0%, 100% { height: 7px; opacity: 0.6; }\n 50% { height: 28px; opacity: 1; }\n}\n.hero__name[_ngcontent-%COMP%] {\n font-size: 19px;\n font-weight: 800;\n letter-spacing: -0.01em;\n color: var(--mj-text-primary);\n}\n.hero__sub[_ngcontent-%COMP%] {\n font-size: 12.5px;\n color: var(--mj-text-muted);\n}\n\n\n\n\n\n\n\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__orb[_ngcontent-%COMP%] {\n animation: none;\n transform: scale(calc(1 + var(--voice-out, 0) * 0.18));\n transition: transform 70ms linear;\n}\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__ring[_ngcontent-%COMP%] {\n opacity: calc(var(--voice-out, 0) * 0.55);\n animation-duration: 2.1s;\n}\n.call-overlay[data-audio-live='true'][data-voice-dir='user'][_ngcontent-%COMP%] .hero__orb[_ngcontent-%COMP%] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-success) 16%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-status-success) 45%, transparent);\n background: radial-gradient(circle at 35% 30%,\n color-mix(in srgb, var(--mj-status-success) 60%, white), var(--mj-brand-primary) 65%);\n}\n.call-overlay[data-audio-live='true'][data-voice-dir='user'][_ngcontent-%COMP%] .hero__ring[_ngcontent-%COMP%] {\n border-color: color-mix(in srgb, var(--mj-status-success) 55%, transparent);\n}\n\n\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n animation: none;\n transition: height 70ms linear, opacity 120ms linear;\n}\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(1) { height: calc(6px + var(--eq-1, 0) * 24px); opacity: calc(0.45 + var(--eq-1, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(2) { height: calc(6px + var(--eq-2, 0) * 24px); opacity: calc(0.45 + var(--eq-2, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(3) { height: calc(6px + var(--eq-3, 0) * 24px); opacity: calc(0.45 + var(--eq-3, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(4) { height: calc(6px + var(--eq-4, 0) * 24px); opacity: calc(0.45 + var(--eq-4, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(5) { height: calc(6px + var(--eq-5, 0) * 24px); opacity: calc(0.45 + var(--eq-5, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(6) { height: calc(6px + var(--eq-6, 0) * 24px); opacity: calc(0.45 + var(--eq-6, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(7) { height: calc(6px + var(--eq-7, 0) * 24px); opacity: calc(0.45 + var(--eq-7, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(8) { height: calc(6px + var(--eq-8, 0) * 24px); opacity: calc(0.45 + var(--eq-8, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:nth-child(9) { height: calc(6px + var(--eq-9, 0) * 24px); opacity: calc(0.45 + var(--eq-9, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][data-voice-dir='user'][_ngcontent-%COMP%] .hero__eq[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n}\n\n\n\n.hero__reveal[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-top: 18px;\n padding: 6px 14px;\n border-radius: var(--mj-radius-full, 9999px);\n border: 1px dashed var(--mj-border-strong);\n background: transparent;\n color: var(--mj-text-muted);\n font-family: inherit;\n font-size: 11.5px;\n font-weight: 600;\n cursor: pointer;\n transition: color 140ms ease, border-color 140ms ease, background 140ms ease;\n}\n.hero__reveal[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-secondary);\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n.hero__reveal[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n}\n\n\n\n.banner-live-note[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 7px;\n padding: 8px 22px 0;\n font-size: 12px;\n font-style: italic;\n color: var(--mj-text-muted);\n animation: _ngcontent-%COMP%_overlay-note-in 220ms ease;\n}\n.banner-live-note[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n margin-top: 3px;\n flex-shrink: 0;\n}\n@keyframes _ngcontent-%COMP%_overlay-note-in {\n from { opacity: 0; transform: translateY(2px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n\n\n\n\n.call-overlay.board-focus[_ngcontent-%COMP%] .call-main[_ngcontent-%COMP%] {\n display: none;\n}\n.call-overlay.board-focus[_ngcontent-%COMP%] mj-realtime-surface-tabs[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n\n\n.board-focus-pill[_ngcontent-%COMP%] {\n position: absolute;\n left: 64px;\n top: 12px;\n z-index: 60;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 7px 9px 7px 12px;\n border-radius: var(--mj-radius-full, 999px);\n background: color-mix(in srgb, var(--mj-bg-surface-card) 92%, transparent);\n backdrop-filter: blur(8px);\n border: 1px solid var(--mj-border-strong);\n box-shadow: var(--mj-shadow-xl, 0 12px 32px rgba(0, 0, 0, 0.18));\n animation: _ngcontent-%COMP%_overlay-note-in 220ms ease;\n}\n.board-focus-pill__orb[_ngcontent-%COMP%] {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n flex-shrink: 0;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.board-focus-pill__orb[data-state='speaking'][_ngcontent-%COMP%], \n.board-focus-pill__orb[data-state='listening'][_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_board-pill-pulse 1.6s ease-in-out infinite;\n}\n.board-focus-pill__orb[data-state='error'][_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-status-error) 18%, transparent);\n}\n@keyframes _ngcontent-%COMP%_board-pill-pulse {\n 0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent); }\n 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--mj-brand-primary) 8%, transparent); }\n}\n.board-focus-pill__text[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n.board-focus-pill__name[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n line-height: 1.2;\n white-space: nowrap;\n color: var(--mj-text-primary);\n}\n.board-focus-pill__state[_ngcontent-%COMP%] {\n font-size: 10.5px;\n font-weight: 600;\n white-space: nowrap;\n color: var(--mj-brand-primary);\n}\n.board-focus-pill__btn[_ngcontent-%COMP%] {\n width: 30px;\n height: 30px;\n border-radius: 50%;\n border: 1px solid var(--mj-border-strong);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n font-size: 11px;\n display: grid;\n place-items: center;\n font-family: inherit;\n flex-shrink: 0;\n}\n.board-focus-pill__btn[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n.board-focus-pill__btn--active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n.board-focus-pill__btn--end[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n color: var(--mj-text-inverse, #fff);\n}\n\n\n\n\n\n.call-overlay--review[_ngcontent-%COMP%] {\n background:\n linear-gradient(180deg, color-mix(in srgb, var(--mj-text-muted) 4%, transparent), transparent 120px),\n var(--mj-bg-surface);\n}\n\n\n\n\n.review-board-host[_ngcontent-%COMP%] {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n\n\n\n.review-recording-host[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n.review-recording-host[_ngcontent-%COMP%] mj-realtime-evidence-playback[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n}\n\n\n\n\n\n\n.call-resizer[_ngcontent-%COMP%] {\n flex: 0 0 7px;\n width: 7px;\n cursor: col-resize;\n background: transparent;\n transition: background 140ms ease;\n}\n.call-resizer[_ngcontent-%COMP%]:hover, \n.call-resizer--active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 26%, transparent);\n}\n\n\n\n\n\n.call-panel[_ngcontent-%COMP%] {\n flex: 0 1 auto;\n min-width: 0;\n display: flex;\n overflow: hidden;\n \n\n\n animation: _ngcontent-%COMP%_panel-in 420ms cubic-bezier(0.25, 0.8, 0.3, 1);\n}\n@keyframes _ngcontent-%COMP%_panel-in {\n from { opacity: 0; transform: translateX(48px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.call-panel[_ngcontent-%COMP%] > *[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n min-height: 0;\n}\n\n\n.call-overlay.board-focus[_ngcontent-%COMP%] .call-panel[_ngcontent-%COMP%] {\n flex: 1;\n}"] });
|
|
1229
1865
|
}
|
|
1230
1866
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RealtimeSessionOverlayComponent, [{
|
|
1231
1867
|
type: Component,
|
|
@@ -1237,8 +1873,9 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1237
1873
|
RealtimeChannelStripComponent,
|
|
1238
1874
|
RealtimeComposerComponent,
|
|
1239
1875
|
RealtimeSurfaceTabsComponent,
|
|
1240
|
-
RealtimeWhiteboardBoardComponent
|
|
1241
|
-
], template: "@if (ConnectionState$ | async; as state) {\n <div class=\"call-overlay\"\n [class.call-overlay--hidden]=\"Hidden\"\n [class.call-overlay--review]=\"IsReviewing\"\n [class.board-focus]=\"ChannelFocusMode\"\n role=\"region\" [attr.aria-label]=\"IsReviewing ? 'Past session review' : 'Live voice session'\">\n\n <!-- PROGRESSIVE DISCLOSURE (the console that grows with you \u2014 Redesign A Progressive):\n level 0 is PURE AUDIO (the hero orb owns the main column; no thread, no composer;\n the strip's Details control peeks at the panels on demand); level 1 reveals the\n caption thread + captions control + type-to-type hint; level 2+ docks the composer\n and keeps the surface panel. CONTENT NEVER FLIPS THE CONSOLE OPEN: finished\n artifacts land as glowing unfocused tabs; the ONE auto-reveal is a channel's\n first agent activity (e.g. the first whiteboard write), which opens the panel as\n a peek with that tab focused \u2014 the left column stays exactly as it was.\n REVIEW always renders the full console. -->\n <div class=\"call-main\">\n <!-- The UNIFIED APP-BAR: identity + state + every session action (Redesign A). -->\n <mj-realtime-agent-banner\n [State]=\"state\"\n [AgentName]=\"AgentName\"\n [ModelName]=\"IsReviewing ? null : (ModelName$ | async)\"\n [DevMode]=\"DevMode\"\n [SessionID]=\"IsReviewing ? ReviewSessionID : SessionID\"\n [ReviewMode]=\"IsReviewing\"\n [ReviewStartedAt]=\"ReviewStartedAt\"\n [ReviewClosedAt]=\"ReviewClosedAt\"\n [ReviewCloseReason]=\"ReviewCloseReason\"\n [CaptionsOn]=\"ShowCaptions\"\n [ShowCaptionsControl]=\"!IsReviewing\"\n [ShowGear]=\"!IsReviewing && Disclosure.ShowGear\"\n [ShowEnd]=\"!IsReviewing && Disclosure.ShowComposer\"\n [ShowMinimize]=\"!IsReviewing\"\n [ShowPureAudio]=\"!IsReviewing && Disclosure.SessionLevel > 0\"\n [Density]=\"Disclosure.Milestones.Density\"\n (OpenSessionRequested)=\"OnOpenSessionRequested($event)\"\n (CaptionsToggled)=\"OnCaptionsToggled($event)\"\n (DevModeToggled)=\"OnDevModeToggled($event)\"\n (DensityChanged)=\"OnDensityChanged($event)\"\n (MinimizeRequested)=\"OnMinimize()\"\n (PureAudioRequested)=\"OnPureAudio()\"\n (EndRequested)=\"OnEndCall()\"\n (StartLiveRequested)=\"OnStartLive()\"\n (CloseRequested)=\"OnReviewClose()\">\n </mj-realtime-agent-banner>\n\n <!-- Ephemeral narration with no active working card to anchor to \u2192 under the banner -->\n @if (!IsReviewing && State.Narration && !State.ActiveCallId && Disclosure.ShowThread) {\n <div class=\"banner-live-note\" role=\"status\" aria-live=\"polite\">\n <i class=\"fa-solid fa-comment-dots\" aria-hidden=\"true\"></i>\n {{ State.Narration }}\n </div>\n }\n\n <div class=\"call-body\" [class.call-body--hero]=\"ShowHero && state !== 'connecting'\">\n @if (!IsReviewing && state === 'connecting') {\n <div class=\"call-connecting\">\n <mj-loading text=\"Connecting to the live session\u2026\" size=\"medium\"></mj-loading>\n </div>\n } @else if (ShowHero) {\n <!-- PURE-AUDIO HERO (level 0): the orb IS the interface \u2014 it breathes with the\n audio inside expanding sound-wave rings; nothing to read, nothing to configure. -->\n <div class=\"hero\" [attr.data-state]=\"state\" role=\"status\" aria-live=\"polite\">\n <div class=\"hero__stage\">\n <span class=\"hero__ring\" aria-hidden=\"true\"></span>\n <span class=\"hero__ring hero__ring--2\" aria-hidden=\"true\"></span>\n <span class=\"hero__ring hero__ring--3\" aria-hidden=\"true\"></span>\n <div class=\"hero__orb\" [attr.data-state]=\"HeroOrbState(state)\" aria-hidden=\"true\"></div>\n </div>\n <div class=\"hero__eq\" aria-hidden=\"true\">\n <span></span><span></span><span></span><span></span><span></span>\n <span></span><span></span><span></span><span></span>\n </div>\n <div class=\"hero__name\">{{ AgentName }}</div>\n <div class=\"hero__sub\">{{ HeroStateLabel(state) }}</div>\n <button type=\"button\" class=\"hero__reveal\" (click)=\"OnTextReveal()\"\n title=\"Show the conversation as text\">\n <i class=\"fa-solid fa-closed-captioning\" aria-hidden=\"true\"></i>\n Show the conversation\n </button>\n </div>\n } @else {\n <mj-realtime-session-thread\n [State]=\"State\"\n [AgentName]=\"AgentName\"\n [UserName]=\"CurrentUser?.Name || 'You'\"\n [ShowCaptions]=\"IsReviewing ? true : ShowCaptions\"\n [DevMode]=\"DevMode\"\n (OpenRunRequested)=\"OnOpenRunRequested($event)\"\n (OpenArtifactRequested)=\"OnOpenArtifactRequested($event)\"\n (CancelRequested)=\"OnCancelDelegation($event)\">\n </mj-realtime-session-thread>\n }\n </div>\n\n @if (!IsReviewing) {\n <!-- Active channels (earned with the console at level 2+) -->\n @if (Disclosure.ShowComposer) {\n <mj-realtime-channel-strip></mj-realtime-channel-strip>\n }\n\n <!-- The BOTTOM DOCK: phone-call strip \u21C4 fused minis+composer \u2014 the user's own\n two-way toggle (Type opens, hide closes), never level-forced. -->\n <mj-realtime-composer\n [Open]=\"ComposerOpen\"\n (OpenChanged)=\"OnComposerOpenChanged($event)\"\n [CaptionsOn]=\"ShowCaptions\"\n [ShowDetails]=\"ShowDetailsControl\"\n [DetailsOn]=\"DetailsPeek\"\n (CaptionsToggled)=\"OnCaptionsToggled($event)\"\n (DetailsToggled)=\"OnDetailsToggled()\"\n (EndRequested)=\"OnEndCall()\">\n </mj-realtime-composer>\n }\n </div>\n\n <!-- RIGHT: TABBED SURFACE PANEL \u2014 disclosure-gated: it EXISTS once earned (level 2+,\n which the first delegation forces), peeked on demand via the strip's Details\n control, or always in review. Slides in from the right when it arrives \u2014 the\n panel's arrival IS the progress cue. -->\n @if (ShowPanelArea) {\n <!-- Resize handle (hidden while the panel is collapsed or a channel holds focus). -->\n @if (!PanelResizeDisabled) {\n <div class=\"call-resizer\"\n [class.call-resizer--active]=\"IsPanelResizing\"\n role=\"separator\" aria-orientation=\"vertical\"\n aria-label=\"Resize the surface panel (double-click to reset)\"\n title=\"Drag to resize \u2022 double-click to reset\"\n (mousedown)=\"OnPanelResizeStart($event)\"\n (dblclick)=\"OnPanelResizeReset()\"></div>\n }\n\n <div class=\"call-panel\" [style.width.px]=\"ChannelFocusMode ? null : PanelAreaSize\">\n <mj-realtime-surface-tabs\n [State]=\"State\"\n [DevMode]=\"DevMode\"\n [CurrentUser]=\"CurrentUser\"\n [EnvironmentID]=\"EnvironmentID\"\n [Fill]=\"ChannelFocusMode\"\n (OpenRunRequested)=\"OnOpenRunRequested($event)\"\n (CollapsedChange)=\"OnPanelCollapsedChange($event)\"\n (WideChanged)=\"OnPanelWideChanged($event)\">\n </mj-realtime-surface-tabs>\n </div>\n }\n\n <!-- FOCUS floating call pill: the main column is hidden, so the live call's\n identity + state + mute / show-thread / end controls ride over the surface. -->\n @if (!IsReviewing && ChannelFocusMode) {\n <div class=\"board-focus-pill\" role=\"status\" aria-label=\"Live call (board focus)\">\n <span class=\"board-focus-pill__orb\" [attr.data-state]=\"state\" aria-hidden=\"true\"></span>\n <span class=\"board-focus-pill__text\">\n <span class=\"board-focus-pill__name\">Voice for {{ AgentName }}</span>\n @if (FocusStateLabel(state); as label) {\n <span class=\"board-focus-pill__state\">{{ label }}</span>\n }\n </span>\n <button type=\"button\" class=\"board-focus-pill__btn\"\n [class.board-focus-pill__btn--active]=\"FocusPillMuted\"\n (click)=\"OnFocusPillMute()\"\n [title]=\"FocusPillMuted ? 'Unmute' : 'Mute'\"\n [attr.aria-label]=\"FocusPillMuted ? 'Unmute microphone' : 'Mute microphone'\">\n <i class=\"fa-solid\" [class.fa-microphone]=\"!FocusPillMuted\" [class.fa-microphone-slash]=\"FocusPillMuted\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"board-focus-pill__btn\"\n (click)=\"OnFocusPillExit()\"\n title=\"Show thread\" aria-label=\"Show the conversation thread\">\n <i class=\"fa-solid fa-table-columns\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"board-focus-pill__btn board-focus-pill__btn--end\"\n (click)=\"OnFocusPillEnd()\"\n title=\"End call\" aria-label=\"End the call\">\n <i class=\"fa-solid fa-phone-slash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n </div>\n}\n\n<!-- SESSION REVIEW whiteboard pane body: the reviewed session's saved board, rehydrated\n locally (WhiteboardState.FromJSON) and rendered READ-ONLY (pan/zoom only). Registered\n as a template-based channel tab ONLY when a parseable Whiteboard state exists. -->\n<ng-template #reviewBoardTpl>\n @if (ReviewWhiteboard; as board) {\n <!-- The board host positions itself absolutely \u2014 give it a filling, positioned frame. -->\n <div class=\"review-board-host\">\n <mj-realtime-whiteboard\n [State]=\"board\"\n [AgentName]=\"AgentName\"\n [ReadOnly]=\"true\">\n </mj-realtime-whiteboard>\n </div>\n }\n</ng-template>\n", styles: [":host {\n display: contents;\n}\n\n/* Fills the conversation panel IN PLACE: the chat area host provides the positioned\n context (position:relative on .chat-area) and this covers it edge to edge \u2014 header,\n thread and composer included. Opaque-ish page surface with a slight blur so the\n conversation underneath reads as \"paused\", not as an app-wide modal. */\n.call-overlay {\n position: absolute;\n inset: 0;\n z-index: 50;\n display: flex;\n background: color-mix(in srgb, var(--mj-bg-page) 94%, transparent);\n backdrop-filter: blur(8px);\n overflow: hidden;\n}\n\n/* Minimized: hidden (NOT destroyed) so the shell's session/view state survives while\n the host shows its floating \"on call\" pill. The audio session is untouched. */\n.call-overlay--hidden {\n display: none;\n}\n\n/* MAIN column: banner (top) \u2192 scrolling thread \u2192 channel strip \u2192 composer \u2192 controls.\n The 420px floor keeps the call column readable while the panel is dragged wide\n (the drag clamp's 70% cap is the other guard). */\n.call-main {\n flex: 1;\n min-width: 420px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.call-body {\n flex: 1;\n overflow-y: auto;\n padding: 16px 22px;\n min-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--mj-border-strong) transparent;\n}\n.call-body::-webkit-scrollbar {\n width: 6px;\n}\n.call-body::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\n.call-connecting {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 160px;\n}\n\n/* ---------- PURE-AUDIO HERO (disclosure level 0) ----------\n The orb IS the interface: it breathes with the audio inside expanding sound-wave\n rings, with a small voice equalizer underneath. Nothing to read, nothing to\n configure \u2014 text, panels and the composer are revealed later (or peeked on demand). */\n.call-body--hero {\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n}\n.hero {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n animation: hero-in 420ms cubic-bezier(0.2, 0.8, 0.3, 1);\n}\n@keyframes hero-in {\n from { opacity: 0; transform: scale(0.94); }\n to { opacity: 1; transform: scale(1); }\n}\n.hero__stage {\n position: relative;\n width: 220px;\n height: 220px;\n display: grid;\n place-items: center;\n margin-bottom: 4px;\n}\n/* The hero orb mirrors the app-bar orb's visual language at stage scale. */\n.hero__orb {\n width: 120px;\n height: 120px;\n border-radius: 50%;\n position: relative;\n background: radial-gradient(circle at 35% 30%,\n var(--mj-brand-accent, var(--mj-brand-primary)),\n var(--mj-brand-primary) 60%,\n var(--mj-brand-primary-active, var(--mj-brand-primary)));\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-brand-primary) 45%, transparent);\n}\n.hero__orb[data-state='speaking'] {\n animation: hero-orb-bob 0.9s ease-in-out infinite;\n}\n.hero__orb[data-state='listening'] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-success) 16%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-status-success) 40%, transparent);\n background: radial-gradient(circle at 35% 30%,\n color-mix(in srgb, var(--mj-status-success) 60%, white), var(--mj-brand-primary) 65%);\n}\n.hero__orb[data-state='thinking'] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-warning) 14%, transparent),\n 0 0 40px color-mix(in srgb, var(--mj-status-warning) 35%, transparent);\n}\n@keyframes hero-orb-bob {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-4px); }\n}\n/* Expanding sound-wave rings (staggered). */\n.hero__ring {\n position: absolute;\n width: 120px;\n height: 120px;\n border-radius: 50%;\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 55%, transparent);\n opacity: 0;\n animation: hero-wave 2.7s cubic-bezier(0.2, 0.6, 0.4, 1) infinite;\n}\n.hero__ring--2 { animation-delay: 0.9s; }\n.hero__ring--3 { animation-delay: 1.8s; }\n@keyframes hero-wave {\n 0% { transform: scale(1); opacity: 0.5; }\n 70% { opacity: 0.1; }\n 100% { transform: scale(1.85); opacity: 0; }\n}\n/* Voice equalizer: lively while the agent speaks, near-still otherwise. */\n.hero__eq {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 30px;\n margin-bottom: 6px;\n}\n.hero__eq span {\n width: 4px;\n height: 8px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-brand-accent, var(--mj-brand-primary));\n opacity: 0.55;\n}\n.hero[data-state='speaking'] .hero__eq span {\n animation: hero-eq 1.05s ease-in-out infinite;\n}\n.hero__eq span:nth-child(1) { animation-delay: 0s; }\n.hero__eq span:nth-child(2) { animation-delay: 0.14s; }\n.hero__eq span:nth-child(3) { animation-delay: 0.28s; }\n.hero__eq span:nth-child(4) { animation-delay: 0.07s; }\n.hero__eq span:nth-child(5) { animation-delay: 0.21s; }\n.hero__eq span:nth-child(6) { animation-delay: 0.35s; }\n.hero__eq span:nth-child(7) { animation-delay: 0.1s; }\n.hero__eq span:nth-child(8) { animation-delay: 0.24s; }\n.hero__eq span:nth-child(9) { animation-delay: 0.03s; }\n@keyframes hero-eq {\n 0%, 100% { height: 7px; opacity: 0.6; }\n 50% { height: 28px; opacity: 1; }\n}\n.hero__name {\n font-size: 19px;\n font-weight: 800;\n letter-spacing: -0.01em;\n color: var(--mj-text-primary);\n}\n.hero__sub {\n font-size: 12.5px;\n color: var(--mj-text-muted);\n}\n/* ---------- AUDIO-REACTIVE MODE (data-audio-live, set by the rAF meter loop) ----------\n With real audio metering the orb stops acting and starts REACTING: scale + glow follow\n the smoothed output envelope (--voice-out) like a speaker cone, the EQ renders the true\n spectrum (--eq-1..9), and [data-voice-dir] colors the visuals by who is speaking\n (agent = brand, user = green). Without metering (driver attached no meters) the\n attribute stays 'false' and the turn-state keyframe animations above stay in charge. */\n.call-overlay[data-audio-live='true'] .hero__orb {\n animation: none;\n transform: scale(calc(1 + var(--voice-out, 0) * 0.18));\n transition: transform 70ms linear;\n}\n.call-overlay[data-audio-live='true'] .hero__ring {\n opacity: calc(var(--voice-out, 0) * 0.55);\n animation-duration: 2.1s;\n}\n.call-overlay[data-audio-live='true'][data-voice-dir='user'] .hero__orb {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-success) 16%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-status-success) 45%, transparent);\n background: radial-gradient(circle at 35% 30%,\n color-mix(in srgb, var(--mj-status-success) 60%, white), var(--mj-brand-primary) 65%);\n}\n.call-overlay[data-audio-live='true'][data-voice-dir='user'] .hero__ring {\n border-color: color-mix(in srgb, var(--mj-status-success) 55%, transparent);\n}\n/* True-spectrum EQ: per-bar heights from the meter's bins. */\n.call-overlay[data-audio-live='true'] .hero__eq span {\n animation: none;\n transition: height 70ms linear, opacity 120ms linear;\n}\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(1) { height: calc(6px + var(--eq-1, 0) * 24px); opacity: calc(0.45 + var(--eq-1, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(2) { height: calc(6px + var(--eq-2, 0) * 24px); opacity: calc(0.45 + var(--eq-2, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(3) { height: calc(6px + var(--eq-3, 0) * 24px); opacity: calc(0.45 + var(--eq-3, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(4) { height: calc(6px + var(--eq-4, 0) * 24px); opacity: calc(0.45 + var(--eq-4, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(5) { height: calc(6px + var(--eq-5, 0) * 24px); opacity: calc(0.45 + var(--eq-5, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(6) { height: calc(6px + var(--eq-6, 0) * 24px); opacity: calc(0.45 + var(--eq-6, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(7) { height: calc(6px + var(--eq-7, 0) * 24px); opacity: calc(0.45 + var(--eq-7, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(8) { height: calc(6px + var(--eq-8, 0) * 24px); opacity: calc(0.45 + var(--eq-8, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(9) { height: calc(6px + var(--eq-9, 0) * 24px); opacity: calc(0.45 + var(--eq-9, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][data-voice-dir='user'] .hero__eq span {\n background: var(--mj-status-success);\n}\n\n/* The quiet first reveal: show the conversation as text (raises disclosure to level 1). */\n.hero__reveal {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-top: 18px;\n padding: 6px 14px;\n border-radius: var(--mj-radius-full, 9999px);\n border: 1px dashed var(--mj-border-strong);\n background: transparent;\n color: var(--mj-text-muted);\n font-family: inherit;\n font-size: 11.5px;\n font-weight: 600;\n cursor: pointer;\n transition: color 140ms ease, border-color 140ms ease, background 140ms ease;\n}\n.hero__reveal:hover {\n color: var(--mj-text-secondary);\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n.hero__reveal i {\n font-size: 11px;\n}\n\n/* Ephemeral narration shown under the banner when no working card anchors it */\n.banner-live-note {\n display: flex;\n align-items: flex-start;\n gap: 7px;\n padding: 8px 22px 0;\n font-size: 12px;\n font-style: italic;\n color: var(--mj-text-muted);\n animation: overlay-note-in 220ms ease;\n}\n.banner-live-note i {\n font-size: 10px;\n margin-top: 3px;\n flex-shrink: 0;\n}\n@keyframes overlay-note-in {\n from { opacity: 0; transform: translateY(2px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* ---------- FOCUS-BOARD mode (whiteboard \"Focus board\" toggle) ----------\n The main call column collapses away entirely and the surface panel (whiteboard tab)\n fills the overlay; a compact floating call pill keeps the live call reachable. */\n.call-overlay.board-focus .call-main {\n display: none;\n}\n.call-overlay.board-focus mj-realtime-surface-tabs {\n flex: 1;\n min-width: 0;\n}\n\n/* Floating call pill: orb + state + mute / show-thread / end (per the whiteboard mockup). */\n.board-focus-pill {\n position: absolute;\n left: 64px;\n top: 12px;\n z-index: 60;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 7px 9px 7px 12px;\n border-radius: var(--mj-radius-full, 999px);\n background: color-mix(in srgb, var(--mj-bg-surface-card) 92%, transparent);\n backdrop-filter: blur(8px);\n border: 1px solid var(--mj-border-strong);\n box-shadow: var(--mj-shadow-xl, 0 12px 32px rgba(0, 0, 0, 0.18));\n animation: overlay-note-in 220ms ease;\n}\n.board-focus-pill__orb {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n flex-shrink: 0;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.board-focus-pill__orb[data-state='speaking'],\n.board-focus-pill__orb[data-state='listening'] {\n animation: board-pill-pulse 1.6s ease-in-out infinite;\n}\n.board-focus-pill__orb[data-state='error'] {\n background: var(--mj-status-error);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-status-error) 18%, transparent);\n}\n@keyframes board-pill-pulse {\n 0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent); }\n 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--mj-brand-primary) 8%, transparent); }\n}\n.board-focus-pill__text {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n.board-focus-pill__name {\n font-size: 12px;\n font-weight: 700;\n line-height: 1.2;\n white-space: nowrap;\n color: var(--mj-text-primary);\n}\n.board-focus-pill__state {\n font-size: 10.5px;\n font-weight: 600;\n white-space: nowrap;\n color: var(--mj-brand-primary);\n}\n.board-focus-pill__btn {\n width: 30px;\n height: 30px;\n border-radius: 50%;\n border: 1px solid var(--mj-border-strong);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n font-size: 11px;\n display: grid;\n place-items: center;\n font-family: inherit;\n flex-shrink: 0;\n}\n.board-focus-pill__btn:hover {\n color: var(--mj-text-primary);\n}\n.board-focus-pill__btn--active {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n.board-focus-pill__btn--end {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n color: var(--mj-text-inverse, #fff);\n}\n\n/* ---------- Session review mode ---------- */\n/* Subtle archival tint so a glance says \"this is history, not a live call\". */\n.call-overlay--review {\n background:\n linear-gradient(180deg, color-mix(in srgb, var(--mj-text-muted) 4%, transparent), transparent 120px),\n var(--mj-bg-surface);\n}\n\n/* The review whiteboard pane body: the board component positions itself absolutely\n (inset: 0), so its template-based pane needs a filling, positioned frame. */\n.review-board-host {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n/* ---------- flex layout (main column | resize handle | surface panel) ---------- */\n/* Token-styled resize handle: invisible until hover, brand-tinted while interacting.\n 7px hit target, col-resize cursor; removed (not rendered) while collapsed / focus. */\n.call-resizer {\n flex: 0 0 7px;\n width: 7px;\n cursor: col-resize;\n background: transparent;\n transition: background 140ms ease;\n}\n.call-resizer:hover,\n.call-resizer--active {\n background: color-mix(in srgb, var(--mj-brand-primary) 26%, transparent);\n}\n/* The surface panel: a plain fixed-width flex item (inline width set by the overlay;\n collapsed = the slim strip width). flex-shrink lets it yield to the call column's\n min-width on tight overlays; overflow hidden keeps the flex height chain intact so\n the panel's content scrolls internally. */\n.call-panel {\n flex: 0 1 auto;\n min-width: 0;\n display: flex;\n overflow: hidden;\n /* Disclosure-gated: the panel is CREATED when earned/peeked \u2014 its arrival slides in\n from the right so the reveal doubles as the progress cue. */\n animation: panel-in 420ms cubic-bezier(0.25, 0.8, 0.3, 1);\n}\n@keyframes panel-in {\n from { opacity: 0; transform: translateX(48px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.call-panel > * {\n flex: 1;\n min-width: 0;\n min-height: 0;\n}\n/* Board-focus fill: the panel owns the whole overlay (the main column is hidden). */\n.call-overlay.board-focus .call-panel {\n flex: 1;\n}\n"] }]
|
|
1876
|
+
RealtimeWhiteboardBoardComponent,
|
|
1877
|
+
MJStorageMediaPlayerComponent
|
|
1878
|
+
], template: "@if (ConnectionState$ | async; as state) {\n <div class=\"call-overlay\"\n [class.call-overlay--hidden]=\"Hidden\"\n [class.call-overlay--review]=\"IsReviewing\"\n [class.board-focus]=\"ChannelFocusMode\"\n role=\"region\" [attr.aria-label]=\"IsReviewing ? 'Past session review' : 'Live voice session'\">\n\n <!-- PROGRESSIVE DISCLOSURE (the console that grows with you \u2014 Redesign A Progressive):\n level 0 is PURE AUDIO (the hero orb owns the main column; no thread, no composer;\n the strip's Details control peeks at the panels on demand); level 1 reveals the\n caption thread + captions control + type-to-type hint; level 2+ docks the composer\n and keeps the surface panel. CONTENT NEVER FLIPS THE CONSOLE OPEN: finished\n artifacts land as glowing unfocused tabs; the ONE auto-reveal is a channel's\n first agent activity (e.g. the first whiteboard write), which opens the panel as\n a peek with that tab focused \u2014 the left column stays exactly as it was.\n REVIEW always renders the full console. -->\n <div class=\"call-main\">\n <!-- The UNIFIED APP-BAR: identity + state + every session action (Redesign A). -->\n <mj-realtime-agent-banner\n [State]=\"state\"\n [Compact]=\"!IsReviewing && Ui.compact && Ui.chrome === 'orb'\"\n [AgentName]=\"AgentName\"\n [ModelName]=\"IsReviewing ? null : (ModelName$ | async)\"\n [DevMode]=\"DevMode\"\n [SessionID]=\"IsReviewing ? ReviewSessionID : SessionID\"\n [ReviewMode]=\"IsReviewing\"\n [ReviewStartedAt]=\"ReviewStartedAt\"\n [ReviewClosedAt]=\"ReviewClosedAt\"\n [ReviewCloseReason]=\"ReviewCloseReason\"\n [ShowGear]=\"!IsReviewing && Ui.showGear\"\n [ShowDensityPicker]=\"Ui.showDensityPicker\"\n [ShowMinimize]=\"!IsReviewing && Ui.showMinimize\"\n [Density]=\"Disclosure.Milestones.Density\"\n (OpenSessionRequested)=\"OnOpenSessionRequested($event)\"\n (DevModeToggled)=\"OnDevModeToggled($event)\"\n (DensityChanged)=\"OnDensityChanged($event)\"\n (MinimizeRequested)=\"OnMinimize()\"\n (StartLiveRequested)=\"OnStartLive()\"\n (CloseRequested)=\"OnReviewClose()\">\n </mj-realtime-agent-banner>\n\n <!-- Ephemeral narration with no active working card to anchor to \u2192 under the banner -->\n @if (!IsReviewing && State.Narration && !State.ActiveCallId && Disclosure.ShowThread) {\n <div class=\"banner-live-note\" role=\"status\" aria-live=\"polite\">\n <i class=\"fa-solid fa-comment-dots\" aria-hidden=\"true\"></i>\n {{ State.Narration }}\n </div>\n }\n\n <div class=\"call-body\" [class.call-body--hero]=\"ShowHero && state !== 'connecting'\">\n @if (!IsReviewing && state === 'connecting') {\n <div class=\"call-connecting\">\n <mj-loading text=\"Connecting to the live session\u2026\" size=\"medium\"></mj-loading>\n </div>\n } @else if (ShowHero) {\n <!-- PURE-AUDIO HERO (level 0): the orb IS the interface \u2014 it breathes with the\n audio inside expanding sound-wave rings; nothing to read, nothing to configure. -->\n <div class=\"hero\" [attr.data-state]=\"state\" role=\"status\" aria-live=\"polite\">\n <div class=\"hero__stage\">\n <span class=\"hero__ring\" aria-hidden=\"true\"></span>\n <span class=\"hero__ring hero__ring--2\" aria-hidden=\"true\"></span>\n <span class=\"hero__ring hero__ring--3\" aria-hidden=\"true\"></span>\n <div class=\"hero__orb\" [attr.data-state]=\"HeroOrbState(state)\" aria-hidden=\"true\"></div>\n </div>\n <div class=\"hero__eq\" aria-hidden=\"true\">\n <span></span><span></span><span></span><span></span><span></span>\n <span></span><span></span><span></span><span></span>\n </div>\n <div class=\"hero__name\">{{ AgentName }}</div>\n <div class=\"hero__sub\">{{ HeroStateLabel(state) }}</div>\n <button type=\"button\" class=\"hero__reveal\" (click)=\"OnTextReveal()\"\n title=\"Show the conversation as text\">\n <i class=\"fa-solid fa-closed-captioning\" aria-hidden=\"true\"></i>\n Show the conversation\n </button>\n </div>\n } @else {\n <mj-realtime-session-thread\n [State]=\"State\"\n [AgentName]=\"AgentName\"\n [UserName]=\"CurrentUser?.Name || 'You'\"\n [ShowCaptions]=\"IsReviewing ? true : ShowCaptions\"\n [DevMode]=\"DevMode\"\n (OpenRunRequested)=\"OnOpenRunRequested($event)\"\n (OpenArtifactRequested)=\"OnOpenArtifactRequested($event)\"\n (CancelRequested)=\"OnCancelDelegation($event)\">\n </mj-realtime-session-thread>\n }\n </div>\n\n @if (!IsReviewing) {\n <!-- Active channels (earned with the console at level 2+, host-gated via [ShowChannels]).\n The strip component self-hides when there are no active channels. -->\n @if (ChannelStripVisible) {\n <mj-realtime-channel-strip></mj-realtime-channel-strip>\n }\n\n <!-- The BOTTOM DOCK: phone-call strip \u21C4 fused minis+composer \u2014 the user's own\n two-way toggle (Type opens, hide closes), never level-forced. -->\n <mj-realtime-composer\n [Open]=\"ComposerOpen\"\n [Compact]=\"Ui.compact && Ui.chrome === 'orb'\"\n (OpenChanged)=\"OnComposerOpenChanged($event)\"\n [CaptionsOn]=\"ShowCaptions\"\n [IsMuted]=\"Muted\"\n [ShowDetails]=\"ShowDetailsControl\"\n [DetailsOn]=\"DetailsPeek\"\n (CaptionsToggled)=\"OnCaptionsToggled($event)\"\n (DetailsToggled)=\"OnDetailsToggled()\"\n (MuteChanged)=\"OnComposerMuteChanged($event)\"\n (EndRequested)=\"OnEndCall()\">\n </mj-realtime-composer>\n }\n </div>\n\n <!-- RIGHT: TABBED SURFACE PANEL \u2014 disclosure-gated: it EXISTS once earned (level 2+,\n which the first delegation forces), peeked on demand via the strip's Details\n control, or always in review. Slides in from the right when it arrives \u2014 the\n panel's arrival IS the progress cue. -->\n @if (ShowPanelArea) {\n <!-- Resize handle (hidden while the panel is collapsed or a channel holds focus). -->\n @if (!PanelResizeDisabled) {\n <div class=\"call-resizer\"\n [class.call-resizer--active]=\"IsPanelResizing\"\n role=\"separator\" aria-orientation=\"vertical\"\n aria-label=\"Resize the surface panel (double-click to reset)\"\n title=\"Drag to resize \u2022 double-click to reset\"\n (mousedown)=\"OnPanelResizeStart($event)\"\n (dblclick)=\"OnPanelResizeReset()\"></div>\n }\n\n <div class=\"call-panel\" [style.width.px]=\"ChannelFocusMode ? null : PanelAreaSize\">\n <mj-realtime-surface-tabs\n [State]=\"State\"\n [DevMode]=\"DevMode\"\n [CurrentUser]=\"CurrentUser\"\n [EnvironmentID]=\"EnvironmentID\"\n [ShowActivityTab]=\"ShowActivityTab\"\n [ExtraArtifacts]=\"ReviewArtifacts\"\n [Fill]=\"ChannelFocusMode\"\n (OpenRunRequested)=\"OnOpenRunRequested($event)\"\n (CollapsedChange)=\"OnPanelCollapsedChange($event)\"\n (WideChanged)=\"OnPanelWideChanged($event)\">\n </mj-realtime-surface-tabs>\n </div>\n }\n\n <!-- FOCUS floating call pill: the main column is hidden, so the live call's\n identity + state + mute / show-thread / end controls ride over the surface. -->\n @if (!IsReviewing && ChannelFocusMode) {\n <div class=\"board-focus-pill\" role=\"status\" aria-label=\"Live call (board focus)\">\n <span class=\"board-focus-pill__orb\" [attr.data-state]=\"state\" aria-hidden=\"true\"></span>\n <span class=\"board-focus-pill__text\">\n <span class=\"board-focus-pill__name\">Voice for {{ AgentName }}</span>\n @if (FocusStateLabel(state); as label) {\n <span class=\"board-focus-pill__state\">{{ label }}</span>\n }\n </span>\n <button type=\"button\" class=\"board-focus-pill__btn\"\n [class.board-focus-pill__btn--active]=\"FocusPillMuted\"\n (click)=\"OnFocusPillMute()\"\n [title]=\"FocusPillMuted ? 'Unmute' : 'Mute'\"\n [attr.aria-label]=\"FocusPillMuted ? 'Unmute microphone' : 'Mute microphone'\">\n <i class=\"fa-solid\" [class.fa-microphone]=\"!FocusPillMuted\" [class.fa-microphone-slash]=\"FocusPillMuted\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"board-focus-pill__btn\"\n (click)=\"OnFocusPillExit()\"\n title=\"Show thread\" aria-label=\"Show the conversation thread\">\n <i class=\"fa-solid fa-table-columns\" aria-hidden=\"true\"></i>\n </button>\n <button type=\"button\" class=\"board-focus-pill__btn board-focus-pill__btn--end\"\n (click)=\"OnFocusPillEnd()\"\n title=\"End call\" aria-label=\"End the call\">\n <i class=\"fa-solid fa-phone-slash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n </div>\n}\n\n<!-- SESSION REVIEW whiteboard pane body: the reviewed session's saved board, rehydrated\n locally (WhiteboardState.FromJSON) and rendered READ-ONLY (pan/zoom only). Registered\n as a template-based channel tab ONLY when a parseable Whiteboard state exists. -->\n<ng-template #reviewBoardTpl>\n @if (ReviewWhiteboard; as board) {\n <!-- The board host positions itself absolutely \u2014 give it a filling, positioned frame. -->\n <div class=\"review-board-host\">\n <mj-realtime-whiteboard\n [State]=\"board\"\n [AgentName]=\"AgentName\"\n [ReadOnly]=\"true\">\n </mj-realtime-whiteboard>\n </div>\n }\n</ng-template>\n\n<!-- SESSION REVIEW recording pane body: the reviewed session's recorded audio synced to its\n transcript, replayed via the shared storage media player (click-to-seek transcript + playhead\n highlight). Registered as a template-based channel tab ONLY when the reviewed session carries a\n RecordingFileID; the player resolves the authenticated audio itself from that file id. -->\n<ng-template #recordingTpl>\n <div class=\"review-recording-host\">\n <mj-storage-media-player\n [FileID]=\"ReviewRecordingFileID\"\n [Transcript]=\"ReviewCues\"\n [Provider]=\"Provider\">\n </mj-storage-media-player>\n </div>\n</ng-template>\n", styles: [":host {\n display: contents;\n}\n\n/* Fills the conversation panel IN PLACE: the chat area host provides the positioned\n context (position:relative on .chat-area) and this covers it edge to edge \u2014 header,\n thread and composer included. Opaque-ish page surface with a slight blur so the\n conversation underneath reads as \"paused\", not as an app-wide modal. */\n.call-overlay {\n position: absolute;\n inset: 0;\n z-index: 50;\n display: flex;\n background: color-mix(in srgb, var(--mj-bg-page) 94%, transparent);\n backdrop-filter: blur(8px);\n overflow: hidden;\n}\n\n/* Minimized: hidden (NOT destroyed) so the shell's session/view state survives while\n the host shows its floating \"on call\" pill. The audio session is untouched. */\n.call-overlay--hidden {\n display: none;\n}\n\n/* MAIN column: banner (top) \u2192 scrolling thread \u2192 channel strip \u2192 composer \u2192 controls.\n The 420px floor keeps the call column readable while the panel is dragged wide\n (the drag clamp's 70% cap is the other guard). */\n.call-main {\n flex: 1;\n min-width: 420px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.call-body {\n flex: 1;\n overflow-y: auto;\n padding: 16px 22px;\n min-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--mj-border-strong) transparent;\n}\n.call-body::-webkit-scrollbar {\n width: 6px;\n}\n.call-body::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\n.call-connecting {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 160px;\n}\n\n/* ---------- PURE-AUDIO HERO (disclosure level 0) ----------\n The orb IS the interface: it breathes with the audio inside expanding sound-wave\n rings, with a small voice equalizer underneath. Nothing to read, nothing to\n configure \u2014 text, panels and the composer are revealed later (or peeked on demand). */\n.call-body--hero {\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n}\n.hero {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n animation: hero-in 420ms cubic-bezier(0.2, 0.8, 0.3, 1);\n}\n@keyframes hero-in {\n from { opacity: 0; transform: scale(0.94); }\n to { opacity: 1; transform: scale(1); }\n}\n.hero__stage {\n position: relative;\n width: 220px;\n height: 220px;\n display: grid;\n place-items: center;\n margin-bottom: 4px;\n}\n/* The hero orb mirrors the app-bar orb's visual language at stage scale. */\n.hero__orb {\n width: 120px;\n height: 120px;\n border-radius: 50%;\n position: relative;\n background: radial-gradient(circle at 35% 30%,\n var(--mj-brand-accent, var(--mj-brand-primary)),\n var(--mj-brand-primary) 60%,\n var(--mj-brand-primary-active, var(--mj-brand-primary)));\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-brand-primary) 45%, transparent);\n}\n.hero__orb[data-state='speaking'] {\n animation: hero-orb-bob 0.9s ease-in-out infinite;\n}\n.hero__orb[data-state='listening'] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-success) 16%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-status-success) 40%, transparent);\n background: radial-gradient(circle at 35% 30%,\n color-mix(in srgb, var(--mj-status-success) 60%, white), var(--mj-brand-primary) 65%);\n}\n.hero__orb[data-state='thinking'] {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-warning) 14%, transparent),\n 0 0 40px color-mix(in srgb, var(--mj-status-warning) 35%, transparent);\n}\n@keyframes hero-orb-bob {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-4px); }\n}\n/* Expanding sound-wave rings (staggered). */\n.hero__ring {\n position: absolute;\n width: 120px;\n height: 120px;\n border-radius: 50%;\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 55%, transparent);\n opacity: 0;\n animation: hero-wave 2.7s cubic-bezier(0.2, 0.6, 0.4, 1) infinite;\n}\n.hero__ring--2 { animation-delay: 0.9s; }\n.hero__ring--3 { animation-delay: 1.8s; }\n@keyframes hero-wave {\n 0% { transform: scale(1); opacity: 0.5; }\n 70% { opacity: 0.1; }\n 100% { transform: scale(1.85); opacity: 0; }\n}\n/* Voice equalizer: lively while the agent speaks, near-still otherwise. */\n.hero__eq {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 30px;\n margin-bottom: 6px;\n}\n.hero__eq span {\n width: 4px;\n height: 8px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-brand-accent, var(--mj-brand-primary));\n opacity: 0.55;\n}\n.hero[data-state='speaking'] .hero__eq span {\n animation: hero-eq 1.05s ease-in-out infinite;\n}\n.hero__eq span:nth-child(1) { animation-delay: 0s; }\n.hero__eq span:nth-child(2) { animation-delay: 0.14s; }\n.hero__eq span:nth-child(3) { animation-delay: 0.28s; }\n.hero__eq span:nth-child(4) { animation-delay: 0.07s; }\n.hero__eq span:nth-child(5) { animation-delay: 0.21s; }\n.hero__eq span:nth-child(6) { animation-delay: 0.35s; }\n.hero__eq span:nth-child(7) { animation-delay: 0.1s; }\n.hero__eq span:nth-child(8) { animation-delay: 0.24s; }\n.hero__eq span:nth-child(9) { animation-delay: 0.03s; }\n@keyframes hero-eq {\n 0%, 100% { height: 7px; opacity: 0.6; }\n 50% { height: 28px; opacity: 1; }\n}\n.hero__name {\n font-size: 19px;\n font-weight: 800;\n letter-spacing: -0.01em;\n color: var(--mj-text-primary);\n}\n.hero__sub {\n font-size: 12.5px;\n color: var(--mj-text-muted);\n}\n/* ---------- AUDIO-REACTIVE MODE (data-audio-live, set by the rAF meter loop) ----------\n With real audio metering the orb stops acting and starts REACTING: scale + glow follow\n the smoothed output envelope (--voice-out) like a speaker cone, the EQ renders the true\n spectrum (--eq-1..9), and [data-voice-dir] colors the visuals by who is speaking\n (agent = brand, user = green). Without metering (driver attached no meters) the\n attribute stays 'false' and the turn-state keyframe animations above stay in charge. */\n.call-overlay[data-audio-live='true'] .hero__orb {\n animation: none;\n transform: scale(calc(1 + var(--voice-out, 0) * 0.18));\n transition: transform 70ms linear;\n}\n.call-overlay[data-audio-live='true'] .hero__ring {\n opacity: calc(var(--voice-out, 0) * 0.55);\n animation-duration: 2.1s;\n}\n.call-overlay[data-audio-live='true'][data-voice-dir='user'] .hero__orb {\n box-shadow:\n 0 0 0 8px color-mix(in srgb, var(--mj-status-success) 16%, transparent),\n 0 0 48px color-mix(in srgb, var(--mj-status-success) 45%, transparent);\n background: radial-gradient(circle at 35% 30%,\n color-mix(in srgb, var(--mj-status-success) 60%, white), var(--mj-brand-primary) 65%);\n}\n.call-overlay[data-audio-live='true'][data-voice-dir='user'] .hero__ring {\n border-color: color-mix(in srgb, var(--mj-status-success) 55%, transparent);\n}\n/* True-spectrum EQ: per-bar heights from the meter's bins. */\n.call-overlay[data-audio-live='true'] .hero__eq span {\n animation: none;\n transition: height 70ms linear, opacity 120ms linear;\n}\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(1) { height: calc(6px + var(--eq-1, 0) * 24px); opacity: calc(0.45 + var(--eq-1, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(2) { height: calc(6px + var(--eq-2, 0) * 24px); opacity: calc(0.45 + var(--eq-2, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(3) { height: calc(6px + var(--eq-3, 0) * 24px); opacity: calc(0.45 + var(--eq-3, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(4) { height: calc(6px + var(--eq-4, 0) * 24px); opacity: calc(0.45 + var(--eq-4, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(5) { height: calc(6px + var(--eq-5, 0) * 24px); opacity: calc(0.45 + var(--eq-5, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(6) { height: calc(6px + var(--eq-6, 0) * 24px); opacity: calc(0.45 + var(--eq-6, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(7) { height: calc(6px + var(--eq-7, 0) * 24px); opacity: calc(0.45 + var(--eq-7, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(8) { height: calc(6px + var(--eq-8, 0) * 24px); opacity: calc(0.45 + var(--eq-8, 0) * 0.55); }\n.call-overlay[data-audio-live='true'] .hero__eq span:nth-child(9) { height: calc(6px + var(--eq-9, 0) * 24px); opacity: calc(0.45 + var(--eq-9, 0) * 0.55); }\n.call-overlay[data-audio-live='true'][data-voice-dir='user'] .hero__eq span {\n background: var(--mj-status-success);\n}\n\n/* The quiet first reveal: show the conversation as text (raises disclosure to level 1). */\n.hero__reveal {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-top: 18px;\n padding: 6px 14px;\n border-radius: var(--mj-radius-full, 9999px);\n border: 1px dashed var(--mj-border-strong);\n background: transparent;\n color: var(--mj-text-muted);\n font-family: inherit;\n font-size: 11.5px;\n font-weight: 600;\n cursor: pointer;\n transition: color 140ms ease, border-color 140ms ease, background 140ms ease;\n}\n.hero__reveal:hover {\n color: var(--mj-text-secondary);\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n.hero__reveal i {\n font-size: 11px;\n}\n\n/* Ephemeral narration shown under the banner when no working card anchors it */\n.banner-live-note {\n display: flex;\n align-items: flex-start;\n gap: 7px;\n padding: 8px 22px 0;\n font-size: 12px;\n font-style: italic;\n color: var(--mj-text-muted);\n animation: overlay-note-in 220ms ease;\n}\n.banner-live-note i {\n font-size: 10px;\n margin-top: 3px;\n flex-shrink: 0;\n}\n@keyframes overlay-note-in {\n from { opacity: 0; transform: translateY(2px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* ---------- FOCUS-BOARD mode (whiteboard \"Focus board\" toggle) ----------\n The main call column collapses away entirely and the surface panel (whiteboard tab)\n fills the overlay; a compact floating call pill keeps the live call reachable. */\n.call-overlay.board-focus .call-main {\n display: none;\n}\n.call-overlay.board-focus mj-realtime-surface-tabs {\n flex: 1;\n min-width: 0;\n}\n\n/* Floating call pill: orb + state + mute / show-thread / end (per the whiteboard mockup). */\n.board-focus-pill {\n position: absolute;\n left: 64px;\n top: 12px;\n z-index: 60;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 7px 9px 7px 12px;\n border-radius: var(--mj-radius-full, 999px);\n background: color-mix(in srgb, var(--mj-bg-surface-card) 92%, transparent);\n backdrop-filter: blur(8px);\n border: 1px solid var(--mj-border-strong);\n box-shadow: var(--mj-shadow-xl, 0 12px 32px rgba(0, 0, 0, 0.18));\n animation: overlay-note-in 220ms ease;\n}\n.board-focus-pill__orb {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n flex-shrink: 0;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.board-focus-pill__orb[data-state='speaking'],\n.board-focus-pill__orb[data-state='listening'] {\n animation: board-pill-pulse 1.6s ease-in-out infinite;\n}\n.board-focus-pill__orb[data-state='error'] {\n background: var(--mj-status-error);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-status-error) 18%, transparent);\n}\n@keyframes board-pill-pulse {\n 0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent); }\n 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--mj-brand-primary) 8%, transparent); }\n}\n.board-focus-pill__text {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n.board-focus-pill__name {\n font-size: 12px;\n font-weight: 700;\n line-height: 1.2;\n white-space: nowrap;\n color: var(--mj-text-primary);\n}\n.board-focus-pill__state {\n font-size: 10.5px;\n font-weight: 600;\n white-space: nowrap;\n color: var(--mj-brand-primary);\n}\n.board-focus-pill__btn {\n width: 30px;\n height: 30px;\n border-radius: 50%;\n border: 1px solid var(--mj-border-strong);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n font-size: 11px;\n display: grid;\n place-items: center;\n font-family: inherit;\n flex-shrink: 0;\n}\n.board-focus-pill__btn:hover {\n color: var(--mj-text-primary);\n}\n.board-focus-pill__btn--active {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n.board-focus-pill__btn--end {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n color: var(--mj-text-inverse, #fff);\n}\n\n/* ---------- Session review mode ---------- */\n/* Subtle archival tint so a glance says \"this is history, not a live call\". */\n.call-overlay--review {\n background:\n linear-gradient(180deg, color-mix(in srgb, var(--mj-text-muted) 4%, transparent), transparent 120px),\n var(--mj-bg-surface);\n}\n\n/* The review whiteboard pane body: the board component positions itself absolutely\n (inset: 0), so its template-based pane needs a filling, positioned frame. */\n.review-board-host {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n/* The review recording pane body: the evidence player fills its host (:host height:100%),\n so its template-based pane needs a filling, constrained frame. */\n.review-recording-host {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n.review-recording-host mj-realtime-evidence-playback {\n flex: 1;\n min-height: 0;\n}\n\n/* ---------- flex layout (main column | resize handle | surface panel) ---------- */\n/* Token-styled resize handle: invisible until hover, brand-tinted while interacting.\n 7px hit target, col-resize cursor; removed (not rendered) while collapsed / focus. */\n.call-resizer {\n flex: 0 0 7px;\n width: 7px;\n cursor: col-resize;\n background: transparent;\n transition: background 140ms ease;\n}\n.call-resizer:hover,\n.call-resizer--active {\n background: color-mix(in srgb, var(--mj-brand-primary) 26%, transparent);\n}\n/* The surface panel: a plain fixed-width flex item (inline width set by the overlay;\n collapsed = the slim strip width). flex-shrink lets it yield to the call column's\n min-width on tight overlays; overflow hidden keeps the flex height chain intact so\n the panel's content scrolls internally. */\n.call-panel {\n flex: 0 1 auto;\n min-width: 0;\n display: flex;\n overflow: hidden;\n /* Disclosure-gated: the panel is CREATED when earned/peeked \u2014 its arrival slides in\n from the right so the reveal doubles as the progress cue. */\n animation: panel-in 420ms cubic-bezier(0.25, 0.8, 0.3, 1);\n}\n@keyframes panel-in {\n from { opacity: 0; transform: translateX(48px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.call-panel > * {\n flex: 1;\n min-width: 0;\n min-height: 0;\n}\n/* Board-focus fill: the panel owns the whole overlay (the main column is hidden). */\n.call-overlay.board-focus .call-panel {\n flex: 1;\n}\n"] }]
|
|
1242
1879
|
}], () => [], { Hidden: [{
|
|
1243
1880
|
type: Input
|
|
1244
1881
|
}], AgentName: [{
|
|
@@ -1247,6 +1884,36 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1247
1884
|
type: Input
|
|
1248
1885
|
}], EnvironmentID: [{
|
|
1249
1886
|
type: Input
|
|
1887
|
+
}], Chrome: [{
|
|
1888
|
+
type: Input
|
|
1889
|
+
}], ConsoleBreakpointPx: [{
|
|
1890
|
+
type: Input
|
|
1891
|
+
}], Compact: [{
|
|
1892
|
+
type: Input
|
|
1893
|
+
}], AutoHideControls: [{
|
|
1894
|
+
type: Input
|
|
1895
|
+
}], AllowTextReveal: [{
|
|
1896
|
+
type: Input
|
|
1897
|
+
}], ShowCaptionsControl: [{
|
|
1898
|
+
type: Input
|
|
1899
|
+
}], ShowDensityPicker: [{
|
|
1900
|
+
type: Input
|
|
1901
|
+
}], ShowMinimize: [{
|
|
1902
|
+
type: Input
|
|
1903
|
+
}], ShowEnd: [{
|
|
1904
|
+
type: Input
|
|
1905
|
+
}], ShowSurfacePanel: [{
|
|
1906
|
+
type: Input
|
|
1907
|
+
}], ShowChannels: [{
|
|
1908
|
+
type: Input
|
|
1909
|
+
}], ShowActivityRail: [{
|
|
1910
|
+
type: Input
|
|
1911
|
+
}], ShowDevLinks: [{
|
|
1912
|
+
type: Input
|
|
1913
|
+
}], AllowResize: [{
|
|
1914
|
+
type: Input
|
|
1915
|
+
}], UiConfig: [{
|
|
1916
|
+
type: Input
|
|
1250
1917
|
}], ReviewData: [{
|
|
1251
1918
|
type: Input
|
|
1252
1919
|
}], Ended: [{
|
|
@@ -1257,6 +1924,22 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1257
1924
|
type: Output
|
|
1258
1925
|
}], ReviewClosed: [{
|
|
1259
1926
|
type: Output
|
|
1927
|
+
}], Minimized: [{
|
|
1928
|
+
type: Output
|
|
1929
|
+
}], TextRevealed: [{
|
|
1930
|
+
type: Output
|
|
1931
|
+
}], ChromeChanged: [{
|
|
1932
|
+
type: Output
|
|
1933
|
+
}], ConnectionStateChanged: [{
|
|
1934
|
+
type: Output
|
|
1935
|
+
}], MuteChanged: [{
|
|
1936
|
+
type: Output
|
|
1937
|
+
}], DensityChanged: [{
|
|
1938
|
+
type: Output
|
|
1939
|
+
}], SurfacePanelResized: [{
|
|
1940
|
+
type: Output
|
|
1941
|
+
}], ControlInvoked: [{
|
|
1942
|
+
type: Output
|
|
1260
1943
|
}], surfaceTabsRef: [{
|
|
1261
1944
|
type: ViewChild,
|
|
1262
1945
|
args: [RealtimeSurfaceTabsComponent]
|
|
@@ -1266,9 +1949,12 @@ export class RealtimeSessionOverlayComponent {
|
|
|
1266
1949
|
}], reviewBoardTpl: [{
|
|
1267
1950
|
type: ViewChild,
|
|
1268
1951
|
args: ['reviewBoardTpl']
|
|
1952
|
+
}], recordingTpl: [{
|
|
1953
|
+
type: ViewChild,
|
|
1954
|
+
args: ['recordingTpl']
|
|
1269
1955
|
}], OnDocumentKeydown: [{
|
|
1270
1956
|
type: HostListener,
|
|
1271
1957
|
args: ['document:keydown', ['$event']]
|
|
1272
1958
|
}] }); })();
|
|
1273
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RealtimeSessionOverlayComponent, { className: "RealtimeSessionOverlayComponent", filePath: "src/lib/components/realtime/realtime-session-overlay.component.ts", lineNumber:
|
|
1959
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RealtimeSessionOverlayComponent, { className: "RealtimeSessionOverlayComponent", filePath: "src/lib/components/realtime/realtime-session-overlay.component.ts", lineNumber: 128 }); })();
|
|
1274
1960
|
//# sourceMappingURL=realtime-session-overlay.component.js.map
|