@athenaintel/react 0.22.3 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/README.md +394 -4
  2. package/dist/chat/AthenaChat.cjs +389 -240
  3. package/dist/chat/AthenaChat.cjs.map +1 -1
  4. package/dist/chat/AthenaChat.d.ts +26 -8
  5. package/dist/chat/AthenaChat.js +394 -245
  6. package/dist/chat/AthenaChat.js.map +1 -1
  7. package/dist/chat/StatewireApprovalCard.cjs +2 -1
  8. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  9. package/dist/chat/StatewireApprovalCard.js +2 -1
  10. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  11. package/dist/chat/StatewireClientToolBridge.cjs +1 -0
  12. package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
  13. package/dist/chat/StatewireClientToolBridge.js +1 -0
  14. package/dist/chat/StatewireClientToolBridge.js.map +1 -1
  15. package/dist/chat/StatewireQueuedMessages.cjs +1 -0
  16. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  17. package/dist/chat/StatewireQueuedMessages.js +1 -0
  18. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  19. package/dist/chat/WaitingOnYouBanner.cjs +100 -0
  20. package/dist/chat/WaitingOnYouBanner.cjs.map +1 -0
  21. package/dist/chat/WaitingOnYouBanner.d.ts +29 -0
  22. package/dist/chat/WaitingOnYouBanner.js +100 -0
  23. package/dist/chat/WaitingOnYouBanner.js.map +1 -0
  24. package/dist/chat/athena-chat-welcome.cjs +148 -0
  25. package/dist/chat/athena-chat-welcome.cjs.map +1 -0
  26. package/dist/chat/athena-chat-welcome.d.ts +54 -0
  27. package/dist/chat/athena-chat-welcome.js +148 -0
  28. package/dist/chat/athena-chat-welcome.js.map +1 -0
  29. package/dist/chat/athena-composer-controls.cjs +76 -0
  30. package/dist/chat/athena-composer-controls.cjs.map +1 -0
  31. package/dist/chat/athena-composer-controls.d.ts +21 -0
  32. package/dist/chat/athena-composer-controls.js +76 -0
  33. package/dist/chat/athena-composer-controls.js.map +1 -0
  34. package/dist/chat/composer-keyboard-hint.cjs +61 -0
  35. package/dist/chat/composer-keyboard-hint.cjs.map +1 -0
  36. package/dist/chat/composer-keyboard-hint.d.ts +33 -0
  37. package/dist/chat/composer-keyboard-hint.js +61 -0
  38. package/dist/chat/composer-keyboard-hint.js.map +1 -0
  39. package/dist/chat/message-time.cjs +64 -0
  40. package/dist/chat/message-time.cjs.map +1 -0
  41. package/dist/chat/message-time.d.ts +45 -0
  42. package/dist/chat/message-time.js +64 -0
  43. package/dist/chat/message-time.js.map +1 -0
  44. package/dist/chat/run-activity.cjs +38 -7
  45. package/dist/chat/run-activity.cjs.map +1 -1
  46. package/dist/chat/run-activity.d.ts +14 -3
  47. package/dist/chat/run-activity.js +39 -8
  48. package/dist/chat/run-activity.js.map +1 -1
  49. package/dist/chat/statewire-client-tools.cjs +2 -1
  50. package/dist/chat/statewire-client-tools.cjs.map +1 -1
  51. package/dist/chat/statewire-client-tools.js +2 -1
  52. package/dist/chat/statewire-client-tools.js.map +1 -1
  53. package/dist/chat/super-grouping/activity-timeline.cjs +2 -1
  54. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  55. package/dist/chat/super-grouping/activity-timeline.js +2 -1
  56. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  57. package/dist/chat/thread-scroll-to-bottom.cjs +24 -0
  58. package/dist/chat/thread-scroll-to-bottom.cjs.map +1 -0
  59. package/dist/chat/thread-scroll-to-bottom.d.ts +11 -0
  60. package/dist/chat/thread-scroll-to-bottom.js +24 -0
  61. package/dist/chat/thread-scroll-to-bottom.js.map +1 -0
  62. package/dist/chat/tiptap-text.cjs +26 -99
  63. package/dist/chat/tiptap-text.cjs.map +1 -1
  64. package/dist/chat/tiptap-text.d.ts +13 -0
  65. package/dist/chat/tiptap-text.js +26 -99
  66. package/dist/chat/tiptap-text.js.map +1 -1
  67. package/dist/chat/use-athena-run-live.cjs +2 -1
  68. package/dist/chat/use-athena-run-live.cjs.map +1 -1
  69. package/dist/chat/use-athena-run-live.js +2 -1
  70. package/dist/chat/use-athena-run-live.js.map +1 -1
  71. package/dist/chat/use-statewire-approval-request.cjs +1 -0
  72. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  73. package/dist/chat/use-statewire-approval-request.js +1 -0
  74. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  75. package/dist/chat-ui/composer/tiptap-composer.cjs +39 -4
  76. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  77. package/dist/chat-ui/composer/tiptap-composer.d.ts +21 -0
  78. package/dist/chat-ui/composer/tiptap-composer.js +39 -4
  79. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  80. package/dist/chat-ui/index.cjs +15 -1
  81. package/dist/chat-ui/index.cjs.map +1 -1
  82. package/dist/chat-ui/index.d.ts +6 -3
  83. package/dist/chat-ui/index.js +16 -2
  84. package/dist/chat-ui/index.js.map +1 -1
  85. package/dist/chat-ui/mentions/mention-chip.cjs +31 -4
  86. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  87. package/dist/chat-ui/mentions/mention-chip.js +31 -4
  88. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  89. package/dist/chat-ui/messages/message-components.cjs +19 -1
  90. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  91. package/dist/chat-ui/messages/message-components.d.ts +13 -0
  92. package/dist/chat-ui/messages/message-components.js +19 -1
  93. package/dist/chat-ui/messages/message-components.js.map +1 -1
  94. package/dist/chat-ui/messages/runtime-message.cjs +2 -1
  95. package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
  96. package/dist/chat-ui/messages/runtime-message.js +2 -1
  97. package/dist/chat-ui/messages/runtime-message.js.map +1 -1
  98. package/dist/chat-ui/messages/statewire-continue-run.cjs +1 -0
  99. package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
  100. package/dist/chat-ui/messages/statewire-continue-run.js +1 -0
  101. package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
  102. package/dist/chat-ui/messages/user-edit-composer.cjs +3 -1
  103. package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
  104. package/dist/chat-ui/messages/user-edit-composer.js +3 -1
  105. package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
  106. package/dist/chat-ui/messages/wake-marker.cjs +84 -0
  107. package/dist/chat-ui/messages/wake-marker.cjs.map +1 -0
  108. package/dist/chat-ui/messages/wake-marker.d.ts +16 -0
  109. package/dist/chat-ui/messages/wake-marker.js +84 -0
  110. package/dist/chat-ui/messages/wake-marker.js.map +1 -0
  111. package/dist/chat-ui/rich-text/chat-blocks.cjs +177 -0
  112. package/dist/chat-ui/rich-text/chat-blocks.cjs.map +1 -0
  113. package/dist/chat-ui/rich-text/chat-blocks.d.ts +58 -0
  114. package/dist/chat-ui/rich-text/chat-blocks.js +177 -0
  115. package/dist/chat-ui/rich-text/chat-blocks.js.map +1 -0
  116. package/dist/chat-ui/rich-text/chat-link.cjs +69 -0
  117. package/dist/chat-ui/rich-text/chat-link.cjs.map +1 -0
  118. package/dist/chat-ui/rich-text/chat-link.d.ts +24 -0
  119. package/dist/chat-ui/rich-text/chat-link.js +69 -0
  120. package/dist/chat-ui/rich-text/chat-link.js.map +1 -0
  121. package/dist/chat-ui/rich-text/chat-message-body.cjs +144 -18
  122. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
  123. package/dist/chat-ui/rich-text/chat-message-body.d.ts +10 -1
  124. package/dist/chat-ui/rich-text/chat-message-body.js +149 -23
  125. package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
  126. package/dist/chat-ui/rich-text/chat-node-views.cjs +84 -4
  127. package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -1
  128. package/dist/chat-ui/rich-text/chat-node-views.d.ts +12 -1
  129. package/dist/chat-ui/rich-text/chat-node-views.js +85 -5
  130. package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -1
  131. package/dist/chat-ui/rich-text/chat-text-schema.cjs +21 -3
  132. package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -1
  133. package/dist/chat-ui/rich-text/chat-text-schema.d.ts +17 -2
  134. package/dist/chat-ui/rich-text/chat-text-schema.js +21 -3
  135. package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -1
  136. package/dist/chat-ui/rich-text/citation-anchors.cjs +139 -0
  137. package/dist/chat-ui/rich-text/citation-anchors.cjs.map +1 -0
  138. package/dist/chat-ui/rich-text/citation-anchors.d.ts +74 -0
  139. package/dist/chat-ui/rich-text/citation-anchors.js +139 -0
  140. package/dist/chat-ui/rich-text/citation-anchors.js.map +1 -0
  141. package/dist/chat-ui/statewire.cjs +8 -1
  142. package/dist/chat-ui/statewire.cjs.map +1 -1
  143. package/dist/chat-ui/statewire.d.ts +2 -0
  144. package/dist/chat-ui/statewire.js +8 -1
  145. package/dist/chat-ui/statewire.js.map +1 -1
  146. package/dist/chat-ui/thread/chat-composer-dock.cjs +27 -6
  147. package/dist/chat-ui/thread/chat-composer-dock.cjs.map +1 -1
  148. package/dist/chat-ui/thread/chat-composer-dock.d.ts +21 -1
  149. package/dist/chat-ui/thread/chat-composer-dock.js +28 -7
  150. package/dist/chat-ui/thread/chat-composer-dock.js.map +1 -1
  151. package/dist/chat-ui/thread/statewire-history-edge.cjs +12 -13
  152. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  153. package/dist/chat-ui/thread/statewire-history-edge.d.ts +11 -10
  154. package/dist/chat-ui/thread/statewire-history-edge.js +13 -14
  155. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  156. package/dist/chat-ui/thread/thread-loading-overlay.cjs +31 -0
  157. package/dist/chat-ui/thread/thread-loading-overlay.cjs.map +1 -0
  158. package/dist/chat-ui/thread/thread-loading-overlay.d.ts +14 -0
  159. package/dist/chat-ui/thread/thread-loading-overlay.js +31 -0
  160. package/dist/chat-ui/thread/thread-loading-overlay.js.map +1 -0
  161. package/dist/composer/ComposerDropZone.cjs +8 -1
  162. package/dist/composer/ComposerDropZone.cjs.map +1 -1
  163. package/dist/composer/ComposerDropZone.d.ts +6 -0
  164. package/dist/composer/ComposerDropZone.js +9 -2
  165. package/dist/composer/ComposerDropZone.js.map +1 -1
  166. package/dist/diagnostics/errors.cjs +20 -0
  167. package/dist/diagnostics/errors.cjs.map +1 -1
  168. package/dist/diagnostics/errors.d.ts +20 -0
  169. package/dist/diagnostics/errors.js +20 -0
  170. package/dist/diagnostics/errors.js.map +1 -1
  171. package/dist/index.cjs +47 -1
  172. package/dist/index.cjs.map +1 -1
  173. package/dist/index.d.ts +26 -7
  174. package/dist/index.js +51 -5
  175. package/dist/index.js.map +1 -1
  176. package/dist/lib/use-prefers-reduced-motion.cjs +29 -0
  177. package/dist/lib/use-prefers-reduced-motion.cjs.map +1 -0
  178. package/dist/lib/use-prefers-reduced-motion.d.ts +6 -0
  179. package/dist/lib/use-prefers-reduced-motion.js +29 -0
  180. package/dist/lib/use-prefers-reduced-motion.js.map +1 -0
  181. package/dist/packages/agent-runtime/src/conversation-index.cjs.map +1 -1
  182. package/dist/packages/agent-runtime/src/conversation-index.js.map +1 -1
  183. package/dist/packages/agent-runtime/src/conversation-list.cjs +59 -0
  184. package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
  185. package/dist/packages/agent-runtime/src/conversation-list.js +60 -1
  186. package/dist/packages/agent-runtime/src/conversation-list.js.map +1 -1
  187. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.cjs +5 -3
  188. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
  189. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.js +5 -3
  190. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
  191. package/dist/packages/agent-runtime/src/edit-command.cjs +21 -17
  192. package/dist/packages/agent-runtime/src/edit-command.cjs.map +1 -1
  193. package/dist/packages/agent-runtime/src/edit-command.js +22 -18
  194. package/dist/packages/agent-runtime/src/edit-command.js.map +1 -1
  195. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +9 -11
  196. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  197. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +9 -11
  198. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  199. package/dist/packages/agent-runtime/src/github-wake-event.cjs +86 -0
  200. package/dist/packages/agent-runtime/src/github-wake-event.cjs.map +1 -0
  201. package/dist/packages/agent-runtime/src/github-wake-event.js +86 -0
  202. package/dist/packages/agent-runtime/src/github-wake-event.js.map +1 -0
  203. package/dist/packages/agent-runtime/src/message-window.cjs +150 -42
  204. package/dist/packages/agent-runtime/src/message-window.cjs.map +1 -1
  205. package/dist/packages/agent-runtime/src/message-window.js +151 -43
  206. package/dist/packages/agent-runtime/src/message-window.js.map +1 -1
  207. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +13 -9
  208. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  209. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +13 -9
  210. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  211. package/dist/packages/agent-runtime/src/statewire-suspended.cjs +1 -3
  212. package/dist/packages/agent-runtime/src/statewire-suspended.cjs.map +1 -1
  213. package/dist/packages/agent-runtime/src/statewire-suspended.js +1 -3
  214. package/dist/packages/agent-runtime/src/statewire-suspended.js.map +1 -1
  215. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -1
  216. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js.map +1 -1
  217. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs +61 -30
  218. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  219. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js +59 -28
  220. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  221. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.cjs +56 -36
  222. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
  223. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.js +59 -39
  224. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
  225. package/dist/packages/react/package.json.cjs +1 -1
  226. package/dist/packages/react/package.json.js +1 -1
  227. package/dist/provider/AthenaContext.cjs.map +1 -1
  228. package/dist/provider/AthenaContext.d.ts +3 -0
  229. package/dist/provider/AthenaContext.js.map +1 -1
  230. package/dist/provider/AthenaProvider.cjs +93 -45
  231. package/dist/provider/AthenaProvider.cjs.map +1 -1
  232. package/dist/provider/AthenaProvider.d.ts +6 -1
  233. package/dist/provider/AthenaProvider.js +97 -49
  234. package/dist/provider/AthenaProvider.js.map +1 -1
  235. package/dist/runtime/statewire-lifecycle.cjs +2 -1
  236. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  237. package/dist/runtime/statewire-lifecycle.js +2 -1
  238. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  239. package/dist/runtime/useAthenaStatewireRuntime.cjs +3 -2
  240. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  241. package/dist/runtime/useAthenaStatewireRuntime.d.ts +8 -12
  242. package/dist/runtime/useAthenaStatewireRuntime.js +3 -2
  243. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  244. package/dist/search/useAthenaRepoSearch.cjs +94 -0
  245. package/dist/search/useAthenaRepoSearch.cjs.map +1 -0
  246. package/dist/search/useAthenaRepoSearch.d.ts +95 -0
  247. package/dist/search/useAthenaRepoSearch.js +94 -0
  248. package/dist/search/useAthenaRepoSearch.js.map +1 -0
  249. package/dist/styles.css +1 -1
  250. package/dist/threads/SessionList.cjs +486 -0
  251. package/dist/threads/SessionList.cjs.map +1 -0
  252. package/dist/threads/SessionList.d.ts +60 -0
  253. package/dist/threads/SessionList.js +486 -0
  254. package/dist/threads/SessionList.js.map +1 -0
  255. package/dist/threads/api.cjs +112 -4
  256. package/dist/threads/api.cjs.map +1 -1
  257. package/dist/threads/api.d.ts +128 -1
  258. package/dist/threads/api.js +113 -5
  259. package/dist/threads/api.js.map +1 -1
  260. package/dist/threads/conversation-list-fetcher.cjs +27 -3
  261. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  262. package/dist/threads/conversation-list-fetcher.d.ts +16 -3
  263. package/dist/threads/conversation-list-fetcher.js +27 -3
  264. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  265. package/dist/threads/index.d.ts +5 -1
  266. package/dist/threads/session-list-model.cjs +130 -0
  267. package/dist/threads/session-list-model.cjs.map +1 -0
  268. package/dist/threads/session-list-model.d.ts +62 -0
  269. package/dist/threads/session-list-model.js +130 -0
  270. package/dist/threads/session-list-model.js.map +1 -0
  271. package/dist/threads/statewire-thread-list.cjs +220 -38
  272. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  273. package/dist/threads/statewire-thread-list.d.ts +152 -2
  274. package/dist/threads/statewire-thread-list.js +222 -40
  275. package/dist/threads/statewire-thread-list.js.map +1 -1
  276. package/dist/threads/thread-prefetcher.cjs +63 -0
  277. package/dist/threads/thread-prefetcher.cjs.map +1 -0
  278. package/dist/threads/thread-prefetcher.d.ts +54 -0
  279. package/dist/threads/thread-prefetcher.js +63 -0
  280. package/dist/threads/thread-prefetcher.js.map +1 -0
  281. package/dist/threads/use-athena-session-attention.cjs +22 -0
  282. package/dist/threads/use-athena-session-attention.cjs.map +1 -0
  283. package/dist/threads/use-athena-session-attention.d.ts +24 -0
  284. package/dist/threads/use-athena-session-attention.js +22 -0
  285. package/dist/threads/use-athena-session-attention.js.map +1 -0
  286. package/dist/threads/use-rest-conversation-history.cjs +25 -2
  287. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  288. package/dist/threads/use-rest-conversation-history.d.ts +8 -1
  289. package/dist/threads/use-rest-conversation-history.js +25 -2
  290. package/dist/threads/use-rest-conversation-history.js.map +1 -1
  291. package/dist/threads/use-statewire-conversation-history.cjs +40 -16
  292. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  293. package/dist/threads/use-statewire-conversation-history.d.ts +13 -0
  294. package/dist/threads/use-statewire-conversation-history.js +40 -16
  295. package/dist/threads/use-statewire-conversation-history.js.map +1 -1
  296. package/dist/threads/use-thread-archive.cjs +128 -0
  297. package/dist/threads/use-thread-archive.cjs.map +1 -0
  298. package/dist/threads/use-thread-archive.d.ts +26 -0
  299. package/dist/threads/use-thread-archive.js +128 -0
  300. package/dist/threads/use-thread-archive.js.map +1 -0
  301. package/dist/threads/use-thread-favorite.cjs +53 -0
  302. package/dist/threads/use-thread-favorite.cjs.map +1 -0
  303. package/dist/threads/use-thread-favorite.d.ts +49 -0
  304. package/dist/threads/use-thread-favorite.js +53 -0
  305. package/dist/threads/use-thread-favorite.js.map +1 -0
  306. package/dist/threads/use-thread-field-override.cjs +184 -0
  307. package/dist/threads/use-thread-field-override.cjs.map +1 -0
  308. package/dist/threads/use-thread-field-override.d.ts +55 -0
  309. package/dist/threads/use-thread-field-override.js +184 -0
  310. package/dist/threads/use-thread-field-override.js.map +1 -0
  311. package/dist/threads/use-thread-rename.cjs +53 -0
  312. package/dist/threads/use-thread-rename.cjs.map +1 -0
  313. package/dist/threads/use-thread-rename.d.ts +48 -0
  314. package/dist/threads/use-thread-rename.js +53 -0
  315. package/dist/threads/use-thread-rename.js.map +1 -0
  316. package/dist/tools/index.cjs +5 -1
  317. package/dist/tools/index.cjs.map +1 -1
  318. package/dist/tools/index.d.ts +1 -1
  319. package/dist/tools/index.js +7 -3
  320. package/dist/tools/index.js.map +1 -1
  321. package/dist/tools/misc-tool-uis.cjs +2 -1
  322. package/dist/tools/misc-tool-uis.cjs.map +1 -1
  323. package/dist/tools/misc-tool-uis.js +2 -1
  324. package/dist/tools/misc-tool-uis.js.map +1 -1
  325. package/dist/tools/primitives.cjs +25 -2
  326. package/dist/tools/primitives.cjs.map +1 -1
  327. package/dist/tools/primitives.d.ts +9 -2
  328. package/dist/tools/primitives.js +26 -3
  329. package/dist/tools/primitives.js.map +1 -1
  330. package/dist/tools/tool-appearance.cjs +11 -0
  331. package/dist/tools/tool-appearance.cjs.map +1 -0
  332. package/dist/tools/tool-appearance.d.ts +29 -0
  333. package/dist/tools/tool-appearance.js +11 -0
  334. package/dist/tools/tool-appearance.js.map +1 -0
  335. package/dist/tools/tool-fallback.cjs +211 -13
  336. package/dist/tools/tool-fallback.cjs.map +1 -1
  337. package/dist/tools/tool-fallback.d.ts +31 -4
  338. package/dist/tools/tool-fallback.js +214 -16
  339. package/dist/tools/tool-fallback.js.map +1 -1
  340. package/dist/tools/use-elapsed-seconds.cjs +24 -0
  341. package/dist/tools/use-elapsed-seconds.cjs.map +1 -0
  342. package/dist/tools/use-elapsed-seconds.d.ts +10 -0
  343. package/dist/tools/use-elapsed-seconds.js +24 -0
  344. package/dist/tools/use-elapsed-seconds.js.map +1 -0
  345. package/dist/tools/voice-update-tool-ui.cjs +2 -1
  346. package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
  347. package/dist/tools/voice-update-tool-ui.js +2 -1
  348. package/dist/tools/voice-update-tool-ui.js.map +1 -1
  349. package/package.json +1 -1
  350. package/src/chat-ui/tailwind-preset.js +14 -1
