@memberjunction/ng-conversations 5.40.2 → 5.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,496 @@
1
+ import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { UserInfoEngine } from '@memberjunction/core-entities';
4
+ import { ArtifactsModule } from '@memberjunction/ng-artifacts';
5
+ import { RealtimeActivityRailComponent } from './realtime-activity-rail.component';
6
+ import { RealtimeChannelPaneComponent } from './channels/realtime-channel-pane.component';
7
+ import { ChannelOnboardingPanelComponent } from './channels/channel-onboarding-panel.component';
8
+ import { RealtimeSurfaceTabsModel } from './realtime-surface-tabs.model';
9
+ import * as i0 from "@angular/core";
10
+ import * as i1 from "@angular/common";
11
+ import * as i2 from "@memberjunction/ng-artifacts";
12
+ function RealtimeSurfaceTabsComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
13
+ const _r1 = i0.ɵɵgetCurrentView();
14
+ i0.ɵɵelementStart(0, "div", 1)(1, "button", 2);
15
+ i0.ɵɵlistener("click", function RealtimeSurfaceTabsComponent_Conditional_1_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ToggleCollapsed()); });
16
+ i0.ɵɵelement(2, "i", 3);
17
+ i0.ɵɵelementEnd();
18
+ i0.ɵɵelement(3, "i", 4);
19
+ i0.ɵɵelementEnd();
20
+ } }
21
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
22
+ i0.ɵɵelement(0, "span", 14);
23
+ } }
24
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1) {
25
+ const _r4 = i0.ɵɵgetCurrentView();
26
+ i0.ɵɵelementStart(0, "button", 11);
27
+ i0.ɵɵlistener("click", function RealtimeSurfaceTabsComponent_Conditional_2_For_2_Template_button_click_0_listener() { const tab_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.Model.Focus(tab_r5.Key)); });
28
+ i0.ɵɵelement(1, "i", 12);
29
+ i0.ɵɵelementStart(2, "span", 13);
30
+ i0.ɵɵtext(3);
31
+ i0.ɵɵelementEnd();
32
+ i0.ɵɵconditionalCreate(4, RealtimeSurfaceTabsComponent_Conditional_2_For_2_Conditional_4_Template, 1, 0, "span", 14);
33
+ i0.ɵɵelementEnd();
34
+ } if (rf & 2) {
35
+ const tab_r5 = ctx.$implicit;
36
+ const ctx_r1 = i0.ɵɵnextContext(2);
37
+ i0.ɵɵclassProp("s-tab--active", tab_r5.Key === ctx_r1.Model.ActiveKey)("s-tab--flash", tab_r5.Key === ctx_r1.Model.FlashKey)("s-tab--unseen", ctx_r1.Model.IsUnseen(tab_r5.Key));
38
+ i0.ɵɵproperty("title", ctx_r1.Model.IsUnseen(tab_r5.Key) ? tab_r5.Title + " (new)" : tab_r5.Title);
39
+ i0.ɵɵattribute("aria-selected", tab_r5.Key === ctx_r1.Model.ActiveKey);
40
+ i0.ɵɵadvance();
41
+ i0.ɵɵclassMap(tab_r5.Icon);
42
+ i0.ɵɵadvance(2);
43
+ i0.ɵɵtextInterpolate(tab_r5.Title);
44
+ i0.ɵɵadvance();
45
+ i0.ɵɵconditional(ctx_r1.Model.IsUnseen(tab_r5.Key) ? 4 : -1);
46
+ } }
47
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_1_Template(rf, ctx) { if (rf & 1) {
48
+ const _r6 = i0.ɵɵgetCurrentView();
49
+ i0.ɵɵelementStart(0, "mj-realtime-activity-rail", 17);
50
+ i0.ɵɵlistener("OpenRunRequested", function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_1_Template_mj_realtime_activity_rail_OpenRunRequested_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OpenRunRequested.emit($event)); })("OpenArtifactRequested", function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_1_Template_mj_realtime_activity_rail_OpenArtifactRequested_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.FocusArtifact($event)); });
51
+ i0.ɵɵelementEnd();
52
+ } if (rf & 2) {
53
+ const ctx_r1 = i0.ɵɵnextContext(3);
54
+ i0.ɵɵproperty("State", ctx_r1.State)("DevMode", ctx_r1.DevMode)("Embedded", true);
55
+ } }
56
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
57
+ i0.ɵɵelementStart(0, "div", 18);
58
+ i0.ɵɵelement(1, "mj-artifact-viewer-panel", 19);
59
+ i0.ɵɵelementEnd();
60
+ } if (rf & 2) {
61
+ const ctx_r1 = i0.ɵɵnextContext(4);
62
+ i0.ɵɵadvance();
63
+ i0.ɵɵproperty("artifactId", ctx.ArtifactID)("currentUser", ctx_r1.CurrentUser)("environmentId", ctx_r1.EnvironmentID)("showHeader", false)("showTabs", false)("showSaveToCollection", false)("showCloseButton", false)("showMaximizeButton", false);
64
+ } }
65
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_2_Template(rf, ctx) { if (rf & 1) {
66
+ i0.ɵɵconditionalCreate(0, RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_2_Conditional_0_Template, 2, 8, "div", 18);
67
+ } if (rf & 2) {
68
+ let tmp_12_0;
69
+ const tab_r7 = i0.ɵɵnextContext().$implicit;
70
+ i0.ɵɵconditional((tmp_12_0 = tab_r7.Data == null ? null : tab_r7.Data.Artifact) ? 0 : -1, tmp_12_0);
71
+ } }
72
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
73
+ const _r8 = i0.ɵɵgetCurrentView();
74
+ i0.ɵɵelementStart(0, "mj-channel-onboarding-panel", 24);
75
+ i0.ɵɵlistener("Dismissed", function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Conditional_0_Conditional_1_Template_mj_channel_onboarding_panel_Dismissed_0_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.DismissOnboarding()); });
76
+ i0.ɵɵelementEnd();
77
+ } if (rf & 2) {
78
+ const ctx_r1 = i0.ɵɵnextContext(5);
79
+ i0.ɵɵproperty("Content", ctx_r1.OnboardingContent);
80
+ } }
81
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Conditional_0_Template(rf, ctx) { if (rf & 1) {
82
+ i0.ɵɵelement(0, "mj-realtime-channel-pane", 22);
83
+ i0.ɵɵconditionalCreate(1, RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Conditional_0_Conditional_1_Template, 1, 1, "mj-channel-onboarding-panel", 23);
84
+ } if (rf & 2) {
85
+ const tab_r7 = i0.ɵɵnextContext(2).$implicit;
86
+ const ctx_r1 = i0.ɵɵnextContext(2);
87
+ i0.ɵɵproperty("Plugin", ctx);
88
+ i0.ɵɵadvance();
89
+ i0.ɵɵconditional(tab_r7.Key === ctx_r1.Model.ActiveKey && ctx_r1.OnboardingContent ? 1 : -1);
90
+ } }
91
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
92
+ i0.ɵɵelementContainer(0, 20);
93
+ } if (rf & 2) {
94
+ i0.ɵɵproperty("ngTemplateOutlet", ctx);
95
+ } }
96
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Conditional_2_Template(rf, ctx) { if (rf & 1) {
97
+ i0.ɵɵelementStart(0, "div", 21);
98
+ i0.ɵɵelement(1, "i", 25);
99
+ i0.ɵɵelementStart(2, "span");
100
+ i0.ɵɵtext(3);
101
+ i0.ɵɵelementEnd()();
102
+ } if (rf & 2) {
103
+ const tab_r7 = i0.ɵɵnextContext(2).$implicit;
104
+ i0.ɵɵadvance(3);
105
+ i0.ɵɵtextInterpolate1("", tab_r7.Title, " coming online\u2026");
106
+ } }
107
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Template(rf, ctx) { if (rf & 1) {
108
+ i0.ɵɵconditionalCreate(0, RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Conditional_0_Template, 2, 2)(1, RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Conditional_1_Template, 1, 1, "ng-container", 20)(2, RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Conditional_2_Template, 4, 1, "div", 21);
109
+ } if (rf & 2) {
110
+ let tmp_12_0;
111
+ const tab_r7 = i0.ɵɵnextContext().$implicit;
112
+ i0.ɵɵconditional((tmp_12_0 = tab_r7.Data == null ? null : tab_r7.Data.Plugin) ? 0 : (tmp_12_0 = tab_r7.Data == null ? null : tab_r7.Data.Content) ? 1 : 2, tmp_12_0);
113
+ } }
114
+ function RealtimeSurfaceTabsComponent_Conditional_2_For_7_Template(rf, ctx) { if (rf & 1) {
115
+ i0.ɵɵelementStart(0, "div", 15);
116
+ i0.ɵɵconditionalCreate(1, RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_1_Template, 1, 3, "mj-realtime-activity-rail", 16)(2, RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_2_Template, 1, 1)(3, RealtimeSurfaceTabsComponent_Conditional_2_For_7_Case_3_Template, 3, 1);
117
+ i0.ɵɵelementEnd();
118
+ } if (rf & 2) {
119
+ let tmp_12_0;
120
+ const tab_r7 = ctx.$implicit;
121
+ const ctx_r1 = i0.ɵɵnextContext(2);
122
+ i0.ɵɵclassProp("s-pane--active", tab_r7.Key === ctx_r1.Model.ActiveKey);
123
+ i0.ɵɵadvance();
124
+ i0.ɵɵconditional((tmp_12_0 = tab_r7.Kind) === "activity" ? 1 : tmp_12_0 === "artifact" ? 2 : tmp_12_0 === "channel" ? 3 : -1);
125
+ } }
126
+ function RealtimeSurfaceTabsComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
127
+ const _r3 = i0.ɵɵgetCurrentView();
128
+ i0.ɵɵelementStart(0, "div", 5);
129
+ i0.ɵɵrepeaterCreate(1, RealtimeSurfaceTabsComponent_Conditional_2_For_2_Template, 5, 12, "button", 6, i0.ɵɵcomponentInstance().TrackTab, true);
130
+ i0.ɵɵelementStart(3, "div", 7)(4, "button", 8);
131
+ i0.ɵɵlistener("click", function RealtimeSurfaceTabsComponent_Conditional_2_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ToggleCollapsed()); });
132
+ i0.ɵɵelement(5, "i", 9);
133
+ i0.ɵɵelementEnd()()();
134
+ i0.ɵɵrepeaterCreate(6, RealtimeSurfaceTabsComponent_Conditional_2_For_7_Template, 4, 3, "div", 10, i0.ɵɵcomponentInstance().TrackTab, true);
135
+ } if (rf & 2) {
136
+ const ctx_r1 = i0.ɵɵnextContext();
137
+ i0.ɵɵadvance();
138
+ i0.ɵɵrepeater(ctx_r1.Model.Tabs);
139
+ i0.ɵɵadvance(5);
140
+ i0.ɵɵrepeater(ctx_r1.Model.Tabs);
141
+ } }
142
+ /**
143
+ * User-settings key (NOT localStorage — see `UserInfoEngine`) under which the per-user "which
144
+ * channel intros have been seen" map is persisted: a JSON object of `{ [channelName]: true }`,
145
+ * so the first-run onboarding for each interactive channel shows exactly once per user and
146
+ * follows them across devices.
147
+ */
148
+ const CHANNEL_ONBOARDING_SEEN_SETTING_KEY = 'mj.realtimeChannels.onboardingSeen.v1';
149
+ /**
150
+ * The call overlay's TABBED SURFACE PANEL (the right panel) — per the approved
151
+ * `plans/realtime/mockups/whiteboard.html` mockup:
152
+ *
153
+ * - **Activity** — always the first tab; hosts the existing
154
+ * {@link RealtimeActivityRailComponent} unchanged (in `Embedded` mode, since this panel
155
+ * owns the chrome + collapse).
156
+ * - **One tab per artifact** produced by delegated runs: when a delegation result carries
157
+ * artifacts, a tab is auto-opened, FOCUSED, and briefly flashed violet. The pane reuses
158
+ * the standard `mj-artifact-viewer-panel` (read-only embed: no header/tabs/actions),
159
+ * loading by ArtifactID — the latest version, i.e. the one the run just produced.
160
+ * - **Channel tabs** — rendered ONLY once a channel registers via
161
+ * {@link RegisterChannelTab}. The overlay shell registers one per registry-resolved
162
+ * {@link BaseRealtimeChannelClient} plugin; the pane creates the plugin's surface
163
+ * component dynamically (via `mj-realtime-channel-pane`). Until a registration supplies
164
+ * a plugin (or legacy template), the pane shows the "coming online…" placeholder;
165
+ * re-registering the same key swaps the real surface in.
166
+ *
167
+ * Panes are kept ALIVE while hidden (CSS `display:none`, mirroring the mockup's
168
+ * `.s-pane.active`) so switching tabs never reloads an artifact or resets the rail.
169
+ * The whole panel collapses to a slim strip via the chevron at the tab strip's end —
170
+ * the collapse-to-strip behavior that used to live on the rail, lifted to the panel.
171
+ *
172
+ * SIZING IS EXTERNAL: the overlay shell hosts this panel in a fixed-width flex item
173
+ * and owns the width (user drag via the resize handle + persisted preference +
174
+ * default tiers). This panel just fills it and REPORTS the layout signals the shell sizes from:
175
+ * {@link CollapsedChange} (slim-strip toggle) and {@link WideChanged} (a content tab
176
+ * is focused → the default width tier widens).
177
+ */
178
+ export class RealtimeSurfaceTabsComponent {
179
+ /** How long a just-arrived tab keeps its violet flash highlight. */
180
+ static FlashDurationMs = 1400;
181
+ /** Shared live-session state, owned by the overlay shell (feeds the Activity rail). */
182
+ State;
183
+ /** Whether developer affordances ("Open run" links) are revealed (gear-gated). */
184
+ DevMode = false;
185
+ /**
186
+ * FILL presentation: the panel stretches to the overlay's full width (the board-focus
187
+ * layout, where the main call column is hidden and a channel surface owns the screen).
188
+ * Bound by the overlay shell; overrides the normal / wide width tiers.
189
+ */
190
+ Fill = false;
191
+ /** The signed-in user, threaded to the artifact viewer panel. */
192
+ CurrentUser = null;
193
+ /** The active environment id, threaded to the artifact viewer panel. */
194
+ EnvironmentID = '';
195
+ /** Re-emitted from the Activity rail's dev "Open run" links. */
196
+ OpenRunRequested = new EventEmitter();
197
+ /**
198
+ * Emitted when the panel toggles between expanded and the slim collapsed strip —
199
+ * the overlay shell resizes this panel's split area to the strip width.
200
+ */
201
+ CollapsedChange = new EventEmitter();
202
+ /**
203
+ * Emitted when {@link IsWide} flips (a content tab gained / lost focus) — the
204
+ * overlay shell widens the panel's DEFAULT split-area size while wide (only when
205
+ * the user has never dragged an explicit width).
206
+ */
207
+ WideChanged = new EventEmitter();
208
+ /** The panel's tab state (add / focus / dedupe / flash) — see the model for the rules. */
209
+ Model = new RealtimeSurfaceTabsModel();
210
+ /** Whether the panel is collapsed to its slim strip. */
211
+ Collapsed = false;
212
+ /** Artifact version ids already turned into tabs (guards the State rescan). */
213
+ tabbedVersionIds = new Set();
214
+ flashTimer = null;
215
+ subs = [];
216
+ lastWide = false;
217
+ cdr = inject(ChangeDetectorRef);
218
+ /**
219
+ * The channel whose first-run intro is currently being shown (its `ChannelName`), or `null`
220
+ * when no intro is up. Set when the user opens a channel tab they've never seen the intro
221
+ * for; cleared on dismiss. Only one intro shows at a time (the active channel's).
222
+ */
223
+ onboardingChannelName = null;
224
+ /** The intro content for {@link onboardingChannelName}, mirrored for the template binding. */
225
+ OnboardingContent = null;
226
+ /** The currently focused tab. */
227
+ get ActiveTab() {
228
+ return this.Model.ActiveTab;
229
+ }
230
+ /** Wide presentation when a content tab (artifact / channel) is focused. */
231
+ get IsWide() {
232
+ return !this.Collapsed && this.ActiveTab.Kind !== 'activity';
233
+ }
234
+ ngOnInit() {
235
+ this.subs.push(this.State.Changed$.subscribe(() => this.onStateChanged()), this.Model.Changed$.subscribe(() => this.onModelChanged()));
236
+ this.syncArtifactTabs();
237
+ }
238
+ ngOnDestroy() {
239
+ for (const s of this.subs) {
240
+ s.unsubscribe();
241
+ }
242
+ this.subs = [];
243
+ if (this.flashTimer) {
244
+ clearTimeout(this.flashTimer);
245
+ this.flashTimer = null;
246
+ }
247
+ }
248
+ /** Toggle the panel between expanded and slim-collapsed. */
249
+ ToggleCollapsed() {
250
+ this.setCollapsed(!this.Collapsed);
251
+ }
252
+ /** Collapse-state transitions funnel through here so the shell always hears about them. */
253
+ setCollapsed(value) {
254
+ if (this.Collapsed !== value) {
255
+ this.Collapsed = value;
256
+ this.CollapsedChange.emit(value);
257
+ this.syncWide();
258
+ }
259
+ }
260
+ /** Emits {@link WideChanged} when the wide tier flips (content tab focus / collapse). */
261
+ syncWide() {
262
+ const wide = this.IsWide;
263
+ if (wide !== this.lastWide) {
264
+ this.lastWide = wide;
265
+ this.WideChanged.emit(wide);
266
+ }
267
+ }
268
+ /** track fn for the @for over tabs. */
269
+ TrackTab(index, tab) {
270
+ return tab.Key;
271
+ }
272
+ /**
273
+ * Focuses (opening if needed) the tab for an artifact — the "View →" affordance target
274
+ * on done delegation cards and rail entries. Expands the panel if it was collapsed.
275
+ */
276
+ FocusArtifact(artifact) {
277
+ this.tabbedVersionIds.add(artifact.ArtifactVersionID);
278
+ this.Model.OpenArtifactTab(artifact, true);
279
+ this.setCollapsed(false);
280
+ this.cdr.markForCheck();
281
+ }
282
+ /**
283
+ * Registers (or updates) an interactive-channel tab — one per registry-resolved channel
284
+ * plugin, forwarded from `RealtimeSessionOverlayComponent.RegisterChannelTab`.
285
+ */
286
+ RegisterChannelTab(registration) {
287
+ // Microtask defer: the overlay forwards this while handling agent/channel activity, which can
288
+ // land mid change-detection. Adding a tab to Model.Tabs synchronously then trips NG0100 on the
289
+ // tab-strip bindings (s-tab--active). A microtask lands the mutation in a fresh CD turn —
290
+ // imperceptible for an async reveal, and ordered with any follow-on RevealChannel.
291
+ Promise.resolve().then(() => {
292
+ this.Model.RegisterChannelTab(registration);
293
+ this.cdr.markForCheck();
294
+ });
295
+ }
296
+ /**
297
+ * AUTO-REVEALS a channel surface the moment the agent first acts on it: expands the
298
+ * panel if collapsed, focuses the channel's tab and flashes it violet — so the user
299
+ * discovers the whiteboard (or any channel) exists the instant it comes alive,
300
+ * instead of having to find the tab themselves. No-op for unknown keys.
301
+ */
302
+ RevealChannel(key) {
303
+ // Microtask defer (same NG0100 reason as RegisterChannelTab): the agent-activity reveal mutates
304
+ // ActiveKey/FlashKey, which feed the tab-strip class bindings; doing it mid-CD trips the
305
+ // ExpressionChanged check. Deferring lands it in a fresh CD turn and stays ordered after any
306
+ // RegisterChannelTab queued just before it.
307
+ Promise.resolve().then(() => {
308
+ this.setCollapsed(false);
309
+ this.Model.Focus(key);
310
+ this.Model.FlashTab(key);
311
+ this.cdr.markForCheck();
312
+ });
313
+ }
314
+ /**
315
+ * Removes a tab from the panel (Activity is irremovable; focus falls back to Activity —
316
+ * see {@link RealtimeSurfaceTabsModel.RemoveTab}). Used by the overlay shell on a
317
+ * review→live continuation whose live channel set resolved WITHOUT the channel a stale
318
+ * review tab represents (e.g. no Whiteboard channel → drop the read-only review board tab).
319
+ *
320
+ * @returns `true` when a tab was removed.
321
+ */
322
+ RemoveTab(key) {
323
+ const removed = this.Model.RemoveTab(key);
324
+ if (removed) {
325
+ this.cdr.markForCheck();
326
+ }
327
+ return removed;
328
+ }
329
+ /**
330
+ * Registers an artifact tab WITHOUT stealing focus (default) — the SESSION REVIEW /
331
+ * resume-carryover path, where a reviewed session's (and its prior legs') history
332
+ * artifacts are surfaced as tabs the user can visit, as opposed to the live
333
+ * auto-open-on-arrival behavior. Idempotent per artifact version; the registered tab
334
+ * survives the review→live transition (tabs are never cleared on resume).
335
+ */
336
+ RegisterArtifactTab(artifact, focus = false) {
337
+ this.tabbedVersionIds.add(artifact.ArtifactVersionID);
338
+ // History carryover is context, not news — never marked unseen (no glow).
339
+ this.Model.OpenArtifactTab(artifact, focus, false);
340
+ this.cdr.markForCheck();
341
+ }
342
+ /** On session-state changes: open tabs for newly-arrived artifacts, then re-render. */
343
+ onStateChanged() {
344
+ this.syncArtifactTabs();
345
+ this.cdr.markForCheck();
346
+ }
347
+ /** On model changes: schedule the flash clear, report a wide-tier flip, re-render. */
348
+ onModelChanged() {
349
+ this.scheduleFlashClear();
350
+ this.syncWide();
351
+ this.evaluateOnboarding();
352
+ this.cdr.markForCheck();
353
+ }
354
+ /**
355
+ * Decides whether the first-run channel intro should be visible for the ACTIVE tab: shows it
356
+ * the first time the user opens (focuses) a channel tab whose plugin supplies onboarding and
357
+ * which this user hasn't dismissed before. Re-runs on every model change so switching away
358
+ * from a channel tab tears the intro down (only the active channel's intro is ever up).
359
+ */
360
+ evaluateOnboarding() {
361
+ const tab = this.ActiveTab;
362
+ const plugin = tab.Kind === 'channel' ? tab.Data?.Plugin ?? null : null;
363
+ const details = plugin?.GetOnboardingDetails() ?? null;
364
+ if (!plugin || !details || this.HasSeenOnboarding(plugin.ChannelName)) {
365
+ this.onboardingChannelName = null;
366
+ this.OnboardingContent = null;
367
+ return;
368
+ }
369
+ this.onboardingChannelName = plugin.ChannelName;
370
+ this.OnboardingContent = details;
371
+ }
372
+ /**
373
+ * Dismisses the current channel intro: marks that channel seen for this user (persisted via
374
+ * `UserInfoEngine`, debounced — NOT localStorage) and hides the panel so it never re-appears.
375
+ */
376
+ DismissOnboarding() {
377
+ const channelName = this.onboardingChannelName;
378
+ this.onboardingChannelName = null;
379
+ this.OnboardingContent = null;
380
+ if (channelName) {
381
+ this.markOnboardingSeen(channelName);
382
+ }
383
+ this.cdr.markForCheck();
384
+ }
385
+ /** Reads the per-user seen-map and reports whether this channel's intro has been dismissed. */
386
+ HasSeenOnboarding(channelName) {
387
+ return this.readOnboardingSeen()[channelName] === true;
388
+ }
389
+ /** Persists `channelName` into the per-user seen-map (merge + debounced save). */
390
+ markOnboardingSeen(channelName) {
391
+ const map = this.readOnboardingSeen();
392
+ if (map[channelName] === true) {
393
+ return;
394
+ }
395
+ map[channelName] = true;
396
+ UserInfoEngine.Instance.SetSettingDebounced(CHANNEL_ONBOARDING_SEEN_SETTING_KEY, JSON.stringify(map));
397
+ }
398
+ /** Reads + parses the per-user seen-map setting (tolerant: malformed / unset → empty map). */
399
+ readOnboardingSeen() {
400
+ const raw = UserInfoEngine.Instance.GetSetting(CHANNEL_ONBOARDING_SEEN_SETTING_KEY);
401
+ if (!raw) {
402
+ return {};
403
+ }
404
+ try {
405
+ const parsed = JSON.parse(raw);
406
+ return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
407
+ ? parsed
408
+ : {};
409
+ }
410
+ catch {
411
+ return {};
412
+ }
413
+ }
414
+ /**
415
+ * Scans the session's delegation cards for artifacts that don't have a tab yet and
416
+ * adds one per artifact — UNFOCUSED, with the brief violet flash plus the persistent
417
+ * unseen glow. Per product direction a finished artifact never steals the screen
418
+ * (only the agent's first channel activity auto-reveals); the glowing tab is the
419
+ * invitation, the user opens it when they want it.
420
+ */
421
+ syncArtifactTabs() {
422
+ for (const card of this.State.Cards) {
423
+ if (!card.Done || !card.Artifacts) {
424
+ continue;
425
+ }
426
+ for (const artifact of card.Artifacts) {
427
+ if (this.tabbedVersionIds.has(artifact.ArtifactVersionID)) {
428
+ continue;
429
+ }
430
+ this.tabbedVersionIds.add(artifact.ArtifactVersionID);
431
+ this.Model.OpenArtifactTab(artifact, false);
432
+ }
433
+ }
434
+ }
435
+ /**
436
+ * Focuses the FIRST tab in the strip — channels lead, so a Details peek lands on the
437
+ * marquee surface (e.g. the Whiteboard) when one exists, else on Activity.
438
+ */
439
+ FocusFirstTab() {
440
+ const first = this.Model.Tabs[0];
441
+ if (first) {
442
+ this.Model.Focus(first.Key);
443
+ this.cdr.markForCheck();
444
+ }
445
+ }
446
+ /** Clears the model's flash highlight after a beat (one timer; latest flash wins). */
447
+ scheduleFlashClear() {
448
+ if (this.Model.FlashKey === null) {
449
+ return;
450
+ }
451
+ if (this.flashTimer) {
452
+ clearTimeout(this.flashTimer);
453
+ }
454
+ this.flashTimer = setTimeout(() => {
455
+ this.flashTimer = null;
456
+ this.Model.ClearFlash();
457
+ }, RealtimeSurfaceTabsComponent.FlashDurationMs);
458
+ }
459
+ static ɵfac = function RealtimeSurfaceTabsComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || RealtimeSurfaceTabsComponent)(); };
460
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RealtimeSurfaceTabsComponent, selectors: [["mj-realtime-surface-tabs"]], inputs: { State: "State", DevMode: "DevMode", Fill: "Fill", CurrentUser: "CurrentUser", EnvironmentID: "EnvironmentID" }, outputs: { OpenRunRequested: "OpenRunRequested", CollapsedChange: "CollapsedChange", WideChanged: "WideChanged" }, decls: 3, vars: 5, consts: [["aria-label", "Session surfaces", 1, "surface"], [1, "surface__strip"], ["type", "button", "title", "Show session panel", "aria-expanded", "false", 1, "surface__toggle", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-left"], ["aria-hidden", "true", "title", "Session activity", 1, "fa-solid", "fa-list-check", "surface__strip-icon"], ["role", "tablist", 1, "surface-tabs"], ["type", "button", "role", "tab", 1, "s-tab", 3, "s-tab--active", "s-tab--flash", "s-tab--unseen", "title"], [1, "tabs-end"], ["type", "button", "title", "Hide session panel", "aria-expanded", "true", 1, "surface__toggle", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-right"], ["role", "tabpanel", 1, "s-pane", 3, "s-pane--active"], ["type", "button", "role", "tab", 1, "s-tab", 3, "click", "title"], ["aria-hidden", "true"], [1, "s-tab__title"], ["aria-hidden", "true", 1, "s-tab__unseen-dot"], ["role", "tabpanel", 1, "s-pane"], [3, "State", "DevMode", "Embedded"], [3, "OpenRunRequested", "OpenArtifactRequested", "State", "DevMode", "Embedded"], [1, "artifact-pane"], [3, "artifactId", "currentUser", "environmentId", "showHeader", "showTabs", "showSaveToCollection", "showCloseButton", "showMaximizeButton"], [3, "ngTemplateOutlet"], ["role", "status", 1, "channel-placeholder"], [3, "Plugin"], [3, "Content"], [3, "Dismissed", "Content"], ["aria-hidden", "true", 1, "fa-solid", "fa-chalkboard"]], template: function RealtimeSurfaceTabsComponent_Template(rf, ctx) { if (rf & 1) {
461
+ i0.ɵɵelementStart(0, "aside", 0);
462
+ i0.ɵɵconditionalCreate(1, RealtimeSurfaceTabsComponent_Conditional_1_Template, 4, 0, "div", 1)(2, RealtimeSurfaceTabsComponent_Conditional_2_Template, 8, 0);
463
+ i0.ɵɵelementEnd();
464
+ } if (rf & 2) {
465
+ i0.ɵɵclassProp("surface--collapsed", ctx.Collapsed)("surface--fill", ctx.Fill && !ctx.Collapsed);
466
+ i0.ɵɵadvance();
467
+ i0.ɵɵconditional(ctx.Collapsed ? 1 : 2);
468
+ } }, dependencies: [CommonModule, i1.NgTemplateOutlet, ArtifactsModule, i2.ArtifactViewerPanelComponent, RealtimeActivityRailComponent, RealtimeChannelPaneComponent,
469
+ ChannelOnboardingPanelComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n height: 100%;\n \n\n --sage: var(--mj-color-violet-400, #a78bfa);\n --sage-bg: color-mix(in srgb, var(--mj-color-violet-400, #a78bfa) 10%, transparent);\n --sage-border: color-mix(in srgb, var(--mj-color-violet-400, #a78bfa) 38%, transparent);\n}\n\n\n\n\n.surface[_ngcontent-%COMP%] {\n position: relative;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n}\n\n\n.surface--fill[_ngcontent-%COMP%] {\n width: 100%;\n border-left: none;\n}\n.surface--collapsed[_ngcontent-%COMP%] {\n width: 40px;\n background: var(--mj-bg-surface-card);\n}\n\n\n\n.surface__strip[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n}\n.surface__strip-icon[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.surface__toggle[_ngcontent-%COMP%] {\n width: 24px;\n height: 24px;\n display: grid;\n place-items: center;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n color: var(--mj-text-muted);\n cursor: pointer;\n flex-shrink: 0;\n font-size: 10px;\n transition: color 140ms ease, border-color 140ms ease;\n}\n.surface__toggle[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n\n\n.surface-tabs[_ngcontent-%COMP%] {\n display: flex;\n align-items: stretch;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.surface-tabs[_ngcontent-%COMP%]::-webkit-scrollbar {\n display: none;\n}\n\n\n\n\n\n\n.s-tab[_ngcontent-%COMP%] {\n padding: 10px 14px;\n border: 0;\n background: transparent;\n cursor: pointer;\n font-family: inherit;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n display: inline-flex;\n align-items: center;\n gap: 7px;\n border-bottom: 3px solid transparent;\n transition: color 150ms ease, border-color 150ms ease, background 150ms ease;\n white-space: nowrap;\n flex: 0 1 auto;\n min-width: calc(11px + 7px + 8ch + 28px); \n\n}\n.s-tab[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n flex-shrink: 0;\n}\n.s-tab__title[_ngcontent-%COMP%] {\n max-width: 24ch;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n}\n.s-tab[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-secondary);\n}\n.s-tab--active[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n border-bottom-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n \n\n flex-shrink: 0;\n}\n\n\n\n.s-tab--flash[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_tab-flash 1.4s ease;\n}\n@keyframes _ngcontent-%COMP%_tab-flash {\n 0% {\n background: color-mix(in srgb, var(--sage) 28%, transparent);\n border-bottom-color: var(--sage);\n color: var(--mj-text-primary);\n }\n 60% {\n background: color-mix(in srgb, var(--sage) 16%, transparent);\n border-bottom-color: var(--sage);\n }\n 100% {\n background: var(--mj-bg-surface);\n }\n}\n\n\n\n\n.s-tab--unseen[_ngcontent-%COMP%] {\n color: var(--mj-color-violet-300, var(--mj-text-secondary));\n animation: _ngcontent-%COMP%_tab-unseen 2.2s ease-in-out infinite;\n}\n@keyframes _ngcontent-%COMP%_tab-unseen {\n 0%, 100% { background: color-mix(in srgb, var(--sage) 6%, transparent); }\n 50% { background: color-mix(in srgb, var(--sage) 18%, transparent); }\n}\n.s-tab__unseen-dot[_ngcontent-%COMP%] {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n flex-shrink: 0;\n background: var(--sage);\n box-shadow: 0 0 6px var(--sage);\n}\n\n.tabs-end[_ngcontent-%COMP%] {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 0 8px;\n flex-shrink: 0;\n}\n\n\n\n.s-pane[_ngcontent-%COMP%] {\n display: none;\n flex: 1;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n \n\n position: relative;\n}\n.s-pane--active[_ngcontent-%COMP%] {\n display: flex;\n}\n\n\n\n.artifact-pane[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.artifact-pane[_ngcontent-%COMP%] > *[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n}\n\n\n\n.channel-placeholder[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n color: var(--mj-text-muted);\n font-size: 12.5px;\n font-weight: 600;\n padding: 24px;\n text-align: center;\n}\n.channel-placeholder[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 22px;\n color: var(--sage);\n opacity: 0.7;\n}\n\n\n\n@media (max-width: 860px) {\n .surface[_ngcontent-%COMP%] {\n display: none;\n }\n}"] });
470
+ }
471
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RealtimeSurfaceTabsComponent, [{
472
+ type: Component,
473
+ args: [{ standalone: true, selector: 'mj-realtime-surface-tabs', imports: [
474
+ CommonModule, ArtifactsModule, RealtimeActivityRailComponent, RealtimeChannelPaneComponent,
475
+ ChannelOnboardingPanelComponent
476
+ ], template: "<!-- SIZING IS EXTERNAL: the overlay hosts this panel inside a fixed-width flex item and\n owns its width (drag, persistence, default tiers) \u2014 the panel just fills it. -->\n<aside class=\"surface\"\n [class.surface--collapsed]=\"Collapsed\"\n [class.surface--fill]=\"Fill && !Collapsed\"\n aria-label=\"Session surfaces\">\n\n @if (Collapsed) {\n <!-- Slim strip: the panel's collapse target (lifted from the old rail behavior) -->\n <div class=\"surface__strip\">\n <button type=\"button\"\n class=\"surface__toggle\"\n (click)=\"ToggleCollapsed()\"\n title=\"Show session panel\"\n aria-expanded=\"false\">\n <i class=\"fa-solid fa-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <i class=\"fa-solid fa-list-check surface__strip-icon\" aria-hidden=\"true\" title=\"Session activity\"></i>\n </div>\n } @else {\n <!-- Tab strip: Activity | artifact tabs\u2026 | channel tabs\u2026 -->\n <div class=\"surface-tabs\" role=\"tablist\">\n @for (tab of Model.Tabs; track TrackTab($index, tab)) {\n <button type=\"button\"\n class=\"s-tab\"\n role=\"tab\"\n [class.s-tab--active]=\"tab.Key === Model.ActiveKey\"\n [class.s-tab--flash]=\"tab.Key === Model.FlashKey\"\n [class.s-tab--unseen]=\"Model.IsUnseen(tab.Key)\"\n [attr.aria-selected]=\"tab.Key === Model.ActiveKey\"\n [title]=\"Model.IsUnseen(tab.Key) ? tab.Title + ' (new)' : tab.Title\"\n (click)=\"Model.Focus(tab.Key)\">\n <i [class]=\"tab.Icon\" aria-hidden=\"true\"></i>\n <span class=\"s-tab__title\">{{ tab.Title }}</span>\n @if (Model.IsUnseen(tab.Key)) {\n <span class=\"s-tab__unseen-dot\" aria-hidden=\"true\"></span>\n }\n </button>\n }\n <div class=\"tabs-end\">\n <button type=\"button\"\n class=\"surface__toggle\"\n (click)=\"ToggleCollapsed()\"\n title=\"Hide session panel\"\n aria-expanded=\"true\">\n <i class=\"fa-solid fa-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n <!-- Panes: ALL kept alive; only the active one is displayed (mirrors the mockup) -->\n @for (tab of Model.Tabs; track TrackTab($index, tab)) {\n <div class=\"s-pane\" [class.s-pane--active]=\"tab.Key === Model.ActiveKey\" role=\"tabpanel\">\n @switch (tab.Kind) {\n @case ('activity') {\n <mj-realtime-activity-rail\n [State]=\"State\"\n [DevMode]=\"DevMode\"\n [Embedded]=\"true\"\n (OpenRunRequested)=\"OpenRunRequested.emit($event)\"\n (OpenArtifactRequested)=\"FocusArtifact($event)\">\n </mj-realtime-activity-rail>\n }\n @case ('artifact') {\n @if (tab.Data?.Artifact; as artifact) {\n <div class=\"artifact-pane\">\n <mj-artifact-viewer-panel\n [artifactId]=\"artifact.ArtifactID\"\n [currentUser]=\"CurrentUser!\"\n [environmentId]=\"EnvironmentID\"\n [showHeader]=\"false\"\n [showTabs]=\"false\"\n [showSaveToCollection]=\"false\"\n [showCloseButton]=\"false\"\n [showMaximizeButton]=\"false\">\n </mj-artifact-viewer-panel>\n </div>\n }\n }\n @case ('channel') {\n @if (tab.Data?.Plugin; as plugin) {\n <!-- Registry-resolved channel: the pane host creates the plugin's surface\n component dynamically and hands the instance back for binding -->\n <mj-realtime-channel-pane [Plugin]=\"plugin\"></mj-realtime-channel-pane>\n <!-- First-run intro: overlaid over the active channel pane the first time this user\n opens it; the pane host renders display:none + a sibling surface, so the overlay\n sits here (inside the positioned .s-pane) rather than inside the pane host. -->\n @if (tab.Key === Model.ActiveKey && OnboardingContent) {\n <mj-channel-onboarding-panel\n [Content]=\"OnboardingContent\"\n (Dismissed)=\"DismissOnboarding()\">\n </mj-channel-onboarding-panel>\n }\n } @else if (tab.Data?.Content; as content) {\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n } @else {\n <!-- Placeholder until the channel registration supplies a plugin / template -->\n <div class=\"channel-placeholder\" role=\"status\">\n <i class=\"fa-solid fa-chalkboard\" aria-hidden=\"true\"></i>\n <span>{{ tab.Title }} coming online\u2026</span>\n </div>\n }\n }\n }\n </div>\n }\n }\n</aside>\n", styles: [":host {\n display: block;\n height: 100%;\n /* sage / delegated-agent accent \u2014 same palette the rail + delegation cards use */\n --sage: var(--mj-color-violet-400, #a78bfa);\n --sage-bg: color-mix(in srgb, var(--mj-color-violet-400, #a78bfa) 10%, transparent);\n --sage-border: color-mix(in srgb, var(--mj-color-violet-400, #a78bfa) 38%, transparent);\n}\n\n/* The surface panel fills the overlay's `.call-panel` flex item \u2014 the overlay owns\n the width (user drag via the resize handle, persisted per-user, default tiers). */\n.surface {\n position: relative;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n}\n/* Board-focus fill: the panel owns the whole overlay (the main call column is hidden). */\n.surface--fill {\n width: 100%;\n border-left: none;\n}\n.surface--collapsed {\n width: 40px;\n background: var(--mj-bg-surface-card);\n}\n\n/* ---------- collapsed slim strip ---------- */\n.surface__strip {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n}\n.surface__strip-icon {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.surface__toggle {\n width: 24px;\n height: 24px;\n display: grid;\n place-items: center;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n color: var(--mj-text-muted);\n cursor: pointer;\n flex-shrink: 0;\n font-size: 10px;\n transition: color 140ms ease, border-color 140ms ease;\n}\n.surface__toggle:hover {\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n/* ---------- tab strip (mirrors the whiteboard mockup's .surface-tabs) ---------- */\n.surface-tabs {\n display: flex;\n align-items: stretch;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.surface-tabs::-webkit-scrollbar {\n display: none;\n}\n\n/* Tabs keep their natural width while there's room; when space gets genuinely tight\n they shrink (ellipsizing their titles) down to a readable floor (icon + ~8ch).\n The ACTIVE tab never shrinks \u2014 inactive tabs give way first. Only when everything\n is at its floor does the strip actually overflow (horizontal scroll). */\n.s-tab {\n padding: 10px 14px;\n border: 0;\n background: transparent;\n cursor: pointer;\n font-family: inherit;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n display: inline-flex;\n align-items: center;\n gap: 7px;\n border-bottom: 3px solid transparent;\n transition: color 150ms ease, border-color 150ms ease, background 150ms ease;\n white-space: nowrap;\n flex: 0 1 auto;\n min-width: calc(11px + 7px + 8ch + 28px); /* icon + gap + ~8ch + padding */\n}\n.s-tab i {\n font-size: 11px;\n flex-shrink: 0;\n}\n.s-tab__title {\n max-width: 24ch;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n}\n.s-tab:hover {\n color: var(--mj-text-secondary);\n}\n.s-tab--active {\n color: var(--mj-text-primary);\n border-bottom-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n /* the active tab stays fully readable \u2014 others shrink first */\n flex-shrink: 0;\n}\n\n/* Brief violet flash for a just-arrived artifact tab (per the mockup's accent) */\n.s-tab--flash {\n animation: tab-flash 1.4s ease;\n}\n@keyframes tab-flash {\n 0% {\n background: color-mix(in srgb, var(--sage) 28%, transparent);\n border-bottom-color: var(--sage);\n color: var(--mj-text-primary);\n }\n 60% {\n background: color-mix(in srgb, var(--sage) 16%, transparent);\n border-bottom-color: var(--sage);\n }\n 100% {\n background: var(--mj-bg-surface);\n }\n}\n\n/* PERSISTENT unseen glow: content arrived without stealing focus (e.g. a finished\n artifact) \u2014 the tab breathes violet until the user visits it. */\n.s-tab--unseen {\n color: var(--mj-color-violet-300, var(--mj-text-secondary));\n animation: tab-unseen 2.2s ease-in-out infinite;\n}\n@keyframes tab-unseen {\n 0%, 100% { background: color-mix(in srgb, var(--sage) 6%, transparent); }\n 50% { background: color-mix(in srgb, var(--sage) 18%, transparent); }\n}\n.s-tab__unseen-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n flex-shrink: 0;\n background: var(--sage);\n box-shadow: 0 0 6px var(--sage);\n}\n\n.tabs-end {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 0 8px;\n flex-shrink: 0;\n}\n\n/* ---------- panes: all alive, only the active one displayed ---------- */\n.s-pane {\n display: none;\n flex: 1;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n /* Positioning context for the first-run channel-onboarding overlay (absolute, inset:0). */\n position: relative;\n}\n.s-pane--active {\n display: flex;\n}\n\n/* Read-only embedded artifact viewer fills the pane */\n.artifact-pane {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.artifact-pane > * {\n flex: 1;\n min-height: 0;\n}\n\n/* Channel slot placeholder (\"Whiteboard coming online\u2026\") */\n.channel-placeholder {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n color: var(--mj-text-muted);\n font-size: 12.5px;\n font-weight: 600;\n padding: 24px;\n text-align: center;\n}\n.channel-placeholder i {\n font-size: 22px;\n color: var(--sage);\n opacity: 0.7;\n}\n\n/* On narrow widths the panel stacks out of the way (overlay falls back to single column) */\n@media (max-width: 860px) {\n .surface {\n display: none;\n }\n}\n"] }]
477
+ }], null, { State: [{
478
+ type: Input,
479
+ args: [{ required: true }]
480
+ }], DevMode: [{
481
+ type: Input
482
+ }], Fill: [{
483
+ type: Input
484
+ }], CurrentUser: [{
485
+ type: Input
486
+ }], EnvironmentID: [{
487
+ type: Input
488
+ }], OpenRunRequested: [{
489
+ type: Output
490
+ }], CollapsedChange: [{
491
+ type: Output
492
+ }], WideChanged: [{
493
+ type: Output
494
+ }] }); })();
495
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RealtimeSurfaceTabsComponent, { className: "RealtimeSurfaceTabsComponent", filePath: "src/lib/components/realtime/realtime-surface-tabs.component.ts", lineNumber: 66 }); })();
496
+ //# sourceMappingURL=realtime-surface-tabs.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-surface-tabs.component.js","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-surface-tabs.component.ts","../../../../src/lib/components/realtime/realtime-surface-tabs.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, EventEmitter, Input, Output, OnInit, OnDestroy, ChangeDetectorRef, inject\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Subscription } from 'rxjs';\nimport { UserInfo } from '@memberjunction/core';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { ArtifactsModule } from '@memberjunction/ng-artifacts';\nimport { RealtimeSessionState } from './realtime-session-state';\nimport { RealtimeActivityRailComponent } from './realtime-activity-rail.component';\nimport { RealtimeChannelPaneComponent } from './channels/realtime-channel-pane.component';\nimport { ChannelOnboardingPanelComponent } from './channels/channel-onboarding-panel.component';\nimport { ChannelOnboardingDetails } from './channels/base-realtime-channel-client';\nimport {\n RealtimeSurfaceTabsModel, RealtimeSurfaceTab, RealtimeChannelTabRegistration\n} from './realtime-surface-tabs.model';\nimport { ParsedDelegationArtifact } from '../../services/delegation-result-parser';\n\n/**\n * User-settings key (NOT localStorage — see `UserInfoEngine`) under which the per-user \"which\n * channel intros have been seen\" map is persisted: a JSON object of `{ [channelName]: true }`,\n * so the first-run onboarding for each interactive channel shows exactly once per user and\n * follows them across devices.\n */\nconst CHANNEL_ONBOARDING_SEEN_SETTING_KEY = 'mj.realtimeChannels.onboardingSeen.v1';\n\n/**\n * The call overlay's TABBED SURFACE PANEL (the right panel) — per the approved\n * `plans/realtime/mockups/whiteboard.html` mockup:\n *\n * - **Activity** — always the first tab; hosts the existing\n * {@link RealtimeActivityRailComponent} unchanged (in `Embedded` mode, since this panel\n * owns the chrome + collapse).\n * - **One tab per artifact** produced by delegated runs: when a delegation result carries\n * artifacts, a tab is auto-opened, FOCUSED, and briefly flashed violet. The pane reuses\n * the standard `mj-artifact-viewer-panel` (read-only embed: no header/tabs/actions),\n * loading by ArtifactID — the latest version, i.e. the one the run just produced.\n * - **Channel tabs** — rendered ONLY once a channel registers via\n * {@link RegisterChannelTab}. The overlay shell registers one per registry-resolved\n * {@link BaseRealtimeChannelClient} plugin; the pane creates the plugin's surface\n * component dynamically (via `mj-realtime-channel-pane`). Until a registration supplies\n * a plugin (or legacy template), the pane shows the \"coming online…\" placeholder;\n * re-registering the same key swaps the real surface in.\n *\n * Panes are kept ALIVE while hidden (CSS `display:none`, mirroring the mockup's\n * `.s-pane.active`) so switching tabs never reloads an artifact or resets the rail.\n * The whole panel collapses to a slim strip via the chevron at the tab strip's end —\n * the collapse-to-strip behavior that used to live on the rail, lifted to the panel.\n *\n * SIZING IS EXTERNAL: the overlay shell hosts this panel in a fixed-width flex item\n * and owns the width (user drag via the resize handle + persisted preference +\n * default tiers). This panel just fills it and REPORTS the layout signals the shell sizes from:\n * {@link CollapsedChange} (slim-strip toggle) and {@link WideChanged} (a content tab\n * is focused → the default width tier widens).\n */\n@Component({\n standalone: true,\n selector: 'mj-realtime-surface-tabs',\n imports: [\n CommonModule, ArtifactsModule, RealtimeActivityRailComponent, RealtimeChannelPaneComponent,\n ChannelOnboardingPanelComponent\n ],\n templateUrl: './realtime-surface-tabs.component.html',\n styleUrl: './realtime-surface-tabs.component.css'\n})\nexport class RealtimeSurfaceTabsComponent implements OnInit, OnDestroy {\n /** How long a just-arrived tab keeps its violet flash highlight. */\n private static readonly FlashDurationMs = 1400;\n\n /** Shared live-session state, owned by the overlay shell (feeds the Activity rail). */\n @Input({ required: true }) State!: RealtimeSessionState;\n\n /** Whether developer affordances (\"Open run\" links) are revealed (gear-gated). */\n @Input() DevMode = false;\n\n /**\n * FILL presentation: the panel stretches to the overlay's full width (the board-focus\n * layout, where the main call column is hidden and a channel surface owns the screen).\n * Bound by the overlay shell; overrides the normal / wide width tiers.\n */\n @Input() Fill = false;\n\n /** The signed-in user, threaded to the artifact viewer panel. */\n @Input() CurrentUser: UserInfo | null = null;\n\n /** The active environment id, threaded to the artifact viewer panel. */\n @Input() EnvironmentID = '';\n\n /** Re-emitted from the Activity rail's dev \"Open run\" links. */\n @Output() OpenRunRequested = new EventEmitter<string>();\n\n /**\n * Emitted when the panel toggles between expanded and the slim collapsed strip —\n * the overlay shell resizes this panel's split area to the strip width.\n */\n @Output() CollapsedChange = new EventEmitter<boolean>();\n\n /**\n * Emitted when {@link IsWide} flips (a content tab gained / lost focus) — the\n * overlay shell widens the panel's DEFAULT split-area size while wide (only when\n * the user has never dragged an explicit width).\n */\n @Output() WideChanged = new EventEmitter<boolean>();\n\n /** The panel's tab state (add / focus / dedupe / flash) — see the model for the rules. */\n public readonly Model = new RealtimeSurfaceTabsModel();\n\n /** Whether the panel is collapsed to its slim strip. */\n public Collapsed = false;\n\n /** Artifact version ids already turned into tabs (guards the State rescan). */\n private tabbedVersionIds = new Set<string>();\n private flashTimer: ReturnType<typeof setTimeout> | null = null;\n private subs: Subscription[] = [];\n private lastWide = false;\n private cdr = inject(ChangeDetectorRef);\n\n /**\n * The channel whose first-run intro is currently being shown (its `ChannelName`), or `null`\n * when no intro is up. Set when the user opens a channel tab they've never seen the intro\n * for; cleared on dismiss. Only one intro shows at a time (the active channel's).\n */\n private onboardingChannelName: string | null = null;\n /** The intro content for {@link onboardingChannelName}, mirrored for the template binding. */\n public OnboardingContent: ChannelOnboardingDetails | null = null;\n\n /** The currently focused tab. */\n public get ActiveTab(): RealtimeSurfaceTab {\n return this.Model.ActiveTab;\n }\n\n /** Wide presentation when a content tab (artifact / channel) is focused. */\n public get IsWide(): boolean {\n return !this.Collapsed && this.ActiveTab.Kind !== 'activity';\n }\n\n ngOnInit(): void {\n this.subs.push(\n this.State.Changed$.subscribe(() => this.onStateChanged()),\n this.Model.Changed$.subscribe(() => this.onModelChanged())\n );\n this.syncArtifactTabs();\n }\n\n ngOnDestroy(): void {\n for (const s of this.subs) {\n s.unsubscribe();\n }\n this.subs = [];\n if (this.flashTimer) {\n clearTimeout(this.flashTimer);\n this.flashTimer = null;\n }\n }\n\n /** Toggle the panel between expanded and slim-collapsed. */\n public ToggleCollapsed(): void {\n this.setCollapsed(!this.Collapsed);\n }\n\n /** Collapse-state transitions funnel through here so the shell always hears about them. */\n private setCollapsed(value: boolean): void {\n if (this.Collapsed !== value) {\n this.Collapsed = value;\n this.CollapsedChange.emit(value);\n this.syncWide();\n }\n }\n\n /** Emits {@link WideChanged} when the wide tier flips (content tab focus / collapse). */\n private syncWide(): void {\n const wide = this.IsWide;\n if (wide !== this.lastWide) {\n this.lastWide = wide;\n this.WideChanged.emit(wide);\n }\n }\n\n /** track fn for the @for over tabs. */\n public TrackTab(index: number, tab: RealtimeSurfaceTab): string {\n return tab.Key;\n }\n\n /**\n * Focuses (opening if needed) the tab for an artifact — the \"View →\" affordance target\n * on done delegation cards and rail entries. Expands the panel if it was collapsed.\n */\n public FocusArtifact(artifact: ParsedDelegationArtifact): void {\n this.tabbedVersionIds.add(artifact.ArtifactVersionID);\n this.Model.OpenArtifactTab(artifact, true);\n this.setCollapsed(false);\n this.cdr.markForCheck();\n }\n\n /**\n * Registers (or updates) an interactive-channel tab — one per registry-resolved channel\n * plugin, forwarded from `RealtimeSessionOverlayComponent.RegisterChannelTab`.\n */\n public RegisterChannelTab(registration: RealtimeChannelTabRegistration): void {\n // Microtask defer: the overlay forwards this while handling agent/channel activity, which can\n // land mid change-detection. Adding a tab to Model.Tabs synchronously then trips NG0100 on the\n // tab-strip bindings (s-tab--active). A microtask lands the mutation in a fresh CD turn —\n // imperceptible for an async reveal, and ordered with any follow-on RevealChannel.\n Promise.resolve().then(() => {\n this.Model.RegisterChannelTab(registration);\n this.cdr.markForCheck();\n });\n }\n\n /**\n * AUTO-REVEALS a channel surface the moment the agent first acts on it: expands the\n * panel if collapsed, focuses the channel's tab and flashes it violet — so the user\n * discovers the whiteboard (or any channel) exists the instant it comes alive,\n * instead of having to find the tab themselves. No-op for unknown keys.\n */\n public RevealChannel(key: string): void {\n // Microtask defer (same NG0100 reason as RegisterChannelTab): the agent-activity reveal mutates\n // ActiveKey/FlashKey, which feed the tab-strip class bindings; doing it mid-CD trips the\n // ExpressionChanged check. Deferring lands it in a fresh CD turn and stays ordered after any\n // RegisterChannelTab queued just before it.\n Promise.resolve().then(() => {\n this.setCollapsed(false);\n this.Model.Focus(key);\n this.Model.FlashTab(key);\n this.cdr.markForCheck();\n });\n }\n\n /**\n * Removes a tab from the panel (Activity is irremovable; focus falls back to Activity —\n * see {@link RealtimeSurfaceTabsModel.RemoveTab}). Used by the overlay shell on a\n * review→live continuation whose live channel set resolved WITHOUT the channel a stale\n * review tab represents (e.g. no Whiteboard channel → drop the read-only review board tab).\n *\n * @returns `true` when a tab was removed.\n */\n public RemoveTab(key: string): boolean {\n const removed = this.Model.RemoveTab(key);\n if (removed) {\n this.cdr.markForCheck();\n }\n return removed;\n }\n\n /**\n * Registers an artifact tab WITHOUT stealing focus (default) — the SESSION REVIEW /\n * resume-carryover path, where a reviewed session's (and its prior legs') history\n * artifacts are surfaced as tabs the user can visit, as opposed to the live\n * auto-open-on-arrival behavior. Idempotent per artifact version; the registered tab\n * survives the review→live transition (tabs are never cleared on resume).\n */\n public RegisterArtifactTab(artifact: ParsedDelegationArtifact, focus: boolean = false): void {\n this.tabbedVersionIds.add(artifact.ArtifactVersionID);\n // History carryover is context, not news — never marked unseen (no glow).\n this.Model.OpenArtifactTab(artifact, focus, false);\n this.cdr.markForCheck();\n }\n\n /** On session-state changes: open tabs for newly-arrived artifacts, then re-render. */\n private onStateChanged(): void {\n this.syncArtifactTabs();\n this.cdr.markForCheck();\n }\n\n /** On model changes: schedule the flash clear, report a wide-tier flip, re-render. */\n private onModelChanged(): void {\n this.scheduleFlashClear();\n this.syncWide();\n this.evaluateOnboarding();\n this.cdr.markForCheck();\n }\n\n /**\n * Decides whether the first-run channel intro should be visible for the ACTIVE tab: shows it\n * the first time the user opens (focuses) a channel tab whose plugin supplies onboarding and\n * which this user hasn't dismissed before. Re-runs on every model change so switching away\n * from a channel tab tears the intro down (only the active channel's intro is ever up).\n */\n private evaluateOnboarding(): void {\n const tab = this.ActiveTab;\n const plugin = tab.Kind === 'channel' ? tab.Data?.Plugin ?? null : null;\n const details = plugin?.GetOnboardingDetails() ?? null;\n if (!plugin || !details || this.HasSeenOnboarding(plugin.ChannelName)) {\n this.onboardingChannelName = null;\n this.OnboardingContent = null;\n return;\n }\n this.onboardingChannelName = plugin.ChannelName;\n this.OnboardingContent = details;\n }\n\n /**\n * Dismisses the current channel intro: marks that channel seen for this user (persisted via\n * `UserInfoEngine`, debounced — NOT localStorage) and hides the panel so it never re-appears.\n */\n public DismissOnboarding(): void {\n const channelName = this.onboardingChannelName;\n this.onboardingChannelName = null;\n this.OnboardingContent = null;\n if (channelName) {\n this.markOnboardingSeen(channelName);\n }\n this.cdr.markForCheck();\n }\n\n /** Reads the per-user seen-map and reports whether this channel's intro has been dismissed. */\n private HasSeenOnboarding(channelName: string): boolean {\n return this.readOnboardingSeen()[channelName] === true;\n }\n\n /** Persists `channelName` into the per-user seen-map (merge + debounced save). */\n private markOnboardingSeen(channelName: string): void {\n const map = this.readOnboardingSeen();\n if (map[channelName] === true) {\n return;\n }\n map[channelName] = true;\n UserInfoEngine.Instance.SetSettingDebounced(CHANNEL_ONBOARDING_SEEN_SETTING_KEY, JSON.stringify(map));\n }\n\n /** Reads + parses the per-user seen-map setting (tolerant: malformed / unset → empty map). */\n private readOnboardingSeen(): Record<string, boolean> {\n const raw = UserInfoEngine.Instance.GetSetting(CHANNEL_ONBOARDING_SEEN_SETTING_KEY);\n if (!raw) {\n return {};\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, boolean>)\n : {};\n } catch {\n return {};\n }\n }\n\n /**\n * Scans the session's delegation cards for artifacts that don't have a tab yet and\n * adds one per artifact — UNFOCUSED, with the brief violet flash plus the persistent\n * unseen glow. Per product direction a finished artifact never steals the screen\n * (only the agent's first channel activity auto-reveals); the glowing tab is the\n * invitation, the user opens it when they want it.\n */\n private syncArtifactTabs(): void {\n for (const card of this.State.Cards) {\n if (!card.Done || !card.Artifacts) {\n continue;\n }\n for (const artifact of card.Artifacts) {\n if (this.tabbedVersionIds.has(artifact.ArtifactVersionID)) {\n continue;\n }\n this.tabbedVersionIds.add(artifact.ArtifactVersionID);\n this.Model.OpenArtifactTab(artifact, false);\n }\n }\n }\n\n /**\n * Focuses the FIRST tab in the strip — channels lead, so a Details peek lands on the\n * marquee surface (e.g. the Whiteboard) when one exists, else on Activity.\n */\n public FocusFirstTab(): void {\n const first = this.Model.Tabs[0];\n if (first) {\n this.Model.Focus(first.Key);\n this.cdr.markForCheck();\n }\n }\n\n /** Clears the model's flash highlight after a beat (one timer; latest flash wins). */\n private scheduleFlashClear(): void {\n if (this.Model.FlashKey === null) {\n return;\n }\n if (this.flashTimer) {\n clearTimeout(this.flashTimer);\n }\n this.flashTimer = setTimeout(() => {\n this.flashTimer = null;\n this.Model.ClearFlash();\n }, RealtimeSurfaceTabsComponent.FlashDurationMs);\n }\n}\n","<!-- SIZING IS EXTERNAL: the overlay hosts this panel inside a fixed-width flex item and\n owns its width (drag, persistence, default tiers) — the panel just fills it. -->\n<aside class=\"surface\"\n [class.surface--collapsed]=\"Collapsed\"\n [class.surface--fill]=\"Fill && !Collapsed\"\n aria-label=\"Session surfaces\">\n\n @if (Collapsed) {\n <!-- Slim strip: the panel's collapse target (lifted from the old rail behavior) -->\n <div class=\"surface__strip\">\n <button type=\"button\"\n class=\"surface__toggle\"\n (click)=\"ToggleCollapsed()\"\n title=\"Show session panel\"\n aria-expanded=\"false\">\n <i class=\"fa-solid fa-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <i class=\"fa-solid fa-list-check surface__strip-icon\" aria-hidden=\"true\" title=\"Session activity\"></i>\n </div>\n } @else {\n <!-- Tab strip: Activity | artifact tabs… | channel tabs… -->\n <div class=\"surface-tabs\" role=\"tablist\">\n @for (tab of Model.Tabs; track TrackTab($index, tab)) {\n <button type=\"button\"\n class=\"s-tab\"\n role=\"tab\"\n [class.s-tab--active]=\"tab.Key === Model.ActiveKey\"\n [class.s-tab--flash]=\"tab.Key === Model.FlashKey\"\n [class.s-tab--unseen]=\"Model.IsUnseen(tab.Key)\"\n [attr.aria-selected]=\"tab.Key === Model.ActiveKey\"\n [title]=\"Model.IsUnseen(tab.Key) ? tab.Title + ' (new)' : tab.Title\"\n (click)=\"Model.Focus(tab.Key)\">\n <i [class]=\"tab.Icon\" aria-hidden=\"true\"></i>\n <span class=\"s-tab__title\">{{ tab.Title }}</span>\n @if (Model.IsUnseen(tab.Key)) {\n <span class=\"s-tab__unseen-dot\" aria-hidden=\"true\"></span>\n }\n </button>\n }\n <div class=\"tabs-end\">\n <button type=\"button\"\n class=\"surface__toggle\"\n (click)=\"ToggleCollapsed()\"\n title=\"Hide session panel\"\n aria-expanded=\"true\">\n <i class=\"fa-solid fa-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n <!-- Panes: ALL kept alive; only the active one is displayed (mirrors the mockup) -->\n @for (tab of Model.Tabs; track TrackTab($index, tab)) {\n <div class=\"s-pane\" [class.s-pane--active]=\"tab.Key === Model.ActiveKey\" role=\"tabpanel\">\n @switch (tab.Kind) {\n @case ('activity') {\n <mj-realtime-activity-rail\n [State]=\"State\"\n [DevMode]=\"DevMode\"\n [Embedded]=\"true\"\n (OpenRunRequested)=\"OpenRunRequested.emit($event)\"\n (OpenArtifactRequested)=\"FocusArtifact($event)\">\n </mj-realtime-activity-rail>\n }\n @case ('artifact') {\n @if (tab.Data?.Artifact; as artifact) {\n <div class=\"artifact-pane\">\n <mj-artifact-viewer-panel\n [artifactId]=\"artifact.ArtifactID\"\n [currentUser]=\"CurrentUser!\"\n [environmentId]=\"EnvironmentID\"\n [showHeader]=\"false\"\n [showTabs]=\"false\"\n [showSaveToCollection]=\"false\"\n [showCloseButton]=\"false\"\n [showMaximizeButton]=\"false\">\n </mj-artifact-viewer-panel>\n </div>\n }\n }\n @case ('channel') {\n @if (tab.Data?.Plugin; as plugin) {\n <!-- Registry-resolved channel: the pane host creates the plugin's surface\n component dynamically and hands the instance back for binding -->\n <mj-realtime-channel-pane [Plugin]=\"plugin\"></mj-realtime-channel-pane>\n <!-- First-run intro: overlaid over the active channel pane the first time this user\n opens it; the pane host renders display:none + a sibling surface, so the overlay\n sits here (inside the positioned .s-pane) rather than inside the pane host. -->\n @if (tab.Key === Model.ActiveKey && OnboardingContent) {\n <mj-channel-onboarding-panel\n [Content]=\"OnboardingContent\"\n (Dismissed)=\"DismissOnboarding()\">\n </mj-channel-onboarding-panel>\n }\n } @else if (tab.Data?.Content; as content) {\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n } @else {\n <!-- Placeholder until the channel registration supplies a plugin / template -->\n <div class=\"channel-placeholder\" role=\"status\">\n <i class=\"fa-solid fa-chalkboard\" aria-hidden=\"true\"></i>\n <span>{{ tab.Title }} coming online…</span>\n </div>\n }\n }\n }\n </div>\n }\n }\n</aside>\n"]}