@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.
- package/CHANGELOG.md +6 -0
- package/LICENSE.md +2095 -0
- package/README.md +115 -29
- package/dist/accordion.d.ts +59 -0
- package/dist/accordion.js +1 -0
- package/dist/alert.d.ts +26 -0
- package/dist/alert.js +1 -0
- package/dist/animate-presence.d.ts +18 -0
- package/dist/animate-presence.js +1 -0
- package/dist/approval-prompt.d.ts +65 -0
- package/dist/approval-prompt.js +1 -0
- package/dist/area-chart.d.ts +66 -0
- package/dist/area-chart.js +1 -0
- package/dist/badge.d.ts +22 -0
- package/dist/badge.js +1 -0
- package/dist/bar-chart.d.ts +67 -0
- package/dist/bar-chart.js +1 -0
- package/dist/big-text.d.ts +81 -0
- package/dist/big-text.js +1 -0
- package/dist/blink-dot.d.ts +36 -0
- package/dist/blink-dot.js +1 -0
- package/dist/breadcrumb.d.ts +33 -0
- package/dist/breadcrumb.js +1 -0
- package/dist/button.d.ts +45 -0
- package/dist/button.js +1 -0
- package/dist/calendar.d.ts +72 -0
- package/dist/calendar.js +1 -0
- package/dist/card.d.ts +57 -0
- package/dist/card.js +1 -0
- package/dist/checkbox-group.d.ts +59 -0
- package/dist/checkbox-group.js +1 -0
- package/dist/checkbox.d.ts +48 -0
- package/dist/checkbox.js +1 -0
- package/dist/code.d.ts +48 -0
- package/dist/code.js +2 -0
- package/dist/collapsible.d.ts +53 -0
- package/dist/collapsible.js +1 -0
- package/dist/color-picker.d.ts +71 -0
- package/dist/color-picker.js +1 -0
- package/dist/command-block.d.ts +47 -0
- package/dist/command-block.js +4 -0
- package/dist/command-palette.d.ts +82 -0
- package/dist/command-palette.js +1 -0
- package/dist/confirm-dialog.d.ts +75 -0
- package/dist/confirm-dialog.js +1 -0
- package/dist/confirm-input.d.ts +38 -0
- package/dist/confirm-input.js +1 -0
- package/dist/console-overlay.d.ts +54 -0
- package/dist/console-overlay.js +1 -0
- package/dist/content-switcher.d.ts +57 -0
- package/dist/content-switcher.js +1 -0
- package/dist/controlled-scroll-view.d.ts +3 -0
- package/dist/controlled-scroll-view.js +1 -0
- package/dist/data-grid.d.ts +49 -0
- package/dist/data-grid.js +1 -0
- package/dist/date-picker.d.ts +65 -0
- package/dist/date-picker.js +1 -0
- package/dist/definition-list.d.ts +44 -0
- package/dist/definition-list.js +1 -0
- package/dist/dialog.d.ts +110 -0
- package/dist/dialog.js +1 -0
- package/dist/diff-view.d.ts +85 -0
- package/dist/diff-view.js +1 -0
- package/dist/digits.d.ts +30 -0
- package/dist/digits.js +1 -0
- package/dist/directory-tree.d.ts +51 -0
- package/dist/directory-tree.js +1 -0
- package/dist/divider.d.ts +49 -0
- package/dist/divider.js +1 -0
- package/dist/drawer.d.ts +53 -0
- package/dist/drawer.js +1 -0
- package/dist/file-change.d.ts +40 -0
- package/dist/file-change.js +1 -0
- package/dist/file-picker-entry.d.ts +2 -0
- package/dist/file-picker-entry.js +1 -0
- package/dist/form.d.ts +81 -0
- package/dist/form.js +1 -0
- package/dist/gauge.d.ts +87 -0
- package/dist/gauge.js +1 -0
- package/dist/gradient.d.ts +42 -0
- package/dist/gradient.js +1 -0
- package/dist/grid.d.ts +40 -0
- package/dist/grid.js +1 -0
- package/dist/heading.d.ts +33 -0
- package/dist/heading.js +1 -0
- package/dist/heatmap.d.ts +62 -0
- package/dist/heatmap.js +1 -0
- package/dist/help.d.ts +52 -0
- package/dist/help.js +1 -0
- package/dist/histogram.d.ts +62 -0
- package/dist/histogram.js +1 -0
- package/dist/image.d.ts +26 -0
- package/dist/image.js +1 -0
- package/dist/index.d.ts +125 -0
- package/dist/index.js +1 -0
- package/dist/json.d.ts +58 -0
- package/dist/json.js +1 -0
- package/dist/kbd.d.ts +31 -0
- package/dist/kbd.js +1 -0
- package/dist/line-chart.d.ts +109 -0
- package/dist/line-chart.js +1 -0
- package/dist/link.d.ts +38 -0
- package/dist/link.js +1 -0
- package/dist/loading-indicator.d.ts +29 -0
- package/dist/loading-indicator.js +1 -0
- package/dist/log.d.ts +38 -0
- package/dist/log.js +1 -0
- package/dist/markdown.d.ts +40 -0
- package/dist/markdown.js +1 -0
- package/dist/masked-input.d.ts +50 -0
- package/dist/masked-input.js +1 -0
- package/dist/menu.d.ts +78 -0
- package/dist/menu.js +1 -0
- package/dist/message-bubble.d.ts +43 -0
- package/dist/message-bubble.js +1 -0
- package/dist/model-badge.d.ts +39 -0
- package/dist/model-badge.js +1 -0
- package/dist/model-selector.d.ts +52 -0
- package/dist/model-selector.js +1 -0
- package/dist/multi-progress.d.ts +55 -0
- package/dist/multi-progress.js +1 -0
- package/dist/multi-select.d.ts +65 -0
- package/dist/multi-select.js +1 -0
- package/dist/notification-badge.d.ts +47 -0
- package/dist/notification-badge.js +1 -0
- package/dist/number-input.d.ts +85 -0
- package/dist/number-input.js +1 -0
- package/dist/operation-tree.d.ts +49 -0
- package/dist/operation-tree.js +1 -0
- package/dist/option-list.d.ts +47 -0
- package/dist/option-list.js +1 -0
- package/dist/ordered-list.d.ts +24 -0
- package/dist/ordered-list.js +1 -0
- package/dist/packem_shared/ControlledScrollView-BzlbIUph.js +1 -0
- package/dist/packem_shared/FilePicker-V5H2fb-v.js +1 -0
- package/dist/packem_shared/ScrollBar-C9i3z3Ft.js +1 -0
- package/dist/packem_shared/ScrollBarBox-DZEC8YnV.js +1 -0
- package/dist/packem_shared/ScrollList-Djtb2vVy.js +1 -0
- package/dist/packem_shared/ScrollView-ESxpl6Hy.js +1 -0
- package/dist/packem_shared/TreeNodeMap-cJlYqckB.js +1 -0
- package/dist/packem_shared/TreeView-q0l446NV.js +1 -0
- package/dist/packem_shared/controlled-scroll-view-DerVKKv6.js +1 -0
- package/dist/packem_shared/controlled-scroll-view.d-vF4wevEJ.d.ts +54 -0
- package/dist/packem_shared/file-picker.d-Cie6aAMD.d.ts +104 -0
- package/dist/packem_shared/line-chart-BbhMhcZg.js +1 -0
- package/dist/packem_shared/scroll-bar-box.d-DSBbBsRc.d.ts +30 -0
- package/dist/packem_shared/scroll-bar.d-B4sD1hBR.d.ts +18 -0
- package/dist/packem_shared/scroll-list.d-BP0UZSY4.d.ts +12 -0
- package/dist/packem_shared/scroll-view.d-BqKpI-F-.d.ts +119 -0
- package/dist/packem_shared/treeViewTheme-Dq3ekRAC.js +1 -0
- package/dist/packem_shared/use-tree-view.d-dXTKoDPM.d.ts +350 -0
- package/dist/packem_shared/useTreeView-D0JEqT6W.js +1 -0
- package/dist/packem_shared/useTreeViewState-BiTJakkh.js +1 -0
- package/dist/packem_shared/variant-config-BJqpNo7H.js +1 -0
- package/dist/packem_shared/variant-config.d-D5NnrrQK.d.ts +2 -0
- package/dist/paginator.d.ts +69 -0
- package/dist/paginator.js +1 -0
- package/dist/paragraph.d.ts +38 -0
- package/dist/paragraph.js +1 -0
- package/dist/path-input.d.ts +67 -0
- package/dist/path-input.js +1 -0
- package/dist/pie-chart.d.ts +50 -0
- package/dist/pie-chart.js +1 -0
- package/dist/placeholder.d.ts +55 -0
- package/dist/placeholder.js +1 -0
- package/dist/popover.d.ts +46 -0
- package/dist/popover.js +1 -0
- package/dist/progress-bar.d.ts +51 -0
- package/dist/progress-bar.js +1 -0
- package/dist/progress-circle.d.ts +59 -0
- package/dist/progress-circle.js +1 -0
- package/dist/qr-code.d.ts +45 -0
- package/dist/qr-code.js +1 -0
- package/dist/r/accordion.json +24 -0
- package/dist/r/alert.json +24 -0
- package/dist/r/animate-presence.json +22 -0
- package/dist/r/approval-prompt.json +24 -0
- package/dist/r/area-chart.json +27 -0
- package/dist/r/badge.json +24 -0
- package/dist/r/bar-chart.json +24 -0
- package/dist/r/big-text.json +23 -0
- package/dist/r/blink-dot.json +24 -0
- package/dist/r/breadcrumb.json +24 -0
- package/dist/r/button.json +24 -0
- package/dist/r/calendar.json +24 -0
- package/dist/r/card.json +26 -0
- package/dist/r/chart-utils.json +24 -0
- package/dist/r/checkbox-group.json +24 -0
- package/dist/r/checkbox.json +24 -0
- package/dist/r/code.json +23 -0
- package/dist/r/collapsible.json +24 -0
- package/dist/r/color-picker.json +24 -0
- package/dist/r/command-block.json +26 -0
- package/dist/r/command-palette.json +24 -0
- package/dist/r/confirm-dialog.json +24 -0
- package/dist/r/confirm-input.json +22 -0
- package/dist/r/console-overlay.json +22 -0
- package/dist/r/content-switcher.json +24 -0
- package/dist/r/controlled-scroll-view.json +24 -0
- package/dist/r/data-grid.json +24 -0
- package/dist/r/date-picker.json +26 -0
- package/dist/r/definition-list.json +24 -0
- package/dist/r/dialog.json +24 -0
- package/dist/r/diff-view.json +24 -0
- package/dist/r/digits.json +24 -0
- package/dist/r/directory-tree.json +24 -0
- package/dist/r/divider.json +24 -0
- package/dist/r/drawer.json +24 -0
- package/dist/r/file-change.json +24 -0
- package/dist/r/file-picker.json +44 -0
- package/dist/r/form.json +24 -0
- package/dist/r/gauge.json +24 -0
- package/dist/r/gradient.json +24 -0
- package/dist/r/grid.json +22 -0
- package/dist/r/heading.json +24 -0
- package/dist/r/heatmap.json +24 -0
- package/dist/r/help.json +22 -0
- package/dist/r/histogram.json +26 -0
- package/dist/r/image.json +24 -0
- package/dist/r/json.json +24 -0
- package/dist/r/kbd.json +24 -0
- package/dist/r/line-chart.json +26 -0
- package/dist/r/link.json +23 -0
- package/dist/r/loading-indicator.json +27 -0
- package/dist/r/log.json +24 -0
- package/dist/r/markdown.json +29 -0
- package/dist/r/masked-input.json +22 -0
- package/dist/r/menu.json +24 -0
- package/dist/r/message-bubble.json +24 -0
- package/dist/r/model-badge.json +24 -0
- package/dist/r/model-selector.json +24 -0
- package/dist/r/multi-progress.json +24 -0
- package/dist/r/multi-select.json +24 -0
- package/dist/r/notification-badge.json +24 -0
- package/dist/r/number-input.json +24 -0
- package/dist/r/operation-tree.json +26 -0
- package/dist/r/option-list.json +24 -0
- package/dist/r/ordered-list.json +22 -0
- package/dist/r/paginator.json +22 -0
- package/dist/r/paragraph.json +24 -0
- package/dist/r/path-input.json +26 -0
- package/dist/r/pie-chart.json +24 -0
- package/dist/r/placeholder.json +24 -0
- package/dist/r/popover.json +24 -0
- package/dist/r/progress-bar.json +22 -0
- package/dist/r/progress-circle.json +24 -0
- package/dist/r/qr-code.json +24 -0
- package/dist/r/radio-group.json +24 -0
- package/dist/r/registry.json +1051 -0
- package/dist/r/scatter-plot.json +27 -0
- package/dist/r/scroll-bar-box.json +24 -0
- package/dist/r/scroll-bar.json +24 -0
- package/dist/r/scroll-list.json +24 -0
- package/dist/r/scroll-view.json +24 -0
- package/dist/r/scroll.json +58 -0
- package/dist/r/search-input.json +26 -0
- package/dist/r/select-input-indicator.json +24 -0
- package/dist/r/select-input-item.json +24 -0
- package/dist/r/select-input.json +28 -0
- package/dist/r/shimmer-text.json +24 -0
- package/dist/r/sidebar.json +24 -0
- package/dist/r/slider.json +24 -0
- package/dist/r/sparkline.json +24 -0
- package/dist/r/spinner.json +23 -0
- package/dist/r/status-line.json +24 -0
- package/dist/r/status-message.json +24 -0
- package/dist/r/stepper.json +24 -0
- package/dist/r/stopwatch.json +22 -0
- package/dist/r/streaming-text.json +24 -0
- package/dist/r/switch.json +24 -0
- package/dist/r/tab.json +22 -0
- package/dist/r/table.json +23 -0
- package/dist/r/tabs.json +26 -0
- package/dist/r/tag-input.json +24 -0
- package/dist/r/tag.json +24 -0
- package/dist/r/text-input.json +22 -0
- package/dist/r/textarea.json +22 -0
- package/dist/r/thinking-block.json +24 -0
- package/dist/r/time-picker.json +24 -0
- package/dist/r/timer.json +22 -0
- package/dist/r/toast.json +24 -0
- package/dist/r/token-usage.json +24 -0
- package/dist/r/tool-call.json +24 -0
- package/dist/r/tooltip.json +24 -0
- package/dist/r/transition.json +22 -0
- package/dist/r/tree-select.json +24 -0
- package/dist/r/tree-view.json +64 -0
- package/dist/r/unordered-list.json +22 -0
- package/dist/r/use-linked-scroll.json +24 -0
- package/dist/r/variant-config.json +22 -0
- package/dist/radio-group.d.ts +74 -0
- package/dist/radio-group.js +1 -0
- package/dist/scatter-plot.d.ts +35 -0
- package/dist/scatter-plot.js +1 -0
- package/dist/scroll-bar-box.d.ts +3 -0
- package/dist/scroll-bar-box.js +1 -0
- package/dist/scroll-bar.d.ts +3 -0
- package/dist/scroll-bar.js +1 -0
- package/dist/scroll-list.d.ts +4 -0
- package/dist/scroll-list.js +1 -0
- package/dist/scroll-view.d.ts +3 -0
- package/dist/scroll-view.js +1 -0
- package/dist/search-input.d.ts +55 -0
- package/dist/search-input.js +1 -0
- package/dist/select-input-indicator.d.ts +32 -0
- package/dist/select-input-indicator.js +1 -0
- package/dist/select-input-item.d.ts +41 -0
- package/dist/select-input-item.js +1 -0
- package/dist/select-input.d.ts +110 -0
- package/dist/select-input.js +1 -0
- package/dist/shimmer-text.d.ts +47 -0
- package/dist/shimmer-text.js +1 -0
- package/dist/sidebar.d.ts +47 -0
- package/dist/sidebar.js +1 -0
- package/dist/slider.d.ts +103 -0
- package/dist/slider.js +1 -0
- package/dist/sparkline.d.ts +42 -0
- package/dist/sparkline.js +1 -0
- package/dist/spinner.d.ts +17 -0
- package/dist/spinner.js +1 -0
- package/dist/status-line.d.ts +37 -0
- package/dist/status-line.js +1 -0
- package/dist/status-message.d.ts +21 -0
- package/dist/status-message.js +1 -0
- package/dist/stepper.d.ts +48 -0
- package/dist/stepper.js +1 -0
- package/dist/stopwatch.d.ts +49 -0
- package/dist/stopwatch.js +1 -0
- package/dist/streaming-text.d.ts +46 -0
- package/dist/streaming-text.js +1 -0
- package/dist/switch.d.ts +55 -0
- package/dist/switch.js +1 -0
- package/dist/tab.d.ts +19 -0
- package/dist/tab.js +1 -0
- package/dist/table.d.ts +119 -0
- package/dist/table.js +1 -0
- package/dist/tabs.d.ts +107 -0
- package/dist/tabs.js +1 -0
- package/dist/tag-input.d.ts +71 -0
- package/dist/tag-input.js +1 -0
- package/dist/tag.d.ts +35 -0
- package/dist/tag.js +1 -0
- package/dist/text-input.d.ts +48 -0
- package/dist/text-input.js +1 -0
- package/dist/textarea.d.ts +68 -0
- package/dist/textarea.js +2 -0
- package/dist/thinking-block.d.ts +57 -0
- package/dist/thinking-block.js +1 -0
- package/dist/time-picker.d.ts +67 -0
- package/dist/time-picker.js +1 -0
- package/dist/timer.d.ts +59 -0
- package/dist/timer.js +1 -0
- package/dist/toast.d.ts +49 -0
- package/dist/toast.js +1 -0
- package/dist/token-usage.d.ts +50 -0
- package/dist/token-usage.js +1 -0
- package/dist/tool-call.d.ts +33 -0
- package/dist/tool-call.js +1 -0
- package/dist/tooltip.d.ts +43 -0
- package/dist/tooltip.js +1 -0
- package/dist/transition.d.ts +74 -0
- package/dist/transition.js +1 -0
- package/dist/tree-select.d.ts +65 -0
- package/dist/tree-select.js +1 -0
- package/dist/tree-view-entry.d.ts +4 -0
- package/dist/tree-view-entry.js +1 -0
- package/dist/unordered-list.d.ts +30 -0
- package/dist/unordered-list.js +1 -0
- package/dist/use-linked-scroll.d.ts +42 -0
- package/dist/use-linked-scroll.js +1 -0
- 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 };
|
package/dist/markdown.js
ADDED
|
@@ -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 };
|