termui 0.1.0 → 0.1.2

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 (257) hide show
  1. package/dist/chunk-B2VO7M2O.js +3493 -0
  2. package/dist/chunk-B2VO7M2O.js.map +1 -0
  3. package/dist/chunk-ZZKFZA2U.js +694 -0
  4. package/dist/chunk-ZZKFZA2U.js.map +1 -0
  5. package/dist/components.d.ts +668 -0
  6. package/dist/components.js +130 -0
  7. package/dist/components.js.map +1 -0
  8. package/dist/core.d.ts +144 -0
  9. package/dist/core.js +99 -0
  10. package/dist/core.js.map +1 -0
  11. package/dist/hooks-YADffFjU.d.ts +237 -0
  12. package/dist/hooks.d.ts +3 -0
  13. package/dist/hooks.js +29 -0
  14. package/dist/hooks.js.map +1 -0
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.js +225 -0
  17. package/dist/index.js.map +1 -0
  18. package/package.json +41 -10
  19. package/packages/cli/package.json +0 -36
  20. package/packages/components/dist/data/List.d.ts +0 -15
  21. package/packages/components/dist/data/List.d.ts.map +0 -1
  22. package/packages/components/dist/data/List.js +0 -40
  23. package/packages/components/dist/data/List.js.map +0 -1
  24. package/packages/components/dist/data/Table.d.ts +0 -17
  25. package/packages/components/dist/data/Table.d.ts.map +0 -1
  26. package/packages/components/dist/data/Table.js +0 -74
  27. package/packages/components/dist/data/Table.js.map +0 -1
  28. package/packages/components/dist/data/index.d.ts +0 -5
  29. package/packages/components/dist/data/index.d.ts.map +0 -1
  30. package/packages/components/dist/data/index.js +0 -3
  31. package/packages/components/dist/data/index.js.map +0 -1
  32. package/packages/components/dist/feedback/Alert.d.ts +0 -10
  33. package/packages/components/dist/feedback/Alert.d.ts.map +0 -1
  34. package/packages/components/dist/feedback/Alert.js +0 -23
  35. package/packages/components/dist/feedback/Alert.js.map +0 -1
  36. package/packages/components/dist/feedback/ProgressBar.d.ts +0 -13
  37. package/packages/components/dist/feedback/ProgressBar.d.ts.map +0 -1
  38. package/packages/components/dist/feedback/ProgressBar.js +0 -13
  39. package/packages/components/dist/feedback/ProgressBar.js.map +0 -1
  40. package/packages/components/dist/feedback/Spinner.d.ts +0 -9
  41. package/packages/components/dist/feedback/Spinner.d.ts.map +0 -1
  42. package/packages/components/dist/feedback/Spinner.js +0 -26
  43. package/packages/components/dist/feedback/Spinner.js.map +0 -1
  44. package/packages/components/dist/feedback/index.d.ts +0 -7
  45. package/packages/components/dist/feedback/index.d.ts.map +0 -1
  46. package/packages/components/dist/feedback/index.js +0 -4
  47. package/packages/components/dist/feedback/index.js.map +0 -1
  48. package/packages/components/dist/forms/Form.d.ts +0 -22
  49. package/packages/components/dist/forms/Form.d.ts.map +0 -1
  50. package/packages/components/dist/forms/Form.js +0 -45
  51. package/packages/components/dist/forms/Form.js.map +0 -1
  52. package/packages/components/dist/forms/index.d.ts +0 -3
  53. package/packages/components/dist/forms/index.d.ts.map +0 -1
  54. package/packages/components/dist/forms/index.js +0 -2
  55. package/packages/components/dist/forms/index.js.map +0 -1
  56. package/packages/components/dist/index.d.ts +0 -11
  57. package/packages/components/dist/index.d.ts.map +0 -1
  58. package/packages/components/dist/index.js +0 -21
  59. package/packages/components/dist/index.js.map +0 -1
  60. package/packages/components/dist/input/TextInput.d.ts +0 -14
  61. package/packages/components/dist/input/TextInput.d.ts.map +0 -1
  62. package/packages/components/dist/input/TextInput.js +0 -44
  63. package/packages/components/dist/input/TextInput.js.map +0 -1
  64. package/packages/components/dist/input/index.d.ts +0 -3
  65. package/packages/components/dist/input/index.d.ts.map +0 -1
  66. package/packages/components/dist/input/index.js +0 -2
  67. package/packages/components/dist/input/index.js.map +0 -1
  68. package/packages/components/dist/layout/Box.d.ts +0 -11
  69. package/packages/components/dist/layout/Box.d.ts.map +0 -1
  70. package/packages/components/dist/layout/Box.js +0 -18
  71. package/packages/components/dist/layout/Box.js.map +0 -1
  72. package/packages/components/dist/layout/Grid.d.ts +0 -8
  73. package/packages/components/dist/layout/Grid.d.ts.map +0 -1
  74. package/packages/components/dist/layout/Grid.js +0 -12
  75. package/packages/components/dist/layout/Grid.js.map +0 -1
  76. package/packages/components/dist/layout/ScrollView.d.ts +0 -9
  77. package/packages/components/dist/layout/ScrollView.d.ts.map +0 -1
  78. package/packages/components/dist/layout/ScrollView.js +0 -26
  79. package/packages/components/dist/layout/ScrollView.js.map +0 -1
  80. package/packages/components/dist/layout/Stack.d.ts +0 -12
  81. package/packages/components/dist/layout/Stack.d.ts.map +0 -1
  82. package/packages/components/dist/layout/Stack.js +0 -6
  83. package/packages/components/dist/layout/Stack.js.map +0 -1
  84. package/packages/components/dist/layout/index.d.ts +0 -9
  85. package/packages/components/dist/layout/index.d.ts.map +0 -1
  86. package/packages/components/dist/layout/index.js +0 -5
  87. package/packages/components/dist/layout/index.js.map +0 -1
  88. package/packages/components/dist/navigation/Tabs.d.ts +0 -15
  89. package/packages/components/dist/navigation/Tabs.d.ts.map +0 -1
  90. package/packages/components/dist/navigation/Tabs.js +0 -34
  91. package/packages/components/dist/navigation/Tabs.js.map +0 -1
  92. package/packages/components/dist/navigation/index.d.ts +0 -3
  93. package/packages/components/dist/navigation/index.d.ts.map +0 -1
  94. package/packages/components/dist/navigation/index.js +0 -2
  95. package/packages/components/dist/navigation/index.js.map +0 -1
  96. package/packages/components/dist/overlays/Modal.d.ts +0 -10
  97. package/packages/components/dist/overlays/Modal.d.ts.map +0 -1
  98. package/packages/components/dist/overlays/Modal.js +0 -16
  99. package/packages/components/dist/overlays/Modal.js.map +0 -1
  100. package/packages/components/dist/overlays/index.d.ts +0 -3
  101. package/packages/components/dist/overlays/index.d.ts.map +0 -1
  102. package/packages/components/dist/overlays/index.js +0 -2
  103. package/packages/components/dist/overlays/index.js.map +0 -1
  104. package/packages/components/dist/selection/Checkbox.d.ts +0 -10
  105. package/packages/components/dist/selection/Checkbox.d.ts.map +0 -1
  106. package/packages/components/dist/selection/Checkbox.js +0 -26
  107. package/packages/components/dist/selection/Checkbox.js.map +0 -1
  108. package/packages/components/dist/selection/Select.d.ts +0 -17
  109. package/packages/components/dist/selection/Select.d.ts.map +0 -1
  110. package/packages/components/dist/selection/Select.js +0 -44
  111. package/packages/components/dist/selection/Select.js.map +0 -1
  112. package/packages/components/dist/selection/index.d.ts +0 -5
  113. package/packages/components/dist/selection/index.d.ts.map +0 -1
  114. package/packages/components/dist/selection/index.js +0 -3
  115. package/packages/components/dist/selection/index.js.map +0 -1
  116. package/packages/components/dist/typography/Badge.d.ts +0 -9
  117. package/packages/components/dist/typography/Badge.d.ts.map +0 -1
  118. package/packages/components/dist/typography/Badge.js +0 -18
  119. package/packages/components/dist/typography/Badge.js.map +0 -1
  120. package/packages/components/dist/typography/Text.d.ts +0 -15
  121. package/packages/components/dist/typography/Text.d.ts.map +0 -1
  122. package/packages/components/dist/typography/Text.js +0 -6
  123. package/packages/components/dist/typography/Text.js.map +0 -1
  124. package/packages/components/dist/typography/index.d.ts +0 -5
  125. package/packages/components/dist/typography/index.d.ts.map +0 -1
  126. package/packages/components/dist/typography/index.js +0 -3
  127. package/packages/components/dist/typography/index.js.map +0 -1
  128. package/packages/components/dist/utility/Panel.d.ts +0 -14
  129. package/packages/components/dist/utility/Panel.d.ts.map +0 -1
  130. package/packages/components/dist/utility/Panel.js +0 -8
  131. package/packages/components/dist/utility/Panel.js.map +0 -1
  132. package/packages/components/dist/utility/Toggle.d.ts +0 -11
  133. package/packages/components/dist/utility/Toggle.d.ts.map +0 -1
  134. package/packages/components/dist/utility/Toggle.js +0 -23
  135. package/packages/components/dist/utility/Toggle.js.map +0 -1
  136. package/packages/components/dist/utility/index.d.ts +0 -5
  137. package/packages/components/dist/utility/index.d.ts.map +0 -1
  138. package/packages/components/dist/utility/index.js +0 -3
  139. package/packages/components/dist/utility/index.js.map +0 -1
  140. package/packages/components/package.json +0 -36
  141. package/packages/core/dist/hooks/index.d.ts +0 -18
  142. package/packages/core/dist/hooks/index.d.ts.map +0 -1
  143. package/packages/core/dist/hooks/index.js +0 -13
  144. package/packages/core/dist/hooks/index.js.map +0 -1
  145. package/packages/core/dist/hooks/useAnimation.d.ts +0 -7
  146. package/packages/core/dist/hooks/useAnimation.d.ts.map +0 -1
  147. package/packages/core/dist/hooks/useAnimation.js +0 -40
  148. package/packages/core/dist/hooks/useAnimation.js.map +0 -1
  149. package/packages/core/dist/hooks/useAsync.d.ts +0 -25
  150. package/packages/core/dist/hooks/useAsync.d.ts.map +0 -1
  151. package/packages/core/dist/hooks/useAsync.js +0 -37
  152. package/packages/core/dist/hooks/useAsync.js.map +0 -1
  153. package/packages/core/dist/hooks/useClipboard.d.ts +0 -9
  154. package/packages/core/dist/hooks/useClipboard.d.ts.map +0 -1
  155. package/packages/core/dist/hooks/useClipboard.js +0 -14
  156. package/packages/core/dist/hooks/useClipboard.js.map +0 -1
  157. package/packages/core/dist/hooks/useFocus.d.ts +0 -10
  158. package/packages/core/dist/hooks/useFocus.d.ts.map +0 -1
  159. package/packages/core/dist/hooks/useFocus.js +0 -6
  160. package/packages/core/dist/hooks/useFocus.js.map +0 -1
  161. package/packages/core/dist/hooks/useFocusManager.d.ts +0 -9
  162. package/packages/core/dist/hooks/useFocusManager.d.ts.map +0 -1
  163. package/packages/core/dist/hooks/useFocusManager.js +0 -6
  164. package/packages/core/dist/hooks/useFocusManager.js.map +0 -1
  165. package/packages/core/dist/hooks/useInput.d.ts +0 -22
  166. package/packages/core/dist/hooks/useInput.d.ts.map +0 -1
  167. package/packages/core/dist/hooks/useInput.js +0 -6
  168. package/packages/core/dist/hooks/useInput.js.map +0 -1
  169. package/packages/core/dist/hooks/useInterval.d.ts +0 -6
  170. package/packages/core/dist/hooks/useInterval.d.ts.map +0 -1
  171. package/packages/core/dist/hooks/useInterval.js +0 -18
  172. package/packages/core/dist/hooks/useInterval.js.map +0 -1
  173. package/packages/core/dist/hooks/useKeymap.d.ts +0 -13
  174. package/packages/core/dist/hooks/useKeymap.d.ts.map +0 -1
  175. package/packages/core/dist/hooks/useKeymap.js +0 -36
  176. package/packages/core/dist/hooks/useKeymap.js.map +0 -1
  177. package/packages/core/dist/hooks/useMouse.d.ts +0 -19
  178. package/packages/core/dist/hooks/useMouse.d.ts.map +0 -1
  179. package/packages/core/dist/hooks/useMouse.js +0 -60
  180. package/packages/core/dist/hooks/useMouse.js.map +0 -1
  181. package/packages/core/dist/hooks/useResize.d.ts +0 -7
  182. package/packages/core/dist/hooks/useResize.d.ts.map +0 -1
  183. package/packages/core/dist/hooks/useResize.js +0 -27
  184. package/packages/core/dist/hooks/useResize.js.map +0 -1
  185. package/packages/core/dist/hooks/useTerminal.d.ts +0 -4
  186. package/packages/core/dist/hooks/useTerminal.d.ts.map +0 -1
  187. package/packages/core/dist/hooks/useTerminal.js +0 -17
  188. package/packages/core/dist/hooks/useTerminal.js.map +0 -1
  189. package/packages/core/dist/hooks/useTheme.d.ts +0 -2
  190. package/packages/core/dist/hooks/useTheme.d.ts.map +0 -1
  191. package/packages/core/dist/hooks/useTheme.js +0 -3
  192. package/packages/core/dist/hooks/useTheme.js.map +0 -1
  193. package/packages/core/dist/index.d.ts +0 -4
  194. package/packages/core/dist/index.d.ts.map +0 -1
  195. package/packages/core/dist/index.js +0 -7
  196. package/packages/core/dist/index.js.map +0 -1
  197. package/packages/core/dist/styling/ThemeProvider.d.ts +0 -15
  198. package/packages/core/dist/styling/ThemeProvider.d.ts.map +0 -1
  199. package/packages/core/dist/styling/ThemeProvider.js +0 -44
  200. package/packages/core/dist/styling/ThemeProvider.js.map +0 -1
  201. package/packages/core/dist/styling/index.d.ts +0 -7
  202. package/packages/core/dist/styling/index.d.ts.map +0 -1
  203. package/packages/core/dist/styling/index.js +0 -7
  204. package/packages/core/dist/styling/index.js.map +0 -1
  205. package/packages/core/dist/styling/style.d.ts +0 -25
  206. package/packages/core/dist/styling/style.d.ts.map +0 -1
  207. package/packages/core/dist/styling/style.js +0 -57
  208. package/packages/core/dist/styling/style.js.map +0 -1
  209. package/packages/core/dist/styling/themes/default.d.ts +0 -3
  210. package/packages/core/dist/styling/themes/default.d.ts.map +0 -1
  211. package/packages/core/dist/styling/themes/default.js +0 -49
  212. package/packages/core/dist/styling/themes/default.js.map +0 -1
  213. package/packages/core/dist/styling/themes/dracula.d.ts +0 -3
  214. package/packages/core/dist/styling/themes/dracula.d.ts.map +0 -1
  215. package/packages/core/dist/styling/themes/dracula.js +0 -49
  216. package/packages/core/dist/styling/themes/dracula.js.map +0 -1
  217. package/packages/core/dist/styling/themes/nord.d.ts +0 -3
  218. package/packages/core/dist/styling/themes/nord.d.ts.map +0 -1
  219. package/packages/core/dist/styling/themes/nord.js +0 -49
  220. package/packages/core/dist/styling/themes/nord.js.map +0 -1
  221. package/packages/core/dist/styling/tokens.d.ts +0 -57
  222. package/packages/core/dist/styling/tokens.d.ts.map +0 -1
  223. package/packages/core/dist/styling/tokens.js +0 -6
  224. package/packages/core/dist/styling/tokens.js.map +0 -1
  225. package/packages/core/dist/terminal/ansi.d.ts +0 -107
  226. package/packages/core/dist/terminal/ansi.d.ts.map +0 -1
  227. package/packages/core/dist/terminal/ansi.js +0 -153
  228. package/packages/core/dist/terminal/ansi.js.map +0 -1
  229. package/packages/core/dist/terminal/capabilities.d.ts +0 -21
  230. package/packages/core/dist/terminal/capabilities.d.ts.map +0 -1
  231. package/packages/core/dist/terminal/capabilities.js +0 -74
  232. package/packages/core/dist/terminal/capabilities.js.map +0 -1
  233. package/packages/core/dist/terminal/index.d.ts +0 -3
  234. package/packages/core/dist/terminal/index.d.ts.map +0 -1
  235. package/packages/core/dist/terminal/index.js +0 -3
  236. package/packages/core/dist/terminal/index.js.map +0 -1
  237. package/packages/core/package.json +0 -49
  238. package/registry/components/alert/meta.json +0 -9
  239. package/registry/components/badge/meta.json +0 -9
  240. package/registry/components/box/meta.json +0 -9
  241. package/registry/components/checkbox/meta.json +0 -9
  242. package/registry/components/form/meta.json +0 -9
  243. package/registry/components/grid/meta.json +0 -9
  244. package/registry/components/list/meta.json +0 -9
  245. package/registry/components/modal/meta.json +0 -9
  246. package/registry/components/panel/meta.json +0 -9
  247. package/registry/components/progress-bar/meta.json +0 -9
  248. package/registry/components/scroll-view/meta.json +0 -9
  249. package/registry/components/select/meta.json +0 -9
  250. package/registry/components/spinner/meta.json +0 -9
  251. package/registry/components/stack/meta.json +0 -9
  252. package/registry/components/table/meta.json +0 -9
  253. package/registry/components/tabs/meta.json +0 -9
  254. package/registry/components/text/meta.json +0 -9
  255. package/registry/components/text-input/meta.json +0 -9
  256. package/registry/components/toggle/meta.json +0 -9
  257. package/registry/schema.json +0 -178
