@athenaintel/react 0.48.0 → 0.52.1

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 (255) hide show
  1. package/README.md +68 -10
  2. package/dist/agent-runtime/src/call-recap.cjs +319 -0
  3. package/dist/agent-runtime/src/call-recap.cjs.map +1 -0
  4. package/dist/agent-runtime/src/call-recap.d.ts +142 -0
  5. package/dist/agent-runtime/src/call-recap.js +319 -0
  6. package/dist/agent-runtime/src/call-recap.js.map +1 -0
  7. package/dist/agent-runtime/src/chat-outbox-store.cjs +1 -0
  8. package/dist/agent-runtime/src/chat-outbox-store.cjs.map +1 -1
  9. package/dist/agent-runtime/src/chat-outbox-store.d.ts +7 -0
  10. package/dist/agent-runtime/src/chat-outbox-store.js +1 -0
  11. package/dist/agent-runtime/src/chat-outbox-store.js.map +1 -1
  12. package/dist/agent-runtime/src/chat-outbox.cjs +46 -5
  13. package/dist/agent-runtime/src/chat-outbox.cjs.map +1 -1
  14. package/dist/agent-runtime/src/chat-outbox.d.ts +29 -0
  15. package/dist/agent-runtime/src/chat-outbox.js +46 -5
  16. package/dist/agent-runtime/src/chat-outbox.js.map +1 -1
  17. package/dist/agent-runtime/src/conversation-index.cjs.map +1 -1
  18. package/dist/agent-runtime/src/conversation-index.d.ts +7 -0
  19. package/dist/agent-runtime/src/conversation-index.js.map +1 -1
  20. package/dist/agent-runtime/src/conversation-list.cjs +2 -1
  21. package/dist/agent-runtime/src/conversation-list.cjs.map +1 -1
  22. package/dist/agent-runtime/src/conversation-list.d.ts +19 -3
  23. package/dist/agent-runtime/src/conversation-list.js +2 -1
  24. package/dist/agent-runtime/src/conversation-list.js.map +1 -1
  25. package/dist/agent-runtime/src/desktop-handoff.d.ts +10 -0
  26. package/dist/agent-runtime/src/edit-command.cjs +21 -0
  27. package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
  28. package/dist/agent-runtime/src/edit-command.d.ts +8 -0
  29. package/dist/agent-runtime/src/edit-command.js +21 -0
  30. package/dist/agent-runtime/src/edit-command.js.map +1 -1
  31. package/dist/agent-runtime/src/handoff-arrival.cjs +19 -0
  32. package/dist/agent-runtime/src/handoff-arrival.cjs.map +1 -1
  33. package/dist/agent-runtime/src/handoff-arrival.d.ts +18 -3
  34. package/dist/agent-runtime/src/handoff-arrival.js +20 -1
  35. package/dist/agent-runtime/src/handoff-arrival.js.map +1 -1
  36. package/dist/agent-runtime/src/handoff-cursor-id.cjs +6 -0
  37. package/dist/agent-runtime/src/handoff-cursor-id.cjs.map +1 -0
  38. package/dist/agent-runtime/src/handoff-cursor-id.d.ts +12 -0
  39. package/dist/agent-runtime/src/handoff-cursor-id.js +6 -0
  40. package/dist/agent-runtime/src/handoff-cursor-id.js.map +1 -0
  41. package/dist/agent-runtime/src/handoff-cursor.cjs +88 -0
  42. package/dist/agent-runtime/src/handoff-cursor.cjs.map +1 -0
  43. package/dist/agent-runtime/src/handoff-cursor.d.ts +120 -0
  44. package/dist/agent-runtime/src/handoff-cursor.js +88 -0
  45. package/dist/agent-runtime/src/handoff-cursor.js.map +1 -0
  46. package/dist/agent-runtime/src/index.d.ts +9 -7
  47. package/dist/agent-runtime/src/interrupts.cjs +28 -1
  48. package/dist/agent-runtime/src/interrupts.cjs.map +1 -1
  49. package/dist/agent-runtime/src/interrupts.d.ts +21 -1
  50. package/dist/agent-runtime/src/interrupts.js +28 -1
  51. package/dist/agent-runtime/src/interrupts.js.map +1 -1
  52. package/dist/agent-runtime/src/model-context.cjs +3 -2
  53. package/dist/agent-runtime/src/model-context.cjs.map +1 -1
  54. package/dist/agent-runtime/src/model-context.d.ts +6 -1
  55. package/dist/agent-runtime/src/model-context.js +3 -2
  56. package/dist/agent-runtime/src/model-context.js.map +1 -1
  57. package/dist/agent-runtime/src/runtime.d.ts +3 -2
  58. package/dist/agent-runtime/src/send-recovery.cjs +37 -3
  59. package/dist/agent-runtime/src/send-recovery.cjs.map +1 -1
  60. package/dist/agent-runtime/src/send-recovery.d.ts +19 -0
  61. package/dist/agent-runtime/src/send-recovery.js +37 -3
  62. package/dist/agent-runtime/src/send-recovery.js.map +1 -1
  63. package/dist/agent-runtime/src/thread-handoff-flow-machine.d.ts +14 -8
  64. package/dist/agent-runtime/src/thread-handoff-protocol.d.ts +227 -5
  65. package/dist/agent-runtime/src/thread-handoff.d.ts +1 -1
  66. package/dist/agent-runtime/src/use-conversation-list.cjs +3 -2
  67. package/dist/agent-runtime/src/use-conversation-list.cjs.map +1 -1
  68. package/dist/agent-runtime/src/use-conversation-list.d.ts +6 -0
  69. package/dist/agent-runtime/src/use-conversation-list.js +3 -2
  70. package/dist/agent-runtime/src/use-conversation-list.js.map +1 -1
  71. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs +21 -1
  72. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs.map +1 -1
  73. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.d.ts +8 -2
  74. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js +21 -1
  75. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js.map +1 -1
  76. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs +24 -6
  77. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
  78. package/dist/agent-runtime/src/use-deep-agent-runtime.d.ts +17 -0
  79. package/dist/agent-runtime/src/use-deep-agent-runtime.js +24 -6
  80. package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
  81. package/dist/agent-runtime/src/use-deep-agent-voice.cjs +44 -9
  82. package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -1
  83. package/dist/agent-runtime/src/use-deep-agent-voice.d.ts +34 -1
  84. package/dist/agent-runtime/src/use-deep-agent-voice.js +44 -9
  85. package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -1
  86. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +8 -3
  87. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
  88. package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +7 -0
  89. package/dist/agent-runtime/src/use-deep-agent-window-sync.js +9 -4
  90. package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
  91. package/dist/agent-runtime/src/use-thread-handoff-flow.d.ts +13 -2
  92. package/dist/agent-runtime/src/voice-earcon.cjs +139 -0
  93. package/dist/agent-runtime/src/voice-earcon.cjs.map +1 -0
  94. package/dist/agent-runtime/src/voice-earcon.d.ts +38 -0
  95. package/dist/agent-runtime/src/voice-earcon.js +139 -0
  96. package/dist/agent-runtime/src/voice-earcon.js.map +1 -0
  97. package/dist/agent-runtime/src/voice-media.cjs.map +1 -1
  98. package/dist/agent-runtime/src/voice-media.d.ts +14 -0
  99. package/dist/agent-runtime/src/voice-media.js.map +1 -1
  100. package/dist/agent-runtime/src/voice-presentation.cjs +2 -0
  101. package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -1
  102. package/dist/agent-runtime/src/voice-presentation.js +2 -0
  103. package/dist/agent-runtime/src/voice-presentation.js.map +1 -1
  104. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  105. package/dist/assets/AthenaAssetEmbed.d.ts +11 -5
  106. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  107. package/dist/assets/use-asset-embed.cjs.map +1 -1
  108. package/dist/assets/use-asset-embed.d.ts +12 -5
  109. package/dist/assets/use-asset-embed.js.map +1 -1
  110. package/dist/chat/AthenaChat.cjs +27 -12
  111. package/dist/chat/AthenaChat.cjs.map +1 -1
  112. package/dist/chat/AthenaChat.d.ts +13 -0
  113. package/dist/chat/AthenaChat.js +27 -12
  114. package/dist/chat/AthenaChat.js.map +1 -1
  115. package/dist/chat/AthenaPlanStrip.cjs +97 -0
  116. package/dist/chat/AthenaPlanStrip.cjs.map +1 -0
  117. package/dist/chat/AthenaPlanStrip.d.ts +15 -0
  118. package/dist/chat/AthenaPlanStrip.js +97 -0
  119. package/dist/chat/AthenaPlanStrip.js.map +1 -0
  120. package/dist/chat/StatewireApprovalCard.cjs +41 -6
  121. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  122. package/dist/chat/StatewireApprovalCard.d.ts +18 -1
  123. package/dist/chat/StatewireApprovalCard.js +42 -7
  124. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  125. package/dist/chat/StatewireQueuedMessages.cjs +56 -6
  126. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  127. package/dist/chat/StatewireQueuedMessages.d.ts +14 -0
  128. package/dist/chat/StatewireQueuedMessages.js +58 -8
  129. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  130. package/dist/chat/WaitingOnYouBanner.cjs +6 -2
  131. package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
  132. package/dist/chat/WaitingOnYouBanner.js +7 -3
  133. package/dist/chat/WaitingOnYouBanner.js.map +1 -1
  134. package/dist/chat/chat-voice-controls.cjs +3 -1
  135. package/dist/chat/chat-voice-controls.cjs.map +1 -1
  136. package/dist/chat/chat-voice-controls.d.ts +6 -1
  137. package/dist/chat/chat-voice-controls.js +3 -1
  138. package/dist/chat/chat-voice-controls.js.map +1 -1
  139. package/dist/chat/super-grouping/SuperGroupingCard.cjs +4 -2
  140. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  141. package/dist/chat/super-grouping/SuperGroupingCard.js +4 -2
  142. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  143. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +4 -2
  144. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
  145. package/dist/chat/super-grouping/SuperGroupingTranscript.js +4 -2
  146. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
  147. package/dist/chat/super-grouping/activity-timeline.cjs +6 -4
  148. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  149. package/dist/chat/super-grouping/activity-timeline.d.ts +9 -1
  150. package/dist/chat/super-grouping/activity-timeline.js +6 -4
  151. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  152. package/dist/chat/super-grouping/index.d.ts +1 -1
  153. package/dist/chat/super-grouping/use-earlier-steps-unloaded.cjs +17 -0
  154. package/dist/chat/super-grouping/use-earlier-steps-unloaded.cjs.map +1 -0
  155. package/dist/chat/super-grouping/use-earlier-steps-unloaded.d.ts +8 -0
  156. package/dist/chat/super-grouping/use-earlier-steps-unloaded.js +17 -0
  157. package/dist/chat/super-grouping/use-earlier-steps-unloaded.js.map +1 -0
  158. package/dist/chat-ui/index.cjs +11 -0
  159. package/dist/chat-ui/index.cjs.map +1 -1
  160. package/dist/chat-ui/index.d.ts +3 -1
  161. package/dist/chat-ui/index.js +12 -1
  162. package/dist/chat-ui/index.js.map +1 -1
  163. package/dist/chat-ui/messages/from-the-call-card.cjs +124 -0
  164. package/dist/chat-ui/messages/from-the-call-card.cjs.map +1 -0
  165. package/dist/chat-ui/messages/from-the-call-card.d.ts +17 -0
  166. package/dist/chat-ui/messages/from-the-call-card.js +124 -0
  167. package/dist/chat-ui/messages/from-the-call-card.js.map +1 -0
  168. package/dist/chat-ui/rich-text/chat-link.cjs +1 -0
  169. package/dist/chat-ui/rich-text/chat-link.cjs.map +1 -1
  170. package/dist/chat-ui/rich-text/chat-link.d.ts +4 -0
  171. package/dist/chat-ui/rich-text/chat-link.js +4 -3
  172. package/dist/chat-ui/rich-text/chat-link.js.map +1 -1
  173. package/dist/chat-ui/rich-text/chat-message-body.cjs +1 -0
  174. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
  175. package/dist/chat-ui/rich-text/chat-message-body.d.ts +12 -0
  176. package/dist/chat-ui/rich-text/chat-message-body.js +2 -1
  177. package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
  178. package/dist/chat-ui/thread/chat-thread-shell.cjs +4 -0
  179. package/dist/chat-ui/thread/chat-thread-shell.cjs.map +1 -1
  180. package/dist/chat-ui/thread/chat-thread-shell.d.ts +4 -0
  181. package/dist/chat-ui/thread/chat-thread-shell.js +4 -0
  182. package/dist/chat-ui/thread/chat-thread-shell.js.map +1 -1
  183. package/dist/chat-ui/thread/handoff-cursor.cjs +216 -0
  184. package/dist/chat-ui/thread/handoff-cursor.cjs.map +1 -0
  185. package/dist/chat-ui/thread/handoff-cursor.d.ts +57 -0
  186. package/dist/chat-ui/thread/handoff-cursor.js +216 -0
  187. package/dist/chat-ui/thread/handoff-cursor.js.map +1 -0
  188. package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -0
  189. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  190. package/dist/chat-ui/thread/statewire-history-edge.d.ts +2 -0
  191. package/dist/chat-ui/thread/statewire-history-edge.js +3 -1
  192. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  193. package/dist/collab/client.cjs +15 -3
  194. package/dist/collab/client.cjs.map +1 -1
  195. package/dist/collab/client.d.ts +4 -2
  196. package/dist/collab/client.js +16 -4
  197. package/dist/collab/client.js.map +1 -1
  198. package/dist/composer/index.d.ts +1 -1
  199. package/dist/composer/useReadComposerDraft.cjs +26 -7
  200. package/dist/composer/useReadComposerDraft.cjs.map +1 -1
  201. package/dist/composer/useReadComposerDraft.d.ts +23 -1
  202. package/dist/composer/useReadComposerDraft.js +27 -8
  203. package/dist/composer/useReadComposerDraft.js.map +1 -1
  204. package/dist/index.cjs +5 -0
  205. package/dist/index.cjs.map +1 -1
  206. package/dist/index.d.ts +5 -2
  207. package/dist/index.js +6 -1
  208. package/dist/index.js.map +1 -1
  209. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs +207 -0
  210. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs.map +1 -0
  211. package/dist/keryx-http/src/unconfirmed-update-attributions.d.ts +141 -0
  212. package/dist/keryx-http/src/unconfirmed-update-attributions.js +207 -0
  213. package/dist/keryx-http/src/unconfirmed-update-attributions.js.map +1 -0
  214. package/dist/keryx-http/src/update-attributions.cjs +34 -4
  215. package/dist/keryx-http/src/update-attributions.cjs.map +1 -1
  216. package/dist/keryx-http/src/update-attributions.d.ts +36 -4
  217. package/dist/keryx-http/src/update-attributions.js +35 -5
  218. package/dist/keryx-http/src/update-attributions.js.map +1 -1
  219. package/dist/provider/AthenaProvider.cjs +4 -0
  220. package/dist/provider/AthenaProvider.cjs.map +1 -1
  221. package/dist/provider/AthenaProvider.d.ts +11 -1
  222. package/dist/provider/AthenaProvider.js +4 -0
  223. package/dist/provider/AthenaProvider.js.map +1 -1
  224. package/dist/react/package.json.cjs +1 -1
  225. package/dist/react/package.json.js +1 -1
  226. package/dist/styles.css +1 -1
  227. package/dist/threads/api.cjs.map +1 -1
  228. package/dist/threads/api.d.ts +15 -0
  229. package/dist/threads/api.js.map +1 -1
  230. package/dist/threads/conversation-list-fetcher.cjs +13 -5
  231. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  232. package/dist/threads/conversation-list-fetcher.d.ts +4 -1
  233. package/dist/threads/conversation-list-fetcher.js +13 -5
  234. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  235. package/dist/threads/statewire-thread-list.cjs +28 -6
  236. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  237. package/dist/threads/statewire-thread-list.d.ts +20 -8
  238. package/dist/threads/statewire-thread-list.js +28 -6
  239. package/dist/threads/statewire-thread-list.js.map +1 -1
  240. package/dist/threads/use-athena-sessions.cjs +27 -5
  241. package/dist/threads/use-athena-sessions.cjs.map +1 -1
  242. package/dist/threads/use-athena-sessions.d.ts +10 -5
  243. package/dist/threads/use-athena-sessions.js +28 -6
  244. package/dist/threads/use-athena-sessions.js.map +1 -1
  245. package/dist/threads/use-rest-conversation-history.d.ts +1 -0
  246. package/dist/tools/write-todos-tool-ui.cjs +8 -0
  247. package/dist/tools/write-todos-tool-ui.cjs.map +1 -1
  248. package/dist/tools/write-todos-tool-ui.d.ts +2 -0
  249. package/dist/tools/write-todos-tool-ui.js +8 -0
  250. package/dist/tools/write-todos-tool-ui.js.map +1 -1
  251. package/dist/workbench/session-workbench-panes.cjs +1 -8
  252. package/dist/workbench/session-workbench-panes.cjs.map +1 -1
  253. package/dist/workbench/session-workbench-panes.js +2 -9
  254. package/dist/workbench/session-workbench-panes.js.map +1 -1
  255. package/package.json +2 -2
