@memberjunction/ng-conversations 5.40.1 → 5.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/README.md +57 -0
  2. package/dist/__tests__/channel-optional-surface.test.d.ts +2 -0
  3. package/dist/__tests__/channel-optional-surface.test.d.ts.map +1 -0
  4. package/dist/__tests__/channel-optional-surface.test.js +53 -0
  5. package/dist/__tests__/channel-optional-surface.test.js.map +1 -0
  6. package/dist/__tests__/chat-events.test.d.ts +14 -0
  7. package/dist/__tests__/chat-events.test.d.ts.map +1 -0
  8. package/dist/__tests__/chat-events.test.js +109 -0
  9. package/dist/__tests__/chat-events.test.js.map +1 -0
  10. package/dist/__tests__/conversation-naming.test.d.ts +2 -0
  11. package/dist/__tests__/conversation-naming.test.d.ts.map +1 -0
  12. package/dist/__tests__/conversation-naming.test.js +110 -0
  13. package/dist/__tests__/conversation-naming.test.js.map +1 -0
  14. package/dist/__tests__/delegation-result-parser.test.d.ts +2 -0
  15. package/dist/__tests__/delegation-result-parser.test.d.ts.map +1 -0
  16. package/dist/__tests__/delegation-result-parser.test.js +107 -0
  17. package/dist/__tests__/delegation-result-parser.test.js.map +1 -0
  18. package/dist/__tests__/event-wiring.test.d.ts +15 -0
  19. package/dist/__tests__/event-wiring.test.d.ts.map +1 -0
  20. package/dist/__tests__/event-wiring.test.js +100 -0
  21. package/dist/__tests__/event-wiring.test.js.map +1 -0
  22. package/dist/__tests__/narration-template.test.d.ts +2 -0
  23. package/dist/__tests__/narration-template.test.d.ts.map +1 -0
  24. package/dist/__tests__/narration-template.test.js +76 -0
  25. package/dist/__tests__/narration-template.test.js.map +1 -0
  26. package/dist/__tests__/realtime-agent-picker-models.test.d.ts +2 -0
  27. package/dist/__tests__/realtime-agent-picker-models.test.d.ts.map +1 -0
  28. package/dist/__tests__/realtime-agent-picker-models.test.js +49 -0
  29. package/dist/__tests__/realtime-agent-picker-models.test.js.map +1 -0
  30. package/dist/__tests__/realtime-audio-visuals.test.d.ts +2 -0
  31. package/dist/__tests__/realtime-audio-visuals.test.d.ts.map +1 -0
  32. package/dist/__tests__/realtime-audio-visuals.test.js +123 -0
  33. package/dist/__tests__/realtime-audio-visuals.test.js.map +1 -0
  34. package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts +2 -0
  35. package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts.map +1 -0
  36. package/dist/__tests__/realtime-delegation-card-cancel.test.js +48 -0
  37. package/dist/__tests__/realtime-delegation-card-cancel.test.js.map +1 -0
  38. package/dist/__tests__/realtime-disclosure.test.d.ts +2 -0
  39. package/dist/__tests__/realtime-disclosure.test.d.ts.map +1 -0
  40. package/dist/__tests__/realtime-disclosure.test.js +164 -0
  41. package/dist/__tests__/realtime-disclosure.test.js.map +1 -0
  42. package/dist/__tests__/realtime-pairing.test.d.ts +2 -0
  43. package/dist/__tests__/realtime-pairing.test.d.ts.map +1 -0
  44. package/dist/__tests__/realtime-pairing.test.js +207 -0
  45. package/dist/__tests__/realtime-pairing.test.js.map +1 -0
  46. package/dist/__tests__/realtime-review-lifecycle.test.d.ts +2 -0
  47. package/dist/__tests__/realtime-review-lifecycle.test.d.ts.map +1 -0
  48. package/dist/__tests__/realtime-review-lifecycle.test.js +154 -0
  49. package/dist/__tests__/realtime-review-lifecycle.test.js.map +1 -0
  50. package/dist/__tests__/realtime-session-cancel-usage.test.d.ts +2 -0
  51. package/dist/__tests__/realtime-session-cancel-usage.test.d.ts.map +1 -0
  52. package/dist/__tests__/realtime-session-cancel-usage.test.js +230 -0
  53. package/dist/__tests__/realtime-session-cancel-usage.test.js.map +1 -0
  54. package/dist/__tests__/realtime-session-channels.test.d.ts +2 -0
  55. package/dist/__tests__/realtime-session-channels.test.d.ts.map +1 -0
  56. package/dist/__tests__/realtime-session-channels.test.js +252 -0
  57. package/dist/__tests__/realtime-session-channels.test.js.map +1 -0
  58. package/dist/__tests__/realtime-session-client-tools.test.d.ts +2 -0
  59. package/dist/__tests__/realtime-session-client-tools.test.d.ts.map +1 -0
  60. package/dist/__tests__/realtime-session-client-tools.test.js +103 -0
  61. package/dist/__tests__/realtime-session-client-tools.test.js.map +1 -0
  62. package/dist/__tests__/realtime-session-minimized.test.d.ts +2 -0
  63. package/dist/__tests__/realtime-session-minimized.test.d.ts.map +1 -0
  64. package/dist/__tests__/realtime-session-minimized.test.js +32 -0
  65. package/dist/__tests__/realtime-session-minimized.test.js.map +1 -0
  66. package/dist/__tests__/realtime-session-mint.test.d.ts +2 -0
  67. package/dist/__tests__/realtime-session-mint.test.d.ts.map +1 -0
  68. package/dist/__tests__/realtime-session-mint.test.js +69 -0
  69. package/dist/__tests__/realtime-session-mint.test.js.map +1 -0
  70. package/dist/__tests__/realtime-session-policy.test.d.ts +2 -0
  71. package/dist/__tests__/realtime-session-policy.test.d.ts.map +1 -0
  72. package/dist/__tests__/realtime-session-policy.test.js +303 -0
  73. package/dist/__tests__/realtime-session-policy.test.js.map +1 -0
  74. package/dist/__tests__/realtime-session-review.service.test.d.ts +2 -0
  75. package/dist/__tests__/realtime-session-review.service.test.d.ts.map +1 -0
  76. package/dist/__tests__/realtime-session-review.service.test.js +743 -0
  77. package/dist/__tests__/realtime-session-review.service.test.js.map +1 -0
  78. package/dist/__tests__/realtime-session-state.test.d.ts +2 -0
  79. package/dist/__tests__/realtime-session-state.test.d.ts.map +1 -0
  80. package/dist/__tests__/realtime-session-state.test.js +83 -0
  81. package/dist/__tests__/realtime-session-state.test.js.map +1 -0
  82. package/dist/__tests__/realtime-session-timeline-card.test.d.ts +2 -0
  83. package/dist/__tests__/realtime-session-timeline-card.test.d.ts.map +1 -0
  84. package/dist/__tests__/realtime-session-timeline-card.test.js +106 -0
  85. package/dist/__tests__/realtime-session-timeline-card.test.js.map +1 -0
  86. package/dist/__tests__/realtime-session-timeline.test.d.ts +2 -0
  87. package/dist/__tests__/realtime-session-timeline.test.d.ts.map +1 -0
  88. package/dist/__tests__/realtime-session-timeline.test.js +142 -0
  89. package/dist/__tests__/realtime-session-timeline.test.js.map +1 -0
  90. package/dist/__tests__/realtime-sessions-adapter.test.d.ts +19 -0
  91. package/dist/__tests__/realtime-sessions-adapter.test.d.ts.map +1 -0
  92. package/dist/__tests__/realtime-sessions-adapter.test.js +188 -0
  93. package/dist/__tests__/realtime-sessions-adapter.test.js.map +1 -0
  94. package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts +2 -0
  95. package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts.map +1 -0
  96. package/dist/__tests__/realtime-surface-panel-prefs.test.js +100 -0
  97. package/dist/__tests__/realtime-surface-panel-prefs.test.js.map +1 -0
  98. package/dist/__tests__/realtime-surface-tabs-model.test.d.ts +2 -0
  99. package/dist/__tests__/realtime-surface-tabs-model.test.d.ts.map +1 -0
  100. package/dist/__tests__/realtime-surface-tabs-model.test.js +193 -0
  101. package/dist/__tests__/realtime-surface-tabs-model.test.js.map +1 -0
  102. package/dist/__tests__/remote-browser-audio-player.test.d.ts +2 -0
  103. package/dist/__tests__/remote-browser-audio-player.test.d.ts.map +1 -0
  104. package/dist/__tests__/remote-browser-audio-player.test.js +137 -0
  105. package/dist/__tests__/remote-browser-audio-player.test.js.map +1 -0
  106. package/dist/__tests__/remote-browser-channel.test.d.ts +2 -0
  107. package/dist/__tests__/remote-browser-channel.test.d.ts.map +1 -0
  108. package/dist/__tests__/remote-browser-channel.test.js +423 -0
  109. package/dist/__tests__/remote-browser-channel.test.js.map +1 -0
  110. package/dist/__tests__/slot-defaults.test.d.ts +24 -0
  111. package/dist/__tests__/slot-defaults.test.d.ts.map +1 -0
  112. package/dist/__tests__/slot-defaults.test.js +63 -0
  113. package/dist/__tests__/slot-defaults.test.js.map +1 -0
  114. package/dist/__tests__/user-authorization.test.d.ts +2 -0
  115. package/dist/__tests__/user-authorization.test.d.ts.map +1 -0
  116. package/dist/__tests__/user-authorization.test.js +97 -0
  117. package/dist/__tests__/user-authorization.test.js.map +1 -0
  118. package/dist/__tests__/voice-session-narration.test.d.ts +2 -0
  119. package/dist/__tests__/voice-session-narration.test.d.ts.map +1 -0
  120. package/dist/__tests__/voice-session-narration.test.js +609 -0
  121. package/dist/__tests__/voice-session-narration.test.js.map +1 -0
  122. package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts +2 -0
  123. package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts.map +1 -0
  124. package/dist/__tests__/whiteboard-artifact-viewer.test.js +101 -0
  125. package/dist/__tests__/whiteboard-artifact-viewer.test.js.map +1 -0
  126. package/dist/__tests__/whiteboard-channel.test.d.ts +2 -0
  127. package/dist/__tests__/whiteboard-channel.test.d.ts.map +1 -0
  128. package/dist/__tests__/whiteboard-channel.test.js +260 -0
  129. package/dist/__tests__/whiteboard-channel.test.js.map +1 -0
  130. package/dist/__tests__/whiteboard-restore-state.test.d.ts +2 -0
  131. package/dist/__tests__/whiteboard-restore-state.test.d.ts.map +1 -0
  132. package/dist/__tests__/whiteboard-restore-state.test.js +108 -0
  133. package/dist/__tests__/whiteboard-restore-state.test.js.map +1 -0
  134. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +205 -3
  135. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  136. package/dist/lib/components/conversation/conversation-chat-area.component.js +911 -342
  137. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  138. package/dist/lib/components/mention/mention-dropdown.component.js +35 -17
  139. package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
  140. package/dist/lib/components/mention/mention-editor.component.d.ts +4 -0
  141. package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
  142. package/dist/lib/components/mention/mention-editor.component.js +43 -19
  143. package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
  144. package/dist/lib/components/message/message-input-box.component.d.ts +17 -1
  145. package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
  146. package/dist/lib/components/message/message-input-box.component.js +73 -15
  147. package/dist/lib/components/message/message-input-box.component.js.map +1 -1
  148. package/dist/lib/components/message/message-input.component.d.ts +142 -6
  149. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  150. package/dist/lib/components/message/message-input.component.js +328 -82
  151. package/dist/lib/components/message/message-input.component.js.map +1 -1
  152. package/dist/lib/components/message/message-item.component.d.ts +28 -3
  153. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  154. package/dist/lib/components/message/message-item.component.js +180 -108
  155. package/dist/lib/components/message/message-item.component.js.map +1 -1
  156. package/dist/lib/components/message/message-list.component.d.ts +81 -2
  157. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  158. package/dist/lib/components/message/message-list.component.js +252 -87
  159. package/dist/lib/components/message/message-list.component.js.map +1 -1
  160. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +282 -0
  161. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -0
  162. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +158 -0
  163. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -0
  164. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts +25 -0
  165. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts.map +1 -0
  166. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js +140 -0
  167. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js.map +1 -0
  168. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts +35 -0
  169. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts.map +1 -0
  170. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js +58 -0
  171. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js.map +1 -0
  172. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +63 -0
  173. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -0
  174. package/dist/lib/components/realtime/realtime-activity-rail.component.js +260 -0
  175. package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -0
  176. package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +117 -0
  177. package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -0
  178. package/dist/lib/components/realtime/realtime-agent-banner.component.js +504 -0
  179. package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -0
  180. package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +168 -0
  181. package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -0
  182. package/dist/lib/components/realtime/realtime-agent-picker.component.js +556 -0
  183. package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -0
  184. package/dist/lib/components/realtime/realtime-audio-visuals.d.ts +97 -0
  185. package/dist/lib/components/realtime/realtime-audio-visuals.d.ts.map +1 -0
  186. package/dist/lib/components/realtime/realtime-audio-visuals.js +139 -0
  187. package/dist/lib/components/realtime/realtime-audio-visuals.js.map +1 -0
  188. package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts +29 -0
  189. package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts.map +1 -0
  190. package/dist/lib/components/realtime/realtime-channel-strip.component.js +69 -0
  191. package/dist/lib/components/realtime/realtime-channel-strip.component.js.map +1 -0
  192. package/dist/lib/components/realtime/realtime-composer.component.d.ts +65 -0
  193. package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -0
  194. package/dist/lib/components/realtime/realtime-composer.component.js +256 -0
  195. package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -0
  196. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +71 -0
  197. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -0
  198. package/dist/lib/components/realtime/realtime-delegation-card.component.js +324 -0
  199. package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -0
  200. package/dist/lib/components/realtime/realtime-disclosure.d.ts +135 -0
  201. package/dist/lib/components/realtime/realtime-disclosure.d.ts.map +1 -0
  202. package/dist/lib/components/realtime/realtime-disclosure.js +188 -0
  203. package/dist/lib/components/realtime/realtime-disclosure.js.map +1 -0
  204. package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +491 -0
  205. package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -0
  206. package/dist/lib/components/realtime/realtime-session-overlay.component.js +1274 -0
  207. package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -0
  208. package/dist/lib/components/realtime/realtime-session-state.d.ts +191 -0
  209. package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -0
  210. package/dist/lib/components/realtime/realtime-session-state.js +244 -0
  211. package/dist/lib/components/realtime/realtime-session-state.js.map +1 -0
  212. package/dist/lib/components/realtime/realtime-session-thread.component.d.ts +56 -0
  213. package/dist/lib/components/realtime/realtime-session-thread.component.d.ts.map +1 -0
  214. package/dist/lib/components/realtime/realtime-session-thread.component.js +246 -0
  215. package/dist/lib/components/realtime/realtime-session-thread.component.js.map +1 -0
  216. package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts +51 -0
  217. package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts.map +1 -0
  218. package/dist/lib/components/realtime/realtime-session-timeline-card.component.js +193 -0
  219. package/dist/lib/components/realtime/realtime-session-timeline-card.component.js.map +1 -0
  220. package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts +77 -0
  221. package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts.map +1 -0
  222. package/dist/lib/components/realtime/realtime-surface-panel-prefs.js +114 -0
  223. package/dist/lib/components/realtime/realtime-surface-panel-prefs.js.map +1 -0
  224. package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +173 -0
  225. package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -0
  226. package/dist/lib/components/realtime/realtime-surface-tabs.component.js +496 -0
  227. package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -0
  228. package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +181 -0
  229. package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -0
  230. package/dist/lib/components/realtime/realtime-surface-tabs.model.js +223 -0
  231. package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -0
  232. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts +163 -0
  233. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts.map +1 -0
  234. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js +309 -0
  235. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js.map +1 -0
  236. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +168 -0
  237. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -0
  238. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +524 -0
  239. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -0
  240. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts +346 -0
  241. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts.map +1 -0
  242. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js +851 -0
  243. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js.map +1 -0
  244. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts +86 -0
  245. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts.map +1 -0
  246. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js +210 -0
  247. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js.map +1 -0
  248. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts +48 -0
  249. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts.map +1 -0
  250. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js +180 -0
  251. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js.map +1 -0
  252. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +119 -0
  253. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -0
  254. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +274 -0
  255. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -0
  256. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts +11 -0
  257. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts.map +1 -0
  258. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js +98 -0
  259. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js.map +1 -0
  260. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts +9 -0
  261. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts.map +1 -0
  262. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js +35 -0
  263. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js.map +1 -0
  264. package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts +28 -0
  265. package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts.map +1 -0
  266. package/dist/lib/components/slots/mj-chat-empty-state-default.component.js +104 -0
  267. package/dist/lib/components/slots/mj-chat-empty-state-default.component.js.map +1 -0
  268. package/dist/lib/components/slots/mj-chat-header-default.component.d.ts +11 -0
  269. package/dist/lib/components/slots/mj-chat-header-default.component.d.ts.map +1 -0
  270. package/dist/lib/components/slots/mj-chat-header-default.component.js +103 -0
  271. package/dist/lib/components/slots/mj-chat-header-default.component.js.map +1 -0
  272. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts +15 -0
  273. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts.map +1 -0
  274. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js +73 -0
  275. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js.map +1 -0
  276. package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts +9 -0
  277. package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts.map +1 -0
  278. package/dist/lib/components/slots/mj-chat-message-extra-default.component.js +34 -0
  279. package/dist/lib/components/slots/mj-chat-message-extra-default.component.js.map +1 -0
  280. package/dist/lib/components/slots/slot-interfaces.d.ts +95 -0
  281. package/dist/lib/components/slots/slot-interfaces.d.ts.map +1 -0
  282. package/dist/lib/components/slots/slot-interfaces.js +18 -0
  283. package/dist/lib/components/slots/slot-interfaces.js.map +1 -0
  284. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +11 -0
  285. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  286. package/dist/lib/components/workspace/conversation-workspace.component.js +28 -4
  287. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  288. package/dist/lib/conversations.module.d.ts +12 -1
  289. package/dist/lib/conversations.module.d.ts.map +1 -1
  290. package/dist/lib/conversations.module.js +93 -5
  291. package/dist/lib/conversations.module.js.map +1 -1
  292. package/dist/lib/directives/chat-slot.directive.d.ts +44 -0
  293. package/dist/lib/directives/chat-slot.directive.d.ts.map +1 -0
  294. package/dist/lib/directives/chat-slot.directive.js +54 -0
  295. package/dist/lib/directives/chat-slot.directive.js.map +1 -0
  296. package/dist/lib/events/chat-events.d.ts +137 -0
  297. package/dist/lib/events/chat-events.d.ts.map +1 -0
  298. package/dist/lib/events/chat-events.js +189 -0
  299. package/dist/lib/events/chat-events.js.map +1 -0
  300. package/dist/lib/models/conversation-state.model.d.ts +2 -1
  301. package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
  302. package/dist/lib/models/conversation-state.model.js.map +1 -1
  303. package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
  304. package/dist/lib/services/artifact-state.service.js +23 -6
  305. package/dist/lib/services/artifact-state.service.js.map +1 -1
  306. package/dist/lib/services/conversation-agent.service.d.ts +60 -74
  307. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  308. package/dist/lib/services/conversation-agent.service.js +100 -313
  309. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  310. package/dist/lib/services/conversation-bridge.service.d.ts +11 -70
  311. package/dist/lib/services/conversation-bridge.service.d.ts.map +1 -1
  312. package/dist/lib/services/conversation-bridge.service.js +51 -85
  313. package/dist/lib/services/conversation-bridge.service.js.map +1 -1
  314. package/dist/lib/services/conversation-naming.d.ts +63 -0
  315. package/dist/lib/services/conversation-naming.d.ts.map +1 -0
  316. package/dist/lib/services/conversation-naming.js +58 -0
  317. package/dist/lib/services/conversation-naming.js.map +1 -0
  318. package/dist/lib/services/conversation-streaming.service.d.ts +24 -154
  319. package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
  320. package/dist/lib/services/conversation-streaming.service.js +39 -361
  321. package/dist/lib/services/conversation-streaming.service.js.map +1 -1
  322. package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts +10 -0
  323. package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts.map +1 -0
  324. package/dist/lib/services/conversations-runtime-bootstrap.service.js +104 -0
  325. package/dist/lib/services/conversations-runtime-bootstrap.service.js.map +1 -0
  326. package/dist/lib/services/delegation-result-parser.d.ts +45 -0
  327. package/dist/lib/services/delegation-result-parser.d.ts.map +1 -0
  328. package/dist/lib/services/delegation-result-parser.js +48 -0
  329. package/dist/lib/services/delegation-result-parser.js.map +1 -0
  330. package/dist/lib/services/mention-autocomplete.service.d.ts +19 -4
  331. package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
  332. package/dist/lib/services/mention-autocomplete.service.js +65 -4
  333. package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
  334. package/dist/lib/services/mention-parser.service.d.ts +8 -53
  335. package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
  336. package/dist/lib/services/mention-parser.service.js +32 -243
  337. package/dist/lib/services/mention-parser.service.js.map +1 -1
  338. package/dist/lib/services/narration-template.d.ts +42 -0
  339. package/dist/lib/services/narration-template.d.ts.map +1 -0
  340. package/dist/lib/services/narration-template.js +73 -0
  341. package/dist/lib/services/narration-template.js.map +1 -0
  342. package/dist/lib/services/realtime-pairing.d.ts +120 -0
  343. package/dist/lib/services/realtime-pairing.d.ts.map +1 -0
  344. package/dist/lib/services/realtime-pairing.js +150 -0
  345. package/dist/lib/services/realtime-pairing.js.map +1 -0
  346. package/dist/lib/services/realtime-session-review.service.d.ts +233 -0
  347. package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -0
  348. package/dist/lib/services/realtime-session-review.service.js +417 -0
  349. package/dist/lib/services/realtime-session-review.service.js.map +1 -0
  350. package/dist/lib/services/realtime-session.service.d.ts +739 -0
  351. package/dist/lib/services/realtime-session.service.d.ts.map +1 -0
  352. package/dist/lib/services/realtime-session.service.js +1647 -0
  353. package/dist/lib/services/realtime-session.service.js.map +1 -0
  354. package/dist/lib/services/realtime-sessions-adapter.d.ts +54 -0
  355. package/dist/lib/services/realtime-sessions-adapter.d.ts.map +1 -0
  356. package/dist/lib/services/realtime-sessions-adapter.js +154 -0
  357. package/dist/lib/services/realtime-sessions-adapter.js.map +1 -0
  358. package/dist/lib/services/user-authorization.d.ts +67 -0
  359. package/dist/lib/services/user-authorization.d.ts.map +1 -0
  360. package/dist/lib/services/user-authorization.js +66 -0
  361. package/dist/lib/services/user-authorization.js.map +1 -0
  362. package/dist/lib/utils/realtime-session-timeline.d.ts +84 -0
  363. package/dist/lib/utils/realtime-session-timeline.d.ts.map +1 -0
  364. package/dist/lib/utils/realtime-session-timeline.js +94 -0
  365. package/dist/lib/utils/realtime-session-timeline.js.map +1 -0
  366. package/dist/public-api.d.ts +41 -0
  367. package/dist/public-api.d.ts.map +1 -1
  368. package/dist/public-api.js +50 -0
  369. package/dist/public-api.js.map +1 -1
  370. package/package.json +27 -24
  371. package/dist/__tests__/conversation-bridge.service.test.d.ts +0 -2
  372. package/dist/__tests__/conversation-bridge.service.test.d.ts.map +0 -1
  373. package/dist/__tests__/conversation-bridge.service.test.js +0 -98
  374. package/dist/__tests__/conversation-bridge.service.test.js.map +0 -1
  375. package/dist/__tests__/mention-parser.test.d.ts +0 -2
  376. package/dist/__tests__/mention-parser.test.d.ts.map +0 -1
  377. package/dist/__tests__/mention-parser.test.js +0 -154
  378. package/dist/__tests__/mention-parser.test.js.map +0 -1
