@adea-ai/ui 0.140.0 → 0.142.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 (455) hide show
  1. package/dist/components/composites/appearance-editor/appearance-editor.js +2 -2
  2. package/dist/components/composites/appearance-editor/appearance-editor.js.map +1 -1
  3. package/dist/components/composites/catalog-browser/catalog-browser-group.js +1 -1
  4. package/dist/components/composites/chain-of-thought/chain-of-thought.d.ts +56 -0
  5. package/dist/components/composites/chain-of-thought/chain-of-thought.d.ts.map +1 -0
  6. package/dist/components/composites/chain-of-thought/chain-of-thought.js +117 -0
  7. package/dist/components/composites/chain-of-thought/chain-of-thought.js.map +1 -0
  8. package/dist/components/composites/chain-of-thought/index.d.ts +2 -0
  9. package/dist/components/composites/chain-of-thought/index.d.ts.map +1 -0
  10. package/dist/components/composites/chain-of-thought/index.js +2 -0
  11. package/dist/components/composites/chat/chat.d.ts +67 -0
  12. package/dist/components/composites/chat/chat.d.ts.map +1 -0
  13. package/dist/components/composites/chat/chat.js +179 -0
  14. package/dist/components/composites/chat/chat.js.map +1 -0
  15. package/dist/components/composites/chat/index.d.ts +2 -0
  16. package/dist/components/composites/chat/index.d.ts.map +1 -0
  17. package/dist/components/composites/chat/index.js +2 -0
  18. package/dist/components/composites/checkpoint/checkpoint-model.d.ts +34 -0
  19. package/dist/components/composites/checkpoint/checkpoint-model.d.ts.map +1 -0
  20. package/dist/components/composites/checkpoint/checkpoint-model.js +48 -0
  21. package/dist/components/composites/checkpoint/checkpoint-model.js.map +1 -0
  22. package/dist/components/composites/checkpoint/checkpoint.d.ts +48 -0
  23. package/dist/components/composites/checkpoint/checkpoint.d.ts.map +1 -0
  24. package/dist/components/composites/checkpoint/checkpoint.js +160 -0
  25. package/dist/components/composites/checkpoint/checkpoint.js.map +1 -0
  26. package/dist/components/composites/checkpoint/index.d.ts +3 -0
  27. package/dist/components/composites/checkpoint/index.d.ts.map +1 -0
  28. package/dist/components/composites/checkpoint/index.js +3 -0
  29. package/dist/components/composites/confirmation/confirmation-model.d.ts +45 -0
  30. package/dist/components/composites/confirmation/confirmation-model.d.ts.map +1 -0
  31. package/dist/components/composites/confirmation/confirmation-model.js +51 -0
  32. package/dist/components/composites/confirmation/confirmation-model.js.map +1 -0
  33. package/dist/components/composites/confirmation/confirmation.d.ts +72 -0
  34. package/dist/components/composites/confirmation/confirmation.d.ts.map +1 -0
  35. package/dist/components/composites/confirmation/confirmation.js +300 -0
  36. package/dist/components/composites/confirmation/confirmation.js.map +1 -0
  37. package/dist/components/composites/confirmation/index.d.ts +3 -0
  38. package/dist/components/composites/confirmation/index.d.ts.map +1 -0
  39. package/dist/components/composites/confirmation/index.js +3 -0
  40. package/dist/components/composites/context/context-model.d.ts +69 -0
  41. package/dist/components/composites/context/context-model.d.ts.map +1 -0
  42. package/dist/components/composites/context/context-model.js +122 -0
  43. package/dist/components/composites/context/context-model.js.map +1 -0
  44. package/dist/components/composites/context/context.d.ts +38 -0
  45. package/dist/components/composites/context/context.d.ts.map +1 -0
  46. package/dist/components/composites/context/context.js +234 -0
  47. package/dist/components/composites/context/context.js.map +1 -0
  48. package/dist/components/composites/context/index.d.ts +3 -0
  49. package/dist/components/composites/context/index.d.ts.map +1 -0
  50. package/dist/components/composites/context/index.js +3 -0
  51. package/dist/components/composites/file-tree/build-file-tree.d.ts +138 -0
  52. package/dist/components/composites/file-tree/build-file-tree.d.ts.map +1 -0
  53. package/dist/components/composites/file-tree/build-file-tree.js +325 -0
  54. package/dist/components/composites/file-tree/build-file-tree.js.map +1 -0
  55. package/dist/components/composites/file-tree/file-tree.d.ts +74 -0
  56. package/dist/components/composites/file-tree/file-tree.d.ts.map +1 -0
  57. package/dist/components/composites/file-tree/file-tree.js +389 -0
  58. package/dist/components/composites/file-tree/file-tree.js.map +1 -0
  59. package/dist/components/composites/file-tree/index.d.ts +3 -0
  60. package/dist/components/composites/file-tree/index.d.ts.map +1 -0
  61. package/dist/components/composites/file-tree/index.js +3 -0
  62. package/dist/components/composites/inline-citation/citation.d.ts +37 -0
  63. package/dist/components/composites/inline-citation/citation.d.ts.map +1 -0
  64. package/dist/components/composites/inline-citation/citation.js +52 -0
  65. package/dist/components/composites/inline-citation/citation.js.map +1 -0
  66. package/dist/components/composites/inline-citation/index.d.ts +3 -0
  67. package/dist/components/composites/inline-citation/index.d.ts.map +1 -0
  68. package/dist/components/composites/inline-citation/index.js +3 -0
  69. package/dist/components/composites/inline-citation/inline-citation.d.ts +64 -0
  70. package/dist/components/composites/inline-citation/inline-citation.d.ts.map +1 -0
  71. package/dist/components/composites/inline-citation/inline-citation.js +272 -0
  72. package/dist/components/composites/inline-citation/inline-citation.js.map +1 -0
  73. package/dist/components/composites/message-scroller/index.d.ts +3 -0
  74. package/dist/components/composites/message-scroller/index.d.ts.map +1 -0
  75. package/dist/components/composites/message-scroller/index.js +3 -0
  76. package/dist/components/composites/message-scroller/message-scroller.d.ts +55 -0
  77. package/dist/components/composites/message-scroller/message-scroller.d.ts.map +1 -0
  78. package/dist/components/composites/message-scroller/message-scroller.js +201 -0
  79. package/dist/components/composites/message-scroller/message-scroller.js.map +1 -0
  80. package/dist/components/composites/message-scroller/scroll-anchor.d.ts +66 -0
  81. package/dist/components/composites/message-scroller/scroll-anchor.d.ts.map +1 -0
  82. package/dist/components/composites/message-scroller/scroll-anchor.js +122 -0
  83. package/dist/components/composites/message-scroller/scroll-anchor.js.map +1 -0
  84. package/dist/components/composites/model-selector/index.d.ts +3 -0
  85. package/dist/components/composites/model-selector/index.d.ts.map +1 -0
  86. package/dist/components/composites/model-selector/index.js +3 -0
  87. package/dist/components/composites/model-selector/model-selector.d.ts +55 -0
  88. package/dist/components/composites/model-selector/model-selector.d.ts.map +1 -0
  89. package/dist/components/composites/model-selector/model-selector.js +490 -0
  90. package/dist/components/composites/model-selector/model-selector.js.map +1 -0
  91. package/dist/components/composites/model-selector/models.d.ts +51 -0
  92. package/dist/components/composites/model-selector/models.d.ts.map +1 -0
  93. package/dist/components/composites/model-selector/models.js +71 -0
  94. package/dist/components/composites/model-selector/models.js.map +1 -0
  95. package/dist/components/composites/plan/index.d.ts +2 -0
  96. package/dist/components/composites/plan/index.d.ts.map +1 -0
  97. package/dist/components/composites/plan/index.js +2 -0
  98. package/dist/components/composites/plan/plan.d.ts +61 -0
  99. package/dist/components/composites/plan/plan.d.ts.map +1 -0
  100. package/dist/components/composites/plan/plan.js +130 -0
  101. package/dist/components/composites/plan/plan.js.map +1 -0
  102. package/dist/components/composites/queue/index.d.ts +2 -0
  103. package/dist/components/composites/queue/index.d.ts.map +1 -0
  104. package/dist/components/composites/queue/index.js +2 -0
  105. package/dist/components/composites/queue/queue.d.ts +77 -0
  106. package/dist/components/composites/queue/queue.d.ts.map +1 -0
  107. package/dist/components/composites/queue/queue.js +271 -0
  108. package/dist/components/composites/queue/queue.js.map +1 -0
  109. package/dist/components/composites/reasoning/index.d.ts +2 -0
  110. package/dist/components/composites/reasoning/index.d.ts.map +1 -0
  111. package/dist/components/composites/reasoning/index.js +2 -0
  112. package/dist/components/composites/reasoning/reasoning.d.ts +62 -0
  113. package/dist/components/composites/reasoning/reasoning.d.ts.map +1 -0
  114. package/dist/components/composites/reasoning/reasoning.js +156 -0
  115. package/dist/components/composites/reasoning/reasoning.js.map +1 -0
  116. package/dist/components/composites/sources/index.d.ts +3 -0
  117. package/dist/components/composites/sources/index.d.ts.map +1 -0
  118. package/dist/components/composites/sources/index.js +3 -0
  119. package/dist/components/composites/sources/source.d.ts +46 -0
  120. package/dist/components/composites/sources/source.d.ts.map +1 -0
  121. package/dist/components/composites/sources/source.js +74 -0
  122. package/dist/components/composites/sources/source.js.map +1 -0
  123. package/dist/components/composites/sources/sources.d.ts +45 -0
  124. package/dist/components/composites/sources/sources.d.ts.map +1 -0
  125. package/dist/components/composites/sources/sources.js +175 -0
  126. package/dist/components/composites/sources/sources.js.map +1 -0
  127. package/dist/components/composites/stack-trace/index.d.ts +3 -0
  128. package/dist/components/composites/stack-trace/index.d.ts.map +1 -0
  129. package/dist/components/composites/stack-trace/index.js +3 -0
  130. package/dist/components/composites/stack-trace/parse-stack-trace.d.ts +118 -0
  131. package/dist/components/composites/stack-trace/parse-stack-trace.d.ts.map +1 -0
  132. package/dist/components/composites/stack-trace/parse-stack-trace.js +376 -0
  133. package/dist/components/composites/stack-trace/parse-stack-trace.js.map +1 -0
  134. package/dist/components/composites/stack-trace/stack-trace.d.ts +63 -0
  135. package/dist/components/composites/stack-trace/stack-trace.d.ts.map +1 -0
  136. package/dist/components/composites/stack-trace/stack-trace.js +491 -0
  137. package/dist/components/composites/stack-trace/stack-trace.js.map +1 -0
  138. package/dist/components/composites/subagent/index.d.ts +2 -0
  139. package/dist/components/composites/subagent/index.d.ts.map +1 -0
  140. package/dist/components/composites/subagent/index.js +2 -0
  141. package/dist/components/composites/subagent/subagent.d.ts +55 -0
  142. package/dist/components/composites/subagent/subagent.d.ts.map +1 -0
  143. package/dist/components/composites/subagent/subagent.js +138 -0
  144. package/dist/components/composites/subagent/subagent.js.map +1 -0
  145. package/dist/components/composites/suggestion/index.d.ts +2 -0
  146. package/dist/components/composites/suggestion/index.d.ts.map +1 -0
  147. package/dist/components/composites/suggestion/index.js +2 -0
  148. package/dist/components/composites/suggestion/suggestion.d.ts +37 -0
  149. package/dist/components/composites/suggestion/suggestion.d.ts.map +1 -0
  150. package/dist/components/composites/suggestion/suggestion.js +100 -0
  151. package/dist/components/composites/suggestion/suggestion.js.map +1 -0
  152. package/dist/components/composites/task/index.d.ts +2 -0
  153. package/dist/components/composites/task/index.d.ts.map +1 -0
  154. package/dist/components/composites/task/index.js +2 -0
  155. package/dist/components/composites/task/task.d.ts +96 -0
  156. package/dist/components/composites/task/task.d.ts.map +1 -0
  157. package/dist/components/composites/task/task.js +311 -0
  158. package/dist/components/composites/task/task.js.map +1 -0
  159. package/dist/components/composites/terminal/ansi.d.ts +101 -0
  160. package/dist/components/composites/terminal/ansi.d.ts.map +1 -0
  161. package/dist/components/composites/terminal/ansi.js +544 -0
  162. package/dist/components/composites/terminal/ansi.js.map +1 -0
  163. package/dist/components/composites/terminal/index.d.ts +4 -0
  164. package/dist/components/composites/terminal/index.d.ts.map +1 -0
  165. package/dist/components/composites/terminal/index.js +4 -0
  166. package/dist/components/composites/terminal/terminal-model.d.ts +63 -0
  167. package/dist/components/composites/terminal/terminal-model.d.ts.map +1 -0
  168. package/dist/components/composites/terminal/terminal-model.js +69 -0
  169. package/dist/components/composites/terminal/terminal-model.js.map +1 -0
  170. package/dist/components/composites/terminal/terminal.d.ts +88 -0
  171. package/dist/components/composites/terminal/terminal.d.ts.map +1 -0
  172. package/dist/components/composites/terminal/terminal.js +500 -0
  173. package/dist/components/composites/terminal/terminal.js.map +1 -0
  174. package/dist/components/composites/test-results/index.d.ts +3 -0
  175. package/dist/components/composites/test-results/index.d.ts.map +1 -0
  176. package/dist/components/composites/test-results/index.js +3 -0
  177. package/dist/components/composites/test-results/test-results-model.d.ts +154 -0
  178. package/dist/components/composites/test-results/test-results-model.d.ts.map +1 -0
  179. package/dist/components/composites/test-results/test-results-model.js +252 -0
  180. package/dist/components/composites/test-results/test-results-model.js.map +1 -0
  181. package/dist/components/composites/test-results/test-results.d.ts +60 -0
  182. package/dist/components/composites/test-results/test-results.d.ts.map +1 -0
  183. package/dist/components/composites/test-results/test-results.js +631 -0
  184. package/dist/components/composites/test-results/test-results.js.map +1 -0
  185. package/dist/components/composites/tool/format-tool-value.d.ts +37 -0
  186. package/dist/components/composites/tool/format-tool-value.d.ts.map +1 -0
  187. package/dist/components/composites/tool/format-tool-value.js +135 -0
  188. package/dist/components/composites/tool/format-tool-value.js.map +1 -0
  189. package/dist/components/composites/tool/index.d.ts +3 -0
  190. package/dist/components/composites/tool/index.d.ts.map +1 -0
  191. package/dist/components/composites/tool/index.js +3 -0
  192. package/dist/components/composites/tool/tool.d.ts +69 -0
  193. package/dist/components/composites/tool/tool.d.ts.map +1 -0
  194. package/dist/components/composites/tool/tool.js +214 -0
  195. package/dist/components/composites/tool/tool.js.map +1 -0
  196. package/dist/components/composites/update-dialog/update-dialog.js +1 -1
  197. package/dist/components/conversation/conversation-surface.d.ts +7 -0
  198. package/dist/components/conversation/conversation-surface.d.ts.map +1 -1
  199. package/dist/components/conversation/conversation-surface.js +2 -0
  200. package/dist/components/conversation/conversation-surface.js.map +1 -1
  201. package/dist/components/conversation/scroll-follow-core.d.ts +24 -0
  202. package/dist/components/conversation/scroll-follow-core.d.ts.map +1 -1
  203. package/dist/components/conversation/scroll-follow-core.js +23 -1
  204. package/dist/components/conversation/scroll-follow-core.js.map +1 -1
  205. package/dist/components/conversation/scroll-follow.d.ts +3 -0
  206. package/dist/components/conversation/scroll-follow.d.ts.map +1 -1
  207. package/dist/components/conversation/scroll-follow.js +80 -1
  208. package/dist/components/conversation/scroll-follow.js.map +1 -1
  209. package/dist/components/ui/attachment/attachment.d.ts +58 -0
  210. package/dist/components/ui/attachment/attachment.d.ts.map +1 -0
  211. package/dist/components/ui/attachment/attachment.js +279 -0
  212. package/dist/components/ui/attachment/attachment.js.map +1 -0
  213. package/dist/components/ui/attachment/index.d.ts +2 -0
  214. package/dist/components/ui/attachment/index.d.ts.map +1 -0
  215. package/dist/components/ui/attachment/index.js +2 -0
  216. package/dist/components/ui/badge/badge.d.ts +1 -1
  217. package/dist/components/ui/bubble/bubble.d.ts +33 -0
  218. package/dist/components/ui/bubble/bubble.d.ts.map +1 -0
  219. package/dist/components/ui/bubble/bubble.js +95 -0
  220. package/dist/components/ui/bubble/bubble.js.map +1 -0
  221. package/dist/components/ui/bubble/index.d.ts +2 -0
  222. package/dist/components/ui/bubble/index.d.ts.map +1 -0
  223. package/dist/components/ui/bubble/index.js +2 -0
  224. package/dist/components/ui/button/button.d.ts +1 -1
  225. package/dist/components/ui/code-block/code-block.d.ts +56 -3
  226. package/dist/components/ui/code-block/code-block.d.ts.map +1 -1
  227. package/dist/components/ui/code-block/code-block.js +231 -50
  228. package/dist/components/ui/code-block/code-block.js.map +1 -1
  229. package/dist/components/ui/code-block/index.d.ts +1 -1
  230. package/dist/components/ui/code-block/index.d.ts.map +1 -1
  231. package/dist/components/ui/code-block/index.js +2 -2
  232. package/dist/components/ui/field/number-field.d.ts +21 -4
  233. package/dist/components/ui/field/number-field.d.ts.map +1 -1
  234. package/dist/components/ui/field/number-field.js +22 -11
  235. package/dist/components/ui/field/number-field.js.map +1 -1
  236. package/dist/components/ui/marker/index.d.ts +2 -0
  237. package/dist/components/ui/marker/index.d.ts.map +1 -0
  238. package/dist/components/ui/marker/index.js +2 -0
  239. package/dist/components/ui/marker/marker.d.ts +35 -0
  240. package/dist/components/ui/marker/marker.d.ts.map +1 -0
  241. package/dist/components/ui/marker/marker.js +123 -0
  242. package/dist/components/ui/marker/marker.js.map +1 -0
  243. package/dist/components/ui/message/cite-ranges.d.ts +42 -0
  244. package/dist/components/ui/message/cite-ranges.d.ts.map +1 -0
  245. package/dist/components/ui/message/cite-ranges.js +105 -0
  246. package/dist/components/ui/message/cite-ranges.js.map +1 -0
  247. package/dist/components/ui/message/index.d.ts +4 -0
  248. package/dist/components/ui/message/index.d.ts.map +1 -0
  249. package/dist/components/ui/message/index.js +4 -0
  250. package/dist/components/ui/message/message.d.ts +95 -0
  251. package/dist/components/ui/message/message.d.ts.map +1 -0
  252. package/dist/components/ui/message/message.js +443 -0
  253. package/dist/components/ui/message/message.js.map +1 -0
  254. package/dist/components/ui/message/parse-message-text.d.ts +93 -0
  255. package/dist/components/ui/message/parse-message-text.d.ts.map +1 -0
  256. package/dist/components/ui/message/parse-message-text.js +365 -0
  257. package/dist/components/ui/message/parse-message-text.js.map +1 -0
  258. package/dist/components/ui/questionnaire/index.d.ts +2 -0
  259. package/dist/components/ui/questionnaire/index.d.ts.map +1 -0
  260. package/dist/components/ui/questionnaire/index.js +2 -0
  261. package/dist/components/ui/questionnaire/questionnaire.d.ts +83 -0
  262. package/dist/components/ui/questionnaire/questionnaire.d.ts.map +1 -0
  263. package/dist/components/ui/questionnaire/questionnaire.js +470 -0
  264. package/dist/components/ui/questionnaire/questionnaire.js.map +1 -0
  265. package/dist/components/ui/status-chip/status-chip.d.ts +1 -1
  266. package/dist/components/ui/switch/switch.js +11 -11
  267. package/dist/components/ui/switch/switch.js.map +1 -1
  268. package/dist/components/ui/trace/index.d.ts +2 -0
  269. package/dist/components/ui/trace/index.d.ts.map +1 -0
  270. package/dist/components/ui/trace/index.js +2 -0
  271. package/dist/components/ui/trace/trace.d.ts +72 -0
  272. package/dist/components/ui/trace/trace.d.ts.map +1 -0
  273. package/dist/components/ui/trace/trace.js +340 -0
  274. package/dist/components/ui/trace/trace.js.map +1 -0
  275. package/dist/index.d.ts +26 -0
  276. package/dist/index.d.ts.map +1 -1
  277. package/dist/index.js +46 -5
  278. package/dist/r/attachment.json +25 -0
  279. package/dist/r/bubble.json +22 -0
  280. package/dist/r/chain-of-thought.json +26 -0
  281. package/dist/r/chat.json +24 -0
  282. package/dist/r/checkpoint.json +35 -0
  283. package/dist/r/confirmation.json +34 -0
  284. package/dist/r/context.json +33 -0
  285. package/dist/r/file-tree.json +32 -0
  286. package/dist/r/inline-citation.json +35 -0
  287. package/dist/r/marker.json +22 -0
  288. package/dist/r/message-scroller.json +31 -0
  289. package/dist/r/message.json +33 -0
  290. package/dist/r/model-selector.json +34 -0
  291. package/dist/r/plan.json +26 -0
  292. package/dist/r/questionnaire.json +28 -0
  293. package/dist/r/queue.json +26 -0
  294. package/dist/r/reasoning.json +26 -0
  295. package/dist/r/registry.json +811 -52
  296. package/dist/r/sources.json +32 -0
  297. package/dist/r/src/components/composites/chain-of-thought/chain-of-thought.tsx +141 -0
  298. package/dist/r/src/components/composites/chain-of-thought/index.ts +11 -0
  299. package/dist/r/src/components/composites/chat/chat.tsx +190 -0
  300. package/dist/r/src/components/composites/chat/index.ts +11 -0
  301. package/dist/r/src/components/composites/checkpoint/checkpoint-model.ts +71 -0
  302. package/dist/r/src/components/composites/checkpoint/checkpoint.tsx +214 -0
  303. package/dist/r/src/components/composites/checkpoint/index.ts +8 -0
  304. package/dist/r/src/components/composites/confirmation/confirmation-model.ts +83 -0
  305. package/dist/r/src/components/composites/confirmation/confirmation.tsx +311 -0
  306. package/dist/r/src/components/composites/confirmation/index.ts +9 -0
  307. package/dist/r/src/components/composites/context/context-model.ts +168 -0
  308. package/dist/r/src/components/composites/context/context.tsx +187 -0
  309. package/dist/r/src/components/composites/context/index.ts +18 -0
  310. package/dist/r/src/components/composites/file-tree/build-file-tree.ts +456 -0
  311. package/dist/r/src/components/composites/file-tree/file-tree.tsx +405 -0
  312. package/dist/r/src/components/composites/file-tree/index.ts +25 -0
  313. package/dist/r/src/components/composites/inline-citation/citation.ts +78 -0
  314. package/dist/r/src/components/composites/inline-citation/index.ts +9 -0
  315. package/dist/r/src/components/composites/inline-citation/inline-citation.tsx +285 -0
  316. package/dist/r/src/components/composites/message-scroller/index.ts +2 -0
  317. package/dist/r/src/components/composites/message-scroller/message-scroller.tsx +301 -0
  318. package/dist/r/src/components/composites/message-scroller/scroll-anchor.ts +170 -0
  319. package/dist/r/src/components/composites/model-selector/index.ts +14 -0
  320. package/dist/r/src/components/composites/model-selector/model-selector.tsx +420 -0
  321. package/dist/r/src/components/composites/model-selector/models.ts +103 -0
  322. package/dist/r/src/components/composites/plan/index.ts +1 -0
  323. package/dist/r/src/components/composites/plan/plan.tsx +155 -0
  324. package/dist/r/src/components/composites/queue/index.ts +10 -0
  325. package/dist/r/src/components/composites/queue/queue.tsx +264 -0
  326. package/dist/r/src/components/composites/reasoning/index.ts +1 -0
  327. package/dist/r/src/components/composites/reasoning/reasoning.tsx +176 -0
  328. package/dist/r/src/components/composites/sources/index.ts +11 -0
  329. package/dist/r/src/components/composites/sources/source.ts +116 -0
  330. package/dist/r/src/components/composites/sources/sources.tsx +163 -0
  331. package/dist/r/src/components/composites/stack-trace/index.ts +19 -0
  332. package/dist/r/src/components/composites/stack-trace/parse-stack-trace.ts +507 -0
  333. package/dist/r/src/components/composites/stack-trace/stack-trace.tsx +395 -0
  334. package/dist/r/src/components/composites/subagent/index.ts +1 -0
  335. package/dist/r/src/components/composites/subagent/subagent.tsx +159 -0
  336. package/dist/r/src/components/composites/suggestion/index.ts +1 -0
  337. package/dist/r/src/components/composites/suggestion/suggestion.tsx +140 -0
  338. package/dist/r/src/components/composites/task/index.ts +14 -0
  339. package/dist/r/src/components/composites/task/task.tsx +317 -0
  340. package/dist/r/src/components/composites/terminal/ansi.ts +674 -0
  341. package/dist/r/src/components/composites/terminal/index.ts +20 -0
  342. package/dist/r/src/components/composites/terminal/terminal-model.ts +111 -0
  343. package/dist/r/src/components/composites/terminal/terminal.tsx +574 -0
  344. package/dist/r/src/components/composites/test-results/index.ts +20 -0
  345. package/dist/r/src/components/composites/test-results/test-results-model.ts +368 -0
  346. package/dist/r/src/components/composites/test-results/test-results.tsx +586 -0
  347. package/dist/r/src/components/composites/tool/format-tool-value.ts +221 -0
  348. package/dist/r/src/components/composites/tool/index.ts +2 -0
  349. package/dist/r/src/components/composites/tool/tool.tsx +228 -0
  350. package/dist/r/src/components/conversation/conversation-surface.tsx +9 -0
  351. package/dist/r/src/components/conversation/scroll-follow-core.ts +31 -0
  352. package/dist/r/src/components/conversation/scroll-follow.ts +92 -0
  353. package/dist/r/src/components/ui/attachment/attachment.tsx +282 -0
  354. package/dist/r/src/components/ui/attachment/index.ts +10 -0
  355. package/dist/r/src/components/ui/bubble/bubble.tsx +86 -0
  356. package/dist/r/src/components/ui/bubble/index.ts +1 -0
  357. package/dist/r/src/components/ui/code-block/code-block.tsx +256 -26
  358. package/dist/r/src/components/ui/code-block/index.ts +9 -1
  359. package/dist/r/src/components/ui/field/number-field.tsx +51 -14
  360. package/dist/r/src/components/ui/marker/index.ts +1 -0
  361. package/dist/r/src/components/ui/marker/marker.tsx +90 -0
  362. package/dist/r/src/components/ui/message/cite-ranges.ts +144 -0
  363. package/dist/r/src/components/ui/message/index.ts +23 -0
  364. package/dist/r/src/components/ui/message/message.tsx +434 -0
  365. package/dist/r/src/components/ui/message/parse-message-text.ts +450 -0
  366. package/dist/r/src/components/ui/questionnaire/index.ts +10 -0
  367. package/dist/r/src/components/ui/questionnaire/questionnaire.tsx +446 -0
  368. package/dist/r/src/components/ui/trace/index.ts +9 -0
  369. package/dist/r/src/components/ui/trace/trace.tsx +334 -0
  370. package/dist/r/src/styles/base.css +34 -0
  371. package/dist/r/stack-trace.json +32 -0
  372. package/dist/r/subagent.json +27 -0
  373. package/dist/r/suggestion.json +24 -0
  374. package/dist/r/task.json +28 -0
  375. package/dist/r/terminal.json +35 -0
  376. package/dist/r/test-results.json +33 -0
  377. package/dist/r/tool.json +31 -0
  378. package/dist/r/trace.json +25 -0
  379. package/package.json +6 -3
  380. package/registry.json +811 -52
  381. package/src/components/composites/chain-of-thought/chain-of-thought.tsx +141 -0
  382. package/src/components/composites/chain-of-thought/index.ts +11 -0
  383. package/src/components/composites/chat/chat.tsx +190 -0
  384. package/src/components/composites/chat/index.ts +11 -0
  385. package/src/components/composites/checkpoint/checkpoint-model.ts +71 -0
  386. package/src/components/composites/checkpoint/checkpoint.tsx +214 -0
  387. package/src/components/composites/checkpoint/index.ts +8 -0
  388. package/src/components/composites/confirmation/confirmation-model.ts +83 -0
  389. package/src/components/composites/confirmation/confirmation.tsx +311 -0
  390. package/src/components/composites/confirmation/index.ts +9 -0
  391. package/src/components/composites/context/context-model.ts +168 -0
  392. package/src/components/composites/context/context.tsx +187 -0
  393. package/src/components/composites/context/index.ts +18 -0
  394. package/src/components/composites/file-tree/build-file-tree.ts +456 -0
  395. package/src/components/composites/file-tree/file-tree.tsx +405 -0
  396. package/src/components/composites/file-tree/index.ts +25 -0
  397. package/src/components/composites/inline-citation/citation.ts +78 -0
  398. package/src/components/composites/inline-citation/index.ts +9 -0
  399. package/src/components/composites/inline-citation/inline-citation.tsx +285 -0
  400. package/src/components/composites/message-scroller/index.ts +2 -0
  401. package/src/components/composites/message-scroller/message-scroller.tsx +301 -0
  402. package/src/components/composites/message-scroller/scroll-anchor.ts +170 -0
  403. package/src/components/composites/model-selector/index.ts +14 -0
  404. package/src/components/composites/model-selector/model-selector.tsx +420 -0
  405. package/src/components/composites/model-selector/models.ts +103 -0
  406. package/src/components/composites/plan/index.ts +1 -0
  407. package/src/components/composites/plan/plan.tsx +155 -0
  408. package/src/components/composites/queue/index.ts +10 -0
  409. package/src/components/composites/queue/queue.tsx +264 -0
  410. package/src/components/composites/reasoning/index.ts +1 -0
  411. package/src/components/composites/reasoning/reasoning.tsx +176 -0
  412. package/src/components/composites/sources/index.ts +11 -0
  413. package/src/components/composites/sources/source.ts +116 -0
  414. package/src/components/composites/sources/sources.tsx +163 -0
  415. package/src/components/composites/stack-trace/index.ts +19 -0
  416. package/src/components/composites/stack-trace/parse-stack-trace.ts +507 -0
  417. package/src/components/composites/stack-trace/stack-trace.tsx +395 -0
  418. package/src/components/composites/subagent/index.ts +1 -0
  419. package/src/components/composites/subagent/subagent.tsx +159 -0
  420. package/src/components/composites/suggestion/index.ts +1 -0
  421. package/src/components/composites/suggestion/suggestion.tsx +140 -0
  422. package/src/components/composites/task/index.ts +14 -0
  423. package/src/components/composites/task/task.tsx +317 -0
  424. package/src/components/composites/terminal/ansi.ts +674 -0
  425. package/src/components/composites/terminal/index.ts +20 -0
  426. package/src/components/composites/terminal/terminal-model.ts +111 -0
  427. package/src/components/composites/terminal/terminal.tsx +574 -0
  428. package/src/components/composites/test-results/index.ts +20 -0
  429. package/src/components/composites/test-results/test-results-model.ts +368 -0
  430. package/src/components/composites/test-results/test-results.tsx +586 -0
  431. package/src/components/composites/tool/format-tool-value.ts +221 -0
  432. package/src/components/composites/tool/index.ts +2 -0
  433. package/src/components/composites/tool/tool.tsx +228 -0
  434. package/src/components/conversation/conversation-surface.tsx +9 -0
  435. package/src/components/conversation/scroll-follow-core.ts +31 -0
  436. package/src/components/conversation/scroll-follow.ts +92 -0
  437. package/src/components/ui/attachment/attachment.tsx +282 -0
  438. package/src/components/ui/attachment/index.ts +10 -0
  439. package/src/components/ui/bubble/bubble.tsx +86 -0
  440. package/src/components/ui/bubble/index.ts +1 -0
  441. package/src/components/ui/code-block/code-block.tsx +256 -26
  442. package/src/components/ui/code-block/index.ts +9 -1
  443. package/src/components/ui/field/number-field.tsx +51 -14
  444. package/src/components/ui/marker/index.ts +1 -0
  445. package/src/components/ui/marker/marker.tsx +90 -0
  446. package/src/components/ui/message/cite-ranges.ts +144 -0
  447. package/src/components/ui/message/index.ts +23 -0
  448. package/src/components/ui/message/message.tsx +434 -0
  449. package/src/components/ui/message/parse-message-text.ts +450 -0
  450. package/src/components/ui/questionnaire/index.ts +10 -0
  451. package/src/components/ui/questionnaire/questionnaire.tsx +446 -0
  452. package/src/components/ui/trace/index.ts +9 -0
  453. package/src/components/ui/trace/trace.tsx +334 -0
  454. package/src/index.ts +26 -0
  455. package/src/styles/base.css +34 -0