@@ -0,0 +1,3493 @@
1
+ import {
2
+ useAnimation,
3
+ useFocus,
4
+ useInput,
5
+ useInterval,
6
+ useTheme
7
+ } from "./chunk-ZZKFZA2U.js";
8
+
9
+ // packages/components/src/layout/Box.tsx
10
+ import { Box as InkBox } from "ink";
11
+ import { jsx } from "react/jsx-runtime";
12
+ function Box({
13
+ border,
14
+ borderVariant = "default",
15
+ borderColor,
16
+ children,
17
+ ...props
18
+ }) {
19
+ const theme = useTheme();
20
+ const resolvedBorderColor = borderColor ?? (() => {
21
+ switch (borderVariant) {
22
+ case "focus":
23
+ return theme.colors.focusRing;
24
+ case "success":
25
+ return theme.colors.success;
26
+ case "error":
27
+ return theme.colors.error;
28
+ case "warning":
29
+ return theme.colors.warning;
30
+ case "muted":
31
+ return theme.colors.mutedForeground;
32
+ default:
33
+ return theme.colors.border;
34
+ }
35
+ })();
36
+ return /* @__PURE__ */ jsx(
37
+ InkBox,
38
+ {
39
+ borderStyle: border ? props.borderStyle ?? theme.border.style : void 0,
40
+ borderColor: border ? resolvedBorderColor : void 0,
41
+ ...props,
42
+ children
43
+ }
44
+ );
45
+ }
46
+
47
+ // packages/components/src/layout/Stack.tsx
48
+ import { Box as Box2 } from "ink";
49
+ import { jsx as jsx2 } from "react/jsx-runtime";
50
+ function Stack({
51
+ direction = "vertical",
52
+ gap = 0,
53
+ children,
54
+ width,
55
+ height,
56
+ alignItems,
57
+ justifyContent
58
+ }) {
59
+ return /* @__PURE__ */ jsx2(
60
+ Box2,
61
+ {
62
+ flexDirection: direction === "vertical" ? "column" : "row",
63
+ gap,
64
+ width,
65
+ height,
66
+ alignItems,
67
+ justifyContent,
68
+ children
69
+ }
70
+ );
71
+ }
72
+
73
+ // packages/components/src/layout/Grid.tsx
74
+ import { Children } from "react";
75
+ import { Box as Box3 } from "ink";
76
+ import { jsx as jsx3 } from "react/jsx-runtime";
77
+ function Grid({ columns, gap = 0, children }) {
78
+ const items = Children.toArray(children);
79
+ const rows = [];
80
+ for (let i = 0; i < items.length; i += columns) {
81
+ rows.push(items.slice(i, i + columns));
82
+ }
83
+ return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", gap, children: rows.map((row, rowIdx) => /* @__PURE__ */ jsx3(Box3, { flexDirection: "row", gap, children: row.map((cell, colIdx) => /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, children: cell }, colIdx)) }, rowIdx)) });
84
+ }
85
+
86
+ // packages/components/src/layout/ScrollView.tsx
87
+ import { useState } from "react";
88
+ import { Box as Box4, Text } from "ink";
89
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
90
+ function ScrollView({
91
+ height,
92
+ children,
93
+ showScrollbar = true,
94
+ scrollbarColor = "gray"
95
+ }) {
96
+ const [scrollTop, setScrollTop] = useState(0);
97
+ useInput((input, key) => {
98
+ if (key.upArrow) {
99
+ setScrollTop((s) => Math.max(0, s - 1));
100
+ } else if (key.downArrow) {
101
+ setScrollTop((s) => s + 1);
102
+ } else if (key.pageUp) {
103
+ setScrollTop((s) => Math.max(0, s - height));
104
+ } else if (key.pageDown) {
105
+ setScrollTop((s) => s + height);
106
+ }
107
+ });
108
+ return /* @__PURE__ */ jsxs(Box4, { flexDirection: "row", height, overflow: "hidden", children: [
109
+ /* @__PURE__ */ jsx4(Box4, { flexGrow: 1, flexDirection: "column", marginTop: -scrollTop, children }),
110
+ showScrollbar && /* @__PURE__ */ jsx4(Box4, { width: 1, flexDirection: "column", height, children: /* @__PURE__ */ jsx4(Text, { color: scrollbarColor, children: "\u2502".repeat(height) }) })
111
+ ] });
112
+ }
113
+
114
+ // packages/components/src/layout/Divider.tsx
115
+ import { Box as Box5, Text as Text2 } from "ink";
116
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
117
+ var HORIZONTAL_CHARS = {
118
+ single: "\u2500",
119
+ double: "\u2550",
120
+ bold: "\u2501"
121
+ };
122
+ var VERTICAL_CHARS = {
123
+ single: "\u2502",
124
+ double: "\u2551",
125
+ bold: "\u2503"
126
+ };
127
+ function Divider({
128
+ variant = "single",
129
+ orientation = "horizontal",
130
+ color,
131
+ label,
132
+ height = 1,
133
+ width
134
+ }) {
135
+ const theme = useTheme();
136
+ const resolvedColor = color ?? theme.colors.border;
137
+ const hChar = HORIZONTAL_CHARS[variant];
138
+ const vChar = VERTICAL_CHARS[variant];
139
+ if (orientation === "vertical") {
140
+ const lines = Array.from({ length: height }, (_, i) => i);
141
+ return /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: lines.map((i) => /* @__PURE__ */ jsx5(Text2, { color: resolvedColor, children: vChar }, i)) });
142
+ }
143
+ if (label) {
144
+ return /* @__PURE__ */ jsxs2(Box5, { flexDirection: "row", width, children: [
145
+ /* @__PURE__ */ jsxs2(Text2, { color: resolvedColor, children: [
146
+ hChar,
147
+ hChar,
148
+ hChar,
149
+ " "
150
+ ] }),
151
+ /* @__PURE__ */ jsx5(Text2, { color: resolvedColor, children: label }),
152
+ /* @__PURE__ */ jsxs2(Text2, { color: resolvedColor, children: [
153
+ " ",
154
+ hChar,
155
+ hChar,
156
+ hChar
157
+ ] }),
158
+ /* @__PURE__ */ jsx5(Box5, { flexGrow: 1, children: /* @__PURE__ */ jsx5(Text2, { color: resolvedColor, children: hChar.repeat(1) }) })
159
+ ] });
160
+ }
161
+ return /* @__PURE__ */ jsx5(Box5, { width: width ?? "100%", children: /* @__PURE__ */ jsx5(Text2, { color: resolvedColor, wrap: "truncate", children: hChar.repeat(width ?? 80) }) });
162
+ }
163
+
164
+ // packages/components/src/layout/Spacer.tsx
165
+ import { Box as Box6 } from "ink";
166
+ import { jsx as jsx6 } from "react/jsx-runtime";
167
+ function Spacer({ size, direction = "horizontal" }) {
168
+ if (size === void 0) {
169
+ return /* @__PURE__ */ jsx6(Box6, { flexGrow: 1 });
170
+ }
171
+ if (direction === "vertical") {
172
+ return /* @__PURE__ */ jsx6(Box6, { height: size });
173
+ }
174
+ return /* @__PURE__ */ jsx6(Box6, { width: size });
175
+ }
176
+
177
+ // packages/components/src/layout/Columns.tsx
178
+ import React3 from "react";
179
+ import { Box as Box7 } from "ink";
180
+ import { jsx as jsx7 } from "react/jsx-runtime";
181
+ var ALIGN_MAP = {
182
+ top: "flex-start",
183
+ center: "center",
184
+ bottom: "flex-end"
185
+ };
186
+ function Columns({ children, gap = 0, align = "top" }) {
187
+ const items = React3.Children.toArray(children);
188
+ return /* @__PURE__ */ jsx7(Box7, { flexDirection: "row", gap, alignItems: ALIGN_MAP[align], children: items.map((child, index) => /* @__PURE__ */ jsx7(Box7, { flexGrow: 1, flexDirection: "column", children: child }, index)) });
189
+ }
190
+
191
+ // packages/components/src/layout/Center.tsx
192
+ import { Box as Box8 } from "ink";
193
+ import { jsx as jsx8 } from "react/jsx-runtime";
194
+ function Center({ children, axis = "both" }) {
195
+ const justifyContent = axis === "both" || axis === "horizontal" ? "center" : void 0;
196
+ const alignItems = axis === "both" || axis === "vertical" ? "center" : void 0;
197
+ return /* @__PURE__ */ jsx8(
198
+ Box8,
199
+ {
200
+ flexGrow: 1,
201
+ justifyContent,
202
+ alignItems,
203
+ children
204
+ }
205
+ );
206
+ }
207
+
208
+ // packages/components/src/layout/AspectRatio.tsx
209
+ import { Box as Box9 } from "ink";
210
+ import { jsx as jsx9 } from "react/jsx-runtime";
211
+ function AspectRatio({ children, ratio = 16 / 9, width = 80 }) {
212
+ const height = Math.round(width / ratio / 2);
213
+ return /* @__PURE__ */ jsx9(Box9, { width, height, overflow: "hidden", children });
214
+ }
215
+
216
+ // packages/components/src/typography/Text.tsx
217
+ import { Text as InkText } from "ink";
218
+ import { jsx as jsx10 } from "react/jsx-runtime";
219
+ function Text3({
220
+ children,
221
+ bold,
222
+ italic,
223
+ underline,
224
+ strikethrough,
225
+ dim,
226
+ inverse,
227
+ color,
228
+ backgroundColor,
229
+ wrap
230
+ }) {
231
+ return /* @__PURE__ */ jsx10(
232
+ InkText,
233
+ {
234
+ bold,
235
+ italic,
236
+ underline,
237
+ strikethrough,
238
+ dimColor: dim,
239
+ inverse,
240
+ color,
241
+ backgroundColor,
242
+ wrap,
243
+ children
244
+ }
245
+ );
246
+ }
247
+
248
+ // packages/components/src/typography/Badge.tsx
249
+ import { Box as Box10, Text as Text4 } from "ink";
250
+ import { jsx as jsx11 } from "react/jsx-runtime";
251
+ function Badge({ children, variant = "default", color, bold = false }) {
252
+ const theme = useTheme();
253
+ const variantColor = color ?? (() => {
254
+ switch (variant) {
255
+ case "success":
256
+ return theme.colors.success;
257
+ case "warning":
258
+ return theme.colors.warning;
259
+ case "error":
260
+ return theme.colors.error;
261
+ case "info":
262
+ return theme.colors.info;
263
+ case "secondary":
264
+ return theme.colors.secondary;
265
+ default:
266
+ return theme.colors.primary;
267
+ }
268
+ })();
269
+ return /* @__PURE__ */ jsx11(Box10, { borderStyle: "round", borderColor: variantColor, paddingX: 1, children: /* @__PURE__ */ jsx11(Text4, { color: variantColor, bold, children }) });
270
+ }
271
+
272
+ // packages/components/src/typography/Heading.tsx
273
+ import { Box as Box11, Text as Text5 } from "ink";
274
+ import { jsx as jsx12, jsxs as jsxs3 } from "react/jsx-runtime";
275
+ function Heading({ level = 1, children, color }) {
276
+ const theme = useTheme();
277
+ const resolvedColor = color ?? theme.colors.primary;
278
+ switch (level) {
279
+ case 1:
280
+ return /* @__PURE__ */ jsxs3(Box11, { children: [
281
+ /* @__PURE__ */ jsx12(Text5, { color: resolvedColor, bold: true, children: "\u2588\u2588 " }),
282
+ /* @__PURE__ */ jsx12(Text5, { color: resolvedColor, bold: true, children: typeof children === "string" ? children.toUpperCase() : children })
283
+ ] });
284
+ case 2:
285
+ return /* @__PURE__ */ jsxs3(Box11, { children: [
286
+ /* @__PURE__ */ jsx12(Text5, { color: resolvedColor, bold: true, children: "\u258C " }),
287
+ /* @__PURE__ */ jsx12(Text5, { color: resolvedColor, bold: true, children })
288
+ ] });
289
+ case 3:
290
+ return /* @__PURE__ */ jsxs3(Box11, { children: [
291
+ /* @__PURE__ */ jsx12(Text5, { bold: true, children: "\u203A " }),
292
+ /* @__PURE__ */ jsx12(Text5, { bold: true, children })
293
+ ] });
294
+ case 4:
295
+ return /* @__PURE__ */ jsx12(Box11, { children: /* @__PURE__ */ jsx12(Text5, { underline: true, dimColor: true, children }) });
296
+ default:
297
+ return /* @__PURE__ */ jsx12(Box11, { children: /* @__PURE__ */ jsx12(Text5, { children }) });
298
+ }
299
+ }
300
+
301
+ // packages/components/src/typography/Code.tsx
302
+ import { Box as Box12, Text as Text6 } from "ink";
303
+ import { jsx as jsx13, jsxs as jsxs4 } from "react/jsx-runtime";
304
+ var KEYWORDS = /* @__PURE__ */ new Set([
305
+ "const",
306
+ "let",
307
+ "var",
308
+ "function",
309
+ "return",
310
+ "if",
311
+ "else",
312
+ "import",
313
+ "export",
314
+ "from",
315
+ "class",
316
+ "interface",
317
+ "type",
318
+ "async",
319
+ "await",
320
+ "new",
321
+ "this",
322
+ "true",
323
+ "false",
324
+ "null",
325
+ "undefined",
326
+ "void",
327
+ "typeof",
328
+ "instanceof",
329
+ "in",
330
+ "of",
331
+ "for",
332
+ "while",
333
+ "do",
334
+ "switch",
335
+ "case",
336
+ "break",
337
+ "continue",
338
+ "try",
339
+ "catch",
340
+ "finally",
341
+ "throw",
342
+ "extends",
343
+ "implements",
344
+ "static",
345
+ "public",
346
+ "private",
347
+ "protected",
348
+ "readonly",
349
+ "enum",
350
+ "default",
351
+ "delete",
352
+ "yield",
353
+ "super"
354
+ ]);
355
+ var OPERATORS = /^[=+\-*/<>!&|?%^~]+$/;
356
+ function tokenizeLine(line) {
357
+ const trimmed = line.trimStart();
358
+ if (trimmed.startsWith("//")) {
359
+ return [{ text: line, kind: "comment" }];
360
+ }
361
+ const tokens = [];
362
+ let i = 0;
363
+ while (i < line.length) {
364
+ if (line[i] === "/" && line[i + 1] === "/") {
365
+ tokens.push({ text: line.slice(i), kind: "comment" });
366
+ break;
367
+ }
368
+ const quote = line[i];
369
+ if (quote === '"' || quote === "'" || quote === "`") {
370
+ let j = i + 1;
371
+ while (j < line.length && line[j] !== quote) {
372
+ if (line[j] === "\\") j++;
373
+ j++;
374
+ }
375
+ j++;
376
+ tokens.push({ text: line.slice(i, j), kind: "string" });
377
+ i = j;
378
+ continue;
379
+ }
380
+ if (/[0-9]/.test(line[i])) {
381
+ let j = i;
382
+ while (j < line.length && /[0-9._xXa-fA-FbBoO]/.test(line[j])) j++;
383
+ tokens.push({ text: line.slice(i, j), kind: "number" });
384
+ i = j;
385
+ continue;
386
+ }
387
+ if (/[a-zA-Z_$]/.test(line[i])) {
388
+ let j = i;
389
+ while (j < line.length && /[a-zA-Z0-9_$]/.test(line[j])) j++;
390
+ const word = line.slice(i, j);
391
+ tokens.push({ text: word, kind: KEYWORDS.has(word) ? "keyword" : "plain" });
392
+ i = j;
393
+ continue;
394
+ }
395
+ if (/[=+\-*/<>!&|?%^~]/.test(line[i])) {
396
+ let j = i;
397
+ while (j < line.length && OPERATORS.test(line[j])) j++;
398
+ tokens.push({ text: line.slice(i, j), kind: "operator" });
399
+ i = j;
400
+ continue;
401
+ }
402
+ tokens.push({ text: line[i], kind: "plain" });
403
+ i++;
404
+ }
405
+ return tokens;
406
+ }
407
+ function CodeLine({
408
+ line,
409
+ keywordColor,
410
+ stringColor,
411
+ numberColor,
412
+ commentColor,
413
+ operatorColor,
414
+ plainColor
415
+ }) {
416
+ const tokens = tokenizeLine(line);
417
+ return /* @__PURE__ */ jsx13(Box12, { flexDirection: "row", children: tokens.map((token, idx) => {
418
+ switch (token.kind) {
419
+ case "keyword":
420
+ return /* @__PURE__ */ jsx13(Text6, { color: keywordColor, children: token.text }, idx);
421
+ case "string":
422
+ return /* @__PURE__ */ jsx13(Text6, { color: stringColor, children: token.text }, idx);
423
+ case "number":
424
+ return /* @__PURE__ */ jsx13(Text6, { color: numberColor, children: token.text }, idx);
425
+ case "comment":
426
+ return /* @__PURE__ */ jsx13(Text6, { dimColor: true, children: token.text }, idx);
427
+ case "operator":
428
+ return /* @__PURE__ */ jsx13(Text6, { color: operatorColor, children: token.text }, idx);
429
+ default:
430
+ return /* @__PURE__ */ jsx13(Text6, { color: plainColor, children: token.text }, idx);
431
+ }
432
+ }) });
433
+ }
434
+ function Code({ children, language, inline = false }) {
435
+ const theme = useTheme();
436
+ const keywordColor = theme.colors.accent;
437
+ const stringColor = theme.colors.success;
438
+ const numberColor = theme.colors.warning;
439
+ const commentColor = theme.colors.mutedForeground;
440
+ const operatorColor = theme.colors.info;
441
+ const plainColor = theme.colors.foreground;
442
+ const lines = children.split("\n");
443
+ if (inline) {
444
+ const displayLine = lines[0] ?? "";
445
+ return /* @__PURE__ */ jsx13(Box12, { borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, children: /* @__PURE__ */ jsx13(
446
+ CodeLine,
447
+ {
448
+ line: displayLine,
449
+ keywordColor,
450
+ stringColor,
451
+ numberColor,
452
+ commentColor,
453
+ operatorColor,
454
+ plainColor
455
+ }
456
+ ) });
457
+ }
458
+ const lineNumberWidth = String(lines.length).length;
459
+ return /* @__PURE__ */ jsxs4(Box12, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.border, children: [
460
+ language && /* @__PURE__ */ jsx13(Box12, { justifyContent: "flex-end", paddingX: 1, children: /* @__PURE__ */ jsx13(Text6, { color: theme.colors.mutedForeground, children: language }) }),
461
+ lines.map((line, idx) => /* @__PURE__ */ jsxs4(Box12, { flexDirection: "row", paddingX: 1, children: [
462
+ /* @__PURE__ */ jsxs4(Text6, { color: theme.colors.mutedForeground, children: [
463
+ String(idx + 1).padStart(lineNumberWidth, " "),
464
+ " "
465
+ ] }),
466
+ /* @__PURE__ */ jsx13(Text6, { color: theme.colors.mutedForeground, children: "\u2502 " }),
467
+ /* @__PURE__ */ jsx13(
468
+ CodeLine,
469
+ {
470
+ line,
471
+ keywordColor,
472
+ stringColor,
473
+ numberColor,
474
+ commentColor,
475
+ operatorColor,
476
+ plainColor
477
+ }
478
+ )
479
+ ] }, idx))
480
+ ] });
481
+ }
482
+
483
+ // packages/components/src/typography/Link.tsx
484
+ import { Box as Box13, Text as Text7 } from "ink";
485
+ import { jsx as jsx14, jsxs as jsxs5 } from "react/jsx-runtime";
486
+ function Link({ children, href, color, showHref = false }) {
487
+ const theme = useTheme();
488
+ const resolvedColor = color ?? theme.colors.info;
489
+ return /* @__PURE__ */ jsxs5(Box13, { flexDirection: "row", children: [
490
+ /* @__PURE__ */ jsx14(Text7, { color: resolvedColor, underline: true, children }),
491
+ showHref && /* @__PURE__ */ jsx14(Text7, { dimColor: true, children: ` (${href})` })
492
+ ] });
493
+ }
494
+
495
+ // packages/components/src/typography/Tag.tsx
496
+ import { Box as Box14, Text as Text8 } from "ink";
497
+ import { jsx as jsx15, jsxs as jsxs6 } from "react/jsx-runtime";
498
+ function Tag({ children, onRemove, color, variant = "default" }) {
499
+ const theme = useTheme();
500
+ const resolvedColor = color ?? theme.colors.primary;
501
+ const borderColor = variant === "outline" ? theme.colors.mutedForeground : resolvedColor;
502
+ return /* @__PURE__ */ jsxs6(Box14, { borderStyle: "round", borderColor, paddingX: 1, flexDirection: "row", children: [
503
+ /* @__PURE__ */ jsx15(Text8, { color: variant === "outline" ? theme.colors.mutedForeground : resolvedColor, children }),
504
+ onRemove && /* @__PURE__ */ jsx15(Text8, { color: theme.colors.mutedForeground, children: " \xD7" })
505
+ ] });
506
+ }
507
+
508
+ // packages/components/src/typography/Gradient.tsx
509
+ import { Box as Box15, Text as Text9 } from "ink";
510
+ import { jsx as jsx16 } from "react/jsx-runtime";
511
+ function parseHex(hex) {
512
+ const clean = hex.replace("#", "");
513
+ const full = clean.length === 3 ? clean.split("").map((c) => c + c).join("") : clean;
514
+ return {
515
+ r: parseInt(full.slice(0, 2), 16),
516
+ g: parseInt(full.slice(2, 4), 16),
517
+ b: parseInt(full.slice(4, 6), 16)
518
+ };
519
+ }
520
+ function toHex({ r, g, b }) {
521
+ return "#" + [r, g, b].map(
522
+ (v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0")
523
+ ).join("");
524
+ }
525
+ function lerpColor(a, b, t) {
526
+ return {
527
+ r: a.r + (b.r - a.r) * t,
528
+ g: a.g + (b.g - a.g) * t,
529
+ b: a.b + (b.b - a.b) * t
530
+ };
531
+ }
532
+ function gradientText(text, colors) {
533
+ if (colors.length === 0) return text.split("").map((char) => ({ char, color: "" }));
534
+ if (colors.length === 1) return text.split("").map((char) => ({ char, color: colors[0] }));
535
+ const parsedColors = colors.map(parseHex);
536
+ const segments = colors.length - 1;
537
+ const len = text.length;
538
+ return text.split("").map((char, i) => {
539
+ if (len <= 1) {
540
+ return { char, color: colors[0] };
541
+ }
542
+ const pos = i / (len - 1) * segments;
543
+ const segIndex = Math.min(Math.floor(pos), segments - 1);
544
+ const t = pos - segIndex;
545
+ const color = toHex(lerpColor(parsedColors[segIndex], parsedColors[segIndex + 1], t));
546
+ return { char, color };
547
+ });
548
+ }
549
+ function Gradient({ children, colors, bold = false }) {
550
+ const chars = gradientText(children, colors);
551
+ return /* @__PURE__ */ jsx16(Box15, { flexDirection: "row", children: chars.map((item, idx) => /* @__PURE__ */ jsx16(Text9, { color: item.color, bold, children: item.char }, idx)) });
552
+ }
553
+
554
+ // packages/components/src/typography/BigText.tsx
555
+ import { Box as Box16, Text as Text10 } from "ink";
556
+ import { jsx as jsx17 } from "react/jsx-runtime";
557
+ var FONT = {
558
+ A: [
559
+ [0, 1, 0],
560
+ [1, 0, 1],
561
+ [1, 1, 1],
562
+ [1, 0, 1],
563
+ [1, 0, 1]
564
+ ],
565
+ B: [
566
+ [1, 1, 0],
567
+ [1, 0, 1],
568
+ [1, 1, 0],
569
+ [1, 0, 1],
570
+ [1, 1, 0]
571
+ ],
572
+ C: [
573
+ [0, 1, 1],
574
+ [1, 0, 0],
575
+ [1, 0, 0],
576
+ [1, 0, 0],
577
+ [0, 1, 1]
578
+ ],
579
+ D: [
580
+ [1, 1, 0],
581
+ [1, 0, 1],
582
+ [1, 0, 1],
583
+ [1, 0, 1],
584
+ [1, 1, 0]
585
+ ],
586
+ E: [
587
+ [1, 1, 1],
588
+ [1, 0, 0],
589
+ [1, 1, 0],
590
+ [1, 0, 0],
591
+ [1, 1, 1]
592
+ ],
593
+ F: [
594
+ [1, 1, 1],
595
+ [1, 0, 0],
596
+ [1, 1, 0],
597
+ [1, 0, 0],
598
+ [1, 0, 0]
599
+ ],
600
+ G: [
601
+ [0, 1, 1],
602
+ [1, 0, 0],
603
+ [1, 0, 1],
604
+ [1, 0, 1],
605
+ [0, 1, 1]
606
+ ],
607
+ H: [
608
+ [1, 0, 1],
609
+ [1, 0, 1],
610
+ [1, 1, 1],
611
+ [1, 0, 1],
612
+ [1, 0, 1]
613
+ ],
614
+ I: [
615
+ [1, 1, 1],
616
+ [0, 1, 0],
617
+ [0, 1, 0],
618
+ [0, 1, 0],
619
+ [1, 1, 1]
620
+ ],
621
+ J: [
622
+ [0, 0, 1],
623
+ [0, 0, 1],
624
+ [0, 0, 1],
625
+ [1, 0, 1],
626
+ [0, 1, 0]
627
+ ],
628
+ K: [
629
+ [1, 0, 1],
630
+ [1, 0, 1],
631
+ [1, 1, 0],
632
+ [1, 0, 1],
633
+ [1, 0, 1]
634
+ ],
635
+ L: [
636
+ [1, 0, 0],
637
+ [1, 0, 0],
638
+ [1, 0, 0],
639
+ [1, 0, 0],
640
+ [1, 1, 1]
641
+ ],
642
+ M: [
643
+ [1, 0, 1],
644
+ [1, 1, 1],
645
+ [1, 0, 1],
646
+ [1, 0, 1],
647
+ [1, 0, 1]
648
+ ],
649
+ N: [
650
+ [1, 0, 1],
651
+ [1, 1, 1],
652
+ [1, 1, 1],
653
+ [1, 0, 1],
654
+ [1, 0, 1]
655
+ ],
656
+ O: [
657
+ [0, 1, 0],
658
+ [1, 0, 1],
659
+ [1, 0, 1],
660
+ [1, 0, 1],
661
+ [0, 1, 0]
662
+ ],
663
+ P: [
664
+ [1, 1, 0],
665
+ [1, 0, 1],
666
+ [1, 1, 0],
667
+ [1, 0, 0],
668
+ [1, 0, 0]
669
+ ],
670
+ Q: [
671
+ [0, 1, 0],
672
+ [1, 0, 1],
673
+ [1, 0, 1],
674
+ [1, 1, 1],
675
+ [0, 1, 1]
676
+ ],
677
+ R: [
678
+ [1, 1, 0],
679
+ [1, 0, 1],
680
+ [1, 1, 0],
681
+ [1, 0, 1],
682
+ [1, 0, 1]
683
+ ],
684
+ S: [
685
+ [0, 1, 1],
686
+ [1, 0, 0],
687
+ [0, 1, 0],
688
+ [0, 0, 1],
689
+ [1, 1, 0]
690
+ ],
691
+ T: [
692
+ [1, 1, 1],
693
+ [0, 1, 0],
694
+ [0, 1, 0],
695
+ [0, 1, 0],
696
+ [0, 1, 0]
697
+ ],
698
+ U: [
699
+ [1, 0, 1],
700
+ [1, 0, 1],
701
+ [1, 0, 1],
702
+ [1, 0, 1],
703
+ [0, 1, 0]
704
+ ],
705
+ V: [
706
+ [1, 0, 1],
707
+ [1, 0, 1],
708
+ [1, 0, 1],
709
+ [1, 0, 1],
710
+ [0, 1, 0]
711
+ ],
712
+ W: [
713
+ [1, 0, 1],
714
+ [1, 0, 1],
715
+ [1, 0, 1],
716
+ [1, 1, 1],
717
+ [1, 0, 1]
718
+ ],
719
+ X: [
720
+ [1, 0, 1],
721
+ [1, 0, 1],
722
+ [0, 1, 0],
723
+ [1, 0, 1],
724
+ [1, 0, 1]
725
+ ],
726
+ Y: [
727
+ [1, 0, 1],
728
+ [1, 0, 1],
729
+ [0, 1, 0],
730
+ [0, 1, 0],
731
+ [0, 1, 0]
732
+ ],
733
+ Z: [
734
+ [1, 1, 1],
735
+ [0, 0, 1],
736
+ [0, 1, 0],
737
+ [1, 0, 0],
738
+ [1, 1, 1]
739
+ ],
740
+ "0": [
741
+ [0, 1, 0],
742
+ [1, 0, 1],
743
+ [1, 0, 1],
744
+ [1, 0, 1],
745
+ [0, 1, 0]
746
+ ],
747
+ "1": [
748
+ [0, 1, 0],
749
+ [1, 1, 0],
750
+ [0, 1, 0],
751
+ [0, 1, 0],
752
+ [1, 1, 1]
753
+ ],
754
+ "2": [
755
+ [1, 1, 0],
756
+ [0, 0, 1],
757
+ [0, 1, 0],
758
+ [1, 0, 0],
759
+ [1, 1, 1]
760
+ ],
761
+ "3": [
762
+ [1, 1, 0],
763
+ [0, 0, 1],
764
+ [0, 1, 0],
765
+ [0, 0, 1],
766
+ [1, 1, 0]
767
+ ],
768
+ "4": [
769
+ [1, 0, 1],
770
+ [1, 0, 1],
771
+ [1, 1, 1],
772
+ [0, 0, 1],
773
+ [0, 0, 1]
774
+ ],
775
+ "5": [
776
+ [1, 1, 1],
777
+ [1, 0, 0],
778
+ [1, 1, 0],
779
+ [0, 0, 1],
780
+ [1, 1, 0]
781
+ ],
782
+ "6": [
783
+ [0, 1, 1],
784
+ [1, 0, 0],
785
+ [1, 1, 0],
786
+ [1, 0, 1],
787
+ [0, 1, 0]
788
+ ],
789
+ "7": [
790
+ [1, 1, 1],
791
+ [0, 0, 1],
792
+ [0, 1, 0],
793
+ [0, 1, 0],
794
+ [0, 1, 0]
795
+ ],
796
+ "8": [
797
+ [0, 1, 0],
798
+ [1, 0, 1],
799
+ [0, 1, 0],
800
+ [1, 0, 1],
801
+ [0, 1, 0]
802
+ ],
803
+ "9": [
804
+ [0, 1, 0],
805
+ [1, 0, 1],
806
+ [0, 1, 1],
807
+ [0, 0, 1],
808
+ [1, 1, 0]
809
+ ],
810
+ " ": [
811
+ [0, 0, 0],
812
+ [0, 0, 0],
813
+ [0, 0, 0],
814
+ [0, 0, 0],
815
+ [0, 0, 0]
816
+ ],
817
+ "!": [
818
+ [0, 1, 0],
819
+ [0, 1, 0],
820
+ [0, 1, 0],
821
+ [0, 0, 0],
822
+ [0, 1, 0]
823
+ ],
824
+ "?": [
825
+ [0, 1, 0],
826
+ [1, 0, 1],
827
+ [0, 0, 1],
828
+ [0, 1, 0],
829
+ [0, 1, 0]
830
+ ],
831
+ ".": [
832
+ [0, 0, 0],
833
+ [0, 0, 0],
834
+ [0, 0, 0],
835
+ [0, 0, 0],
836
+ [0, 1, 0]
837
+ ],
838
+ ":": [
839
+ [0, 0, 0],
840
+ [0, 1, 0],
841
+ [0, 0, 0],
842
+ [0, 1, 0],
843
+ [0, 0, 0]
844
+ ],
845
+ "-": [
846
+ [0, 0, 0],
847
+ [0, 0, 0],
848
+ [1, 1, 1],
849
+ [0, 0, 0],
850
+ [0, 0, 0]
851
+ ]
852
+ };
853
+ var FALLBACK = [
854
+ [1, 1, 1],
855
+ [1, 0, 1],
856
+ [1, 0, 1],
857
+ [1, 0, 1],
858
+ [1, 1, 1]
859
+ ];
860
+ function getCharRows(ch) {
861
+ const upper = ch.toUpperCase();
862
+ return FONT[upper] ?? FONT[ch] ?? FALLBACK;
863
+ }
864
+ function BigText({ children, color, font = "block" }) {
865
+ const theme = useTheme();
866
+ const resolvedColor = color ?? theme.colors.primary;
867
+ const onChar = font === "block" ? "\u2588" : "\u2593";
868
+ const offChar = " ";
869
+ const chars = children.split("");
870
+ const rows = 5;
871
+ return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: Array.from({ length: rows }, (_, rowIdx) => /* @__PURE__ */ jsx17(Box16, { flexDirection: "row", children: chars.map((ch, charIdx) => {
872
+ const charRows = getCharRows(ch);
873
+ const row = charRows[rowIdx] ?? [0, 0, 0];
874
+ const rowStr = row.map((pixel) => pixel ? onChar : offChar).join("");
875
+ return /* @__PURE__ */ jsx17(Text10, { color: resolvedColor, children: rowStr + " " }, charIdx);
876
+ }) }, rowIdx)) });
877
+ }
878
+
879
+ // packages/components/src/typography/Digits.tsx
880
+ import { Box as Box17, Text as Text11 } from "ink";
881
+ import { jsx as jsx18, jsxs as jsxs7 } from "react/jsx-runtime";
882
+ var SEGMENTS_MD = {
883
+ "0": ["\u256D\u2500\u256E", "\u2502 \u2502", "\u2502 \u2502", "\u2502 \u2502", "\u2570\u2500\u256F"],
884
+ "1": [" \u2502", " \u2502", " \u2502", " \u2502", " \u2502"],
885
+ "2": ["\u256D\u2500\u256E", " \u2502", "\u256D\u2500\u256F", "\u2502 ", "\u2570\u2500\u2574"],
886
+ "3": ["\u256D\u2500\u256E", " \u2502", " \u2500\u2524", " \u2502", "\u2570\u2500\u256F"],
887
+ "4": ["\u2577 \u2577", "\u2502 \u2502", "\u2570\u2500\u2524", " \u2502", " \u2575"],
888
+ "5": ["\u256D\u2500\u2574", "\u2502 ", "\u2570\u2500\u256E", " \u2502", "\u2570\u2500\u256F"],
889
+ "6": ["\u256D\u2500\u2574", "\u2502 ", "\u251C\u2500\u256E", "\u2502 \u2502", "\u2570\u2500\u256F"],
890
+ "7": ["\u256D\u2500\u256E", " \u2502", " \u2502", " \u2502", " \u2575"],
891
+ "8": ["\u256D\u2500\u256E", "\u2502 \u2502", "\u251C\u2500\u2524", "\u2502 \u2502", "\u2570\u2500\u256F"],
892
+ "9": ["\u256D\u2500\u256E", "\u2502 \u2502", "\u2570\u2500\u2524", " \u2502", "\u2570\u2500\u256F"],
893
+ ":": [" ", " \u25CF ", " ", " \u25CF ", " "],
894
+ ".": [" ", " ", " ", " ", " \u25CF "],
895
+ "-": [" ", " ", " \u2500 ", " ", " "],
896
+ " ": [" ", " ", " ", " ", " "]
897
+ };
898
+ var SEGMENTS_LG = {
899
+ "0": ["\u256D\u2500\u2500\u2500\u256E", "\u2502 \u2502", "\u2502 \u2502", "\u2502 \u2502", "\u2570\u2500\u2500\u2500\u256F"],
900
+ "1": [" \u2577 ", " \u2502 ", " \u2502 ", " \u2502 ", " \u2575 "],
901
+ "2": ["\u256D\u2500\u2500\u2500\u256E", " \u2502", " \u2500\u2500\u2500\u256F", "\u2502 ", "\u2570\u2500\u2500\u2500\u2574"],
902
+ "3": ["\u256D\u2500\u2500\u2500\u256E", " \u2502", " \u2500\u2500\u2500\u2524", " \u2502", "\u2570\u2500\u2500\u2500\u256F"],
903
+ "4": ["\u2577 \u2577", "\u2502 \u2502", "\u2570\u2500\u2500\u2500\u2524", " \u2502", " \u2575"],
904
+ "5": ["\u256D\u2500\u2500\u2500\u2574", "\u2502 ", "\u2570\u2500\u2500\u2500\u256E", " \u2502", "\u2570\u2500\u2500\u2500\u256F"],
905
+ "6": ["\u256D\u2500\u2500\u2500\u2574", "\u2502 ", "\u251C\u2500\u2500\u2500\u256E", "\u2502 \u2502", "\u2570\u2500\u2500\u2500\u256F"],
906
+ "7": ["\u256D\u2500\u2500\u2500\u256E", " \u2502", " \u2502", " \u2502", " \u2575"],
907
+ "8": ["\u256D\u2500\u2500\u2500\u256E", "\u2502 \u2502", "\u251C\u2500\u2500\u2500\u2524", "\u2502 \u2502", "\u2570\u2500\u2500\u2500\u256F"],
908
+ "9": ["\u256D\u2500\u2500\u2500\u256E", "\u2502 \u2502", "\u2570\u2500\u2500\u2500\u2524", " \u2502", "\u2570\u2500\u2500\u2500\u256F"],
909
+ ":": [" ", " \u25CF ", " ", " \u25CF ", " "],
910
+ ".": [" ", " ", " ", " ", " \u25CF "],
911
+ "-": [" ", " ", " \u2500\u2500\u2500 ", " ", " "],
912
+ " ": [" ", " ", " ", " ", " "]
913
+ };
914
+ function getSegmentMap(size) {
915
+ return size === "lg" ? SEGMENTS_LG : SEGMENTS_MD;
916
+ }
917
+ function getFallback(size) {
918
+ const w = size === "lg" ? 5 : 3;
919
+ const bar = "\u2500".repeat(w - 2);
920
+ const side = "\u2502" + " ".repeat(w - 2) + "\u2502";
921
+ return [`\u256D${bar}\u256E`, side, side, side, `\u2570${bar}\u256F`];
922
+ }
923
+ function Digits({ value, color, size = "md" }) {
924
+ const theme = useTheme();
925
+ const resolvedColor = color ?? theme.colors.primary;
926
+ const str = String(value);
927
+ if (size === "sm") {
928
+ return /* @__PURE__ */ jsx18(Text11, { color: resolvedColor, bold: true, children: str });
929
+ }
930
+ const segMap = getSegmentMap(size);
931
+ const fallback = getFallback(size);
932
+ const chars = str.split("");
933
+ const rows = 5;
934
+ return /* @__PURE__ */ jsx18(Box17, { flexDirection: "column", children: Array.from({ length: rows }, (_, rowIdx) => /* @__PURE__ */ jsx18(Box17, { flexDirection: "row", children: chars.map((ch, charIdx) => {
935
+ const segments = segMap[ch] ?? fallback;
936
+ const rowStr = segments[rowIdx] ?? " ".repeat(size === "lg" ? 5 : 3);
937
+ return /* @__PURE__ */ jsxs7(Text11, { color: resolvedColor, children: [
938
+ rowStr,
939
+ " "
940
+ ] }, charIdx);
941
+ }) }, rowIdx)) });
942
+ }
943
+
944
+ // packages/components/src/input/TextInput.tsx
945
+ import { useState as useState2 } from "react";
946
+ import { Box as Box18, Text as Text12 } from "ink";
947
+ import { jsx as jsx19, jsxs as jsxs8 } from "react/jsx-runtime";
948
+ function TextInput({
949
+ value: controlledValue,
950
+ onChange,
951
+ onSubmit,
952
+ placeholder = "",
953
+ mask,
954
+ validate,
955
+ width = 40,
956
+ label,
957
+ autoFocus = false,
958
+ id
959
+ }) {
960
+ const [internalValue, setInternalValue] = useState2("");
961
+ const [error, setError] = useState2(null);
962
+ const theme = useTheme();
963
+ const { isFocused } = useFocus({ autoFocus, id });
964
+ const value = controlledValue ?? internalValue;
965
+ useInput((input, key) => {
966
+ if (!isFocused) return;
967
+ if (key.return) {
968
+ const err = validate ? validate(value) : null;
969
+ if (err) {
970
+ setError(err);
971
+ return;
972
+ }
973
+ setError(null);
974
+ onSubmit?.(value);
975
+ return;
976
+ }
977
+ if (key.backspace || key.delete) {
978
+ const newVal2 = value.slice(0, -1);
979
+ onChange ? onChange(newVal2) : setInternalValue(newVal2);
980
+ return;
981
+ }
982
+ if (key.escape) return;
983
+ if (key.upArrow || key.downArrow || key.tab) return;
984
+ const newVal = value + input;
985
+ onChange ? onChange(newVal) : setInternalValue(newVal);
986
+ });
987
+ const displayValue = mask ? mask.repeat(value.length) : value;
988
+ const borderColor = error ? theme.colors.error : isFocused ? theme.colors.focusRing : theme.colors.border;
989
+ return /* @__PURE__ */ jsxs8(Box18, { flexDirection: "column", children: [
990
+ label && /* @__PURE__ */ jsx19(Text12, { bold: true, children: label }),
991
+ /* @__PURE__ */ jsxs8(Box18, { borderStyle: "round", borderColor, width, paddingX: 1, children: [
992
+ /* @__PURE__ */ jsx19(Text12, { color: value ? theme.colors.foreground : theme.colors.mutedForeground, children: displayValue || placeholder }),
993
+ isFocused && /* @__PURE__ */ jsx19(Text12, { color: theme.colors.focusRing, children: "\u2588" })
994
+ ] }),
995
+ error && /* @__PURE__ */ jsx19(Text12, { color: theme.colors.error, children: error })
996
+ ] });
997
+ }
998
+
999
+ // packages/components/src/input/TextArea.tsx
1000
+ import { useState as useState3 } from "react";
1001
+ import { Box as Box19, Text as Text13 } from "ink";
1002
+ import { jsx as jsx20, jsxs as jsxs9 } from "react/jsx-runtime";
1003
+ function TextArea({
1004
+ value: controlledValue,
1005
+ onChange,
1006
+ onSubmit,
1007
+ placeholder = "",
1008
+ rows = 4,
1009
+ label,
1010
+ id
1011
+ }) {
1012
+ const [internalValue, setInternalValue] = useState3("");
1013
+ const [cursorLine, setCursorLine] = useState3(0);
1014
+ const [cursorCol, setCursorCol] = useState3(0);
1015
+ const [scrollOffset, setScrollOffset] = useState3(0);
1016
+ const theme = useTheme();
1017
+ const { isFocused } = useFocus({ id });
1018
+ const value = controlledValue ?? internalValue;
1019
+ function setValue(newVal) {
1020
+ onChange ? onChange(newVal) : setInternalValue(newVal);
1021
+ }
1022
+ function getLines(v) {
1023
+ return v.split("\n");
1024
+ }
1025
+ function joinLines(lines2) {
1026
+ return lines2.join("\n");
1027
+ }
1028
+ useInput((input, key) => {
1029
+ if (!isFocused) return;
1030
+ const lines2 = getLines(value);
1031
+ if (key.return && key.ctrl) {
1032
+ onSubmit?.(value);
1033
+ return;
1034
+ }
1035
+ if (key.return) {
1036
+ const totalLines = lines2.length;
1037
+ if (totalLines >= rows && cursorLine === rows - 1) {
1038
+ return;
1039
+ }
1040
+ const currentLine = lines2[cursorLine] ?? "";
1041
+ const before = currentLine.slice(0, cursorCol);
1042
+ const after = currentLine.slice(cursorCol);
1043
+ const newLines = [
1044
+ ...lines2.slice(0, cursorLine),
1045
+ before,
1046
+ after,
1047
+ ...lines2.slice(cursorLine + 1)
1048
+ ];
1049
+ setValue(joinLines(newLines));
1050
+ const newLine = cursorLine + 1;
1051
+ setCursorLine(newLine);
1052
+ setCursorCol(0);
1053
+ if (newLine >= scrollOffset + rows) {
1054
+ setScrollOffset(newLine - rows + 1);
1055
+ }
1056
+ return;
1057
+ }
1058
+ if (key.backspace || key.delete) {
1059
+ const currentLine = lines2[cursorLine] ?? "";
1060
+ if (cursorCol > 0) {
1061
+ const newLine = currentLine.slice(0, cursorCol - 1) + currentLine.slice(cursorCol);
1062
+ const newLines = [...lines2.slice(0, cursorLine), newLine, ...lines2.slice(cursorLine + 1)];
1063
+ setValue(joinLines(newLines));
1064
+ setCursorCol(cursorCol - 1);
1065
+ } else if (cursorLine > 0) {
1066
+ const prevLine = lines2[cursorLine - 1] ?? "";
1067
+ const mergedLine = prevLine + currentLine;
1068
+ const newLines = [
1069
+ ...lines2.slice(0, cursorLine - 1),
1070
+ mergedLine,
1071
+ ...lines2.slice(cursorLine + 1)
1072
+ ];
1073
+ setValue(joinLines(newLines));
1074
+ const newLineIdx = cursorLine - 1;
1075
+ setCursorLine(newLineIdx);
1076
+ setCursorCol(prevLine.length);
1077
+ if (newLineIdx < scrollOffset) {
1078
+ setScrollOffset(newLineIdx);
1079
+ }
1080
+ }
1081
+ return;
1082
+ }
1083
+ if (key.leftArrow) {
1084
+ if (cursorCol > 0) {
1085
+ setCursorCol(cursorCol - 1);
1086
+ } else if (cursorLine > 0) {
1087
+ const prevLine = lines2[cursorLine - 1] ?? "";
1088
+ const newLineIdx = cursorLine - 1;
1089
+ setCursorLine(newLineIdx);
1090
+ setCursorCol(prevLine.length);
1091
+ if (newLineIdx < scrollOffset) {
1092
+ setScrollOffset(newLineIdx);
1093
+ }
1094
+ }
1095
+ return;
1096
+ }
1097
+ if (key.rightArrow) {
1098
+ const currentLine = lines2[cursorLine] ?? "";
1099
+ if (cursorCol < currentLine.length) {
1100
+ setCursorCol(cursorCol + 1);
1101
+ } else if (cursorLine < lines2.length - 1) {
1102
+ const newLineIdx = cursorLine + 1;
1103
+ setCursorLine(newLineIdx);
1104
+ setCursorCol(0);
1105
+ if (newLineIdx >= scrollOffset + rows) {
1106
+ setScrollOffset(newLineIdx - rows + 1);
1107
+ }
1108
+ }
1109
+ return;
1110
+ }
1111
+ if (key.upArrow) {
1112
+ if (cursorLine > 0) {
1113
+ const newLineIdx = cursorLine - 1;
1114
+ const targetLine = lines2[newLineIdx] ?? "";
1115
+ setCursorLine(newLineIdx);
1116
+ setCursorCol(Math.min(cursorCol, targetLine.length));
1117
+ if (newLineIdx < scrollOffset) {
1118
+ setScrollOffset(newLineIdx);
1119
+ }
1120
+ }
1121
+ return;
1122
+ }
1123
+ if (key.downArrow) {
1124
+ if (cursorLine < lines2.length - 1) {
1125
+ const newLineIdx = cursorLine + 1;
1126
+ const targetLine = lines2[newLineIdx] ?? "";
1127
+ setCursorLine(newLineIdx);
1128
+ setCursorCol(Math.min(cursorCol, targetLine.length));
1129
+ if (newLineIdx >= scrollOffset + rows) {
1130
+ setScrollOffset(newLineIdx - rows + 1);
1131
+ }
1132
+ }
1133
+ return;
1134
+ }
1135
+ if (key.escape || key.tab) return;
1136
+ if (input && input.length > 0) {
1137
+ const currentLine = lines2[cursorLine] ?? "";
1138
+ const newLine = currentLine.slice(0, cursorCol) + input + currentLine.slice(cursorCol);
1139
+ const newLines = [...lines2.slice(0, cursorLine), newLine, ...lines2.slice(cursorLine + 1)];
1140
+ setValue(joinLines(newLines));
1141
+ setCursorCol(cursorCol + input.length);
1142
+ }
1143
+ });
1144
+ const borderColor = isFocused ? theme.colors.focusRing : theme.colors.border;
1145
+ const lines = getLines(value);
1146
+ const visibleLines = lines.slice(scrollOffset, scrollOffset + rows);
1147
+ const paddedLines = [...visibleLines];
1148
+ while (paddedLines.length < rows) {
1149
+ paddedLines.push("");
1150
+ }
1151
+ const isEmpty = value.length === 0;
1152
+ return /* @__PURE__ */ jsxs9(Box19, { flexDirection: "column", children: [
1153
+ label && /* @__PURE__ */ jsx20(Text13, { bold: true, children: label }),
1154
+ /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", borderStyle: "round", borderColor, paddingX: 1, children: paddedLines.map((line, rowIdx) => {
1155
+ const absoluteLineIdx = rowIdx + scrollOffset;
1156
+ const isActiveLine = isFocused && absoluteLineIdx === cursorLine;
1157
+ if (isEmpty && rowIdx === 0) {
1158
+ return /* @__PURE__ */ jsxs9(Box19, { flexDirection: "row", children: [
1159
+ /* @__PURE__ */ jsx20(Text13, { color: theme.colors.mutedForeground, children: placeholder }),
1160
+ isFocused && /* @__PURE__ */ jsx20(Text13, { color: theme.colors.focusRing, children: "\u2588" })
1161
+ ] }, rowIdx);
1162
+ }
1163
+ if (isActiveLine) {
1164
+ const before = line.slice(0, cursorCol);
1165
+ const after = line.slice(cursorCol);
1166
+ return /* @__PURE__ */ jsxs9(Box19, { flexDirection: "row", children: [
1167
+ /* @__PURE__ */ jsx20(Text13, { color: theme.colors.foreground, children: before }),
1168
+ /* @__PURE__ */ jsx20(Text13, { color: theme.colors.focusRing, children: "\u2588" }),
1169
+ /* @__PURE__ */ jsx20(Text13, { color: theme.colors.foreground, children: after })
1170
+ ] }, rowIdx);
1171
+ }
1172
+ return /* @__PURE__ */ jsx20(Box19, { children: /* @__PURE__ */ jsx20(Text13, { color: theme.colors.foreground, children: line }) }, rowIdx);
1173
+ }) })
1174
+ ] });
1175
+ }
1176
+
1177
+ // packages/components/src/input/PasswordInput.tsx
1178
+ import { useState as useState4 } from "react";
1179
+ import { Box as Box20, Text as Text14 } from "ink";
1180
+ import { jsx as jsx21, jsxs as jsxs10 } from "react/jsx-runtime";
1181
+ function PasswordInput({
1182
+ value: controlledValue,
1183
+ onChange,
1184
+ onSubmit,
1185
+ placeholder = "",
1186
+ mask = "\u25CF",
1187
+ showToggle = false,
1188
+ label,
1189
+ id
1190
+ }) {
1191
+ const [internalValue, setInternalValue] = useState4("");
1192
+ const [isVisible, setIsVisible] = useState4(false);
1193
+ const theme = useTheme();
1194
+ const { isFocused } = useFocus({ id });
1195
+ const value = controlledValue ?? internalValue;
1196
+ function setValue(newVal) {
1197
+ onChange ? onChange(newVal) : setInternalValue(newVal);
1198
+ }
1199
+ useInput((input, key) => {
1200
+ if (!isFocused) return;
1201
+ if (showToggle && input === "\b") {
1202
+ setIsVisible((v) => !v);
1203
+ return;
1204
+ }
1205
+ if (key.return) {
1206
+ onSubmit?.(value);
1207
+ return;
1208
+ }
1209
+ if (key.backspace || key.delete) {
1210
+ setValue(value.slice(0, -1));
1211
+ return;
1212
+ }
1213
+ if (key.escape || key.upArrow || key.downArrow || key.tab) return;
1214
+ if (input && input.length > 0) {
1215
+ setValue(value + input);
1216
+ }
1217
+ });
1218
+ const displayValue = isVisible ? value : mask.repeat(value.length);
1219
+ const borderColor = isFocused ? theme.colors.focusRing : theme.colors.border;
1220
+ return /* @__PURE__ */ jsxs10(Box20, { flexDirection: "column", children: [
1221
+ label && /* @__PURE__ */ jsx21(Text14, { bold: true, children: label }),
1222
+ /* @__PURE__ */ jsxs10(Box20, { flexDirection: "row", alignItems: "center", gap: 1, children: [
1223
+ /* @__PURE__ */ jsxs10(Box20, { borderStyle: "round", borderColor, paddingX: 1, children: [
1224
+ /* @__PURE__ */ jsx21(Text14, { color: value ? theme.colors.foreground : theme.colors.mutedForeground, children: displayValue || placeholder }),
1225
+ isFocused && /* @__PURE__ */ jsx21(Text14, { color: theme.colors.focusRing, children: "\u2588" })
1226
+ ] }),
1227
+ showToggle && isFocused && /* @__PURE__ */ jsx21(Text14, { color: theme.colors.mutedForeground, children: isVisible ? "Ctrl+H hide" : "Ctrl+H show" })
1228
+ ] })
1229
+ ] });
1230
+ }
1231
+
1232
+ // packages/components/src/input/NumberInput.tsx
1233
+ import { useState as useState5 } from "react";
1234
+ import { Box as Box21, Text as Text15 } from "ink";
1235
+ import { jsx as jsx22, jsxs as jsxs11 } from "react/jsx-runtime";
1236
+ function NumberInput({
1237
+ value: controlledValue,
1238
+ onChange,
1239
+ onSubmit,
1240
+ min,
1241
+ max,
1242
+ step = 1,
1243
+ placeholder = "",
1244
+ label,
1245
+ id,
1246
+ format
1247
+ }) {
1248
+ const [internalValue, setInternalValue] = useState5(void 0);
1249
+ const [buffer, setBuffer] = useState5("");
1250
+ const theme = useTheme();
1251
+ const { isFocused } = useFocus({ id });
1252
+ const value = controlledValue ?? internalValue;
1253
+ function clamp(n) {
1254
+ let result = n;
1255
+ if (min !== void 0) result = Math.max(min, result);
1256
+ if (max !== void 0) result = Math.min(max, result);
1257
+ return result;
1258
+ }
1259
+ function commitValue(n) {
1260
+ const clamped = clamp(n);
1261
+ onChange ? onChange(clamped) : setInternalValue(clamped);
1262
+ setBuffer(String(clamped));
1263
+ }
1264
+ useInput((input, key) => {
1265
+ if (!isFocused) return;
1266
+ if (key.upArrow) {
1267
+ const current = value ?? 0;
1268
+ commitValue(current + step);
1269
+ return;
1270
+ }
1271
+ if (key.downArrow) {
1272
+ const current = value ?? 0;
1273
+ commitValue(current - step);
1274
+ return;
1275
+ }
1276
+ if (key.return) {
1277
+ const parsed = buffer !== "" ? parseFloat(buffer) : value;
1278
+ if (parsed !== void 0 && !isNaN(parsed)) {
1279
+ const clamped = clamp(parsed);
1280
+ onSubmit?.(clamped);
1281
+ }
1282
+ return;
1283
+ }
1284
+ if (key.backspace || key.delete) {
1285
+ const newBuffer = buffer.slice(0, -1);
1286
+ setBuffer(newBuffer);
1287
+ if (newBuffer === "" || newBuffer === "-") {
1288
+ return;
1289
+ }
1290
+ const parsed = parseFloat(newBuffer);
1291
+ if (!isNaN(parsed)) {
1292
+ onChange ? onChange(clamp(parsed)) : setInternalValue(clamp(parsed));
1293
+ }
1294
+ return;
1295
+ }
1296
+ if (key.escape || key.tab) return;
1297
+ if (input && /^[\d.\-]$/.test(input)) {
1298
+ if (input === "-" && buffer.length > 0) return;
1299
+ if (input === "." && buffer.includes(".")) return;
1300
+ const newBuffer = buffer + input;
1301
+ setBuffer(newBuffer);
1302
+ const parsed = parseFloat(newBuffer);
1303
+ if (!isNaN(parsed)) {
1304
+ onChange ? onChange(clamp(parsed)) : setInternalValue(clamp(parsed));
1305
+ }
1306
+ }
1307
+ });
1308
+ const borderColor = isFocused ? theme.colors.focusRing : theme.colors.border;
1309
+ let displayValue = "";
1310
+ if (isFocused && buffer !== "") {
1311
+ displayValue = buffer;
1312
+ } else if (value !== void 0) {
1313
+ displayValue = format ? format(value) : String(value);
1314
+ }
1315
+ const stepHint = `\u2191 +${step} \u2193 -${step}`;
1316
+ return /* @__PURE__ */ jsxs11(Box21, { flexDirection: "column", children: [
1317
+ label && /* @__PURE__ */ jsx22(Text15, { bold: true, children: label }),
1318
+ /* @__PURE__ */ jsxs11(Box21, { flexDirection: "row", alignItems: "center", gap: 1, children: [
1319
+ /* @__PURE__ */ jsxs11(Box21, { borderStyle: "round", borderColor, paddingX: 1, children: [
1320
+ /* @__PURE__ */ jsx22(Text15, { color: displayValue ? theme.colors.foreground : theme.colors.mutedForeground, children: displayValue || placeholder }),
1321
+ isFocused && /* @__PURE__ */ jsx22(Text15, { color: theme.colors.focusRing, children: "\u2588" })
1322
+ ] }),
1323
+ isFocused && /* @__PURE__ */ jsx22(Text15, { color: theme.colors.mutedForeground, children: stepHint })
1324
+ ] })
1325
+ ] });
1326
+ }
1327
+
1328
+ // packages/components/src/input/SearchInput.tsx
1329
+ import { useState as useState6, useMemo } from "react";
1330
+ import { Box as Box22, Text as Text16 } from "ink";
1331
+ import { jsx as jsx23, jsxs as jsxs12 } from "react/jsx-runtime";
1332
+ function SearchInput({
1333
+ options,
1334
+ getValue,
1335
+ value: controlledValue,
1336
+ onChange,
1337
+ onSelect,
1338
+ placeholder = "Search...",
1339
+ label,
1340
+ maxResults = 5,
1341
+ id
1342
+ }) {
1343
+ const [internalValue, setInternalValue] = useState6("");
1344
+ const [selectedIndex, setSelectedIndex] = useState6(0);
1345
+ const [showResults, setShowResults] = useState6(false);
1346
+ const theme = useTheme();
1347
+ const { isFocused } = useFocus({ id });
1348
+ const query = controlledValue ?? internalValue;
1349
+ function getItemValue(item) {
1350
+ if (getValue) return getValue(item);
1351
+ return String(item);
1352
+ }
1353
+ function setQuery(newQuery) {
1354
+ onChange ? onChange(newQuery) : setInternalValue(newQuery);
1355
+ }
1356
+ const filteredResults = useMemo(() => {
1357
+ if (!options || options.length === 0) return [];
1358
+ if (!query) return options.slice(0, maxResults);
1359
+ const lower = query.toLowerCase();
1360
+ return options.filter((item) => getItemValue(item).toLowerCase().includes(lower)).slice(0, maxResults);
1361
+ }, [options, query, maxResults]);
1362
+ useInput((input, key) => {
1363
+ if (!isFocused) return;
1364
+ if (key.escape) {
1365
+ setQuery("");
1366
+ setShowResults(false);
1367
+ setSelectedIndex(0);
1368
+ return;
1369
+ }
1370
+ if (key.upArrow) {
1371
+ if (showResults && filteredResults.length > 0) {
1372
+ setSelectedIndex((i) => Math.max(0, i - 1));
1373
+ }
1374
+ return;
1375
+ }
1376
+ if (key.downArrow) {
1377
+ if (filteredResults.length > 0) {
1378
+ setShowResults(true);
1379
+ setSelectedIndex((i) => Math.min(filteredResults.length - 1, i + 1));
1380
+ }
1381
+ return;
1382
+ }
1383
+ if (key.return) {
1384
+ if (showResults && filteredResults.length > 0) {
1385
+ onSelect?.(filteredResults[selectedIndex]);
1386
+ setQuery(getItemValue(filteredResults[selectedIndex]));
1387
+ setShowResults(false);
1388
+ setSelectedIndex(0);
1389
+ }
1390
+ return;
1391
+ }
1392
+ if (key.backspace || key.delete) {
1393
+ const newQuery = query.slice(0, -1);
1394
+ setQuery(newQuery);
1395
+ setSelectedIndex(0);
1396
+ if (newQuery.length === 0) {
1397
+ setShowResults(false);
1398
+ }
1399
+ return;
1400
+ }
1401
+ if (key.tab) return;
1402
+ if (input && input.length > 0) {
1403
+ const newQuery = query + input;
1404
+ setQuery(newQuery);
1405
+ setSelectedIndex(0);
1406
+ if (options && options.length > 0) {
1407
+ setShowResults(true);
1408
+ }
1409
+ }
1410
+ });
1411
+ const borderColor = isFocused ? theme.colors.focusRing : theme.colors.border;
1412
+ const hasResults = showResults && filteredResults.length > 0;
1413
+ return /* @__PURE__ */ jsxs12(Box22, { flexDirection: "column", children: [
1414
+ label && /* @__PURE__ */ jsx23(Text16, { bold: true, children: label }),
1415
+ /* @__PURE__ */ jsxs12(Box22, { borderStyle: "round", borderColor, paddingX: 1, children: [
1416
+ /* @__PURE__ */ jsx23(Text16, { color: theme.colors.mutedForeground, children: "\u{1F50D} " }),
1417
+ /* @__PURE__ */ jsx23(Text16, { color: query ? theme.colors.foreground : theme.colors.mutedForeground, children: query || placeholder }),
1418
+ isFocused && /* @__PURE__ */ jsx23(Text16, { color: theme.colors.focusRing, children: "\u2588" })
1419
+ ] }),
1420
+ hasResults && /* @__PURE__ */ jsx23(Box22, { flexDirection: "column", paddingLeft: 2, children: filteredResults.map((item, idx) => {
1421
+ const isSelected = idx === selectedIndex;
1422
+ return /* @__PURE__ */ jsxs12(Box22, { flexDirection: "row", children: [
1423
+ /* @__PURE__ */ jsx23(Text16, { color: isSelected ? theme.colors.focusRing : theme.colors.mutedForeground, children: isSelected ? "\u203A " : " " }),
1424
+ /* @__PURE__ */ jsx23(Text16, { color: isSelected ? theme.colors.foreground : theme.colors.mutedForeground, children: getItemValue(item) })
1425
+ ] }, idx);
1426
+ }) })
1427
+ ] });
1428
+ }
1429
+
1430
+ // packages/components/src/selection/Checkbox.tsx
1431
+ import { useState as useState7 } from "react";
1432
+ import { Box as Box23, Text as Text17 } from "ink";
1433
+ import { jsx as jsx24, jsxs as jsxs13 } from "react/jsx-runtime";
1434
+ function Checkbox({
1435
+ checked: controlledChecked,
1436
+ onChange,
1437
+ label,
1438
+ indeterminate = false,
1439
+ disabled = false,
1440
+ id
1441
+ }) {
1442
+ const [internalChecked, setInternalChecked] = useState7(false);
1443
+ const theme = useTheme();
1444
+ const { isFocused } = useFocus({ id });
1445
+ const checked = controlledChecked ?? internalChecked;
1446
+ useInput((input) => {
1447
+ if (!isFocused || disabled) return;
1448
+ if (input === " ") {
1449
+ const next = !checked;
1450
+ onChange ? onChange(next) : setInternalChecked(next);
1451
+ }
1452
+ });
1453
+ const icon = indeterminate ? "\u25AA" : checked ? "\u25A0" : "\u25A1";
1454
+ const iconColor = disabled ? theme.colors.mutedForeground : checked || indeterminate ? theme.colors.primary : theme.colors.border;
1455
+ return /* @__PURE__ */ jsxs13(Box23, { gap: 1, children: [
1456
+ /* @__PURE__ */ jsx24(Text17, { color: isFocused ? theme.colors.focusRing : iconColor, bold: isFocused, children: icon }),
1457
+ label && /* @__PURE__ */ jsx24(
1458
+ Text17,
1459
+ {
1460
+ color: disabled ? theme.colors.mutedForeground : theme.colors.foreground,
1461
+ dimColor: disabled,
1462
+ children: label
1463
+ }
1464
+ )
1465
+ ] });
1466
+ }
1467
+
1468
+ // packages/components/src/selection/Select.tsx
1469
+ import { useState as useState8 } from "react";
1470
+ import { Box as Box24, Text as Text18 } from "ink";
1471
+ import { jsx as jsx25, jsxs as jsxs14 } from "react/jsx-runtime";
1472
+ function Select({
1473
+ options,
1474
+ value: controlledValue,
1475
+ onChange,
1476
+ onSubmit,
1477
+ label,
1478
+ cursor = "\u203A",
1479
+ cursorColor
1480
+ }) {
1481
+ const theme = useTheme();
1482
+ const [activeIndex, setActiveIndex] = useState8(0);
1483
+ const resolvedCursorColor = cursorColor ?? theme.colors.primary;
1484
+ useInput((input, key) => {
1485
+ if (key.upArrow) {
1486
+ setActiveIndex((i) => {
1487
+ let next = i - 1;
1488
+ while (next >= 0 && options[next]?.disabled) next--;
1489
+ return next < 0 ? i : next;
1490
+ });
1491
+ } else if (key.downArrow) {
1492
+ setActiveIndex((i) => {
1493
+ let next = i + 1;
1494
+ while (next < options.length && options[next]?.disabled) next++;
1495
+ return next >= options.length ? i : next;
1496
+ });
1497
+ } else if (key.return) {
1498
+ const opt = options[activeIndex];
1499
+ if (opt && !opt.disabled) {
1500
+ onChange?.(opt.value);
1501
+ onSubmit?.(opt.value);
1502
+ }
1503
+ }
1504
+ });
1505
+ return /* @__PURE__ */ jsxs14(Box24, { flexDirection: "column", children: [
1506
+ label && /* @__PURE__ */ jsx25(Text18, { bold: true, children: label }),
1507
+ options.map((opt, idx) => {
1508
+ const isActive = idx === activeIndex;
1509
+ const isSelected = controlledValue !== void 0 && opt.value === controlledValue;
1510
+ return /* @__PURE__ */ jsxs14(Box24, { gap: 1, children: [
1511
+ /* @__PURE__ */ jsx25(Text18, { color: isActive ? resolvedCursorColor : void 0, children: isActive ? cursor : " " }),
1512
+ /* @__PURE__ */ jsx25(
1513
+ Text18,
1514
+ {
1515
+ color: opt.disabled ? theme.colors.mutedForeground : isActive ? resolvedCursorColor : theme.colors.foreground,
1516
+ bold: isActive || isSelected,
1517
+ dimColor: opt.disabled,
1518
+ children: opt.label
1519
+ }
1520
+ ),
1521
+ opt.hint && /* @__PURE__ */ jsx25(Text18, { color: theme.colors.mutedForeground, dimColor: true, children: opt.hint })
1522
+ ] }, idx);
1523
+ })
1524
+ ] });
1525
+ }
1526
+
1527
+ // packages/components/src/selection/MultiSelect.tsx
1528
+ import { useState as useState9 } from "react";
1529
+ import { Box as Box25, Text as Text19 } from "ink";
1530
+ import { jsx as jsx26, jsxs as jsxs15 } from "react/jsx-runtime";
1531
+ function MultiSelect({
1532
+ options,
1533
+ value: controlledValue,
1534
+ onChange,
1535
+ onSubmit,
1536
+ cursor = "\u203A",
1537
+ checkmark = "\u25C9",
1538
+ height
1539
+ }) {
1540
+ const theme = useTheme();
1541
+ const [activeIndex, setActiveIndex] = useState9(0);
1542
+ const [internalSelected, setInternalSelected] = useState9([]);
1543
+ const selected = controlledValue ?? internalSelected;
1544
+ const scrollOffset = (() => {
1545
+ if (!height) return 0;
1546
+ const half = Math.floor(height / 2);
1547
+ const maxOffset = options.length - height;
1548
+ const offset = activeIndex - half;
1549
+ if (offset < 0) return 0;
1550
+ if (offset > maxOffset) return Math.max(0, maxOffset);
1551
+ return offset;
1552
+ })();
1553
+ const visibleOptions = height ? options.slice(scrollOffset, scrollOffset + height) : options;
1554
+ useInput((input, key) => {
1555
+ if (key.upArrow) {
1556
+ setActiveIndex((i) => {
1557
+ let next = i - 1;
1558
+ while (next >= 0 && options[next]?.disabled) next--;
1559
+ return next < 0 ? i : next;
1560
+ });
1561
+ } else if (key.downArrow) {
1562
+ setActiveIndex((i) => {
1563
+ let next = i + 1;
1564
+ while (next < options.length && options[next]?.disabled) next++;
1565
+ return next >= options.length ? i : next;
1566
+ });
1567
+ } else if (input === " ") {
1568
+ const opt = options[activeIndex];
1569
+ if (!opt || opt.disabled) return;
1570
+ const isSelected = selected.includes(opt.value);
1571
+ const next = isSelected ? selected.filter((v) => v !== opt.value) : [...selected, opt.value];
1572
+ if (controlledValue === void 0) {
1573
+ setInternalSelected(next);
1574
+ }
1575
+ onChange?.(next);
1576
+ } else if (key.return) {
1577
+ onSubmit?.(selected);
1578
+ }
1579
+ });
1580
+ return /* @__PURE__ */ jsx26(Box25, { flexDirection: "column", children: visibleOptions.map((opt, visibleIdx) => {
1581
+ const idx = scrollOffset + visibleIdx;
1582
+ const isActive = idx === activeIndex;
1583
+ const isSelected = selected.includes(opt.value);
1584
+ const icon = isSelected ? checkmark : "\u25CB";
1585
+ return /* @__PURE__ */ jsxs15(Box25, { gap: 1, children: [
1586
+ /* @__PURE__ */ jsx26(Text19, { color: isActive ? theme.colors.primary : void 0, children: isActive ? cursor : " " }),
1587
+ /* @__PURE__ */ jsx26(
1588
+ Text19,
1589
+ {
1590
+ color: opt.disabled ? theme.colors.mutedForeground : isSelected ? theme.colors.primary : theme.colors.foreground,
1591
+ dimColor: opt.disabled,
1592
+ children: icon
1593
+ }
1594
+ ),
1595
+ /* @__PURE__ */ jsx26(
1596
+ Text19,
1597
+ {
1598
+ color: opt.disabled ? theme.colors.mutedForeground : isActive ? theme.colors.primary : theme.colors.foreground,
1599
+ bold: isActive,
1600
+ dimColor: opt.disabled,
1601
+ children: opt.label
1602
+ }
1603
+ ),
1604
+ opt.hint && /* @__PURE__ */ jsx26(Text19, { color: theme.colors.mutedForeground, dimColor: true, children: opt.hint })
1605
+ ] }, idx);
1606
+ }) });
1607
+ }
1608
+
1609
+ // packages/components/src/selection/RadioGroup.tsx
1610
+ import { useState as useState10 } from "react";
1611
+ import { Box as Box26, Text as Text20 } from "ink";
1612
+ import { jsx as jsx27, jsxs as jsxs16 } from "react/jsx-runtime";
1613
+ function RadioGroup({
1614
+ options,
1615
+ value: controlledValue,
1616
+ onChange,
1617
+ name: _name,
1618
+ cursor = "\u203A"
1619
+ }) {
1620
+ const theme = useTheme();
1621
+ const [activeIndex, setActiveIndex] = useState10(() => {
1622
+ if (controlledValue === void 0) return 0;
1623
+ const idx = options.findIndex((o) => o.value === controlledValue);
1624
+ return idx >= 0 ? idx : 0;
1625
+ });
1626
+ const [internalValue, setInternalValue] = useState10(controlledValue);
1627
+ const selected = controlledValue ?? internalValue;
1628
+ const select = (idx) => {
1629
+ const opt = options[idx];
1630
+ if (!opt || opt.disabled) return;
1631
+ if (controlledValue === void 0) {
1632
+ setInternalValue(opt.value);
1633
+ }
1634
+ onChange?.(opt.value);
1635
+ };
1636
+ useInput((input, key) => {
1637
+ if (key.upArrow) {
1638
+ setActiveIndex((i) => {
1639
+ let next = i - 1;
1640
+ while (next >= 0 && options[next]?.disabled) next--;
1641
+ return next < 0 ? i : next;
1642
+ });
1643
+ } else if (key.downArrow) {
1644
+ setActiveIndex((i) => {
1645
+ let next = i + 1;
1646
+ while (next < options.length && options[next]?.disabled) next++;
1647
+ return next >= options.length ? i : next;
1648
+ });
1649
+ } else if (input === " " || key.return) {
1650
+ select(activeIndex);
1651
+ }
1652
+ });
1653
+ return /* @__PURE__ */ jsx27(Box26, { flexDirection: "column", children: options.map((opt, idx) => {
1654
+ const isActive = idx === activeIndex;
1655
+ const isSelected = selected !== void 0 && opt.value === selected;
1656
+ const icon = isSelected ? "\u25C9" : "\u25CB";
1657
+ return /* @__PURE__ */ jsxs16(Box26, { gap: 1, children: [
1658
+ /* @__PURE__ */ jsx27(Text20, { color: isActive ? theme.colors.primary : void 0, children: isActive ? cursor : " " }),
1659
+ /* @__PURE__ */ jsx27(
1660
+ Text20,
1661
+ {
1662
+ color: opt.disabled ? theme.colors.mutedForeground : isSelected ? theme.colors.primary : theme.colors.foreground,
1663
+ dimColor: opt.disabled,
1664
+ children: icon
1665
+ }
1666
+ ),
1667
+ /* @__PURE__ */ jsx27(
1668
+ Text20,
1669
+ {
1670
+ color: opt.disabled ? theme.colors.mutedForeground : isActive ? theme.colors.primary : theme.colors.foreground,
1671
+ bold: isActive || isSelected,
1672
+ dimColor: opt.disabled,
1673
+ children: opt.label
1674
+ }
1675
+ ),
1676
+ opt.hint && /* @__PURE__ */ jsx27(Text20, { color: theme.colors.mutedForeground, dimColor: true, children: opt.hint })
1677
+ ] }, idx);
1678
+ }) });
1679
+ }
1680
+
1681
+ // packages/components/src/selection/CheckboxGroup.tsx
1682
+ import { useState as useState11 } from "react";
1683
+ import { Box as Box27, Text as Text21 } from "ink";
1684
+ import { jsx as jsx28, jsxs as jsxs17 } from "react/jsx-runtime";
1685
+ function CheckboxGroup({
1686
+ label,
1687
+ options,
1688
+ value: controlledValue,
1689
+ onChange,
1690
+ min,
1691
+ max
1692
+ }) {
1693
+ const theme = useTheme();
1694
+ const [activeIndex, setActiveIndex] = useState11(0);
1695
+ const [internalSelected, setInternalSelected] = useState11([]);
1696
+ const [error, setError] = useState11(void 0);
1697
+ const selected = controlledValue ?? internalSelected;
1698
+ const validateAndUpdate = (next) => {
1699
+ if (min !== void 0 && next.length < min) {
1700
+ setError(`Select at least ${min} option${min === 1 ? "" : "s"}.`);
1701
+ } else if (max !== void 0 && next.length > max) {
1702
+ setError(`Select at most ${max} option${max === 1 ? "" : "s"}.`);
1703
+ return;
1704
+ } else {
1705
+ setError(void 0);
1706
+ }
1707
+ if (controlledValue === void 0) {
1708
+ setInternalSelected(next);
1709
+ }
1710
+ onChange?.(next);
1711
+ };
1712
+ useInput((input, key) => {
1713
+ if (key.upArrow) {
1714
+ setActiveIndex((i) => {
1715
+ let next = i - 1;
1716
+ while (next >= 0 && options[next]?.disabled) next--;
1717
+ return next < 0 ? i : next;
1718
+ });
1719
+ } else if (key.downArrow) {
1720
+ setActiveIndex((i) => {
1721
+ let next = i + 1;
1722
+ while (next < options.length && options[next]?.disabled) next++;
1723
+ return next >= options.length ? i : next;
1724
+ });
1725
+ } else if (input === " ") {
1726
+ const opt = options[activeIndex];
1727
+ if (!opt || opt.disabled) return;
1728
+ const isSelected = selected.includes(opt.value);
1729
+ const next = isSelected ? selected.filter((v) => v !== opt.value) : [...selected, opt.value];
1730
+ validateAndUpdate(next);
1731
+ }
1732
+ });
1733
+ return /* @__PURE__ */ jsxs17(Box27, { flexDirection: "column", children: [
1734
+ label && /* @__PURE__ */ jsx28(Text21, { bold: true, color: theme.colors.foreground, children: label }),
1735
+ options.map((opt, idx) => {
1736
+ const isActive = idx === activeIndex;
1737
+ const isSelected = selected.includes(opt.value);
1738
+ const icon = isSelected ? "\u25C9" : "\u25CB";
1739
+ return /* @__PURE__ */ jsxs17(Box27, { gap: 1, children: [
1740
+ /* @__PURE__ */ jsx28(Text21, { color: isActive ? theme.colors.primary : void 0, children: isActive ? "\u203A" : " " }),
1741
+ /* @__PURE__ */ jsx28(
1742
+ Text21,
1743
+ {
1744
+ color: opt.disabled ? theme.colors.mutedForeground : isSelected ? theme.colors.primary : theme.colors.foreground,
1745
+ dimColor: opt.disabled,
1746
+ children: icon
1747
+ }
1748
+ ),
1749
+ /* @__PURE__ */ jsx28(
1750
+ Text21,
1751
+ {
1752
+ color: opt.disabled ? theme.colors.mutedForeground : isActive ? theme.colors.primary : theme.colors.foreground,
1753
+ bold: isActive,
1754
+ dimColor: opt.disabled,
1755
+ children: opt.label
1756
+ }
1757
+ )
1758
+ ] }, idx);
1759
+ }),
1760
+ error && /* @__PURE__ */ jsx28(Text21, { color: theme.colors.error, children: error })
1761
+ ] });
1762
+ }
1763
+
1764
+ // packages/components/src/selection/TagInput.tsx
1765
+ import { useState as useState12 } from "react";
1766
+ import { Box as Box28, Text as Text22 } from "ink";
1767
+ import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
1768
+ function TagInput({
1769
+ value: controlledValue,
1770
+ onChange,
1771
+ placeholder = "Type and press Enter\u2026",
1772
+ maxTags
1773
+ }) {
1774
+ const theme = useTheme();
1775
+ const [internalTags, setInternalTags] = useState12([]);
1776
+ const [inputText, setInputText] = useState12("");
1777
+ const tags = controlledValue ?? internalTags;
1778
+ const updateTags = (next) => {
1779
+ if (controlledValue === void 0) {
1780
+ setInternalTags(next);
1781
+ }
1782
+ onChange?.(next);
1783
+ };
1784
+ const addTag = () => {
1785
+ const trimmed = inputText.trim();
1786
+ if (!trimmed) return;
1787
+ if (maxTags !== void 0 && tags.length >= maxTags) return;
1788
+ updateTags([...tags, trimmed]);
1789
+ setInputText("");
1790
+ };
1791
+ const removeLastTag = () => {
1792
+ if (tags.length === 0) return;
1793
+ updateTags(tags.slice(0, -1));
1794
+ };
1795
+ useInput((input, key) => {
1796
+ if (key.return) {
1797
+ addTag();
1798
+ } else if (key.backspace || key.delete) {
1799
+ if (inputText.length > 0) {
1800
+ setInputText((t) => t.slice(0, -1));
1801
+ } else {
1802
+ removeLastTag();
1803
+ }
1804
+ } else if (!key.ctrl && !key.meta && !key.upArrow && !key.downArrow && !key.leftArrow && !key.rightArrow && !key.escape && !key.tab) {
1805
+ if (input) {
1806
+ setInputText((t) => t + input);
1807
+ }
1808
+ }
1809
+ });
1810
+ const atMax = maxTags !== void 0 && tags.length >= maxTags;
1811
+ return /* @__PURE__ */ jsxs18(Box28, { flexDirection: "column", gap: 1, children: [
1812
+ /* @__PURE__ */ jsx29(Box28, { flexWrap: "wrap", gap: 1, children: tags.map((tag, idx) => /* @__PURE__ */ jsxs18(Box28, { children: [
1813
+ /* @__PURE__ */ jsx29(Text22, { bold: true, children: "[" }),
1814
+ /* @__PURE__ */ jsx29(Text22, { children: tag }),
1815
+ /* @__PURE__ */ jsx29(Text22, { bold: true, children: " \xD7]" })
1816
+ ] }, idx)) }),
1817
+ /* @__PURE__ */ jsxs18(Box28, { gap: 1, children: [
1818
+ /* @__PURE__ */ jsx29(Text22, { color: theme.colors.primary, children: "\u203A" }),
1819
+ atMax ? /* @__PURE__ */ jsx29(Text22, { color: theme.colors.mutedForeground, dimColor: true, children: `Max ${maxTags} tag${maxTags === 1 ? "" : "s"} reached` }) : /* @__PURE__ */ jsx29(Text22, { color: inputText ? theme.colors.foreground : theme.colors.mutedForeground, children: inputText || placeholder })
1820
+ ] })
1821
+ ] });
1822
+ }
1823
+
1824
+ // packages/components/src/data/List.tsx
1825
+ import { useState as useState13, useMemo as useMemo2 } from "react";
1826
+ import { Box as Box29, Text as Text23 } from "ink";
1827
+ import { jsx as jsx30, jsxs as jsxs19 } from "react/jsx-runtime";
1828
+ function List({
1829
+ items,
1830
+ onSelect,
1831
+ filterable = false,
1832
+ height = 10,
1833
+ cursor = "\u203A"
1834
+ }) {
1835
+ const theme = useTheme();
1836
+ const [activeIndex, setActiveIndex] = useState13(0);
1837
+ const [filter, setFilter] = useState13("");
1838
+ const filtered = useMemo2(() => {
1839
+ if (!filter) return items;
1840
+ const q = filter.toLowerCase();
1841
+ return items.filter((item) => item.label.toLowerCase().includes(q));
1842
+ }, [items, filter]);
1843
+ useInput((input, key) => {
1844
+ if (key.upArrow) {
1845
+ setActiveIndex((i) => Math.max(0, i - 1));
1846
+ } else if (key.downArrow) {
1847
+ setActiveIndex((i) => Math.min(filtered.length - 1, i + 1));
1848
+ } else if (key.return) {
1849
+ const item = filtered[activeIndex];
1850
+ if (item) onSelect?.(item);
1851
+ } else if (filterable && key.backspace) {
1852
+ setFilter((f) => f.slice(0, -1));
1853
+ } else if (filterable && !key.escape && !key.return && !key.upArrow && !key.downArrow) {
1854
+ setFilter((f) => f + input);
1855
+ }
1856
+ });
1857
+ const visible = filtered.slice(0, height);
1858
+ return /* @__PURE__ */ jsxs19(Box29, { flexDirection: "column", children: [
1859
+ filterable && /* @__PURE__ */ jsx30(Box29, { borderStyle: "round", borderColor: theme.colors.border, paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsx30(Text23, { dimColor: !filter, children: filter || "Type to filter\u2026" }) }),
1860
+ visible.map((item, idx) => {
1861
+ const isActive = idx === activeIndex;
1862
+ return /* @__PURE__ */ jsxs19(Box29, { gap: 1, children: [
1863
+ /* @__PURE__ */ jsx30(Text23, { color: isActive ? theme.colors.primary : void 0, children: isActive ? cursor : " " }),
1864
+ /* @__PURE__ */ jsx30(
1865
+ Text23,
1866
+ {
1867
+ color: item.color ?? (isActive ? theme.colors.primary : theme.colors.foreground),
1868
+ bold: isActive,
1869
+ children: item.label
1870
+ }
1871
+ ),
1872
+ item.description && /* @__PURE__ */ jsx30(Text23, { color: theme.colors.mutedForeground, dimColor: true, children: item.description })
1873
+ ] }, item.key);
1874
+ }),
1875
+ filtered.length > height && /* @__PURE__ */ jsxs19(Text23, { color: theme.colors.mutedForeground, dimColor: true, children: [
1876
+ filtered.length - height,
1877
+ " more\u2026"
1878
+ ] })
1879
+ ] });
1880
+ }
1881
+
1882
+ // packages/components/src/data/Table.tsx
1883
+ import { useState as useState14, useMemo as useMemo3 } from "react";
1884
+ import { Box as Box30, Text as Text24 } from "ink";
1885
+ import { jsx as jsx31, jsxs as jsxs20 } from "react/jsx-runtime";
1886
+ function pad(str, width, align = "left") {
1887
+ const s = String(str);
1888
+ if (s.length >= width) return s.slice(0, width);
1889
+ const diff = width - s.length;
1890
+ if (align === "right") return " ".repeat(diff) + s;
1891
+ if (align === "center") {
1892
+ const left = Math.floor(diff / 2);
1893
+ const right = diff - left;
1894
+ return " ".repeat(left) + s + " ".repeat(right);
1895
+ }
1896
+ return s + " ".repeat(diff);
1897
+ }
1898
+ function Table({
1899
+ data,
1900
+ columns,
1901
+ sortable = false,
1902
+ selectable = false,
1903
+ onSelect,
1904
+ maxRows = 20,
1905
+ borderColor
1906
+ }) {
1907
+ const theme = useTheme();
1908
+ const [sortKey, setSortKey] = useState14(null);
1909
+ const [sortDir, setSortDir] = useState14("asc");
1910
+ const [activeRow, setActiveRow] = useState14(0);
1911
+ const [sortColIdx, setSortColIdx] = useState14(0);
1912
+ const resolvedBorderColor = borderColor ?? theme.colors.border;
1913
+ const sorted = useMemo3(() => {
1914
+ if (!sortKey) return data;
1915
+ return [...data].sort((a, b) => {
1916
+ const av = a[sortKey];
1917
+ const bv = b[sortKey];
1918
+ const cmp = String(av).localeCompare(String(bv));
1919
+ return sortDir === "asc" ? cmp : -cmp;
1920
+ });
1921
+ }, [data, sortKey, sortDir]);
1922
+ const visible = sorted.slice(0, maxRows);
1923
+ useInput((input, key) => {
1924
+ if (key.upArrow) setActiveRow((r) => Math.max(0, r - 1));
1925
+ else if (key.downArrow) setActiveRow((r) => Math.min(visible.length - 1, r + 1));
1926
+ else if (key.return && selectable) onSelect?.(visible[activeRow]);
1927
+ else if (sortable && key.leftArrow) setSortColIdx((i) => Math.max(0, i - 1));
1928
+ else if (sortable && key.rightArrow) setSortColIdx((i) => Math.min(columns.length - 1, i + 1));
1929
+ else if (sortable && input === "s") {
1930
+ const col = columns[sortColIdx];
1931
+ if (!col) return;
1932
+ if (sortKey === col.key) {
1933
+ setSortDir((d) => d === "asc" ? "desc" : "asc");
1934
+ } else {
1935
+ setSortKey(col.key);
1936
+ setSortDir("asc");
1937
+ }
1938
+ }
1939
+ });
1940
+ const colWidths = columns.map((col) => {
1941
+ const dataMax = data.reduce((max, row) => Math.max(max, String(row[col.key] ?? "").length), 0);
1942
+ return col.width ?? Math.max(col.header.length, dataMax) + 2;
1943
+ });
1944
+ const headerRow = columns.map((col, i) => pad(col.header, colWidths[i] ?? col.header.length, col.align)).join(" \u2502 ");
1945
+ const separator = colWidths.map((w) => "\u2500".repeat(w)).join("\u2500\u253C\u2500");
1946
+ return /* @__PURE__ */ jsxs20(Box30, { flexDirection: "column", borderStyle: "round", borderColor: resolvedBorderColor, children: [
1947
+ /* @__PURE__ */ jsx31(Box30, { paddingX: 1, children: /* @__PURE__ */ jsx31(Text24, { bold: true, color: theme.colors.primary, children: headerRow }) }),
1948
+ /* @__PURE__ */ jsx31(Box30, { paddingX: 1, children: /* @__PURE__ */ jsx31(Text24, { color: resolvedBorderColor, children: separator }) }),
1949
+ visible.map((row, rowIdx) => {
1950
+ const isActive = rowIdx === activeRow && selectable;
1951
+ const cells = columns.map((col, i) => pad(String(row[col.key] ?? ""), colWidths[i] ?? 8, col.align)).join(" \u2502 ");
1952
+ return /* @__PURE__ */ jsx31(Box30, { paddingX: 1, children: /* @__PURE__ */ jsx31(
1953
+ Text24,
1954
+ {
1955
+ color: isActive ? theme.colors.selectionForeground : theme.colors.foreground,
1956
+ inverse: isActive,
1957
+ children: cells
1958
+ }
1959
+ ) }, rowIdx);
1960
+ }),
1961
+ data.length > maxRows && /* @__PURE__ */ jsx31(Box30, { paddingX: 1, children: /* @__PURE__ */ jsxs20(Text24, { color: theme.colors.mutedForeground, dimColor: true, children: [
1962
+ "\u2026 ",
1963
+ data.length - maxRows,
1964
+ " more rows"
1965
+ ] }) })
1966
+ ] });
1967
+ }
1968
+
1969
+ // packages/components/src/data/Card.tsx
1970
+ import { Box as Box31, Text as Text25 } from "ink";
1971
+ import { jsx as jsx32, jsxs as jsxs21 } from "react/jsx-runtime";
1972
+ function Card({ title, subtitle, children, footer, borderColor, width }) {
1973
+ const theme = useTheme();
1974
+ const resolvedBorderColor = borderColor ?? theme.colors.border;
1975
+ return /* @__PURE__ */ jsxs21(
1976
+ Box31,
1977
+ {
1978
+ flexDirection: "column",
1979
+ borderStyle: "round",
1980
+ borderColor: resolvedBorderColor,
1981
+ width,
1982
+ paddingX: 1,
1983
+ paddingY: 0,
1984
+ children: [
1985
+ (title || subtitle) && /* @__PURE__ */ jsxs21(Box31, { flexDirection: "column", paddingBottom: 1, children: [
1986
+ title && /* @__PURE__ */ jsx32(Text25, { bold: true, color: theme.colors.foreground, children: title }),
1987
+ subtitle && /* @__PURE__ */ jsx32(Text25, { dimColor: true, color: theme.colors.mutedForeground, children: subtitle })
1988
+ ] }),
1989
+ /* @__PURE__ */ jsx32(Box31, { flexDirection: "column", children }),
1990
+ footer && /* @__PURE__ */ jsxs21(Box31, { flexDirection: "column", marginTop: 1, paddingTop: 1, children: [
1991
+ /* @__PURE__ */ jsx32(Text25, { color: resolvedBorderColor, children: "\u2500".repeat(30) }),
1992
+ /* @__PURE__ */ jsx32(Box31, { marginTop: 0, children: footer })
1993
+ ] })
1994
+ ]
1995
+ }
1996
+ );
1997
+ }
1998
+
1999
+ // packages/components/src/data/KeyValue.tsx
2000
+ import { useMemo as useMemo4 } from "react";
2001
+ import { Box as Box32, Text as Text26 } from "ink";
2002
+ import { jsx as jsx33, jsxs as jsxs22 } from "react/jsx-runtime";
2003
+ function KeyValue({
2004
+ items,
2005
+ keyWidth,
2006
+ separator = ":",
2007
+ keyColor,
2008
+ valueColor
2009
+ }) {
2010
+ const theme = useTheme();
2011
+ const resolvedKeyWidth = useMemo4(() => {
2012
+ if (keyWidth !== void 0) return keyWidth;
2013
+ return items.reduce((max, item) => Math.max(max, item.key.length), 0) + 1;
2014
+ }, [items, keyWidth]);
2015
+ const resolvedKeyColor = keyColor ?? theme.colors.mutedForeground;
2016
+ const resolvedValueColor = valueColor ?? theme.colors.foreground;
2017
+ return /* @__PURE__ */ jsx33(Box32, { flexDirection: "column", children: items.map((item, idx) => {
2018
+ const paddedKey = item.key.padEnd(resolvedKeyWidth, " ");
2019
+ return /* @__PURE__ */ jsxs22(Box32, { flexDirection: "row", gap: 1, children: [
2020
+ /* @__PURE__ */ jsx33(Text26, { color: resolvedKeyColor, children: paddedKey }),
2021
+ /* @__PURE__ */ jsx33(Text26, { color: resolvedKeyColor, children: separator }),
2022
+ /* @__PURE__ */ jsx33(Text26, { color: item.color ?? resolvedValueColor, children: item.value })
2023
+ ] }, idx);
2024
+ }) });
2025
+ }
2026
+
2027
+ // packages/components/src/data/Definition.tsx
2028
+ import { Box as Box33, Text as Text27 } from "ink";
2029
+ import { jsx as jsx34, jsxs as jsxs23 } from "react/jsx-runtime";
2030
+ function Definition({ items, termColor }) {
2031
+ const theme = useTheme();
2032
+ const resolvedTermColor = termColor ?? theme.colors.primary;
2033
+ return /* @__PURE__ */ jsx34(Box33, { flexDirection: "column", children: items.map((item, idx) => /* @__PURE__ */ jsxs23(Box33, { flexDirection: "column", marginBottom: idx < items.length - 1 ? 1 : 0, children: [
2034
+ /* @__PURE__ */ jsx34(Text27, { bold: true, color: resolvedTermColor, children: item.term }),
2035
+ /* @__PURE__ */ jsx34(Box33, { paddingLeft: 2, children: /* @__PURE__ */ jsx34(Text27, { color: theme.colors.foreground, children: item.description }) })
2036
+ ] }, idx)) });
2037
+ }
2038
+
2039
+ // packages/components/src/data/VirtualList.tsx
2040
+ import { useState as useState15, useMemo as useMemo5 } from "react";
2041
+ import { Box as Box34, Text as Text28 } from "ink";
2042
+ import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
2043
+ function VirtualList({
2044
+ items,
2045
+ renderItem,
2046
+ height,
2047
+ onSelect,
2048
+ overscan = 2
2049
+ }) {
2050
+ const theme = useTheme();
2051
+ const [activeIndex, setActiveIndex] = useState15(0);
2052
+ const [windowStart, setWindowStart] = useState15(0);
2053
+ useInput((_input, key) => {
2054
+ if (key.upArrow) {
2055
+ setActiveIndex((prev) => {
2056
+ const next = Math.max(0, prev - 1);
2057
+ setWindowStart((ws) => Math.min(ws, next));
2058
+ return next;
2059
+ });
2060
+ } else if (key.downArrow) {
2061
+ setActiveIndex((prev) => {
2062
+ const next = Math.min(items.length - 1, prev + 1);
2063
+ setWindowStart((ws) => {
2064
+ if (next >= ws + height) return next - height + 1;
2065
+ return ws;
2066
+ });
2067
+ return next;
2068
+ });
2069
+ } else if (key.return) {
2070
+ const item = items[activeIndex];
2071
+ if (item !== void 0) onSelect?.(item, activeIndex);
2072
+ }
2073
+ });
2074
+ const visibleStart = Math.max(0, windowStart - overscan);
2075
+ const visibleEnd = Math.min(items.length, windowStart + height + overscan);
2076
+ const visibleItems = useMemo5(
2077
+ () => items.slice(visibleStart, visibleEnd),
2078
+ [items, visibleStart, visibleEnd]
2079
+ );
2080
+ const thumbSize = Math.max(1, Math.floor(height * height / items.length));
2081
+ const thumbPosition = items.length <= height ? 0 : Math.floor(activeIndex / (items.length - 1) * (height - thumbSize));
2082
+ const scrollbar = useMemo5(() => {
2083
+ return Array.from({ length: height }, (_, i) => {
2084
+ if (i >= thumbPosition && i < thumbPosition + thumbSize) return "\u2588";
2085
+ return "\u2502";
2086
+ });
2087
+ }, [height, thumbPosition, thumbSize]);
2088
+ return /* @__PURE__ */ jsxs24(Box34, { flexDirection: "row", children: [
2089
+ /* @__PURE__ */ jsx35(Box34, { flexDirection: "column", flexGrow: 1, children: visibleItems.map((item, localIdx) => {
2090
+ const globalIdx = visibleStart + localIdx;
2091
+ const isVisible = globalIdx >= windowStart && globalIdx < windowStart + height;
2092
+ if (!isVisible) return null;
2093
+ const isActive = globalIdx === activeIndex;
2094
+ return /* @__PURE__ */ jsx35(Box34, { children: renderItem(item, globalIdx, isActive) }, globalIdx);
2095
+ }) }),
2096
+ items.length > height && /* @__PURE__ */ jsx35(Box34, { flexDirection: "column", marginLeft: 1, children: scrollbar.map((char, i) => /* @__PURE__ */ jsx35(
2097
+ Text28,
2098
+ {
2099
+ color: char === "\u2588" ? theme.colors.primary : theme.colors.mutedForeground,
2100
+ children: char
2101
+ },
2102
+ i
2103
+ )) })
2104
+ ] });
2105
+ }
2106
+
2107
+ // packages/components/src/data/Tree.tsx
2108
+ import { useState as useState16, useMemo as useMemo6 } from "react";
2109
+ import { Box as Box35, Text as Text29 } from "ink";
2110
+ import { jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
2111
+ function flattenTree(nodes, expandedKeys, depth = 0) {
2112
+ const result = [];
2113
+ for (const node of nodes) {
2114
+ const hasChildren = Boolean(node.children && node.children.length > 0);
2115
+ result.push({ node, depth, hasChildren });
2116
+ if (hasChildren && expandedKeys.has(node.key)) {
2117
+ const childFlat = flattenTree(node.children, expandedKeys, depth + 1);
2118
+ result.push(...childFlat);
2119
+ }
2120
+ }
2121
+ return result;
2122
+ }
2123
+ function Tree({ nodes, onSelect, defaultExpanded = [] }) {
2124
+ const theme = useTheme();
2125
+ const [expandedKeys, setExpandedKeys] = useState16(new Set(defaultExpanded));
2126
+ const [activeIndex, setActiveIndex] = useState16(0);
2127
+ const flatNodes = useMemo6(() => flattenTree(nodes, expandedKeys), [nodes, expandedKeys]);
2128
+ useInput((_input, key) => {
2129
+ const current = flatNodes[activeIndex];
2130
+ if (key.upArrow) {
2131
+ setActiveIndex((i) => Math.max(0, i - 1));
2132
+ } else if (key.downArrow) {
2133
+ setActiveIndex((i) => Math.min(flatNodes.length - 1, i + 1));
2134
+ } else if (key.rightArrow || _input === " ") {
2135
+ if (current?.hasChildren && !expandedKeys.has(current.node.key)) {
2136
+ setExpandedKeys((prev) => {
2137
+ const next = new Set(prev);
2138
+ next.add(current.node.key);
2139
+ return next;
2140
+ });
2141
+ }
2142
+ } else if (key.leftArrow) {
2143
+ if (current?.hasChildren && expandedKeys.has(current.node.key)) {
2144
+ setExpandedKeys((prev) => {
2145
+ const next = new Set(prev);
2146
+ next.delete(current.node.key);
2147
+ return next;
2148
+ });
2149
+ }
2150
+ } else if (key.return) {
2151
+ if (current) onSelect?.(current.node);
2152
+ }
2153
+ });
2154
+ return /* @__PURE__ */ jsx36(Box35, { flexDirection: "column", children: flatNodes.map(({ node, depth, hasChildren }, idx) => {
2155
+ const isActive = idx === activeIndex;
2156
+ const isExpanded = expandedKeys.has(node.key);
2157
+ let prefix = " ".repeat(depth);
2158
+ let indicator;
2159
+ if (hasChildren) {
2160
+ indicator = isExpanded ? "\u25BC" : "\u25B6";
2161
+ } else {
2162
+ indicator = node.icon ?? "\u2022";
2163
+ }
2164
+ return /* @__PURE__ */ jsxs25(Box35, { flexDirection: "row", children: [
2165
+ /* @__PURE__ */ jsx36(Text29, { children: prefix }),
2166
+ /* @__PURE__ */ jsxs25(Text29, { color: isActive ? theme.colors.primary : theme.colors.mutedForeground, children: [
2167
+ indicator,
2168
+ " "
2169
+ ] }),
2170
+ /* @__PURE__ */ jsx36(Text29, { bold: isActive, color: isActive ? theme.colors.primary : theme.colors.foreground, children: node.label })
2171
+ ] }, node.key);
2172
+ }) });
2173
+ }
2174
+
2175
+ // packages/components/src/feedback/Spinner.tsx
2176
+ import { Text as Text30 } from "ink";
2177
+ import { jsx as jsx37, jsxs as jsxs26 } from "react/jsx-runtime";
2178
+ var FRAMES = {
2179
+ dots: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"],
2180
+ line: ["\u2014", "\\", "|", "/"],
2181
+ star: ["\u2736", "\u2738", "\u2739", "\u273A", "\u2739", "\u2738"],
2182
+ clock: ["\u{1F550}", "\u{1F551}", "\u{1F552}", "\u{1F553}", "\u{1F554}", "\u{1F555}", "\u{1F556}", "\u{1F557}", "\u{1F558}", "\u{1F559}", "\u{1F55A}", "\u{1F55B}"],
2183
+ bounce: ["\u2801", "\u2802", "\u2804", "\u2840", "\u2848", "\u2820", "\u2810", "\u2808"],
2184
+ bar: ["\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u258A", "\u2589", "\u2588", "\u2589", "\u258A", "\u258B", "\u258C", "\u258D", "\u258E"],
2185
+ arc: ["\u25DC", "\u25E0", "\u25DD", "\u25DE", "\u25E1", "\u25DF"],
2186
+ arrow: ["\u2190", "\u2196", "\u2191", "\u2197", "\u2192", "\u2198", "\u2193", "\u2199"],
2187
+ toggle: ["\u22B6", "\u22B7"],
2188
+ box: ["\u2596", "\u2598", "\u259D", "\u2597"],
2189
+ pipe: ["\u2524", "\u2518", "\u2534", "\u2514", "\u251C", "\u250C", "\u252C", "\u2510"],
2190
+ earth: ["\u{1F30D}", "\u{1F30E}", "\u{1F30F}"]
2191
+ };
2192
+ function Spinner({ style: spinnerStyle = "dots", label, color, fps = 12 }) {
2193
+ const theme = useTheme();
2194
+ const frame = useAnimation(fps);
2195
+ const frames = FRAMES[spinnerStyle];
2196
+ const icon = frames[frame % frames.length];
2197
+ const resolvedColor = color ?? theme.colors.primary;
2198
+ return /* @__PURE__ */ jsxs26(Text30, { children: [
2199
+ /* @__PURE__ */ jsx37(Text30, { color: resolvedColor, children: icon }),
2200
+ label && /* @__PURE__ */ jsxs26(Text30, { children: [
2201
+ " ",
2202
+ label
2203
+ ] })
2204
+ ] });
2205
+ }
2206
+
2207
+ // packages/components/src/feedback/ProgressBar.tsx
2208
+ import { Box as Box36, Text as Text31 } from "ink";
2209
+ import { jsx as jsx38, jsxs as jsxs27 } from "react/jsx-runtime";
2210
+ function ProgressBar({
2211
+ value,
2212
+ total,
2213
+ width = 30,
2214
+ showPercent = true,
2215
+ showEta = false,
2216
+ fillChar = "\u2588",
2217
+ emptyChar = "\u2591",
2218
+ color,
2219
+ label
2220
+ }) {
2221
+ const theme = useTheme();
2222
+ const resolvedColor = color ?? theme.colors.primary;
2223
+ const percent = total !== void 0 ? Math.min(100, Math.round(value / total * 100)) : Math.min(100, Math.round(value));
2224
+ const filled = Math.round(percent / 100 * width);
2225
+ const empty = width - filled;
2226
+ const bar = fillChar.repeat(filled) + emptyChar.repeat(empty);
2227
+ return /* @__PURE__ */ jsxs27(Box36, { flexDirection: "column", children: [
2228
+ label && /* @__PURE__ */ jsx38(Text31, { children: label }),
2229
+ /* @__PURE__ */ jsxs27(Box36, { gap: 1, children: [
2230
+ /* @__PURE__ */ jsx38(Text31, { color: resolvedColor, children: bar }),
2231
+ showPercent && /* @__PURE__ */ jsxs27(Text31, { color: theme.colors.mutedForeground, children: [
2232
+ percent,
2233
+ "%"
2234
+ ] }),
2235
+ total !== void 0 && /* @__PURE__ */ jsxs27(Text31, { color: theme.colors.mutedForeground, dimColor: true, children: [
2236
+ value,
2237
+ "/",
2238
+ total
2239
+ ] })
2240
+ ] })
2241
+ ] });
2242
+ }
2243
+
2244
+ // packages/components/src/feedback/Alert.tsx
2245
+ import { Box as Box37, Text as Text32 } from "ink";
2246
+ import { jsx as jsx39, jsxs as jsxs28 } from "react/jsx-runtime";
2247
+ var ICONS = {
2248
+ success: "\u2713",
2249
+ error: "\u2717",
2250
+ warning: "\u26A0",
2251
+ info: "\u2139"
2252
+ };
2253
+ function Alert({ variant = "info", title, children, icon }) {
2254
+ const theme = useTheme();
2255
+ const variantColor = (() => {
2256
+ switch (variant) {
2257
+ case "success":
2258
+ return theme.colors.success;
2259
+ case "error":
2260
+ return theme.colors.error;
2261
+ case "warning":
2262
+ return theme.colors.warning;
2263
+ default:
2264
+ return theme.colors.info;
2265
+ }
2266
+ })();
2267
+ const resolvedIcon = icon ?? ICONS[variant];
2268
+ return /* @__PURE__ */ jsxs28(
2269
+ Box37,
2270
+ {
2271
+ borderStyle: "round",
2272
+ borderColor: variantColor,
2273
+ paddingX: 1,
2274
+ paddingY: 0,
2275
+ flexDirection: "column",
2276
+ children: [
2277
+ /* @__PURE__ */ jsxs28(Box37, { gap: 1, children: [
2278
+ /* @__PURE__ */ jsx39(Text32, { color: variantColor, bold: true, children: resolvedIcon }),
2279
+ title && /* @__PURE__ */ jsx39(Text32, { bold: true, color: variantColor, children: title })
2280
+ ] }),
2281
+ children && /* @__PURE__ */ jsx39(Text32, { children })
2282
+ ]
2283
+ }
2284
+ );
2285
+ }
2286
+
2287
+ // packages/components/src/feedback/StatusMessage.tsx
2288
+ import { Box as Box38, Text as Text33 } from "ink";
2289
+ import { jsx as jsx40, jsxs as jsxs29 } from "react/jsx-runtime";
2290
+ var ICONS2 = {
2291
+ success: "\u2713",
2292
+ error: "\u2717",
2293
+ warning: "\u26A0",
2294
+ info: "\u2139",
2295
+ pending: "\u25CB"
2296
+ };
2297
+ function StatusMessage({ variant = "info", children, icon }) {
2298
+ const theme = useTheme();
2299
+ const variantColor = (() => {
2300
+ switch (variant) {
2301
+ case "success":
2302
+ return theme.colors.success;
2303
+ case "error":
2304
+ return theme.colors.error;
2305
+ case "warning":
2306
+ return theme.colors.warning;
2307
+ case "loading":
2308
+ return theme.colors.primary;
2309
+ case "pending":
2310
+ return theme.colors.muted;
2311
+ default:
2312
+ return theme.colors.info;
2313
+ }
2314
+ })();
2315
+ return /* @__PURE__ */ jsxs29(Box38, { gap: 1, flexDirection: "row", children: [
2316
+ variant === "loading" ? /* @__PURE__ */ jsx40(Spinner, { style: "dots", color: variantColor }) : /* @__PURE__ */ jsx40(Text33, { color: variantColor, children: icon ?? ICONS2[variant] }),
2317
+ /* @__PURE__ */ jsx40(Text33, { children })
2318
+ ] });
2319
+ }
2320
+
2321
+ // packages/components/src/feedback/Toast.tsx
2322
+ import { useState as useState17, useEffect } from "react";
2323
+ import { Box as Box39, Text as Text34 } from "ink";
2324
+ import { jsx as jsx41, jsxs as jsxs30 } from "react/jsx-runtime";
2325
+ var ICONS3 = {
2326
+ success: "\u2713",
2327
+ error: "\u2717",
2328
+ warning: "\u26A0",
2329
+ info: "\u2139"
2330
+ };
2331
+ var BAR_WIDTH = 20;
2332
+ var TICK_MS = 100;
2333
+ function Toast({ message, variant = "info", duration = 3e3, onDismiss, icon }) {
2334
+ const theme = useTheme();
2335
+ const [elapsed, setElapsed] = useState17(0);
2336
+ const [dismissed, setDismissed] = useState17(false);
2337
+ const variantColor = (() => {
2338
+ switch (variant) {
2339
+ case "success":
2340
+ return theme.colors.success;
2341
+ case "error":
2342
+ return theme.colors.error;
2343
+ case "warning":
2344
+ return theme.colors.warning;
2345
+ default:
2346
+ return theme.colors.info;
2347
+ }
2348
+ })();
2349
+ useEffect(() => {
2350
+ const id = setTimeout(() => {
2351
+ setDismissed(true);
2352
+ onDismiss?.();
2353
+ }, duration);
2354
+ return () => clearTimeout(id);
2355
+ }, [duration, onDismiss]);
2356
+ useInterval(
2357
+ () => {
2358
+ setElapsed((e) => Math.min(e + TICK_MS, duration));
2359
+ },
2360
+ dismissed ? null : TICK_MS
2361
+ );
2362
+ if (dismissed) return null;
2363
+ const remaining = Math.max(0, duration - elapsed);
2364
+ const remainingSeconds = (remaining / 1e3).toFixed(1);
2365
+ const progress = remaining / duration;
2366
+ const filledChars = Math.round(progress * BAR_WIDTH);
2367
+ const emptyChars = BAR_WIDTH - filledChars;
2368
+ const bar = "\u2588".repeat(filledChars) + "\u2591".repeat(emptyChars);
2369
+ const resolvedIcon = icon ?? ICONS3[variant];
2370
+ return /* @__PURE__ */ jsxs30(
2371
+ Box39,
2372
+ {
2373
+ borderStyle: "round",
2374
+ borderColor: variantColor,
2375
+ paddingX: 1,
2376
+ paddingY: 0,
2377
+ flexDirection: "column",
2378
+ children: [
2379
+ /* @__PURE__ */ jsxs30(Box39, { gap: 1, children: [
2380
+ /* @__PURE__ */ jsx41(Text34, { color: variantColor, bold: true, children: resolvedIcon }),
2381
+ /* @__PURE__ */ jsx41(Text34, { children: message })
2382
+ ] }),
2383
+ /* @__PURE__ */ jsxs30(Box39, { gap: 1, children: [
2384
+ /* @__PURE__ */ jsx41(Text34, { color: variantColor, children: bar }),
2385
+ /* @__PURE__ */ jsxs30(Text34, { color: theme.colors.muted, children: [
2386
+ remainingSeconds,
2387
+ "s"
2388
+ ] })
2389
+ ] })
2390
+ ]
2391
+ }
2392
+ );
2393
+ }
2394
+
2395
+ // packages/components/src/feedback/Banner.tsx
2396
+ import { useState as useState18 } from "react";
2397
+ import { Box as Box40, Text as Text35, useInput as useInput2 } from "ink";
2398
+ import { jsx as jsx42, jsxs as jsxs31 } from "react/jsx-runtime";
2399
+ var ICONS4 = {
2400
+ info: "\u2139",
2401
+ warning: "\u26A0",
2402
+ error: "\u2717",
2403
+ success: "\u2713",
2404
+ neutral: "\xB7"
2405
+ };
2406
+ function Banner({
2407
+ children,
2408
+ variant = "info",
2409
+ icon,
2410
+ title,
2411
+ dismissible = false,
2412
+ onDismiss
2413
+ }) {
2414
+ const theme = useTheme();
2415
+ const [dismissed, setDismissed] = useState18(false);
2416
+ const variantColor = (() => {
2417
+ switch (variant) {
2418
+ case "success":
2419
+ return theme.colors.success;
2420
+ case "error":
2421
+ return theme.colors.error;
2422
+ case "warning":
2423
+ return theme.colors.warning;
2424
+ case "neutral":
2425
+ return theme.colors.muted;
2426
+ default:
2427
+ return theme.colors.info;
2428
+ }
2429
+ })();
2430
+ useInput2((_, key) => {
2431
+ if (dismissible && key.escape) {
2432
+ setDismissed(true);
2433
+ onDismiss?.();
2434
+ }
2435
+ });
2436
+ if (dismissed) return null;
2437
+ const resolvedIcon = icon ?? ICONS4[variant];
2438
+ return /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsxs31(Box40, { flexDirection: "row", gap: 1, children: [
2439
+ /* @__PURE__ */ jsx42(Text35, { color: variantColor, children: "\u2503" }),
2440
+ /* @__PURE__ */ jsxs31(Box40, { flexDirection: "column", children: [
2441
+ /* @__PURE__ */ jsxs31(Box40, { flexDirection: "row", gap: 1, children: [
2442
+ /* @__PURE__ */ jsx42(Text35, { color: variantColor, children: resolvedIcon }),
2443
+ title && /* @__PURE__ */ jsxs31(Text35, { bold: true, color: variantColor, children: [
2444
+ title,
2445
+ ":"
2446
+ ] }),
2447
+ /* @__PURE__ */ jsx42(Text35, { children })
2448
+ ] }),
2449
+ dismissible && /* @__PURE__ */ jsx42(Text35, { color: theme.colors.muted, dimColor: true, children: "press Esc to dismiss" })
2450
+ ] })
2451
+ ] }) });
2452
+ }
2453
+
2454
+ // packages/components/src/feedback/Skeleton.tsx
2455
+ import { Box as Box41, Text as Text36 } from "ink";
2456
+ import { jsx as jsx43 } from "react/jsx-runtime";
2457
+ function Skeleton({ width = 20, height = 1, animated = true }) {
2458
+ const theme = useTheme();
2459
+ const frame = useAnimation(4);
2460
+ const offset = animated ? frame % (width + 6) : -1;
2461
+ const buildRow = () => {
2462
+ let row = "";
2463
+ for (let i = 0; i < width; i++) {
2464
+ const inHighlight = i >= offset - 3 && i <= offset + 3;
2465
+ row += inHighlight ? "\u2588" : "\u2591";
2466
+ }
2467
+ return row;
2468
+ };
2469
+ const rows = Array.from({ length: height }, (_, rowIndex) => {
2470
+ const rowOffset = animated ? (frame + rowIndex * 2) % (width + 6) : -1;
2471
+ let row = "";
2472
+ for (let i = 0; i < width; i++) {
2473
+ const inHighlight = i >= rowOffset - 3 && i <= rowOffset + 3;
2474
+ row += inHighlight ? "\u2588" : "\u2591";
2475
+ }
2476
+ return row;
2477
+ });
2478
+ return /* @__PURE__ */ jsx43(Box41, { flexDirection: "column", children: rows.map((row, i) => /* @__PURE__ */ jsx43(Text36, { children: row.split("").map((char, j) => /* @__PURE__ */ jsx43(Text36, { color: char === "\u2588" ? theme.colors.mutedForeground : theme.colors.muted, children: char }, j)) }, i)) });
2479
+ }
2480
+
2481
+ // packages/components/src/feedback/ProgressCircle.tsx
2482
+ import { Box as Box42, Text as Text37 } from "ink";
2483
+ import { jsx as jsx44, jsxs as jsxs32 } from "react/jsx-runtime";
2484
+ var BRAILLE_CHARS = ["\u25CB", "\u25D4", "\u25D1", "\u25D5", "\u25CF", "\u25C9", "\u2B24", "\u25CF"];
2485
+ function getSmChar(value) {
2486
+ const clamped = Math.max(0, Math.min(100, value));
2487
+ const step = Math.floor(clamped / 100 * 7);
2488
+ return BRAILLE_CHARS[step];
2489
+ }
2490
+ function ProgressCircle({
2491
+ value,
2492
+ size = "sm",
2493
+ color,
2494
+ label,
2495
+ showPercent = false
2496
+ }) {
2497
+ const theme = useTheme();
2498
+ const clamped = Math.max(0, Math.min(100, value));
2499
+ const resolvedColor = color ?? theme.colors.primary;
2500
+ if (size === "sm") {
2501
+ const char = getSmChar(clamped);
2502
+ return /* @__PURE__ */ jsxs32(Box42, { flexDirection: "column", alignItems: "flex-start", children: [
2503
+ /* @__PURE__ */ jsxs32(Box42, { flexDirection: "row", gap: 1, children: [
2504
+ /* @__PURE__ */ jsx44(Text37, { color: resolvedColor, children: char }),
2505
+ showPercent && /* @__PURE__ */ jsxs32(Text37, { color: theme.colors.muted, children: [
2506
+ Math.round(clamped),
2507
+ "%"
2508
+ ] })
2509
+ ] }),
2510
+ label && /* @__PURE__ */ jsx44(Text37, { color: theme.colors.muted, children: label })
2511
+ ] });
2512
+ }
2513
+ const percentLabel = `${Math.round(clamped)}%`;
2514
+ if (size === "md") {
2515
+ return /* @__PURE__ */ jsxs32(Box42, { flexDirection: "column", alignItems: "flex-start", children: [
2516
+ /* @__PURE__ */ jsxs32(Box42, { flexDirection: "row", children: [
2517
+ /* @__PURE__ */ jsx44(Text37, { color: resolvedColor, children: "\u27E8" }),
2518
+ /* @__PURE__ */ jsx44(Text37, { color: resolvedColor, bold: true, children: percentLabel }),
2519
+ /* @__PURE__ */ jsx44(Text37, { color: resolvedColor, children: "\u27E9" })
2520
+ ] }),
2521
+ label && /* @__PURE__ */ jsx44(Text37, { color: theme.colors.muted, children: label })
2522
+ ] });
2523
+ }
2524
+ const fillLevel = clamped / 100;
2525
+ const topArc = " \u2584\u2588\u2584";
2526
+ const midLeft = "\u2588";
2527
+ const midRight = "\u2588";
2528
+ const midInner = fillLevel >= 0.5 ? "\u2588\u2588\u2588" : " ";
2529
+ const botArc = " \u2580\u2588\u2580";
2530
+ return /* @__PURE__ */ jsxs32(Box42, { flexDirection: "column", alignItems: "flex-start", children: [
2531
+ /* @__PURE__ */ jsx44(Text37, { color: resolvedColor, children: topArc }),
2532
+ /* @__PURE__ */ jsxs32(Box42, { flexDirection: "row", children: [
2533
+ /* @__PURE__ */ jsx44(Text37, { color: resolvedColor, children: midLeft }),
2534
+ /* @__PURE__ */ jsx44(Text37, { color: fillLevel > 0 ? resolvedColor : theme.colors.muted, children: midInner }),
2535
+ /* @__PURE__ */ jsx44(Text37, { color: resolvedColor, children: midRight })
2536
+ ] }),
2537
+ /* @__PURE__ */ jsx44(Text37, { color: resolvedColor, children: botArc }),
2538
+ showPercent && /* @__PURE__ */ jsx44(Text37, { color: theme.colors.muted, children: percentLabel }),
2539
+ label && /* @__PURE__ */ jsx44(Text37, { color: theme.colors.muted, children: label })
2540
+ ] });
2541
+ }
2542
+
2543
+ // packages/components/src/navigation/Tabs.tsx
2544
+ import { useState as useState19 } from "react";
2545
+ import { Box as Box43, Text as Text38, useStdout } from "ink";
2546
+ import { jsx as jsx45, jsxs as jsxs33 } from "react/jsx-runtime";
2547
+ function Tabs({
2548
+ tabs,
2549
+ defaultTab,
2550
+ activeTab: controlledTab,
2551
+ onTabChange,
2552
+ borderColor
2553
+ }) {
2554
+ const theme = useTheme();
2555
+ const { stdout } = useStdout();
2556
+ const [internalTab, setInternalTab] = useState19(defaultTab ?? tabs[0]?.key ?? "");
2557
+ const activeKey = controlledTab ?? internalTab;
2558
+ const activeIndex = tabs.findIndex((t) => t.key === activeKey);
2559
+ const resolvedBorderColor = borderColor ?? theme.colors.border;
2560
+ function switchTab(nextKey) {
2561
+ if (!nextKey || nextKey === activeKey) return;
2562
+ stdout.write("\x1B[2J\x1B[H");
2563
+ onTabChange ? onTabChange(nextKey) : setInternalTab(nextKey);
2564
+ }
2565
+ useInput((input, key) => {
2566
+ if (key.leftArrow || key.shift && key.tab) {
2567
+ switchTab(tabs[Math.max(0, activeIndex - 1)]?.key);
2568
+ } else if (key.rightArrow || key.tab) {
2569
+ switchTab(tabs[Math.min(tabs.length - 1, activeIndex + 1)]?.key);
2570
+ }
2571
+ });
2572
+ const activeTab = tabs.find((t) => t.key === activeKey);
2573
+ return /* @__PURE__ */ jsxs33(Box43, { flexDirection: "column", children: [
2574
+ /* @__PURE__ */ jsx45(Box43, { paddingX: 2, gap: 0, children: tabs.map((tab, idx) => {
2575
+ const isActive = tab.key === activeKey;
2576
+ return /* @__PURE__ */ jsxs33(Box43, { children: [
2577
+ /* @__PURE__ */ jsx45(
2578
+ Text38,
2579
+ {
2580
+ color: isActive ? theme.colors.primary : theme.colors.mutedForeground,
2581
+ bold: isActive,
2582
+ underline: isActive,
2583
+ children: tab.label
2584
+ }
2585
+ ),
2586
+ idx < tabs.length - 1 && /* @__PURE__ */ jsx45(Text38, { color: resolvedBorderColor, children: " \u2502 " })
2587
+ ] }, tab.key);
2588
+ }) }),
2589
+ /* @__PURE__ */ jsx45(Box43, { borderStyle: "single", borderColor: resolvedBorderColor, paddingX: 1, paddingY: 0, children: activeTab?.content })
2590
+ ] });
2591
+ }
2592
+
2593
+ // packages/components/src/navigation/Breadcrumb.tsx
2594
+ import { Box as Box44, Text as Text39 } from "ink";
2595
+ import { jsx as jsx46, jsxs as jsxs34 } from "react/jsx-runtime";
2596
+ function Breadcrumb({ items, separator = "\u203A", activeKey }) {
2597
+ const theme = useTheme();
2598
+ const activeIndex = activeKey !== void 0 ? items.findIndex((i) => i.key === activeKey) : items.length - 1;
2599
+ useInput((_input, key) => {
2600
+ if (key.leftArrow && activeIndex > 0) {
2601
+ const prev = items[activeIndex - 1];
2602
+ if (prev?.onSelect) prev.onSelect();
2603
+ }
2604
+ });
2605
+ return /* @__PURE__ */ jsx46(Box44, { flexDirection: "row", alignItems: "center", children: items.map((item, idx) => {
2606
+ const isActive = idx === activeIndex;
2607
+ return /* @__PURE__ */ jsxs34(Box44, { flexDirection: "row", alignItems: "center", children: [
2608
+ /* @__PURE__ */ jsx46(
2609
+ Text39,
2610
+ {
2611
+ color: isActive ? theme.colors.primary : theme.colors.mutedForeground,
2612
+ bold: isActive,
2613
+ children: item.label
2614
+ }
2615
+ ),
2616
+ idx < items.length - 1 && /* @__PURE__ */ jsxs34(Text39, { color: theme.colors.mutedForeground, children: [
2617
+ " ",
2618
+ separator,
2619
+ " "
2620
+ ] })
2621
+ ] }, item.key);
2622
+ }) });
2623
+ }
2624
+
2625
+ // packages/components/src/navigation/Pagination.tsx
2626
+ import { useState as useState20 } from "react";
2627
+ import { Box as Box45, Text as Text40 } from "ink";
2628
+ import { jsx as jsx47, jsxs as jsxs35 } from "react/jsx-runtime";
2629
+ function buildPages(total, current, siblings) {
2630
+ if (total <= 7) {
2631
+ return Array.from({ length: total }, (_, i) => i + 1);
2632
+ }
2633
+ const pages = [];
2634
+ pages.push(1);
2635
+ const leftSibling = Math.max(2, current - siblings);
2636
+ const rightSibling = Math.min(total - 1, current + siblings);
2637
+ if (leftSibling > 2) {
2638
+ pages.push("...");
2639
+ }
2640
+ for (let i = leftSibling; i <= rightSibling; i++) {
2641
+ pages.push(i);
2642
+ }
2643
+ if (rightSibling < total - 1) {
2644
+ pages.push("...");
2645
+ }
2646
+ pages.push(total);
2647
+ return pages;
2648
+ }
2649
+ function Pagination({
2650
+ total,
2651
+ current,
2652
+ onChange,
2653
+ showEdges = true,
2654
+ siblings = 1
2655
+ }) {
2656
+ const theme = useTheme();
2657
+ const [internalPage, setInternalPage] = useState20(current);
2658
+ const activePage = current ?? internalPage;
2659
+ function goTo(page) {
2660
+ const clamped = Math.min(Math.max(1, page), total);
2661
+ if (clamped === activePage) return;
2662
+ if (onChange) {
2663
+ onChange(clamped);
2664
+ } else {
2665
+ setInternalPage(clamped);
2666
+ }
2667
+ }
2668
+ useInput((_input, key) => {
2669
+ if (key.leftArrow) goTo(activePage - 1);
2670
+ if (key.rightArrow) goTo(activePage + 1);
2671
+ });
2672
+ const pages = buildPages(total, activePage, siblings);
2673
+ return /* @__PURE__ */ jsxs35(Box45, { flexDirection: "row", alignItems: "center", gap: 1, children: [
2674
+ /* @__PURE__ */ jsx47(
2675
+ Text40,
2676
+ {
2677
+ color: activePage === 1 ? theme.colors.mutedForeground : theme.colors.primary,
2678
+ dimColor: activePage === 1,
2679
+ children: "\u2039"
2680
+ }
2681
+ ),
2682
+ showEdges && total > 7 ? null : null,
2683
+ pages.map((p, idx) => {
2684
+ if (p === "...") {
2685
+ return /* @__PURE__ */ jsx47(Text40, { color: theme.colors.mutedForeground, children: "\u2026" }, `ellipsis-${idx}`);
2686
+ }
2687
+ const isActive = p === activePage;
2688
+ return /* @__PURE__ */ jsx47(
2689
+ Text40,
2690
+ {
2691
+ color: isActive ? theme.colors.primary : theme.colors.mutedForeground,
2692
+ bold: isActive,
2693
+ children: isActive ? `[${p}]` : `${p}`
2694
+ },
2695
+ p
2696
+ );
2697
+ }),
2698
+ /* @__PURE__ */ jsx47(
2699
+ Text40,
2700
+ {
2701
+ color: activePage === total ? theme.colors.mutedForeground : theme.colors.primary,
2702
+ dimColor: activePage === total,
2703
+ children: "\u203A"
2704
+ }
2705
+ )
2706
+ ] });
2707
+ }
2708
+
2709
+ // packages/components/src/navigation/Menu.tsx
2710
+ import { useState as useState21 } from "react";
2711
+ import { Box as Box46, Text as Text41 } from "ink";
2712
+ import { jsx as jsx48, jsxs as jsxs36 } from "react/jsx-runtime";
2713
+ function Menu({ items, onSelect, title }) {
2714
+ const theme = useTheme();
2715
+ const [focusIndex, setFocusIndex] = useState21(0);
2716
+ const [submenuStack, setSubmenuStack] = useState21([]);
2717
+ const activeItems = submenuStack.length > 0 ? submenuStack[submenuStack.length - 1] : items;
2718
+ const selectableIndices = activeItems.map((item, idx) => ({ item, idx })).filter(({ item }) => !item.separator && !item.disabled).map(({ idx }) => idx);
2719
+ function moveFocus(direction) {
2720
+ const currentPos = selectableIndices.indexOf(focusIndex);
2721
+ const nextPos = currentPos + direction;
2722
+ if (nextPos >= 0 && nextPos < selectableIndices.length) {
2723
+ setFocusIndex(selectableIndices[nextPos]);
2724
+ }
2725
+ }
2726
+ function openSubmenu(item) {
2727
+ if (item.children && item.children.length > 0) {
2728
+ setSubmenuStack((prev) => [...prev, item.children]);
2729
+ setFocusIndex(0);
2730
+ }
2731
+ }
2732
+ function closeSubmenu() {
2733
+ if (submenuStack.length > 0) {
2734
+ setSubmenuStack((prev) => prev.slice(0, -1));
2735
+ setFocusIndex(0);
2736
+ }
2737
+ }
2738
+ function activateItem(item) {
2739
+ if (item.disabled || item.separator) return;
2740
+ if (item.children && item.children.length > 0) {
2741
+ openSubmenu(item);
2742
+ } else {
2743
+ onSelect?.(item);
2744
+ }
2745
+ }
2746
+ useInput((_input, key) => {
2747
+ if (key.upArrow) {
2748
+ moveFocus(-1);
2749
+ } else if (key.downArrow) {
2750
+ moveFocus(1);
2751
+ } else if (key.return) {
2752
+ const item = activeItems[focusIndex];
2753
+ if (item) activateItem(item);
2754
+ } else if (key.rightArrow) {
2755
+ const item = activeItems[focusIndex];
2756
+ if (item?.children && item.children.length > 0) openSubmenu(item);
2757
+ } else if (key.leftArrow) {
2758
+ closeSubmenu();
2759
+ } else if (key.escape) {
2760
+ closeSubmenu();
2761
+ }
2762
+ });
2763
+ const depth = submenuStack.length;
2764
+ return /* @__PURE__ */ jsxs36(
2765
+ Box46,
2766
+ {
2767
+ flexDirection: "column",
2768
+ borderStyle: "single",
2769
+ borderColor: theme.colors.border,
2770
+ paddingX: 1,
2771
+ paddingY: 0,
2772
+ marginLeft: depth * 2,
2773
+ children: [
2774
+ title && /* @__PURE__ */ jsx48(Box46, { marginBottom: 1, children: /* @__PURE__ */ jsx48(Text41, { bold: true, color: theme.colors.primary, children: title }) }),
2775
+ activeItems.map((item, idx) => {
2776
+ if (item.separator) {
2777
+ return /* @__PURE__ */ jsx48(Box46, { children: /* @__PURE__ */ jsx48(Text41, { color: theme.colors.mutedForeground, children: "\u2500".repeat(20) }) }, item.key);
2778
+ }
2779
+ const isFocused = idx === focusIndex;
2780
+ return /* @__PURE__ */ jsxs36(Box46, { justifyContent: "space-between", children: [
2781
+ /* @__PURE__ */ jsxs36(Box46, { flexDirection: "row", gap: 1, children: [
2782
+ /* @__PURE__ */ jsx48(Text41, { color: isFocused ? theme.colors.primary : "transparent", children: isFocused ? "\u203A" : " " }),
2783
+ item.icon && /* @__PURE__ */ jsx48(
2784
+ Text41,
2785
+ {
2786
+ color: item.disabled ? theme.colors.mutedForeground : theme.colors.foreground,
2787
+ children: item.icon
2788
+ }
2789
+ ),
2790
+ /* @__PURE__ */ jsx48(
2791
+ Text41,
2792
+ {
2793
+ color: item.disabled ? theme.colors.mutedForeground : isFocused ? theme.colors.primary : theme.colors.foreground,
2794
+ bold: isFocused && !item.disabled,
2795
+ dimColor: item.disabled,
2796
+ children: item.label
2797
+ }
2798
+ ),
2799
+ item.children && item.children.length > 0 && /* @__PURE__ */ jsx48(Text41, { color: theme.colors.mutedForeground, children: "\u203A" })
2800
+ ] }),
2801
+ item.shortcut && /* @__PURE__ */ jsx48(Text41, { color: theme.colors.mutedForeground, children: item.shortcut })
2802
+ ] }, item.key);
2803
+ })
2804
+ ]
2805
+ }
2806
+ );
2807
+ }
2808
+
2809
+ // packages/components/src/navigation/Sidebar.tsx
2810
+ import { useState as useState22 } from "react";
2811
+ import { Box as Box47, Text as Text42 } from "ink";
2812
+ import { jsx as jsx49, jsxs as jsxs37 } from "react/jsx-runtime";
2813
+ function flattenItems(items, expandedKeys, depth = 0) {
2814
+ const result = [];
2815
+ for (const item of items) {
2816
+ result.push({ item, depth });
2817
+ if (item.children && expandedKeys.has(item.key)) {
2818
+ result.push(...flattenItems(item.children, expandedKeys, depth + 1));
2819
+ }
2820
+ }
2821
+ return result;
2822
+ }
2823
+ function Sidebar({
2824
+ items,
2825
+ activeKey,
2826
+ onSelect,
2827
+ collapsed = false,
2828
+ width = 20,
2829
+ title
2830
+ }) {
2831
+ const theme = useTheme();
2832
+ const [focusIndex, setFocusIndex] = useState22(0);
2833
+ const [expandedKeys, setExpandedKeys] = useState22(/* @__PURE__ */ new Set());
2834
+ const effectiveWidth = collapsed ? 3 : width;
2835
+ const flatItems = flattenItems(items, expandedKeys);
2836
+ function toggleExpand(key) {
2837
+ setExpandedKeys((prev) => {
2838
+ const next = new Set(prev);
2839
+ if (next.has(key)) {
2840
+ next.delete(key);
2841
+ } else {
2842
+ next.add(key);
2843
+ }
2844
+ return next;
2845
+ });
2846
+ }
2847
+ useInput((_input, key) => {
2848
+ if (key.upArrow) {
2849
+ setFocusIndex((prev) => Math.max(0, prev - 1));
2850
+ } else if (key.downArrow) {
2851
+ setFocusIndex((prev) => Math.min(flatItems.length - 1, prev + 1));
2852
+ } else if (key.return) {
2853
+ const entry = flatItems[focusIndex];
2854
+ if (!entry) return;
2855
+ if (entry.item.children && entry.item.children.length > 0) {
2856
+ toggleExpand(entry.item.key);
2857
+ } else {
2858
+ onSelect?.(entry.item.key);
2859
+ }
2860
+ } else if (key.rightArrow) {
2861
+ const entry = flatItems[focusIndex];
2862
+ if (entry?.item.children && entry.item.children.length > 0) {
2863
+ setExpandedKeys((prev) => /* @__PURE__ */ new Set([...prev, entry.item.key]));
2864
+ }
2865
+ } else if (key.leftArrow) {
2866
+ const entry = flatItems[focusIndex];
2867
+ if (entry?.item.children && expandedKeys.has(entry.item.key)) {
2868
+ setExpandedKeys((prev) => {
2869
+ const next = new Set(prev);
2870
+ next.delete(entry.item.key);
2871
+ return next;
2872
+ });
2873
+ }
2874
+ }
2875
+ });
2876
+ return /* @__PURE__ */ jsxs37(
2877
+ Box47,
2878
+ {
2879
+ flexDirection: "column",
2880
+ borderStyle: "single",
2881
+ borderColor: theme.colors.border,
2882
+ width: effectiveWidth,
2883
+ paddingX: 0,
2884
+ paddingY: 0,
2885
+ children: [
2886
+ title && !collapsed && /* @__PURE__ */ jsx49(Box47, { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsx49(Text42, { bold: true, color: theme.colors.primary, children: title }) }),
2887
+ flatItems.map(({ item, depth }, idx) => {
2888
+ const isFocused = idx === focusIndex;
2889
+ const isActive = item.key === activeKey;
2890
+ const indent = collapsed ? 0 : depth * 2;
2891
+ const hasChildren = item.children && item.children.length > 0;
2892
+ const isExpanded = expandedKeys.has(item.key);
2893
+ if (collapsed) {
2894
+ return /* @__PURE__ */ jsx49(Box47, { paddingX: 0, children: /* @__PURE__ */ jsx49(
2895
+ Text42,
2896
+ {
2897
+ color: isActive ? theme.colors.primary : isFocused ? theme.colors.foreground : theme.colors.mutedForeground,
2898
+ bold: isActive,
2899
+ children: item.icon ?? item.label.charAt(0)
2900
+ }
2901
+ ) }, item.key);
2902
+ }
2903
+ return /* @__PURE__ */ jsxs37(Box47, { flexDirection: "row", alignItems: "center", children: [
2904
+ /* @__PURE__ */ jsx49(Text42, { color: isActive ? theme.colors.primary : "transparent", children: isActive ? "\u258C" : " " }),
2905
+ indent > 0 && /* @__PURE__ */ jsx49(Text42, { children: " ".repeat(indent) }),
2906
+ hasChildren ? /* @__PURE__ */ jsx49(Text42, { color: theme.colors.mutedForeground, children: isExpanded ? "\u25BE " : "\u25B8 " }) : /* @__PURE__ */ jsx49(Text42, { children: " " }),
2907
+ item.icon && /* @__PURE__ */ jsxs37(Text42, { color: isActive ? theme.colors.primary : theme.colors.mutedForeground, children: [
2908
+ item.icon,
2909
+ " "
2910
+ ] }),
2911
+ /* @__PURE__ */ jsx49(
2912
+ Text42,
2913
+ {
2914
+ color: isActive ? theme.colors.primary : isFocused ? theme.colors.foreground : theme.colors.mutedForeground,
2915
+ bold: isActive || isFocused,
2916
+ children: item.label
2917
+ }
2918
+ ),
2919
+ item.badge !== void 0 && /* @__PURE__ */ jsx49(Box47, { marginLeft: 1, children: /* @__PURE__ */ jsx49(Text42, { color: theme.colors.primary, children: item.badge }) })
2920
+ ] }, item.key);
2921
+ })
2922
+ ]
2923
+ }
2924
+ );
2925
+ }
2926
+
2927
+ // packages/components/src/overlays/Modal.tsx
2928
+ import { Box as Box48, Text as Text43 } from "ink";
2929
+ import { jsx as jsx50, jsxs as jsxs38 } from "react/jsx-runtime";
2930
+ function Modal({ open, onClose, title, width = 60, children }) {
2931
+ const theme = useTheme();
2932
+ useInput(
2933
+ (input, key) => {
2934
+ if (!open) return;
2935
+ if (key.escape) onClose();
2936
+ },
2937
+ { isActive: open }
2938
+ );
2939
+ if (!open) return null;
2940
+ return /* @__PURE__ */ jsxs38(
2941
+ Box48,
2942
+ {
2943
+ flexDirection: "column",
2944
+ borderStyle: "round",
2945
+ borderColor: theme.colors.primary,
2946
+ width,
2947
+ paddingX: 1,
2948
+ paddingY: 0,
2949
+ children: [
2950
+ title && /* @__PURE__ */ jsx50(Box48, { marginBottom: 1, borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, children: /* @__PURE__ */ jsx50(Text43, { bold: true, color: theme.colors.primary, children: title }) }),
2951
+ /* @__PURE__ */ jsx50(Box48, { flexDirection: "column", children }),
2952
+ /* @__PURE__ */ jsx50(Box48, { marginTop: 1, children: /* @__PURE__ */ jsx50(Text43, { color: theme.colors.mutedForeground, dimColor: true, children: "Press Esc to close" }) })
2953
+ ]
2954
+ }
2955
+ );
2956
+ }
2957
+
2958
+ // packages/components/src/overlays/Dialog.tsx
2959
+ import { useState as useState23 } from "react";
2960
+ import { Box as Box49, Text as Text44 } from "ink";
2961
+ import { jsx as jsx51, jsxs as jsxs39 } from "react/jsx-runtime";
2962
+ function Dialog({
2963
+ title,
2964
+ children,
2965
+ confirmLabel = "OK",
2966
+ cancelLabel = "Cancel",
2967
+ onConfirm,
2968
+ onCancel,
2969
+ variant = "default",
2970
+ isOpen = false
2971
+ }) {
2972
+ const theme = useTheme();
2973
+ const [focusedButton, setFocusedButton] = useState23(0);
2974
+ useInput(
2975
+ (_input, key) => {
2976
+ if (!isOpen) return;
2977
+ if (key.tab || key.leftArrow || key.rightArrow) {
2978
+ setFocusedButton((prev) => prev === 0 ? 1 : 0);
2979
+ } else if (key.return) {
2980
+ if (focusedButton === 1) {
2981
+ onConfirm?.();
2982
+ } else {
2983
+ onCancel?.();
2984
+ }
2985
+ } else if (key.escape) {
2986
+ onCancel?.();
2987
+ }
2988
+ },
2989
+ { isActive: isOpen }
2990
+ );
2991
+ if (!isOpen) return null;
2992
+ const confirmColor = variant === "danger" ? theme.colors.error ?? "red" : theme.colors.primary;
2993
+ return /* @__PURE__ */ jsxs39(
2994
+ Box49,
2995
+ {
2996
+ flexDirection: "column",
2997
+ borderStyle: "round",
2998
+ borderColor: variant === "danger" ? theme.colors.error ?? "red" : theme.colors.primary,
2999
+ paddingX: 1,
3000
+ paddingY: 0,
3001
+ children: [
3002
+ title && /* @__PURE__ */ jsx51(Box49, { marginBottom: 1, children: /* @__PURE__ */ jsx51(
3003
+ Text44,
3004
+ {
3005
+ bold: true,
3006
+ color: variant === "danger" ? theme.colors.error ?? "red" : theme.colors.primary,
3007
+ children: title
3008
+ }
3009
+ ) }),
3010
+ /* @__PURE__ */ jsx51(Box49, { marginBottom: 1, flexDirection: "column", children }),
3011
+ /* @__PURE__ */ jsxs39(Box49, { flexDirection: "row", gap: 2, justifyContent: "flex-end", marginTop: 1, children: [
3012
+ /* @__PURE__ */ jsxs39(
3013
+ Text44,
3014
+ {
3015
+ color: focusedButton === 0 ? theme.colors.foreground : theme.colors.mutedForeground,
3016
+ bold: focusedButton === 0,
3017
+ inverse: focusedButton === 0,
3018
+ children: [
3019
+ " ",
3020
+ cancelLabel,
3021
+ " "
3022
+ ]
3023
+ }
3024
+ ),
3025
+ /* @__PURE__ */ jsxs39(
3026
+ Text44,
3027
+ {
3028
+ color: focusedButton === 1 ? confirmColor : theme.colors.mutedForeground,
3029
+ bold: focusedButton === 1,
3030
+ inverse: focusedButton === 1,
3031
+ children: [
3032
+ " ",
3033
+ confirmLabel,
3034
+ " "
3035
+ ]
3036
+ }
3037
+ )
3038
+ ] })
3039
+ ]
3040
+ }
3041
+ );
3042
+ }
3043
+
3044
+ // packages/components/src/overlays/Drawer.tsx
3045
+ import { Box as Box50, Text as Text45 } from "ink";
3046
+ import { jsx as jsx52, jsxs as jsxs40 } from "react/jsx-runtime";
3047
+ function Drawer({
3048
+ isOpen = false,
3049
+ edge = "right",
3050
+ title,
3051
+ children,
3052
+ onClose,
3053
+ width = 40,
3054
+ height = 10
3055
+ }) {
3056
+ const theme = useTheme();
3057
+ useInput(
3058
+ (_input, key) => {
3059
+ if (!isOpen) return;
3060
+ if (key.escape) onClose?.();
3061
+ },
3062
+ { isActive: isOpen }
3063
+ );
3064
+ if (!isOpen) return null;
3065
+ const isHorizontal = edge === "left" || edge === "right";
3066
+ return /* @__PURE__ */ jsxs40(
3067
+ Box50,
3068
+ {
3069
+ flexDirection: "column",
3070
+ borderStyle: "single",
3071
+ borderColor: theme.colors.border,
3072
+ width: isHorizontal ? width : void 0,
3073
+ height: !isHorizontal ? height : void 0,
3074
+ paddingX: 1,
3075
+ paddingY: 0,
3076
+ children: [
3077
+ /* @__PURE__ */ jsxs40(Box50, { justifyContent: "space-between", marginBottom: 1, children: [
3078
+ title ? /* @__PURE__ */ jsx52(Text45, { bold: true, color: theme.colors.primary, children: title }) : /* @__PURE__ */ jsx52(Text45, { children: " " }),
3079
+ /* @__PURE__ */ jsx52(Text45, { color: theme.colors.mutedForeground, dimColor: true, children: "Esc to close" })
3080
+ ] }),
3081
+ /* @__PURE__ */ jsx52(Box50, { flexDirection: "column", flexGrow: 1, children })
3082
+ ]
3083
+ }
3084
+ );
3085
+ }
3086
+
3087
+ // packages/components/src/overlays/Tooltip.tsx
3088
+ import { Box as Box51, Text as Text46 } from "ink";
3089
+ import { jsx as jsx53, jsxs as jsxs41 } from "react/jsx-runtime";
3090
+ function Tooltip({ children, content, position = "top", isVisible = false }) {
3091
+ const theme = useTheme();
3092
+ const tooltipBox = /* @__PURE__ */ jsx53(Box51, { borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx53(Text46, { color: theme.colors.foreground, children: content }) });
3093
+ if (!isVisible) {
3094
+ return /* @__PURE__ */ jsx53(Box51, { children });
3095
+ }
3096
+ if (position === "top") {
3097
+ return /* @__PURE__ */ jsxs41(Box51, { flexDirection: "column", alignItems: "flex-start", children: [
3098
+ tooltipBox,
3099
+ /* @__PURE__ */ jsx53(Text46, { color: theme.colors.mutedForeground, children: "\u2193" }),
3100
+ /* @__PURE__ */ jsx53(Box51, { children })
3101
+ ] });
3102
+ }
3103
+ if (position === "bottom") {
3104
+ return /* @__PURE__ */ jsxs41(Box51, { flexDirection: "column", alignItems: "flex-start", children: [
3105
+ /* @__PURE__ */ jsx53(Box51, { children }),
3106
+ /* @__PURE__ */ jsx53(Text46, { color: theme.colors.mutedForeground, children: "\u2191" }),
3107
+ tooltipBox
3108
+ ] });
3109
+ }
3110
+ if (position === "left") {
3111
+ return /* @__PURE__ */ jsxs41(Box51, { flexDirection: "row", alignItems: "center", gap: 1, children: [
3112
+ tooltipBox,
3113
+ /* @__PURE__ */ jsx53(Box51, { children })
3114
+ ] });
3115
+ }
3116
+ return /* @__PURE__ */ jsxs41(Box51, { flexDirection: "row", alignItems: "center", gap: 1, children: [
3117
+ /* @__PURE__ */ jsx53(Box51, { children }),
3118
+ tooltipBox
3119
+ ] });
3120
+ }
3121
+
3122
+ // packages/components/src/overlays/Popover.tsx
3123
+ import { Box as Box52, Text as Text47 } from "ink";
3124
+ import { jsx as jsx54, jsxs as jsxs42 } from "react/jsx-runtime";
3125
+ function Popover({ trigger, children, isOpen = false, onClose, title }) {
3126
+ const theme = useTheme();
3127
+ useInput(
3128
+ (_input, key) => {
3129
+ if (!isOpen) return;
3130
+ if (key.escape) onClose?.();
3131
+ },
3132
+ { isActive: isOpen }
3133
+ );
3134
+ return /* @__PURE__ */ jsxs42(Box52, { flexDirection: "column", alignItems: "flex-start", children: [
3135
+ /* @__PURE__ */ jsx54(Box52, { children: trigger }),
3136
+ isOpen && /* @__PURE__ */ jsxs42(
3137
+ Box52,
3138
+ {
3139
+ flexDirection: "column",
3140
+ borderStyle: "single",
3141
+ borderColor: theme.colors.border,
3142
+ paddingX: 1,
3143
+ paddingY: 0,
3144
+ marginTop: 0,
3145
+ children: [
3146
+ title && /* @__PURE__ */ jsx54(Box52, { marginBottom: 1, children: /* @__PURE__ */ jsx54(Text47, { bold: true, color: theme.colors.primary, children: title }) }),
3147
+ /* @__PURE__ */ jsx54(Box52, { flexDirection: "column", children }),
3148
+ /* @__PURE__ */ jsx54(Box52, { marginTop: 1, children: /* @__PURE__ */ jsx54(Text47, { color: theme.colors.mutedForeground, dimColor: true, children: "Press Esc to close" }) })
3149
+ ]
3150
+ }
3151
+ )
3152
+ ] });
3153
+ }
3154
+
3155
+ // packages/components/src/forms/Form.tsx
3156
+ import { useState as useState24, useCallback, createContext, useContext } from "react";
3157
+ import { Box as Box53, Text as Text48 } from "ink";
3158
+ import { jsx as jsx55, jsxs as jsxs43 } from "react/jsx-runtime";
3159
+ var FormContext = createContext({
3160
+ values: {},
3161
+ errors: {},
3162
+ isDirty: false,
3163
+ setFieldValue: () => {
3164
+ },
3165
+ setFieldError: () => {
3166
+ }
3167
+ });
3168
+ function useFormContext() {
3169
+ return useContext(FormContext);
3170
+ }
3171
+ function Form({ onSubmit, initialValues = {}, fields = [], children }) {
3172
+ const theme = useTheme();
3173
+ const [values, setValues] = useState24(initialValues);
3174
+ const [errors, setErrors] = useState24({});
3175
+ const [isDirty, setIsDirty] = useState24(false);
3176
+ const setFieldValue = useCallback((name, value) => {
3177
+ setValues((v) => ({ ...v, [name]: value }));
3178
+ setIsDirty(true);
3179
+ }, []);
3180
+ const setFieldError = useCallback((name, error) => {
3181
+ setErrors((e) => ({ ...e, [name]: error }));
3182
+ }, []);
3183
+ useInput((input, key) => {
3184
+ if (key.ctrl && input === "s") {
3185
+ const newErrors = {};
3186
+ for (const field of fields) {
3187
+ const err = field.validate ? field.validate(values[field.name]) : null;
3188
+ if (err) newErrors[field.name] = err;
3189
+ }
3190
+ if (Object.keys(newErrors).length > 0) {
3191
+ setErrors(newErrors);
3192
+ return;
3193
+ }
3194
+ onSubmit(values);
3195
+ }
3196
+ });
3197
+ return /* @__PURE__ */ jsx55(FormContext.Provider, { value: { values, errors, isDirty, setFieldValue, setFieldError }, children: /* @__PURE__ */ jsxs43(Box53, { flexDirection: "column", gap: 1, children: [
3198
+ children,
3199
+ /* @__PURE__ */ jsx55(Text48, { color: theme.colors.mutedForeground, dimColor: true, children: "Press Ctrl+S to submit" })
3200
+ ] }) });
3201
+ }
3202
+
3203
+ // packages/components/src/forms/FormField.tsx
3204
+ import { Box as Box54, Text as Text49 } from "ink";
3205
+ import { jsx as jsx56, jsxs as jsxs44 } from "react/jsx-runtime";
3206
+ function FormField({ label, children, error, hint, required }) {
3207
+ const theme = useTheme();
3208
+ return /* @__PURE__ */ jsxs44(Box54, { flexDirection: "column", gap: 0, children: [
3209
+ /* @__PURE__ */ jsxs44(Box54, { gap: 0, children: [
3210
+ /* @__PURE__ */ jsx56(Text49, { bold: true, children: label }),
3211
+ required && /* @__PURE__ */ jsx56(Text49, { color: theme.colors.error, children: " *" })
3212
+ ] }),
3213
+ /* @__PURE__ */ jsx56(Box54, { children }),
3214
+ hint && !error && /* @__PURE__ */ jsx56(Text49, { color: theme.colors.mutedForeground, dimColor: true, children: hint }),
3215
+ error && /* @__PURE__ */ jsxs44(Text49, { color: theme.colors.error, children: [
3216
+ "\u2717 ",
3217
+ error
3218
+ ] })
3219
+ ] });
3220
+ }
3221
+
3222
+ // packages/components/src/forms/Confirm.tsx
3223
+ import { useState as useState25 } from "react";
3224
+ import { Box as Box55, Text as Text50 } from "ink";
3225
+ import { jsx as jsx57, jsxs as jsxs45 } from "react/jsx-runtime";
3226
+ function Confirm({
3227
+ message,
3228
+ onConfirm,
3229
+ onCancel,
3230
+ confirmLabel = "Yes",
3231
+ cancelLabel = "No",
3232
+ defaultValue = false,
3233
+ variant = "default"
3234
+ }) {
3235
+ const theme = useTheme();
3236
+ const [selected, setSelected] = useState25(defaultValue);
3237
+ useInput((input, key) => {
3238
+ if (key.leftArrow || key.rightArrow) {
3239
+ setSelected((s) => !s);
3240
+ } else if (key.return) {
3241
+ if (selected) {
3242
+ onConfirm?.();
3243
+ } else {
3244
+ onCancel?.();
3245
+ }
3246
+ } else if (input === "y" || input === "Y") {
3247
+ onConfirm?.();
3248
+ } else if (input === "n" || input === "N") {
3249
+ onCancel?.();
3250
+ }
3251
+ });
3252
+ const yesColor = variant === "danger" ? theme.colors.error : theme.colors.primary;
3253
+ return /* @__PURE__ */ jsxs45(Box55, { flexDirection: "column", gap: 0, children: [
3254
+ /* @__PURE__ */ jsxs45(Text50, { children: [
3255
+ /* @__PURE__ */ jsx57(Text50, { color: theme.colors.primary, children: "? " }),
3256
+ message
3257
+ ] }),
3258
+ /* @__PURE__ */ jsxs45(Box55, { gap: 2, paddingLeft: 2, children: [
3259
+ /* @__PURE__ */ jsx57(Box55, { gap: 1, children: selected ? /* @__PURE__ */ jsxs45(Text50, { color: yesColor, bold: true, children: [
3260
+ "\u203A ",
3261
+ confirmLabel
3262
+ ] }) : /* @__PURE__ */ jsxs45(Text50, { color: theme.colors.mutedForeground, children: [
3263
+ " ",
3264
+ confirmLabel
3265
+ ] }) }),
3266
+ /* @__PURE__ */ jsx57(Box55, { gap: 1, children: !selected ? /* @__PURE__ */ jsxs45(Text50, { bold: true, children: [
3267
+ "\u203A ",
3268
+ cancelLabel
3269
+ ] }) : /* @__PURE__ */ jsxs45(Text50, { color: theme.colors.mutedForeground, children: [
3270
+ " ",
3271
+ cancelLabel
3272
+ ] }) })
3273
+ ] })
3274
+ ] });
3275
+ }
3276
+
3277
+ // packages/components/src/forms/Wizard.tsx
3278
+ import { useState as useState26 } from "react";
3279
+ import { Box as Box56, Text as Text51 } from "ink";
3280
+ import { jsx as jsx58, jsxs as jsxs46 } from "react/jsx-runtime";
3281
+ function Wizard({ steps, onComplete, onCancel, showProgress = true }) {
3282
+ const theme = useTheme();
3283
+ const [currentStep, setCurrentStep] = useState26(0);
3284
+ const [validationError, setValidationError] = useState26(null);
3285
+ const isLast = currentStep === steps.length - 1;
3286
+ const isFirst = currentStep === 0;
3287
+ useInput((input, key) => {
3288
+ if (key.escape) {
3289
+ onCancel?.();
3290
+ return;
3291
+ }
3292
+ const goNext = key.tab && !key.shift || key.rightArrow;
3293
+ const goBack = key.tab && key.shift || key.leftArrow;
3294
+ if (goNext) {
3295
+ const step = steps[currentStep];
3296
+ if (step.validate) {
3297
+ const result = step.validate();
3298
+ if (result !== true) {
3299
+ setValidationError(typeof result === "string" ? result : "Validation failed");
3300
+ return;
3301
+ }
3302
+ }
3303
+ setValidationError(null);
3304
+ if (isLast) {
3305
+ onComplete?.(steps.map((s) => s.key));
3306
+ } else {
3307
+ setCurrentStep((i) => i + 1);
3308
+ }
3309
+ } else if (goBack) {
3310
+ setValidationError(null);
3311
+ if (!isFirst) {
3312
+ setCurrentStep((i) => i - 1);
3313
+ }
3314
+ }
3315
+ });
3316
+ return /* @__PURE__ */ jsxs46(Box56, { flexDirection: "column", gap: 1, children: [
3317
+ showProgress && /* @__PURE__ */ jsx58(Box56, { flexDirection: "row", alignItems: "center", children: steps.map((step, index) => {
3318
+ const isCompleted = index < currentStep;
3319
+ const isCurrent = index === currentStep;
3320
+ let icon;
3321
+ let iconColor;
3322
+ if (isCompleted) {
3323
+ icon = "\u25CF";
3324
+ iconColor = theme.colors.success;
3325
+ } else if (isCurrent) {
3326
+ icon = "\u25C9";
3327
+ iconColor = theme.colors.primary;
3328
+ } else {
3329
+ icon = "\u25CB";
3330
+ iconColor = theme.colors.mutedForeground;
3331
+ }
3332
+ return /* @__PURE__ */ jsxs46(Box56, { flexDirection: "row", alignItems: "center", children: [
3333
+ /* @__PURE__ */ jsxs46(Text51, { color: iconColor, bold: isCurrent, children: [
3334
+ icon,
3335
+ " ",
3336
+ step.title
3337
+ ] }),
3338
+ index < steps.length - 1 && /* @__PURE__ */ jsx58(Text51, { color: theme.colors.mutedForeground, children: " \u2500\u2500\u2500 " })
3339
+ ] }, step.key);
3340
+ }) }),
3341
+ /* @__PURE__ */ jsx58(Box56, { flexDirection: "column", gap: 1, children: steps[currentStep]?.content }),
3342
+ validationError && /* @__PURE__ */ jsxs46(Text51, { color: theme.colors.error, children: [
3343
+ "\u2717 ",
3344
+ validationError
3345
+ ] }),
3346
+ /* @__PURE__ */ jsxs46(Box56, { flexDirection: "row", gap: 2, children: [
3347
+ !isFirst && /* @__PURE__ */ jsx58(Text51, { color: theme.colors.mutedForeground, children: "[\u2190 Back]" }),
3348
+ isLast ? /* @__PURE__ */ jsx58(Text51, { color: theme.colors.primary, bold: true, children: "[Finish]" }) : /* @__PURE__ */ jsx58(Text51, { color: theme.colors.primary, bold: true, children: "[Next \u2192]" })
3349
+ ] })
3350
+ ] });
3351
+ }
3352
+
3353
+ // packages/components/src/utility/Panel.tsx
3354
+ import { Box as Box57, Text as Text52 } from "ink";
3355
+ import { jsx as jsx59, jsxs as jsxs47 } from "react/jsx-runtime";
3356
+ function Panel({
3357
+ title,
3358
+ titleColor,
3359
+ borderColor,
3360
+ borderStyle,
3361
+ width,
3362
+ height,
3363
+ paddingX = 1,
3364
+ paddingY = 0,
3365
+ children
3366
+ }) {
3367
+ const theme = useTheme();
3368
+ return /* @__PURE__ */ jsxs47(
3369
+ Box57,
3370
+ {
3371
+ flexDirection: "column",
3372
+ borderStyle: borderStyle ?? theme.border.style,
3373
+ borderColor: borderColor ?? theme.colors.border,
3374
+ width,
3375
+ height,
3376
+ children: [
3377
+ title && /* @__PURE__ */ jsx59(
3378
+ Box57,
3379
+ {
3380
+ paddingX,
3381
+ borderStyle: "single",
3382
+ borderColor: borderColor ?? theme.colors.border,
3383
+ children: /* @__PURE__ */ jsx59(Text52, { bold: true, color: titleColor ?? theme.colors.primary, children: title })
3384
+ }
3385
+ ),
3386
+ /* @__PURE__ */ jsx59(Box57, { flexDirection: "column", paddingX, paddingY, children })
3387
+ ]
3388
+ }
3389
+ );
3390
+ }
3391
+
3392
+ // packages/components/src/utility/Toggle.tsx
3393
+ import { useState as useState27 } from "react";
3394
+ import { Box as Box58, Text as Text53 } from "ink";
3395
+ import { jsx as jsx60, jsxs as jsxs48 } from "react/jsx-runtime";
3396
+ function Toggle({
3397
+ checked: controlledChecked,
3398
+ onChange,
3399
+ label,
3400
+ onLabel = "ON",
3401
+ offLabel = "OFF",
3402
+ id,
3403
+ disabled = false
3404
+ }) {
3405
+ const theme = useTheme();
3406
+ const { isFocused } = useFocus({ id });
3407
+ const [internalChecked, setInternalChecked] = useState27(false);
3408
+ const checked = controlledChecked ?? internalChecked;
3409
+ useInput((input) => {
3410
+ if (!isFocused || disabled) return;
3411
+ if (input === " ") {
3412
+ const next = !checked;
3413
+ onChange ? onChange(next) : setInternalChecked(next);
3414
+ }
3415
+ });
3416
+ const trackColor = checked ? theme.colors.success : theme.colors.mutedForeground;
3417
+ const focusColor = isFocused ? theme.colors.focusRing : trackColor;
3418
+ const stateLabel = checked ? onLabel : offLabel;
3419
+ return /* @__PURE__ */ jsxs48(Box58, { gap: 1, alignItems: "center", children: [
3420
+ /* @__PURE__ */ jsx60(Box58, { borderStyle: "round", borderColor: focusColor, paddingX: 1, children: /* @__PURE__ */ jsxs48(Text53, { color: focusColor, bold: checked, children: [
3421
+ checked ? "\u25CF" : "\u25CB",
3422
+ " ",
3423
+ stateLabel
3424
+ ] }) }),
3425
+ label && /* @__PURE__ */ jsx60(Text53, { color: disabled ? theme.colors.mutedForeground : theme.colors.foreground, children: label })
3426
+ ] });
3427
+ }
3428
+
3429
+ export {
3430
+ Box,
3431
+ Stack,
3432
+ Grid,
3433
+ ScrollView,
3434
+ Divider,
3435
+ Spacer,
3436
+ Columns,
3437
+ Center,
3438
+ AspectRatio,
3439
+ Text3 as Text,
3440
+ Badge,
3441
+ Heading,
3442
+ Code,
3443
+ Link,
3444
+ Tag,
3445
+ gradientText,
3446
+ Gradient,
3447
+ BigText,
3448
+ Digits,
3449
+ TextInput,
3450
+ TextArea,
3451
+ PasswordInput,
3452
+ NumberInput,
3453
+ SearchInput,
3454
+ Checkbox,
3455
+ Select,
3456
+ MultiSelect,
3457
+ RadioGroup,
3458
+ CheckboxGroup,
3459
+ TagInput,
3460
+ List,
3461
+ Table,
3462
+ Card,
3463
+ KeyValue,
3464
+ Definition,
3465
+ VirtualList,
3466
+ Tree,
3467
+ Spinner,
3468
+ ProgressBar,
3469
+ Alert,
3470
+ StatusMessage,
3471
+ Toast,
3472
+ Banner,
3473
+ Skeleton,
3474
+ ProgressCircle,
3475
+ Tabs,
3476
+ Breadcrumb,
3477
+ Pagination,
3478
+ Menu,
3479
+ Sidebar,
3480
+ Modal,
3481
+ Dialog,
3482
+ Drawer,
3483
+ Tooltip,
3484
+ Popover,
3485
+ useFormContext,
3486
+ Form,
3487
+ FormField,
3488
+ Confirm,
3489
+ Wizard,
3490
+ Panel,
3491
+ Toggle
3492
+ };
3493
+ //# sourceMappingURL=chunk-B2VO7M2O.js.map