@@ -1 +1 @@
1
- {"version":3,"file":"chat-node-views.js","sources":["../../../src/chat-ui/rich-text/chat-node-views.tsx"],"sourcesContent":["import type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\nimport {\n NodeViewContent,\n NodeViewWrapper,\n type NodeViewProps,\n} from \"@tiptap/react\";\nimport type { ReactNode } from \"react\";\nimport { MentionLink } from \"../mentions/mention-chip\";\nimport {\n makeMentionUrl,\n type MentionNodeAttrs,\n} from \"../mentions/mention-extension\";\n\nexport function ChatMention({\n node,\n interactive = true,\n}: {\n node: ProseMirrorNode;\n interactive?: boolean;\n}) {\n return (\n <span\n className=\"mention-chip\"\n data-mention=\"\"\n data-type={node.attrs.type}\n data-name={node.attrs.name}\n data-id={node.attrs.id}\n data-asset-type={node.attrs.assetType}\n data-display-as-preview={String(node.attrs.displayAsPreview === true)}\n >\n <MentionLink\n href={makeMentionUrl(node.attrs as MentionNodeAttrs)}\n interactive={interactive}\n >\n {node.attrs.name}\n </MentionLink>\n </span>\n );\n}\n\nexport function ChatMentionNodeView({ node, editor }: NodeViewProps) {\n return (\n <NodeViewWrapper as=\"span\">\n <ChatMention node={node} interactive={!editor.isEditable} />\n </NodeViewWrapper>\n );\n}\n\nexport function ChatCodeBlock({\n node,\n children,\n}: {\n node: ProseMirrorNode;\n children: ReactNode;\n}) {\n return (\n <div className=\"not-prose my-2 overflow-hidden rounded-lg border border-border bg-muted/30\">\n {node.attrs.language && (\n <div className=\"border-b border-border bg-muted/50 px-3 py-1 text-[10px] font-medium text-muted-foreground\">\n {node.attrs.language}\n </div>\n )}\n <pre className=\"overflow-x-auto p-3 font-mono text-xs leading-relaxed text-foreground\">\n {children}\n </pre>\n </div>\n );\n}\n\nexport function ChatCodeBlockNodeView({ node }: NodeViewProps) {\n return (\n <NodeViewWrapper>\n <ChatCodeBlock node={node}>\n <NodeViewContent as=\"code\" />\n </ChatCodeBlock>\n </NodeViewWrapper>\n );\n}\n"],"names":[],"mappings":";;;;AAaO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,cAAc;AAChB,GAGG;AACD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAU;AAAA,MACV,gBAAa;AAAA,MACb,aAAW,KAAK,MAAM;AAAA,MACtB,aAAW,KAAK,MAAM;AAAA,MACtB,WAAS,KAAK,MAAM;AAAA,MACpB,mBAAiB,KAAK,MAAM;AAAA,MAC5B,2BAAyB,OAAO,KAAK,MAAM,qBAAqB,IAAI;AAAA,MAEpE,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAM,eAAe,KAAK,KAAyB;AAAA,UACnD;AAAA,UAEC,eAAK,MAAM;AAAA,QAAA;AAAA,MAAA;AAAA,IACd;AAAA,EAAA;AAGN;AAEO,SAAS,oBAAoB,EAAE,MAAM,UAAyB;AACnE,SACE,oBAAC,iBAAA,EAAgB,IAAG,QAClB,UAAA,oBAAC,aAAA,EAAY,MAAY,aAAa,CAAC,OAAO,WAAA,CAAY,EAAA,CAC5D;AAEJ;AAEO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AACF,GAGG;AACD,SACE,qBAAC,OAAA,EAAI,WAAU,8EACZ,UAAA;AAAA,IAAA,KAAK,MAAM,YACV,oBAAC,OAAA,EAAI,WAAU,8FACZ,UAAA,KAAK,MAAM,SAAA,CACd;AAAA,IAEF,oBAAC,OAAA,EAAI,WAAU,yEACZ,SAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAEO,SAAS,sBAAsB,EAAE,QAAuB;AAC7D,SACE,oBAAC,iBAAA,EACC,UAAA,oBAAC,eAAA,EAAc,MACb,8BAAC,iBAAA,EAAgB,IAAG,OAAA,CAAO,EAAA,CAC7B,GACF;AAEJ;"}
1
+ {"version":3,"file":"chat-node-views.js","sources":["../../../src/chat-ui/rich-text/chat-node-views.tsx"],"sourcesContent":["import type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\nimport {\n NodeViewContent,\n NodeViewWrapper,\n type NodeViewProps,\n} from \"@tiptap/react\";\nimport { CheckIcon, CopyIcon, XIcon } from \"lucide-react\";\nimport { type ReactNode, useCallback, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { MentionLink } from \"../mentions/mention-chip\";\nimport {\n makeMentionUrl,\n type MentionNodeAttrs,\n} from \"../mentions/mention-extension\";\n\nexport function ChatMention({\n node,\n interactive = true,\n}: {\n node: ProseMirrorNode;\n interactive?: boolean;\n}) {\n return (\n <span\n className=\"mention-chip\"\n data-mention=\"\"\n data-type={node.attrs.type}\n data-name={node.attrs.name}\n data-id={node.attrs.id}\n data-asset-type={node.attrs.assetType}\n data-display-as-preview={String(node.attrs.displayAsPreview === true)}\n >\n <MentionLink\n href={makeMentionUrl(node.attrs as MentionNodeAttrs)}\n interactive={interactive}\n >\n {node.attrs.name}\n </MentionLink>\n </span>\n );\n}\n\nexport function ChatMentionNodeView({ node, editor }: NodeViewProps) {\n return (\n <NodeViewWrapper as=\"span\">\n <ChatMention node={node} interactive={!editor.isEditable} />\n </NodeViewWrapper>\n );\n}\n\nconst COPIED_RESET_MS = 1500;\n\n/**\n * Copies the block's source. Reads `node.textContent` at click time so a\n * still-streaming block copies what is on screen, not a stale snapshot.\n */\nfunction CodeBlockCopyButton({ node }: { node: ProseMirrorNode }) {\n const [status, setStatus] = useState<\"idle\" | \"copied\" | \"failed\">(\"idle\");\n const resetTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(\n () => () => {\n if (resetTimer.current) clearTimeout(resetTimer.current);\n },\n [],\n );\n const flash = useCallback((next: \"copied\" | \"failed\") => {\n setStatus(next);\n if (resetTimer.current) clearTimeout(resetTimer.current);\n resetTimer.current = setTimeout(() => setStatus(\"idle\"), COPIED_RESET_MS);\n }, []);\n const onCopy = useCallback(() => {\n // writeText rejects when clipboard-write is denied (permissions, an\n // embedding iframe without the allow attribute); surface that instead of\n // leaving an unhandled rejection and a button that looks like it worked.\n if (typeof navigator === \"undefined\" || !navigator.clipboard) {\n flash(\"failed\");\n return;\n }\n navigator.clipboard.writeText(node.textContent).then(\n () => flash(\"copied\"),\n () => flash(\"failed\"),\n );\n }, [node, flash]);\n const label =\n status === \"copied\" ? \"Copied\" : status === \"failed\" ? \"Copy failed\" : \"Copy code\";\n return (\n <button\n type=\"button\"\n title={label}\n aria-label={label}\n onClick={onCopy}\n className={cn(\n \"inline-flex size-6 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground\",\n \"opacity-0 transition-opacity group-hover/code-block:opacity-100 focus-visible:opacity-100\",\n status !== \"idle\" && \"opacity-100\",\n status === \"failed\" && \"text-destructive\",\n )}\n >\n {status === \"copied\" ? (\n <CheckIcon className=\"size-3.5\" />\n ) : status === \"failed\" ? (\n <XIcon className=\"size-3.5\" />\n ) : (\n <CopyIcon className=\"size-3.5\" />\n )}\n </button>\n );\n}\n\n/** The frame both code renderings share: language label, an optional corner action, the `<pre>`. */\nfunction CodeBlockFrame({\n language,\n action,\n children,\n ...attributes\n}: {\n language: string | null | undefined;\n action?: ReactNode;\n children: ReactNode;\n \"data-chat-open-fence\"?: string;\n}) {\n return (\n <div\n className=\"not-prose group/code-block relative my-2 overflow-hidden rounded-lg border border-border bg-muted/30\"\n {...attributes}\n >\n {language && (\n <div className=\"border-b border-border bg-muted/50 px-3 py-1 text-[10px] font-medium text-muted-foreground\">\n {language}\n </div>\n )}\n {action && (\n <div className=\"absolute top-1 right-1\" contentEditable={false}>\n {action}\n </div>\n )}\n <pre className=\"overflow-x-auto p-3 pr-9 font-mono text-xs leading-relaxed text-foreground\">\n {children}\n </pre>\n </div>\n );\n}\n\nexport function ChatCodeBlock({\n node,\n children,\n}: {\n node: ProseMirrorNode;\n children: ReactNode;\n}) {\n return (\n <CodeBlockFrame\n language={node.attrs.language}\n action={<CodeBlockCopyButton node={node} />}\n >\n {children}\n </CodeBlockFrame>\n );\n}\n\n/**\n * A fenced block whose closing fence has not arrived: the same frame with the\n * code as plain text and no copy action (there is no settled source to copy\n * yet). Anything that decorates code — highlighting, a language pick — belongs\n * after the fence closes, when the block parses into a `codeBlock` node.\n */\nexport function StreamingCodeBlock({\n language,\n code,\n caret,\n}: {\n language: string;\n code: string;\n caret?: ReactNode;\n}) {\n return (\n <CodeBlockFrame language={language || null} data-chat-open-fence=\"\">\n <code>\n {code}\n {caret}\n </code>\n </CodeBlockFrame>\n );\n}\n\nexport function ChatCodeBlockNodeView({ node }: NodeViewProps) {\n return (\n <NodeViewWrapper>\n <ChatCodeBlock node={node}>\n <NodeViewContent as=\"code\" />\n </ChatCodeBlock>\n </NodeViewWrapper>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -16,21 +16,36 @@ const TaskItem = require("@tiptap/extension-task-item");
16
16
  const tiptapMarkdown = require("tiptap-markdown");
17
17
  const mentionExtension = require("../mentions/mention-extension.cjs");
18
18
  const chatNodeViews = require("./chat-node-views.cjs");
19
+ const citationAnchors = require("./citation-anchors.cjs");
19
20
  const ChatMentionExtension = mentionExtension.MentionExtension.extend({
20
21
  addNodeView: () => react.ReactNodeViewRenderer(chatNodeViews.ChatMentionNodeView, { as: "span" })
21
22
  });
22
23
  const ChatCodeBlockExtension = CodeBlock.extend({
23
24
  addNodeView: () => react.ReactNodeViewRenderer(chatNodeViews.ChatCodeBlockNodeView)
24
25
  });
26
+ const ChatLinkExtension = Link.extend({
27
+ addAttributes() {
28
+ var _a;
29
+ return {
30
+ ...(_a = this.parent) == null ? void 0 : _a.call(this),
31
+ excerpt: {
32
+ default: null,
33
+ parseHTML: (element) => element.getAttribute("data-excerpt"),
34
+ renderHTML: (attributes) => typeof attributes.excerpt === "string" && attributes.excerpt ? { "data-excerpt": attributes.excerpt } : {}
35
+ }
36
+ };
37
+ }
38
+ });
25
39
  function createChatTextExtensions() {
26
40
  return [
27
41
  StarterKit.configure({ codeBlock: false }),
28
42
  ChatCodeBlockExtension,
29
- Link.configure({
43
+ ChatLinkExtension.configure({
30
44
  openOnClick: false,
31
45
  autolink: true,
32
46
  linkOnPaste: true,
33
- protocols: ["athena-point"]
47
+ protocols: ["athena-point"],
48
+ isAllowedUri: (url) => citationAnchors.isAllowedChatHref(url)
34
49
  }),
35
50
  Image.configure({ inline: true }),
36
51
  Table.configure({ resizable: false }),
@@ -61,10 +76,13 @@ function parseChatText(markdown) {
61
76
  }));
62
77
  const html = parserEditor.storage.markdown.parser.parse(markdown);
63
78
  const body = new window.DOMParser().parseFromString(html, "text/html").body;
79
+ citationAnchors.normalizeCitationAnchors(body);
64
80
  return model.DOMParser.fromSchema(parserEditor.schema).parse(body);
65
81
  }
66
- const CHAT_TEXT_CLASS = "aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2";
82
+ const CHAT_TEXT_CLASS = "aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:underline prose-a:decoration-primary/40 prose-a:underline-offset-2 hover:prose-a:decoration-primary prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2";
67
83
  exports.CHAT_TEXT_CLASS = CHAT_TEXT_CLASS;
84
+ exports.ChatCodeBlockExtension = ChatCodeBlockExtension;
85
+ exports.ChatLinkExtension = ChatLinkExtension;
68
86
  exports.createChatTextExtensions = createChatTextExtensions;
69
87
  exports.parseChatText = parseChatText;
70
88
  //# sourceMappingURL=chat-text-schema.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-text-schema.cjs","sources":["../../../src/chat-ui/rich-text/chat-text-schema.tsx"],"sourcesContent":["import { Editor, type Extensions } from \"@tiptap/core\";\nimport { DOMParser as ProseMirrorDOMParser } from \"@tiptap/pm/model\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport CodeBlock from \"@tiptap/extension-code-block\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\nimport Link from \"@tiptap/extension-link\";\nimport Image from \"@tiptap/extension-image\";\nimport Table from \"@tiptap/extension-table\";\nimport TableRow from \"@tiptap/extension-table-row\";\nimport TableCell from \"@tiptap/extension-table-cell\";\nimport TableHeader from \"@tiptap/extension-table-header\";\nimport TaskList from \"@tiptap/extension-task-list\";\nimport TaskItem from \"@tiptap/extension-task-item\";\nimport { Markdown } from \"tiptap-markdown\";\nimport { MentionExtension } from \"../mentions/mention-extension\";\nimport { ChatCodeBlockNodeView, ChatMentionNodeView } from \"./chat-node-views\";\n\nconst ChatMentionExtension = MentionExtension.extend({\n addNodeView: () => ReactNodeViewRenderer(ChatMentionNodeView, { as: \"span\" }),\n});\nconst ChatCodeBlockExtension = CodeBlock.extend({\n addNodeView: () => ReactNodeViewRenderer(ChatCodeBlockNodeView),\n});\n\n/** Both the editable draft and the static message body use this schema and parser. */\nexport function createChatTextExtensions(): Extensions {\n return [\n StarterKit.configure({ codeBlock: false }),\n ChatCodeBlockExtension,\n Link.configure({\n openOnClick: false,\n autolink: true,\n linkOnPaste: true,\n protocols: [\"athena-point\"],\n }),\n Image.configure({ inline: true }),\n Table.configure({ resizable: false }),\n TableRow,\n TableCell,\n TableHeader,\n TaskList,\n TaskItem.configure({ nested: true }),\n Markdown.configure({\n html: true,\n tightLists: true,\n tightListClass: \"tight\",\n bulletListMarker: \"-\",\n linkify: true,\n breaks: true,\n transformPastedText: true,\n transformCopiedText: false,\n }),\n ChatMentionExtension,\n ];\n}\n\nlet parserEditor: Editor | undefined;\n\n/** A single detached parser per browser tab, never an editor per message. */\nexport function parseChatText(markdown: string) {\n parserEditor ??= new Editor({\n extensions: createChatTextExtensions(),\n content: \"\",\n editable: false,\n });\n const html: string = parserEditor.storage.markdown.parser.parse(markdown);\n const body = new window.DOMParser().parseFromString(html, \"text/html\").body;\n return ProseMirrorDOMParser.fromSchema(parserEditor.schema).parse(body);\n}\n\n/** Typography is shared; the composer supplies only its padding and scroll boundary. */\nexport const CHAT_TEXT_CLASS =\n \"aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2\";\n"],"names":["MentionExtension","ReactNodeViewRenderer","ChatMentionNodeView","ChatCodeBlockNodeView","Markdown","Editor","ProseMirrorDOMParser"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,MAAM,uBAAuBA,iBAAAA,iBAAiB,OAAO;AAAA,EACnD,aAAa,MAAMC,MAAAA,sBAAsBC,cAAAA,qBAAqB,EAAE,IAAI,QAAQ;AAC9E,CAAC;AACD,MAAM,yBAAyB,UAAU,OAAO;AAAA,EAC9C,aAAa,MAAMD,MAAAA,sBAAsBE,cAAAA,qBAAqB;AAChE,CAAC;AAGM,SAAS,2BAAuC;AACrD,SAAO;AAAA,IACL,WAAW,UAAU,EAAE,WAAW,OAAO;AAAA,IACzC;AAAA,IACA,KAAK,UAAU;AAAA,MACb,aAAa;AAAA,MACb,UAAU;AAAA,MACV,aAAa;AAAA,MACb,WAAW,CAAC,cAAc;AAAA,IAAA,CAC3B;AAAA,IACD,MAAM,UAAU,EAAE,QAAQ,MAAM;AAAA,IAChC,MAAM,UAAU,EAAE,WAAW,OAAO;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,UAAU,EAAE,QAAQ,MAAM;AAAA,IACnCC,eAAAA,SAAS,UAAU;AAAA,MACjB,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,qBAAqB;AAAA,MACrB,qBAAqB;AAAA,IAAA,CACtB;AAAA,IACD;AAAA,EAAA;AAEJ;AAEA,IAAI;AAGG,SAAS,cAAc,UAAkB;AAC9C,kCAAiB,IAAIC,KAAAA,OAAO;AAAA,IAC1B,YAAY,yBAAA;AAAA,IACZ,SAAS;AAAA,IACT,UAAU;AAAA,EAAA,CACX;AACD,QAAM,OAAe,aAAa,QAAQ,SAAS,OAAO,MAAM,QAAQ;AACxE,QAAM,OAAO,IAAI,OAAO,UAAA,EAAY,gBAAgB,MAAM,WAAW,EAAE;AACvE,SAAOC,MAAAA,UAAqB,WAAW,aAAa,MAAM,EAAE,MAAM,IAAI;AACxE;AAGO,MAAM,kBACX;;;;"}
1
+ {"version":3,"file":"chat-text-schema.cjs","sources":["../../../src/chat-ui/rich-text/chat-text-schema.tsx"],"sourcesContent":["import { Editor, type Extensions } from \"@tiptap/core\";\nimport { DOMParser as ProseMirrorDOMParser } from \"@tiptap/pm/model\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport CodeBlock from \"@tiptap/extension-code-block\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\nimport Link from \"@tiptap/extension-link\";\nimport Image from \"@tiptap/extension-image\";\nimport Table from \"@tiptap/extension-table\";\nimport TableRow from \"@tiptap/extension-table-row\";\nimport TableCell from \"@tiptap/extension-table-cell\";\nimport TableHeader from \"@tiptap/extension-table-header\";\nimport TaskList from \"@tiptap/extension-task-list\";\nimport TaskItem from \"@tiptap/extension-task-item\";\nimport { Markdown } from \"tiptap-markdown\";\nimport { MentionExtension } from \"../mentions/mention-extension\";\nimport { ChatCodeBlockNodeView, ChatMentionNodeView } from \"./chat-node-views\";\nimport { isAllowedChatHref, normalizeCitationAnchors } from \"./citation-anchors\";\n\nconst ChatMentionExtension = MentionExtension.extend({\n addNodeView: () => ReactNodeViewRenderer(ChatMentionNodeView, { as: \"span\" }),\n});\n/** Fenced code rendered through `ChatCodeBlock` (language label + copy button). */\nexport const ChatCodeBlockExtension = CodeBlock.extend({\n addNodeView: () => ReactNodeViewRenderer(ChatCodeBlockNodeView),\n});\n\n/**\n * The Link mark with the excerpt a citation carries (`data-excerpt`, the\n * source chip's tooltip and scroll-to-text target) and an href allow-list\n * narrower than tiptap's default: `http(s):`, `mailto:`, the screen-point\n * protocol and app-relative Spaces paths pass; every other scheme\n * (`javascript:`, `ftp:`, `data:`, protocol-relative `//host`) is dropped and\n * its label kept as text. The same rule governs the composer's autolink.\n */\nexport const ChatLinkExtension = Link.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n excerpt: {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute(\"data-excerpt\"),\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.excerpt === \"string\" && attributes.excerpt\n ? { \"data-excerpt\": attributes.excerpt }\n : {},\n },\n };\n },\n});\n\n/** Both the editable draft and the static message body use this schema and parser. */\nexport function createChatTextExtensions(): Extensions {\n return [\n StarterKit.configure({ codeBlock: false }),\n ChatCodeBlockExtension,\n ChatLinkExtension.configure({\n openOnClick: false,\n autolink: true,\n linkOnPaste: true,\n protocols: [\"athena-point\"],\n isAllowedUri: (url) => isAllowedChatHref(url),\n }),\n Image.configure({ inline: true }),\n Table.configure({ resizable: false }),\n TableRow,\n TableCell,\n TableHeader,\n TaskList,\n TaskItem.configure({ nested: true }),\n Markdown.configure({\n html: true,\n tightLists: true,\n tightListClass: \"tight\",\n bulletListMarker: \"-\",\n linkify: true,\n breaks: true,\n transformPastedText: true,\n transformCopiedText: false,\n }),\n ChatMentionExtension,\n ];\n}\n\nlet parserEditor: Editor | undefined;\n\n/**\n * A single detached parser per browser tab, never an editor per message.\n * Markdown → HTML (markdown-it, with bare URLs linkified and inline HTML kept)\n * → agora's citation anchors given their hrefs → the schema's document.\n */\nexport function parseChatText(markdown: string) {\n parserEditor ??= new Editor({\n extensions: createChatTextExtensions(),\n content: \"\",\n editable: false,\n });\n const html: string = parserEditor.storage.markdown.parser.parse(markdown);\n const body = new window.DOMParser().parseFromString(html, \"text/html\").body;\n normalizeCitationAnchors(body);\n return ProseMirrorDOMParser.fromSchema(parserEditor.schema).parse(body);\n}\n\n/** Typography is shared; the composer supplies only its padding and scroll boundary. */\nexport const CHAT_TEXT_CLASS =\n \"aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:underline prose-a:decoration-primary/40 prose-a:underline-offset-2 hover:prose-a:decoration-primary prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2\";\n"],"names":["MentionExtension","ReactNodeViewRenderer","ChatMentionNodeView","ChatCodeBlockNodeView","isAllowedChatHref","Markdown","Editor","normalizeCitationAnchors","ProseMirrorDOMParser"],"mappings":";;;;;;;;;;;;;;;;;;;AAkBA,MAAM,uBAAuBA,iBAAAA,iBAAiB,OAAO;AAAA,EACnD,aAAa,MAAMC,MAAAA,sBAAsBC,cAAAA,qBAAqB,EAAE,IAAI,QAAQ;AAC9E,CAAC;AAEM,MAAM,yBAAyB,UAAU,OAAO;AAAA,EACrD,aAAa,MAAMD,MAAAA,sBAAsBE,cAAAA,qBAAqB;AAChE,CAAC;AAUM,MAAM,oBAAoB,KAAK,OAAO;AAAA,EAC3C,gBAAgB;;AACd,WAAO;AAAA,MACL,IAAG,UAAK,WAAL;AAAA,MACH,SAAS;AAAA,QACP,SAAS;AAAA,QACT,WAAW,CAAC,YAAyB,QAAQ,aAAa,cAAc;AAAA,QACxE,YAAY,CAAC,eACX,OAAO,WAAW,YAAY,YAAY,WAAW,UACjD,EAAE,gBAAgB,WAAW,QAAA,IAC7B,CAAA;AAAA,MAAC;AAAA,IACT;AAAA,EAEJ;AACF,CAAC;AAGM,SAAS,2BAAuC;AACrD,SAAO;AAAA,IACL,WAAW,UAAU,EAAE,WAAW,OAAO;AAAA,IACzC;AAAA,IACA,kBAAkB,UAAU;AAAA,MAC1B,aAAa;AAAA,MACb,UAAU;AAAA,MACV,aAAa;AAAA,MACb,WAAW,CAAC,cAAc;AAAA,MAC1B,cAAc,CAAC,QAAQC,gBAAAA,kBAAkB,GAAG;AAAA,IAAA,CAC7C;AAAA,IACD,MAAM,UAAU,EAAE,QAAQ,MAAM;AAAA,IAChC,MAAM,UAAU,EAAE,WAAW,OAAO;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,UAAU,EAAE,QAAQ,MAAM;AAAA,IACnCC,eAAAA,SAAS,UAAU;AAAA,MACjB,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,qBAAqB;AAAA,MACrB,qBAAqB;AAAA,IAAA,CACtB;AAAA,IACD;AAAA,EAAA;AAEJ;AAEA,IAAI;AAOG,SAAS,cAAc,UAAkB;AAC9C,kCAAiB,IAAIC,KAAAA,OAAO;AAAA,IAC1B,YAAY,yBAAA;AAAA,IACZ,SAAS;AAAA,IACT,UAAU;AAAA,EAAA,CACX;AACD,QAAM,OAAe,aAAa,QAAQ,SAAS,OAAO,MAAM,QAAQ;AACxE,QAAM,OAAO,IAAI,OAAO,UAAA,EAAY,gBAAgB,MAAM,WAAW,EAAE;AACvEC,kBAAAA,yBAAyB,IAAI;AAC7B,SAAOC,MAAAA,UAAqB,WAAW,aAAa,MAAM,EAAE,MAAM,IAAI;AACxE;AAGO,MAAM,kBACX;;;;;;"}
@@ -1,7 +1,22 @@
1
1
  import { type Extensions } from "@tiptap/core";
2
+ /** Fenced code rendered through `ChatCodeBlock` (language label + copy button). */
3
+ export declare const ChatCodeBlockExtension: import("@tiptap/core").Node<import("@tiptap/extension-code-block").CodeBlockOptions, any>;
4
+ /**
5
+ * The Link mark with the excerpt a citation carries (`data-excerpt`, the
6
+ * source chip's tooltip and scroll-to-text target) and an href allow-list
7
+ * narrower than tiptap's default: `http(s):`, `mailto:`, the screen-point
8
+ * protocol and app-relative Spaces paths pass; every other scheme
9
+ * (`javascript:`, `ftp:`, `data:`, protocol-relative `//host`) is dropped and
10
+ * its label kept as text. The same rule governs the composer's autolink.
11
+ */
12
+ export declare const ChatLinkExtension: import("@tiptap/core").Mark<import("@tiptap/extension-link").LinkOptions, any>;
2
13
  /** Both the editable draft and the static message body use this schema and parser. */
3
14
  export declare function createChatTextExtensions(): Extensions;
4
- /** A single detached parser per browser tab, never an editor per message. */
15
+ /**
16
+ * A single detached parser per browser tab, never an editor per message.
17
+ * Markdown → HTML (markdown-it, with bare URLs linkified and inline HTML kept)
18
+ * → agora's citation anchors given their hrefs → the schema's document.
19
+ */
5
20
  export declare function parseChatText(markdown: string): import("prosemirror-model").Node;
6
21
  /** Typography is shared; the composer supplies only its padding and scroll boundary. */
7
- export declare const CHAT_TEXT_CLASS = "aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2";
22
+ export declare const CHAT_TEXT_CLASS = "aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:underline prose-a:decoration-primary/40 prose-a:underline-offset-2 hover:prose-a:decoration-primary prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2";
@@ -14,21 +14,36 @@ import TaskItem from "@tiptap/extension-task-item";
14
14
  import { Markdown } from "tiptap-markdown";
15
15
  import { MentionExtension } from "../mentions/mention-extension.js";
16
16
  import { ChatCodeBlockNodeView, ChatMentionNodeView } from "./chat-node-views.js";
17
+ import { isAllowedChatHref, normalizeCitationAnchors } from "./citation-anchors.js";
17
18
  const ChatMentionExtension = MentionExtension.extend({
18
19
  addNodeView: () => ReactNodeViewRenderer(ChatMentionNodeView, { as: "span" })
19
20
  });
20
21
  const ChatCodeBlockExtension = CodeBlock.extend({
21
22
  addNodeView: () => ReactNodeViewRenderer(ChatCodeBlockNodeView)
22
23
  });
24
+ const ChatLinkExtension = Link.extend({
25
+ addAttributes() {
26
+ var _a;
27
+ return {
28
+ ...(_a = this.parent) == null ? void 0 : _a.call(this),
29
+ excerpt: {
30
+ default: null,
31
+ parseHTML: (element) => element.getAttribute("data-excerpt"),
32
+ renderHTML: (attributes) => typeof attributes.excerpt === "string" && attributes.excerpt ? { "data-excerpt": attributes.excerpt } : {}
33
+ }
34
+ };
35
+ }
36
+ });
23
37
  function createChatTextExtensions() {
24
38
  return [
25
39
  StarterKit.configure({ codeBlock: false }),
26
40
  ChatCodeBlockExtension,
27
- Link.configure({
41
+ ChatLinkExtension.configure({
28
42
  openOnClick: false,
29
43
  autolink: true,
30
44
  linkOnPaste: true,
31
- protocols: ["athena-point"]
45
+ protocols: ["athena-point"],
46
+ isAllowedUri: (url) => isAllowedChatHref(url)
32
47
  }),
33
48
  Image.configure({ inline: true }),
34
49
  Table.configure({ resizable: false }),
@@ -59,11 +74,14 @@ function parseChatText(markdown) {
59
74
  }));
60
75
  const html = parserEditor.storage.markdown.parser.parse(markdown);
61
76
  const body = new window.DOMParser().parseFromString(html, "text/html").body;
77
+ normalizeCitationAnchors(body);
62
78
  return DOMParser.fromSchema(parserEditor.schema).parse(body);
63
79
  }
64
- const CHAT_TEXT_CLASS = "aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2";
80
+ const CHAT_TEXT_CLASS = "aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:underline prose-a:decoration-primary/40 prose-a:underline-offset-2 hover:prose-a:decoration-primary prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2";
65
81
  export {
66
82
  CHAT_TEXT_CLASS,
83
+ ChatCodeBlockExtension,
84
+ ChatLinkExtension,
67
85
  createChatTextExtensions,
68
86
  parseChatText
69
87
  };
@@ -1 +1 @@
1
- {"version":3,"file":"chat-text-schema.js","sources":["../../../src/chat-ui/rich-text/chat-text-schema.tsx"],"sourcesContent":["import { Editor, type Extensions } from \"@tiptap/core\";\nimport { DOMParser as ProseMirrorDOMParser } from \"@tiptap/pm/model\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport CodeBlock from \"@tiptap/extension-code-block\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\nimport Link from \"@tiptap/extension-link\";\nimport Image from \"@tiptap/extension-image\";\nimport Table from \"@tiptap/extension-table\";\nimport TableRow from \"@tiptap/extension-table-row\";\nimport TableCell from \"@tiptap/extension-table-cell\";\nimport TableHeader from \"@tiptap/extension-table-header\";\nimport TaskList from \"@tiptap/extension-task-list\";\nimport TaskItem from \"@tiptap/extension-task-item\";\nimport { Markdown } from \"tiptap-markdown\";\nimport { MentionExtension } from \"../mentions/mention-extension\";\nimport { ChatCodeBlockNodeView, ChatMentionNodeView } from \"./chat-node-views\";\n\nconst ChatMentionExtension = MentionExtension.extend({\n addNodeView: () => ReactNodeViewRenderer(ChatMentionNodeView, { as: \"span\" }),\n});\nconst ChatCodeBlockExtension = CodeBlock.extend({\n addNodeView: () => ReactNodeViewRenderer(ChatCodeBlockNodeView),\n});\n\n/** Both the editable draft and the static message body use this schema and parser. */\nexport function createChatTextExtensions(): Extensions {\n return [\n StarterKit.configure({ codeBlock: false }),\n ChatCodeBlockExtension,\n Link.configure({\n openOnClick: false,\n autolink: true,\n linkOnPaste: true,\n protocols: [\"athena-point\"],\n }),\n Image.configure({ inline: true }),\n Table.configure({ resizable: false }),\n TableRow,\n TableCell,\n TableHeader,\n TaskList,\n TaskItem.configure({ nested: true }),\n Markdown.configure({\n html: true,\n tightLists: true,\n tightListClass: \"tight\",\n bulletListMarker: \"-\",\n linkify: true,\n breaks: true,\n transformPastedText: true,\n transformCopiedText: false,\n }),\n ChatMentionExtension,\n ];\n}\n\nlet parserEditor: Editor | undefined;\n\n/** A single detached parser per browser tab, never an editor per message. */\nexport function parseChatText(markdown: string) {\n parserEditor ??= new Editor({\n extensions: createChatTextExtensions(),\n content: \"\",\n editable: false,\n });\n const html: string = parserEditor.storage.markdown.parser.parse(markdown);\n const body = new window.DOMParser().parseFromString(html, \"text/html\").body;\n return ProseMirrorDOMParser.fromSchema(parserEditor.schema).parse(body);\n}\n\n/** Typography is shared; the composer supplies only its padding and scroll boundary. */\nexport const CHAT_TEXT_CLASS =\n \"aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2\";\n"],"names":["ProseMirrorDOMParser"],"mappings":";;;;;;;;;;;;;;;;AAiBA,MAAM,uBAAuB,iBAAiB,OAAO;AAAA,EACnD,aAAa,MAAM,sBAAsB,qBAAqB,EAAE,IAAI,QAAQ;AAC9E,CAAC;AACD,MAAM,yBAAyB,UAAU,OAAO;AAAA,EAC9C,aAAa,MAAM,sBAAsB,qBAAqB;AAChE,CAAC;AAGM,SAAS,2BAAuC;AACrD,SAAO;AAAA,IACL,WAAW,UAAU,EAAE,WAAW,OAAO;AAAA,IACzC;AAAA,IACA,KAAK,UAAU;AAAA,MACb,aAAa;AAAA,MACb,UAAU;AAAA,MACV,aAAa;AAAA,MACb,WAAW,CAAC,cAAc;AAAA,IAAA,CAC3B;AAAA,IACD,MAAM,UAAU,EAAE,QAAQ,MAAM;AAAA,IAChC,MAAM,UAAU,EAAE,WAAW,OAAO;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,UAAU,EAAE,QAAQ,MAAM;AAAA,IACnC,SAAS,UAAU;AAAA,MACjB,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,qBAAqB;AAAA,MACrB,qBAAqB;AAAA,IAAA,CACtB;AAAA,IACD;AAAA,EAAA;AAEJ;AAEA,IAAI;AAGG,SAAS,cAAc,UAAkB;AAC9C,kCAAiB,IAAI,OAAO;AAAA,IAC1B,YAAY,yBAAA;AAAA,IACZ,SAAS;AAAA,IACT,UAAU;AAAA,EAAA,CACX;AACD,QAAM,OAAe,aAAa,QAAQ,SAAS,OAAO,MAAM,QAAQ;AACxE,QAAM,OAAO,IAAI,OAAO,UAAA,EAAY,gBAAgB,MAAM,WAAW,EAAE;AACvE,SAAOA,UAAqB,WAAW,aAAa,MAAM,EAAE,MAAM,IAAI;AACxE;AAGO,MAAM,kBACX;"}
1
+ {"version":3,"file":"chat-text-schema.js","sources":["../../../src/chat-ui/rich-text/chat-text-schema.tsx"],"sourcesContent":["import { Editor, type Extensions } from \"@tiptap/core\";\nimport { DOMParser as ProseMirrorDOMParser } from \"@tiptap/pm/model\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport CodeBlock from \"@tiptap/extension-code-block\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\nimport Link from \"@tiptap/extension-link\";\nimport Image from \"@tiptap/extension-image\";\nimport Table from \"@tiptap/extension-table\";\nimport TableRow from \"@tiptap/extension-table-row\";\nimport TableCell from \"@tiptap/extension-table-cell\";\nimport TableHeader from \"@tiptap/extension-table-header\";\nimport TaskList from \"@tiptap/extension-task-list\";\nimport TaskItem from \"@tiptap/extension-task-item\";\nimport { Markdown } from \"tiptap-markdown\";\nimport { MentionExtension } from \"../mentions/mention-extension\";\nimport { ChatCodeBlockNodeView, ChatMentionNodeView } from \"./chat-node-views\";\nimport { isAllowedChatHref, normalizeCitationAnchors } from \"./citation-anchors\";\n\nconst ChatMentionExtension = MentionExtension.extend({\n addNodeView: () => ReactNodeViewRenderer(ChatMentionNodeView, { as: \"span\" }),\n});\n/** Fenced code rendered through `ChatCodeBlock` (language label + copy button). */\nexport const ChatCodeBlockExtension = CodeBlock.extend({\n addNodeView: () => ReactNodeViewRenderer(ChatCodeBlockNodeView),\n});\n\n/**\n * The Link mark with the excerpt a citation carries (`data-excerpt`, the\n * source chip's tooltip and scroll-to-text target) and an href allow-list\n * narrower than tiptap's default: `http(s):`, `mailto:`, the screen-point\n * protocol and app-relative Spaces paths pass; every other scheme\n * (`javascript:`, `ftp:`, `data:`, protocol-relative `//host`) is dropped and\n * its label kept as text. The same rule governs the composer's autolink.\n */\nexport const ChatLinkExtension = Link.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n excerpt: {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute(\"data-excerpt\"),\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.excerpt === \"string\" && attributes.excerpt\n ? { \"data-excerpt\": attributes.excerpt }\n : {},\n },\n };\n },\n});\n\n/** Both the editable draft and the static message body use this schema and parser. */\nexport function createChatTextExtensions(): Extensions {\n return [\n StarterKit.configure({ codeBlock: false }),\n ChatCodeBlockExtension,\n ChatLinkExtension.configure({\n openOnClick: false,\n autolink: true,\n linkOnPaste: true,\n protocols: [\"athena-point\"],\n isAllowedUri: (url) => isAllowedChatHref(url),\n }),\n Image.configure({ inline: true }),\n Table.configure({ resizable: false }),\n TableRow,\n TableCell,\n TableHeader,\n TaskList,\n TaskItem.configure({ nested: true }),\n Markdown.configure({\n html: true,\n tightLists: true,\n tightListClass: \"tight\",\n bulletListMarker: \"-\",\n linkify: true,\n breaks: true,\n transformPastedText: true,\n transformCopiedText: false,\n }),\n ChatMentionExtension,\n ];\n}\n\nlet parserEditor: Editor | undefined;\n\n/**\n * A single detached parser per browser tab, never an editor per message.\n * Markdown → HTML (markdown-it, with bare URLs linkified and inline HTML kept)\n * → agora's citation anchors given their hrefs → the schema's document.\n */\nexport function parseChatText(markdown: string) {\n parserEditor ??= new Editor({\n extensions: createChatTextExtensions(),\n content: \"\",\n editable: false,\n });\n const html: string = parserEditor.storage.markdown.parser.parse(markdown);\n const body = new window.DOMParser().parseFromString(html, \"text/html\").body;\n normalizeCitationAnchors(body);\n return ProseMirrorDOMParser.fromSchema(parserEditor.schema).parse(body);\n}\n\n/** Typography is shared; the composer supplies only its padding and scroll boundary. */\nexport const CHAT_TEXT_CLASS =\n \"aui-md prose prose-sm max-w-none min-w-0 break-words text-sm leading-snug text-foreground prose-p:my-1.5 prose-p:leading-snug prose-headings:mt-3 prose-headings:mb-1.5 prose-headings:font-semibold prose-h1:text-sm prose-h2:text-sm prose-h3:text-xs prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-blockquote:border-l-2 prose-blockquote:border-border prose-blockquote:text-muted-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-xs prose-code:font-mono prose-code:before:content-none prose-code:after:content-none prose-pre:my-2 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/30 prose-pre:p-3 prose-a:text-primary prose-a:underline prose-a:decoration-primary/40 prose-a:underline-offset-2 hover:prose-a:decoration-primary prose-strong:font-semibold prose-hr:border-border [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_table]:block [&_table]:overflow-x-auto [&_td]:border [&_td]:border-border [&_td]:px-2 [&_th]:border [&_th]:border-border [&_th]:px-2 [&_ul[data-type=taskList]]:list-none [&_ul[data-type=taskList]]:pl-0 [&_li[data-type=taskItem]]:flex [&_li[data-type=taskItem]]:gap-2\";\n"],"names":["ProseMirrorDOMParser"],"mappings":";;;;;;;;;;;;;;;;;AAkBA,MAAM,uBAAuB,iBAAiB,OAAO;AAAA,EACnD,aAAa,MAAM,sBAAsB,qBAAqB,EAAE,IAAI,QAAQ;AAC9E,CAAC;AAEM,MAAM,yBAAyB,UAAU,OAAO;AAAA,EACrD,aAAa,MAAM,sBAAsB,qBAAqB;AAChE,CAAC;AAUM,MAAM,oBAAoB,KAAK,OAAO;AAAA,EAC3C,gBAAgB;;AACd,WAAO;AAAA,MACL,IAAG,UAAK,WAAL;AAAA,MACH,SAAS;AAAA,QACP,SAAS;AAAA,QACT,WAAW,CAAC,YAAyB,QAAQ,aAAa,cAAc;AAAA,QACxE,YAAY,CAAC,eACX,OAAO,WAAW,YAAY,YAAY,WAAW,UACjD,EAAE,gBAAgB,WAAW,QAAA,IAC7B,CAAA;AAAA,MAAC;AAAA,IACT;AAAA,EAEJ;AACF,CAAC;AAGM,SAAS,2BAAuC;AACrD,SAAO;AAAA,IACL,WAAW,UAAU,EAAE,WAAW,OAAO;AAAA,IACzC;AAAA,IACA,kBAAkB,UAAU;AAAA,MAC1B,aAAa;AAAA,MACb,UAAU;AAAA,MACV,aAAa;AAAA,MACb,WAAW,CAAC,cAAc;AAAA,MAC1B,cAAc,CAAC,QAAQ,kBAAkB,GAAG;AAAA,IAAA,CAC7C;AAAA,IACD,MAAM,UAAU,EAAE,QAAQ,MAAM;AAAA,IAChC,MAAM,UAAU,EAAE,WAAW,OAAO;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,UAAU,EAAE,QAAQ,MAAM;AAAA,IACnC,SAAS,UAAU;AAAA,MACjB,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,qBAAqB;AAAA,MACrB,qBAAqB;AAAA,IAAA,CACtB;AAAA,IACD;AAAA,EAAA;AAEJ;AAEA,IAAI;AAOG,SAAS,cAAc,UAAkB;AAC9C,kCAAiB,IAAI,OAAO;AAAA,IAC1B,YAAY,yBAAA;AAAA,IACZ,SAAS;AAAA,IACT,UAAU;AAAA,EAAA,CACX;AACD,QAAM,OAAe,aAAa,QAAQ,SAAS,OAAO,MAAM,QAAQ;AACxE,QAAM,OAAO,IAAI,OAAO,UAAA,EAAY,gBAAgB,MAAM,WAAW,EAAE;AACvE,2BAAyB,IAAI;AAC7B,SAAOA,UAAqB,WAAW,aAAa,MAAM,EAAE,MAAM,IAAI;AACxE;AAGO,MAAM,kBACX;"}
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const ASSET_ID = /^asset_[\w-]+$/;
4
+ const MENTION_CLASS = /(?:^|\s)mention(?:\s|$)/;
5
+ const SCHEME = /^([a-z][a-z0-9+.-]*):/i;
6
+ const IGNORED_CHARACTERS = /[\s\p{Cc}\p{Cf}]/gu;
7
+ const SPACES_PATH = /^\/dashboard\/spaces\/?(?:[?#]|$)/;
8
+ const ALLOWED_SCHEMES = /* @__PURE__ */ new Set([
9
+ "http",
10
+ "https",
11
+ "mailto",
12
+ "athena-point"
13
+ ]);
14
+ function isAllowedChatHref(href) {
15
+ var _a, _b;
16
+ const value = href.replace(IGNORED_CHARACTERS, "");
17
+ if (!value) return false;
18
+ const scheme = (_b = (_a = SCHEME.exec(value)) == null ? void 0 : _a[1]) == null ? void 0 : _b.toLowerCase();
19
+ if (scheme !== void 0) return ALLOWED_SCHEMES.has(scheme);
20
+ return SPACES_PATH.test(value);
21
+ }
22
+ function isWebHref(href) {
23
+ var _a, _b;
24
+ const scheme = (_b = (_a = SCHEME.exec(href.replace(IGNORED_CHARACTERS, ""))) == null ? void 0 : _a[1]) == null ? void 0 : _b.toLowerCase();
25
+ return scheme === "http" || scheme === "https";
26
+ }
27
+ function parsePage(value) {
28
+ if (value === null) return null;
29
+ const trimmed = value.trim().replace(/^"|"$/g, "");
30
+ if (!/^\d+$/.test(trimmed)) return null;
31
+ const page = Number(trimmed);
32
+ return page > 0 ? page : null;
33
+ }
34
+ function normalizeAssetType(value) {
35
+ const trimmed = (value == null ? void 0 : value.trim()) ?? "";
36
+ if (!trimmed) return null;
37
+ const bare = trimmed.slice(trimmed.lastIndexOf("/") + 1).toLowerCase();
38
+ return bare && bare !== "url" ? bare : null;
39
+ }
40
+ function buildAssetCitationHref({
41
+ assetId,
42
+ assetType,
43
+ pageNumber,
44
+ endPageNumber,
45
+ excerpt
46
+ }) {
47
+ const params = new URLSearchParams({ asset_ids: assetId });
48
+ const type = normalizeAssetType(assetType);
49
+ if (type) params.set("type", type);
50
+ const start = parsePage(pageNumber);
51
+ const end = parsePage(endPageNumber);
52
+ if (start !== null && end !== null && end > start) {
53
+ params.set("anchor", "page_range");
54
+ params.set("start_page", String(start));
55
+ params.set("end_page", String(end));
56
+ } else if (start !== null) {
57
+ params.set("anchor", "page");
58
+ params.set("page", String(start));
59
+ }
60
+ const trimmedExcerpt = excerpt == null ? void 0 : excerpt.trim();
61
+ if (trimmedExcerpt) params.set("excerpt", trimmedExcerpt);
62
+ return `/dashboard/spaces/?${params.toString()}`;
63
+ }
64
+ function normalizeCitationAnchors(root) {
65
+ var _a;
66
+ for (const anchor of root.querySelectorAll("a")) {
67
+ const label = anchor.firstChild;
68
+ if (MENTION_CLASS.test(anchor.getAttribute("class") ?? "") && (label == null ? void 0 : label.nodeType) === 3 && ((_a = label.textContent) == null ? void 0 : _a.startsWith("@"))) {
69
+ label.textContent = label.textContent.slice(1);
70
+ }
71
+ if (anchor.hasAttribute("href")) continue;
72
+ const type = anchor.getAttribute("data-type");
73
+ const assetId = anchor.getAttribute("data-id");
74
+ const dataUrl = anchor.getAttribute("data-url");
75
+ if (dataUrl && (type === "url" || !assetId)) {
76
+ const href = dataUrl.trim();
77
+ if (isAllowedChatHref(href)) anchor.setAttribute("href", href);
78
+ continue;
79
+ }
80
+ if (assetId && ASSET_ID.test(assetId)) {
81
+ anchor.setAttribute(
82
+ "href",
83
+ buildAssetCitationHref({
84
+ assetId,
85
+ assetType: type,
86
+ pageNumber: anchor.getAttribute("data-page-number"),
87
+ endPageNumber: anchor.getAttribute("data-end-page-number"),
88
+ excerpt: anchor.getAttribute("data-excerpt")
89
+ })
90
+ );
91
+ }
92
+ }
93
+ }
94
+ function buildScrollToTextUrl(url, excerpt) {
95
+ if (!excerpt) return url;
96
+ try {
97
+ new URL(url);
98
+ } catch {
99
+ return url;
100
+ }
101
+ let fragment;
102
+ const separator = excerpt.indexOf("...");
103
+ const start = separator === -1 ? "" : excerpt.slice(0, separator).trim();
104
+ const end = separator === -1 ? "" : excerpt.slice(separator + 3).trim();
105
+ if (start && end) {
106
+ fragment = `${encodeURIComponent(start)},${encodeURIComponent(end)}`;
107
+ } else {
108
+ fragment = encodeURIComponent(excerpt);
109
+ }
110
+ const hash = url.indexOf("#");
111
+ if (hash === -1) return `${url}#:~:text=${fragment}`;
112
+ return `${url.slice(0, hash)}#${url.slice(hash + 1)}:~:text=${fragment}`;
113
+ }
114
+ const PRIVATE_HOSTNAME = /^(?:[^.]+|.*\.(?:local|localhost|internal|intranet|corp|home|lan|private|test|example|invalid|onion))$/i;
115
+ const IP_LITERAL = /^(?:\d{1,3}(?:\.\d{1,3}){3}|\[[0-9a-f:.]+\])$/i;
116
+ function isPublicHostname(hostname) {
117
+ if (!hostname || IP_LITERAL.test(hostname) || PRIVATE_HOSTNAME.test(hostname)) {
118
+ return false;
119
+ }
120
+ return /\.[a-z]{2,}$/i.test(hostname);
121
+ }
122
+ function sourceFaviconUrl(href) {
123
+ if (!isWebHref(href)) return null;
124
+ try {
125
+ const { hostname } = new URL(href);
126
+ if (!isPublicHostname(hostname)) return null;
127
+ return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(hostname)}&sz=32`;
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+ exports.buildAssetCitationHref = buildAssetCitationHref;
133
+ exports.buildScrollToTextUrl = buildScrollToTextUrl;
134
+ exports.isAllowedChatHref = isAllowedChatHref;
135
+ exports.isPublicHostname = isPublicHostname;
136
+ exports.isWebHref = isWebHref;
137
+ exports.normalizeCitationAnchors = normalizeCitationAnchors;
138
+ exports.sourceFaviconUrl = sourceFaviconUrl;
139
+ //# sourceMappingURL=citation-anchors.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"citation-anchors.cjs","sources":["../../../src/chat-ui/rich-text/citation-anchors.ts"],"sourcesContent":["/**\n * Citation anchors in assistant text, made parseable and safe for the chat\n * text schema.\n *\n * Agora's deep agent cites sources as inline HTML anchors that carry no\n * `href` (`agora/agora/graphs/tools/citation_readables.py`,\n * `agora/agora/services/reader/service.py`):\n *\n * <a class=\"mention\" data-type=\"url\" data-url=\"https://…\" data-excerpt=\"…\">Label</a>\n * <a class=\"mention\" data-type=\"pdf\" data-id=\"asset_…\" data-page-number=10\n * data-end-page-number=null data-excerpt=\"…\">Citation</a>\n *\n * ProseMirror's Link mark parses `a[href]` only, so without a rewrite both\n * shapes render as their label text — a source nobody can open. This module\n * rewrites the anchors in the HTML markdown-it produced, before the schema\n * parses it: a URL citation gets its `data-url` as `href`; an asset citation\n * gets the Spaces deep link (`asset_ids`, `type`, the page anchor, `excerpt`)\n * that the mention rule turns into an asset chip. `data-excerpt` stays on the\n * anchor for the Link mark's `excerpt` attribute — the source chip's tooltip\n * and its scroll-to-text target. Working on the parsed DOM rather than the\n * markdown keeps fenced and inline code (where markdown-it has already\n * escaped the tag into text) untouched.\n */\n\n/** `asset_<uuid>`, `asset_thread_<uuid>`, the ids agora hands the model. */\nconst ASSET_ID = /^asset_[\\w-]+$/;\n/** The `mention` class agora and Olympus put on citation anchors. */\nconst MENTION_CLASS = /(?:^|\\s)mention(?:\\s|$)/;\n/** A URL scheme at the start of an href. */\nconst SCHEME = /^([a-z][a-z0-9+.-]*):/i;\n/** Whitespace, control and format characters, which a scheme check must not be fooled by (`java\\nscript:`, a zero-width space). */\nconst IGNORED_CHARACTERS = /[\\s\\p{Cc}\\p{Cf}]/gu;\n/** An app-relative Spaces path — the one relative href assistant text may carry. */\nconst SPACES_PATH = /^\\/dashboard\\/spaces\\/?(?:[?#]|$)/;\n/** Only these schemes pass; every other one is dropped and its label kept as text. */\nconst ALLOWED_SCHEMES: ReadonlySet<string> = new Set([\n \"http\",\n \"https\",\n \"mailto\",\n \"athena-point\",\n]);\n\n/**\n * Whether an href may become a link in chat text: `http(s):`, `mailto:`, the\n * screen-point protocol, or an app-relative Spaces path. Scheme-less\n * protocol-relative (`//host`) and fragment/query-only hrefs fail, and so\n * does every other scheme (`javascript:`, `vbscript:`, `data:`, `ftp:`).\n */\nexport function isAllowedChatHref(href: string): boolean {\n const value = href.replace(IGNORED_CHARACTERS, \"\");\n if (!value) return false;\n const scheme = SCHEME.exec(value)?.[1]?.toLowerCase();\n if (scheme !== undefined) return ALLOWED_SCHEMES.has(scheme);\n return SPACES_PATH.test(value);\n}\n\n/** Whether an href points at a web page (the source chip's domain) rather than an app path or mail address. */\nexport function isWebHref(href: string): boolean {\n const scheme = SCHEME.exec(href.replace(IGNORED_CHARACTERS, \"\"))?.[1]?.toLowerCase();\n return scheme === \"http\" || scheme === \"https\";\n}\n\n/** The page number an agora citation attribute names, or null for `null`, blanks and non-numbers. */\nfunction parsePage(value: string | null): number | null {\n if (value === null) return null;\n const trimmed = value.trim().replace(/^\"|\"$/g, \"\");\n if (!/^\\d+$/.test(trimmed)) return null;\n const page = Number(trimmed);\n return page > 0 ? page : null;\n}\n\n/** `pdf`, `Citation/pdf`, `athena/document` → the bare asset type Spaces expects. */\nfunction normalizeAssetType(value: string | null): string | null {\n const trimmed = value?.trim() ?? \"\";\n if (!trimmed) return null;\n const bare = trimmed.slice(trimmed.lastIndexOf(\"/\") + 1).toLowerCase();\n return bare && bare !== \"url\" ? bare : null;\n}\n\n/**\n * The Spaces deep link for an agora asset citation: the cited asset, its type,\n * a `page` anchor (or `page_range` when the citation spans pages) and the\n * excerpt — the same query the SDK's `parseAthenaCitationLink` reads back\n * into an embed reference when the chip is clicked.\n */\nexport function buildAssetCitationHref({\n assetId,\n assetType,\n pageNumber,\n endPageNumber,\n excerpt,\n}: {\n assetId: string;\n assetType: string | null;\n pageNumber: string | null;\n endPageNumber: string | null;\n excerpt: string | null;\n}): string {\n const params = new URLSearchParams({ asset_ids: assetId });\n const type = normalizeAssetType(assetType);\n if (type) params.set(\"type\", type);\n const start = parsePage(pageNumber);\n const end = parsePage(endPageNumber);\n if (start !== null && end !== null && end > start) {\n params.set(\"anchor\", \"page_range\");\n params.set(\"start_page\", String(start));\n params.set(\"end_page\", String(end));\n } else if (start !== null) {\n params.set(\"anchor\", \"page\");\n params.set(\"page\", String(start));\n }\n const trimmedExcerpt = excerpt?.trim();\n if (trimmedExcerpt) params.set(\"excerpt\", trimmedExcerpt);\n return `/dashboard/spaces/?${params.toString()}`;\n}\n\n/**\n * Give every `href`-less citation anchor under `root` the href the schema's\n * parse rules need. Anchors that already carry an href are left alone (their\n * `data-excerpt`, if any, is read by the Link mark itself).\n */\nexport function normalizeCitationAnchors(root: ParentNode): void {\n for (const anchor of root.querySelectorAll(\"a\")) {\n // Olympus serializes a mention's label as `@Label`; the chip draws the name.\n const label = anchor.firstChild;\n if (\n MENTION_CLASS.test(anchor.getAttribute(\"class\") ?? \"\") &&\n label?.nodeType === 3 &&\n label.textContent?.startsWith(\"@\")\n ) {\n label.textContent = label.textContent.slice(1);\n }\n if (anchor.hasAttribute(\"href\")) continue;\n const type = anchor.getAttribute(\"data-type\");\n const assetId = anchor.getAttribute(\"data-id\");\n const dataUrl = anchor.getAttribute(\"data-url\");\n if (dataUrl && (type === \"url\" || !assetId)) {\n const href = dataUrl.trim();\n if (isAllowedChatHref(href)) anchor.setAttribute(\"href\", href);\n continue;\n }\n if (assetId && ASSET_ID.test(assetId)) {\n anchor.setAttribute(\n \"href\",\n buildAssetCitationHref({\n assetId,\n assetType: type,\n pageNumber: anchor.getAttribute(\"data-page-number\"),\n endPageNumber: anchor.getAttribute(\"data-end-page-number\"),\n excerpt: anchor.getAttribute(\"data-excerpt\"),\n }),\n );\n }\n }\n}\n\n/**\n * The cited page with a Scroll-to-Text Fragment (`#:~:text=`) for the excerpt,\n * so the browser lands on and highlights the quoted passage. Agora's\n * `start...end` range form becomes `text=start,end`. A URL that does not parse\n * is returned as is.\n * @see https://wicg.github.io/scroll-to-text-fragment/\n */\nexport function buildScrollToTextUrl(url: string, excerpt: string | null | undefined): string {\n if (!excerpt) return url;\n try {\n new URL(url);\n } catch {\n return url;\n }\n let fragment: string;\n const separator = excerpt.indexOf(\"...\");\n const start = separator === -1 ? \"\" : excerpt.slice(0, separator).trim();\n const end = separator === -1 ? \"\" : excerpt.slice(separator + 3).trim();\n if (start && end) {\n fragment = `${encodeURIComponent(start)},${encodeURIComponent(end)}`;\n } else {\n fragment = encodeURIComponent(excerpt);\n }\n const hash = url.indexOf(\"#\");\n if (hash === -1) return `${url}#:~:text=${fragment}`;\n return `${url.slice(0, hash)}#${url.slice(hash + 1)}:~:text=${fragment}`;\n}\n\n/** A single-label host (`intranet`, `localhost`) or a reserved / private-network suffix. */\nconst PRIVATE_HOSTNAME =\n /^(?:[^.]+|.*\\.(?:local|localhost|internal|intranet|corp|home|lan|private|test|example|invalid|onion))$/i;\n/** An IPv4 or bracketed IPv6 literal, as `URL.hostname` spells them. */\nconst IP_LITERAL = /^(?:\\d{1,3}(?:\\.\\d{1,3}){3}|\\[[0-9a-f:.]+\\])$/i;\n\n/**\n * Whether a hostname looks like a public site: dotted, ending in an\n * alphabetic top-level label, not an IP literal and not a reserved or\n * intranet suffix. Only such a host is ever named to a third party.\n */\nexport function isPublicHostname(hostname: string): boolean {\n if (!hostname || IP_LITERAL.test(hostname) || PRIVATE_HOSTNAME.test(hostname)) {\n return false;\n }\n return /\\.[a-z]{2,}$/i.test(hostname);\n}\n\n/**\n * The favicon of a web source, from Google's favicon service, or null when\n * there is none to ask for: a non-web href, or a host that looks private\n * (single label, IP literal, reserved or intranet suffix). The service is a\n * third party, so a hostname that may belong to a tenant's own network must\n * not leave it just because a message rendered; the chip shows a globe\n * instead. The image also carries `referrerPolicy=\"no-referrer\"`.\n */\nexport function sourceFaviconUrl(href: string): string | null {\n if (!isWebHref(href)) return null;\n try {\n const { hostname } = new URL(href);\n if (!isPublicHostname(hostname)) return null;\n return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(hostname)}&sz=32`;\n } catch {\n return null;\n }\n}\n"],"names":[],"mappings":";;AAyBA,MAAM,WAAW;AAEjB,MAAM,gBAAgB;AAEtB,MAAM,SAAS;AAEf,MAAM,qBAAqB;AAE3B,MAAM,cAAc;AAEpB,MAAM,sCAA2C,IAAI;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAQM,SAAS,kBAAkB,MAAuB;;AACvD,QAAM,QAAQ,KAAK,QAAQ,oBAAoB,EAAE;AACjD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAS,kBAAO,KAAK,KAAK,MAAjB,mBAAqB,OAArB,mBAAyB;AACxC,MAAI,WAAW,OAAW,QAAO,gBAAgB,IAAI,MAAM;AAC3D,SAAO,YAAY,KAAK,KAAK;AAC/B;AAGO,SAAS,UAAU,MAAuB;;AAC/C,QAAM,UAAS,kBAAO,KAAK,KAAK,QAAQ,oBAAoB,EAAE,CAAC,MAAhD,mBAAoD,OAApD,mBAAwD;AACvE,SAAO,WAAW,UAAU,WAAW;AACzC;AAGA,SAAS,UAAU,OAAqC;AACtD,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,UAAU,MAAM,KAAA,EAAO,QAAQ,UAAU,EAAE;AACjD,MAAI,CAAC,QAAQ,KAAK,OAAO,EAAG,QAAO;AACnC,QAAM,OAAO,OAAO,OAAO;AAC3B,SAAO,OAAO,IAAI,OAAO;AAC3B;AAGA,SAAS,mBAAmB,OAAqC;AAC/D,QAAM,WAAU,+BAAO,WAAU;AACjC,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,OAAO,QAAQ,MAAM,QAAQ,YAAY,GAAG,IAAI,CAAC,EAAE,YAAA;AACzD,SAAO,QAAQ,SAAS,QAAQ,OAAO;AACzC;AAQO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMW;AACT,QAAM,SAAS,IAAI,gBAAgB,EAAE,WAAW,SAAS;AACzD,QAAM,OAAO,mBAAmB,SAAS;AACzC,MAAI,KAAM,QAAO,IAAI,QAAQ,IAAI;AACjC,QAAM,QAAQ,UAAU,UAAU;AAClC,QAAM,MAAM,UAAU,aAAa;AACnC,MAAI,UAAU,QAAQ,QAAQ,QAAQ,MAAM,OAAO;AACjD,WAAO,IAAI,UAAU,YAAY;AACjC,WAAO,IAAI,cAAc,OAAO,KAAK,CAAC;AACtC,WAAO,IAAI,YAAY,OAAO,GAAG,CAAC;AAAA,EACpC,WAAW,UAAU,MAAM;AACzB,WAAO,IAAI,UAAU,MAAM;AAC3B,WAAO,IAAI,QAAQ,OAAO,KAAK,CAAC;AAAA,EAClC;AACA,QAAM,iBAAiB,mCAAS;AAChC,MAAI,eAAgB,QAAO,IAAI,WAAW,cAAc;AACxD,SAAO,sBAAsB,OAAO,SAAA,CAAU;AAChD;AAOO,SAAS,yBAAyB,MAAwB;;AAC/D,aAAW,UAAU,KAAK,iBAAiB,GAAG,GAAG;AAE/C,UAAM,QAAQ,OAAO;AACrB,QACE,cAAc,KAAK,OAAO,aAAa,OAAO,KAAK,EAAE,MACrD,+BAAO,cAAa,OACpB,WAAM,gBAAN,mBAAmB,WAAW,OAC9B;AACA,YAAM,cAAc,MAAM,YAAY,MAAM,CAAC;AAAA,IAC/C;AACA,QAAI,OAAO,aAAa,MAAM,EAAG;AACjC,UAAM,OAAO,OAAO,aAAa,WAAW;AAC5C,UAAM,UAAU,OAAO,aAAa,SAAS;AAC7C,UAAM,UAAU,OAAO,aAAa,UAAU;AAC9C,QAAI,YAAY,SAAS,SAAS,CAAC,UAAU;AAC3C,YAAM,OAAO,QAAQ,KAAA;AACrB,UAAI,kBAAkB,IAAI,EAAG,QAAO,aAAa,QAAQ,IAAI;AAC7D;AAAA,IACF;AACA,QAAI,WAAW,SAAS,KAAK,OAAO,GAAG;AACrC,aAAO;AAAA,QACL;AAAA,QACA,uBAAuB;AAAA,UACrB;AAAA,UACA,WAAW;AAAA,UACX,YAAY,OAAO,aAAa,kBAAkB;AAAA,UAClD,eAAe,OAAO,aAAa,sBAAsB;AAAA,UACzD,SAAS,OAAO,aAAa,cAAc;AAAA,QAAA,CAC5C;AAAA,MAAA;AAAA,IAEL;AAAA,EACF;AACF;AASO,SAAS,qBAAqB,KAAa,SAA4C;AAC5F,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACF,QAAI,IAAI,GAAG;AAAA,EACb,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AACJ,QAAM,YAAY,QAAQ,QAAQ,KAAK;AACvC,QAAM,QAAQ,cAAc,KAAK,KAAK,QAAQ,MAAM,GAAG,SAAS,EAAE,KAAA;AAClE,QAAM,MAAM,cAAc,KAAK,KAAK,QAAQ,MAAM,YAAY,CAAC,EAAE,KAAA;AACjE,MAAI,SAAS,KAAK;AAChB,eAAW,GAAG,mBAAmB,KAAK,CAAC,IAAI,mBAAmB,GAAG,CAAC;AAAA,EACpE,OAAO;AACL,eAAW,mBAAmB,OAAO;AAAA,EACvC;AACA,QAAM,OAAO,IAAI,QAAQ,GAAG;AAC5B,MAAI,SAAS,GAAI,QAAO,GAAG,GAAG,YAAY,QAAQ;AAClD,SAAO,GAAG,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,WAAW,QAAQ;AACxE;AAGA,MAAM,mBACJ;AAEF,MAAM,aAAa;AAOZ,SAAS,iBAAiB,UAA2B;AAC1D,MAAI,CAAC,YAAY,WAAW,KAAK,QAAQ,KAAK,iBAAiB,KAAK,QAAQ,GAAG;AAC7E,WAAO;AAAA,EACT;AACA,SAAO,gBAAgB,KAAK,QAAQ;AACtC;AAUO,SAAS,iBAAiB,MAA6B;AAC5D,MAAI,CAAC,UAAU,IAAI,EAAG,QAAO;AAC7B,MAAI;AACF,UAAM,EAAE,SAAA,IAAa,IAAI,IAAI,IAAI;AACjC,QAAI,CAAC,iBAAiB,QAAQ,EAAG,QAAO;AACxC,WAAO,6CAA6C,mBAAmB,QAAQ,CAAC;AAAA,EAClF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;;;;;;"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Citation anchors in assistant text, made parseable and safe for the chat
3
+ * text schema.
4
+ *
5
+ * Agora's deep agent cites sources as inline HTML anchors that carry no
6
+ * `href` (`agora/agora/graphs/tools/citation_readables.py`,
7
+ * `agora/agora/services/reader/service.py`):
8
+ *
9
+ * <a class="mention" data-type="url" data-url="https://…" data-excerpt="…">Label</a>
10
+ * <a class="mention" data-type="pdf" data-id="asset_…" data-page-number=10
11
+ * data-end-page-number=null data-excerpt="…">Citation</a>
12
+ *
13
+ * ProseMirror's Link mark parses `a[href]` only, so without a rewrite both
14
+ * shapes render as their label text — a source nobody can open. This module
15
+ * rewrites the anchors in the HTML markdown-it produced, before the schema
16
+ * parses it: a URL citation gets its `data-url` as `href`; an asset citation
17
+ * gets the Spaces deep link (`asset_ids`, `type`, the page anchor, `excerpt`)
18
+ * that the mention rule turns into an asset chip. `data-excerpt` stays on the
19
+ * anchor for the Link mark's `excerpt` attribute — the source chip's tooltip
20
+ * and its scroll-to-text target. Working on the parsed DOM rather than the
21
+ * markdown keeps fenced and inline code (where markdown-it has already
22
+ * escaped the tag into text) untouched.
23
+ */
24
+ /**
25
+ * Whether an href may become a link in chat text: `http(s):`, `mailto:`, the
26
+ * screen-point protocol, or an app-relative Spaces path. Scheme-less
27
+ * protocol-relative (`//host`) and fragment/query-only hrefs fail, and so
28
+ * does every other scheme (`javascript:`, `vbscript:`, `data:`, `ftp:`).
29
+ */
30
+ export declare function isAllowedChatHref(href: string): boolean;
31
+ /** Whether an href points at a web page (the source chip's domain) rather than an app path or mail address. */
32
+ export declare function isWebHref(href: string): boolean;
33
+ /**
34
+ * The Spaces deep link for an agora asset citation: the cited asset, its type,
35
+ * a `page` anchor (or `page_range` when the citation spans pages) and the
36
+ * excerpt — the same query the SDK's `parseAthenaCitationLink` reads back
37
+ * into an embed reference when the chip is clicked.
38
+ */
39
+ export declare function buildAssetCitationHref({ assetId, assetType, pageNumber, endPageNumber, excerpt, }: {
40
+ assetId: string;
41
+ assetType: string | null;
42
+ pageNumber: string | null;
43
+ endPageNumber: string | null;
44
+ excerpt: string | null;
45
+ }): string;
46
+ /**
47
+ * Give every `href`-less citation anchor under `root` the href the schema's
48
+ * parse rules need. Anchors that already carry an href are left alone (their
49
+ * `data-excerpt`, if any, is read by the Link mark itself).
50
+ */
51
+ export declare function normalizeCitationAnchors(root: ParentNode): void;
52
+ /**
53
+ * The cited page with a Scroll-to-Text Fragment (`#:~:text=`) for the excerpt,
54
+ * so the browser lands on and highlights the quoted passage. Agora's
55
+ * `start...end` range form becomes `text=start,end`. A URL that does not parse
56
+ * is returned as is.
57
+ * @see https://wicg.github.io/scroll-to-text-fragment/
58
+ */
59
+ export declare function buildScrollToTextUrl(url: string, excerpt: string | null | undefined): string;
60
+ /**
61
+ * Whether a hostname looks like a public site: dotted, ending in an
62
+ * alphabetic top-level label, not an IP literal and not a reserved or
63
+ * intranet suffix. Only such a host is ever named to a third party.
64
+ */
65
+ export declare function isPublicHostname(hostname: string): boolean;
66
+ /**
67
+ * The favicon of a web source, from Google's favicon service, or null when
68
+ * there is none to ask for: a non-web href, or a host that looks private
69
+ * (single label, IP literal, reserved or intranet suffix). The service is a
70
+ * third party, so a hostname that may belong to a tenant's own network must
71
+ * not leave it just because a message rendered; the chip shows a globe
72
+ * instead. The image also carries `referrerPolicy="no-referrer"`.
73
+ */
74
+ export declare function sourceFaviconUrl(href: string): string | null;
@@ -0,0 +1,139 @@
1
+ const ASSET_ID = /^asset_[\w-]+$/;
2
+ const MENTION_CLASS = /(?:^|\s)mention(?:\s|$)/;
3
+ const SCHEME = /^([a-z][a-z0-9+.-]*):/i;
4
+ const IGNORED_CHARACTERS = /[\s\p{Cc}\p{Cf}]/gu;
5
+ const SPACES_PATH = /^\/dashboard\/spaces\/?(?:[?#]|$)/;
6
+ const ALLOWED_SCHEMES = /* @__PURE__ */ new Set([
7
+ "http",
8
+ "https",
9
+ "mailto",
10
+ "athena-point"
11
+ ]);
12
+ function isAllowedChatHref(href) {
13
+ var _a, _b;
14
+ const value = href.replace(IGNORED_CHARACTERS, "");
15
+ if (!value) return false;
16
+ const scheme = (_b = (_a = SCHEME.exec(value)) == null ? void 0 : _a[1]) == null ? void 0 : _b.toLowerCase();
17
+ if (scheme !== void 0) return ALLOWED_SCHEMES.has(scheme);
18
+ return SPACES_PATH.test(value);
19
+ }
20
+ function isWebHref(href) {
21
+ var _a, _b;
22
+ const scheme = (_b = (_a = SCHEME.exec(href.replace(IGNORED_CHARACTERS, ""))) == null ? void 0 : _a[1]) == null ? void 0 : _b.toLowerCase();
23
+ return scheme === "http" || scheme === "https";
24
+ }
25
+ function parsePage(value) {
26
+ if (value === null) return null;
27
+ const trimmed = value.trim().replace(/^"|"$/g, "");
28
+ if (!/^\d+$/.test(trimmed)) return null;
29
+ const page = Number(trimmed);
30
+ return page > 0 ? page : null;
31
+ }
32
+ function normalizeAssetType(value) {
33
+ const trimmed = (value == null ? void 0 : value.trim()) ?? "";
34
+ if (!trimmed) return null;
35
+ const bare = trimmed.slice(trimmed.lastIndexOf("/") + 1).toLowerCase();
36
+ return bare && bare !== "url" ? bare : null;
37
+ }
38
+ function buildAssetCitationHref({
39
+ assetId,
40
+ assetType,
41
+ pageNumber,
42
+ endPageNumber,
43
+ excerpt
44
+ }) {
45
+ const params = new URLSearchParams({ asset_ids: assetId });
46
+ const type = normalizeAssetType(assetType);
47
+ if (type) params.set("type", type);
48
+ const start = parsePage(pageNumber);
49
+ const end = parsePage(endPageNumber);
50
+ if (start !== null && end !== null && end > start) {
51
+ params.set("anchor", "page_range");
52
+ params.set("start_page", String(start));
53
+ params.set("end_page", String(end));
54
+ } else if (start !== null) {
55
+ params.set("anchor", "page");
56
+ params.set("page", String(start));
57
+ }
58
+ const trimmedExcerpt = excerpt == null ? void 0 : excerpt.trim();
59
+ if (trimmedExcerpt) params.set("excerpt", trimmedExcerpt);
60
+ return `/dashboard/spaces/?${params.toString()}`;
61
+ }
62
+ function normalizeCitationAnchors(root) {
63
+ var _a;
64
+ for (const anchor of root.querySelectorAll("a")) {
65
+ const label = anchor.firstChild;
66
+ if (MENTION_CLASS.test(anchor.getAttribute("class") ?? "") && (label == null ? void 0 : label.nodeType) === 3 && ((_a = label.textContent) == null ? void 0 : _a.startsWith("@"))) {
67
+ label.textContent = label.textContent.slice(1);
68
+ }
69
+ if (anchor.hasAttribute("href")) continue;
70
+ const type = anchor.getAttribute("data-type");
71
+ const assetId = anchor.getAttribute("data-id");
72
+ const dataUrl = anchor.getAttribute("data-url");
73
+ if (dataUrl && (type === "url" || !assetId)) {
74
+ const href = dataUrl.trim();
75
+ if (isAllowedChatHref(href)) anchor.setAttribute("href", href);
76
+ continue;
77
+ }
78
+ if (assetId && ASSET_ID.test(assetId)) {
79
+ anchor.setAttribute(
80
+ "href",
81
+ buildAssetCitationHref({
82
+ assetId,
83
+ assetType: type,
84
+ pageNumber: anchor.getAttribute("data-page-number"),
85
+ endPageNumber: anchor.getAttribute("data-end-page-number"),
86
+ excerpt: anchor.getAttribute("data-excerpt")
87
+ })
88
+ );
89
+ }
90
+ }
91
+ }
92
+ function buildScrollToTextUrl(url, excerpt) {
93
+ if (!excerpt) return url;
94
+ try {
95
+ new URL(url);
96
+ } catch {
97
+ return url;
98
+ }
99
+ let fragment;
100
+ const separator = excerpt.indexOf("...");
101
+ const start = separator === -1 ? "" : excerpt.slice(0, separator).trim();
102
+ const end = separator === -1 ? "" : excerpt.slice(separator + 3).trim();
103
+ if (start && end) {
104
+ fragment = `${encodeURIComponent(start)},${encodeURIComponent(end)}`;
105
+ } else {
106
+ fragment = encodeURIComponent(excerpt);
107
+ }
108
+ const hash = url.indexOf("#");
109
+ if (hash === -1) return `${url}#:~:text=${fragment}`;
110
+ return `${url.slice(0, hash)}#${url.slice(hash + 1)}:~:text=${fragment}`;
111
+ }
112
+ const PRIVATE_HOSTNAME = /^(?:[^.]+|.*\.(?:local|localhost|internal|intranet|corp|home|lan|private|test|example|invalid|onion))$/i;
113
+ const IP_LITERAL = /^(?:\d{1,3}(?:\.\d{1,3}){3}|\[[0-9a-f:.]+\])$/i;
114
+ function isPublicHostname(hostname) {
115
+ if (!hostname || IP_LITERAL.test(hostname) || PRIVATE_HOSTNAME.test(hostname)) {
116
+ return false;
117
+ }
118
+ return /\.[a-z]{2,}$/i.test(hostname);
119
+ }
120
+ function sourceFaviconUrl(href) {
121
+ if (!isWebHref(href)) return null;
122
+ try {
123
+ const { hostname } = new URL(href);
124
+ if (!isPublicHostname(hostname)) return null;
125
+ return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(hostname)}&sz=32`;
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+ export {
131
+ buildAssetCitationHref,
132
+ buildScrollToTextUrl,
133
+ isAllowedChatHref,
134
+ isPublicHostname,
135
+ isWebHref,
136
+ normalizeCitationAnchors,
137
+ sourceFaviconUrl
138
+ };
139
+ //# sourceMappingURL=citation-anchors.js.map