@visulima/tui-kit 0.0.0 → 1.0.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 (371) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE.md +2095 -0
  3. package/README.md +115 -29
  4. package/dist/accordion.d.ts +59 -0
  5. package/dist/accordion.js +1 -0
  6. package/dist/alert.d.ts +26 -0
  7. package/dist/alert.js +1 -0
  8. package/dist/animate-presence.d.ts +18 -0
  9. package/dist/animate-presence.js +1 -0
  10. package/dist/approval-prompt.d.ts +65 -0
  11. package/dist/approval-prompt.js +1 -0
  12. package/dist/area-chart.d.ts +66 -0
  13. package/dist/area-chart.js +1 -0
  14. package/dist/badge.d.ts +22 -0
  15. package/dist/badge.js +1 -0
  16. package/dist/bar-chart.d.ts +67 -0
  17. package/dist/bar-chart.js +1 -0
  18. package/dist/big-text.d.ts +81 -0
  19. package/dist/big-text.js +1 -0
  20. package/dist/blink-dot.d.ts +36 -0
  21. package/dist/blink-dot.js +1 -0
  22. package/dist/breadcrumb.d.ts +33 -0
  23. package/dist/breadcrumb.js +1 -0
  24. package/dist/button.d.ts +45 -0
  25. package/dist/button.js +1 -0
  26. package/dist/calendar.d.ts +72 -0
  27. package/dist/calendar.js +1 -0
  28. package/dist/card.d.ts +57 -0
  29. package/dist/card.js +1 -0
  30. package/dist/checkbox-group.d.ts +59 -0
  31. package/dist/checkbox-group.js +1 -0
  32. package/dist/checkbox.d.ts +48 -0
  33. package/dist/checkbox.js +1 -0
  34. package/dist/code.d.ts +48 -0
  35. package/dist/code.js +2 -0
  36. package/dist/collapsible.d.ts +53 -0
  37. package/dist/collapsible.js +1 -0
  38. package/dist/color-picker.d.ts +71 -0
  39. package/dist/color-picker.js +1 -0
  40. package/dist/command-block.d.ts +47 -0
  41. package/dist/command-block.js +4 -0
  42. package/dist/command-palette.d.ts +82 -0
  43. package/dist/command-palette.js +1 -0
  44. package/dist/confirm-dialog.d.ts +75 -0
  45. package/dist/confirm-dialog.js +1 -0
  46. package/dist/confirm-input.d.ts +38 -0
  47. package/dist/confirm-input.js +1 -0
  48. package/dist/console-overlay.d.ts +54 -0
  49. package/dist/console-overlay.js +1 -0
  50. package/dist/content-switcher.d.ts +57 -0
  51. package/dist/content-switcher.js +1 -0
  52. package/dist/controlled-scroll-view.d.ts +3 -0
  53. package/dist/controlled-scroll-view.js +1 -0
  54. package/dist/data-grid.d.ts +49 -0
  55. package/dist/data-grid.js +1 -0
  56. package/dist/date-picker.d.ts +65 -0
  57. package/dist/date-picker.js +1 -0
  58. package/dist/definition-list.d.ts +44 -0
  59. package/dist/definition-list.js +1 -0
  60. package/dist/dialog.d.ts +110 -0
  61. package/dist/dialog.js +1 -0
  62. package/dist/diff-view.d.ts +85 -0
  63. package/dist/diff-view.js +1 -0
  64. package/dist/digits.d.ts +30 -0
  65. package/dist/digits.js +1 -0
  66. package/dist/directory-tree.d.ts +51 -0
  67. package/dist/directory-tree.js +1 -0
  68. package/dist/divider.d.ts +49 -0
  69. package/dist/divider.js +1 -0
  70. package/dist/drawer.d.ts +53 -0
  71. package/dist/drawer.js +1 -0
  72. package/dist/file-change.d.ts +40 -0
  73. package/dist/file-change.js +1 -0
  74. package/dist/file-picker-entry.d.ts +2 -0
  75. package/dist/file-picker-entry.js +1 -0
  76. package/dist/form.d.ts +81 -0
  77. package/dist/form.js +1 -0
  78. package/dist/gauge.d.ts +87 -0
  79. package/dist/gauge.js +1 -0
  80. package/dist/gradient.d.ts +42 -0
  81. package/dist/gradient.js +1 -0
  82. package/dist/grid.d.ts +40 -0
  83. package/dist/grid.js +1 -0
  84. package/dist/heading.d.ts +33 -0
  85. package/dist/heading.js +1 -0
  86. package/dist/heatmap.d.ts +62 -0
  87. package/dist/heatmap.js +1 -0
  88. package/dist/help.d.ts +52 -0
  89. package/dist/help.js +1 -0
  90. package/dist/histogram.d.ts +62 -0
  91. package/dist/histogram.js +1 -0
  92. package/dist/image.d.ts +26 -0
  93. package/dist/image.js +1 -0
  94. package/dist/index.d.ts +125 -0
  95. package/dist/index.js +1 -0
  96. package/dist/json.d.ts +58 -0
  97. package/dist/json.js +1 -0
  98. package/dist/kbd.d.ts +31 -0
  99. package/dist/kbd.js +1 -0
  100. package/dist/line-chart.d.ts +109 -0
  101. package/dist/line-chart.js +1 -0
  102. package/dist/link.d.ts +38 -0
  103. package/dist/link.js +1 -0
  104. package/dist/loading-indicator.d.ts +29 -0
  105. package/dist/loading-indicator.js +1 -0
  106. package/dist/log.d.ts +38 -0
  107. package/dist/log.js +1 -0
  108. package/dist/markdown.d.ts +40 -0
  109. package/dist/markdown.js +1 -0
  110. package/dist/masked-input.d.ts +50 -0
  111. package/dist/masked-input.js +1 -0
  112. package/dist/menu.d.ts +78 -0
  113. package/dist/menu.js +1 -0
  114. package/dist/message-bubble.d.ts +43 -0
  115. package/dist/message-bubble.js +1 -0
  116. package/dist/model-badge.d.ts +39 -0
  117. package/dist/model-badge.js +1 -0
  118. package/dist/model-selector.d.ts +52 -0
  119. package/dist/model-selector.js +1 -0
  120. package/dist/multi-progress.d.ts +55 -0
  121. package/dist/multi-progress.js +1 -0
  122. package/dist/multi-select.d.ts +65 -0
  123. package/dist/multi-select.js +1 -0
  124. package/dist/notification-badge.d.ts +47 -0
  125. package/dist/notification-badge.js +1 -0
  126. package/dist/number-input.d.ts +85 -0
  127. package/dist/number-input.js +1 -0
  128. package/dist/operation-tree.d.ts +49 -0
  129. package/dist/operation-tree.js +1 -0
  130. package/dist/option-list.d.ts +47 -0
  131. package/dist/option-list.js +1 -0
  132. package/dist/ordered-list.d.ts +24 -0
  133. package/dist/ordered-list.js +1 -0
  134. package/dist/packem_shared/ControlledScrollView-BzlbIUph.js +1 -0
  135. package/dist/packem_shared/FilePicker-V5H2fb-v.js +1 -0
  136. package/dist/packem_shared/ScrollBar-C9i3z3Ft.js +1 -0
  137. package/dist/packem_shared/ScrollBarBox-DZEC8YnV.js +1 -0
  138. package/dist/packem_shared/ScrollList-Djtb2vVy.js +1 -0
  139. package/dist/packem_shared/ScrollView-ESxpl6Hy.js +1 -0
  140. package/dist/packem_shared/TreeNodeMap-cJlYqckB.js +1 -0
  141. package/dist/packem_shared/TreeView-q0l446NV.js +1 -0
  142. package/dist/packem_shared/controlled-scroll-view-DerVKKv6.js +1 -0
  143. package/dist/packem_shared/controlled-scroll-view.d-vF4wevEJ.d.ts +54 -0
  144. package/dist/packem_shared/file-picker.d-Cie6aAMD.d.ts +104 -0
  145. package/dist/packem_shared/line-chart-BbhMhcZg.js +1 -0
  146. package/dist/packem_shared/scroll-bar-box.d-DSBbBsRc.d.ts +30 -0
  147. package/dist/packem_shared/scroll-bar.d-B4sD1hBR.d.ts +18 -0
  148. package/dist/packem_shared/scroll-list.d-BP0UZSY4.d.ts +12 -0
  149. package/dist/packem_shared/scroll-view.d-BqKpI-F-.d.ts +119 -0
  150. package/dist/packem_shared/treeViewTheme-Dq3ekRAC.js +1 -0
  151. package/dist/packem_shared/use-tree-view.d-dXTKoDPM.d.ts +350 -0
  152. package/dist/packem_shared/useTreeView-D0JEqT6W.js +1 -0
  153. package/dist/packem_shared/useTreeViewState-BiTJakkh.js +1 -0
  154. package/dist/packem_shared/variant-config-BJqpNo7H.js +1 -0
  155. package/dist/packem_shared/variant-config.d-D5NnrrQK.d.ts +2 -0
  156. package/dist/paginator.d.ts +69 -0
  157. package/dist/paginator.js +1 -0
  158. package/dist/paragraph.d.ts +38 -0
  159. package/dist/paragraph.js +1 -0
  160. package/dist/path-input.d.ts +67 -0
  161. package/dist/path-input.js +1 -0
  162. package/dist/pie-chart.d.ts +50 -0
  163. package/dist/pie-chart.js +1 -0
  164. package/dist/placeholder.d.ts +55 -0
  165. package/dist/placeholder.js +1 -0
  166. package/dist/popover.d.ts +46 -0
  167. package/dist/popover.js +1 -0
  168. package/dist/progress-bar.d.ts +51 -0
  169. package/dist/progress-bar.js +1 -0
  170. package/dist/progress-circle.d.ts +59 -0
  171. package/dist/progress-circle.js +1 -0
  172. package/dist/qr-code.d.ts +45 -0
  173. package/dist/qr-code.js +1 -0
  174. package/dist/r/accordion.json +24 -0
  175. package/dist/r/alert.json +24 -0
  176. package/dist/r/animate-presence.json +22 -0
  177. package/dist/r/approval-prompt.json +24 -0
  178. package/dist/r/area-chart.json +27 -0
  179. package/dist/r/badge.json +24 -0
  180. package/dist/r/bar-chart.json +24 -0
  181. package/dist/r/big-text.json +23 -0
  182. package/dist/r/blink-dot.json +24 -0
  183. package/dist/r/breadcrumb.json +24 -0
  184. package/dist/r/button.json +24 -0
  185. package/dist/r/calendar.json +24 -0
  186. package/dist/r/card.json +26 -0
  187. package/dist/r/chart-utils.json +24 -0
  188. package/dist/r/checkbox-group.json +24 -0
  189. package/dist/r/checkbox.json +24 -0
  190. package/dist/r/code.json +23 -0
  191. package/dist/r/collapsible.json +24 -0
  192. package/dist/r/color-picker.json +24 -0
  193. package/dist/r/command-block.json +26 -0
  194. package/dist/r/command-palette.json +24 -0
  195. package/dist/r/confirm-dialog.json +24 -0
  196. package/dist/r/confirm-input.json +22 -0
  197. package/dist/r/console-overlay.json +22 -0
  198. package/dist/r/content-switcher.json +24 -0
  199. package/dist/r/controlled-scroll-view.json +24 -0
  200. package/dist/r/data-grid.json +24 -0
  201. package/dist/r/date-picker.json +26 -0
  202. package/dist/r/definition-list.json +24 -0
  203. package/dist/r/dialog.json +24 -0
  204. package/dist/r/diff-view.json +24 -0
  205. package/dist/r/digits.json +24 -0
  206. package/dist/r/directory-tree.json +24 -0
  207. package/dist/r/divider.json +24 -0
  208. package/dist/r/drawer.json +24 -0
  209. package/dist/r/file-change.json +24 -0
  210. package/dist/r/file-picker.json +44 -0
  211. package/dist/r/form.json +24 -0
  212. package/dist/r/gauge.json +24 -0
  213. package/dist/r/gradient.json +24 -0
  214. package/dist/r/grid.json +22 -0
  215. package/dist/r/heading.json +24 -0
  216. package/dist/r/heatmap.json +24 -0
  217. package/dist/r/help.json +22 -0
  218. package/dist/r/histogram.json +26 -0
  219. package/dist/r/image.json +24 -0
  220. package/dist/r/json.json +24 -0
  221. package/dist/r/kbd.json +24 -0
  222. package/dist/r/line-chart.json +26 -0
  223. package/dist/r/link.json +23 -0
  224. package/dist/r/loading-indicator.json +27 -0
  225. package/dist/r/log.json +24 -0
  226. package/dist/r/markdown.json +29 -0
  227. package/dist/r/masked-input.json +22 -0
  228. package/dist/r/menu.json +24 -0
  229. package/dist/r/message-bubble.json +24 -0
  230. package/dist/r/model-badge.json +24 -0
  231. package/dist/r/model-selector.json +24 -0
  232. package/dist/r/multi-progress.json +24 -0
  233. package/dist/r/multi-select.json +24 -0
  234. package/dist/r/notification-badge.json +24 -0
  235. package/dist/r/number-input.json +24 -0
  236. package/dist/r/operation-tree.json +26 -0
  237. package/dist/r/option-list.json +24 -0
  238. package/dist/r/ordered-list.json +22 -0
  239. package/dist/r/paginator.json +22 -0
  240. package/dist/r/paragraph.json +24 -0
  241. package/dist/r/path-input.json +26 -0
  242. package/dist/r/pie-chart.json +24 -0
  243. package/dist/r/placeholder.json +24 -0
  244. package/dist/r/popover.json +24 -0
  245. package/dist/r/progress-bar.json +22 -0
  246. package/dist/r/progress-circle.json +24 -0
  247. package/dist/r/qr-code.json +24 -0
  248. package/dist/r/radio-group.json +24 -0
  249. package/dist/r/registry.json +1051 -0
  250. package/dist/r/scatter-plot.json +27 -0
  251. package/dist/r/scroll-bar-box.json +24 -0
  252. package/dist/r/scroll-bar.json +24 -0
  253. package/dist/r/scroll-list.json +24 -0
  254. package/dist/r/scroll-view.json +24 -0
  255. package/dist/r/scroll.json +58 -0
  256. package/dist/r/search-input.json +26 -0
  257. package/dist/r/select-input-indicator.json +24 -0
  258. package/dist/r/select-input-item.json +24 -0
  259. package/dist/r/select-input.json +28 -0
  260. package/dist/r/shimmer-text.json +24 -0
  261. package/dist/r/sidebar.json +24 -0
  262. package/dist/r/slider.json +24 -0
  263. package/dist/r/sparkline.json +24 -0
  264. package/dist/r/spinner.json +23 -0
  265. package/dist/r/status-line.json +24 -0
  266. package/dist/r/status-message.json +24 -0
  267. package/dist/r/stepper.json +24 -0
  268. package/dist/r/stopwatch.json +22 -0
  269. package/dist/r/streaming-text.json +24 -0
  270. package/dist/r/switch.json +24 -0
  271. package/dist/r/tab.json +22 -0
  272. package/dist/r/table.json +23 -0
  273. package/dist/r/tabs.json +26 -0
  274. package/dist/r/tag-input.json +24 -0
  275. package/dist/r/tag.json +24 -0
  276. package/dist/r/text-input.json +22 -0
  277. package/dist/r/textarea.json +22 -0
  278. package/dist/r/thinking-block.json +24 -0
  279. package/dist/r/time-picker.json +24 -0
  280. package/dist/r/timer.json +22 -0
  281. package/dist/r/toast.json +24 -0
  282. package/dist/r/token-usage.json +24 -0
  283. package/dist/r/tool-call.json +24 -0
  284. package/dist/r/tooltip.json +24 -0
  285. package/dist/r/transition.json +22 -0
  286. package/dist/r/tree-select.json +24 -0
  287. package/dist/r/tree-view.json +64 -0
  288. package/dist/r/unordered-list.json +22 -0
  289. package/dist/r/use-linked-scroll.json +24 -0
  290. package/dist/r/variant-config.json +22 -0
  291. package/dist/radio-group.d.ts +74 -0
  292. package/dist/radio-group.js +1 -0
  293. package/dist/scatter-plot.d.ts +35 -0
  294. package/dist/scatter-plot.js +1 -0
  295. package/dist/scroll-bar-box.d.ts +3 -0
  296. package/dist/scroll-bar-box.js +1 -0
  297. package/dist/scroll-bar.d.ts +3 -0
  298. package/dist/scroll-bar.js +1 -0
  299. package/dist/scroll-list.d.ts +4 -0
  300. package/dist/scroll-list.js +1 -0
  301. package/dist/scroll-view.d.ts +3 -0
  302. package/dist/scroll-view.js +1 -0
  303. package/dist/search-input.d.ts +55 -0
  304. package/dist/search-input.js +1 -0
  305. package/dist/select-input-indicator.d.ts +32 -0
  306. package/dist/select-input-indicator.js +1 -0
  307. package/dist/select-input-item.d.ts +41 -0
  308. package/dist/select-input-item.js +1 -0
  309. package/dist/select-input.d.ts +110 -0
  310. package/dist/select-input.js +1 -0
  311. package/dist/shimmer-text.d.ts +47 -0
  312. package/dist/shimmer-text.js +1 -0
  313. package/dist/sidebar.d.ts +47 -0
  314. package/dist/sidebar.js +1 -0
  315. package/dist/slider.d.ts +103 -0
  316. package/dist/slider.js +1 -0
  317. package/dist/sparkline.d.ts +42 -0
  318. package/dist/sparkline.js +1 -0
  319. package/dist/spinner.d.ts +17 -0
  320. package/dist/spinner.js +1 -0
  321. package/dist/status-line.d.ts +37 -0
  322. package/dist/status-line.js +1 -0
  323. package/dist/status-message.d.ts +21 -0
  324. package/dist/status-message.js +1 -0
  325. package/dist/stepper.d.ts +48 -0
  326. package/dist/stepper.js +1 -0
  327. package/dist/stopwatch.d.ts +49 -0
  328. package/dist/stopwatch.js +1 -0
  329. package/dist/streaming-text.d.ts +46 -0
  330. package/dist/streaming-text.js +1 -0
  331. package/dist/switch.d.ts +55 -0
  332. package/dist/switch.js +1 -0
  333. package/dist/tab.d.ts +19 -0
  334. package/dist/tab.js +1 -0
  335. package/dist/table.d.ts +119 -0
  336. package/dist/table.js +1 -0
  337. package/dist/tabs.d.ts +107 -0
  338. package/dist/tabs.js +1 -0
  339. package/dist/tag-input.d.ts +71 -0
  340. package/dist/tag-input.js +1 -0
  341. package/dist/tag.d.ts +35 -0
  342. package/dist/tag.js +1 -0
  343. package/dist/text-input.d.ts +48 -0
  344. package/dist/text-input.js +1 -0
  345. package/dist/textarea.d.ts +68 -0
  346. package/dist/textarea.js +2 -0
  347. package/dist/thinking-block.d.ts +57 -0
  348. package/dist/thinking-block.js +1 -0
  349. package/dist/time-picker.d.ts +67 -0
  350. package/dist/time-picker.js +1 -0
  351. package/dist/timer.d.ts +59 -0
  352. package/dist/timer.js +1 -0
  353. package/dist/toast.d.ts +49 -0
  354. package/dist/toast.js +1 -0
  355. package/dist/token-usage.d.ts +50 -0
  356. package/dist/token-usage.js +1 -0
  357. package/dist/tool-call.d.ts +33 -0
  358. package/dist/tool-call.js +1 -0
  359. package/dist/tooltip.d.ts +43 -0
  360. package/dist/tooltip.js +1 -0
  361. package/dist/transition.d.ts +74 -0
  362. package/dist/transition.js +1 -0
  363. package/dist/tree-select.d.ts +65 -0
  364. package/dist/tree-select.js +1 -0
  365. package/dist/tree-view-entry.d.ts +4 -0
  366. package/dist/tree-view-entry.js +1 -0
  367. package/dist/unordered-list.d.ts +30 -0
  368. package/dist/unordered-list.js +1 -0
  369. package/dist/use-linked-scroll.d.ts +42 -0
  370. package/dist/use-linked-scroll.js +1 -0
  371. package/package.json +553 -7