@@ -10,6 +10,8 @@ const messageActionsConfig = require("../messages/message-actions-config.cjs");
10
10
  const userEditComposer = require("../messages/user-edit-composer.cjs");
11
11
  const runningIndicator = require("../messages/running-indicator.cjs");
12
12
  const handoffArrivalDivider = require("../messages/handoff-arrival-divider.cjs");
13
+ const fromTheCallCard = require("../messages/from-the-call-card.cjs");
14
+ const statewireHistoryEdge = require("./statewire-history-edge.cjs");
13
15
  const threadChrome = require("../messages/thread-chrome.cjs");
14
16
  const messages = require("../../agent-runtime/src/messages.cjs");
15
17
  const withArrivalDivider = /* @__PURE__ */ (() => {
@@ -19,6 +21,7 @@ const withArrivalDivider = /* @__PURE__ */ (() => {
19
21
  if (existing) return existing;
20
22
  const WithArrivalDivider = () => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
21
23
  /* @__PURE__ */ jsxRuntime.jsx(Message, {}),
24
+ /* @__PURE__ */ jsxRuntime.jsx(fromTheCallCard.FromTheCallAfterMessage, {}),
22
25
  /* @__PURE__ */ jsxRuntime.jsx(handoffArrivalDivider.HandoffArrivalAfterMessage, {})
23
26
  ] });
24
27
  wrapped.set(Message, WithArrivalDivider);
@@ -113,6 +116,7 @@ function ChatThreadShell({
113
116
  react.ThreadPrimitive.ViewportFooter,
114
117
  {
115
118
  className: viewportFooterClassName ?? DEFAULT_VIEWPORT_FOOTER_CLASS,
119
+ ...{ [statewireHistoryEdge.THREAD_VIEWPORT_FOOTER_ATTR]: "" },
116
120
  children: [
117
121
  scrollToBottom,
118
122
  viewportFooter
@@ -1 +1 @@
1
- {"version":3,"file":"chat-thread-shell.cjs","sources":["../../../src/chat-ui/thread/chat-thread-shell.tsx"],"sourcesContent":["import { AuiIf, ThreadPrimitive, useAuiState } from \"@assistant-ui/react\";\nimport { deepAgentRowKey } from \"@athenaintel/agent-runtime\";\nimport { ChevronDownIcon } from \"lucide-react\";\nimport type { ComponentProps, CSSProperties, FC, ReactNode } from \"react\";\nimport { useMemo } from \"react\";\nimport {\n THREAD_PANEL_CLASS,\n THREAD_SCROLL_TO_BOTTOM_CLASS,\n} from \"../messages/message-components\";\nimport { AnswerLabelScope } from \"../messages/answer-label\";\nimport { MessageActionsReadOnlyProvider } from \"../messages/message-actions-config\";\nimport { UserEditComposer } from \"../messages/user-edit-composer\";\nimport { ThreadRunningIndicator } from \"../messages/running-indicator\";\nimport {\n HandoffArrivalAfterMessage,\n HandoffArrivalScope,\n} from \"../messages/handoff-arrival-divider\";\n\nexport interface ChatThreadMessageComponents {\n UserMessage: FC;\n AssistantMessage: FC;\n /**\n * Rendered in place of a user message while it is being edited (the action\n * bar's Edit control). Defaults to the shared UserEditComposer.\n */\n UserEditComposer?: FC;\n}\n\n// One wrapper per message renderer, so a renderer keeps its identity across\n// shells and renders: a row renders its message through the component it is\n// handed, and a new component identity would remount every row.\nconst withArrivalDivider = (() => {\n const wrapped = new WeakMap<FC, FC>();\n return (Message: FC): FC => {\n const existing = wrapped.get(Message);\n if (existing) return existing;\n const WithArrivalDivider: FC = () => (\n <>\n <Message />\n <HandoffArrivalAfterMessage />\n </>\n );\n wrapped.set(Message, WithArrivalDivider);\n return WithArrivalDivider;\n };\n})();\n\ntype ThreadRowComponents = ComponentProps<typeof ThreadPrimitive.MessageByIndex>[\"components\"];\n\nconst ROW_KEY_SEPARATOR = \"\\u0000\";\n\n/**\n * The thread's messages, one row each under its row key (`deepAgentRowKey`).\n *\n * `ThreadPrimitive.Messages` keys a row by its message id. A page of older\n * history that folds into the first loaded turn (a long run's earlier steps)\n * gives that turn a new id on every page, so keyed by id the turn's row\n * remounted once per page: it flashed through its entrance animation, closed\n * what the person had opened in it and took the browser's scroll anchor with\n * it. The row key stays with the row the turn continues, so the page lands in\n * the mounted row. A message from another runtime keys by its id, as before.\n */\nconst ThreadRows: FC<{ components: ThreadRowComponents }> = ({ components }) => {\n const joinedKeys = useAuiState((s) =>\n s.thread.messages.map((message) => deepAgentRowKey(message)).join(ROW_KEY_SEPARATOR),\n );\n const rowKeys = useMemo(\n () => (joinedKeys === \"\" ? [] : joinedKeys.split(ROW_KEY_SEPARATOR)),\n [joinedKeys],\n );\n return (\n <>\n {rowKeys.map((key, index) => (\n <ThreadPrimitive.MessageByIndex key={key} index={index} components={components} />\n ))}\n </>\n );\n};\n\nconst DEFAULT_VIEWPORT_CLASS =\n \"relative flex flex-1 flex-col overflow-y-auto scroll-smooth\";\nconst DEFAULT_CONTENT_CLASS = \"w-full flex-1 px-3 pt-3\";\nconst DEFAULT_VIEWPORT_FOOTER_CLASS = \"sticky bottom-0\";\n\nconst DefaultScrollToBottom: FC = () => (\n <AuiIf condition={(state) => !state.thread.isEmpty}>\n <div className=\"flex justify-center pb-0.5\">\n <ThreadPrimitive.ScrollToBottom\n aria-label=\"Scroll to bottom\"\n title=\"Scroll to bottom\"\n className={THREAD_SCROLL_TO_BOTTOM_CLASS}\n >\n <ChevronDownIcon className=\"size-3.5\" />\n </ThreadPrimitive.ScrollToBottom>\n </div>\n </AuiIf>\n);\n\nexport interface ChatThreadShellProps {\n /**\n * Rendered as the first child of the panel — for absolutely-positioned\n * scrims that must cover the whole panel (AthenaChat's thread-loading\n * overlay). The shell does not force a positioning context; hosts include\n * `relative` in `panelClassName` when the scrim anchors to the panel.\n */\n overlay?: ReactNode;\n /**\n * Rendered above the viewport, inside the panel: header rows, slide-over\n * panels, and anything else the host docks at the top.\n */\n header?: ReactNode;\n /** Status/approval banners rendered between the header and the viewport. */\n banners?: ReactNode;\n /**\n * Nodes rendered inside the scroll viewport before the message column —\n * absolutely/sticky-positioned overlays (thread-switch skeletons,\n * scroll-to-top controls) and invisible observers (read receipts).\n */\n viewportOverlay?: ReactNode;\n /** Empty-thread state, rendered via ThreadPrimitive.Empty when provided. */\n welcome?: ReactNode;\n /**\n * Rendered inside the message column right above the messages — the\n * windowed wire's history edge (`StatewireHistoryEdge`): the \"N earlier\n * messages\" row that pages older history in and fetches cut tool outputs.\n */\n beforeMessages?: ReactNode;\n /** Message renderers for the thread's rows. */\n components: ChatThreadMessageComponents;\n /**\n * Wraps the messages element — for hosts that provide message-scoped\n * context (e.g. activity-grouping providers). Must render its argument.\n */\n renderMessages?: (messages: ReactNode) => ReactNode;\n /**\n * Rendered between the viewport and the composer: connection banners,\n * queued-message trays.\n */\n belowViewport?: ReactNode;\n /** The composer dock (or a read-only notice replacing it). */\n composer?: ReactNode;\n /**\n * Replaces the docked-panel card chrome — for hosts that render a\n * full-page column (AthenaChat's `aui-*` surface) instead of the\n * side-panel/task-pane card.\n */\n panelClassName?: string;\n /**\n * Value of the `--thread-max-width` variable read by width-capped\n * message/welcome/footer columns. Panels fill their width (the default,\n * \"100%\"); full-page hosts cap the column (AthenaChat defaults to 44rem).\n */\n maxWidth?: string;\n /**\n * Anchors the latest turn to the top of the viewport instead of pinning\n * scroll to the bottom (AthenaChat). Omitted = the primitive's default.\n */\n turnAnchor?: \"top\" | \"bottom\";\n /** Replaces the scroll-viewport chrome (padding, overflow tuning). */\n viewportClassName?: string;\n /**\n * Replaces the message-column chrome. Hosts whose welcome screen centers\n * vertically make this a flex column so the welcome node can grow.\n */\n contentClassName?: string;\n /**\n * Thread-level \"Thinking...\" indicator rendered after the messages. Hosts\n * whose message components render their own per-message indicators pass\n * null so the two never double up.\n */\n runningIndicator?: ReactNode;\n /**\n * Replaces the default centered scroll-to-bottom pill inside the sticky\n * viewport footer (AthenaChat floats a circular button above its composer).\n */\n scrollToBottom?: ReactNode;\n /**\n * Content docked inside the sticky viewport footer, after the\n * scroll-to-bottom control — for hosts whose banners + composer float over\n * the scrolling messages (AthenaChat) instead of sitting below the\n * viewport via `belowViewport`/`composer`.\n */\n viewportFooter?: ReactNode;\n /** Replaces the sticky viewport-footer chrome. */\n viewportFooterClassName?: string;\n /**\n * Marks the transcript read-only: mutating message actions (Edit,\n * Regenerate) hide while Copy stays available. For threads whose server\n * rejects dispatch (e.g. legacy pre-cutover conversations) — the runtime's\n * static capability flags cannot express a per-thread fence.\n */\n readOnly?: boolean;\n /**\n * The backend id of the thread on screen. When this surface picked the\n * conversation up from another one (`recordHandoffArrival`), the transcript\n * shows \"Continued from …\" after the last message that was already there.\n */\n arrivalThreadId?: string | null;\n /**\n * The signed-in viewer's Athena user id (`user_…`), for the answer labels\n * (`AnswerLabel`): their own answers read \"for you\", and an answer for\n * another person is labelled even in a 1:1 chat. Omitted, answers name the\n * person by name and are labelled only once the thread holds several agents\n * or several people.\n */\n viewerUserId?: string | null;\n}\n\n/**\n * The shared thread layout: panel chrome, scroll viewport with welcome +\n * messages + running indicator, the sticky scroll-to-bottom control, and the\n * host-supplied composer dock.\n *\n * Hosts express their genuine divergences through the slots above; the\n * layout, scroll behaviour, and empty/running states stay identical across\n * surfaces so fixes land once.\n */\nexport function ChatThreadShell({\n overlay,\n header,\n banners,\n viewportOverlay,\n welcome,\n beforeMessages,\n components,\n renderMessages,\n belowViewport,\n composer,\n panelClassName,\n maxWidth = \"100%\",\n turnAnchor,\n viewportClassName,\n contentClassName,\n runningIndicator = <ThreadRunningIndicator />,\n scrollToBottom = <DefaultScrollToBottom />,\n viewportFooter,\n viewportFooterClassName,\n readOnly = false,\n arrivalThreadId = null,\n viewerUserId = null,\n}: ChatThreadShellProps) {\n const {\n UserMessage,\n AssistantMessage,\n UserEditComposer: UserEditComposerComponent = UserEditComposer,\n } = components;\n // A row resolves its renderer by component identity, so keep the merged map\n // referentially stable across renders.\n const messageComponents = useMemo(\n () => ({\n UserMessage: withArrivalDivider(UserMessage),\n AssistantMessage: withArrivalDivider(AssistantMessage),\n UserEditComposer: UserEditComposerComponent,\n }),\n [UserMessage, AssistantMessage, UserEditComposerComponent],\n );\n const messages = (\n <HandoffArrivalScope threadId={arrivalThreadId}>\n <AnswerLabelScope viewerUserId={viewerUserId}>\n <ThreadRows components={messageComponents} />\n </AnswerLabelScope>\n </HandoffArrivalScope>\n );\n return (\n <MessageActionsReadOnlyProvider readOnly={readOnly}>\n <ThreadPrimitive.Root\n className={panelClassName ?? THREAD_PANEL_CLASS}\n style={{ \"--thread-max-width\": maxWidth } as CSSProperties}\n >\n {overlay}\n {header}\n {banners}\n <ThreadPrimitive.Viewport\n turnAnchor={turnAnchor}\n className={viewportClassName ?? DEFAULT_VIEWPORT_CLASS}\n data-aui-thread-viewport=\"\"\n >\n {viewportOverlay}\n {/* No min-h-0 here: the column's automatic minimum height is what\n lets it extend past one screen, keeping the sticky footer's flow\n position after the full message list. Capping it detaches the\n footer (scroll pill, docked composers) as soon as the thread\n scrolls — proven in a live-browser probe of both structures. */}\n <div className=\"flex flex-1 flex-col items-center\">\n <div className={contentClassName ?? DEFAULT_CONTENT_CLASS}>\n {welcome != null ? (\n <ThreadPrimitive.Empty>{welcome}</ThreadPrimitive.Empty>\n ) : null}\n {beforeMessages}\n {renderMessages ? renderMessages(messages) : messages}\n {runningIndicator}\n </div>\n </div>\n <ThreadPrimitive.ViewportFooter\n className={viewportFooterClassName ?? DEFAULT_VIEWPORT_FOOTER_CLASS}\n >\n {scrollToBottom}\n {viewportFooter}\n </ThreadPrimitive.ViewportFooter>\n </ThreadPrimitive.Viewport>\n {belowViewport}\n {composer}\n </ThreadPrimitive.Root>\n </MessageActionsReadOnlyProvider>\n );\n}\n"],"names":["jsxs","Fragment","jsx","HandoffArrivalAfterMessage","useAuiState","deepAgentRowKey","useMemo","ThreadPrimitive","AuiIf","THREAD_SCROLL_TO_BOTTOM_CLASS","ChevronDownIcon","runningIndicator","ThreadRunningIndicator","UserEditComposer","messageComponents","messages","HandoffArrivalScope","AnswerLabelScope","MessageActionsReadOnlyProvider","THREAD_PANEL_CLASS"],"mappings":"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"}
1
+ {"version":3,"file":"chat-thread-shell.cjs","sources":["../../../src/chat-ui/thread/chat-thread-shell.tsx"],"sourcesContent":["import { AuiIf, ThreadPrimitive, useAuiState } from \"@assistant-ui/react\";\nimport { deepAgentRowKey } from \"@athenaintel/agent-runtime\";\nimport { ChevronDownIcon } from \"lucide-react\";\nimport type { ComponentProps, CSSProperties, FC, ReactNode } from \"react\";\nimport { useMemo } from \"react\";\nimport {\n THREAD_PANEL_CLASS,\n THREAD_SCROLL_TO_BOTTOM_CLASS,\n} from \"../messages/message-components\";\nimport { AnswerLabelScope } from \"../messages/answer-label\";\nimport { MessageActionsReadOnlyProvider } from \"../messages/message-actions-config\";\nimport { UserEditComposer } from \"../messages/user-edit-composer\";\nimport { ThreadRunningIndicator } from \"../messages/running-indicator\";\nimport {\n HandoffArrivalAfterMessage,\n HandoffArrivalScope,\n} from \"../messages/handoff-arrival-divider\";\nimport { FromTheCallAfterMessage } from \"../messages/from-the-call-card\";\nimport { THREAD_VIEWPORT_FOOTER_ATTR } from \"./statewire-history-edge\";\n\nexport interface ChatThreadMessageComponents {\n UserMessage: FC;\n AssistantMessage: FC;\n /**\n * Rendered in place of a user message while it is being edited (the action\n * bar's Edit control). Defaults to the shared UserEditComposer.\n */\n UserEditComposer?: FC;\n}\n\n// One wrapper per message renderer, so a renderer keeps its identity across\n// shells and renders: a row renders its message through the component it is\n// handed, and a new component identity would remount every row.\nconst withArrivalDivider = (() => {\n const wrapped = new WeakMap<FC, FC>();\n return (Message: FC): FC => {\n const existing = wrapped.get(Message);\n if (existing) return existing;\n // After the message: the \"From the call\" card where a call's stretch\n // ends, then the arrival divider (the arrival came after the call).\n const WithArrivalDivider: FC = () => (\n <>\n <Message />\n <FromTheCallAfterMessage />\n <HandoffArrivalAfterMessage />\n </>\n );\n wrapped.set(Message, WithArrivalDivider);\n return WithArrivalDivider;\n };\n})();\n\ntype ThreadRowComponents = ComponentProps<typeof ThreadPrimitive.MessageByIndex>[\"components\"];\n\nconst ROW_KEY_SEPARATOR = \"\\u0000\";\n\n/**\n * The thread's messages, one row each under its row key (`deepAgentRowKey`).\n *\n * `ThreadPrimitive.Messages` keys a row by its message id. A page of older\n * history that folds into the first loaded turn (a long run's earlier steps)\n * gives that turn a new id on every page, so keyed by id the turn's row\n * remounted once per page: it flashed through its entrance animation, closed\n * what the person had opened in it and took the browser's scroll anchor with\n * it. The row key stays with the row the turn continues, so the page lands in\n * the mounted row. A message from another runtime keys by its id, as before.\n */\nconst ThreadRows: FC<{ components: ThreadRowComponents }> = ({ components }) => {\n const joinedKeys = useAuiState((s) =>\n s.thread.messages.map((message) => deepAgentRowKey(message)).join(ROW_KEY_SEPARATOR),\n );\n const rowKeys = useMemo(\n () => (joinedKeys === \"\" ? [] : joinedKeys.split(ROW_KEY_SEPARATOR)),\n [joinedKeys],\n );\n return (\n <>\n {rowKeys.map((key, index) => (\n <ThreadPrimitive.MessageByIndex key={key} index={index} components={components} />\n ))}\n </>\n );\n};\n\nconst DEFAULT_VIEWPORT_CLASS =\n \"relative flex flex-1 flex-col overflow-y-auto scroll-smooth\";\nconst DEFAULT_CONTENT_CLASS = \"w-full flex-1 px-3 pt-3\";\nconst DEFAULT_VIEWPORT_FOOTER_CLASS = \"sticky bottom-0\";\n\nconst DefaultScrollToBottom: FC = () => (\n <AuiIf condition={(state) => !state.thread.isEmpty}>\n <div className=\"flex justify-center pb-0.5\">\n <ThreadPrimitive.ScrollToBottom\n aria-label=\"Scroll to bottom\"\n title=\"Scroll to bottom\"\n className={THREAD_SCROLL_TO_BOTTOM_CLASS}\n >\n <ChevronDownIcon className=\"size-3.5\" />\n </ThreadPrimitive.ScrollToBottom>\n </div>\n </AuiIf>\n);\n\nexport interface ChatThreadShellProps {\n /**\n * Rendered as the first child of the panel — for absolutely-positioned\n * scrims that must cover the whole panel (AthenaChat's thread-loading\n * overlay). The shell does not force a positioning context; hosts include\n * `relative` in `panelClassName` when the scrim anchors to the panel.\n */\n overlay?: ReactNode;\n /**\n * Rendered above the viewport, inside the panel: header rows, slide-over\n * panels, and anything else the host docks at the top.\n */\n header?: ReactNode;\n /** Status/approval banners rendered between the header and the viewport. */\n banners?: ReactNode;\n /**\n * Nodes rendered inside the scroll viewport before the message column —\n * absolutely/sticky-positioned overlays (thread-switch skeletons,\n * scroll-to-top controls) and invisible observers (read receipts).\n */\n viewportOverlay?: ReactNode;\n /** Empty-thread state, rendered via ThreadPrimitive.Empty when provided. */\n welcome?: ReactNode;\n /**\n * Rendered inside the message column right above the messages — the\n * windowed wire's history edge (`StatewireHistoryEdge`): the \"N earlier\n * messages\" row that pages older history in and fetches cut tool outputs.\n */\n beforeMessages?: ReactNode;\n /** Message renderers for the thread's rows. */\n components: ChatThreadMessageComponents;\n /**\n * Wraps the messages element — for hosts that provide message-scoped\n * context (e.g. activity-grouping providers). Must render its argument.\n */\n renderMessages?: (messages: ReactNode) => ReactNode;\n /**\n * Rendered between the viewport and the composer: connection banners,\n * queued-message trays.\n */\n belowViewport?: ReactNode;\n /** The composer dock (or a read-only notice replacing it). */\n composer?: ReactNode;\n /**\n * Replaces the docked-panel card chrome — for hosts that render a\n * full-page column (AthenaChat's `aui-*` surface) instead of the\n * side-panel/task-pane card.\n */\n panelClassName?: string;\n /**\n * Value of the `--thread-max-width` variable read by width-capped\n * message/welcome/footer columns. Panels fill their width (the default,\n * \"100%\"); full-page hosts cap the column (AthenaChat defaults to 44rem).\n */\n maxWidth?: string;\n /**\n * Anchors the latest turn to the top of the viewport instead of pinning\n * scroll to the bottom (AthenaChat). Omitted = the primitive's default.\n */\n turnAnchor?: \"top\" | \"bottom\";\n /** Replaces the scroll-viewport chrome (padding, overflow tuning). */\n viewportClassName?: string;\n /**\n * Replaces the message-column chrome. Hosts whose welcome screen centers\n * vertically make this a flex column so the welcome node can grow.\n */\n contentClassName?: string;\n /**\n * Thread-level \"Thinking...\" indicator rendered after the messages. Hosts\n * whose message components render their own per-message indicators pass\n * null so the two never double up.\n */\n runningIndicator?: ReactNode;\n /**\n * Replaces the default centered scroll-to-bottom pill inside the sticky\n * viewport footer (AthenaChat floats a circular button above its composer).\n */\n scrollToBottom?: ReactNode;\n /**\n * Content docked inside the sticky viewport footer, after the\n * scroll-to-bottom control — for hosts whose banners + composer float over\n * the scrolling messages (AthenaChat) instead of sitting below the\n * viewport via `belowViewport`/`composer`.\n */\n viewportFooter?: ReactNode;\n /** Replaces the sticky viewport-footer chrome. */\n viewportFooterClassName?: string;\n /**\n * Marks the transcript read-only: mutating message actions (Edit,\n * Regenerate) hide while Copy stays available. For threads whose server\n * rejects dispatch (e.g. legacy pre-cutover conversations) — the runtime's\n * static capability flags cannot express a per-thread fence.\n */\n readOnly?: boolean;\n /**\n * The backend id of the thread on screen. When this surface picked the\n * conversation up from another one (`recordHandoffArrival`), the transcript\n * shows \"Continued from …\" after the last message that was already there.\n * A host that is a handover destination also mounts `HandoffCursorReveal`\n * for it in `viewportOverlay`, so the transcript opens at the message the\n * other surface was reading (hosts that never receive one, the Office pane,\n * don't carry it).\n */\n arrivalThreadId?: string | null;\n /**\n * The signed-in viewer's Athena user id (`user_…`), for the answer labels\n * (`AnswerLabel`): their own answers read \"for you\", and an answer for\n * another person is labelled even in a 1:1 chat. Omitted, answers name the\n * person by name and are labelled only once the thread holds several agents\n * or several people.\n */\n viewerUserId?: string | null;\n}\n\n/**\n * The shared thread layout: panel chrome, scroll viewport with welcome +\n * messages + running indicator, the sticky scroll-to-bottom control, and the\n * host-supplied composer dock.\n *\n * Hosts express their genuine divergences through the slots above; the\n * layout, scroll behaviour, and empty/running states stay identical across\n * surfaces so fixes land once.\n */\nexport function ChatThreadShell({\n overlay,\n header,\n banners,\n viewportOverlay,\n welcome,\n beforeMessages,\n components,\n renderMessages,\n belowViewport,\n composer,\n panelClassName,\n maxWidth = \"100%\",\n turnAnchor,\n viewportClassName,\n contentClassName,\n runningIndicator = <ThreadRunningIndicator />,\n scrollToBottom = <DefaultScrollToBottom />,\n viewportFooter,\n viewportFooterClassName,\n readOnly = false,\n arrivalThreadId = null,\n viewerUserId = null,\n}: ChatThreadShellProps) {\n const {\n UserMessage,\n AssistantMessage,\n UserEditComposer: UserEditComposerComponent = UserEditComposer,\n } = components;\n // A row resolves its renderer by component identity, so keep the merged map\n // referentially stable across renders.\n const messageComponents = useMemo(\n () => ({\n UserMessage: withArrivalDivider(UserMessage),\n AssistantMessage: withArrivalDivider(AssistantMessage),\n UserEditComposer: UserEditComposerComponent,\n }),\n [UserMessage, AssistantMessage, UserEditComposerComponent],\n );\n const messages = (\n <HandoffArrivalScope threadId={arrivalThreadId}>\n <AnswerLabelScope viewerUserId={viewerUserId}>\n <ThreadRows components={messageComponents} />\n </AnswerLabelScope>\n </HandoffArrivalScope>\n );\n return (\n <MessageActionsReadOnlyProvider readOnly={readOnly}>\n <ThreadPrimitive.Root\n className={panelClassName ?? THREAD_PANEL_CLASS}\n style={{ \"--thread-max-width\": maxWidth } as CSSProperties}\n >\n {overlay}\n {header}\n {banners}\n <ThreadPrimitive.Viewport\n turnAnchor={turnAnchor}\n className={viewportClassName ?? DEFAULT_VIEWPORT_CLASS}\n data-aui-thread-viewport=\"\"\n >\n {viewportOverlay}\n {/* No min-h-0 here: the column's automatic minimum height is what\n lets it extend past one screen, keeping the sticky footer's flow\n position after the full message list. Capping it detaches the\n footer (scroll pill, docked composers) as soon as the thread\n scrolls — proven in a live-browser probe of both structures. */}\n <div className=\"flex flex-1 flex-col items-center\">\n <div className={contentClassName ?? DEFAULT_CONTENT_CLASS}>\n {welcome != null ? (\n <ThreadPrimitive.Empty>{welcome}</ThreadPrimitive.Empty>\n ) : null}\n {beforeMessages}\n {renderMessages ? renderMessages(messages) : messages}\n {runningIndicator}\n </div>\n </div>\n <ThreadPrimitive.ViewportFooter\n className={viewportFooterClassName ?? DEFAULT_VIEWPORT_FOOTER_CLASS}\n {...{ [THREAD_VIEWPORT_FOOTER_ATTR]: \"\" }}\n >\n {scrollToBottom}\n {viewportFooter}\n </ThreadPrimitive.ViewportFooter>\n </ThreadPrimitive.Viewport>\n {belowViewport}\n {composer}\n </ThreadPrimitive.Root>\n </MessageActionsReadOnlyProvider>\n );\n}\n"],"names":["jsxs","Fragment","jsx","FromTheCallAfterMessage","HandoffArrivalAfterMessage","useAuiState","deepAgentRowKey","useMemo","ThreadPrimitive","AuiIf","THREAD_SCROLL_TO_BOTTOM_CLASS","ChevronDownIcon","runningIndicator","ThreadRunningIndicator","UserEditComposer","messageComponents","messages","HandoffArrivalScope","AnswerLabelScope","MessageActionsReadOnlyProvider","THREAD_PANEL_CLASS","THREAD_VIEWPORT_FOOTER_ATTR"],"mappings":"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"}
@@ -106,6 +106,10 @@ export interface ChatThreadShellProps {
106
106
  * The backend id of the thread on screen. When this surface picked the
107
107
  * conversation up from another one (`recordHandoffArrival`), the transcript
108
108
  * shows "Continued from …" after the last message that was already there.
109
+ * A host that is a handover destination also mounts `HandoffCursorReveal`
110
+ * for it in `viewportOverlay`, so the transcript opens at the message the
111
+ * other surface was reading (hosts that never receive one, the Office pane,
112
+ * don't carry it).
109
113
  */
110
114
  arrivalThreadId?: string | null;
111
115
  /**
@@ -8,6 +8,8 @@ import { MessageActionsReadOnlyProvider } from "../messages/message-actions-conf
8
8
  import { UserEditComposer } from "../messages/user-edit-composer.js";
9
9
  import { ThreadRunningIndicator } from "../messages/running-indicator.js";
10
10
  import { HandoffArrivalScope, HandoffArrivalAfterMessage } from "../messages/handoff-arrival-divider.js";
11
+ import { FromTheCallAfterMessage } from "../messages/from-the-call-card.js";
12
+ import { THREAD_VIEWPORT_FOOTER_ATTR } from "./statewire-history-edge.js";
11
13
  import { THREAD_PANEL_CLASS } from "../messages/thread-chrome.js";
12
14
  import { deepAgentRowKey } from "../../agent-runtime/src/messages.js";
13
15
  const withArrivalDivider = /* @__PURE__ */ (() => {
@@ -17,6 +19,7 @@ const withArrivalDivider = /* @__PURE__ */ (() => {
17
19
  if (existing) return existing;
18
20
  const WithArrivalDivider = () => /* @__PURE__ */ jsxs(Fragment, { children: [
19
21
  /* @__PURE__ */ jsx(Message, {}),
22
+ /* @__PURE__ */ jsx(FromTheCallAfterMessage, {}),
20
23
  /* @__PURE__ */ jsx(HandoffArrivalAfterMessage, {})
21
24
  ] });
22
25
  wrapped.set(Message, WithArrivalDivider);
@@ -111,6 +114,7 @@ function ChatThreadShell({
111
114
  ThreadPrimitive.ViewportFooter,
112
115
  {
113
116
  className: viewportFooterClassName ?? DEFAULT_VIEWPORT_FOOTER_CLASS,
117
+ ...{ [THREAD_VIEWPORT_FOOTER_ATTR]: "" },
114
118
  children: [
115
119
  scrollToBottom,
116
120
  viewportFooter
@@ -1 +1 @@
1
- {"version":3,"file":"chat-thread-shell.js","sources":["../../../src/chat-ui/thread/chat-thread-shell.tsx"],"sourcesContent":["import { AuiIf, ThreadPrimitive, useAuiState } from \"@assistant-ui/react\";\nimport { deepAgentRowKey } from \"@athenaintel/agent-runtime\";\nimport { ChevronDownIcon } from \"lucide-react\";\nimport type { ComponentProps, CSSProperties, FC, ReactNode } from \"react\";\nimport { useMemo } from \"react\";\nimport {\n THREAD_PANEL_CLASS,\n THREAD_SCROLL_TO_BOTTOM_CLASS,\n} from \"../messages/message-components\";\nimport { AnswerLabelScope } from \"../messages/answer-label\";\nimport { MessageActionsReadOnlyProvider } from \"../messages/message-actions-config\";\nimport { UserEditComposer } from \"../messages/user-edit-composer\";\nimport { ThreadRunningIndicator } from \"../messages/running-indicator\";\nimport {\n HandoffArrivalAfterMessage,\n HandoffArrivalScope,\n} from \"../messages/handoff-arrival-divider\";\n\nexport interface ChatThreadMessageComponents {\n UserMessage: FC;\n AssistantMessage: FC;\n /**\n * Rendered in place of a user message while it is being edited (the action\n * bar's Edit control). Defaults to the shared UserEditComposer.\n */\n UserEditComposer?: FC;\n}\n\n// One wrapper per message renderer, so a renderer keeps its identity across\n// shells and renders: a row renders its message through the component it is\n// handed, and a new component identity would remount every row.\nconst withArrivalDivider = (() => {\n const wrapped = new WeakMap<FC, FC>();\n return (Message: FC): FC => {\n const existing = wrapped.get(Message);\n if (existing) return existing;\n const WithArrivalDivider: FC = () => (\n <>\n <Message />\n <HandoffArrivalAfterMessage />\n </>\n );\n wrapped.set(Message, WithArrivalDivider);\n return WithArrivalDivider;\n };\n})();\n\ntype ThreadRowComponents = ComponentProps<typeof ThreadPrimitive.MessageByIndex>[\"components\"];\n\nconst ROW_KEY_SEPARATOR = \"\\u0000\";\n\n/**\n * The thread's messages, one row each under its row key (`deepAgentRowKey`).\n *\n * `ThreadPrimitive.Messages` keys a row by its message id. A page of older\n * history that folds into the first loaded turn (a long run's earlier steps)\n * gives that turn a new id on every page, so keyed by id the turn's row\n * remounted once per page: it flashed through its entrance animation, closed\n * what the person had opened in it and took the browser's scroll anchor with\n * it. The row key stays with the row the turn continues, so the page lands in\n * the mounted row. A message from another runtime keys by its id, as before.\n */\nconst ThreadRows: FC<{ components: ThreadRowComponents }> = ({ components }) => {\n const joinedKeys = useAuiState((s) =>\n s.thread.messages.map((message) => deepAgentRowKey(message)).join(ROW_KEY_SEPARATOR),\n );\n const rowKeys = useMemo(\n () => (joinedKeys === \"\" ? [] : joinedKeys.split(ROW_KEY_SEPARATOR)),\n [joinedKeys],\n );\n return (\n <>\n {rowKeys.map((key, index) => (\n <ThreadPrimitive.MessageByIndex key={key} index={index} components={components} />\n ))}\n </>\n );\n};\n\nconst DEFAULT_VIEWPORT_CLASS =\n \"relative flex flex-1 flex-col overflow-y-auto scroll-smooth\";\nconst DEFAULT_CONTENT_CLASS = \"w-full flex-1 px-3 pt-3\";\nconst DEFAULT_VIEWPORT_FOOTER_CLASS = \"sticky bottom-0\";\n\nconst DefaultScrollToBottom: FC = () => (\n <AuiIf condition={(state) => !state.thread.isEmpty}>\n <div className=\"flex justify-center pb-0.5\">\n <ThreadPrimitive.ScrollToBottom\n aria-label=\"Scroll to bottom\"\n title=\"Scroll to bottom\"\n className={THREAD_SCROLL_TO_BOTTOM_CLASS}\n >\n <ChevronDownIcon className=\"size-3.5\" />\n </ThreadPrimitive.ScrollToBottom>\n </div>\n </AuiIf>\n);\n\nexport interface ChatThreadShellProps {\n /**\n * Rendered as the first child of the panel — for absolutely-positioned\n * scrims that must cover the whole panel (AthenaChat's thread-loading\n * overlay). The shell does not force a positioning context; hosts include\n * `relative` in `panelClassName` when the scrim anchors to the panel.\n */\n overlay?: ReactNode;\n /**\n * Rendered above the viewport, inside the panel: header rows, slide-over\n * panels, and anything else the host docks at the top.\n */\n header?: ReactNode;\n /** Status/approval banners rendered between the header and the viewport. */\n banners?: ReactNode;\n /**\n * Nodes rendered inside the scroll viewport before the message column —\n * absolutely/sticky-positioned overlays (thread-switch skeletons,\n * scroll-to-top controls) and invisible observers (read receipts).\n */\n viewportOverlay?: ReactNode;\n /** Empty-thread state, rendered via ThreadPrimitive.Empty when provided. */\n welcome?: ReactNode;\n /**\n * Rendered inside the message column right above the messages — the\n * windowed wire's history edge (`StatewireHistoryEdge`): the \"N earlier\n * messages\" row that pages older history in and fetches cut tool outputs.\n */\n beforeMessages?: ReactNode;\n /** Message renderers for the thread's rows. */\n components: ChatThreadMessageComponents;\n /**\n * Wraps the messages element — for hosts that provide message-scoped\n * context (e.g. activity-grouping providers). Must render its argument.\n */\n renderMessages?: (messages: ReactNode) => ReactNode;\n /**\n * Rendered between the viewport and the composer: connection banners,\n * queued-message trays.\n */\n belowViewport?: ReactNode;\n /** The composer dock (or a read-only notice replacing it). */\n composer?: ReactNode;\n /**\n * Replaces the docked-panel card chrome — for hosts that render a\n * full-page column (AthenaChat's `aui-*` surface) instead of the\n * side-panel/task-pane card.\n */\n panelClassName?: string;\n /**\n * Value of the `--thread-max-width` variable read by width-capped\n * message/welcome/footer columns. Panels fill their width (the default,\n * \"100%\"); full-page hosts cap the column (AthenaChat defaults to 44rem).\n */\n maxWidth?: string;\n /**\n * Anchors the latest turn to the top of the viewport instead of pinning\n * scroll to the bottom (AthenaChat). Omitted = the primitive's default.\n */\n turnAnchor?: \"top\" | \"bottom\";\n /** Replaces the scroll-viewport chrome (padding, overflow tuning). */\n viewportClassName?: string;\n /**\n * Replaces the message-column chrome. Hosts whose welcome screen centers\n * vertically make this a flex column so the welcome node can grow.\n */\n contentClassName?: string;\n /**\n * Thread-level \"Thinking...\" indicator rendered after the messages. Hosts\n * whose message components render their own per-message indicators pass\n * null so the two never double up.\n */\n runningIndicator?: ReactNode;\n /**\n * Replaces the default centered scroll-to-bottom pill inside the sticky\n * viewport footer (AthenaChat floats a circular button above its composer).\n */\n scrollToBottom?: ReactNode;\n /**\n * Content docked inside the sticky viewport footer, after the\n * scroll-to-bottom control — for hosts whose banners + composer float over\n * the scrolling messages (AthenaChat) instead of sitting below the\n * viewport via `belowViewport`/`composer`.\n */\n viewportFooter?: ReactNode;\n /** Replaces the sticky viewport-footer chrome. */\n viewportFooterClassName?: string;\n /**\n * Marks the transcript read-only: mutating message actions (Edit,\n * Regenerate) hide while Copy stays available. For threads whose server\n * rejects dispatch (e.g. legacy pre-cutover conversations) — the runtime's\n * static capability flags cannot express a per-thread fence.\n */\n readOnly?: boolean;\n /**\n * The backend id of the thread on screen. When this surface picked the\n * conversation up from another one (`recordHandoffArrival`), the transcript\n * shows \"Continued from …\" after the last message that was already there.\n */\n arrivalThreadId?: string | null;\n /**\n * The signed-in viewer's Athena user id (`user_…`), for the answer labels\n * (`AnswerLabel`): their own answers read \"for you\", and an answer for\n * another person is labelled even in a 1:1 chat. Omitted, answers name the\n * person by name and are labelled only once the thread holds several agents\n * or several people.\n */\n viewerUserId?: string | null;\n}\n\n/**\n * The shared thread layout: panel chrome, scroll viewport with welcome +\n * messages + running indicator, the sticky scroll-to-bottom control, and the\n * host-supplied composer dock.\n *\n * Hosts express their genuine divergences through the slots above; the\n * layout, scroll behaviour, and empty/running states stay identical across\n * surfaces so fixes land once.\n */\nexport function ChatThreadShell({\n overlay,\n header,\n banners,\n viewportOverlay,\n welcome,\n beforeMessages,\n components,\n renderMessages,\n belowViewport,\n composer,\n panelClassName,\n maxWidth = \"100%\",\n turnAnchor,\n viewportClassName,\n contentClassName,\n runningIndicator = <ThreadRunningIndicator />,\n scrollToBottom = <DefaultScrollToBottom />,\n viewportFooter,\n viewportFooterClassName,\n readOnly = false,\n arrivalThreadId = null,\n viewerUserId = null,\n}: ChatThreadShellProps) {\n const {\n UserMessage,\n AssistantMessage,\n UserEditComposer: UserEditComposerComponent = UserEditComposer,\n } = components;\n // A row resolves its renderer by component identity, so keep the merged map\n // referentially stable across renders.\n const messageComponents = useMemo(\n () => ({\n UserMessage: withArrivalDivider(UserMessage),\n AssistantMessage: withArrivalDivider(AssistantMessage),\n UserEditComposer: UserEditComposerComponent,\n }),\n [UserMessage, AssistantMessage, UserEditComposerComponent],\n );\n const messages = (\n <HandoffArrivalScope threadId={arrivalThreadId}>\n <AnswerLabelScope viewerUserId={viewerUserId}>\n <ThreadRows components={messageComponents} />\n </AnswerLabelScope>\n </HandoffArrivalScope>\n );\n return (\n <MessageActionsReadOnlyProvider readOnly={readOnly}>\n <ThreadPrimitive.Root\n className={panelClassName ?? THREAD_PANEL_CLASS}\n style={{ \"--thread-max-width\": maxWidth } as CSSProperties}\n >\n {overlay}\n {header}\n {banners}\n <ThreadPrimitive.Viewport\n turnAnchor={turnAnchor}\n className={viewportClassName ?? DEFAULT_VIEWPORT_CLASS}\n data-aui-thread-viewport=\"\"\n >\n {viewportOverlay}\n {/* No min-h-0 here: the column's automatic minimum height is what\n lets it extend past one screen, keeping the sticky footer's flow\n position after the full message list. Capping it detaches the\n footer (scroll pill, docked composers) as soon as the thread\n scrolls — proven in a live-browser probe of both structures. */}\n <div className=\"flex flex-1 flex-col items-center\">\n <div className={contentClassName ?? DEFAULT_CONTENT_CLASS}>\n {welcome != null ? (\n <ThreadPrimitive.Empty>{welcome}</ThreadPrimitive.Empty>\n ) : null}\n {beforeMessages}\n {renderMessages ? renderMessages(messages) : messages}\n {runningIndicator}\n </div>\n </div>\n <ThreadPrimitive.ViewportFooter\n className={viewportFooterClassName ?? DEFAULT_VIEWPORT_FOOTER_CLASS}\n >\n {scrollToBottom}\n {viewportFooter}\n </ThreadPrimitive.ViewportFooter>\n </ThreadPrimitive.Viewport>\n {belowViewport}\n {composer}\n </ThreadPrimitive.Root>\n </MessageActionsReadOnlyProvider>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-thread-shell.js","sources":["../../../src/chat-ui/thread/chat-thread-shell.tsx"],"sourcesContent":["import { AuiIf, ThreadPrimitive, useAuiState } from \"@assistant-ui/react\";\nimport { deepAgentRowKey } from \"@athenaintel/agent-runtime\";\nimport { ChevronDownIcon } from \"lucide-react\";\nimport type { ComponentProps, CSSProperties, FC, ReactNode } from \"react\";\nimport { useMemo } from \"react\";\nimport {\n THREAD_PANEL_CLASS,\n THREAD_SCROLL_TO_BOTTOM_CLASS,\n} from \"../messages/message-components\";\nimport { AnswerLabelScope } from \"../messages/answer-label\";\nimport { MessageActionsReadOnlyProvider } from \"../messages/message-actions-config\";\nimport { UserEditComposer } from \"../messages/user-edit-composer\";\nimport { ThreadRunningIndicator } from \"../messages/running-indicator\";\nimport {\n HandoffArrivalAfterMessage,\n HandoffArrivalScope,\n} from \"../messages/handoff-arrival-divider\";\nimport { FromTheCallAfterMessage } from \"../messages/from-the-call-card\";\nimport { THREAD_VIEWPORT_FOOTER_ATTR } from \"./statewire-history-edge\";\n\nexport interface ChatThreadMessageComponents {\n UserMessage: FC;\n AssistantMessage: FC;\n /**\n * Rendered in place of a user message while it is being edited (the action\n * bar's Edit control). Defaults to the shared UserEditComposer.\n */\n UserEditComposer?: FC;\n}\n\n// One wrapper per message renderer, so a renderer keeps its identity across\n// shells and renders: a row renders its message through the component it is\n// handed, and a new component identity would remount every row.\nconst withArrivalDivider = (() => {\n const wrapped = new WeakMap<FC, FC>();\n return (Message: FC): FC => {\n const existing = wrapped.get(Message);\n if (existing) return existing;\n // After the message: the \"From the call\" card where a call's stretch\n // ends, then the arrival divider (the arrival came after the call).\n const WithArrivalDivider: FC = () => (\n <>\n <Message />\n <FromTheCallAfterMessage />\n <HandoffArrivalAfterMessage />\n </>\n );\n wrapped.set(Message, WithArrivalDivider);\n return WithArrivalDivider;\n };\n})();\n\ntype ThreadRowComponents = ComponentProps<typeof ThreadPrimitive.MessageByIndex>[\"components\"];\n\nconst ROW_KEY_SEPARATOR = \"\\u0000\";\n\n/**\n * The thread's messages, one row each under its row key (`deepAgentRowKey`).\n *\n * `ThreadPrimitive.Messages` keys a row by its message id. A page of older\n * history that folds into the first loaded turn (a long run's earlier steps)\n * gives that turn a new id on every page, so keyed by id the turn's row\n * remounted once per page: it flashed through its entrance animation, closed\n * what the person had opened in it and took the browser's scroll anchor with\n * it. The row key stays with the row the turn continues, so the page lands in\n * the mounted row. A message from another runtime keys by its id, as before.\n */\nconst ThreadRows: FC<{ components: ThreadRowComponents }> = ({ components }) => {\n const joinedKeys = useAuiState((s) =>\n s.thread.messages.map((message) => deepAgentRowKey(message)).join(ROW_KEY_SEPARATOR),\n );\n const rowKeys = useMemo(\n () => (joinedKeys === \"\" ? [] : joinedKeys.split(ROW_KEY_SEPARATOR)),\n [joinedKeys],\n );\n return (\n <>\n {rowKeys.map((key, index) => (\n <ThreadPrimitive.MessageByIndex key={key} index={index} components={components} />\n ))}\n </>\n );\n};\n\nconst DEFAULT_VIEWPORT_CLASS =\n \"relative flex flex-1 flex-col overflow-y-auto scroll-smooth\";\nconst DEFAULT_CONTENT_CLASS = \"w-full flex-1 px-3 pt-3\";\nconst DEFAULT_VIEWPORT_FOOTER_CLASS = \"sticky bottom-0\";\n\nconst DefaultScrollToBottom: FC = () => (\n <AuiIf condition={(state) => !state.thread.isEmpty}>\n <div className=\"flex justify-center pb-0.5\">\n <ThreadPrimitive.ScrollToBottom\n aria-label=\"Scroll to bottom\"\n title=\"Scroll to bottom\"\n className={THREAD_SCROLL_TO_BOTTOM_CLASS}\n >\n <ChevronDownIcon className=\"size-3.5\" />\n </ThreadPrimitive.ScrollToBottom>\n </div>\n </AuiIf>\n);\n\nexport interface ChatThreadShellProps {\n /**\n * Rendered as the first child of the panel — for absolutely-positioned\n * scrims that must cover the whole panel (AthenaChat's thread-loading\n * overlay). The shell does not force a positioning context; hosts include\n * `relative` in `panelClassName` when the scrim anchors to the panel.\n */\n overlay?: ReactNode;\n /**\n * Rendered above the viewport, inside the panel: header rows, slide-over\n * panels, and anything else the host docks at the top.\n */\n header?: ReactNode;\n /** Status/approval banners rendered between the header and the viewport. */\n banners?: ReactNode;\n /**\n * Nodes rendered inside the scroll viewport before the message column —\n * absolutely/sticky-positioned overlays (thread-switch skeletons,\n * scroll-to-top controls) and invisible observers (read receipts).\n */\n viewportOverlay?: ReactNode;\n /** Empty-thread state, rendered via ThreadPrimitive.Empty when provided. */\n welcome?: ReactNode;\n /**\n * Rendered inside the message column right above the messages — the\n * windowed wire's history edge (`StatewireHistoryEdge`): the \"N earlier\n * messages\" row that pages older history in and fetches cut tool outputs.\n */\n beforeMessages?: ReactNode;\n /** Message renderers for the thread's rows. */\n components: ChatThreadMessageComponents;\n /**\n * Wraps the messages element — for hosts that provide message-scoped\n * context (e.g. activity-grouping providers). Must render its argument.\n */\n renderMessages?: (messages: ReactNode) => ReactNode;\n /**\n * Rendered between the viewport and the composer: connection banners,\n * queued-message trays.\n */\n belowViewport?: ReactNode;\n /** The composer dock (or a read-only notice replacing it). */\n composer?: ReactNode;\n /**\n * Replaces the docked-panel card chrome — for hosts that render a\n * full-page column (AthenaChat's `aui-*` surface) instead of the\n * side-panel/task-pane card.\n */\n panelClassName?: string;\n /**\n * Value of the `--thread-max-width` variable read by width-capped\n * message/welcome/footer columns. Panels fill their width (the default,\n * \"100%\"); full-page hosts cap the column (AthenaChat defaults to 44rem).\n */\n maxWidth?: string;\n /**\n * Anchors the latest turn to the top of the viewport instead of pinning\n * scroll to the bottom (AthenaChat). Omitted = the primitive's default.\n */\n turnAnchor?: \"top\" | \"bottom\";\n /** Replaces the scroll-viewport chrome (padding, overflow tuning). */\n viewportClassName?: string;\n /**\n * Replaces the message-column chrome. Hosts whose welcome screen centers\n * vertically make this a flex column so the welcome node can grow.\n */\n contentClassName?: string;\n /**\n * Thread-level \"Thinking...\" indicator rendered after the messages. Hosts\n * whose message components render their own per-message indicators pass\n * null so the two never double up.\n */\n runningIndicator?: ReactNode;\n /**\n * Replaces the default centered scroll-to-bottom pill inside the sticky\n * viewport footer (AthenaChat floats a circular button above its composer).\n */\n scrollToBottom?: ReactNode;\n /**\n * Content docked inside the sticky viewport footer, after the\n * scroll-to-bottom control — for hosts whose banners + composer float over\n * the scrolling messages (AthenaChat) instead of sitting below the\n * viewport via `belowViewport`/`composer`.\n */\n viewportFooter?: ReactNode;\n /** Replaces the sticky viewport-footer chrome. */\n viewportFooterClassName?: string;\n /**\n * Marks the transcript read-only: mutating message actions (Edit,\n * Regenerate) hide while Copy stays available. For threads whose server\n * rejects dispatch (e.g. legacy pre-cutover conversations) — the runtime's\n * static capability flags cannot express a per-thread fence.\n */\n readOnly?: boolean;\n /**\n * The backend id of the thread on screen. When this surface picked the\n * conversation up from another one (`recordHandoffArrival`), the transcript\n * shows \"Continued from …\" after the last message that was already there.\n * A host that is a handover destination also mounts `HandoffCursorReveal`\n * for it in `viewportOverlay`, so the transcript opens at the message the\n * other surface was reading (hosts that never receive one, the Office pane,\n * don't carry it).\n */\n arrivalThreadId?: string | null;\n /**\n * The signed-in viewer's Athena user id (`user_…`), for the answer labels\n * (`AnswerLabel`): their own answers read \"for you\", and an answer for\n * another person is labelled even in a 1:1 chat. Omitted, answers name the\n * person by name and are labelled only once the thread holds several agents\n * or several people.\n */\n viewerUserId?: string | null;\n}\n\n/**\n * The shared thread layout: panel chrome, scroll viewport with welcome +\n * messages + running indicator, the sticky scroll-to-bottom control, and the\n * host-supplied composer dock.\n *\n * Hosts express their genuine divergences through the slots above; the\n * layout, scroll behaviour, and empty/running states stay identical across\n * surfaces so fixes land once.\n */\nexport function ChatThreadShell({\n overlay,\n header,\n banners,\n viewportOverlay,\n welcome,\n beforeMessages,\n components,\n renderMessages,\n belowViewport,\n composer,\n panelClassName,\n maxWidth = \"100%\",\n turnAnchor,\n viewportClassName,\n contentClassName,\n runningIndicator = <ThreadRunningIndicator />,\n scrollToBottom = <DefaultScrollToBottom />,\n viewportFooter,\n viewportFooterClassName,\n readOnly = false,\n arrivalThreadId = null,\n viewerUserId = null,\n}: ChatThreadShellProps) {\n const {\n UserMessage,\n AssistantMessage,\n UserEditComposer: UserEditComposerComponent = UserEditComposer,\n } = components;\n // A row resolves its renderer by component identity, so keep the merged map\n // referentially stable across renders.\n const messageComponents = useMemo(\n () => ({\n UserMessage: withArrivalDivider(UserMessage),\n AssistantMessage: withArrivalDivider(AssistantMessage),\n UserEditComposer: UserEditComposerComponent,\n }),\n [UserMessage, AssistantMessage, UserEditComposerComponent],\n );\n const messages = (\n <HandoffArrivalScope threadId={arrivalThreadId}>\n <AnswerLabelScope viewerUserId={viewerUserId}>\n <ThreadRows components={messageComponents} />\n </AnswerLabelScope>\n </HandoffArrivalScope>\n );\n return (\n <MessageActionsReadOnlyProvider readOnly={readOnly}>\n <ThreadPrimitive.Root\n className={panelClassName ?? THREAD_PANEL_CLASS}\n style={{ \"--thread-max-width\": maxWidth } as CSSProperties}\n >\n {overlay}\n {header}\n {banners}\n <ThreadPrimitive.Viewport\n turnAnchor={turnAnchor}\n className={viewportClassName ?? DEFAULT_VIEWPORT_CLASS}\n data-aui-thread-viewport=\"\"\n >\n {viewportOverlay}\n {/* No min-h-0 here: the column's automatic minimum height is what\n lets it extend past one screen, keeping the sticky footer's flow\n position after the full message list. Capping it detaches the\n footer (scroll pill, docked composers) as soon as the thread\n scrolls — proven in a live-browser probe of both structures. */}\n <div className=\"flex flex-1 flex-col items-center\">\n <div className={contentClassName ?? DEFAULT_CONTENT_CLASS}>\n {welcome != null ? (\n <ThreadPrimitive.Empty>{welcome}</ThreadPrimitive.Empty>\n ) : null}\n {beforeMessages}\n {renderMessages ? renderMessages(messages) : messages}\n {runningIndicator}\n </div>\n </div>\n <ThreadPrimitive.ViewportFooter\n className={viewportFooterClassName ?? DEFAULT_VIEWPORT_FOOTER_CLASS}\n {...{ [THREAD_VIEWPORT_FOOTER_ATTR]: \"\" }}\n >\n {scrollToBottom}\n {viewportFooter}\n </ThreadPrimitive.ViewportFooter>\n </ThreadPrimitive.Viewport>\n {belowViewport}\n {composer}\n </ThreadPrimitive.Root>\n </MessageActionsReadOnlyProvider>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAiCA,MAAM,qBAAsB,uBAAM;AAChC,QAAM,8BAAc,QAAA;AACpB,SAAO,CAAC,YAAoB;AAC1B,UAAM,WAAW,QAAQ,IAAI,OAAO;AACpC,QAAI,SAAU,QAAO;AAGrB,UAAM,qBAAyB,MAC7B,qBAAA,UAAA,EACE,UAAA;AAAA,MAAA,oBAAC,SAAA,EAAQ;AAAA,0BACR,yBAAA,EAAwB;AAAA,0BACxB,4BAAA,CAAA,CAA2B;AAAA,IAAA,GAC9B;AAEF,YAAQ,IAAI,SAAS,kBAAkB;AACvC,WAAO;AAAA,EACT;AACF,GAAA;AAIA,MAAM,oBAAoB;AAa1B,MAAM,aAAsD,CAAC,EAAE,iBAAiB;AAC9E,QAAM,aAAa;AAAA,IAAY,CAAC,MAC9B,EAAE,OAAO,SAAS,IAAI,CAAC,YAAY,gBAAgB,OAAO,CAAC,EAAE,KAAK,iBAAiB;AAAA,EAAA;AAErF,QAAM,UAAU;AAAA,IACd,MAAO,eAAe,KAAK,CAAA,IAAK,WAAW,MAAM,iBAAiB;AAAA,IAClE,CAAC,UAAU;AAAA,EAAA;AAEb,SACE,oBAAA,UAAA,EACG,UAAA,QAAQ,IAAI,CAAC,KAAK,UACjB,oBAAC,gBAAgB,gBAAhB,EAAyC,OAAc,cAAnB,GAA2C,CACjF,GACH;AAEJ;AAEA,MAAM,yBACJ;AACF,MAAM,wBAAwB;AAC9B,MAAM,gCAAgC;AAEtC,MAAM,wBAA4B,MAChC,oBAAC,OAAA,EAAM,WAAW,CAAC,UAAU,CAAC,MAAM,OAAO,SACzC,UAAA,oBAAC,OAAA,EAAI,WAAU,8BACb,UAAA;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,cAAW;AAAA,IACX,OAAM;AAAA,IACN,WAAW;AAAA,IAEX,UAAA,oBAAC,iBAAA,EAAgB,WAAU,WAAA,CAAW;AAAA,EAAA;AACxC,EAAA,CACF,EAAA,CACF;AA8HK,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA,uCAAoB,wBAAA,EAAuB;AAAA,EAC3C,qCAAkB,uBAAA,EAAsB;AAAA,EACxC;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,eAAe;AACjB,GAAyB;AACvB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,kBAAkB,4BAA4B;AAAA,EAAA,IAC5C;AAGJ,QAAM,oBAAoB;AAAA,IACxB,OAAO;AAAA,MACL,aAAa,mBAAmB,WAAW;AAAA,MAC3C,kBAAkB,mBAAmB,gBAAgB;AAAA,MACrD,kBAAkB;AAAA,IAAA;AAAA,IAEpB,CAAC,aAAa,kBAAkB,yBAAyB;AAAA,EAAA;AAE3D,QAAM,WACJ,oBAAC,qBAAA,EAAoB,UAAU,iBAC7B,UAAA,oBAAC,kBAAA,EAAiB,cAChB,UAAA,oBAAC,YAAA,EAAW,YAAY,kBAAA,CAAmB,GAC7C,GACF;AAEF,SACE,oBAAC,kCAA+B,UAC9B,UAAA;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,WAAW,kBAAkB;AAAA,MAC7B,OAAO,EAAE,sBAAsB,SAAA;AAAA,MAE9B,UAAA;AAAA,QAAA;AAAA,QACA;AAAA,QACA;AAAA,QACD;AAAA,UAAC,gBAAgB;AAAA,UAAhB;AAAA,YACC;AAAA,YACA,WAAW,qBAAqB;AAAA,YAChC,4BAAyB;AAAA,YAExB,UAAA;AAAA,cAAA;AAAA,cAMD,oBAAC,SAAI,WAAU,qCACb,+BAAC,OAAA,EAAI,WAAW,oBAAoB,uBACjC,UAAA;AAAA,gBAAA,WAAW,OACV,oBAAC,gBAAgB,OAAhB,EAAuB,mBAAQ,IAC9B;AAAA,gBACH;AAAA,gBACA,iBAAiB,eAAe,QAAQ,IAAI;AAAA,gBAC5C;AAAA,cAAA,EAAA,CACH,EAAA,CACF;AAAA,cACA;AAAA,gBAAC,gBAAgB;AAAA,gBAAhB;AAAA,kBACC,WAAW,2BAA2B;AAAA,kBACrC,GAAG,EAAE,CAAC,2BAA2B,GAAG,GAAA;AAAA,kBAEpC,UAAA;AAAA,oBAAA;AAAA,oBACA;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,YACH;AAAA,UAAA;AAAA,QAAA;AAAA,QAED;AAAA,QACA;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAEL;AAEJ;"}
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const react = require("@assistant-ui/react");
4
+ const require$$0 = require("react");
5
+ const statewireHistoryEdge = require("./statewire-history-edge.cjs");
6
+ const handoffArrival = require("../../agent-runtime/src/handoff-arrival.cjs");
7
+ const handoffCursor = require("../../agent-runtime/src/handoff-cursor.cjs");
8
+ const editCommand = require("../../agent-runtime/src/edit-command.cjs");
9
+ const THREAD_ROOT_SELECTOR = "[data-aui-thread-root]";
10
+ const HANDOFF_CURSOR_REVEALED_ATTR = "data-handoff-cursor-revealed";
11
+ const HANDOFF_CURSOR_POLL_MS = 150;
12
+ const escapeAttributeValue = (value) => typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
13
+ function findThreadViewport(anchor) {
14
+ var _a;
15
+ if (!anchor) return null;
16
+ const inside = anchor.closest(`[${statewireHistoryEdge.THREAD_VIEWPORT_ATTR}]`);
17
+ if (inside) return inside;
18
+ return ((_a = anchor.closest(THREAD_ROOT_SELECTOR)) == null ? void 0 : _a.querySelector(`[${statewireHistoryEdge.THREAD_VIEWPORT_ATTR}]`)) ?? null;
19
+ }
20
+ function coveredBottomOf(viewport) {
21
+ const footer = viewport.querySelector(`[${statewireHistoryEdge.THREAD_VIEWPORT_FOOTER_ATTR}]`);
22
+ if (!footer) return 0;
23
+ return Math.max(0, viewport.getBoundingClientRect().bottom - footer.getBoundingClientRect().top);
24
+ }
25
+ function readThreadReadingCursor(viewport) {
26
+ if (!viewport) return null;
27
+ return handoffCursor.readingCursorOf({ viewport, coveredBottomPx: coveredBottomOf(viewport) });
28
+ }
29
+ function useReadThreadReadingCursor() {
30
+ const store = react.useThreadViewportStore({ optional: true });
31
+ return require$$0.useCallback(
32
+ (anchor) => readThreadReadingCursor((store == null ? void 0 : store.getState().element.viewport) ?? findThreadViewport(anchor)),
33
+ [store]
34
+ );
35
+ }
36
+ function scrollMessageIntoView({
37
+ viewport,
38
+ element
39
+ }) {
40
+ const box = viewport.getBoundingClientRect();
41
+ const rect = element.getBoundingClientRect();
42
+ const visible = Math.max(1, box.height - coveredBottomOf(viewport));
43
+ const offsetTop = rect.top - box.top + viewport.scrollTop;
44
+ const top = rect.height >= visible ? offsetTop - 8 : offsetTop - (visible - rect.height) / 2;
45
+ viewport.scrollTo({ top: Math.max(0, top), behavior: "instant" });
46
+ }
47
+ function isInView({ viewport, element }) {
48
+ const box = viewport.getBoundingClientRect();
49
+ const rect = element.getBoundingClientRect();
50
+ return rect.bottom > box.top && rect.top < box.bottom - coveredBottomOf(viewport);
51
+ }
52
+ function highlightMessage(element) {
53
+ element.setAttribute(HANDOFF_CURSOR_REVEALED_ATTR, "");
54
+ const animation = typeof element.animate === "function" ? element.animate(
55
+ [
56
+ {
57
+ backgroundColor: "rgba(37, 99, 235, 0.12)",
58
+ boxShadow: "0 0 0 2px rgba(37, 99, 235, 0.35)",
59
+ borderRadius: "12px"
60
+ },
61
+ {
62
+ backgroundColor: "rgba(37, 99, 235, 0.12)",
63
+ boxShadow: "0 0 0 2px rgba(37, 99, 235, 0.35)",
64
+ borderRadius: "12px",
65
+ offset: 0.5
66
+ },
67
+ {
68
+ backgroundColor: "rgba(37, 99, 235, 0)",
69
+ boxShadow: "0 0 0 2px rgba(37, 99, 235, 0)",
70
+ borderRadius: "12px"
71
+ }
72
+ ],
73
+ { duration: handoffCursor.HANDOFF_CURSOR_HIGHLIGHT_MS, easing: "ease-out" }
74
+ ) : null;
75
+ const timer = setTimeout(
76
+ () => element.removeAttribute(HANDOFF_CURSOR_REVEALED_ATTR),
77
+ handoffCursor.HANDOFF_CURSOR_HIGHLIGHT_MS
78
+ );
79
+ return () => {
80
+ clearTimeout(timer);
81
+ animation == null ? void 0 : animation.cancel();
82
+ element.removeAttribute(HANDOFF_CURSOR_REVEALED_ATTR);
83
+ };
84
+ }
85
+ function startHandoffCursorReveal({
86
+ cursor,
87
+ readThread,
88
+ readViewport,
89
+ onSettled,
90
+ now = () => Date.now()
91
+ }) {
92
+ let waitingSince = now();
93
+ let pagesLoaded = 0;
94
+ let paging = false;
95
+ let pageLandsAt = 0;
96
+ let settled = false;
97
+ let revealed = null;
98
+ let personScrolled = false;
99
+ let unhighlight = null;
100
+ let detach = null;
101
+ const settle = () => {
102
+ if (settled) return;
103
+ settled = true;
104
+ clearInterval(timer);
105
+ detach == null ? void 0 : detach();
106
+ onSettled();
107
+ };
108
+ const pageOlder = () => {
109
+ paging = true;
110
+ editCommand.loadOlderDeepAgentHistoryPage({
111
+ readExtras: () => editCommand.readDeepAgentStatewireExtras(readThread().extras)
112
+ }).then((loaded) => {
113
+ if (!loaded) {
114
+ pagesLoaded = Number.POSITIVE_INFINITY;
115
+ return;
116
+ }
117
+ pagesLoaded += 1;
118
+ waitingSince = now();
119
+ }).catch(() => {
120
+ pagesLoaded = Number.POSITIVE_INFINITY;
121
+ }).finally(() => {
122
+ paging = false;
123
+ pageLandsAt = now() + 2 * HANDOFF_CURSOR_POLL_MS;
124
+ });
125
+ };
126
+ const tick = () => {
127
+ if (settled) return;
128
+ if (revealed) {
129
+ if (personScrolled || now() - revealed.at >= handoffCursor.HANDOFF_CURSOR_SETTLE_MS) {
130
+ settle();
131
+ return;
132
+ }
133
+ if (revealed.element.isConnected && !isInView(revealed)) scrollMessageIntoView(revealed);
134
+ return;
135
+ }
136
+ const viewport = readViewport();
137
+ const { messages, isLoading, extras } = readThread();
138
+ const targetId = handoffCursor.handoffCursorMessageId({ messages, cursor });
139
+ const element = targetId !== null && viewport ? viewport.querySelector(
140
+ `[${handoffCursor.MESSAGE_ROW_ID_ATTRIBUTE}="${escapeAttributeValue(targetId)}"]`
141
+ ) : null;
142
+ const step = handoffCursor.handoffCursorRevealStep({
143
+ found: element !== null,
144
+ // Still attaching, a page in flight, or the row not rendered yet.
145
+ loading: isLoading || paging || now() < pageLandsAt || messages.length === 0 || targetId !== null && !element,
146
+ hasOlder: editCommand.deepAgentHistoryHasOlder(extras),
147
+ pagesLoaded,
148
+ elapsedMs: now() - waitingSince
149
+ });
150
+ switch (step) {
151
+ case "reveal": {
152
+ if (!viewport || !element) return;
153
+ scrollMessageIntoView({ viewport, element });
154
+ unhighlight = highlightMessage(element);
155
+ revealed = { element, viewport, at: now() };
156
+ const stop = () => {
157
+ personScrolled = true;
158
+ };
159
+ const events = ["wheel", "touchstart", "pointerdown", "keydown"];
160
+ for (const event of events) viewport.addEventListener(event, stop, { passive: true });
161
+ detach = () => {
162
+ for (const event of events) viewport.removeEventListener(event, stop);
163
+ };
164
+ return;
165
+ }
166
+ case "page":
167
+ pageOlder();
168
+ return;
169
+ case "wait":
170
+ return;
171
+ case "bottom":
172
+ settle();
173
+ return;
174
+ }
175
+ };
176
+ const timer = setInterval(tick, HANDOFF_CURSOR_POLL_MS);
177
+ tick();
178
+ return () => {
179
+ if (settled) return;
180
+ settled = true;
181
+ clearInterval(timer);
182
+ detach == null ? void 0 : detach();
183
+ unhighlight == null ? void 0 : unhighlight();
184
+ };
185
+ }
186
+ const HandoffCursorReveal = ({ threadId }) => {
187
+ const cursor = handoffArrival.useHandoffArrivalCursor(threadId);
188
+ const aui = react.useAui();
189
+ const viewport = react.useThreadViewport({
190
+ optional: true,
191
+ selector: (state) => state.element.viewport
192
+ });
193
+ const viewportRef = require$$0.useRef(viewport);
194
+ viewportRef.current = viewport;
195
+ require$$0.useEffect(() => {
196
+ if (!threadId || !cursor) return void 0;
197
+ return startHandoffCursorReveal({
198
+ cursor,
199
+ readThread: () => {
200
+ const state = aui.thread().getState();
201
+ return { messages: state.messages, isLoading: state.isLoading, extras: state.extras };
202
+ },
203
+ readViewport: () => viewportRef.current ?? null,
204
+ onSettled: () => handoffArrival.settleHandoffArrivalCursor(threadId)
205
+ });
206
+ }, [aui, cursor, threadId]);
207
+ return null;
208
+ };
209
+ exports.HANDOFF_CURSOR_POLL_MS = HANDOFF_CURSOR_POLL_MS;
210
+ exports.HANDOFF_CURSOR_REVEALED_ATTR = HANDOFF_CURSOR_REVEALED_ATTR;
211
+ exports.HandoffCursorReveal = HandoffCursorReveal;
212
+ exports.findThreadViewport = findThreadViewport;
213
+ exports.readThreadReadingCursor = readThreadReadingCursor;
214
+ exports.startHandoffCursorReveal = startHandoffCursorReveal;
215
+ exports.useReadThreadReadingCursor = useReadThreadReadingCursor;
216
+ //# sourceMappingURL=handoff-cursor.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff-cursor.cjs","sources":["../../../src/chat-ui/thread/handoff-cursor.tsx"],"sourcesContent":["/**\n * Handovers open where the person was reading (`@athenaintel/agent-runtime`'s\n * `handoff-cursor`), for every host of `ChatThreadShell` (Athena in Chrome,\n * the desktop app, `AthenaChat`).\n *\n * As a source, `useReadThreadReadingCursor` reads the shell's viewport at the\n * moment of the click: `null` at the bottom, else the id of the last message\n * fully in view. As a destination, `HandoffCursorReveal` (mounted by the shell\n * for its `arrivalThreadId`) scrolls to the message the arrival names once the\n * transcript has it, paging older history in when it is further back, and\n * highlights it briefly; a cursor it can't find leaves the view at the bottom.\n */\nimport {\n type ThreadMessage,\n type ThreadViewportState,\n useAui,\n useThreadViewport,\n useThreadViewportStore,\n} from \"@assistant-ui/react\";\nimport {\n deepAgentHistoryHasOlder,\n HANDOFF_CURSOR_HIGHLIGHT_MS,\n HANDOFF_CURSOR_SETTLE_MS,\n handoffCursorMessageId,\n handoffCursorRevealStep,\n loadOlderDeepAgentHistoryPage,\n MESSAGE_ROW_ID_ATTRIBUTE,\n readDeepAgentStatewireExtras,\n readingCursorOf,\n settleHandoffArrivalCursor,\n useHandoffArrivalCursor,\n} from \"@athenaintel/agent-runtime\";\nimport type { FC } from \"react\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { THREAD_VIEWPORT_ATTR, THREAD_VIEWPORT_FOOTER_ATTR } from \"./statewire-history-edge\";\n/** What assistant-ui's `ThreadPrimitive.Root` stamps on the thread panel. */\nconst THREAD_ROOT_SELECTOR = \"[data-aui-thread-root]\";\n/** Set on the revealed message while it is highlighted. */\nexport const HANDOFF_CURSOR_REVEALED_ATTR = \"data-handoff-cursor-revealed\";\n/** How often a landing looks for its message while the transcript loads. */\nexport const HANDOFF_CURSOR_POLL_MS = 150;\n\nconst escapeAttributeValue = (value: string): string =>\n typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\"\n ? CSS.escape(value)\n : value.replace(/[\"\\\\]/g, \"\\\\$&\");\n\n/** The thread viewport `anchor` sits in, or the one of the thread panel it sits in. */\nexport function findThreadViewport(anchor: Element | null | undefined): HTMLElement | null {\n if (!anchor) return null;\n const inside = anchor.closest<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`);\n if (inside) return inside;\n return (\n anchor.closest(THREAD_ROOT_SELECTOR)?.querySelector<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`) ??\n null\n );\n}\n\n/** How much of `viewport` its sticky footer (the composer dock) covers at the bottom. */\nfunction coveredBottomOf(viewport: HTMLElement): number {\n const footer = viewport.querySelector<HTMLElement>(`[${THREAD_VIEWPORT_FOOTER_ATTR}]`);\n if (!footer) return 0;\n return Math.max(0, viewport.getBoundingClientRect().bottom - footer.getBoundingClientRect().top);\n}\n\n/** Where `viewport` is being read: `null` at the bottom, else a message id. */\nexport function readThreadReadingCursor(viewport: HTMLElement | null): string | null {\n if (!viewport) return null;\n return readingCursorOf({ viewport, coveredBottomPx: coveredBottomOf(viewport) });\n}\n\n/**\n * Reads where the thread is being read, at the moment of a click: inside the\n * viewport (a composer toolbar, even from a portaled dialog) through the\n * thread's viewport store; elsewhere in the panel (a header menu) from the\n * DOM around `anchor`. `null` at the bottom or with no viewport found.\n */\nexport function useReadThreadReadingCursor(): (anchor?: Element | null) => string | null {\n const store = useThreadViewportStore({ optional: true });\n return useCallback(\n (anchor?: Element | null) =>\n readThreadReadingCursor(store?.getState().element.viewport ?? findThreadViewport(anchor)),\n [store],\n );\n}\n\n/** Scrolls `element` into `viewport`'s visible part: centred, or its top when taller. */\nfunction scrollMessageIntoView({\n viewport,\n element,\n}: {\n viewport: HTMLElement;\n element: HTMLElement;\n}): void {\n const box = viewport.getBoundingClientRect();\n const rect = element.getBoundingClientRect();\n const visible = Math.max(1, box.height - coveredBottomOf(viewport));\n const offsetTop = rect.top - box.top + viewport.scrollTop;\n const top =\n rect.height >= visible ? offsetTop - 8 : offsetTop - (visible - rect.height) / 2;\n // `instant`: the shell's viewport scrolls smoothly by default.\n viewport.scrollTo({ top: Math.max(0, top), behavior: \"instant\" });\n}\n\nfunction isInView({ viewport, element }: { viewport: HTMLElement; element: HTMLElement }): boolean {\n const box = viewport.getBoundingClientRect();\n const rect = element.getBoundingClientRect();\n return rect.bottom > box.top && rect.top < box.bottom - coveredBottomOf(viewport);\n}\n\n/**\n * A brief tint on the revealed message, through the Web Animations API so it\n * needs nothing from the host's stylesheet. Returns its undo.\n */\nfunction highlightMessage(element: HTMLElement): () => void {\n element.setAttribute(HANDOFF_CURSOR_REVEALED_ATTR, \"\");\n const animation =\n typeof element.animate === \"function\"\n ? element.animate(\n [\n {\n backgroundColor: \"rgba(37, 99, 235, 0.12)\",\n boxShadow: \"0 0 0 2px rgba(37, 99, 235, 0.35)\",\n borderRadius: \"12px\",\n },\n {\n backgroundColor: \"rgba(37, 99, 235, 0.12)\",\n boxShadow: \"0 0 0 2px rgba(37, 99, 235, 0.35)\",\n borderRadius: \"12px\",\n offset: 0.5,\n },\n {\n backgroundColor: \"rgba(37, 99, 235, 0)\",\n boxShadow: \"0 0 0 2px rgba(37, 99, 235, 0)\",\n borderRadius: \"12px\",\n },\n ],\n { duration: HANDOFF_CURSOR_HIGHLIGHT_MS, easing: \"ease-out\" },\n )\n : null;\n const timer = setTimeout(\n () => element.removeAttribute(HANDOFF_CURSOR_REVEALED_ATTR),\n HANDOFF_CURSOR_HIGHLIGHT_MS,\n );\n return () => {\n clearTimeout(timer);\n animation?.cancel();\n element.removeAttribute(HANDOFF_CURSOR_REVEALED_ATTR);\n };\n}\n\ninterface ThreadSnapshot {\n messages: readonly ThreadMessage[];\n isLoading: boolean;\n extras: unknown;\n}\n\n/**\n * Opens the transcript at `cursor`: looks for the message every\n * {@link HANDOFF_CURSOR_POLL_MS}, pages older history in while it is further\n * back, scrolls to it, holds it in view while the transcript settles (until\n * the person scrolls), and highlights it. Calls `onSettled` once it revealed\n * the message or gave up (the bottom). Returns its cancel.\n */\nexport function startHandoffCursorReveal({\n cursor,\n readThread,\n readViewport,\n onSettled,\n now = () => Date.now(),\n}: {\n cursor: string;\n readThread: () => ThreadSnapshot;\n readViewport: () => HTMLElement | null;\n onSettled: () => void;\n now?: () => number;\n}): () => void {\n // Progress restarts the wait: the reveal started, or a page of older history landed.\n let waitingSince = now();\n let pagesLoaded = 0;\n let paging = false;\n // A page that landed renders on a later commit: look again before paging on.\n let pageLandsAt = 0;\n let settled = false;\n let revealed: { element: HTMLElement; viewport: HTMLElement; at: number } | null = null;\n let personScrolled = false;\n let unhighlight: (() => void) | null = null;\n let detach: (() => void) | null = null;\n\n const settle = () => {\n if (settled) return;\n settled = true;\n clearInterval(timer);\n detach?.();\n onSettled();\n };\n const pageOlder = () => {\n paging = true;\n loadOlderDeepAgentHistoryPage({\n readExtras: () => readDeepAgentStatewireExtras(readThread().extras),\n })\n .then((loaded) => {\n if (!loaded) {\n // Nothing older could be stated: the history is exhausted.\n pagesLoaded = Number.POSITIVE_INFINITY;\n return;\n }\n pagesLoaded += 1;\n waitingSince = now();\n })\n .catch(() => {\n pagesLoaded = Number.POSITIVE_INFINITY;\n })\n .finally(() => {\n paging = false;\n pageLandsAt = now() + 2 * HANDOFF_CURSOR_POLL_MS;\n });\n };\n const tick = () => {\n if (settled) return;\n if (revealed) {\n if (personScrolled || now() - revealed.at >= HANDOFF_CURSOR_SETTLE_MS) {\n settle();\n return;\n }\n if (revealed.element.isConnected && !isInView(revealed)) scrollMessageIntoView(revealed);\n return;\n }\n const viewport = readViewport();\n const { messages, isLoading, extras } = readThread();\n const targetId = handoffCursorMessageId({ messages, cursor });\n const element =\n targetId !== null && viewport\n ? viewport.querySelector<HTMLElement>(\n `[${MESSAGE_ROW_ID_ATTRIBUTE}=\"${escapeAttributeValue(targetId)}\"]`,\n )\n : null;\n const step = handoffCursorRevealStep({\n found: element !== null,\n // Still attaching, a page in flight, or the row not rendered yet.\n loading:\n isLoading ||\n paging ||\n now() < pageLandsAt ||\n messages.length === 0 ||\n (targetId !== null && !element),\n hasOlder: deepAgentHistoryHasOlder(extras),\n pagesLoaded,\n elapsedMs: now() - waitingSince,\n });\n switch (step) {\n case \"reveal\": {\n if (!viewport || !element) return;\n scrollMessageIntoView({ viewport, element });\n unhighlight = highlightMessage(element);\n revealed = { element, viewport, at: now() };\n const stop = () => {\n personScrolled = true;\n };\n const events = [\"wheel\", \"touchstart\", \"pointerdown\", \"keydown\"] as const;\n for (const event of events) viewport.addEventListener(event, stop, { passive: true });\n detach = () => {\n for (const event of events) viewport.removeEventListener(event, stop);\n };\n return;\n }\n case \"page\":\n pageOlder();\n return;\n case \"wait\":\n return;\n case \"bottom\":\n settle();\n return;\n }\n };\n const timer = setInterval(tick, HANDOFF_CURSOR_POLL_MS);\n tick();\n return () => {\n // Once settled, the highlight fades out on its own timer.\n if (settled) return;\n settled = true;\n clearInterval(timer);\n detach?.();\n unhighlight?.();\n };\n}\n\n/**\n * Opens `threadId`'s transcript at the message the source was reading, once,\n * when this surface picked the conversation up with a cursor\n * (`recordHandoffArrival({ cursor })`). Renders nothing.\n */\nexport const HandoffCursorReveal: FC<{ threadId: string | null | undefined }> = ({ threadId }) => {\n const cursor = useHandoffArrivalCursor(threadId);\n const aui = useAui();\n const viewport = useThreadViewport({\n optional: true,\n selector: (state: ThreadViewportState) => state.element.viewport,\n });\n const viewportRef = useRef(viewport);\n viewportRef.current = viewport;\n useEffect(() => {\n if (!threadId || !cursor) return undefined;\n return startHandoffCursorReveal({\n cursor,\n readThread: () => {\n const state = aui.thread().getState();\n return { messages: state.messages, isLoading: state.isLoading, extras: state.extras };\n },\n readViewport: () => viewportRef.current ?? null,\n onSettled: () => settleHandoffArrivalCursor(threadId),\n });\n }, [aui, cursor, threadId]);\n return null;\n};\n"],"names":["THREAD_VIEWPORT_ATTR","THREAD_VIEWPORT_FOOTER_ATTR","readingCursorOf","useThreadViewportStore","useCallback","HANDOFF_CURSOR_HIGHLIGHT_MS","loadOlderDeepAgentHistoryPage","readDeepAgentStatewireExtras","HANDOFF_CURSOR_SETTLE_MS","handoffCursorMessageId","MESSAGE_ROW_ID_ATTRIBUTE","handoffCursorRevealStep","deepAgentHistoryHasOlder","useHandoffArrivalCursor","useAui","useThreadViewport","useRef","useEffect","settleHandoffArrivalCursor"],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Handovers open where the person was reading (`@athenaintel/agent-runtime`'s
3
+ * `handoff-cursor`), for every host of `ChatThreadShell` (Athena in Chrome,
4
+ * the desktop app, `AthenaChat`).
5
+ *
6
+ * As a source, `useReadThreadReadingCursor` reads the shell's viewport at the
7
+ * moment of the click: `null` at the bottom, else the id of the last message
8
+ * fully in view. As a destination, `HandoffCursorReveal` (mounted by the shell
9
+ * for its `arrivalThreadId`) scrolls to the message the arrival names once the
10
+ * transcript has it, paging older history in when it is further back, and
11
+ * highlights it briefly; a cursor it can't find leaves the view at the bottom.
12
+ */
13
+ import { type ThreadMessage } from "@assistant-ui/react";
14
+ import type { FC } from "react";
15
+ /** Set on the revealed message while it is highlighted. */
16
+ export declare const HANDOFF_CURSOR_REVEALED_ATTR = "data-handoff-cursor-revealed";
17
+ /** How often a landing looks for its message while the transcript loads. */
18
+ export declare const HANDOFF_CURSOR_POLL_MS = 150;
19
+ /** The thread viewport `anchor` sits in, or the one of the thread panel it sits in. */
20
+ export declare function findThreadViewport(anchor: Element | null | undefined): HTMLElement | null;
21
+ /** Where `viewport` is being read: `null` at the bottom, else a message id. */
22
+ export declare function readThreadReadingCursor(viewport: HTMLElement | null): string | null;
23
+ /**
24
+ * Reads where the thread is being read, at the moment of a click: inside the
25
+ * viewport (a composer toolbar, even from a portaled dialog) through the
26
+ * thread's viewport store; elsewhere in the panel (a header menu) from the
27
+ * DOM around `anchor`. `null` at the bottom or with no viewport found.
28
+ */
29
+ export declare function useReadThreadReadingCursor(): (anchor?: Element | null) => string | null;
30
+ interface ThreadSnapshot {
31
+ messages: readonly ThreadMessage[];
32
+ isLoading: boolean;
33
+ extras: unknown;
34
+ }
35
+ /**
36
+ * Opens the transcript at `cursor`: looks for the message every
37
+ * {@link HANDOFF_CURSOR_POLL_MS}, pages older history in while it is further
38
+ * back, scrolls to it, holds it in view while the transcript settles (until
39
+ * the person scrolls), and highlights it. Calls `onSettled` once it revealed
40
+ * the message or gave up (the bottom). Returns its cancel.
41
+ */
42
+ export declare function startHandoffCursorReveal({ cursor, readThread, readViewport, onSettled, now, }: {
43
+ cursor: string;
44
+ readThread: () => ThreadSnapshot;
45
+ readViewport: () => HTMLElement | null;
46
+ onSettled: () => void;
47
+ now?: () => number;
48
+ }): () => void;
49
+ /**
50
+ * Opens `threadId`'s transcript at the message the source was reading, once,
51
+ * when this surface picked the conversation up with a cursor
52
+ * (`recordHandoffArrival({ cursor })`). Renders nothing.
53
+ */
54
+ export declare const HandoffCursorReveal: FC<{
55
+ threadId: string | null | undefined;
56
+ }>;
57
+ export {};