@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,111 @@
1
+ /**
2
+ * The terminal's pure decisions: what its status says, which lines are mounted,
3
+ * and whether the reader is at the end. They live apart from the component so
4
+ * the rules that are easy to get subtly wrong — an off-by-one in "N earlier
5
+ * lines", a tolerance that yanks the reader back to the bottom — have tests.
6
+ */
7
+
8
+ /**
9
+ * What the host knows about the command. Absent means no statement: a terminal
10
+ * handed output with no status does not say whether the command is still running.
11
+ */
12
+ export type TerminalStatus =
13
+ | { state: 'running' }
14
+ | { state: 'exited'; code: number }
15
+ | { state: 'cancelled' }
16
+
17
+ /** The words for a status, which are also what a screen reader hears. */
18
+ export function terminalStatusLabel(status: TerminalStatus): string {
19
+ if (status.state === 'running') return 'Running'
20
+ if (status.state === 'cancelled') return 'Cancelled'
21
+ return Number.isInteger(status.code) ? `Exited with code ${status.code}` : 'Exited'
22
+ }
23
+
24
+ /** Whether a status is a non-zero exit — the one thing the header tones as a failure. */
25
+ export function isFailedExit(status: TerminalStatus | undefined): boolean {
26
+ return status?.state === 'exited' && Number.isInteger(status.code) && status.code !== 0
27
+ }
28
+
29
+ export type TerminalWindow = Readonly<{
30
+ /** Index of the first mounted finished line. */
31
+ start: number
32
+ /** Index after the last mounted finished line. */
33
+ end: number
34
+ /** Retained lines before `start` that "show earlier" can reveal. */
35
+ revealable: number
36
+ /** Every earlier line not on screen, including those the parser discarded. */
37
+ hidden: number
38
+ /** Retained finished lines after `end`: output that arrived while the view was parked. */
39
+ later: number
40
+ /** Lines of the parked view that retention discarded before the reader moved on. */
41
+ evicted: number
42
+ /** The whole parked view was discarded, so the oldest retained lines are shown instead. */
43
+ lost: boolean
44
+ }>
45
+
46
+ /**
47
+ * The slice of the retained lines that is mounted. `limit` is how many finished
48
+ * lines to show; the rest are counted, not rendered, because a terminal that
49
+ * mounts every line of a long build is a frozen page.
50
+ *
51
+ * While the reader follows the tail the slice ends at the newest line. Once they
52
+ * have scrolled away it is *parked*: `anchorEnd` is the number of lines (counting
53
+ * the ones retention already discarded) that existed when they left, and the
54
+ * slice stays ending there while output keeps arriving, so what they are reading
55
+ * does not move under them. If retention discards the lines they were reading,
56
+ * `evicted` says how many, and when all of them are gone `lost` is set and the
57
+ * oldest retained lines are shown, because the nearest thing that still exists
58
+ * is better than a blank box.
59
+ */
60
+ export function terminalWindow(
61
+ retained: number,
62
+ dropped: number,
63
+ limit: number,
64
+ anchorEnd?: number
65
+ ): TerminalWindow {
66
+ const size = Math.max(1, Math.floor(limit))
67
+ if (anchorEnd === undefined || anchorEnd >= dropped + retained) {
68
+ const start = Math.max(0, retained - size)
69
+ return {
70
+ start,
71
+ end: retained,
72
+ revealable: start,
73
+ hidden: start + dropped,
74
+ later: 0,
75
+ evicted: 0,
76
+ lost: false,
77
+ }
78
+ }
79
+ const wantedStart = Math.max(0, anchorEnd - size)
80
+ const lost = anchorEnd <= dropped
81
+ const end = lost ? Math.min(retained, size) : anchorEnd - dropped
82
+ const start = lost ? 0 : Math.max(0, end - size)
83
+ const evicted = lost
84
+ ? anchorEnd - wantedStart
85
+ : Math.max(0, Math.min(dropped, anchorEnd) - wantedStart)
86
+ return {
87
+ start,
88
+ end,
89
+ revealable: start,
90
+ hidden: start + dropped,
91
+ later: retained - end,
92
+ evicted,
93
+ lost,
94
+ }
95
+ }
96
+
97
+ /** The limit after one "show earlier" press: one more page, never past what exists. */
98
+ export function revealEarlier(limit: number, page: number, retained: number): number {
99
+ return Math.min(Math.max(retained, 1), limit + Math.max(1, Math.floor(page)))
100
+ }
101
+
102
+ /** Subpixel scroll positions and zoom make "exactly at the bottom" unreliable. */
103
+ export const BOTTOM_TOLERANCE_PX = 4
104
+
105
+ /**
106
+ * Whether a scroller is showing its end. This decides whether new output
107
+ * follows the reader: at the bottom it does, anywhere else the view is theirs.
108
+ */
109
+ export function isAtBottom(scrollHeight: number, scrollTop: number, clientHeight: number): boolean {
110
+ return scrollHeight - scrollTop - clientHeight <= BOTTOM_TOLERANCE_PX
111
+ }
@@ -0,0 +1,574 @@
1
+ import {
2
+ ArrowDown,
3
+ Check,
4
+ CircleAlert,
5
+ CircleSlash,
6
+ Copy,
7
+ Eraser,
8
+ LoaderCircle,
9
+ } from 'lucide-solid'
10
+ import {
11
+ createEffect,
12
+ createMemo,
13
+ createSignal,
14
+ For,
15
+ on,
16
+ Show,
17
+ splitProps,
18
+ type ComponentProps,
19
+ } from 'solid-js'
20
+ import { writeClipboardText } from '#lib/clipboard'
21
+ import { cn } from '#lib/utils'
22
+ import { Button } from '../../ui/button'
23
+ import {
24
+ createAnsiParser,
25
+ isPlainStyle,
26
+ resolveColors,
27
+ type AnsiLine,
28
+ type AnsiStyle,
29
+ } from './ansi'
30
+ import {
31
+ isAtBottom,
32
+ isFailedExit,
33
+ revealEarlier,
34
+ terminalStatusLabel,
35
+ terminalWindow,
36
+ type TerminalStatus,
37
+ } from './terminal-model'
38
+
39
+ /**
40
+ * Terminal.
41
+ *
42
+ * A command and its output, as it arrives. It is the thing to put inside an
43
+ * agent's "ran a command" line (a `TraceLine`, with `size="sm"`) when the output
44
+ * is the evidence; reach for `CodeBlock` instead when the text is source or a
45
+ * finished log that needs no live behaviour, and for `TestResults` when the
46
+ * output is a test run the host can structure.
47
+ *
48
+ * It does not wrap `CodeBlock`. A block's `highlight` seam re-renders the whole
49
+ * body whenever the code changes, which is exactly wrong for output that grows a
50
+ * line at a time, and a terminal has to own its scroller to follow the tail. It
51
+ * keeps the block's header and surface vocabulary so the two read as one family.
52
+ *
53
+ * ## What the output is allowed to do
54
+ *
55
+ * Output is untrusted bytes. It is parsed by `createAnsiParser` into plain text
56
+ * and a fixed set of styles — sixteen theme colours, bold, dim, italic,
57
+ * underline, strike-through, reverse — and everything else (cursor movement,
58
+ * title and clipboard sequences, anything malformed) is dropped, never echoed.
59
+ * Colours come from the theme's `--terminal-ansi-*` tokens, painted on the
60
+ * theme's own terminal background, so a program's colours keep the contrast the
61
+ * theme measured for them. Nothing is rendered as markup.
62
+ *
63
+ * ## Streaming
64
+ *
65
+ * `output` is the whole string so far. When the new value begins with the old
66
+ * one, only the appended text is parsed, so a chunk costs the size of the chunk
67
+ * and not of the transcript. Finished lines are keyed by identity and are never
68
+ * re-rendered; only the line still being written changes, which is what makes a
69
+ * `\r` progress bar redraw in place. Batch appends per frame — the host sees a
70
+ * prefix check proportional to the output on every change.
71
+ *
72
+ * Only the last `maxLines` finished lines are mounted. The rest are counted in
73
+ * an explicit note with a control to show them a page at a time, because tens of
74
+ * thousands of row elements are a frozen page, not a transcript. Copy still
75
+ * writes `output` exactly as given — not the rendered lines, not the visible tail.
76
+ *
77
+ * ## Following the tail
78
+ *
79
+ * The terminal scrolls inside its own bounded box and follows new output only
80
+ * while the reader is at the bottom of that box. Scrolling up *parks* the view:
81
+ * the lines mounted are the ones that were there when they left, however much
82
+ * output arrives, and "Jump to latest" is the only way back to the tail (reaching
83
+ * the bottom of a parked view with nothing newer resumes it, because nothing was
84
+ * missed). If retention discards the lines they were reading the note says how
85
+ * many, and when none are left the oldest retained lines are shown. The page
86
+ * itself is never scrolled. Scrolling is instant, so there is no animation to
87
+ * reduce.
88
+ *
89
+ * ## Status is a claim
90
+ *
91
+ * `status` is drawn only when the host passes it, in words as well as an icon,
92
+ * and only a non-zero exit is toned as a failure: this component does not decide
93
+ * that a command "passed". Without a status the header says nothing about whether
94
+ * the command is still running. `onClear` adds a clear button; without it the
95
+ * output is the host's record and nothing here offers to erase it.
96
+ */
97
+ export type TerminalProps = Omit<ComponentProps<'div'>, 'children'> & {
98
+ /** Everything the command has printed so far, exactly as received. */
99
+ output: string
100
+ /** The command line. Shown as text; the prompt glyph is decoration. */
101
+ command?: string
102
+ status?: TerminalStatus
103
+ /** Finished lines mounted at once; earlier ones are counted and revealed a page at a time. */
104
+ maxLines?: number
105
+ /** Finished lines kept at all; older ones are discarded and counted. Default 200 000. */
106
+ retainLines?: number
107
+ /** Height of the output box, which scrolls past it. A number is pixels. Default `20rem`. */
108
+ maxHeight?: number | string
109
+ /** Soft-wrap long lines instead of scrolling sideways. Off: terminal columns keep their alignment. */
110
+ wrap?: boolean
111
+ /** `sm` is the compact chrome for output nested inside a Trace line. */
112
+ size?: 'default' | 'sm'
113
+ /** Show a clear button. The host empties `output`; the terminal erases nothing itself. */
114
+ onClear?: () => void
115
+ copyLabel?: string
116
+ copiedLabel?: string
117
+ clearLabel?: string
118
+ /** Called after each copy attempt with whether the clipboard accepted it. */
119
+ onCopy?: (ok: boolean) => void
120
+ }
121
+
122
+ /** Finished lines mounted by default. */
123
+ export const TERMINAL_MAX_LINES = 1000
124
+
125
+ // One literal class per colour, written out in full so the stylesheet generator
126
+ // can see each of them; a class assembled from an index would never be emitted.
127
+ const FG_CLASS = [
128
+ 'text-(color:--terminal-ansi-black)',
129
+ 'text-(color:--terminal-ansi-red)',
130
+ 'text-(color:--terminal-ansi-green)',
131
+ 'text-(color:--terminal-ansi-yellow)',
132
+ 'text-(color:--terminal-ansi-blue)',
133
+ 'text-(color:--terminal-ansi-magenta)',
134
+ 'text-(color:--terminal-ansi-cyan)',
135
+ 'text-(color:--terminal-ansi-white)',
136
+ 'text-(color:--terminal-ansi-bright-black)',
137
+ 'text-(color:--terminal-ansi-bright-red)',
138
+ 'text-(color:--terminal-ansi-bright-green)',
139
+ 'text-(color:--terminal-ansi-bright-yellow)',
140
+ 'text-(color:--terminal-ansi-bright-blue)',
141
+ 'text-(color:--terminal-ansi-bright-magenta)',
142
+ 'text-(color:--terminal-ansi-bright-cyan)',
143
+ 'text-(color:--terminal-ansi-bright-white)',
144
+ ] as const
145
+ const BG_CLASS = [
146
+ 'bg-(--terminal-ansi-black)',
147
+ 'bg-(--terminal-ansi-red)',
148
+ 'bg-(--terminal-ansi-green)',
149
+ 'bg-(--terminal-ansi-yellow)',
150
+ 'bg-(--terminal-ansi-blue)',
151
+ 'bg-(--terminal-ansi-magenta)',
152
+ 'bg-(--terminal-ansi-cyan)',
153
+ 'bg-(--terminal-ansi-white)',
154
+ 'bg-(--terminal-ansi-bright-black)',
155
+ 'bg-(--terminal-ansi-bright-red)',
156
+ 'bg-(--terminal-ansi-bright-green)',
157
+ 'bg-(--terminal-ansi-bright-yellow)',
158
+ 'bg-(--terminal-ansi-bright-blue)',
159
+ 'bg-(--terminal-ansi-bright-magenta)',
160
+ 'bg-(--terminal-ansi-bright-cyan)',
161
+ 'bg-(--terminal-ansi-bright-white)',
162
+ ] as const
163
+
164
+ function segmentClass(style: AnsiStyle): string {
165
+ const { fg, bg } = resolveColors(style)
166
+ return cn(
167
+ {
168
+ 'font-bold': style.bold,
169
+ 'opacity-70': style.dim,
170
+ italic: style.italic,
171
+ underline: style.underline && !style.strike,
172
+ 'line-through': style.strike && !style.underline,
173
+ '[text-decoration-line:underline_line-through]': style.underline && style.strike,
174
+ 'text-(color:--terminal-background)': fg === 'default-bg',
175
+ 'bg-(--terminal-foreground)': bg === 'default-fg',
176
+ },
177
+ typeof fg === 'number' && FG_CLASS[fg],
178
+ typeof bg === 'number' && BG_CLASS[bg]
179
+ )
180
+ }
181
+
182
+ function LineView(props: { line: AnsiLine; wrap: boolean }) {
183
+ return (
184
+ // `min-h-lh` keeps an empty line one line tall; a block with no text would
185
+ // collapse and the transcript would lose its blank lines.
186
+ <div
187
+ class={cn('min-h-lh', {
188
+ 'whitespace-pre-wrap [overflow-wrap:anywhere]': props.wrap,
189
+ 'w-max min-w-full': !props.wrap,
190
+ })}
191
+ >
192
+ <For each={props.line.segments}>
193
+ {(segment) =>
194
+ isPlainStyle(segment.style) ? (
195
+ segment.text
196
+ ) : (
197
+ <span class={segmentClass(segment.style)}>{segment.text}</span>
198
+ )
199
+ }
200
+ </For>
201
+ <Show when={props.line.truncated > 0}>
202
+ <span class="opacity-70 select-none">
203
+ {' '}
204
+ … {props.line.truncated.toLocaleString()} more characters not shown
205
+ </span>
206
+ </Show>
207
+ </div>
208
+ )
209
+ }
210
+
211
+ function sameAnsiLine(left: AnsiLine, right: AnsiLine): boolean {
212
+ return (
213
+ left.truncated === right.truncated &&
214
+ left.segments.length === right.segments.length &&
215
+ left.segments.every(
216
+ (segment, index) =>
217
+ segment.text === right.segments[index]?.text &&
218
+ segment.style === right.segments[index]?.style
219
+ )
220
+ )
221
+ }
222
+
223
+ function StatusReadout(props: { status: TerminalStatus }) {
224
+ return (
225
+ // A live region, so a command that finishes while the reader is elsewhere is
226
+ // announced once, in words.
227
+ <span
228
+ role="status"
229
+ data-slot="terminal-status"
230
+ data-state={props.status.state}
231
+ class={cn('flex shrink-0 items-center gap-1 text-xs', {
232
+ 'text-destructive': isFailedExit(props.status),
233
+ 'text-info': props.status.state === 'running',
234
+ 'text-muted-foreground': props.status.state !== 'running' && !isFailedExit(props.status),
235
+ })}
236
+ >
237
+ {/* A zero exit gets no glyph: a tick would be this component deciding the
238
+ command succeeded, and some tools exit non-zero on purpose or zero on
239
+ failure. The words carry the fact; only a non-zero code is toned. */}
240
+ <Show when={props.status.state !== 'exited' || isFailedExit(props.status)}>
241
+ <span aria-hidden="true" class="grid size-3.5 place-items-center [&_svg]:size-3.5">
242
+ <Show
243
+ when={props.status.state === 'running'}
244
+ fallback={
245
+ <Show when={props.status.state === 'cancelled'} fallback={<CircleAlert />}>
246
+ <CircleSlash />
247
+ </Show>
248
+ }
249
+ >
250
+ <LoaderCircle class="motion-safe:animate-spin" />
251
+ </Show>
252
+ </span>
253
+ </Show>
254
+ {terminalStatusLabel(props.status)}
255
+ </span>
256
+ )
257
+ }
258
+
259
+ export function Terminal(props: TerminalProps) {
260
+ const [local, rest] = splitProps(props, [
261
+ 'output',
262
+ 'command',
263
+ 'status',
264
+ 'maxLines',
265
+ 'retainLines',
266
+ 'maxHeight',
267
+ 'wrap',
268
+ 'size',
269
+ 'onClear',
270
+ 'copyLabel',
271
+ 'copiedLabel',
272
+ 'clearLabel',
273
+ 'onCopy',
274
+ 'class',
275
+ ])
276
+
277
+ const small = () => local.size === 'sm'
278
+ const pageSize = () => Math.max(1, Math.floor(local.maxLines ?? TERMINAL_MAX_LINES))
279
+ const maxHeight = () =>
280
+ typeof local.maxHeight === 'number' ? `${local.maxHeight}px` : (local.maxHeight ?? '20rem')
281
+
282
+ // The parser outlives renders: it holds the half-read escape sequence, the
283
+ // current style and the open line. A new `output` that extends the old one is
284
+ // fed only its tail; anything else (a clear, a different command) starts over.
285
+ const newParser = () => createAnsiParser({ retainLines: local.retainLines })
286
+ let parser = newParser()
287
+ let consumed = ''
288
+ // Bumped when the output restarts, so a parked position from the old output
289
+ // is never applied to the new one.
290
+ let epoch = 0
291
+ const revision = createMemo(
292
+ () => {
293
+ const next = local.output
294
+ if (next.length >= consumed.length && next.startsWith(consumed)) {
295
+ parser.write(next.slice(consumed.length))
296
+ } else {
297
+ parser = newParser()
298
+ parser.write(next)
299
+ epoch += 1
300
+ }
301
+ consumed = next
302
+ return epoch
303
+ },
304
+ undefined,
305
+ { equals: false }
306
+ )
307
+
308
+ const [limit, setLimit] = createSignal(pageSize())
309
+ createEffect(on(pageSize, (size) => setLimit(size), { defer: true }))
310
+ // A cleared terminal is a fresh one: the next command starts at the page size.
311
+ createEffect(
312
+ on(
313
+ () => local.output === '',
314
+ (empty) => {
315
+ if (empty) setLimit(pageSize())
316
+ },
317
+ { defer: true }
318
+ )
319
+ )
320
+
321
+ const outputRows = createMemo(
322
+ () => {
323
+ revision()
324
+ const rows = parser.lines.slice()
325
+ const open = parser.openLine()
326
+ if (open) rows.push(open)
327
+ return rows
328
+ },
329
+ [],
330
+ { equals: false }
331
+ )
332
+ // Where the reader parked, as a count of lines since the output began. An open
333
+ // line is a row too: freeze its snapshot so streamed characters and CR redraws
334
+ // do not rewrite the text under a parked reader.
335
+ const [parked, setParked] = createSignal<{ epoch: number; end: number; open?: AnsiLine }>()
336
+ const total = () => parser.droppedLines + outputRows().length
337
+ const anchorEnd = () => {
338
+ const current = revision()
339
+ const position = parked()
340
+ return position && position.epoch === current ? position.end : undefined
341
+ }
342
+ const lineWindow = createMemo(() => {
343
+ revision()
344
+ return terminalWindow(outputRows().length, parser.droppedLines, limit(), anchorEnd())
345
+ })
346
+ // Finished rows keep their parser identity. If an open row existed when the
347
+ // reader parked, substitute its immutable snapshot until they jump to latest.
348
+ const visibleRows = createMemo(
349
+ () => {
350
+ revision()
351
+ const start = lineWindow().start
352
+ const end = lineWindow().end
353
+ const rows = outputRows().slice(start, end)
354
+ const position = parked()
355
+ if (position?.epoch === revision() && position.open) {
356
+ const retainedIndex = position.end - 1 - parser.droppedLines
357
+ if (retainedIndex >= start && retainedIndex < end) {
358
+ rows[retainedIndex - start] = position.open
359
+ }
360
+ }
361
+ return rows
362
+ },
363
+ [],
364
+ { equals: false }
365
+ )
366
+ const changedParkedLine = () => {
367
+ const position = parked()
368
+ if (position?.epoch !== revision() || !position.open) return false
369
+ const retainedIndex = position.end - 1 - parser.droppedLines
370
+ const current = outputRows()[retainedIndex]
371
+ return current !== undefined && !sameAnsiLine(position.open, current)
372
+ }
373
+ const newerRows = () => lineWindow().later + (changedParkedLine() ? 1 : 0)
374
+
375
+ let scroller: HTMLDivElement | undefined
376
+ const following = () => anchorEnd() === undefined
377
+
378
+ const onScroll = () => {
379
+ if (!scroller) return
380
+ const atEnd = isAtBottom(scroller.scrollHeight, scroller.scrollTop, scroller.clientHeight)
381
+ const position = anchorEnd()
382
+ if (!atEnd) {
383
+ if (position === undefined) {
384
+ const currentEpoch = revision()
385
+ setParked({ epoch: currentEpoch, end: total(), open: parser.openLine() })
386
+ }
387
+ } else if (position !== undefined && position >= total() && !changedParkedLine()) {
388
+ // Back at the end of a view with nothing newer: nothing was missed.
389
+ setParked(undefined)
390
+ }
391
+ }
392
+ createEffect(
393
+ on(revision, () => {
394
+ if (scroller && following()) scroller.scrollTop = scroller.scrollHeight
395
+ })
396
+ )
397
+ const jumpToLatest = () => {
398
+ if (!scroller) return
399
+ setParked(undefined)
400
+ scroller.scrollTop = scroller.scrollHeight
401
+ // The button is about to disappear; leave focus somewhere real.
402
+ scroller.focus()
403
+ }
404
+
405
+ const [copied, setCopied] = createSignal(false)
406
+ const [copyFailed, setCopyFailed] = createSignal(false)
407
+ const copy = async () => {
408
+ let ok = true
409
+ try {
410
+ await writeClipboardText(local.output)
411
+ } catch {
412
+ ok = false
413
+ }
414
+ setCopied(ok)
415
+ setCopyFailed(!ok)
416
+ local.onCopy?.(ok)
417
+ if (ok) setTimeout(() => setCopied(false), 1500)
418
+ }
419
+
420
+ const empty = () => local.output === ''
421
+ const regionLabel = () =>
422
+ local.command ? `Output of ${local.command.slice(0, 120)}` : 'Terminal output'
423
+
424
+ return (
425
+ <div
426
+ data-slot="terminal"
427
+ data-size={local.size ?? 'default'}
428
+ aria-busy={local.status?.state === 'running' || undefined}
429
+ class={cn(
430
+ 'group/terminal overflow-hidden border border-border bg-card',
431
+ small() ? 'rounded-lg' : 'rounded-xl',
432
+ local.class
433
+ )}
434
+ {...rest}
435
+ >
436
+ <div class={cn('flex items-center gap-2', small() ? 'px-2 py-0.5' : 'px-3 py-1')}>
437
+ <Show
438
+ when={local.command !== undefined}
439
+ fallback={<span class="flex-1 text-xs text-muted-foreground">Terminal</span>}
440
+ >
441
+ <span class="flex min-w-0 flex-1 items-baseline gap-2">
442
+ <span
443
+ aria-hidden="true"
444
+ class="shrink-0 font-code text-code text-muted-foreground select-none"
445
+ >
446
+ $
447
+ </span>
448
+ <code
449
+ title={local.command}
450
+ class="line-clamp-2 min-w-0 font-code text-code text-foreground [overflow-wrap:anywhere]"
451
+ >
452
+ {local.command}
453
+ </code>
454
+ </span>
455
+ </Show>
456
+ <Show when={local.status}>{(status) => <StatusReadout status={status()} />}</Show>
457
+ {/* Hidden until the terminal is hovered or holds focus, never removed:
458
+ a control that only exists under the pointer is unreachable by
459
+ keyboard, and a touch screen has no hover, so it always shows there. */}
460
+ <div class="flex items-center gap-1 opacity-0 transition-opacity group-focus-within/terminal:opacity-100 group-hover/terminal:opacity-100 [@media(hover:none)]:opacity-100">
461
+ <Show when={local.onClear !== undefined && !empty()}>
462
+ <Button
463
+ variant="ghost"
464
+ size="icon-2xs"
465
+ aria-label={local.clearLabel ?? 'Clear output'}
466
+ onClick={() => local.onClear?.()}
467
+ >
468
+ <Eraser aria-hidden="true" />
469
+ </Button>
470
+ </Show>
471
+ <Show when={!empty()}>
472
+ <Button
473
+ variant="ghost"
474
+ size="icon-2xs"
475
+ aria-label={
476
+ copied() ? (local.copiedLabel ?? 'Copied') : (local.copyLabel ?? 'Copy output')
477
+ }
478
+ title={copyFailed() ? 'Copying is unavailable in this context' : undefined}
479
+ onClick={() => void copy()}
480
+ >
481
+ <Show when={copied()} fallback={<Copy aria-hidden="true" />}>
482
+ <Check aria-hidden="true" />
483
+ </Show>
484
+ </Button>
485
+ </Show>
486
+ </div>
487
+ </div>
488
+
489
+ <Show when={lineWindow().hidden > 0 || lineWindow().evicted > 0}>
490
+ <div
491
+ data-slot="terminal-hidden-note"
492
+ class="flex items-center gap-2 border-t border-border px-3 py-1 text-xs text-muted-foreground"
493
+ >
494
+ <span>
495
+ <Show when={lineWindow().hidden > 0}>
496
+ {lineWindow().hidden.toLocaleString()} earlier{' '}
497
+ {lineWindow().hidden === 1 ? 'line' : 'lines'} hidden
498
+ <Show when={parser.droppedLines > 0}>
499
+ {' '}
500
+ ({parser.droppedLines.toLocaleString()} only in the copy)
501
+ </Show>
502
+ .
503
+ </Show>
504
+ <Show when={lineWindow().evicted > 0}>
505
+ {' '}
506
+ {lineWindow().lost
507
+ ? `The lines you were reading were discarded to keep memory bounded; showing the oldest ${lineWindow().end.toLocaleString()} still kept.`
508
+ : `${lineWindow().evicted.toLocaleString()} of the lines you were reading were discarded to keep memory bounded.`}
509
+ </Show>
510
+ </span>
511
+ <Show when={lineWindow().revealable > 0}>
512
+ <Button
513
+ variant="ghost"
514
+ size="2xs"
515
+ onClick={() => setLimit(revealEarlier(limit(), pageSize(), outputRows().length))}
516
+ >
517
+ Show {Math.min(pageSize(), lineWindow().revealable).toLocaleString()} earlier
518
+ </Button>
519
+ </Show>
520
+ </div>
521
+ </Show>
522
+
523
+ <div class="relative">
524
+ <div
525
+ ref={(element) => (scroller = element)}
526
+ data-slot="terminal-body"
527
+ // A region that scrolls must be focusable, and a tab stop needs a name.
528
+ role="region"
529
+ tabindex="0"
530
+ aria-label={regionLabel()}
531
+ onScroll={onScroll}
532
+ style={{ 'max-height': maxHeight() }}
533
+ class="scroll-fade overflow-auto border-t border-border bg-(--terminal-background) text-(color:--terminal-foreground)"
534
+ >
535
+ <Show
536
+ when={!empty()}
537
+ fallback={
538
+ <p class={cn('m-0 opacity-70', small() ? 'px-2 py-1.5' : 'px-3 py-2')}>
539
+ <span class="font-code text-code">
540
+ {local.status?.state === 'running' ? 'Waiting for output…' : 'No output'}
541
+ </span>
542
+ </p>
543
+ }
544
+ >
545
+ <pre
546
+ class={cn(
547
+ 'm-0 font-code text-code leading-normal',
548
+ small() ? 'px-2 py-1.5' : 'px-3 py-2'
549
+ )}
550
+ >
551
+ <For each={visibleRows()}>
552
+ {(line) => <LineView line={line} wrap={local.wrap === true} />}
553
+ </For>
554
+ </pre>
555
+ </Show>
556
+ </div>
557
+ <Show when={newerRows() > 0}>
558
+ <Button
559
+ variant="secondary"
560
+ size="2xs"
561
+ class="absolute end-3 bottom-2 shadow-sm"
562
+ onClick={jumpToLatest}
563
+ >
564
+ <ArrowDown aria-hidden="true" />
565
+ Jump to latest
566
+ <span class="text-muted-foreground tabular-nums">
567
+ ({newerRows().toLocaleString()} newer {newerRows() === 1 ? 'line' : 'lines'})
568
+ </span>
569
+ </Button>
570
+ </Show>
571
+ </div>
572
+ </div>
573
+ )
574
+ }