@@ -0,0 +1,285 @@
1
+ import { ChevronLeft, ChevronRight } from 'lucide-solid'
2
+ import { createSignal, onCleanup, Show, splitProps } from 'solid-js'
3
+ import { cn } from '../../../lib/utils'
4
+ import { ActionButton } from '../action-button'
5
+ import { Button } from '../../ui/button'
6
+ import type { MessageCitations } from '../../ui/message'
7
+ import { Popover, PopoverContent, PopoverTitle, PopoverTrigger } from '../../ui/popover'
8
+ import { TextLink } from '../../ui/text-link'
9
+ import {
10
+ SOURCE_SNIPPET_LIMIT,
11
+ boundText,
12
+ safeSourceUrl,
13
+ sourceHost,
14
+ sourceTitle,
15
+ type Source,
16
+ } from '../sources/source'
17
+ import {
18
+ citationLabel,
19
+ citationName,
20
+ nextCitationMode,
21
+ wrapIndex,
22
+ type CitationEvent,
23
+ type CitationMode,
24
+ } from './citation'
25
+
26
+ /**
27
+ * InlineCitation.
28
+ *
29
+ * A small badge inside a sentence — "1", or a host — that opens the source(s)
30
+ * behind the claim: title, host, address, snippet, and Previous/Next when several
31
+ * sources share one marker. It is an inline element for prose such as
32
+ * `MessageText`: it never breaks across lines, stays on the text baseline, and
33
+ * adds no block of its own. For the list of everything a reply used, reach for
34
+ * `Sources`; the two number sources the same way.
35
+ *
36
+ * ## One popover, two ways to open it
37
+ *
38
+ * A hover card would exclude everyone who is not holding a mouse, so this is a
39
+ * `Popover` that a pointer can also *peek* at:
40
+ *
41
+ * - **Hover or keyboard focus peeks.** The popover opens without taking focus
42
+ * and closes when the pointer or focus leaves. Touch has no hover, so a touch
43
+ * never peeks.
44
+ * - **Click, tap, Enter or Space pins.** Focus moves into the popover so its
45
+ * link and Previous/Next are reachable; it stays until Escape, an outside press,
46
+ * or the badge is pressed again, and focus returns to the badge. Pressing a badge
47
+ * that is only peeking pins it rather than toggling it shut — the transitions
48
+ * are the pure `nextCitationMode`.
49
+ *
50
+ * Kobalte's trigger toggles on every press, so `open` is controlled and its own
51
+ * close request is ignored for the press that we already decided (`pressing`).
52
+ * Without that, hover-then-click, the commonest pointer path, would close the card.
53
+ *
54
+ * ## It makes no claim without sources
55
+ *
56
+ * No sources renders nothing: a marker that points nowhere is not a citation.
57
+ * The address is a link only when `safeSourceUrl` allows it; the host comes from
58
+ * the parsed URL; titles and snippets are clipped, cleaned of direction
59
+ * overrides and rendered as text. The badge's accessible name carries the title
60
+ * and host (the visible label is only a number or a host), so a reader hears
61
+ * what is cited without opening anything.
62
+ */
63
+ export type InlineCitationProps = {
64
+ /** One source, or several that support the same claim. */
65
+ sources: readonly Source[]
66
+ /**
67
+ * The 1-based number to show, matching the source's position in `Sources`.
68
+ * Omit it to show the host instead; a number is never guessed.
69
+ */
70
+ index?: number
71
+ /** Milliseconds a pointer rests before the peek opens. */
72
+ hoverDelay?: number
73
+ class?: string
74
+ }
75
+
76
+ const LEAVE_DELAY = 200
77
+
78
+ export function InlineCitation(props: InlineCitationProps) {
79
+ const [local] = splitProps(props, ['sources', 'index', 'hoverDelay', 'class'])
80
+ const [mode, setMode] = createSignal<CitationMode>('closed')
81
+ const [position, setPosition] = createSignal(0)
82
+ let timer: ReturnType<typeof setTimeout> | undefined
83
+ let content: HTMLElement | undefined
84
+ let pressing = false
85
+ // Focus handed back to the badge as the popover closes is not a request to
86
+ // peek again; without this, Escape from a pinned card reopens it as a preview.
87
+ let restoringFocus = false
88
+
89
+ const count = () => local.sources.length
90
+ const active = () => local.sources[wrapIndex(position(), count())]
91
+ const clear = () => {
92
+ if (timer !== undefined) clearTimeout(timer)
93
+ timer = undefined
94
+ }
95
+ onCleanup(clear)
96
+
97
+ const dispatch = (event: CitationEvent) => setMode((current) => nextCitationMode(current, event))
98
+ const schedule = (event: CitationEvent, delay: number) => {
99
+ clear()
100
+ timer = setTimeout(() => dispatch(event), delay)
101
+ }
102
+
103
+ const onPointerEnter = (event: PointerEvent) => {
104
+ if (event.pointerType === 'touch') return
105
+ schedule('enter', local.hoverDelay ?? 150)
106
+ }
107
+ const onPointerLeave = (event: PointerEvent) => {
108
+ if (event.pointerType === 'touch') return
109
+ schedule('leave', LEAVE_DELAY)
110
+ }
111
+
112
+ const onClick = () => {
113
+ clear()
114
+ const wasPeeking = mode() === 'peek'
115
+ dispatch('press')
116
+ // Kobalte toggles right after this handler; its request is not a dismissal.
117
+ pressing = true
118
+ queueMicrotask(() => {
119
+ pressing = false
120
+ })
121
+ // The peek never took focus; a deliberate press must hand it over.
122
+ if (wasPeeking) queueMicrotask(() => content?.focus())
123
+ }
124
+
125
+ return (
126
+ <Show when={count() > 0}>
127
+ <Popover
128
+ open={mode() !== 'closed'}
129
+ onOpenChange={(next) => {
130
+ if (!next && !pressing) dispatch('dismiss')
131
+ }}
132
+ placement="bottom"
133
+ gutter={6}
134
+ >
135
+ <PopoverTrigger
136
+ as={Button}
137
+ variant="subtle"
138
+ size="2xs"
139
+ touchTarget="comfortable"
140
+ data-slot="inline-citation"
141
+ aria-label={citationName(local.sources, local.index)}
142
+ class={cn('mx-0.5 align-baseline tabular-nums', local.class)}
143
+ onClick={onClick}
144
+ onPointerEnter={onPointerEnter}
145
+ onPointerLeave={onPointerLeave}
146
+ onFocus={(event: FocusEvent & { currentTarget: HTMLElement }) => {
147
+ // Mouse focus follows the press; only keyboard focus peeks.
148
+ if (restoringFocus) return
149
+ if (event.currentTarget.matches(':focus-visible')) dispatch('focus')
150
+ }}
151
+ onBlur={() => dispatch('blur')}
152
+ >
153
+ <span aria-hidden="true" class="max-w-32 truncate">
154
+ {citationLabel(local.index, local.sources[0])}
155
+ </span>
156
+ <Show when={count() > 1}>
157
+ <span aria-hidden="true">+{count() - 1}</span>
158
+ </Show>
159
+ </PopoverTrigger>
160
+ <PopoverContent
161
+ ref={(element: HTMLElement) => (content = element)}
162
+ class="w-72 max-w-full"
163
+ onOpenAutoFocus={(event: Event) => {
164
+ if (mode() === 'peek') event.preventDefault()
165
+ }}
166
+ onCloseAutoFocus={() => {
167
+ // The focus move that follows is synchronous; the microtask ends the window.
168
+ restoringFocus = true
169
+ queueMicrotask(() => {
170
+ restoringFocus = false
171
+ })
172
+ }}
173
+ onPointerEnter={(event: PointerEvent) => {
174
+ if (event.pointerType !== 'touch') clear()
175
+ }}
176
+ onPointerLeave={onPointerLeave}
177
+ >
178
+ <Show when={active()}>
179
+ {(source) => (
180
+ <div class="flex min-w-0 flex-col gap-1.5 px-2 py-1.5">
181
+ <Show when={count() > 1}>
182
+ <div class="flex items-center justify-between gap-2">
183
+ <span
184
+ role="status"
185
+ aria-live="polite"
186
+ class="text-xs text-muted-foreground tabular-nums"
187
+ >
188
+ Source {wrapIndex(position(), count()) + 1} of {count()}
189
+ </span>
190
+ <span class="flex items-center gap-0.5">
191
+ <ActionButton
192
+ variant="ghost"
193
+ size="icon-xs"
194
+ aria-label="Previous source"
195
+ tooltip="Previous source"
196
+ onClick={() => setPosition((value) => wrapIndex(value - 1, count()))}
197
+ >
198
+ <ChevronLeft />
199
+ </ActionButton>
200
+ <ActionButton
201
+ variant="ghost"
202
+ size="icon-xs"
203
+ aria-label="Next source"
204
+ tooltip="Next source"
205
+ onClick={() => setPosition((value) => wrapIndex(value + 1, count()))}
206
+ >
207
+ <ChevronRight />
208
+ </ActionButton>
209
+ </span>
210
+ </div>
211
+ </Show>
212
+ <PopoverTitle class="p-0 [overflow-wrap:anywhere]">
213
+ {sourceTitle(source())}
214
+ </PopoverTitle>
215
+ <CitationDetails source={source()} />
216
+ </div>
217
+ )}
218
+ </Show>
219
+ </PopoverContent>
220
+ </Popover>
221
+ </Show>
222
+ )
223
+ }
224
+
225
+ /**
226
+ * Citation markers for `MessageText`, from the sources a reply really used.
227
+ *
228
+ * `[^2]` in the text becomes the second source's badge; `[^1,3]` one badge for
229
+ * sources 1 and 3. A number beyond the list leaves the marker as written, and an
230
+ * empty list makes every marker literal, so text can never cite a source the host
231
+ * did not supply.
232
+ *
233
+ * <MessageText text={reply} citations={messageCitations(sources)} />
234
+ */
235
+ export function messageCitations(sources: readonly Source[]): MessageCitations {
236
+ return {
237
+ count: sources.length,
238
+ render: (numbers) => (
239
+ <InlineCitation
240
+ index={numbers[0]}
241
+ sources={numbers.flatMap((number) => {
242
+ const source = sources[number - 1]
243
+ return source ? [source] : []
244
+ })}
245
+ />
246
+ ),
247
+ }
248
+ }
249
+
250
+ function CitationDetails(props: { source: Source }) {
251
+ const href = () => safeSourceUrl(props.source.url)
252
+ const host = () => sourceHost(props.source.url)
253
+ const snippet = () =>
254
+ props.source.snippet ? boundText(props.source.snippet, SOURCE_SNIPPET_LIMIT) : ''
255
+ const withheld = () => props.source.url !== undefined && props.source.url !== '' && !href()
256
+
257
+ return (
258
+ <>
259
+ <Show when={host()}>
260
+ <p class="m-0 text-xs text-muted-foreground [overflow-wrap:anywhere]">{host()}</p>
261
+ </Show>
262
+ <Show when={href()}>
263
+ {(url) => (
264
+ <TextLink
265
+ href={url()}
266
+ target="_blank"
267
+ rel="noreferrer noopener"
268
+ class="w-fit max-w-full text-xs [overflow-wrap:anywhere]"
269
+ >
270
+ {boundText(url(), 96)}
271
+ <span class="visually-hidden"> (opens in a new tab)</span>
272
+ </TextLink>
273
+ )}
274
+ </Show>
275
+ <Show when={withheld()}>
276
+ <p class="m-0 text-xs text-muted-foreground">Address not linked</p>
277
+ </Show>
278
+ <Show when={snippet()}>
279
+ <p class="m-0 line-clamp-6 text-xs text-muted-foreground [overflow-wrap:anywhere]">
280
+ {snippet()}
281
+ </p>
282
+ </Show>
283
+ </>
284
+ )
285
+ }
@@ -0,0 +1,2 @@
1
+ export { MessageScroller, type MessageScrollerProps } from './message-scroller'
2
+ export { captureAnchor, restoreAnchor, type ScrollAnchor } from './scroll-anchor'
@@ -0,0 +1,301 @@
1
+ import { History, LoaderCircle } from 'lucide-solid'
2
+ import { createEffect, onCleanup, Show, splitProps, createSignal, type JSX } from 'solid-js'
3
+ import { cn } from '../../../lib/utils'
4
+ import { Button } from '../../ui/button'
5
+ import {
6
+ ConversationSurface,
7
+ type ConversationSurfaceProps,
8
+ } from '../../conversation/conversation-surface'
9
+ import { anchorIds, captureAnchor, restoreAnchor, type ScrollAnchor } from './scroll-anchor'
10
+
11
+ /**
12
+ * MessageScroller.
13
+ *
14
+ * The thread. It owns the three scroll behaviours a chat gets wrong when each
15
+ * is bolted on separately, and keeps them from fighting:
16
+ *
17
+ * 1. **Follow the reply only while the reader is at the bottom.** New and
18
+ * streaming content scrolls into view when the reader is already at the end,
19
+ * and never otherwise. Scrolling up parks the view; the "Jump to latest"
20
+ * control brings it back. (This is `ConversationSurface`'s follow contract,
21
+ * which this component wraps rather than reimplements.)
22
+ * 2. **Load history without moving what the reader is looking at.** When the
23
+ * reader reaches the top, `onLoadOlder` is asked for earlier turns. Turns
24
+ * inserted *above* the viewport would normally push the visible content down
25
+ * by exactly their height; here the first visible turn (found by
26
+ * `data-anchor-id`, which `Message` sets from `anchorId`) is measured before
27
+ * the load and put back to the same offset after it, on every layout change
28
+ * until the load settles — so images and code blocks that size themselves
29
+ * late do not jolt the view either. A "Load earlier" button is always present
30
+ * while there is more, because the scroll trigger alone is unreachable to a
31
+ * keyboard or switch user and fires nothing in a short pane.
32
+ * 3. **Announce without flooding.** The scroller is a polite log, and a turn that
33
+ * is still arriving marks itself `aria-busy`, so a screen reader waits for the
34
+ * settled turn instead of reading every token. `announcement` is a separate
35
+ * status region for the two moments that matter: a response starting and a
36
+ * response finishing.
37
+ *
38
+ * What it does not own: which turns exist, their identity, or what "older"
39
+ * means. The host passes children and a loader; a session switch is
40
+ * `resetKey`, which re-arms follow and discards any in-flight anchor.
41
+ */
42
+ export type MessageScrollerProps = Omit<ConversationSurfaceProps, 'header' | 'empty'> & {
43
+ /** There are earlier turns the host can load. */
44
+ hasOlder?: boolean
45
+ /** The host is loading earlier turns right now. */
46
+ loadingOlder?: boolean
47
+ /**
48
+ * Load earlier turns. The scroller preserves the reader's position across the
49
+ * resulting insert whether the host resolves before or after rendering them.
50
+ */
51
+ onLoadOlder?: () => void | Promise<void>
52
+ /** Words for the loader row; override for another language. */
53
+ olderLabel?: string
54
+ loadingOlderLabel?: string
55
+ /** Rendered above the first turn, below the history control. */
56
+ header?: JSX.Element
57
+ /** Rendered when there are no turns. */
58
+ empty?: JSX.Element
59
+ /** A short sentence for the live status region ("Assistant is responding"). */
60
+ announcement?: string
61
+ }
62
+
63
+ /** How far from the top counts as "reached the top", in px. */
64
+ const TOP_MARGIN = 160
65
+
66
+ /** How long a host that still reports `loadingOlder` is waited for, in frames (~10 s). */
67
+ const MAX_HOST_WAIT_FRAMES = 600
68
+
69
+ export function MessageScroller(props: MessageScrollerProps) {
70
+ const [local, rest] = splitProps(props, [
71
+ 'class',
72
+ 'hasOlder',
73
+ 'loadingOlder',
74
+ 'onLoadOlder',
75
+ 'olderLabel',
76
+ 'loadingOlderLabel',
77
+ 'header',
78
+ 'empty',
79
+ 'announcement',
80
+ 'ref',
81
+ 'resetKey',
82
+ 'children',
83
+ ])
84
+ let scroller: HTMLDivElement | undefined
85
+ let holdFollow: (() => () => void) | undefined
86
+ let epoch = 0
87
+ const [inFlight, setInFlight] = createSignal(false)
88
+
89
+ // While a load holds the reader's place, the browser's own scroll anchoring is
90
+ // switched off: it moves the offset by the same insert, and the two corrections
91
+ // would be added together.
92
+ let nativeAnchoring: string | undefined
93
+ const holdNativeAnchoring = () => {
94
+ if (!scroller || nativeAnchoring !== undefined) return
95
+ nativeAnchoring = scroller.style.overflowAnchor
96
+ scroller.style.overflowAnchor = 'none'
97
+ }
98
+ const releaseNativeAnchoring = () => {
99
+ if (scroller && nativeAnchoring !== undefined) scroller.style.overflowAnchor = nativeAnchoring
100
+ nativeAnchoring = undefined
101
+ }
102
+
103
+ // A session switch invalidates a load that was started for the previous one.
104
+ createEffect(() => {
105
+ void local.resetKey
106
+ epoch += 1
107
+ setInFlight(false)
108
+ stopWatching()
109
+ releaseNativeAnchoring()
110
+ })
111
+
112
+ const settle = (
113
+ anchor: ScrollAnchor | undefined,
114
+ run: number,
115
+ heightBefore: number,
116
+ restoreFocus: boolean,
117
+ releaseHold?: () => void
118
+ ) => {
119
+ const finish = () => {
120
+ if (run !== epoch) return releaseHold?.()
121
+ if (scroller) restoreAnchor(scroller, anchor)
122
+ stopWatching()
123
+ releaseNativeAnchoring()
124
+ setInFlight(false)
125
+ // The control that started the load was replaced by the loading line, which
126
+ // takes focus with it. Put it back on the control (or the thread), so a
127
+ // keyboard user is not dropped onto the page.
128
+ if (restoreFocus && scroller && document.activeElement === document.body) {
129
+ const again = scroller.querySelector<HTMLElement>(
130
+ '[data-slot="message-scroller-older"] button'
131
+ )
132
+ ;(again ?? scroller).focus({ preventScroll: true })
133
+ }
134
+ // Still at the top after a load that added content: keep going, so a
135
+ // pane taller than one page of history fills without a second scroll.
136
+ if (
137
+ scroller &&
138
+ local.hasOlder &&
139
+ scroller.scrollTop < TOP_MARGIN &&
140
+ scroller.scrollHeight > heightBefore
141
+ )
142
+ void load()
143
+ releaseHold?.()
144
+ }
145
+ // Two frames: the first lets the host's insert commit, the second lets
146
+ // late-sizing content (images, highlighted code) report its height. A host
147
+ // that says it is still loading (`loadingOlder`) after its promise resolved
148
+ // is not finished either: its turns will land later, and the anchor must
149
+ // still be held when they do.
150
+ const wait = (waited: number) =>
151
+ requestAnimationFrame(() => {
152
+ if (run !== epoch) return releaseHold?.()
153
+ if (local.loadingOlder && waited < MAX_HOST_WAIT_FRAMES) return void wait(waited + 1)
154
+ finish()
155
+ })
156
+ requestAnimationFrame(() => wait(0))
157
+ }
158
+
159
+ let observer: MutationObserver | undefined
160
+ let resizer: ResizeObserver | undefined
161
+ let unfollow: (() => void) | undefined
162
+ const stopWatching = () => {
163
+ observer?.disconnect()
164
+ resizer?.disconnect()
165
+ unfollow?.()
166
+ unfollow = undefined
167
+ }
168
+ const load = async (fromControl = false) => {
169
+ if (!scroller || !local.onLoadOlder || !local.hasOlder || local.loadingOlder || inFlight())
170
+ return
171
+ const run = epoch
172
+ const anchor = captureAnchor(scroller)
173
+ anchor.known = anchorIds(scroller)
174
+ const heightBefore = scroller.scrollHeight
175
+ const restoreFocus =
176
+ fromControl && document.activeElement?.closest('[data-slot="message-scroller-older"]') != null
177
+ setInFlight(true)
178
+ holdNativeAnchoring()
179
+ // Every layout change while the load is open re-pins the anchor *before
180
+ // paint*, which is what makes the insert invisible.
181
+ stopWatching()
182
+ observer = new MutationObserver(() => {
183
+ if (run === epoch && scroller) restoreAnchor(scroller, anchor)
184
+ })
185
+ observer.observe(scroller, { childList: true, subtree: true })
186
+ // Turns that size themselves late (`content-visibility`, images, highlighted code)
187
+ // move the anchor without adding or removing a node, and with the browser's own
188
+ // anchoring held off nothing else would carry the reader through that.
189
+ const content = scroller.querySelector('[data-slot="conversation-content"]')
190
+ if (content && typeof ResizeObserver !== 'undefined') {
191
+ resizer = new ResizeObserver(() => {
192
+ if (run === epoch && scroller) restoreAnchor(scroller, anchor)
193
+ })
194
+ resizer.observe(content)
195
+ }
196
+ // The reader may carry on scrolling while the host answers. A restore re-takes the
197
+ // anchor on the turn they have come to, whichever of the observers or this event
198
+ // reaches it first.
199
+ const follow = () => {
200
+ if (run === epoch && scroller) restoreAnchor(scroller, anchor)
201
+ }
202
+ scroller.addEventListener('scroll', follow, { passive: true })
203
+ const watched = scroller
204
+ unfollow = () => watched.removeEventListener('scroll', follow)
205
+ try {
206
+ await local.onLoadOlder()
207
+ } catch {
208
+ // The host reports its own failure; the control stays so the reader can retry.
209
+ }
210
+ // A load that outlived its conversation inserts into a thread whose reader it knows
211
+ // nothing about, and the anchor it held is gone. What it does to the layout (rows
212
+ // taking their height, a clamp, the insert itself) is not the reader scrolling, so the
213
+ // follow position is held until the two frames the insert needs have passed.
214
+ settle(anchor, run, heightBefore, restoreFocus, run !== epoch ? holdFollow?.() : undefined)
215
+ }
216
+
217
+ onCleanup(() => {
218
+ epoch += 1
219
+ stopWatching()
220
+ releaseNativeAnchoring()
221
+ })
222
+
223
+ const watchTop = (element: HTMLDivElement) => {
224
+ if (typeof IntersectionObserver === 'undefined') return
225
+ const io = new IntersectionObserver(
226
+ (entries) => {
227
+ if (entries.some((entry) => entry.isIntersecting)) void load()
228
+ },
229
+ { root: scroller ?? null, rootMargin: `${TOP_MARGIN}px 0px 0px 0px` }
230
+ )
231
+ io.observe(element)
232
+ onCleanup(() => io.disconnect())
233
+ }
234
+
235
+ return (
236
+ <div
237
+ class={cn('relative flex min-h-0 flex-1 flex-col', local.class)}
238
+ data-slot="message-scroller"
239
+ >
240
+ <ConversationSurface
241
+ role="log"
242
+ aria-live="polite"
243
+ aria-relevant="additions"
244
+ resetKey={local.resetKey}
245
+ onFollowHold={(hold) => {
246
+ holdFollow = hold
247
+ }}
248
+ {...rest}
249
+ ref={(element) => {
250
+ scroller = element
251
+ if (typeof local.ref === 'function') local.ref(element)
252
+ }}
253
+ class="flex-1"
254
+ header={
255
+ <>
256
+ <Show when={local.hasOlder || local.loadingOlder}>
257
+ <div
258
+ ref={watchTop}
259
+ data-slot="message-scroller-older"
260
+ class="flex justify-center py-2"
261
+ >
262
+ <Show
263
+ when={local.loadingOlder || inFlight()}
264
+ fallback={
265
+ <Button
266
+ type="button"
267
+ variant="ghost"
268
+ size="sm"
269
+ class="gap-1.5 text-muted-foreground"
270
+ onClick={() => void load(true)}
271
+ >
272
+ <History aria-hidden="true" />
273
+ {local.olderLabel ?? 'Load earlier messages'}
274
+ </Button>
275
+ }
276
+ >
277
+ <p
278
+ role="status"
279
+ // The button's own height: swapping one for the other must not move
280
+ // the turns below it by even a pixel while the anchor is being held.
281
+ class="flex h-control-sm items-center gap-1.5 text-xs text-muted-foreground"
282
+ >
283
+ <LoaderCircle aria-hidden="true" class="size-3.5 motion-safe:animate-spin" />
284
+ {local.loadingOlderLabel ?? 'Loading earlier messages…'}
285
+ </p>
286
+ </Show>
287
+ </div>
288
+ </Show>
289
+ {local.header}
290
+ </>
291
+ }
292
+ empty={local.empty}
293
+ >
294
+ {local.children}
295
+ </ConversationSurface>
296
+ <p role="status" class="sr-only">
297
+ {local.announcement}
298
+ </p>
299
+ </div>
300
+ )
301
+ }