@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,187 @@
1
+ import { OctagonAlert, TriangleAlert } from 'lucide-solid'
2
+ import { For, Show, splitProps, type ComponentProps } from 'solid-js'
3
+ import { cn } from '#lib/utils'
4
+ import { Button } from '../../ui/button'
5
+ import { Popover, PopoverContent, PopoverTitle, PopoverTrigger } from '../../ui/popover'
6
+ import { Progress } from '../../ui/progress'
7
+ import {
8
+ breakdownRows,
9
+ contextLabel,
10
+ contextLevel,
11
+ contextLevelWord,
12
+ contextPercent,
13
+ contextShort,
14
+ formatCost,
15
+ formatExact,
16
+ hasContextFigures,
17
+ isFigure,
18
+ type ContextFigures,
19
+ type ContextLevel,
20
+ } from './context-model'
21
+
22
+ /**
23
+ * Context.
24
+ *
25
+ * How much of the model's context window is in use, as a compact trigger that
26
+ * opens the figures behind it. It sits next to the composer, where the reader
27
+ * decides whether to keep going or start fresh, so the trigger is small and the
28
+ * detail is a press away rather than permanent chrome.
29
+ *
30
+ * ## Every figure is a claim, so every figure is optional
31
+ *
32
+ * A host passes whatever it knows: `used`, `total`, a `breakdown` (input, output,
33
+ * cache read and write, reasoning), a `cost`. A missing value is omitted — never
34
+ * drawn as 0, which would say "empty" about a window nobody measured — and when
35
+ * *nothing* is known the trigger is not rendered at all (`hasContextFigures`).
36
+ * Nothing is estimated here; the one derived row, "Remaining", appears only when
37
+ * both `used` and `total` are known and `used` does not exceed `total`.
38
+ *
39
+ * ## Never colour alone
40
+ *
41
+ * The ring shows the fraction; past 75% the ring gives way to a warning glyph
42
+ * (triangle, then octagon at 90% and above), the popover says "Getting full",
43
+ * "Nearly full" or "Full" in words, and the trigger's accessible name speaks the
44
+ * same. An overrun is reported as it is ("112%"); only the ring saturates.
45
+ *
46
+ * It is a `Popover`, not a hover card: the figures must be reachable by keyboard
47
+ * and by touch, and Escape closes it and returns focus to the trigger.
48
+ */
49
+ export type ContextProps = ContextFigures & {
50
+ /** BCP 47 tag for number formatting. Defaults to `en-US`, so output is stable. */
51
+ locale?: string
52
+ placement?: ComponentProps<typeof Popover>['placement']
53
+ class?: string
54
+ }
55
+
56
+ // A circle of radius 6 in a 16-unit box; the dash array is the fraction of this.
57
+ const RING_RADIUS = 6
58
+ const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS
59
+
60
+ export function Context(props: ContextProps) {
61
+ const [local, figures] = splitProps(props, ['locale', 'placement', 'class'])
62
+ const locale = () => local.locale ?? 'en-US'
63
+ const percent = () => contextPercent(figures.used, figures.total)
64
+ const level = (): ContextLevel => contextLevel(percent())
65
+ const levelWord = () => contextLevelWord(level())
66
+ const rows = () => breakdownRows(figures.breakdown)
67
+ const remaining = () =>
68
+ isFigure(figures.used) && isFigure(figures.total) && figures.used <= figures.total
69
+ ? figures.total - figures.used
70
+ : undefined
71
+ const cost = () => (figures.cost && isFigure(figures.cost.amount) ? figures.cost : undefined)
72
+
73
+ return (
74
+ <Show when={hasContextFigures(figures)}>
75
+ <Popover placement={local.placement ?? 'top'} gutter={6}>
76
+ <PopoverTrigger
77
+ as={Button}
78
+ variant="ghost"
79
+ size="xs"
80
+ data-slot="context"
81
+ data-level={level()}
82
+ aria-label={contextLabel(figures, locale())}
83
+ class={local.class}
84
+ >
85
+ <Show
86
+ when={level() !== 'normal'}
87
+ fallback={
88
+ <Show when={percent() !== undefined}>
89
+ <Ring percent={percent() ?? 0} />
90
+ </Show>
91
+ }
92
+ >
93
+ <span
94
+ aria-hidden="true"
95
+ class={cn({
96
+ 'text-warning': level() === 'high',
97
+ 'text-destructive': level() === 'critical' || level() === 'full',
98
+ })}
99
+ >
100
+ <Show when={level() === 'high'} fallback={<OctagonAlert />}>
101
+ <TriangleAlert />
102
+ </Show>
103
+ </span>
104
+ </Show>
105
+ <span aria-hidden="true" class="tabular-nums">
106
+ {contextShort(figures, locale())}
107
+ </span>
108
+ </PopoverTrigger>
109
+ <PopoverContent class="w-72 max-w-full">
110
+ <PopoverTitle>Context window</PopoverTitle>
111
+ <div class="flex flex-col gap-2 px-2 pb-2">
112
+ <Show when={percent() !== undefined}>
113
+ <Progress
114
+ value={Math.min(percent() ?? 0, 100)}
115
+ minValue={0}
116
+ maxValue={100}
117
+ label="Context used"
118
+ getValueLabel={() => `${percent()}% used`}
119
+ />
120
+ </Show>
121
+ <Show when={levelWord()}>
122
+ <p
123
+ class={cn('m-0 flex items-center gap-1.5 text-sm font-medium', {
124
+ 'text-warning': level() === 'high',
125
+ 'text-destructive': level() === 'critical' || level() === 'full',
126
+ })}
127
+ >
128
+ <Show when={level() === 'high'} fallback={<OctagonAlert class="size-4" />}>
129
+ <TriangleAlert class="size-4" />
130
+ </Show>
131
+ <span class="text-foreground">{levelWord()}</span>
132
+ </p>
133
+ </Show>
134
+ <dl class="m-0 flex flex-col gap-1 text-sm">
135
+ <Show when={isFigure(figures.used)}>
136
+ <Row label="Used" value={`${formatExact(figures.used ?? 0, locale())} tokens`} />
137
+ </Show>
138
+ <Show when={isFigure(figures.total)}>
139
+ <Row label="Window" value={`${formatExact(figures.total ?? 0, locale())} tokens`} />
140
+ </Show>
141
+ <Show when={remaining() !== undefined}>
142
+ <Row
143
+ label="Remaining"
144
+ value={`${formatExact(remaining() ?? 0, locale())} tokens`}
145
+ />
146
+ </Show>
147
+ <For each={rows()}>
148
+ {(row) => <Row label={row.label} value={formatExact(row.value, locale())} />}
149
+ </For>
150
+ <Show when={cost()}>
151
+ {(value) => <Row label="Cost" value={formatCost(value(), locale())} />}
152
+ </Show>
153
+ </dl>
154
+ </div>
155
+ </PopoverContent>
156
+ </Popover>
157
+ </Show>
158
+ )
159
+ }
160
+
161
+ function Ring(props: { percent: number }) {
162
+ const filled = () => (RING_CIRCUMFERENCE * Math.min(Math.max(props.percent, 0), 100)) / 100
163
+ return (
164
+ <svg aria-hidden="true" viewBox="0 0 16 16" fill="none" class="size-4">
165
+ <circle cx="8" cy="8" r={RING_RADIUS} stroke-width="2" class="stroke-border" />
166
+ <circle
167
+ cx="8"
168
+ cy="8"
169
+ r={RING_RADIUS}
170
+ stroke-width="2"
171
+ stroke-linecap="round"
172
+ stroke-dasharray={`${filled()} ${RING_CIRCUMFERENCE}`}
173
+ transform="rotate(-90 8 8)"
174
+ class="stroke-primary"
175
+ />
176
+ </svg>
177
+ )
178
+ }
179
+
180
+ function Row(props: { label: string; value: string }) {
181
+ return (
182
+ <div class="flex items-baseline justify-between gap-4">
183
+ <dt class="text-muted-foreground">{props.label}</dt>
184
+ <dd class="m-0 text-end tabular-nums">{props.value}</dd>
185
+ </div>
186
+ )
187
+ }
@@ -0,0 +1,18 @@
1
+ export { Context, type ContextProps } from './context'
2
+ export {
3
+ breakdownRows,
4
+ contextLabel,
5
+ contextLevel,
6
+ contextLevelWord,
7
+ contextPercent,
8
+ contextShort,
9
+ formatCost,
10
+ formatExact,
11
+ formatTokens,
12
+ hasContextFigures,
13
+ isFigure,
14
+ type ContextBreakdown,
15
+ type ContextCost,
16
+ type ContextFigures,
17
+ type ContextLevel,
18
+ } from './context-model'
@@ -0,0 +1,456 @@
1
+ /**
2
+ * The model behind the changed-files tree: paths in, a folder tree out, and the
3
+ * small pure rules the view needs (what is visible, what to say about the
4
+ * changes, what a typed letter jumps to).
5
+ *
6
+ * Paths come from an agent, which means from a model, which means they are
7
+ * untrusted text. They are used here as labels and as ids, never as places to
8
+ * read or write, and the builder is strict about what counts as a path at all:
9
+ * a `..` segment, a control character or a bidirectional override rejects the
10
+ * path rather than being cleaned up, because a cleaned-up path is a different
11
+ * path and the tree would be making a claim the input did not. Rejected paths
12
+ * are returned, not dropped silently, so the view can say how many it ignored.
13
+ *
14
+ * Everything is iterative. A path with ten thousand segments, or a tree five
15
+ * thousand files wide, must not be able to overflow the stack or stall the page.
16
+ */
17
+
18
+ export type FileChangeKind = 'added' | 'modified' | 'deleted' | 'renamed'
19
+
20
+ export type FileChangeInput = Readonly<{
21
+ path: string
22
+ /** Absent means the host did not say; the tree makes no statement about the file. */
23
+ change?: FileChangeKind
24
+ /** For a rename, where the file was. Shown as text only. */
25
+ renamedFrom?: string
26
+ additions?: number
27
+ deletions?: number
28
+ }>
29
+
30
+ export type FileTreeNode = Readonly<{
31
+ /** The normalised path, `/`-separated. Unique within a tree. */
32
+ id: string
33
+ /** The label. For a compacted folder chain this is `a/b/c`. */
34
+ name: string
35
+ kind: 'file' | 'directory'
36
+ change?: FileChangeKind
37
+ renamedFrom?: string
38
+ additions?: number
39
+ deletions?: number
40
+ children: readonly FileTreeNode[]
41
+ /** Files at or below this node. A file counts itself. */
42
+ fileCount: number
43
+ }>
44
+
45
+ export type FileTreeBuild = Readonly<{
46
+ roots: readonly FileTreeNode[]
47
+ /** Inputs that were not accepted as paths, exactly as given. */
48
+ rejected: readonly string[]
49
+ /** Inputs that named a path already present. The first one wins. */
50
+ duplicates: number
51
+ fileCount: number
52
+ }>
53
+
54
+ export type BuildFileTreeOptions = {
55
+ /** Merge a folder with a single folder child into one row (`src/lib`). Default true. */
56
+ compact?: boolean
57
+ }
58
+
59
+ /** Deeper paths are rejected: no real repository nests this far, and a row's indent would be unusable. */
60
+ export const FILE_TREE_MAX_DEPTH = 256
61
+ const MAX_SEGMENT_LENGTH = 1024
62
+
63
+ function hasForbiddenCharacter(value: string): boolean {
64
+ for (let i = 0; i < value.length; i += 1) {
65
+ const code = value.charCodeAt(i)
66
+ if (
67
+ code < 0x20 ||
68
+ (code >= 0x7f && code <= 0x9f) ||
69
+ (code >= 0x202a && code <= 0x202e) ||
70
+ (code >= 0x2066 && code <= 0x2069) ||
71
+ code === 0x2028 ||
72
+ code === 0x2029
73
+ ) {
74
+ return true
75
+ }
76
+ }
77
+ return false
78
+ }
79
+
80
+ /**
81
+ * A path as its segments, or `null` when it is not acceptable. Both separators
82
+ * are accepted, empty and `.` segments are dropped (`a//b`, `./a`, trailing `/`),
83
+ * and a leading separator is dropped, so an absolute path becomes relative to
84
+ * the tree's root rather than escaping it.
85
+ */
86
+ export function normalizePath(path: unknown): string[] | null {
87
+ if (typeof path !== 'string' || hasForbiddenCharacter(path)) return null
88
+ const segments: string[] = []
89
+ let start = 0
90
+ for (let i = 0; i <= path.length; i += 1) {
91
+ if (i !== path.length && path[i] !== '/' && path[i] !== '\\') continue
92
+ const segment = path.slice(start, i)
93
+ start = i + 1
94
+ if (segment === '' || segment === '.') continue
95
+ if (segment === '..' || segment.length > MAX_SEGMENT_LENGTH) return null
96
+ segments.push(segment)
97
+ if (segments.length > FILE_TREE_MAX_DEPTH) return null
98
+ }
99
+ return segments.length === 0 ? null : segments
100
+ }
101
+
102
+ type Draft = {
103
+ id: string
104
+ name: string
105
+ children: Map<string, Draft>
106
+ change?: FileChangeKind
107
+ renamedFrom?: string
108
+ additions?: number
109
+ deletions?: number
110
+ explicit: boolean
111
+ }
112
+
113
+ function newDraft(id: string, name: string): Draft {
114
+ return { id, name, children: new Map(), explicit: false }
115
+ }
116
+
117
+ function compareNodes(a: FileTreeNode, b: FileTreeNode): number {
118
+ if (a.kind !== b.kind) return a.kind === 'directory' ? -1 : 1
119
+ const left = a.name.toLowerCase()
120
+ const right = b.name.toLowerCase()
121
+ if (left !== right) return left < right ? -1 : 1
122
+ return a.name < b.name ? -1 : a.name > b.name ? 1 : 0
123
+ }
124
+
125
+ function safeCount(value: unknown): number | undefined {
126
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0
127
+ ? Math.floor(value)
128
+ : undefined
129
+ }
130
+
131
+ const CHANGE_KINDS: ReadonlySet<string> = new Set(['added', 'modified', 'deleted', 'renamed'])
132
+
133
+ /**
134
+ * Build the tree. Folders sort before files, then by name without regard to
135
+ * case. A path that is both a file and the parent of another path is a folder
136
+ * that keeps the file entry's change (a file replaced by a directory is a real
137
+ * thing to report).
138
+ */
139
+ export function buildFileTree(
140
+ paths: readonly (string | FileChangeInput)[],
141
+ options: BuildFileTreeOptions = {}
142
+ ): FileTreeBuild {
143
+ const root = newDraft('', '')
144
+ const rejected: string[] = []
145
+ let duplicates = 0
146
+
147
+ for (const input of paths) {
148
+ const entry: FileChangeInput | undefined =
149
+ typeof input === 'string'
150
+ ? { path: input }
151
+ : input && typeof input === 'object'
152
+ ? input
153
+ : undefined
154
+ const segments = normalizePath(entry?.path)
155
+ if (!entry || !segments) {
156
+ rejected.push(
157
+ typeof input === 'string' ? input : String((input as { path?: unknown })?.path ?? '')
158
+ )
159
+ continue
160
+ }
161
+
162
+ let node = root
163
+ let id = ''
164
+ for (const segment of segments) {
165
+ id = id === '' ? segment : `${id}/${segment}`
166
+ let child = node.children.get(segment)
167
+ if (!child) {
168
+ child = newDraft(id, segment)
169
+ node.children.set(segment, child)
170
+ }
171
+ node = child
172
+ }
173
+ if (node.explicit) {
174
+ duplicates += 1
175
+ continue
176
+ }
177
+ node.explicit = true
178
+ if (entry.change && CHANGE_KINDS.has(entry.change)) node.change = entry.change
179
+ if (typeof entry.renamedFrom === 'string' && !hasForbiddenCharacter(entry.renamedFrom)) {
180
+ node.renamedFrom = entry.renamedFrom
181
+ }
182
+ node.additions = safeCount(entry.additions)
183
+ node.deletions = safeCount(entry.deletions)
184
+ }
185
+
186
+ // Pre-order flatten, then fold back in reverse: a child always has a larger
187
+ // index than its parent, so one backwards pass builds every node after its
188
+ // children without recursion.
189
+ const order: Draft[] = []
190
+ const parentOf: number[] = []
191
+ const stack: [Draft, number][] = [...root.children.values()].map((draft) => [draft, -1])
192
+ while (stack.length > 0) {
193
+ const [draft, parent] = stack.pop()!
194
+ const index = order.length
195
+ order.push(draft)
196
+ parentOf.push(parent)
197
+ for (const child of draft.children.values()) stack.push([child, index])
198
+ }
199
+
200
+ const built: FileTreeNode[][] = order.map(() => [])
201
+ const roots: FileTreeNode[] = []
202
+ const compact = options.compact !== false
203
+ let fileCount = 0
204
+
205
+ for (let index = order.length - 1; index >= 0; index -= 1) {
206
+ const draft = order[index]!
207
+ const children = built[index]!.toSorted(compareNodes)
208
+ let node: FileTreeNode
209
+ const only = children.length === 1 ? children[0]! : undefined
210
+ if (compact && only?.kind === 'directory' && draft.change === undefined) {
211
+ // A folder holding only another folder reads as one row: `src/lib`, not two.
212
+ node = { ...only, name: `${draft.name}/${only.name}` }
213
+ } else if (children.length > 0) {
214
+ node = {
215
+ id: draft.id,
216
+ name: draft.name,
217
+ kind: 'directory',
218
+ ...(draft.change && { change: draft.change }),
219
+ ...(draft.renamedFrom !== undefined && { renamedFrom: draft.renamedFrom }),
220
+ children,
221
+ fileCount: children.reduce((sum, child) => sum + child.fileCount, 0),
222
+ }
223
+ } else {
224
+ fileCount += 1
225
+ node = {
226
+ id: draft.id,
227
+ name: draft.name,
228
+ kind: 'file',
229
+ ...(draft.change && { change: draft.change }),
230
+ ...(draft.renamedFrom !== undefined && { renamedFrom: draft.renamedFrom }),
231
+ ...(draft.additions !== undefined && { additions: draft.additions }),
232
+ ...(draft.deletions !== undefined && { deletions: draft.deletions }),
233
+ children: [],
234
+ fileCount: 1,
235
+ }
236
+ }
237
+ const parent = parentOf[index]!
238
+ if (parent === -1) roots.push(node)
239
+ else built[parent]!.push(node)
240
+ }
241
+
242
+ roots.sort(compareNodes)
243
+ return { roots, rejected, duplicates, fileCount }
244
+ }
245
+
246
+ // ---------------------------------------------------------------------------
247
+ // Projection
248
+ // ---------------------------------------------------------------------------
249
+
250
+ export type FileTreeRow = Readonly<{
251
+ node: FileTreeNode
252
+ /** One-based depth. */
253
+ level: number
254
+ parentId: string | null
255
+ expanded: boolean
256
+ }>
257
+
258
+ /**
259
+ * The rows that are on screen, in reading order. A collapsed folder's subtree
260
+ * is never visited, so toggling a folder in a 5 000-file tree costs the size of
261
+ * what is visible, not of what exists.
262
+ */
263
+ export function flattenFileTree(
264
+ roots: readonly FileTreeNode[],
265
+ expanded: ReadonlySet<string>
266
+ ): FileTreeRow[] {
267
+ const rows: FileTreeRow[] = []
268
+ const stack: [FileTreeNode, number, string | null][] = []
269
+ for (let i = roots.length - 1; i >= 0; i -= 1) stack.push([roots[i]!, 1, null])
270
+ while (stack.length > 0) {
271
+ const [node, level, parentId] = stack.pop()!
272
+ const open = node.kind === 'directory' && expanded.has(node.id)
273
+ rows.push({ node, level, parentId, expanded: open })
274
+ if (open) {
275
+ for (let i = node.children.length - 1; i >= 0; i -= 1) {
276
+ stack.push([node.children[i]!, level + 1, node.id])
277
+ }
278
+ }
279
+ }
280
+ return rows
281
+ }
282
+
283
+ /** Every node by id, for turning a row id back into its node. */
284
+ export function indexFileTree(roots: readonly FileTreeNode[]): Map<string, FileTreeNode> {
285
+ const index = new Map<string, FileTreeNode>()
286
+ const stack = [...roots]
287
+ while (stack.length > 0) {
288
+ const node = stack.pop()!
289
+ index.set(node.id, node)
290
+ for (const child of node.children) stack.push(child)
291
+ }
292
+ return index
293
+ }
294
+
295
+ export type FileTreeExpansion = boolean | number | readonly string[] | undefined
296
+
297
+ /** Tree this small opens fully by default; a larger one opens only its top level. */
298
+ export const FILE_TREE_AUTO_EXPAND_LIMIT = 100
299
+
300
+ /**
301
+ * Which folders start open. `true` opens every folder, a number opens folders up
302
+ * to that depth (1 is the top level), a list opens exactly those ids, `false`
303
+ * none. Unset is "auto": everything for a small tree, the top level for a large
304
+ * one, because a tree that opens 5 000 rows is the freeze this exists to avoid.
305
+ */
306
+ export function initialExpanded(
307
+ roots: readonly FileTreeNode[],
308
+ spec: FileTreeExpansion,
309
+ fileCount: number
310
+ ): Set<string> {
311
+ const open = new Set<string>()
312
+ if (Array.isArray(spec)) {
313
+ for (const id of spec) open.add(id)
314
+ return open
315
+ }
316
+ const depth =
317
+ spec === true
318
+ ? Infinity
319
+ : spec === false
320
+ ? 0
321
+ : typeof spec === 'number'
322
+ ? Math.max(0, Math.floor(spec))
323
+ : fileCount <= FILE_TREE_AUTO_EXPAND_LIMIT
324
+ ? Infinity
325
+ : 1
326
+ const stack: [FileTreeNode, number][] = roots.map((node) => [node, 1])
327
+ while (stack.length > 0) {
328
+ const [node, level] = stack.pop()!
329
+ if (node.kind !== 'directory' || level > depth) continue
330
+ open.add(node.id)
331
+ for (const child of node.children) stack.push([child, level + 1])
332
+ }
333
+ return open
334
+ }
335
+
336
+ // ---------------------------------------------------------------------------
337
+ // Mounted window
338
+ // ---------------------------------------------------------------------------
339
+
340
+ /** The slice of visible rows that is mounted: `start` inclusive, `end` exclusive. */
341
+ export type RowWindow = Readonly<{ start: number; end: number }>
342
+
343
+ /**
344
+ * A window clamped to what exists, and never narrower than one page when that
345
+ * many rows are there. Rows collapse and files are removed while a window is
346
+ * held, so every read goes through this.
347
+ */
348
+ export function clampWindow(window: RowWindow, total: number, page: number): RowWindow {
349
+ if (total <= 0) return { start: 0, end: 0 }
350
+ const start = Math.min(Math.max(0, window.start), total - 1)
351
+ return { start, end: Math.min(total, Math.max(window.end, start + Math.max(1, page))) }
352
+ }
353
+
354
+ /**
355
+ * The window after the keyboard (or typeahead) asks for a row. A row inside the
356
+ * window changes nothing. A row just outside it — an arrow key at the edge — grows
357
+ * the window by one page, so walking the tree costs a page at a time. Anything
358
+ * further is a jump (End, Home, a typed letter): the window moves to the page that
359
+ * holds the row instead of mounting every row between, which is what keeps End on
360
+ * a 5 000-row tree from being a multi-second freeze.
361
+ */
362
+ export function revealInWindow(
363
+ window: RowWindow,
364
+ index: number,
365
+ total: number,
366
+ page: number
367
+ ): RowWindow {
368
+ const current = clampWindow(window, total, page)
369
+ if (index < 0 || index >= total) return current
370
+ if (index >= current.start && index < current.end) return current
371
+ if (index === current.start - 1) {
372
+ return { start: Math.max(0, current.start - page), end: current.end }
373
+ }
374
+ if (index === current.end)
375
+ return { start: current.start, end: Math.min(total, current.end + page) }
376
+ const start = Math.floor(index / page) * page
377
+ return { start, end: Math.min(total, start + page) }
378
+ }
379
+
380
+ // ---------------------------------------------------------------------------
381
+ // Words
382
+ // ---------------------------------------------------------------------------
383
+
384
+ export type ChangeCounts = Readonly<{
385
+ total: number
386
+ added: number
387
+ modified: number
388
+ deleted: number
389
+ renamed: number
390
+ /** Files with no change kind supplied. */
391
+ unspecified: number
392
+ }>
393
+
394
+ export function countChanges(roots: readonly FileTreeNode[]): ChangeCounts {
395
+ const counts = { total: 0, added: 0, modified: 0, deleted: 0, renamed: 0, unspecified: 0 }
396
+ const stack = [...roots]
397
+ while (stack.length > 0) {
398
+ const node = stack.pop()!
399
+ for (const child of node.children) stack.push(child)
400
+ if (node.kind !== 'file') continue
401
+ counts.total += 1
402
+ if (node.change) counts[node.change] += 1
403
+ else counts.unspecified += 1
404
+ }
405
+ return counts
406
+ }
407
+
408
+ /**
409
+ * The summary in words: "12 files changed: 3 added, 8 modified, 1 deleted". Only
410
+ * kinds that occur are named, and files with no kind are not folded into any of
411
+ * them. Empty is empty, not "0 files changed".
412
+ */
413
+ export function describeChanges(counts: ChangeCounts): string {
414
+ if (counts.total === 0) return 'No changed files'
415
+ const parts: string[] = []
416
+ for (const kind of ['added', 'modified', 'deleted', 'renamed'] as const) {
417
+ if (counts[kind] > 0) parts.push(`${counts[kind].toLocaleString()} ${kind}`)
418
+ }
419
+ const noun = counts.total === 1 ? 'file' : 'files'
420
+ const head = `${counts.total.toLocaleString()} ${noun} changed`
421
+ return parts.length > 0 ? `${head}: ${parts.join(', ')}` : head
422
+ }
423
+
424
+ /** The words for a change, which are drawn on the row and so also spoken. */
425
+ export const FILE_CHANGE_LABELS: Readonly<Record<FileChangeKind, string>> = {
426
+ added: 'Added',
427
+ modified: 'Modified',
428
+ deleted: 'Deleted',
429
+ renamed: 'Renamed',
430
+ }
431
+
432
+ // ---------------------------------------------------------------------------
433
+ // Typeahead
434
+ // ---------------------------------------------------------------------------
435
+
436
+ /**
437
+ * The row a typed prefix jumps to. One typed letter searches from the row after
438
+ * the active one, so pressing it again cycles; a longer string searches from the
439
+ * active row, so refining a match does not skip past it. Matching ignores case.
440
+ */
441
+ export function typeaheadIndex(
442
+ labels: readonly string[],
443
+ activeIndex: number,
444
+ query: string
445
+ ): number {
446
+ const needle = query.toLowerCase()
447
+ if (needle === '' || labels.length === 0) return -1
448
+ const repeated = needle.length > 1 && [...needle].every((char) => char === needle[0])
449
+ const prefix = repeated ? needle[0]! : needle
450
+ const first = needle.length === 1 || repeated ? activeIndex + 1 : Math.max(0, activeIndex)
451
+ for (let offset = 0; offset < labels.length; offset += 1) {
452
+ const index = (first + offset) % labels.length
453
+ if (labels[index]!.toLowerCase().startsWith(prefix)) return index
454
+ }
455
+ return -1
456
+ }