@@ -0,0 +1,1274 @@
1
+ import { Component, ElementRef, EventEmitter, HostListener, Input, Output, ChangeDetectorRef, NgZone, ViewChild, inject } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { UserInfoEngine } from '@memberjunction/core-entities';
4
+ import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
5
+ import { RealtimeSessionService } from '../../services/realtime-session.service';
6
+ import { BuildReviewThreadItems } from '../../services/realtime-session-review.service';
7
+ import { RealtimeSessionState } from './realtime-session-state';
8
+ import { RealtimeAgentBannerComponent } from './realtime-agent-banner.component';
9
+ import { RealtimeSessionThreadComponent } from './realtime-session-thread.component';
10
+ import { RealtimeChannelStripComponent } from './realtime-channel-strip.component';
11
+ import { RealtimeComposerComponent } from './realtime-composer.component';
12
+ import { RealtimeSurfaceTabsComponent } from './realtime-surface-tabs.component';
13
+ 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
+ import { RealtimeDisclosureModel, SerializeUxMilestones, REALTIME_UX_PREF_KEY } from './realtime-disclosure';
15
+ import { RealtimeAudioVisualSmoother } from './realtime-audio-visuals';
16
+ import { ShouldRemoveReviewWhiteboardTab } from './realtime-surface-tabs.model';
17
+ import { RealtimeWhiteboardBoardComponent, WhiteboardState } from '@memberjunction/ng-whiteboard';
18
+ import * as i0 from "@angular/core";
19
+ import * as i1 from "@memberjunction/ng-shared-generic";
20
+ import * as i2 from "@angular/common";
21
+ const _c0 = ["reviewBoardTpl"];
22
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_4_Template(rf, ctx) { if (rf & 1) {
23
+ i0.ɵɵelementStart(0, "div", 5);
24
+ i0.ɵɵelement(1, "i", 11);
25
+ i0.ɵɵtext(2);
26
+ i0.ɵɵelementEnd();
27
+ } if (rf & 2) {
28
+ const ctx_r1 = i0.ɵɵnextContext(2);
29
+ i0.ɵɵadvance(2);
30
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.State.Narration, " ");
31
+ } }
32
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_6_Template(rf, ctx) { if (rf & 1) {
33
+ i0.ɵɵelementStart(0, "div", 7);
34
+ i0.ɵɵelement(1, "mj-loading", 12);
35
+ i0.ɵɵelementEnd();
36
+ } }
37
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_7_Template(rf, ctx) { if (rf & 1) {
38
+ const _r3 = i0.ɵɵgetCurrentView();
39
+ i0.ɵɵelementStart(0, "div", 8)(1, "div", 13);
40
+ i0.ɵɵelement(2, "span", 14)(3, "span", 15)(4, "span", 16)(5, "div", 17);
41
+ i0.ɵɵelementEnd();
42
+ i0.ɵɵelementStart(6, "div", 18);
43
+ i0.ɵɵelement(7, "span")(8, "span")(9, "span")(10, "span")(11, "span")(12, "span")(13, "span")(14, "span")(15, "span");
44
+ i0.ɵɵelementEnd();
45
+ i0.ɵɵelementStart(16, "div", 19);
46
+ i0.ɵɵtext(17);
47
+ i0.ɵɵelementEnd();
48
+ i0.ɵɵelementStart(18, "div", 20);
49
+ i0.ɵɵtext(19);
50
+ i0.ɵɵelementEnd();
51
+ i0.ɵɵelementStart(20, "button", 21);
52
+ 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", 22);
54
+ i0.ɵɵtext(22, " Show the conversation ");
55
+ i0.ɵɵelementEnd()();
56
+ } if (rf & 2) {
57
+ const state_r4 = i0.ɵɵnextContext();
58
+ const ctx_r1 = i0.ɵɵnextContext();
59
+ i0.ɵɵattribute("data-state", state_r4);
60
+ i0.ɵɵadvance(5);
61
+ i0.ɵɵattribute("data-state", ctx_r1.HeroOrbState(state_r4));
62
+ i0.ɵɵadvance(12);
63
+ i0.ɵɵtextInterpolate(ctx_r1.AgentName);
64
+ i0.ɵɵadvance(2);
65
+ i0.ɵɵtextInterpolate(ctx_r1.HeroStateLabel(state_r4));
66
+ } }
67
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_8_Template(rf, ctx) { if (rf & 1) {
68
+ const _r5 = i0.ɵɵgetCurrentView();
69
+ i0.ɵɵelementStart(0, "mj-realtime-session-thread", 23);
70
+ 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
+ i0.ɵɵelementEnd();
72
+ } if (rf & 2) {
73
+ const ctx_r1 = i0.ɵɵnextContext(2);
74
+ i0.ɵɵproperty("State", ctx_r1.State)("AgentName", ctx_r1.AgentName)("UserName", (ctx_r1.CurrentUser == null ? null : ctx_r1.CurrentUser.Name) || "You")("ShowCaptions", ctx_r1.IsReviewing ? true : ctx_r1.ShowCaptions)("DevMode", ctx_r1.DevMode);
75
+ } }
76
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Conditional_0_Template(rf, ctx) { if (rf & 1) {
77
+ i0.ɵɵelement(0, "mj-realtime-channel-strip");
78
+ } }
79
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template(rf, ctx) { if (rf & 1) {
80
+ const _r6 = i0.ɵɵgetCurrentView();
81
+ i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Conditional_0_Template, 1, 0, "mj-realtime-channel-strip");
82
+ i0.ɵɵelementStart(1, "mj-realtime-composer", 24);
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()); });
84
+ i0.ɵɵelementEnd();
85
+ } if (rf & 2) {
86
+ const ctx_r1 = i0.ɵɵnextContext(2);
87
+ i0.ɵɵconditional(ctx_r1.Disclosure.ShowComposer ? 0 : -1);
88
+ i0.ɵɵadvance();
89
+ i0.ɵɵproperty("Open", ctx_r1.ComposerOpen)("CaptionsOn", ctx_r1.ShowCaptions)("ShowDetails", ctx_r1.ShowDetailsControl)("DetailsOn", ctx_r1.DetailsPeek);
90
+ } }
91
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Conditional_0_Template(rf, ctx) { if (rf & 1) {
92
+ const _r8 = i0.ɵɵgetCurrentView();
93
+ i0.ɵɵelementStart(0, "div", 28);
94
+ 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
+ i0.ɵɵelementEnd();
96
+ } if (rf & 2) {
97
+ const ctx_r1 = i0.ɵɵnextContext(3);
98
+ i0.ɵɵclassProp("call-resizer--active", ctx_r1.IsPanelResizing);
99
+ } }
100
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Template(rf, ctx) { if (rf & 1) {
101
+ const _r7 = i0.ɵɵgetCurrentView();
102
+ i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Conditional_0_Template, 1, 2, "div", 25);
103
+ i0.ɵɵelementStart(1, "div", 26)(2, "mj-realtime-surface-tabs", 27);
104
+ 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
+ i0.ɵɵelementEnd()();
106
+ } if (rf & 2) {
107
+ const ctx_r1 = i0.ɵɵnextContext(2);
108
+ i0.ɵɵconditional(!ctx_r1.PanelResizeDisabled ? 0 : -1);
109
+ i0.ɵɵadvance();
110
+ i0.ɵɵstyleProp("width", ctx_r1.ChannelFocusMode ? null : ctx_r1.PanelAreaSize, "px");
111
+ 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);
113
+ } }
114
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Conditional_5_Template(rf, ctx) { if (rf & 1) {
115
+ i0.ɵɵelementStart(0, "span", 32);
116
+ i0.ɵɵtext(1);
117
+ i0.ɵɵelementEnd();
118
+ } if (rf & 2) {
119
+ i0.ɵɵadvance();
120
+ i0.ɵɵtextInterpolate(ctx);
121
+ } }
122
+ function RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template(rf, ctx) { if (rf & 1) {
123
+ const _r9 = i0.ɵɵgetCurrentView();
124
+ i0.ɵɵelementStart(0, "div", 10);
125
+ i0.ɵɵelement(1, "span", 29);
126
+ i0.ɵɵelementStart(2, "span", 30)(3, "span", 31);
127
+ i0.ɵɵtext(4);
128
+ i0.ɵɵelementEnd();
129
+ i0.ɵɵconditionalCreate(5, RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Conditional_5_Template, 2, 1, "span", 32);
130
+ i0.ɵɵelementEnd();
131
+ i0.ɵɵelementStart(6, "button", 33);
132
+ 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", 34);
134
+ i0.ɵɵelementEnd();
135
+ i0.ɵɵelementStart(8, "button", 35);
136
+ 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", 36);
138
+ i0.ɵɵelementEnd();
139
+ i0.ɵɵelementStart(10, "button", 37);
140
+ 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", 38);
142
+ i0.ɵɵelementEnd()();
143
+ } if (rf & 2) {
144
+ let tmp_6_0;
145
+ const state_r4 = i0.ɵɵnextContext();
146
+ const ctx_r1 = i0.ɵɵnextContext();
147
+ i0.ɵɵadvance();
148
+ i0.ɵɵattribute("data-state", state_r4);
149
+ i0.ɵɵadvance(3);
150
+ i0.ɵɵtextInterpolate1("Voice for ", ctx_r1.AgentName);
151
+ i0.ɵɵadvance();
152
+ i0.ɵɵconditional((tmp_6_0 = ctx_r1.FocusStateLabel(state_r4)) ? 5 : -1, tmp_6_0);
153
+ i0.ɵɵadvance();
154
+ i0.ɵɵclassProp("board-focus-pill__btn--active", ctx_r1.FocusPillMuted);
155
+ i0.ɵɵproperty("title", ctx_r1.FocusPillMuted ? "Unmute" : "Mute");
156
+ i0.ɵɵattribute("aria-label", ctx_r1.FocusPillMuted ? "Unmute microphone" : "Mute microphone");
157
+ i0.ɵɵadvance();
158
+ i0.ɵɵclassProp("fa-microphone", !ctx_r1.FocusPillMuted)("fa-microphone-slash", ctx_r1.FocusPillMuted);
159
+ } }
160
+ function RealtimeSessionOverlayComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
161
+ const _r1 = i0.ɵɵgetCurrentView();
162
+ i0.ɵɵelementStart(0, "div", 2)(1, "div", 3)(2, "mj-realtime-agent-banner", 4);
163
+ 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)); })("CaptionsToggled", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_CaptionsToggled_2_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnCaptionsToggled($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()); })("PureAudioRequested", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_PureAudioRequested_2_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnPureAudio()); })("EndRequested", function RealtimeSessionOverlayComponent_Conditional_0_Template_mj_realtime_agent_banner_EndRequested_2_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnEndCall()); })("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
+ i0.ɵɵelementEnd();
166
+ i0.ɵɵconditionalCreate(4, RealtimeSessionOverlayComponent_Conditional_0_Conditional_4_Template, 3, 1, "div", 5);
167
+ i0.ɵɵelementStart(5, "div", 6);
168
+ i0.ɵɵconditionalCreate(6, RealtimeSessionOverlayComponent_Conditional_0_Conditional_6_Template, 2, 0, "div", 7)(7, RealtimeSessionOverlayComponent_Conditional_0_Conditional_7_Template, 23, 4, "div", 8)(8, RealtimeSessionOverlayComponent_Conditional_0_Conditional_8_Template, 1, 5, "mj-realtime-session-thread", 9);
169
+ i0.ɵɵelementEnd();
170
+ i0.ɵɵconditionalCreate(9, RealtimeSessionOverlayComponent_Conditional_0_Conditional_9_Template, 2, 5);
171
+ i0.ɵɵelementEnd();
172
+ i0.ɵɵconditionalCreate(10, RealtimeSessionOverlayComponent_Conditional_0_Conditional_10_Template, 3, 8);
173
+ i0.ɵɵconditionalCreate(11, RealtimeSessionOverlayComponent_Conditional_0_Conditional_11_Template, 12, 11, "div", 10);
174
+ i0.ɵɵelementEnd();
175
+ } if (rf & 2) {
176
+ const state_r4 = ctx;
177
+ const ctx_r1 = i0.ɵɵnextContext();
178
+ i0.ɵɵclassProp("call-overlay--hidden", ctx_r1.Hidden)("call-overlay--review", ctx_r1.IsReviewing)("board-focus", ctx_r1.ChannelFocusMode);
179
+ i0.ɵɵattribute("aria-label", ctx_r1.IsReviewing ? "Past session review" : "Live voice session");
180
+ i0.ɵɵadvance(2);
181
+ i0.ɵɵproperty("State", state_r4)("AgentName", ctx_r1.AgentName)("ModelName", ctx_r1.IsReviewing ? null : i0.ɵɵpipeBind1(3, 30, 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)("CaptionsOn", ctx_r1.ShowCaptions)("ShowCaptionsControl", !ctx_r1.IsReviewing)("ShowGear", !ctx_r1.IsReviewing && ctx_r1.Disclosure.ShowGear)("ShowEnd", !ctx_r1.IsReviewing && ctx_r1.Disclosure.ShowComposer)("ShowMinimize", !ctx_r1.IsReviewing)("ShowPureAudio", !ctx_r1.IsReviewing && ctx_r1.Disclosure.SessionLevel > 0)("Density", ctx_r1.Disclosure.Milestones.Density);
182
+ i0.ɵɵadvance(2);
183
+ i0.ɵɵconditional(!ctx_r1.IsReviewing && ctx_r1.State.Narration && !ctx_r1.State.ActiveCallId && ctx_r1.Disclosure.ShowThread ? 4 : -1);
184
+ i0.ɵɵadvance();
185
+ i0.ɵɵclassProp("call-body--hero", ctx_r1.ShowHero && state_r4 !== "connecting");
186
+ i0.ɵɵadvance();
187
+ i0.ɵɵconditional(!ctx_r1.IsReviewing && state_r4 === "connecting" ? 6 : ctx_r1.ShowHero ? 7 : 8);
188
+ i0.ɵɵadvance(3);
189
+ i0.ɵɵconditional(!ctx_r1.IsReviewing ? 9 : -1);
190
+ i0.ɵɵadvance();
191
+ i0.ɵɵconditional(ctx_r1.ShowPanelArea ? 10 : -1);
192
+ i0.ɵɵadvance();
193
+ i0.ɵɵconditional(!ctx_r1.IsReviewing && ctx_r1.ChannelFocusMode ? 11 : -1);
194
+ } }
195
+ function RealtimeSessionOverlayComponent_ng_template_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
196
+ i0.ɵɵelementStart(0, "div", 39);
197
+ i0.ɵɵelement(1, "mj-realtime-whiteboard", 40);
198
+ i0.ɵɵelementEnd();
199
+ } if (rf & 2) {
200
+ const ctx_r1 = i0.ɵɵnextContext(2);
201
+ i0.ɵɵadvance();
202
+ i0.ɵɵproperty("State", ctx)("AgentName", ctx_r1.AgentName)("ReadOnly", true);
203
+ } }
204
+ 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", 39);
206
+ } if (rf & 2) {
207
+ let tmp_2_0;
208
+ const ctx_r1 = i0.ɵɵnextContext();
209
+ i0.ɵɵconditional((tmp_2_0 = ctx_r1.ReviewWhiteboard) ? 0 : -1, tmp_2_0);
210
+ } }
211
+ /**
212
+ * The "call mode" overlay for a live real-time voice session. Hosted by the
213
+ * conversation chat area (`<mj-conversation-chat-area>`) behind `Active$`, it fills the
214
+ * conversation panel IN PLACE (`position:absolute; inset:0` over the panel — not a
215
+ * fixed app-wide dialog), replacing the conversation view including the composer.
216
+ *
217
+ * Two-column layout:
218
+ * - MAIN column — {@link RealtimeAgentBannerComponent} (the unified APP-BAR: identity +
219
+ * turn-state + the disclosure-gated action cluster), the unified
220
+ * {@link RealtimeSessionThreadComponent} (or the level-0 pure-audio hero), the channel
221
+ * strip, and {@link RealtimeComposerComponent} (the bottom dock: phone-call strip ⇄
222
+ * fused minis+composer).
223
+ * - RIGHT PANEL — {@link RealtimeSurfaceTabsComponent}: the TABBED surface panel.
224
+ * Tab 1 "Activity" hosts the session activity rail; one tab opens per ARTIFACT a
225
+ * delegated run produces (auto-focused + flashed on arrival, viewed read-only via the
226
+ * standard artifact viewer); ONE TAB PER INTERACTIVE CHANNEL the session resolved from
227
+ * the `MJ: AI Agent Channels` registry. Collapsible to a slim strip at the panel level.
228
+ *
229
+ * INTERACTIVE CHANNELS ARE PLUGINS — this shell is channel-agnostic. It subscribes
230
+ * {@link RealtimeSessionService.ActiveChannels$} and registers one surface tab per
231
+ * {@link BaseRealtimeChannelClient} (key/title/icon from the plugin); the tab pane creates
232
+ * the plugin's surface component dynamically and the PLUGIN wires its own inputs/outputs.
233
+ * The only channel-generic affordance the shell owns is the FOCUS layout: any channel may
234
+ * request it (via its context's `SetFocusMode` → {@link RealtimeSessionService.ChannelFocus$}),
235
+ * which collapses the main call column (`.board-focus`) and shows the floating call pill.
236
+ *
237
+ * Owns the shared {@link RealtimeSessionState} — the SINGLE merge of the service's
238
+ * caption/delegation/narration streams — and passes it to both thread and rail via
239
+ * inputs, so neither duplicates subscription logic.
240
+ *
241
+ * DEVELOPER MODE: the controls row's gear toggles {@link DevMode} (per-session, off by
242
+ * default, never persisted), revealing "Open run" links on delegation cards / rail items
243
+ * and an "Open session" link in the banner. Clicking one emits {@link NavigateRequest}
244
+ * and MINIMIZES the call (via {@link RealtimeSessionService.SetMinimized}) — the session
245
+ * stays live while the host navigates to the record.
246
+ *
247
+ * SESSION REVIEW MODE: when the host supplies {@link ReviewData} (a loaded
248
+ * `RealtimeSessionReview`) and NO live session is active, the overlay renders what went
249
+ * down in that PAST session instead of a live call: the banner shows a "Session review"
250
+ * badge + lifecycle range + close-reason chip; the SAME thread/rail components render the
251
+ * historical caption turns and delegated-run cards (via
252
+ * {@link RealtimeSessionState.LoadHistoricalItems}); a read-only Whiteboard tab is
253
+ * registered ONLY when the session saved a parseable Whiteboard channel state. Everything
254
+ * live is DEAD in review — no mic, no captions stream, no composer, no channel strip; the
255
+ * controls collapse to a single "Start live session" button ({@link StartLiveRequested},
256
+ * which resumes by chaining `lastSessionId`) and a Close button ({@link ReviewClosed}).
257
+ */
258
+ export class RealtimeSessionOverlayComponent {
259
+ _agentName = 'Sage';
260
+ /**
261
+ * True while the call is MINIMIZED: the overlay hides itself (CSS, not destruction) so
262
+ * the host can show its floating "on call" pill WITHOUT losing this shell's merged
263
+ * session state (delegation cards ride non-replaying streams) or view state (dev mode,
264
+ * expanded chips, scroll position). The host binds this from `Minimized$`.
265
+ */
266
+ Hidden = false;
267
+ /** Display name of the agent the voice session fronts (e.g. "Sage"). */
268
+ set AgentName(value) {
269
+ this._agentName = value || 'Sage';
270
+ this.State.AgentName = this._agentName;
271
+ }
272
+ get AgentName() {
273
+ return this._agentName;
274
+ }
275
+ /** The signed-in user, threaded to the surface panel's artifact viewer. */
276
+ CurrentUser = null;
277
+ /** The active environment id, threaded to the surface panel's artifact viewer. */
278
+ EnvironmentID = '';
279
+ /**
280
+ * SESSION REVIEW data: when set (and no live session is active) the overlay renders
281
+ * the reviewed past session instead of a live call. Setting it populates the shared
282
+ * {@link State} with the historical thread and prepares the read-only whiteboard tab;
283
+ * clearing it returns the state to its live-merge baseline.
284
+ */
285
+ set ReviewData(value) {
286
+ if (value === this._reviewData) {
287
+ return;
288
+ }
289
+ this._reviewData = value;
290
+ if (value && !this.voice.IsActive) {
291
+ this.enterReview(value);
292
+ }
293
+ else if (!value) {
294
+ this.exitReview();
295
+ }
296
+ }
297
+ get ReviewData() {
298
+ return this._reviewData;
299
+ }
300
+ /** Emitted after the call ends so the host can react (visibility is driven by Active$). */
301
+ Ended = new EventEmitter();
302
+ /**
303
+ * Emitted when a gear-gated developer link asks to open an entity record. The host is
304
+ * expected to bubble this through its standard record-open path (the chat area re-emits
305
+ * it on `openEntityRecord`, which Explorer routes via `NavigationService`). The overlay
306
+ * minimizes itself first so the live call survives the navigation.
307
+ */
308
+ NavigateRequest = new EventEmitter();
309
+ /**
310
+ * Review mode's "Start live session": the host clears its review state and resumes the
311
+ * reviewed session as a new live call (chaining `LastSessionId` to restore channel states).
312
+ */
313
+ StartLiveRequested = new EventEmitter();
314
+ /** Review mode's Close: the host clears its review state, returning to the conversation. */
315
+ ReviewClosed = new EventEmitter();
316
+ voice = inject(RealtimeSessionService);
317
+ cdr = inject(ChangeDetectorRef);
318
+ _reviewData = null;
319
+ /**
320
+ * Set by {@link OnStartLive} just before emitting {@link StartLiveRequested}, so the
321
+ * ReviewData→null transition that follows is recognized as a REVIEW→LIVE CONTINUATION
322
+ * (keep the historical thread + append the "Resumed live session" divider) rather than
323
+ * a plain review close (clear everything).
324
+ */
325
+ pendingLiveContinuation = false;
326
+ /** The reviewed chain's history artifacts, registered as unfocused surface tabs. */
327
+ reviewArtifacts = [];
328
+ /**
329
+ * True while the surface panel carries the REVIEW-registered (template-based, read-only)
330
+ * Whiteboard tab. Drives the review→live continuation edge: when the resumed live
331
+ * session's channel set resolves WITHOUT a Whiteboard channel, the stale review tab is
332
+ * removed (see {@link cleanupStaleReviewBoardTab}); when it resolves WITH one, the live
333
+ * plugin re-registers the same key and the tab upgrades in place.
334
+ */
335
+ reviewWhiteboardTabRegistered = false;
336
+ /** True while the overlay renders a PAST session (review data set, no live call). */
337
+ get IsReviewing() {
338
+ return this._reviewData !== null && !this.voice.IsActive;
339
+ }
340
+ /**
341
+ * The reviewed session's rehydrated whiteboard, when it saved a parseable Whiteboard
342
+ * channel state — rendered read-only by the review whiteboard tab. Null = no tab.
343
+ */
344
+ ReviewWhiteboard = null;
345
+ /** Shared session state — single source for the thread AND the activity rail. */
346
+ State = new RealtimeSessionState();
347
+ /**
348
+ * PROGRESSIVE DISCLOSURE: the levels/milestones model behind the pure-audio-first UX
349
+ * (see {@link RealtimeDisclosureModel}). Loaded from UserInfoEngine at construction;
350
+ * content events ({@link onSessionStateChanged}, {@link onChannelActivity}) raise the
351
+ * volatile session level; milestones ratchet + persist when the call ends. REVIEW mode
352
+ * bypasses disclosure entirely — a past session always renders the full console.
353
+ */
354
+ Disclosure = new RealtimeDisclosureModel();
355
+ /**
356
+ * The strip's Details peek: shows the surface panel ON DEMAND while it isn't earned yet
357
+ * (disclosure level < 2) — Activity and channel surfaces exist before their content
358
+ * does. Cleared automatically once content earns the panel for real.
359
+ */
360
+ DetailsPeek = false;
361
+ /**
362
+ * Whether the typed-input dock is open — a TWO-WAY user door (the strip's Type
363
+ * control / the T hotkey open it; the dock's hide control closes it), volatile and
364
+ * reset per session. Typing never becomes permanent chrome.
365
+ */
366
+ ComposerOpen = false;
367
+ /** Live turn-state from the session service — drives the banner + connecting screen. */
368
+ ConnectionState$ = this.voice.ConnectionState$;
369
+ /** Server-reported realtime model name for the active session — shown subtly in the banner. */
370
+ ModelName$ = this.voice.ModelName$;
371
+ /**
372
+ * Whether the conversation renders as TEXT (the thread) or stays voice-first (the
373
+ * orb hero). A PERSISTED per-user preference (`mj.realtimeVoice.captions.v1`) —
374
+ * default OFF: voice-first, the orb owns the screen until the user opts into text.
375
+ */
376
+ ShowCaptions = false;
377
+ /** UserInfoEngine key for the persisted captions (text-vs-orb) preference. */
378
+ static CaptionsPrefKey = 'mj.realtimeVoice.captions.v1';
379
+ /**
380
+ * Whether developer affordances (open-record links) are revealed. Per-session view
381
+ * state on this shell — off by default, reset with the overlay, never persisted.
382
+ */
383
+ DevMode = false;
384
+ /** ID of the live server-side agent session record, for the banner's dev link. */
385
+ get SessionID() {
386
+ return this.voice.CurrentAgentSessionId;
387
+ }
388
+ /**
389
+ * The tabbed surface panel (right panel) — channel registrations are forwarded to it.
390
+ * A SETTER because the panel is disclosure-gated (`@if`): it can be created LATE (the
391
+ * first delegation / channel activity reveals it mid-call), at which point any queued
392
+ * channel registrations and a pending auto-reveal must flush to the fresh instance.
393
+ */
394
+ surfaceTabs;
395
+ set surfaceTabsRef(ref) {
396
+ this.surfaceTabs = ref;
397
+ if (ref) {
398
+ // A (re)created panel starts with a FRESH tab model: artifact tabs self-recover
399
+ // (the panel re-scans the session state) but CHANNEL tabs only registered when
400
+ // ActiveChannels$ emitted at session start — re-register the live set here so
401
+ // hiding the panel (pure-audio return, Details off) never loses the Whiteboard.
402
+ this.registerChannelTabs([...this.voice.ActiveChannels]);
403
+ this.flushPendingChannelTabs();
404
+ const reveal = this.pendingRevealKey;
405
+ this.pendingRevealKey = null;
406
+ // Deferred: focus/expand feed the parent's width bindings (wide tier) — never
407
+ // mutate those inside the change-detection pass that created the panel.
408
+ setTimeout(() => {
409
+ if (reveal) {
410
+ // The agent's first channel activity caused this creation — land ON the board.
411
+ ref.RevealChannel(reveal);
412
+ }
413
+ else {
414
+ // Default for a fresh panel (live AND review): the marquee surface — channels
415
+ // lead the strip — NOT the Activity rail; agent-run plumbing is opt-in only.
416
+ // (Review channel tabs register a beat later and take focus themselves.)
417
+ ref.FocusFirstTab();
418
+ }
419
+ });
420
+ }
421
+ }
422
+ /** The bottom dock — the T-to-type hotkey focuses its input. */
423
+ composer;
424
+ /** Channel keys already auto-revealed this session (first activity only). */
425
+ revealedChannelKeys = new Set();
426
+ /** Auto-reveal that arrived before the (disclosure-gated) panel rendered. */
427
+ pendingRevealKey = null;
428
+ /** Previous Active$ value — edges drive disclosure session begin/ratchet. */
429
+ prevActive = false;
430
+ /** Template hosting the read-only review whiteboard (root-level, so always resolvable). */
431
+ reviewBoardTpl;
432
+ /** Channel registrations received before the surface panel rendered (flushed in ngAfterViewInit). */
433
+ pendingChannelTabs = [];
434
+ /** True once the view (and the review-board template ref) exists. */
435
+ viewReady = false;
436
+ // ── Channel FOCUS layout (channel-generic — any plugin may request it) ─────
437
+ /**
438
+ * True while a channel surface is in FOCUS mode: the main call column collapses
439
+ * (`.board-focus` on the overlay) and a compact floating call pill (orb + state +
440
+ * mute / show-thread / end) rides over the surface.
441
+ */
442
+ ChannelFocusMode = false;
443
+ /** Mic-muted state reflected on the focus pill's mute button. */
444
+ FocusPillMuted = false;
445
+ /** The channel currently holding the focus layout (the pill's exit routes back to it). */
446
+ focusChannel = null;
447
+ subs = [];
448
+ constructor() {
449
+ this.loadPanelWidthPref();
450
+ this.loadDisclosurePref();
451
+ this.loadCaptionsPref();
452
+ this.State.Attach(this.voice);
453
+ this.subs.push(
454
+ // Re-render on merged-state changes; content arrival raises the disclosure level.
455
+ this.State.Changed$.subscribe(() => this.onSessionStateChanged()), this.Disclosure.Changed$.subscribe(() => this.cdr.markForCheck()),
456
+ // One surface tab per registry-resolved channel plugin (replays the current set).
457
+ this.voice.ActiveChannels$.subscribe(channels => this.registerChannelTabs(channels)),
458
+ // Any channel may request the focus layout through its host context.
459
+ this.voice.ChannelFocus$.subscribe(event => this.onChannelFocus(event.Channel, event.Focused)),
460
+ // The agent ACTED on a channel — auto-reveal its surface tab on first activity.
461
+ this.voice.ChannelActivity$.subscribe(plugin => this.onChannelActivity(plugin)),
462
+ // Live/idle flips: reset/ratchet disclosure + re-evaluate the review-vs-live branch.
463
+ this.voice.Active$.subscribe(active => this.onActiveChanged(active)));
464
+ }
465
+ ngAfterViewInit() {
466
+ this.viewReady = true;
467
+ this.flushPendingChannelTabs();
468
+ this.registerReviewBoardTab();
469
+ this.registerReviewArtifactTabs();
470
+ }
471
+ // ── Surface-panel sizing (flex layout + pointer-drag handle; width persisted per-user) ──
472
+ //
473
+ // The panel width is a PLAIN FIELD rendered via [style.width.px] — there is no split
474
+ // library with its own internal size state to fight. The resize handle uses the same
475
+ // mechanics as ui-components' slide-panel (`MjSlidePanelComponent`): mousedown →
476
+ // document mousemove/mouseup registered OUTSIDE Angular, live clamp while dragging,
477
+ // adopt + persist on release. A bare CLICK cannot move the panel by construction
478
+ // (width follows the pointer delta) and the click-vs-drag guard keeps it from being
479
+ // adopted or persisted.
480
+ /** Whether the surface panel is collapsed to its slim strip (reported by the panel). */
481
+ PanelCollapsed = false;
482
+ /** Wide tier active (a content tab is focused) — drives the DEFAULT width only. */
483
+ PanelWide = false;
484
+ /** The user's explicit dragged width (persisted); null = follow the default tiers. */
485
+ userPanelWidth = null;
486
+ /** Current expanded panel width in px (rendered as the panel's inline width). */
487
+ PanelWidthPx = SURFACE_PANEL_DEFAULT_WIDTH;
488
+ /** True while the resize handle is mid-drag (brand-tints the handle). */
489
+ IsPanelResizing = false;
490
+ panelResizeStartX = 0;
491
+ panelResizeStartWidth = 0;
492
+ boundPanelResizeMove = this.onPanelResizeMove.bind(this);
493
+ boundPanelResizeEnd = this.onPanelResizeEnd.bind(this);
494
+ hostRef = inject(ElementRef);
495
+ ngZone = inject(NgZone);
496
+ /** The panel's rendered width: slim strip when collapsed, otherwise the current width. */
497
+ get PanelAreaSize() {
498
+ return this.PanelCollapsed ? SURFACE_PANEL_COLLAPSED_WIDTH : this.PanelWidthPx;
499
+ }
500
+ /** Resizing is meaningless while collapsed or when the surface fills (focus mode). */
501
+ get PanelResizeDisabled() {
502
+ return this.PanelCollapsed || this.ChannelFocusMode;
503
+ }
504
+ OnPanelCollapsedChange(collapsed) {
505
+ this.PanelCollapsed = collapsed;
506
+ this.cdr.markForCheck();
507
+ }
508
+ /** Wide-tier flips only move the DEFAULT width — an explicit user width always wins. */
509
+ OnPanelWideChanged(wide) {
510
+ this.PanelWide = wide;
511
+ if (this.userPanelWidth === null) {
512
+ this.PanelWidthPx = DefaultSurfacePanelWidth(wide, this.hostWidth());
513
+ }
514
+ this.cdr.markForCheck();
515
+ }
516
+ /** Mousedown on the resize handle: capture the start state and track the pointer document-wide. */
517
+ OnPanelResizeStart(event) {
518
+ if (this.PanelResizeDisabled) {
519
+ return;
520
+ }
521
+ event.preventDefault();
522
+ this.IsPanelResizing = true;
523
+ this.panelResizeStartX = event.clientX;
524
+ this.panelResizeStartWidth = this.PanelWidthPx;
525
+ document.body.style.cursor = 'col-resize';
526
+ document.body.style.userSelect = 'none';
527
+ this.ngZone.runOutsideAngular(() => {
528
+ document.addEventListener('mousemove', this.boundPanelResizeMove);
529
+ document.addEventListener('mouseup', this.boundPanelResizeEnd);
530
+ });
531
+ this.cdr.markForCheck();
532
+ }
533
+ /** Live drag: panel width follows the pointer delta, clamped to [min, 70% of the overlay]. */
534
+ onPanelResizeMove(event) {
535
+ if (!this.IsPanelResizing) {
536
+ return;
537
+ }
538
+ this.PanelWidthPx = SurfacePanelDragWidth(this.panelResizeStartWidth, this.panelResizeStartX, event.clientX, this.hostWidth());
539
+ this.ngZone.run(() => this.cdr.markForCheck());
540
+ }
541
+ /**
542
+ * Release: a genuine DRAG adopts the width as the user's explicit preference and
543
+ * persists it (debounced); a bare CLICK (movement under the tolerance) restores the
544
+ * start width and persists nothing — the handle never snaps on a click.
545
+ */
546
+ onPanelResizeEnd(event) {
547
+ if (!this.IsPanelResizing) {
548
+ return;
549
+ }
550
+ this.teardownPanelResize();
551
+ if (IsSurfacePanelDrag(this.panelResizeStartX, event.clientX)) {
552
+ this.userPanelWidth = this.PanelWidthPx;
553
+ this.persistPanelWidth(this.PanelWidthPx);
554
+ }
555
+ else {
556
+ this.PanelWidthPx = this.panelResizeStartWidth;
557
+ }
558
+ this.ngZone.run(() => {
559
+ this.IsPanelResizing = false;
560
+ this.cdr.markForCheck();
561
+ });
562
+ }
563
+ /** Double-click the handle: back to the default tier width; persist the reset. */
564
+ OnPanelResizeReset() {
565
+ this.userPanelWidth = null;
566
+ this.PanelWidthPx = DefaultSurfacePanelWidth(this.PanelWide, this.hostWidth());
567
+ this.persistPanelWidth(null);
568
+ this.cdr.markForCheck();
569
+ }
570
+ /** Removes the document-wide drag listeners and restores the body cursor/selection. */
571
+ teardownPanelResize() {
572
+ document.removeEventListener('mousemove', this.boundPanelResizeMove);
573
+ document.removeEventListener('mouseup', this.boundPanelResizeEnd);
574
+ document.body.style.cursor = '';
575
+ document.body.style.userSelect = '';
576
+ }
577
+ /**
578
+ * The overlay's measurable width for clamping. The component host is
579
+ * `display: contents` (zero rect) — measure the rendered `.call-overlay` div instead.
580
+ */
581
+ hostWidth() {
582
+ const host = this.hostRef.nativeElement;
583
+ const el = host.firstElementChild ?? host;
584
+ return el.getBoundingClientRect ? el.getBoundingClientRect().width : 0;
585
+ }
586
+ /** Reads the persisted width once per overlay instance (no-op when the engine isn't configured). */
587
+ loadPanelWidthPref() {
588
+ try {
589
+ const pref = ParseSurfacePanelPref(UserInfoEngine.Instance.GetSetting(SURFACE_PANEL_PREF_KEY));
590
+ if (pref) {
591
+ this.userPanelWidth = pref.Width;
592
+ // hostWidth() is 0 before first layout — the clamp then enforces only the
593
+ // minimum, and the 70% cap re-applies on the next real drag.
594
+ this.PanelWidthPx = ClampSurfacePanelWidth(pref.Width, this.hostWidth());
595
+ this.cdr.markForCheck();
596
+ }
597
+ }
598
+ catch {
599
+ // UserInfoEngine not configured (plain-node tests / early bootstrap) — default tiers apply.
600
+ }
601
+ }
602
+ /** Persists the width preference server-side (debounced); reset serializes {"width":null}. */
603
+ persistPanelWidth(width) {
604
+ try {
605
+ UserInfoEngine.Instance.SetSettingDebounced(SURFACE_PANEL_PREF_KEY, SerializeSurfacePanelPref(width));
606
+ }
607
+ catch {
608
+ // engine unavailable — width still applies for this session
609
+ }
610
+ }
611
+ // ── Progressive disclosure (the console that grows with you) ────────────────
612
+ /**
613
+ * True while the PURE-AUDIO hero (the orb) owns the main column. The captions
614
+ * preference IS the text-vs-orb switch: captions off (the voice-first default) shows
615
+ * the breathing orb at ANY disclosure level; captions on shows the thread. Review
616
+ * always shows the thread.
617
+ */
618
+ get ShowHero() {
619
+ return !this.IsReviewing && !this.ShowCaptions;
620
+ }
621
+ /**
622
+ * Whether the surface-panel area renders. The tabs have their OWN control (the
623
+ * Details toggle, plus the board auto-reveal and artifact-view requests) — they are
624
+ * deliberately DECOUPLED from typing/disclosure levels: revealing the composer must
625
+ * never surprise the user with the whole tab panel.
626
+ */
627
+ get ShowPanelArea() {
628
+ return this.IsReviewing || this.DetailsPeek;
629
+ }
630
+ /** The Details (tabs) toggle is always offered while live — it's the panel's one door. */
631
+ get ShowDetailsControl() {
632
+ return !this.IsReviewing;
633
+ }
634
+ /** Reads the persisted disclosure milestones (tolerant; defaults to day one). */
635
+ loadDisclosurePref() {
636
+ try {
637
+ this.Disclosure.Load(UserInfoEngine.Instance.GetSetting(REALTIME_UX_PREF_KEY));
638
+ }
639
+ catch {
640
+ // UserInfoEngine not configured (plain-node tests / early bootstrap) — day-one defaults.
641
+ this.Disclosure.Load(null);
642
+ }
643
+ }
644
+ /** Reads the persisted text-vs-orb preference (tolerant; default = voice-first OFF). */
645
+ loadCaptionsPref() {
646
+ try {
647
+ this.ShowCaptions = UserInfoEngine.Instance.GetSetting(RealtimeSessionOverlayComponent.CaptionsPrefKey) === 'true';
648
+ }
649
+ catch {
650
+ // UserInfoEngine not configured — voice-first default applies.
651
+ }
652
+ }
653
+ /** Persists the text-vs-orb preference (debounced, best-effort). */
654
+ persistCaptionsPref() {
655
+ try {
656
+ UserInfoEngine.Instance.SetSettingDebounced(RealtimeSessionOverlayComponent.CaptionsPrefKey, String(this.ShowCaptions));
657
+ }
658
+ catch {
659
+ // engine unavailable — the preference still applies for this session
660
+ }
661
+ }
662
+ /** Persists the disclosure milestones server-side (debounced, best-effort). */
663
+ persistDisclosure(serialized) {
664
+ try {
665
+ UserInfoEngine.Instance.SetSettingDebounced(REALTIME_UX_PREF_KEY, serialized);
666
+ }
667
+ catch {
668
+ // engine unavailable — the ratchet still applies for this browser session
669
+ }
670
+ }
671
+ /**
672
+ * Merged-state change: re-render only. DELIBERATELY no disclosure raise — per product
673
+ * direction, content never flips the console open: a running delegation is narrated
674
+ * aloud, a finished artifact lands as a GLOWING (unfocused) tab, and the ONLY thing
675
+ * that auto-reveals is a channel's first agent activity (the whiteboard demands eyes).
676
+ * A pure-audio user stays in pure audio until THEY ask for more.
677
+ */
678
+ onSessionStateChanged() {
679
+ this.cdr.markForCheck();
680
+ }
681
+ /**
682
+ * The agent ACTED on a channel (a tool call routed to its local executor — e.g. the
683
+ * first whiteboard write). THE ONE AUTO-REVEAL: the surface panel opens (as a peek —
684
+ * the left column stays exactly as it was, pure audio included) with the channel's tab
685
+ * focused and flashed, exactly once per channel — so the user discovers the board the
686
+ * moment it comes alive. Disclosure levels are NOT raised: no text, no composer, no
687
+ * extra chrome — just the board.
688
+ */
689
+ onChannelActivity(plugin) {
690
+ if (this.IsReviewing || this.revealedChannelKeys.has(plugin.ChannelName)) {
691
+ return;
692
+ }
693
+ this.revealedChannelKeys.add(plugin.ChannelName);
694
+ this.DetailsPeek = true; // the panel shows via the same on-demand mechanism Details uses
695
+ if (this.surfaceTabs) {
696
+ // 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.
698
+ this.surfaceTabs.RevealChannel(plugin.ChannelName);
699
+ }
700
+ else {
701
+ // Panel not rendered yet (the peek just created it) — reveal once it exists.
702
+ this.pendingRevealKey = plugin.ChannelName;
703
+ }
704
+ this.cdr.markForCheck();
705
+ }
706
+ /** Active$ edges: session start resets the volatile level; session end ratchets + persists. */
707
+ onActiveChanged(active) {
708
+ if (active && !this.prevActive) {
709
+ this.Disclosure.BeginSession();
710
+ this.DetailsPeek = false;
711
+ this.ComposerOpen = false; // typing is opt-in per session (voice-first)
712
+ this.revealedChannelKeys.clear();
713
+ this.pendingRevealKey = null;
714
+ this.startAudioVisualLoop();
715
+ }
716
+ else if (!active && this.prevActive) {
717
+ this.persistDisclosure(this.Disclosure.RatchetOnSessionEnd());
718
+ this.stopAudioVisualLoop();
719
+ }
720
+ this.prevActive = active;
721
+ this.cdr.markForCheck();
722
+ }
723
+ // ── Audio-reactive visuals (the orb that vibrates like a speaker cone) ──────
724
+ //
725
+ // A requestAnimationFrame loop OUTSIDE Angular samples the client's audio meters
726
+ // (RealtimeSessionService.GetAudioActivity → driver AnalyserNodes), runs the frame through
727
+ // the smoothing state machine, and writes CSS custom properties + data attributes
728
+ // straight onto the rendered overlay element — zero change detection per frame. CSS
729
+ // gates on [data-audio-live]: with real metering the orb/EQ follow the waveform; when
730
+ // the driver attached no meters the attribute stays 'false' and the turn-state-driven
731
+ // keyframe animations keep working unchanged.
732
+ /** Per-frame smoothing state (attack/decay envelopes + direction hysteresis). */
733
+ audioSmoother = new RealtimeAudioVisualSmoother();
734
+ audioRafHandle = null;
735
+ /** Last attribute values written (attributes are only touched on change). */
736
+ lastAudioLive = null;
737
+ lastVoiceDirection = null;
738
+ /** Starts the sampling loop (idempotent). Runs outside Angular — no CD per frame. */
739
+ startAudioVisualLoop() {
740
+ if (this.audioRafHandle !== null || typeof requestAnimationFrame !== 'function') {
741
+ return;
742
+ }
743
+ this.audioSmoother.Reset();
744
+ this.ngZone.runOutsideAngular(() => {
745
+ const tick = () => {
746
+ this.audioRafHandle = requestAnimationFrame(tick);
747
+ if (this.Hidden) {
748
+ return; // minimized — skip the work, keep the loop armed
749
+ }
750
+ const frame = this.audioSmoother.Next(this.voice.GetAudioActivity(), performance.now());
751
+ this.applyAudioVisualFrame(frame);
752
+ };
753
+ this.audioRafHandle = requestAnimationFrame(tick);
754
+ });
755
+ }
756
+ /** Stops the loop and returns the overlay to turn-state-driven animation. */
757
+ stopAudioVisualLoop() {
758
+ if (this.audioRafHandle !== null) {
759
+ cancelAnimationFrame(this.audioRafHandle);
760
+ this.audioRafHandle = null;
761
+ }
762
+ this.audioSmoother.Reset();
763
+ this.lastAudioLive = null;
764
+ this.lastVoiceDirection = null;
765
+ this.overlayElement()?.setAttribute('data-audio-live', 'false');
766
+ }
767
+ /** The rendered `.call-overlay` element (the :host is display:contents). */
768
+ overlayElement() {
769
+ const host = this.hostRef.nativeElement;
770
+ return host.firstElementChild ?? null;
771
+ }
772
+ /** Writes one smoothed frame as CSS vars/attributes (attributes only on change). */
773
+ applyAudioVisualFrame(frame) {
774
+ const el = this.overlayElement();
775
+ if (!el) {
776
+ return;
777
+ }
778
+ const live = frame !== null;
779
+ if (live !== this.lastAudioLive) {
780
+ this.lastAudioLive = live;
781
+ el.setAttribute('data-audio-live', String(live));
782
+ }
783
+ if (!frame) {
784
+ return;
785
+ }
786
+ el.style.setProperty('--voice-out', frame.OutputLevel.toFixed(3));
787
+ el.style.setProperty('--voice-in', frame.InputLevel.toFixed(3));
788
+ for (let i = 0; i < frame.Bins.length; i++) {
789
+ el.style.setProperty(`--eq-${i + 1}`, frame.Bins[i].toFixed(3));
790
+ }
791
+ if (frame.Direction !== this.lastVoiceDirection) {
792
+ this.lastVoiceDirection = frame.Direction;
793
+ el.setAttribute('data-voice-dir', frame.Direction);
794
+ }
795
+ }
796
+ /** The strip's Details control: peek at (or hide) the surface panel on demand. */
797
+ OnDetailsToggled() {
798
+ this.DetailsPeek = !this.DetailsPeek;
799
+ if (this.DetailsPeek) {
800
+ // Land on the marquee surface (channels lead the strip; Activity is pinned last).
801
+ setTimeout(() => this.surfaceTabs?.FocusFirstTab());
802
+ }
803
+ this.cdr.markForCheck();
804
+ }
805
+ /** The gear popover picked an interface density — apply + persist immediately. */
806
+ OnDensityChanged(density) {
807
+ this.Disclosure.SetDensity(density);
808
+ this.persistDisclosure(SerializeUxMilestones(this.Disclosure.Milestones));
809
+ }
810
+ /** The hero's "Show the conversation" affordance — turns the text preference on. */
811
+ OnTextReveal() {
812
+ 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();
826
+ }
827
+ /** App-bar Minimize: hide the call view (CSS) — the call stays fully live. */
828
+ OnMinimize() {
829
+ this.voice.SetMinimized(true);
830
+ }
831
+ /** App-bar / strip End: tear the session down, then notify the host. */
832
+ async OnEndCall() {
833
+ await this.voice.EndVoiceSession();
834
+ this.Ended.emit();
835
+ }
836
+ /** Maps the realtime state onto the hero orb's `data-state` (active turn-states only). */
837
+ HeroOrbState(state) {
838
+ switch (state) {
839
+ case 'speaking': return 'speaking';
840
+ case 'thinking': return 'thinking';
841
+ default: return 'listening';
842
+ }
843
+ }
844
+ /** Short first-person status line for the pure-audio hero. */
845
+ HeroStateLabel(state) {
846
+ switch (state) {
847
+ case 'speaking': return `${this.AgentName} is speaking…`;
848
+ case 'thinking': return `${this.AgentName} is working…`;
849
+ case 'listening': return 'Listening';
850
+ case 'connecting': return 'Connecting…';
851
+ case 'error': return 'Connection error';
852
+ default: return 'On call';
853
+ }
854
+ }
855
+ /**
856
+ * T-TO-TYPE: pressing T during a live call reveals the composer (raising disclosure to
857
+ * the engaged level when needed) and focuses its input — typing always exists, it just
858
+ * whispers until used. Ignored while review/minimized, with modifiers, or when an
859
+ * editable element already has focus.
860
+ */
861
+ OnDocumentKeydown(event) {
862
+ if (this.Hidden || this.IsReviewing || !this.voice.IsActive) {
863
+ return;
864
+ }
865
+ if ((event.key !== 't' && event.key !== 'T') || event.metaKey || event.ctrlKey || event.altKey) {
866
+ return;
867
+ }
868
+ if (this.isEditableTarget(event.target) || this.isKeyboardCapturingSurfaceFocused()) {
869
+ return;
870
+ }
871
+ event.preventDefault();
872
+ this.OnComposerOpenChanged(true);
873
+ // The dock may have just been created — focus after this CD pass.
874
+ setTimeout(() => this.composer?.FocusInput());
875
+ }
876
+ /**
877
+ * 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 the T-to-type shortcut.
879
+ *
880
+ * @param eventTarget The keydown event's target.
881
+ * @returns Whether the target is an editable element.
882
+ */
883
+ isEditableTarget(eventTarget) {
884
+ const target = eventTarget;
885
+ return !!target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable);
886
+ }
887
+ /**
888
+ * True when a surface that opts into capturing keyboard input currently holds focus — notably the Remote
889
+ * Browser live canvas, which marks itself with `data-mj-capture-keys` while the user is driving it. When it
890
+ * has focus, the overlay's global shortcuts (T-to-type) must stand down so the user's keystrokes land in the
891
+ * remote browser, not the local composer. Decoupled by an attribute contract so the overlay needs no direct
892
+ * reference to the surface component.
893
+ *
894
+ * @returns Whether a keyboard-capturing surface is focused.
895
+ */
896
+ isKeyboardCapturingSurfaceFocused() {
897
+ const active = (typeof document !== 'undefined' ? document.activeElement : null);
898
+ return !!active && active.hasAttribute('data-mj-capture-keys');
899
+ }
900
+ /**
901
+ * The composer dock opened (strip Type control / T hotkey) or closed (the dock's hide
902
+ * control). Opening raises the 'engaged' milestone for the cross-session ratchet —
903
+ * but the dock itself stays a per-session, user-owned toggle either way.
904
+ */
905
+ OnComposerOpenChanged(open) {
906
+ this.ComposerOpen = open;
907
+ if (open) {
908
+ this.Disclosure.Raise('engaged');
909
+ setTimeout(() => this.composer?.FocusInput());
910
+ }
911
+ this.cdr.markForCheck();
912
+ }
913
+ ngOnDestroy() {
914
+ this.stopAudioVisualLoop();
915
+ if (this.IsPanelResizing) {
916
+ this.teardownPanelResize();
917
+ this.IsPanelResizing = false;
918
+ }
919
+ for (const sub of this.subs) {
920
+ sub.unsubscribe();
921
+ }
922
+ this.subs = [];
923
+ this.State.Detach();
924
+ }
925
+ /**
926
+ * Registers (or updates) an INTERACTIVE-CHANNEL tab on the surface panel. The overlay
927
+ * calls it for every registry-resolved plugin; hosts may also call it for bespoke
928
+ * template-based panes. Until a registration supplies a plugin / template, the tab
929
+ * renders the "coming online…" placeholder (re-register the same `Key` to upgrade).
930
+ * Safe to call before the panel has rendered — registrations are queued and flushed.
931
+ */
932
+ RegisterChannelTab(registration) {
933
+ if (this.surfaceTabs) {
934
+ this.surfaceTabs.RegisterChannelTab(registration);
935
+ }
936
+ else {
937
+ this.pendingChannelTabs.push(registration);
938
+ }
939
+ }
940
+ /**
941
+ * A done delegation card / rail entry asked to view a produced artifact's tab — an
942
+ * EXPLICIT user request, so it may open the (otherwise Details-controlled) panel.
943
+ */
944
+ OnOpenArtifactRequested(artifact) {
945
+ if (this.surfaceTabs) {
946
+ this.surfaceTabs.FocusArtifact(artifact);
947
+ return;
948
+ }
949
+ // Panel hidden — open it (Details mechanism) and focus once it exists.
950
+ this.DetailsPeek = true;
951
+ this.cdr.markForCheck();
952
+ setTimeout(() => this.surfaceTabs?.FocusArtifact(artifact));
953
+ }
954
+ /**
955
+ * Registers one surface tab per active channel plugin THAT HAS A SURFACE (key/title/icon from the
956
+ * plugin). Server-only channels ({@link BaseRealtimeChannelClient.HasSurface} === `false`) render no
957
+ * tab — their tools + perception are already wired by the session service; there is simply nothing
958
+ * to show in the surface panel.
959
+ */
960
+ registerChannelTabs(channels) {
961
+ this.cleanupStaleReviewBoardTab(channels);
962
+ for (const plugin of channels) {
963
+ if (!plugin.HasSurface()) {
964
+ continue; // server-only channel — no surface to tab.
965
+ }
966
+ this.RegisterChannelTab({
967
+ Key: plugin.ChannelName,
968
+ Title: plugin.TabTitle,
969
+ Icon: plugin.TabIcon,
970
+ Plugin: plugin
971
+ });
972
+ }
973
+ this.cdr.markForCheck();
974
+ }
975
+ /**
976
+ * REVIEW→LIVE continuation edge: when the resumed live session resolves its channel set
977
+ * WITHOUT a Whiteboard channel, the review-registered read-only board tab is now a dead
978
+ * surface — remove it (Activity regains focus if it was active). When the set HAS a
979
+ * Whiteboard channel, the plugin registration above upgrades the same tab key in place,
980
+ * so the review flag is simply released. Review ARTIFACT tabs are deliberately kept —
981
+ * they are wanted carryover into the live session.
982
+ */
983
+ cleanupStaleReviewBoardTab(channels) {
984
+ if (!this.reviewWhiteboardTabRegistered) {
985
+ return;
986
+ }
987
+ if (ShouldRemoveReviewWhiteboardTab(this.voice.IsActive, this.reviewWhiteboardTabRegistered, channels)) {
988
+ this.surfaceTabs?.RemoveTab('Whiteboard');
989
+ // Also drop a still-queued review registration (panel not rendered yet — rare but possible).
990
+ this.pendingChannelTabs = this.pendingChannelTabs.filter(r => !(r.Key === 'Whiteboard' && r.Content));
991
+ this.reviewWhiteboardTabRegistered = false;
992
+ }
993
+ else if (channels.some(c => c.ChannelName === 'Whiteboard')) {
994
+ this.reviewWhiteboardTabRegistered = false; // live plugin owns the tab now (upgraded in place)
995
+ }
996
+ }
997
+ /** Forwards channel registrations that arrived before the panel existed. */
998
+ flushPendingChannelTabs() {
999
+ if (this.surfaceTabs && this.pendingChannelTabs.length > 0) {
1000
+ for (const reg of this.pendingChannelTabs) {
1001
+ this.surfaceTabs.RegisterChannelTab(reg);
1002
+ }
1003
+ this.pendingChannelTabs = [];
1004
+ }
1005
+ }
1006
+ /**
1007
+ * The captions toggle = the persisted text-vs-orb switch: on → the thread renders
1008
+ * (raising the text disclosure milestone); off → the orb hero owns the screen again,
1009
+ * at any level. Persisted per-user so the choice survives refresh and devices.
1010
+ */
1011
+ OnCaptionsToggled(on) {
1012
+ this.ShowCaptions = on;
1013
+ if (on) {
1014
+ this.Disclosure.Raise('text');
1015
+ }
1016
+ this.persistCaptionsPref();
1017
+ this.cdr.markForCheck();
1018
+ }
1019
+ /** Reflect the gear toggle from the controls into the dev affordances. */
1020
+ OnDevModeToggled(on) {
1021
+ this.DevMode = on;
1022
+ }
1023
+ /** A dev link asked to open a delegated agent run record. */
1024
+ OnOpenRunRequested(runId) {
1025
+ this.requestNavigate('MJ: AI Agent Runs', runId);
1026
+ }
1027
+ /** The banner's dev link asked to open the live agent session record. */
1028
+ OnOpenSessionRequested(sessionId) {
1029
+ this.requestNavigate('MJ: AI Agent Sessions', sessionId);
1030
+ }
1031
+ // ── Session review mode ────────────────────────────────────────────────────
1032
+ /** Review banner convenience accessors (null-safe against the review data). */
1033
+ get ReviewStartedAt() {
1034
+ return this._reviewData?.StartedAt ?? null;
1035
+ }
1036
+ get ReviewClosedAt() {
1037
+ return this._reviewData?.ClosedAt ?? null;
1038
+ }
1039
+ get ReviewCloseReason() {
1040
+ return this._reviewData?.CloseReason ?? null;
1041
+ }
1042
+ /** The reviewed session record's id — feeds the banner's dev "Open session" link. */
1043
+ get ReviewSessionID() {
1044
+ return this._reviewData?.SessionID ?? null;
1045
+ }
1046
+ /**
1047
+ * Review controls: resume the reviewed session as a NEW live call (host handles the
1048
+ * start). Flags the upcoming ReviewData→null transition as a CONTINUATION so the
1049
+ * historical thread is kept (divider appended) instead of cleared.
1050
+ */
1051
+ OnStartLive() {
1052
+ const review = this._reviewData;
1053
+ if (!review) {
1054
+ return;
1055
+ }
1056
+ this.pendingLiveContinuation = true;
1057
+ this.StartLiveRequested.emit({
1058
+ TargetAgentId: review.TargetAgentID,
1059
+ ConversationId: review.ConversationID,
1060
+ LastSessionId: review.SessionID
1061
+ });
1062
+ }
1063
+ /** Review controls: close the review (host clears its review state). */
1064
+ OnReviewClose() {
1065
+ this.ReviewClosed.emit();
1066
+ }
1067
+ /**
1068
+ * Enters review: replaces the shared state's thread with the historical items (all
1069
+ * chain legs, dividers between legs), names the cards after the reviewed agent,
1070
+ * rehydrates the saved whiteboard (when any), and registers the chain's history
1071
+ * ARTIFACTS as unfocused surface tabs.
1072
+ */
1073
+ enterReview(review) {
1074
+ this.State.AgentName = review.AgentName;
1075
+ this.State.LoadHistoricalItems(BuildReviewThreadItems(review));
1076
+ this.ReviewWhiteboard = this.parseReviewWhiteboard(review);
1077
+ this.reviewArtifacts = review.Artifacts ?? [];
1078
+ if (this.viewReady) {
1079
+ // Let this CD pass create/refresh the surface panel before registering the tabs.
1080
+ // ngZone.run is REQUIRED: review often opens through the deep-link/query-param
1081
+ // path (a plain RxJS stream outside Angular's zone) — without re-entering the
1082
+ // zone, the registration's markForCheck never gets a change-detection pass and
1083
+ // the Whiteboard tab stays invisible until the user's next click.
1084
+ setTimeout(() => this.ngZone.run(() => {
1085
+ this.registerReviewBoardTab();
1086
+ this.registerReviewArtifactTabs();
1087
+ }), 0);
1088
+ }
1089
+ this.cdr.markForCheck();
1090
+ }
1091
+ /**
1092
+ * Leaves review. Two distinct exits:
1093
+ * - REVIEW→LIVE CONTINUATION ({@link OnStartLive} was clicked): KEEP the historical
1094
+ * thread and append the "Resumed live session" divider so the new live items read
1095
+ * as a new section of the same conversation. Artifact tabs are left in place — the
1096
+ * chain's artifacts carry into the live session.
1097
+ * - plain CLOSE: clear everything so the conversation view returns clean.
1098
+ */
1099
+ exitReview() {
1100
+ this.ReviewWhiteboard = null;
1101
+ if (this.pendingLiveContinuation) {
1102
+ this.pendingLiveContinuation = false;
1103
+ this.State.StartLiveContinuation();
1104
+ }
1105
+ else {
1106
+ this.reviewArtifacts = [];
1107
+ this.State.Clear();
1108
+ }
1109
+ this.cdr.markForCheck();
1110
+ }
1111
+ /** Registers the reviewed chain's history artifacts as UNFOCUSED artifact tabs (idempotent). */
1112
+ registerReviewArtifactTabs() {
1113
+ if (!this.surfaceTabs || this.reviewArtifacts.length === 0) {
1114
+ return;
1115
+ }
1116
+ for (const artifact of this.reviewArtifacts) {
1117
+ this.surfaceTabs.RegisterArtifactTab(artifact, false);
1118
+ }
1119
+ this.cdr.markForCheck();
1120
+ }
1121
+ /**
1122
+ * Rehydrates the reviewed session's saved Whiteboard channel state. TOLERANT: a missing
1123
+ * or malformed state returns null — review simply shows no whiteboard tab.
1124
+ */
1125
+ parseReviewWhiteboard(review) {
1126
+ const channel = review.ChannelStates.find(c => c.ChannelName.toLowerCase() === 'whiteboard');
1127
+ if (!channel?.StateJson) {
1128
+ return null;
1129
+ }
1130
+ try {
1131
+ return WhiteboardState.FromJSON(channel.StateJson);
1132
+ }
1133
+ catch {
1134
+ console.warn('[RealtimeSessionReview] Saved whiteboard state was malformed — skipping the board tab.');
1135
+ return null;
1136
+ }
1137
+ }
1138
+ /**
1139
+ * Registers the read-only review whiteboard tab (no-op without a board / template) —
1140
+ * FOCUSED: the session's channel surface is what the reviewer came to see; the
1141
+ * Activity rail stays available but is never the default focus.
1142
+ */
1143
+ registerReviewBoardTab() {
1144
+ if (!this.ReviewWhiteboard || !this.reviewBoardTpl) {
1145
+ return;
1146
+ }
1147
+ this.RegisterChannelTab({
1148
+ Key: 'Whiteboard',
1149
+ Title: 'Whiteboard',
1150
+ Icon: 'fa-solid fa-chalkboard',
1151
+ Content: this.reviewBoardTpl,
1152
+ Focus: true
1153
+ });
1154
+ this.reviewWhiteboardTabRegistered = true;
1155
+ this.cdr.markForCheck();
1156
+ }
1157
+ /**
1158
+ * A working delegation card's ✕ asked to cancel that delegated run — EXPLICIT user
1159
+ * intent (barge-in never cancels work, by deliberate policy). The service calls the
1160
+ * server cancel channel and flips the card to a failed "Cancelled by user" result.
1161
+ */
1162
+ OnCancelDelegation(callId) {
1163
+ void this.voice.CancelDelegation(callId);
1164
+ }
1165
+ /** Minimizes the live call (it stays running) and asks the host to open the record. */
1166
+ requestNavigate(entityName, recordId) {
1167
+ this.voice.SetMinimized(true);
1168
+ this.NavigateRequest.emit({ EntityName: entityName, RecordID: recordId });
1169
+ }
1170
+ // ── Focus layout + pill ────────────────────────────────────────────────────
1171
+ /** A channel requested (or released) the focus layout via its host context. */
1172
+ onChannelFocus(channel, focused) {
1173
+ this.ChannelFocusMode = focused;
1174
+ this.focusChannel = focused ? channel : null;
1175
+ this.cdr.markForCheck();
1176
+ }
1177
+ /** Focus pill: toggle the mic mute. */
1178
+ OnFocusPillMute() {
1179
+ this.FocusPillMuted = this.voice.ToggleMute();
1180
+ }
1181
+ /** Focus pill: leave focus mode (show the thread column again). */
1182
+ OnFocusPillExit() {
1183
+ // Route through the focus-holding channel so ITS surface toggle stays in sync — it
1184
+ // re-emits SetFocusMode(false) → onChannelFocus. Defensively clear the layout flag
1185
+ // too (idempotent), covering channels whose surface isn't instantiated.
1186
+ this.focusChannel?.RequestFocusExit();
1187
+ this.ChannelFocusMode = false;
1188
+ this.focusChannel = null;
1189
+ }
1190
+ /** Focus pill: end the call (mirrors the controls row's End button). */
1191
+ async OnFocusPillEnd() {
1192
+ await this.voice.EndVoiceSession();
1193
+ this.Ended.emit();
1194
+ }
1195
+ /** Short status line for the focus pill (first person — the co-agent owns the work). */
1196
+ FocusStateLabel(state) {
1197
+ switch (state) {
1198
+ case 'speaking': return `${this.AgentName} is speaking…`;
1199
+ case 'thinking': return `${this.AgentName} is working…`;
1200
+ case 'listening': return 'Listening';
1201
+ case 'connecting': return 'Connecting…';
1202
+ default: return '';
1203
+ }
1204
+ }
1205
+ static ɵfac = function RealtimeSessionOverlayComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || RealtimeSessionOverlayComponent)(); };
1206
+ 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);
1208
+ } if (rf & 2) {
1209
+ let _t;
1210
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.surfaceTabsRef = _t.first);
1211
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.composer = _t.first);
1212
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.reviewBoardTpl = _t.first);
1213
+ } }, hostBindings: function RealtimeSessionOverlayComponent_HostBindings(rf, ctx) { if (rf & 1) {
1214
+ 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: 4, vars: 3, consts: [["reviewBoardTpl", ""], ["role", "region", 1, "call-overlay", 3, "call-overlay--hidden", "call-overlay--review", "board-focus"], ["role", "region", 1, "call-overlay"], [1, "call-main"], [3, "OpenSessionRequested", "CaptionsToggled", "DevModeToggled", "DensityChanged", "MinimizeRequested", "PureAudioRequested", "EndRequested", "StartLiveRequested", "CloseRequested", "State", "AgentName", "ModelName", "DevMode", "SessionID", "ReviewMode", "ReviewStartedAt", "ReviewClosedAt", "ReviewCloseReason", "CaptionsOn", "ShowCaptionsControl", "ShowGear", "ShowEnd", "ShowMinimize", "ShowPureAudio", "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", "EndRequested", "Open", "CaptionsOn", "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", "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"]], template: function RealtimeSessionOverlayComponent_Template(rf, ctx) { if (rf & 1) {
1216
+ i0.ɵɵconditionalCreate(0, RealtimeSessionOverlayComponent_Conditional_0_Template, 12, 32, "div", 1);
1217
+ i0.ɵɵpipe(1, "async");
1218
+ i0.ɵɵtemplate(2, RealtimeSessionOverlayComponent_ng_template_2_Template, 1, 1, "ng-template", null, 0, i0.ɵɵtemplateRefExtractor);
1219
+ } if (rf & 2) {
1220
+ let tmp_1_0;
1221
+ i0.ɵɵconditional((tmp_1_0 = i0.ɵɵpipeBind1(1, 1, ctx.ConnectionState$)) ? 0 : -1, tmp_1_0);
1222
+ } }, dependencies: [CommonModule,
1223
+ SharedGenericModule, i1.LoadingComponent, RealtimeAgentBannerComponent,
1224
+ RealtimeSessionThreadComponent,
1225
+ RealtimeChannelStripComponent,
1226
+ RealtimeComposerComponent,
1227
+ 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}"] });
1229
+ }
1230
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RealtimeSessionOverlayComponent, [{
1231
+ type: Component,
1232
+ args: [{ standalone: true, selector: 'mj-realtime-session-overlay', imports: [
1233
+ CommonModule,
1234
+ SharedGenericModule,
1235
+ RealtimeAgentBannerComponent,
1236
+ RealtimeSessionThreadComponent,
1237
+ RealtimeChannelStripComponent,
1238
+ RealtimeComposerComponent,
1239
+ 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"] }]
1242
+ }], () => [], { Hidden: [{
1243
+ type: Input
1244
+ }], AgentName: [{
1245
+ type: Input
1246
+ }], CurrentUser: [{
1247
+ type: Input
1248
+ }], EnvironmentID: [{
1249
+ type: Input
1250
+ }], ReviewData: [{
1251
+ type: Input
1252
+ }], Ended: [{
1253
+ type: Output
1254
+ }], NavigateRequest: [{
1255
+ type: Output
1256
+ }], StartLiveRequested: [{
1257
+ type: Output
1258
+ }], ReviewClosed: [{
1259
+ type: Output
1260
+ }], surfaceTabsRef: [{
1261
+ type: ViewChild,
1262
+ args: [RealtimeSurfaceTabsComponent]
1263
+ }], composer: [{
1264
+ type: ViewChild,
1265
+ args: [RealtimeComposerComponent]
1266
+ }], reviewBoardTpl: [{
1267
+ type: ViewChild,
1268
+ args: ['reviewBoardTpl']
1269
+ }], OnDocumentKeydown: [{
1270
+ type: HostListener,
1271
+ args: ['document:keydown', ['$event']]
1272
+ }] }); })();
1273
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RealtimeSessionOverlayComponent, { className: "RealtimeSessionOverlayComponent", filePath: "src/lib/components/realtime/realtime-session-overlay.component.ts", lineNumber: 118 }); })();
1274
+ //# sourceMappingURL=realtime-session-overlay.component.js.map