@@ -0,0 +1,40 @@
1
+ import { ReactElement } from 'react';
2
+ type Props = {
3
+ /**
4
+ * The Markdown source string.
5
+ */
6
+ readonly children: string;
7
+ /**
8
+ * Shiki theme for code block syntax highlighting.
9
+ * @default "github-dark-default"
10
+ */
11
+ readonly codeTheme?: string;
12
+ /**
13
+ * Maximum width for text wrapping. Defaults to terminal width.
14
+ */
15
+ readonly maxWidth?: number;
16
+ /**
17
+ * Enable streaming mode for progressive rendering.
18
+ * When true, the component re-renders on every prop change and handles
19
+ * incomplete Markdown gracefully (e.g., unclosed code fences, partial lists).
20
+ * Useful for rendering AI-generated text arriving token-by-token.
21
+ * @default false
22
+ */
23
+ readonly streaming?: boolean;
24
+ };
25
+ /**
26
+ * Render Markdown content as Ink terminal UI elements.
27
+ *
28
+ * ```tsx
29
+ * <Markdown>{"# Hello\n\nThis is **bold** and *italic*."}</Markdown>
30
+ * <Markdown codeTheme="github-dark-default">{"```js\nconst x = 1;\n```"}</Markdown>
31
+ * <Markdown streaming>{"# Streaming\n\nText arriving..."}</Markdown>
32
+ * ```
33
+ */
34
+ declare function Markdown({
35
+ children,
36
+ codeTheme,
37
+ maxWidth: maxWidthProp,
38
+ streaming
39
+ }: Props): ReactElement;
40
+ export { Markdown, type Props as MarkdownProps, Props, Markdown as default };
@@ -0,0 +1 @@
1
+ import{jsx as r,jsxs as x}from"react/jsx-runtime";import l from"@visulima/tui/components/box";import w from"@visulima/tui/components/newline";import m from"@visulima/tui/components/text";import D from"@visulima/tui/hooks/use-window-size";import{Lexer as F}from"marked";import k,{useMemo as b}from"react";import{Code as M}from"./code.js";import{Link as j}from"./link.js";import{OrderedList as v}from"./ordered-list.js";import{Table as E}from"./table.js";import{UnorderedList as q}from"./unordered-list.js";const A={1:"cyan",2:"green",3:"yellow",4:"blue",5:"magenta",6:"gray"};function p(c){return!c||c.length===0?null:c.map((a,t)=>{switch(a.type){case"br":return r(w,{},t);case"codespan":return x(m,{inverse:!0,children:[" ",a.text," "]},t);case"del":return r(m,{strikethrough:!0,children:p(a.tokens)},t);case"em":return r(m,{italic:!0,children:p(a.tokens)},t);case"escape":return r(k.Fragment,{children:a.text},t);case"image":{const e=a;return x(m,{dimColor:!0,children:["[image:"," ",e.text||e.href,"]"]},t)}case"link":{const e=a;return r(j,{url:e.href,children:p(e.tokens)??e.text},t)}case"strong":return r(m,{bold:!0,children:p(a.tokens)},t);case"text":return r(k.Fragment,{children:a.text},t);default:{const e="raw"in a?a.raw:"";return r(k.Fragment,{children:e},t)}}})}function T(c){return c.map(a=>{const t=a.tokens??[],e=t.find(i=>i.type==="text"),n=p(e?.tokens??t),h=t.find(i=>i.type==="list");return{children:h?T(h.items):void 0,label:n}})}function y(c,a,t){const e=[];for(const[n,h]of c.entries()){const i=h,s=n>0?1:0;switch(i.type){case"blockquote":{const o=i;e.push(r(l,{borderLeft:!0,borderLeftColor:"gray",marginTop:s,paddingLeft:1,children:r(l,{flexDirection:"column",children:y(o.tokens,a,t-3)})},n));break}case"code":{const o=i;e.push(r(l,{marginTop:s,children:r(M,{code:o.text,language:o.lang,theme:a})},n));break}case"heading":{const o=i,d=A[o.depth]??"white",g=`${"#".repeat(o.depth)} `;e.push(r(l,{marginTop:s,children:x(m,{bold:!0,color:d,children:[g,p(o.tokens)]})},n));break}case"hr":{e.push(r(l,{marginTop:s,children:r(m,{dimColor:!0,children:"─".repeat(Math.min(t,40))})},n));break}case"html":{const o=i.text.replaceAll(/<[^>]*>/g,"");o.trim()&&e.push(r(l,{marginTop:s,children:r(m,{dimColor:!0,children:o})},n));break}case"list":{const o=i,d=T(o.items);o.ordered?e.push(r(l,{marginTop:s,children:r(v,{items:d})},n)):e.push(r(l,{marginTop:s,children:r(q,{items:d})},n));break}case"paragraph":{const o=i;e.push(r(l,{marginTop:s,children:r(m,{wrap:"wrap",children:p(o.tokens)})},n));break}case"space":{e.push(r(w,{},n));break}case"table":{const o=i,d=(o.header??[]).map(u=>u.text),g=(o.rows??[]).map(u=>u.map(f=>f.text)).map(u=>{const f={};return d.forEach((L,C)=>{f[L]=u[C]??""}),f});e.push(r(l,{marginTop:s,children:r(E,{data:g})},n));break}default:{const o="raw"in i?i.raw:"",d="text"in i?i.text:o;d&&e.push(r(l,{marginTop:s,children:r(m,{children:d})},n));break}}}return e}function B(c,a){const t=F.lex(c);if(!a||c.length===0)return t;let e=0;for(const n of t)e+=n.raw?.length??0;if(e<c.length){const n=c.slice(e);n.trim().length>0&&t.push({raw:n,text:n,tokens:[{raw:n,text:n,type:"text"}],type:"paragraph"})}return t}function Q({children:c,codeTheme:a="github-dark-default",maxWidth:t,streaming:e=!1}){const{columns:n}=D(),h=t??n??80,i=b(()=>B(c,e),[c,e]),s=b(()=>y(i,a,h),[i,a,h]);return r(l,{flexDirection:"column",children:s})}export{Q as Markdown,Q as default};
@@ -0,0 +1,50 @@
1
+ import { ReactElement } from 'react';
2
+ type Props = {
3
+ /**
4
+ * Initial raw value (digits/characters only, without mask symbols).
5
+ */
6
+ readonly defaultValue?: string;
7
+ /**
8
+ * Disable input and dim the display.
9
+ */
10
+ readonly isDisabled?: boolean;
11
+ /**
12
+ * Mask definition. Each `#` is replaced with a user character; any other
13
+ * character is rendered literally. Example: `(###) ###-####`.
14
+ */
15
+ readonly mask: string;
16
+ /**
17
+ * Called whenever the underlying raw value changes.
18
+ */
19
+ readonly onChange?: (value: string) => void;
20
+ /**
21
+ * Called when the user presses Enter with the current raw value.
22
+ */
23
+ readonly onSubmit?: (value: string) => void;
24
+ /**
25
+ * Placeholder character used in the mask for empty positions.
26
+ * @default "_"
27
+ */
28
+ readonly placeholderChar?: string;
29
+ /**
30
+ * Sentinel character in the mask string used to mark editable positions.
31
+ * @default "#" (hash)
32
+ */
33
+ readonly token?: string;
34
+ };
35
+ /**
36
+ * Input with a fixed-width mask. The mask token (`#` by default) marks
37
+ * positions where the user can type; every other character is preserved.
38
+ * Supports ←/→ to move within the mask, Home/End to jump, and
39
+ * Backspace/Delete to clear characters.
40
+ */
41
+ declare function MaskedInput({
42
+ defaultValue,
43
+ isDisabled,
44
+ mask,
45
+ onChange,
46
+ onSubmit,
47
+ placeholderChar,
48
+ token
49
+ }: Props): ReactElement;
50
+ export { MaskedInput, type Props as MaskedInputProps, Props, MaskedInput as default };
@@ -0,0 +1 @@
1
+ import{jsxs as S,jsx as C}from"react/jsx-runtime";import V from"@visulima/tui/components/box";import x from"@visulima/tui/components/text";import I from"@visulima/tui/hooks/use-input";import{useMemo as L,useState as b,useCallback as A}from"react";const T=(m,n)=>{const o=[];let s=0;for(const[l,i]of[...m].entries())i===n&&(o.push({maskColumn:l,rawPosition:s}),s+=1);return o},_=(m,n,o,s)=>{let l=0,i="";for(const f of n)if(f===o){const u=m[l];u===void 0?i+=s:(i+=u,l+=1)}else i+=f;return i};function F({defaultValue:m="",isDisabled:n=!1,mask:o,onChange:s,onSubmit:l,placeholderChar:i="_",token:f="#"}){const u=L(()=>T(o,f),[o,f]),h=u.length,M=m.slice(0,h),[e,v]=b(M),[t,d]=b(M.length),g=A((c,r)=>{v(c),d(Math.max(0,Math.min(h,r))),s?.(c)},[h,s]);I(A((c,r)=>{if(r.return){l?.(e);return}if(r.leftArrow){d(a=>Math.max(0,a-1));return}if(r.rightArrow){d(a=>Math.min(e.length,a+1));return}if(r.home||r.ctrl&&c==="a"){d(0);return}if(r.end||r.ctrl&&c==="e"){d(e.length);return}if(r.backspace){if(t===0)return;const a=e.slice(0,t-1)+e.slice(t);g(a,t-1);return}if(r.delete){if(t>=e.length)return;const a=e.slice(0,t)+e.slice(t+1);g(a,t);return}if(c.length!==1||e.length>=h)return;const P=e.slice(0,t)+c+e.slice(t);g(P,t+1)},[e,t,h,g,l]),{isActive:!n});const p=_(e,o,f,i),k=u[t]?.maskColumn??o.length,w=p.slice(0,k),j=p.charAt(k)||" ",D=p.slice(k+1);return S(V,{children:[C(x,{dimColor:n,children:w}),C(x,{dimColor:n,inverse:!n,children:j}),C(x,{dimColor:n,children:D})]})}export{F as MaskedInput,F as default};
package/dist/menu.d.ts ADDED
@@ -0,0 +1,78 @@
1
+ import { AnsiColors } from '@visulima/colorize';
2
+ import { ReactNode, ReactElement } from 'react';
3
+ import { LiteralUnion } from 'type-fest';
4
+ type MenuItem = {
5
+ /**
6
+ * Optional hotkey rendered on the right side of the row (e.g. `"Ctrl+S"`).
7
+ */
8
+ readonly hotkey?: string;
9
+ /**
10
+ * Optional icon rendered before the label.
11
+ */
12
+ readonly icon?: ReactNode;
13
+ /**
14
+ * Identifier returned by `onSelect`.
15
+ */
16
+ readonly id: string;
17
+ /**
18
+ * When true, renders the item dimmed and skips it during navigation.
19
+ */
20
+ readonly isDisabled?: boolean;
21
+ /**
22
+ * Human-readable label.
23
+ */
24
+ readonly label: ReactNode;
25
+ };
26
+ type MenuSection = {
27
+ readonly id: string;
28
+ readonly items: ReadonlyArray<MenuItem>;
29
+ readonly title?: string;
30
+ };
31
+ type Props = {
32
+ /**
33
+ * Accent color for the focused item.
34
+ * @default "blue"
35
+ */
36
+ readonly accentColor?: LiteralUnion<AnsiColors, string>;
37
+ /**
38
+ * Auto-focus the menu on mount.
39
+ */
40
+ readonly autoFocus?: boolean;
41
+ /**
42
+ * Render a border around the menu.
43
+ * @default true
44
+ */
45
+ readonly bordered?: boolean;
46
+ /**
47
+ * Flat list of items. Ignored if `sections` is provided.
48
+ */
49
+ readonly items?: ReadonlyArray<MenuItem>;
50
+ /**
51
+ * Called with the item id when the user presses Enter.
52
+ */
53
+ readonly onSelect: (id: string) => void;
54
+ /**
55
+ * Grouped items. Takes precedence over `items`.
56
+ */
57
+ readonly sections?: ReadonlyArray<MenuSection>;
58
+ /**
59
+ * Optional title rendered at the top of the menu.
60
+ */
61
+ readonly title?: string;
62
+ };
63
+ /**
64
+ * Dropdown / context menu with optional sections and disabled items.
65
+ * Keyboard: ↑/↓ (or j/k) to navigate, Enter to select, Esc to cancel.
66
+ * @returns A `ReactElement` rendering the menu, optionally wrapped in a
67
+ * rounded border when `bordered` is true.
68
+ */
69
+ declare function Menu({
70
+ accentColor,
71
+ autoFocus,
72
+ bordered,
73
+ items,
74
+ onSelect,
75
+ sections,
76
+ title
77
+ }: Props): ReactElement;
78
+ export { Menu, MenuItem, type Props as MenuProps, MenuSection, Props, Menu as default };
package/dist/menu.js ADDED
@@ -0,0 +1 @@
1
+ import{jsxs as b,jsx as l}from"react/jsx-runtime";import m from"@visulima/tui/components/box";import f from"@visulima/tui/components/text";import A from"@visulima/tui/hooks/use-focus";import F from"@visulima/tui/hooks/use-input";import{useMemo as g,useState as j,useRef as y,useEffect as M,useCallback as $}from"react";const B=(t,r)=>{if(r&&r.length>0){const o=[];for(const i of r){i.title!==void 0&&o.push({kind:"section-header",sectionId:i.id,title:i.title});for(const d of i.items)o.push({item:d,kind:"item",sectionId:i.id})}return o}return(t??[]).map(o=>({item:o,kind:"item",sectionId:void 0}))},w=(t,r,o)=>{const i=t.length;if(i===0)return r;for(let d=1;d<=i;d+=1){const a=(r+o*d+i*d)%i,h=t[a];if(h?.kind==="item"&&!h.item.isDisabled)return a}return r},I=t=>{for(const[r,o]of t.entries())if(o.kind==="item"&&!o.item.isDisabled)return r;return 0};function W({accentColor:t="blue",autoFocus:r=!1,bordered:o=!0,items:i,onSelect:d,sections:a,title:h}){const{isFocused:p}=A({autoFocus:r}),n=g(()=>B(i,a),[i,a]),S=g(()=>I(n),[n]),[k,v]=j(S),u=y(k);u.current=k,M(()=>{const{current:c}=u,s=n[c];if(!(s?.kind==="item"&&!s.item.isDisabled)){const e=I(n);v(e),u.current=e}},[n]),F($((c,s)=>{if(p){if(s.downArrow||c==="j")v(w(n,u.current,1));else if(s.upArrow||c==="k")v(w(n,u.current,-1));else if(s.return){const e=n[u.current];e?.kind==="item"&&!e.item.isDisabled&&d(e.item.id)}}},[p,n,d]),{isActive:p});const x=b(m,{flexDirection:"column",children:[h===void 0?void 0:l(m,{marginBottom:1,children:l(f,{bold:!0,color:t,children:h})}),n.map((c,s)=>{if(c.kind==="section-header")return l(m,{marginTop:s===0?0:1,children:l(f,{dimColor:!0,children:c.title.toUpperCase()})},`header:${c.sectionId}`);const{item:e}=c,C=p&&s===k,D=C?t:void 0;return b(m,{children:[l(f,{color:D,dimColor:e.isDisabled,children:C?"▸ ":" "}),e.icon===void 0?void 0:b(f,{color:D,dimColor:e.isDisabled,children:[e.icon," "]}),l(m,{flexGrow:1,flexShrink:1,minWidth:0,children:l(f,{bold:C,color:D,dimColor:e.isDisabled,wrap:"truncate-end",children:e.label})}),e.hotkey===void 0?void 0:l(m,{flexShrink:0,children:b(f,{dimColor:!0,children:[" ",e.hotkey]})})]},`item:${e.id}`)})]});return o?l(m,{borderColor:p?t:void 0,borderStyle:"round",paddingX:1,children:x}):x}export{W as Menu,W as default};
@@ -0,0 +1,43 @@
1
+ import { AnsiColors } from '@visulima/colorize';
2
+ import { ReactNode, ReactElement } from 'react';
3
+ import { LiteralUnion } from 'type-fest';
4
+ type MessageRole = "assistant" | "system" | "tool" | "user";
5
+ type Props = {
6
+ /**
7
+ * Bubble body.
8
+ */
9
+ readonly children: ReactNode;
10
+ /**
11
+ * Color override.
12
+ */
13
+ readonly color?: LiteralUnion<AnsiColors, string>;
14
+ /**
15
+ * When true, draws the bubble without a border.
16
+ */
17
+ readonly flat?: boolean;
18
+ /**
19
+ * Text rendered above the bubble (name, model, etc.).
20
+ */
21
+ readonly label?: string;
22
+ /**
23
+ * Optional metadata rendered next to the label (e.g. timestamp).
24
+ */
25
+ readonly meta?: string;
26
+ /**
27
+ * Determines the alignment, color, and icon of the bubble.
28
+ * @default "assistant"
29
+ */
30
+ readonly role?: MessageRole;
31
+ };
32
+ /**
33
+ * Chat-style message bubble for AI agent UIs.
34
+ */
35
+ declare function MessageBubble({
36
+ children,
37
+ color,
38
+ flat,
39
+ label,
40
+ meta,
41
+ role
42
+ }: Props): ReactElement;
43
+ export { MessageBubble, type Props as MessageBubbleProps, MessageRole, Props, MessageBubble as default };
@@ -0,0 +1 @@
1
+ import{jsxs as c,jsx as o}from"react/jsx-runtime";import i from"@visulima/tui/components/box";import l from"@visulima/tui/components/text";const f={assistant:{align:"flex-start",color:"blueBright",icon:""},system:{align:"flex-start",color:"gray",icon:""},tool:{align:"flex-start",color:"magentaBright",icon:""},user:{align:"flex-end",color:"greenBright",icon:""}};function p({children:r,color:a,flat:s=!1,label:e,meta:n,role:g="assistant"}){const d=f[g],t=a??d.color;return c(i,{alignSelf:d.align,flexDirection:"column",marginBottom:1,children:[e===void 0&&n===void 0?void 0:c(i,{gap:1,paddingX:1,children:[e===void 0?void 0:o(l,{bold:!0,color:t,children:e}),n===void 0?void 0:o(l,{dimColor:!0,children:n})]}),s?c(i,{paddingX:1,children:[o(l,{color:t,children:"│ "}),o(i,{flexDirection:"column",flexGrow:1,children:typeof r=="string"?o(l,{children:r}):r})]}):o(i,{borderColor:t,borderStyle:"round",flexDirection:"column",paddingX:1,children:typeof r=="string"?o(l,{children:r}):r})]})}export{p as MessageBubble,p as default};
@@ -0,0 +1,39 @@
1
+ import { AnsiColors } from '@visulima/colorize';
2
+ import { ReactNode, ReactElement } from 'react';
3
+ import { LiteralUnion } from 'type-fest';
4
+ type Props = {
5
+ /**
6
+ * Background color of the badge.
7
+ * @default "magenta"
8
+ */
9
+ readonly color?: LiteralUnion<AnsiColors, string>;
10
+ /**
11
+ * Optional icon/symbol (e.g. `✨`, `◈`) shown before the label.
12
+ */
13
+ readonly icon?: ReactNode;
14
+ /**
15
+ * Short model identifier, e.g. `claude-opus-4.6`.
16
+ */
17
+ readonly model: string;
18
+ /**
19
+ * Optional provider name rendered as a separate chip before the model.
20
+ */
21
+ readonly provider?: string;
22
+ /**
23
+ * Visual style.
24
+ * @default "solid"
25
+ */
26
+ readonly variant?: "outline" | "solid" | "subtle";
27
+ };
28
+ /**
29
+ * Pill-shaped chip identifying the active model / provider. Pair with
30
+ * `MessageBubble` to label assistant output.
31
+ */
32
+ declare function ModelBadge({
33
+ color,
34
+ icon,
35
+ model,
36
+ provider,
37
+ variant
38
+ }: Props): ReactElement;
39
+ export { ModelBadge, type Props as ModelBadgeProps, Props, ModelBadge as default };
@@ -0,0 +1 @@
1
+ import{jsxs as o,jsx as l,Fragment as s}from"react/jsx-runtime";import u from"@visulima/tui/components/box";import r from"@visulima/tui/components/text";function g({color:d="magenta",icon:n,model:c,provider:t,variant:a="solid"}){const m=e=>{const i=n===void 0?void 0:o(s,{children:[n," "]});return a==="outline"?l(u,{borderColor:d,borderStyle:"round",paddingX:1,children:o(r,{color:d,children:[i,e]})}):a==="subtle"?o(r,{color:d,children:[i,e]}):o(r,{backgroundColor:d,color:"black",children:[" ",i,e," "]})};return t===void 0?m(c):o(u,{children:[l(r,{dimColor:!0,children:t}),l(r,{dimColor:!0,children:"/"}),m(c)]})}export{g as ModelBadge,g as default};
@@ -0,0 +1,52 @@
1
+ import { AnsiColors } from '@visulima/colorize';
2
+ import { ReactElement } from 'react';
3
+ import { LiteralUnion } from 'type-fest';
4
+ type ModelOption = {
5
+ /**
6
+ * Optional context-window hint shown on the right (e.g. `200k`).
7
+ */
8
+ readonly context?: string;
9
+ readonly id: string;
10
+ readonly name: string;
11
+ readonly provider?: string;
12
+ };
13
+ type Props = {
14
+ /**
15
+ * Accent color for the focused row and selected marker.
16
+ * @default "blue"
17
+ */
18
+ readonly accentColor?: LiteralUnion<AnsiColors, string>;
19
+ /**
20
+ * Auto-focus on mount.
21
+ */
22
+ readonly autoFocus?: boolean;
23
+ /**
24
+ * Selected model id when uncontrolled.
25
+ */
26
+ readonly defaultValue?: string;
27
+ /**
28
+ * The models to choose from.
29
+ */
30
+ readonly models: ReadonlyArray<ModelOption>;
31
+ /**
32
+ * Fires when the highlighted model is committed with Enter.
33
+ */
34
+ readonly onSelect?: (model: ModelOption) => void;
35
+ /**
36
+ * Controlled selected model id. When set, `defaultValue` is ignored.
37
+ */
38
+ readonly value?: string;
39
+ };
40
+ /**
41
+ * A selectable list of models with provider and context-window hints. ↑/↓ move
42
+ * the cursor and Enter commits; a ● marks the currently-selected model.
43
+ */
44
+ declare function ModelSelector({
45
+ accentColor,
46
+ autoFocus,
47
+ defaultValue,
48
+ models,
49
+ onSelect,
50
+ value: controlledValue
51
+ }: Props): ReactElement;
52
+ export { ModelOption, ModelSelector, type Props as ModelSelectorProps, Props, ModelSelector as default };
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as M}from"react/jsx-runtime";import u from"@visulima/tui/components/box";import n from"@visulima/tui/components/text";import S from"@visulima/tui/hooks/use-focus";import j from"@visulima/tui/hooks/use-input";import{useState as x,useCallback as D}from"react";function C(c,i){return i===0?0:(c%i+i)%i}function V({accentColor:c="blue",autoFocus:i=!1,defaultValue:b,models:t,onSelect:m,value:a}){const{isFocused:f}=S({autoFocus:i}),s=a!==void 0,[g,w]=x(b),v=a??g,A=Math.max(0,t.findIndex(r=>r.id===v)),[l,h]=x(A),F=D((r,d)=>{if(d.upArrow){h(o=>C(o-1,t.length));return}if(d.downArrow){h(o=>C(o+1,t.length));return}if(d.return){const o=t[l];o!==void 0&&(s||w(o.id),m?.(o))}},[l,s,t,m]);return j(F,{isActive:f}),e(u,{flexDirection:"column",children:t.map((r,d)=>{const o=f&&d===l,p=r.id===v;return M(u,{gap:1,children:[e(n,{color:o?c:void 0,children:o?"❯":" "}),e(n,{color:p?c:void 0,children:p?"●":"○"}),e(n,{bold:o,children:r.name}),r.provider===void 0?void 0:e(n,{dimColor:!0,children:r.provider}),r.context===void 0?void 0:e(u,{marginLeft:1,children:e(n,{dimColor:!0,children:r.context})})]},r.id)})})}export{V as ModelSelector,V as default};
@@ -0,0 +1,55 @@
1
+ import { AnsiColors } from '@visulima/colorize';
2
+ import { ReactElement } from 'react';
3
+ import { LiteralUnion } from 'type-fest';
4
+ type Color = LiteralUnion<AnsiColors, string>;
5
+ type ProgressItem = {
6
+ readonly color?: Color;
7
+ readonly label: string;
8
+ /**
9
+ * Progress in the range [0, 1]. Values outside are clamped.
10
+ */
11
+ readonly value: number;
12
+ };
13
+ type Props = {
14
+ /**
15
+ * Width of each bar in cells.
16
+ * @default 20
17
+ */
18
+ readonly barWidth?: number;
19
+ /**
20
+ * Fallback bar color when an item has none.
21
+ * @default "cyan"
22
+ */
23
+ readonly color?: Color;
24
+ /**
25
+ * Character for the empty portion of each bar; defaults to a light shade.
26
+ */
27
+ readonly emptyChar?: string;
28
+ /**
29
+ * Character for the filled portion of each bar; defaults to a full block.
30
+ */
31
+ readonly filledChar?: string;
32
+ /**
33
+ * The bars to render, top to bottom.
34
+ */
35
+ readonly items: ReadonlyArray<ProgressItem>;
36
+ /**
37
+ * Render the percentage after each bar.
38
+ * @default true
39
+ */
40
+ readonly showPercent?: boolean;
41
+ };
42
+ /**
43
+ * A stack of labeled progress bars sharing one aligned column layout — useful
44
+ * for parallel downloads, multi-step jobs, or per-file progress. Labels are
45
+ * right-padded to the widest so the bars line up.
46
+ */
47
+ declare function MultiProgress({
48
+ barWidth,
49
+ color,
50
+ emptyChar,
51
+ filledChar,
52
+ items,
53
+ showPercent
54
+ }: Props): ReactElement;
55
+ export { MultiProgress, type Props as MultiProgressProps, ProgressItem, Props, MultiProgress as default };
@@ -0,0 +1 @@
1
+ import{jsx as r,jsxs as c}from"react/jsx-runtime";import l from"@visulima/tui/components/box";import i from"@visulima/tui/components/text";const p=t=>Math.max(0,Math.min(1,t));function b({barWidth:t=20,color:d="cyan",emptyChar:m="░",filledChar:s="█",items:n,showPercent:u=!0}){let o=0;for(const e of n)o=Math.max(o,e.label.length);return r(l,{flexDirection:"column",children:n.map((e,f)=>{const a=p(e.value),h=Math.round(a*t);return c(l,{gap:1,children:[r(l,{flexShrink:0,width:o,children:r(i,{children:e.label.padEnd(o)})}),c(l,{children:[r(i,{color:e.color??d,children:s.repeat(h)}),r(i,{dimColor:!0,children:m.repeat(Math.max(0,t-h))})]}),u?r(i,{dimColor:!0,children:`${Math.round(a*100)}%`}):void 0]},f)})})}export{b as MultiProgress,b as default};
@@ -0,0 +1,65 @@
1
+ import { AnsiColors } from '@visulima/colorize';
2
+ import { ReactElement } from 'react';
3
+ import { LiteralUnion } from 'type-fest';
4
+ type MultiSelectOption = {
5
+ readonly key?: string;
6
+ readonly label: string;
7
+ readonly value: string;
8
+ };
9
+ type Props = {
10
+ /**
11
+ * Color for the focused item indicator and selected checkmarks.
12
+ * @default "blue"
13
+ */
14
+ readonly accentColor?: LiteralUnion<AnsiColors, string>;
15
+ /**
16
+ * Color for unselected, unfocused option labels.
17
+ */
18
+ readonly defaultColor?: LiteralUnion<AnsiColors, string>;
19
+ /**
20
+ * Initially selected values.
21
+ */
22
+ readonly defaultValue?: ReadonlyArray<string>;
23
+ /**
24
+ * When true, all input is ignored and labels are dimmed.
25
+ * @default false
26
+ */
27
+ readonly isDisabled?: boolean;
28
+ /**
29
+ * Whether the parent has focus. When false, indicators are dimmed.
30
+ * @default true
31
+ */
32
+ readonly isFocused?: boolean;
33
+ /**
34
+ * Number of visible options before scrolling.
35
+ * When omitted, auto-limits to terminal height.
36
+ */
37
+ readonly limit?: number;
38
+ /**
39
+ * Fires when the set of selected values changes (space to toggle).
40
+ */
41
+ readonly onChange?: (values: ReadonlyArray<string>) => void;
42
+ /**
43
+ * Fires when the user presses Enter to submit.
44
+ */
45
+ readonly onSubmit?: (values: ReadonlyArray<string>) => void;
46
+ /**
47
+ * Available choices to pick from.
48
+ */
49
+ readonly options: ReadonlyArray<MultiSelectOption>;
50
+ };
51
+ /**
52
+ * Renders a multi-choice selection list with checkboxes and keyboard navigation.
53
+ */
54
+ declare function MultiSelect({
55
+ accentColor,
56
+ defaultColor,
57
+ defaultValue,
58
+ isDisabled,
59
+ isFocused,
60
+ limit: customLimit,
61
+ onChange,
62
+ onSubmit,
63
+ options
64
+ }: Props): ReactElement;
65
+ export { MultiSelect, MultiSelectOption, type Props as MultiSelectProps, Props, MultiSelect as default };
@@ -0,0 +1 @@
1
+ import{jsx as m,jsxs as R}from"react/jsx-runtime";import S from"@visulima/tui/components/box";import f from"@visulima/tui/components/text";import V from"@visulima/tui/hooks/use-input";import $ from"@visulima/tui/hooks/use-window-size";import{useReducer as T,useState as U,useRef as a,useMemo as W,useCallback as F}from"react";const Y=[],q=(o,u)=>{switch(u.type){case"focus":return{...o,focusedIndex:u.index};case"focus-next":{const i=o.focusedIndex+1,c=i>=o.visibleFrom+u.limit;return{...o,focusedIndex:i,visibleFrom:c?o.visibleFrom+1:o.visibleFrom}}case"focus-prev":{const i=o.focusedIndex-1,c=i<o.visibleFrom;return{...o,focusedIndex:i,visibleFrom:c?o.visibleFrom-1:o.visibleFrom}}default:return o}};function L({accentColor:o="blue",defaultColor:u,defaultValue:i=Y,isDisabled:c=!1,isFocused:v=!0,limit:g,onChange:C,onSubmit:I,options:t}){const{rows:A}=$(),l=Math.min(typeof g=="number"?g:A,t.length),[s,w]=T(q,{focusedIndex:0,visibleFrom:0}),[b,M]=U(i),j=a(b);j.current=b;const k=a(I);k.current=I;const p=a(C);p.current=C;const x=a(s);x.current=s;const E=W(()=>t.slice(s.visibleFrom,s.visibleFrom+l),[t,s.visibleFrom,l]),y=F(()=>{const e=t[x.current.focusedIndex]?.value;e&&M(r=>{const n=r.includes(e)?r.filter(d=>d!==e):[...r,e];return p.current?.(n),n})},[t]),D=F(()=>{M(e=>{const r=e.length===t.length?[]:t.map(n=>n.value);return p.current?.(r),r})},[t]);return V(F((e,r)=>{const{focusedIndex:n}=x.current;(r.downArrow||e==="j")&&n<t.length-1&&w({limit:l,type:"focus-next"}),(r.upArrow||e==="k")&&n>0&&w({limit:l,type:"focus-prev"}),e===" "&&y(),e==="a"&&D(),r.return&&k.current?.(j.current)},[t.length,l,y,D]),{isActive:!c&&v}),m(S,{flexDirection:"column",children:E.map((e,r)=>{const n=s.visibleFrom+r===s.focusedIndex,d=b.includes(e.value);let h=u;return d?h="green":n&&(h=o),R(S,{gap:1,children:[n?m(f,{color:o,dimColor:!v,children:"▸"}):m(f,{children:" "}),R(f,{color:h,dimColor:c,children:[d?"◼":"◻"," ",e.label]}),d?m(f,{color:"green",dimColor:!v,children:"✓"}):void 0]},e.key??e.value)})})}export{L as MultiSelect,L as default};
@@ -0,0 +1,47 @@
1
+ import { AnsiColors } from '@visulima/colorize';
2
+ import { ReactNode, ReactElement } from 'react';
3
+ import { LiteralUnion } from 'type-fest';
4
+ type Props = {
5
+ /**
6
+ * The element the badge is attached to (rendered before the badge).
7
+ */
8
+ readonly children?: ReactNode;
9
+ /**
10
+ * Badge background color.
11
+ * @default "red"
12
+ */
13
+ readonly color?: LiteralUnion<AnsiColors, string>;
14
+ /**
15
+ * Numeric count. Values above `max` render as `{max}+`. A count of `0`
16
+ * hides the badge unless `showZero` is set.
17
+ */
18
+ readonly count: number;
19
+ /**
20
+ * Upper bound before the count is abbreviated with a trailing `+`.
21
+ * @default 99
22
+ */
23
+ readonly max?: number;
24
+ /**
25
+ * Render the badge even when `count` is `0`.
26
+ * @default false
27
+ */
28
+ readonly showZero?: boolean;
29
+ /**
30
+ * Text color inside the badge.
31
+ * @default "white"
32
+ */
33
+ readonly textColor?: LiteralUnion<AnsiColors, string>;
34
+ };
35
+ /**
36
+ * A small count badge, optionally attached to a child element. Counts above
37
+ * `max` are abbreviated (e.g. `99+`); a zero count is hidden unless `showZero`.
38
+ */
39
+ declare function NotificationBadge({
40
+ children,
41
+ color,
42
+ count,
43
+ max,
44
+ showZero,
45
+ textColor
46
+ }: Props): ReactElement;
47
+ export { NotificationBadge, type Props as NotificationBadgeProps, Props, NotificationBadge as default };
@@ -0,0 +1 @@
1
+ import{jsxs as s,jsx as t}from"react/jsx-runtime";import e from"@visulima/tui/components/box";import f from"@visulima/tui/components/text";function x({children:r,color:n="red",count:o,max:i=99,showZero:c=!1,textColor:d="white"}){const l=o>0||c,a=o>i?`${i}+`:String(o);return s(e,{children:[r,l?t(e,{marginLeft:r===void 0?0:1,children:t(f,{backgroundColor:n,color:d,children:` ${a} `})}):void 0]})}export{x as NotificationBadge,x as default};
@@ -0,0 +1,85 @@
1
+ import { AnsiColors } from '@visulima/colorize';
2
+ import { ReactElement } from 'react';
3
+ import { LiteralUnion } from 'type-fest';
4
+ type Props = {
5
+ /**
6
+ * Accent color for the border/caret when focused.
7
+ * @default "blue"
8
+ */
9
+ readonly accentColor?: LiteralUnion<AnsiColors, string>;
10
+ /**
11
+ * Auto-focus the input on mount.
12
+ */
13
+ readonly autoFocus?: boolean;
14
+ /**
15
+ * Initial value when uncontrolled.
16
+ */
17
+ readonly defaultValue?: number;
18
+ /**
19
+ * Disable input and dim the display.
20
+ */
21
+ readonly isDisabled?: boolean;
22
+ /**
23
+ * Upper bound. Values are clamped on commit and by the stepper.
24
+ */
25
+ readonly max?: number;
26
+ /**
27
+ * Lower bound. Values are clamped on commit and by the stepper.
28
+ */
29
+ readonly min?: number;
30
+ /**
31
+ * Fires whenever the numeric value changes.
32
+ */
33
+ readonly onChange?: (value: number) => void;
34
+ /**
35
+ * Fires when the user presses Enter.
36
+ */
37
+ readonly onSubmit?: (value: number) => void;
38
+ /**
39
+ * Placeholder rendered when the field is empty.
40
+ * @default "0"
41
+ */
42
+ readonly placeholder?: string;
43
+ /**
44
+ * Number of decimal places to allow and display. `0` restricts input to
45
+ * integers.
46
+ * @default 0
47
+ */
48
+ readonly precision?: number;
49
+ /**
50
+ * Show ▲/▼ stepper hints and let ↑/↓ adjust by `step`.
51
+ * @default true
52
+ */
53
+ readonly showControls?: boolean;
54
+ /**
55
+ * Increment applied by ↑/↓ (and Shift for 10× via PageUp/PageDown).
56
+ * @default 1
57
+ */
58
+ readonly step?: number;
59
+ /**
60
+ * Controlled value. When provided, `defaultValue` is ignored.
61
+ */
62
+ readonly value?: number;
63
+ };
64
+ /**
65
+ * A keyboard-driven numeric input. Type digits (and `-`/`.` where allowed),
66
+ * use ↑/↓ to step by `step`, PageUp/PageDown for 10× steps, and Enter to
67
+ * submit. The raw text buffer is validated so partial entries like `-` or
68
+ * `1.` are allowed while typing but clamped/parsed on change.
69
+ */
70
+ declare function NumberInput({
71
+ accentColor,
72
+ autoFocus,
73
+ defaultValue,
74
+ isDisabled,
75
+ max,
76
+ min,
77
+ onChange,
78
+ onSubmit,
79
+ placeholder,
80
+ precision,
81
+ showControls,
82
+ step,
83
+ value: controlledValue
84
+ }: Props): ReactElement;
85
+ export { NumberInput, type Props as NumberInputProps, Props, NumberInput as default };
@@ -0,0 +1 @@
1
+ import{jsxs as L,jsx as b}from"react/jsx-runtime";import R from"@visulima/tui/components/box";import C from"@visulima/tui/components/text";import O from"@visulima/tui/hooks/use-focus";import P from"@visulima/tui/hooks/use-input";import{useState as V,useRef as X,useCallback as A}from"react";const F=(e,o,u)=>{let t=e;return o!==void 0&&t<o&&(t=o),u!==void 0&&t>u&&(t=u),t},S=(e,o)=>{const u=10**o;return Math.round(e*u)/u},q=/^\d$/;function D(e,o){return o>0?e.toFixed(o):String(e)}function K({accentColor:e="blue",autoFocus:o=!1,defaultValue:u,isDisabled:t=!1,max:h,min:s,onChange:j,onSubmit:k,placeholder:U="0",precision:i=0,showControls:$=!0,step:p=1,value:B}){const{isFocused:x}=O({autoFocus:o,isActive:!t}),l=B!==void 0,[w,v]=V(u===void 0?"":D(u,i)),g=X(j);g.current=j;const n=l?D(B,i):w,a=A(d=>{const r=F(S(d,i),s,h);return l||v(D(r,i)),g.current?.(r),r},[l,h,s,i]),c=A(d=>{const r=Number.parseFloat(d);return Number.isFinite(r)?r:0},[]),y=A((d,r)=>{if(r.upArrow){a(c(n)+p);return}if(r.downArrow){a(c(n)-p);return}if(r.pageUp){a(c(n)+p*10);return}if(r.pageDown){a(c(n)-p*10);return}if(r.return){k?.(a(c(n)));return}const m=l?n:w;if(r.backspace||r.delete){const f=m.slice(0,-1);l||v(f),g.current?.(F(S(c(f),i),s,h));return}if(d==="-"&&m.length===0&&(s===void 0||s<0)){l||v("-");return}if(d==="."&&i>0&&!m.includes(".")){l||v(m.length===0?"0.":`${m}.`);return}if(q.test(d)){const f=`${m}${d}`,N=f.indexOf(".");if(N!==-1&&f.length-N-1>i)return;l||v(f),g.current?.(F(S(c(f),i),s,h))}},[w,a,n,l,h,s,k,c,i,p]);P(y,{isActive:x&&!t});const M=n.length===0||n==="-";return L(R,{borderColor:x?e:void 0,borderStyle:"round",paddingX:1,children:[M?b(C,{dimColor:!0,children:U}):b(C,{color:t?void 0:e,dimColor:t,children:n}),x&&!M?b(C,{color:e,children:"▏"}):void 0,$?b(R,{marginLeft:1,children:b(C,{dimColor:!0,children:"▲▼"})}):void 0]})}export{K as NumberInput,K as default};
@@ -0,0 +1,49 @@
1
+ import { ReactNode, ReactElement } from 'react';
2
+ type OperationStatus = "completed" | "error" | "pending" | "running" | "skipped";
3
+ type OperationNode = {
4
+ /**
5
+ * Nested child operations rendered beneath this node.
6
+ */
7
+ readonly children?: ReadonlyArray<OperationNode>;
8
+ /**
9
+ * Optional supplementary info rendered on the next line (dimmed).
10
+ */
11
+ readonly details?: ReactNode;
12
+ /**
13
+ * Wall-clock duration in ms, displayed right-aligned.
14
+ */
15
+ readonly durationMs?: number;
16
+ /**
17
+ * Unique identifier within the tree.
18
+ */
19
+ readonly id: string;
20
+ /**
21
+ * Human-readable label.
22
+ */
23
+ readonly label: ReactNode;
24
+ /**
25
+ * Lifecycle state controlling icon + color.
26
+ */
27
+ readonly status: OperationStatus;
28
+ };
29
+ type Props = {
30
+ /**
31
+ * Tree of operations. Each node can have its own children.
32
+ */
33
+ readonly nodes: ReadonlyArray<OperationNode>;
34
+ /**
35
+ * When true, runs the running-spinner animation.
36
+ * @default true
37
+ */
38
+ readonly showSpinner?: boolean;
39
+ };
40
+ /**
41
+ * Renders a tree of operations with per-node status. Perfect for agent
42
+ * progress panels (reading → editing → running tests → done).
43
+ * @returns A `ReactElement` rendering the nested tree of operation rows.
44
+ */
45
+ declare function OperationTree({
46
+ nodes,
47
+ showSpinner
48
+ }: Props): ReactElement;
49
+ export { OperationNode, OperationStatus, OperationTree, type Props as OperationTreeProps, Props, OperationTree as default };