@gridland/demo 0.2.18 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/run.js CHANGED
@@ -1,32 +1,2135 @@
1
- import {
2
- ChatPanel,
3
- GRADIENTS,
4
- Gradient,
5
- LandingApp,
6
- LinkDemo,
7
- Message,
8
- Modal,
9
- MultiSelect,
10
- PromptInput,
11
- SelectInput,
12
- SpinnerPicker,
13
- StatusBar,
14
- TabBar,
15
- Table,
16
- TextInput,
17
- Timeline,
18
- textStyle
19
- } from "./chunk-R3ENBVV6.js";
20
-
21
1
  // src/run.tsx
22
2
  import { createCliRenderer } from "@opentui/core";
23
3
  import { createRoot, useKeyboard as useKeyboard2 } from "@opentui/react";
24
4
 
25
5
  // ../ui/scripts/demo-apps.tsx
26
- import { useState, useCallback, useRef, useEffect } from "react";
6
+ import { useState as useState11, useCallback as useCallback3, useRef as useRef8, useEffect as useEffect5 } from "react";
27
7
  import { useKeyboard } from "@opentui/react";
8
+
9
+ // ../ui/components/theme/themes.ts
10
+ var darkTheme = {
11
+ primary: "#FF71CE",
12
+ accent: "#01CDFE",
13
+ secondary: "#B967FF",
14
+ muted: "#A69CBD",
15
+ placeholder: "#CEC7DE",
16
+ border: "#B967FF",
17
+ foreground: "#F0E6FF",
18
+ background: "#0D0B10",
19
+ success: "#05FFA1",
20
+ error: "#FF6B6B",
21
+ warning: "#FFC164"
22
+ };
23
+
24
+ // ../ui/components/theme/theme-context.tsx
25
+ import { createContext, useContext } from "react";
26
+ import { jsx } from "react/jsx-runtime";
27
+ var ThemeContext = createContext(darkTheme);
28
+ function useTheme() {
29
+ return useContext(ThemeContext);
30
+ }
31
+
32
+ // ../ui/components/link/link.tsx
33
+ import { jsx as jsx2 } from "react/jsx-runtime";
34
+ var UNDERLINE = 1 << 3;
35
+ var UNDERLINE_DASHED = 1 << 4;
36
+ var UNDERLINE_DOTTED = 1 << 6;
37
+ function Link({ children, url, underline = "solid", color }) {
38
+ const theme = useTheme();
39
+ const resolvedColor = color ?? theme.accent;
40
+ let attributes = 0;
41
+ if (underline === "solid") {
42
+ attributes = UNDERLINE;
43
+ } else if (underline === "dashed") {
44
+ attributes = UNDERLINE | UNDERLINE_DASHED;
45
+ } else if (underline === "dotted") {
46
+ attributes = UNDERLINE | UNDERLINE_DOTTED;
47
+ }
48
+ return /* @__PURE__ */ jsx2("text", { children: /* @__PURE__ */ jsx2("a", { href: url, style: { attributes, fg: resolvedColor }, children }) });
49
+ }
50
+
51
+ // ../ui/components/link/link-demo.tsx
52
+ import { useState } from "react";
53
+
54
+ // ../ui/components/text-style.ts
55
+ var BOLD = 1 << 0;
56
+ var DIM = 1 << 1;
57
+ var ITALIC = 1 << 2;
58
+ var UNDERLINE2 = 1 << 3;
59
+ var INVERSE = 1 << 5;
60
+ function textStyle(opts) {
61
+ let attributes = 0;
62
+ if (opts.bold) attributes |= BOLD;
63
+ if (opts.dim) attributes |= DIM;
64
+ if (opts.italic) attributes |= ITALIC;
65
+ if (opts.underline) attributes |= UNDERLINE2;
66
+ if (opts.inverse) attributes |= INVERSE;
67
+ const result = {};
68
+ if (opts.fg) result.fg = opts.fg;
69
+ if (opts.bg) result.bg = opts.bg;
70
+ if (attributes) result.attributes = attributes;
71
+ return result;
72
+ }
73
+
74
+ // ../ui/components/status-bar/status-bar.tsx
75
+ import { jsx as jsx3 } from "react/jsx-runtime";
76
+ function StatusBar({ items, extra }) {
77
+ const theme = useTheme();
78
+ const parts = [];
79
+ if (extra !== void 0) {
80
+ parts.push(
81
+ /* @__PURE__ */ jsx3("span", { children: extra }, "extra")
82
+ );
83
+ parts.push(
84
+ /* @__PURE__ */ jsx3("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: " \u2502 " }, "pipe")
85
+ );
86
+ }
87
+ items.forEach((item, i) => {
88
+ if (i > 0) {
89
+ parts.push(/* @__PURE__ */ jsx3("span", { children: " " }, `gap-${i}`));
90
+ }
91
+ parts.push(
92
+ /* @__PURE__ */ jsx3("span", { style: textStyle({ bold: true, fg: theme.background, bg: theme.muted }), children: ` ${item.key} ` }, `key-${i}`)
93
+ );
94
+ parts.push(
95
+ /* @__PURE__ */ jsx3("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: ` ${item.label}` }, `label-${i}`)
96
+ );
97
+ });
98
+ if (parts.length === 0) {
99
+ return null;
100
+ }
101
+ return /* @__PURE__ */ jsx3("text", { children: parts });
102
+ }
103
+
104
+ // ../ui/components/provider/provider.tsx
105
+ import { createContext as createContext2, useContext as useContext2 } from "react";
106
+ import { jsx as jsx4 } from "react/jsx-runtime";
107
+ var KeyboardContext = createContext2(null);
108
+ function useKeyboardContext(propOverride) {
109
+ const fromContext = useContext2(KeyboardContext);
110
+ return propOverride ?? fromContext ?? void 0;
111
+ }
112
+
113
+ // ../ui/components/link/link-demo.tsx
114
+ import { jsx as jsx5, jsxs } from "react/jsx-runtime";
115
+ var MODES = ["solid", "dashed", "dotted", "none"];
116
+ function LinkDemo({
117
+ url = "https://opentui.com",
118
+ label = "Visit opentui.com",
119
+ useKeyboard: useKeyboardProp
120
+ }) {
121
+ const useKeyboard3 = useKeyboardContext(useKeyboardProp);
122
+ const [modeIndex, setModeIndex] = useState(0);
123
+ const mode = MODES[modeIndex];
124
+ useKeyboard3?.((event) => {
125
+ if (event.name === "right") {
126
+ setModeIndex((i) => (i + 1) % MODES.length);
127
+ } else if (event.name === "left") {
128
+ setModeIndex((i) => (i - 1 + MODES.length) % MODES.length);
129
+ }
130
+ });
131
+ return /* @__PURE__ */ jsxs("box", { flexDirection: "column", gap: 1, children: [
132
+ /* @__PURE__ */ jsx5(Link, { url, underline: mode, children: label }),
133
+ /* @__PURE__ */ jsx5(
134
+ StatusBar,
135
+ {
136
+ extra: /* @__PURE__ */ jsx5("span", { style: textStyle({ bold: true }), children: mode.padEnd(6) }),
137
+ items: [
138
+ { key: "\u2190\u2192", label: "underline style" }
139
+ ]
140
+ }
141
+ )
142
+ ] });
143
+ }
144
+
145
+ // ../ui/components/ascii/ascii.tsx
146
+ import { jsx as jsx6 } from "react/jsx-runtime";
147
+
148
+ // ../ui/components/spinner/spinner.tsx
149
+ import { useEffect, useState as useState2 } from "react";
150
+ import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
151
+ var VARIANTS = {
152
+ dots: { frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"], interval: 83 },
153
+ pulse: { frames: ["\xB7", "\u2219", "\u25CF", "\u2219", "\xB7", "\xB7", "\xB7"], interval: 180 },
154
+ meter: { frames: ["\u25B1\u25B1\u25B1", "\u25B0\u25B1\u25B1", "\u25B0\u25B0\u25B1", "\u25B0\u25B0\u25B0", "\u25B0\u25B0\u25B1", "\u25B0\u25B1\u25B1", "\u25B1\u25B1\u25B1"], interval: 143 },
155
+ bloom: { frames: ["\xB7", "\u2726", "\u2727", "\u2739", "\u273A", "\u274B", "\u2738", "\u2735", "\u2738", "\u274B", "\u273A", "\u2739", "\u2727", "\u2726", "\xB7", "\xB7"], interval: 100 },
156
+ ellipsis: { frames: [" ", ". ", ".. ", "..."], interval: 333 }
157
+ };
158
+ var VARIANT_NAMES = Object.keys(VARIANTS);
159
+ function Spinner({ variant = "dots", text, color }) {
160
+ const theme = useTheme();
161
+ const resolvedColor = color ?? theme.accent;
162
+ const { frames, interval } = VARIANTS[variant];
163
+ const [frame, setFrame] = useState2(0);
164
+ useEffect(() => {
165
+ setFrame(0);
166
+ const timer = setInterval(() => {
167
+ setFrame((prev) => (prev + 1) % frames.length);
168
+ }, interval);
169
+ return () => clearInterval(timer);
170
+ }, [variant]);
171
+ return /* @__PURE__ */ jsxs2("text", { children: [
172
+ /* @__PURE__ */ jsx7("span", { style: { fg: resolvedColor }, children: frames[frame] }),
173
+ text ? /* @__PURE__ */ jsxs2("span", { style: { fg: theme.foreground }, children: [
174
+ " ",
175
+ text
176
+ ] }) : null
177
+ ] });
178
+ }
179
+
180
+ // ../ui/components/spinner/spinner-showcase.tsx
181
+ import { useState as useState3 } from "react";
182
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
183
+ function SpinnerPicker({ useKeyboard: useKeyboardProp }) {
184
+ const theme = useTheme();
185
+ const useKeyboard3 = useKeyboardContext(useKeyboardProp);
186
+ const [selected, setSelected] = useState3(0);
187
+ useKeyboard3?.((event) => {
188
+ if (event.name === "left") {
189
+ setSelected((s) => s > 0 ? s - 1 : VARIANT_NAMES.length - 1);
190
+ } else if (event.name === "right") {
191
+ setSelected((s) => s < VARIANT_NAMES.length - 1 ? s + 1 : 0);
192
+ }
193
+ });
194
+ const selectedName = VARIANT_NAMES[selected];
195
+ return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
196
+ /* @__PURE__ */ jsx8("box", { padding: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: /* @__PURE__ */ jsx8(Spinner, { variant: selectedName, color: theme.accent }) }),
197
+ /* @__PURE__ */ jsx8(
198
+ StatusBar,
199
+ {
200
+ items: [{ key: "\u2190\u2192", label: "change spinner type" }],
201
+ extra: /* @__PURE__ */ jsx8("span", { style: textStyle({ fg: theme.accent, bold: true }), children: selectedName.padEnd(8) })
202
+ }
203
+ )
204
+ ] });
205
+ }
206
+
207
+ // ../ui/components/text-input/text-input.tsx
208
+ import { useCallback, useRef, useState as useState4 } from "react";
209
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
210
+ function TextInput({ label, description, error, required = false, disabled = false, value: controlledValue, onChange, onSubmit, placeholder, prompt, focus = true, maxLength }) {
211
+ const theme = useTheme();
212
+ const [internalValue, setInternalValue] = useState4("");
213
+ const isControlled = controlledValue !== void 0;
214
+ const controlledRef = useRef(isControlled);
215
+ if (controlledRef.current !== isControlled) {
216
+ console.warn("TextInput: switching between controlled and uncontrolled is not supported.");
217
+ controlledRef.current = isControlled;
218
+ }
219
+ const current = isControlled ? controlledValue : internalValue;
220
+ const isFocused = focus && !disabled;
221
+ const handleInput = useCallback(
222
+ (v) => {
223
+ if (!isControlled) setInternalValue(v);
224
+ onChange?.(v);
225
+ },
226
+ [isControlled, onChange]
227
+ );
228
+ const handleSubmit = useCallback(
229
+ (v) => {
230
+ onSubmit?.(v);
231
+ if (!isControlled) setInternalValue("");
232
+ },
233
+ [isControlled, onSubmit]
234
+ );
235
+ const empty = !current;
236
+ const showLabel = label != null;
237
+ const message = error ?? description;
238
+ return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", children: [
239
+ showLabel && /* @__PURE__ */ jsxs4("text", { children: [
240
+ /* @__PURE__ */ jsxs4("span", { style: textStyle({ fg: error ? theme.error : isFocused ? theme.primary : disabled ? theme.muted : theme.foreground, bold: !disabled, dim: disabled }), children: [
241
+ isFocused ? "\u25B8 " : " ",
242
+ label
243
+ ] }),
244
+ required && /* @__PURE__ */ jsx9("span", { style: textStyle({ fg: theme.error }), children: " *" }),
245
+ current && maxLength != null && /* @__PURE__ */ jsxs4("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
246
+ " ",
247
+ current.length,
248
+ "/",
249
+ maxLength
250
+ ] }),
251
+ message != null && /* @__PURE__ */ jsxs4("span", { style: textStyle({ fg: error ? theme.error : theme.muted, dim: !error }), children: [
252
+ " | ",
253
+ message
254
+ ] })
255
+ ] }),
256
+ /* @__PURE__ */ jsxs4("box", { flexDirection: "row", marginLeft: showLabel ? 2 : 0, children: [
257
+ prompt != null && /* @__PURE__ */ jsx9("text", { style: { fg: isFocused ? theme.primary : theme.muted }, children: prompt }),
258
+ isFocused ? /* @__PURE__ */ jsx9(
259
+ "input",
260
+ {
261
+ value: current,
262
+ placeholder,
263
+ maxLength,
264
+ focused: true,
265
+ onInput: handleInput,
266
+ onSubmit: handleSubmit,
267
+ cursorColor: theme.muted,
268
+ cursorStyle: { style: "line", blinking: empty },
269
+ placeholderColor: theme.placeholder,
270
+ textColor: theme.foreground
271
+ }
272
+ ) : /* @__PURE__ */ jsx9("text", { style: { fg: empty ? theme.placeholder : disabled ? theme.muted : theme.foreground, dim: disabled }, children: empty ? placeholder : current })
273
+ ] })
274
+ ] });
275
+ }
276
+
277
+ // ../ui/components/select-input/select-input.tsx
278
+ import { useReducer, useMemo, useRef as useRef2 } from "react";
279
+ import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
280
+ function reducer(state, action) {
281
+ switch (action.type) {
282
+ case "MOVE": {
283
+ let next = state.cursor + action.direction;
284
+ if (next < 0) next = action.max - 1;
285
+ if (next >= action.max) next = 0;
286
+ return { ...state, cursor: next };
287
+ }
288
+ case "SUBMIT":
289
+ return { ...state, submitted: true };
290
+ default:
291
+ return state;
292
+ }
293
+ }
294
+ var VISIBLE = 12;
295
+ var BAR = "\u2502";
296
+ var RADIO = "\u25CB";
297
+ var CURSOR = "\u25B8";
298
+ var SEPARATOR = "\u2500";
299
+ function SelectInput({
300
+ items = [],
301
+ defaultValue,
302
+ value: controlledValue,
303
+ onChange,
304
+ disabled = false,
305
+ invalid = false,
306
+ required = false,
307
+ placeholder,
308
+ title = "Select",
309
+ submittedStatus = "submitted",
310
+ limit,
311
+ highlightColor,
312
+ onSubmit,
313
+ useKeyboard: useKeyboardProp
314
+ }) {
315
+ const theme = useTheme();
316
+ const useKeyboard3 = useKeyboardContext(useKeyboardProp);
317
+ const resolvedHighlight = highlightColor ?? theme.primary;
318
+ const isControlled = controlledValue !== void 0;
319
+ const controlledRef = useRef2(isControlled);
320
+ if (controlledRef.current !== isControlled) {
321
+ console.warn("SelectInput: switching between controlled and uncontrolled is not supported.");
322
+ }
323
+ const initialIndex = items.findIndex((i) => i.value === (isControlled ? controlledValue : defaultValue));
324
+ const [state, dispatch] = useReducer(reducer, {
325
+ cursor: Math.max(0, initialIndex),
326
+ submitted: false
327
+ });
328
+ const { flatRows, selectableItems } = useMemo(() => {
329
+ const rows = [];
330
+ const selectable = [];
331
+ let index = 0;
332
+ const grouped = /* @__PURE__ */ new Map();
333
+ for (const item of items) {
334
+ const group = item.group ?? "";
335
+ const list = grouped.get(group) ?? [];
336
+ list.push(item);
337
+ grouped.set(group, list);
338
+ }
339
+ let first = true;
340
+ for (const [group, groupItems] of grouped) {
341
+ if (!first) rows.push({ type: "separator" });
342
+ first = false;
343
+ if (group) {
344
+ rows.push({ type: "group", label: group });
345
+ }
346
+ for (const item of groupItems) {
347
+ rows.push({ type: "item", item, index });
348
+ selectable.push({ item, index });
349
+ index++;
350
+ }
351
+ }
352
+ return { flatRows: rows, selectableItems: selectable };
353
+ }, [items]);
354
+ const visibleCount = limit ?? VISIBLE;
355
+ const cursorRowIndex = flatRows.findIndex((r) => r.type === "item" && r.index === state.cursor);
356
+ const scrollOffset = Math.max(0, Math.min(cursorRowIndex - Math.floor(visibleCount / 2), flatRows.length - visibleCount));
357
+ const visibleRows = flatRows.slice(scrollOffset, scrollOffset + visibleCount);
358
+ const diamondColor = invalid ? theme.error : disabled ? theme.muted : theme.accent;
359
+ useKeyboard3?.((event) => {
360
+ if (state.submitted || disabled) return;
361
+ if (event.name === "up" || event.name === "k") {
362
+ const direction = -1;
363
+ let next = state.cursor + direction;
364
+ if (next < 0) next = selectableItems.length - 1;
365
+ dispatch({ type: "MOVE", direction, max: selectableItems.length });
366
+ const nextItem = selectableItems[next];
367
+ if (nextItem && !nextItem.item.disabled) {
368
+ onChange?.(nextItem.item.value);
369
+ }
370
+ } else if (event.name === "down" || event.name === "j") {
371
+ const direction = 1;
372
+ let next = state.cursor + direction;
373
+ if (next >= selectableItems.length) next = 0;
374
+ dispatch({ type: "MOVE", direction, max: selectableItems.length });
375
+ const nextItem = selectableItems[next];
376
+ if (nextItem && !nextItem.item.disabled) {
377
+ onChange?.(nextItem.item.value);
378
+ }
379
+ } else if (event.name === "return") {
380
+ const current = selectableItems[state.cursor];
381
+ if (current && !current.item.disabled) {
382
+ dispatch({ type: "SUBMIT" });
383
+ onSubmit?.(isControlled ? controlledValue : current.item.value);
384
+ }
385
+ }
386
+ });
387
+ if (state.submitted) {
388
+ const selectedItem = isControlled ? items.find((i) => i.value === controlledValue) : selectableItems[state.cursor]?.item;
389
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
390
+ /* @__PURE__ */ jsxs5("text", { children: [
391
+ /* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.success }), children: "\u25C6 " }),
392
+ /* @__PURE__ */ jsx10("span", { style: textStyle({ bold: true, fg: theme.foreground }), children: title })
393
+ ] }),
394
+ selectedItem && /* @__PURE__ */ jsxs5("text", { children: [
395
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.success }), children: [
396
+ BAR,
397
+ " "
398
+ ] }),
399
+ /* @__PURE__ */ jsx10("span", { children: " " }),
400
+ /* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.success }), children: "\u25CF " }),
401
+ /* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.foreground }), children: selectedItem.label })
402
+ ] }),
403
+ /* @__PURE__ */ jsx10("text", { children: " " }),
404
+ /* @__PURE__ */ jsx10("text", { children: /* @__PURE__ */ jsx10("span", { style: textStyle({ dim: true, fg: theme.muted }), children: submittedStatus }) })
405
+ ] });
406
+ }
407
+ const hasItems = selectableItems.length > 0;
408
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
409
+ /* @__PURE__ */ jsxs5("text", { children: [
410
+ /* @__PURE__ */ jsx10("span", { style: textStyle({ fg: diamondColor, dim: disabled }), children: "\u25C6 " }),
411
+ /* @__PURE__ */ jsx10("span", { style: textStyle({ bold: true, dim: disabled, fg: theme.foreground }), children: title }),
412
+ required && /* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.error }), children: " *" })
413
+ ] }),
414
+ invalid && /* @__PURE__ */ jsxs5("text", { children: [
415
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
416
+ BAR,
417
+ " "
418
+ ] }),
419
+ /* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.error }), children: " Please select an option" })
420
+ ] }),
421
+ !hasItems && placeholder && /* @__PURE__ */ jsxs5("text", { children: [
422
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
423
+ BAR,
424
+ " "
425
+ ] }),
426
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
427
+ " ",
428
+ placeholder
429
+ ] })
430
+ ] }),
431
+ visibleRows.map((row, i) => {
432
+ if (row.type === "separator") {
433
+ return /* @__PURE__ */ jsxs5("text", { children: [
434
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
435
+ BAR,
436
+ " "
437
+ ] }),
438
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
439
+ " ",
440
+ SEPARATOR.repeat(20)
441
+ ] })
442
+ ] }, `sep-${i}`);
443
+ }
444
+ if (row.type === "group") {
445
+ return /* @__PURE__ */ jsxs5("text", { children: [
446
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
447
+ BAR,
448
+ " "
449
+ ] }),
450
+ /* @__PURE__ */ jsx10("span", { style: textStyle({ bold: true, fg: theme.muted }), children: ` ${row.label}` })
451
+ ] }, `group-${row.label}`);
452
+ }
453
+ const { item, index: itemIndex } = row;
454
+ const isHighlighted = !disabled && itemIndex === state.cursor;
455
+ const isItemDisabled = disabled || !!item.disabled;
456
+ const itemColor = isItemDisabled ? theme.muted : isHighlighted ? resolvedHighlight : theme.foreground;
457
+ return /* @__PURE__ */ jsxs5("text", { children: [
458
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
459
+ BAR,
460
+ " "
461
+ ] }),
462
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: isHighlighted ? resolvedHighlight : void 0 }), children: [
463
+ isHighlighted ? CURSOR : " ",
464
+ " "
465
+ ] }),
466
+ /* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted, dim: isItemDisabled }), children: [
467
+ RADIO,
468
+ " "
469
+ ] }),
470
+ /* @__PURE__ */ jsx10("span", { style: textStyle({ fg: itemColor, dim: isItemDisabled }), children: item.label })
471
+ ] }, item.key ?? String(item.value));
472
+ })
473
+ ] });
474
+ }
475
+
476
+ // ../ui/components/multi-select/multi-select.tsx
477
+ import { useReducer as useReducer2, useMemo as useMemo2, useRef as useRef3 } from "react";
478
+ import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
479
+ function reducer2(state, action) {
480
+ switch (action.type) {
481
+ case "MOVE": {
482
+ let next = state.cursor + action.direction;
483
+ if (next < 0) next = action.max - 1;
484
+ if (next >= action.max) next = 0;
485
+ return { ...state, cursor: next };
486
+ }
487
+ case "SET_SELECTED":
488
+ return { ...state, selected: new Set(action.values) };
489
+ case "SUBMIT":
490
+ return { ...state, submitted: true };
491
+ default:
492
+ return state;
493
+ }
494
+ }
495
+ var VISIBLE2 = 12;
496
+ var BAR2 = "\u2502";
497
+ var CHECKED = "\u25CF";
498
+ var UNCHECKED = "\u25CB";
499
+ var CURSOR2 = "\u25B8";
500
+ var SEPARATOR2 = "\u2500";
501
+ function MultiSelect({
502
+ items = [],
503
+ defaultSelected = [],
504
+ selected: controlledSelected,
505
+ onChange,
506
+ disabled = false,
507
+ invalid = false,
508
+ required = false,
509
+ placeholder,
510
+ maxCount,
511
+ title = "Select",
512
+ submittedStatus = "submitted",
513
+ limit,
514
+ enableSelectAll = true,
515
+ enableClear = true,
516
+ highlightColor,
517
+ checkboxColor,
518
+ allowEmpty = false,
519
+ onSubmit,
520
+ useKeyboard: useKeyboardProp
521
+ }) {
522
+ const theme = useTheme();
523
+ const useKeyboard3 = useKeyboardContext(useKeyboardProp);
524
+ const resolvedHighlight = highlightColor ?? theme.primary;
525
+ const resolvedCheckbox = checkboxColor ?? theme.accent;
526
+ const isControlled = controlledSelected !== void 0;
527
+ const controlledRef = useRef3(isControlled);
528
+ if (controlledRef.current !== isControlled) {
529
+ console.warn("MultiSelect: switching between controlled and uncontrolled is not supported.");
530
+ }
531
+ const [state, dispatch] = useReducer2(reducer2, {
532
+ cursor: 0,
533
+ selected: new Set(isControlled ? controlledSelected : defaultSelected),
534
+ submitted: false
535
+ });
536
+ const cursorRef = useRef3(0);
537
+ const currentSelected = isControlled ? new Set(controlledSelected) : state.selected;
538
+ const { flatRows, selectableItems } = useMemo2(() => {
539
+ const rows = [];
540
+ const selectable = [];
541
+ let index = 0;
542
+ const grouped = /* @__PURE__ */ new Map();
543
+ for (const item of items) {
544
+ const group = item.group ?? "";
545
+ const list = grouped.get(group) ?? [];
546
+ list.push(item);
547
+ grouped.set(group, list);
548
+ }
549
+ let first = true;
550
+ for (const [group, groupItems] of grouped) {
551
+ if (!first) rows.push({ type: "separator" });
552
+ first = false;
553
+ if (group) {
554
+ rows.push({ type: "group", label: group });
555
+ }
556
+ for (const item of groupItems) {
557
+ rows.push({ type: "item", item, index });
558
+ selectable.push({ item, index });
559
+ index++;
560
+ }
561
+ }
562
+ return { flatRows: rows, selectableItems: selectable };
563
+ }, [items]);
564
+ const hasSubmitRow = allowEmpty || currentSelected.size > 0;
565
+ const totalPositions = selectableItems.length + (hasSubmitRow ? 1 : 0);
566
+ const isOnSubmit = hasSubmitRow && state.cursor === selectableItems.length;
567
+ const visibleCount = limit ?? VISIBLE2;
568
+ const cursorRowIndex = flatRows.findIndex((r) => r.type === "item" && r.index === state.cursor);
569
+ const scrollOffset = Math.max(0, Math.min(cursorRowIndex - Math.floor(visibleCount / 2), flatRows.length - visibleCount));
570
+ const visibleRows = flatRows.slice(scrollOffset, scrollOffset + visibleCount);
571
+ const setSelected = (values) => {
572
+ if (isControlled) {
573
+ onChange?.(values);
574
+ } else {
575
+ dispatch({ type: "SET_SELECTED", values });
576
+ }
577
+ };
578
+ const diamondColor = invalid ? theme.error : disabled ? theme.muted : theme.accent;
579
+ useKeyboard3?.((event) => {
580
+ if (state.submitted || disabled) return;
581
+ const move = (direction) => {
582
+ let next = cursorRef.current + direction;
583
+ if (next < 0) next = totalPositions - 1;
584
+ if (next >= totalPositions) next = 0;
585
+ cursorRef.current = next;
586
+ dispatch({ type: "MOVE", direction, max: totalPositions });
587
+ };
588
+ if (event.name === "up" || event.name === "k") {
589
+ move(-1);
590
+ } else if (event.name === "down" || event.name === "j") {
591
+ move(1);
592
+ } else if (event.name === "return") {
593
+ const onSubmitRow = hasSubmitRow && cursorRef.current === selectableItems.length;
594
+ if (onSubmitRow) {
595
+ dispatch({ type: "SUBMIT" });
596
+ onSubmit?.(Array.from(currentSelected));
597
+ } else {
598
+ const current = selectableItems[cursorRef.current];
599
+ if (current && !current.item.disabled) {
600
+ const isDeselecting = currentSelected.has(current.item.value);
601
+ if (!isDeselecting && maxCount !== void 0 && currentSelected.size >= maxCount) return;
602
+ const next = new Set(currentSelected);
603
+ if (isDeselecting) next.delete(current.item.value);
604
+ else next.add(current.item.value);
605
+ setSelected(Array.from(next));
606
+ }
607
+ }
608
+ } else if (event.name === "a" && enableSelectAll) {
609
+ const enabledValues = items.filter((i) => !i.disabled).map((i) => i.value);
610
+ setSelected(maxCount !== void 0 ? enabledValues.slice(0, maxCount) : enabledValues);
611
+ } else if (event.name === "x" && enableClear) {
612
+ setSelected([]);
613
+ }
614
+ });
615
+ if (state.submitted) {
616
+ const selectedItems = items.filter((i) => currentSelected.has(i.value));
617
+ return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
618
+ /* @__PURE__ */ jsxs6("text", { children: [
619
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: theme.success }), children: "\u25C6 " }),
620
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ bold: true, fg: theme.foreground }), children: title })
621
+ ] }),
622
+ selectedItems.map((item) => /* @__PURE__ */ jsxs6("text", { children: [
623
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.success }), children: [
624
+ BAR2,
625
+ " "
626
+ ] }),
627
+ /* @__PURE__ */ jsx11("span", { children: " " }),
628
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.success }), children: [
629
+ CHECKED,
630
+ " "
631
+ ] }),
632
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: theme.foreground }), children: item.label })
633
+ ] }, item.key ?? String(item.value))),
634
+ /* @__PURE__ */ jsx11("text", { children: " " }),
635
+ /* @__PURE__ */ jsx11("text", { children: /* @__PURE__ */ jsxs6("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
636
+ selectedItems.length,
637
+ " selected \u2014 ",
638
+ submittedStatus
639
+ ] }) })
640
+ ] });
641
+ }
642
+ const hasItems = selectableItems.length > 0;
643
+ return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
644
+ /* @__PURE__ */ jsxs6("text", { children: [
645
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: diamondColor, dim: disabled }), children: "\u25C6 " }),
646
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ bold: true, dim: disabled, fg: theme.foreground }), children: title }),
647
+ required && /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: theme.error }), children: " *" }),
648
+ maxCount !== void 0 && /* @__PURE__ */ jsx11("span", { style: textStyle({ dim: true, fg: theme.muted }), children: ` (${currentSelected.size}/${maxCount})` })
649
+ ] }),
650
+ invalid && /* @__PURE__ */ jsxs6("text", { children: [
651
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
652
+ BAR2,
653
+ " "
654
+ ] }),
655
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: theme.error }), children: " Please select at least one option" })
656
+ ] }),
657
+ !hasItems && placeholder && /* @__PURE__ */ jsxs6("text", { children: [
658
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
659
+ BAR2,
660
+ " "
661
+ ] }),
662
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
663
+ " ",
664
+ placeholder
665
+ ] })
666
+ ] }),
667
+ visibleRows.map((row, i) => {
668
+ if (row.type === "separator") {
669
+ return /* @__PURE__ */ jsxs6("text", { children: [
670
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
671
+ BAR2,
672
+ " "
673
+ ] }),
674
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
675
+ " ",
676
+ SEPARATOR2.repeat(20)
677
+ ] })
678
+ ] }, `sep-${i}`);
679
+ }
680
+ if (row.type === "group") {
681
+ return /* @__PURE__ */ jsxs6("text", { children: [
682
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
683
+ BAR2,
684
+ " "
685
+ ] }),
686
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ bold: true, fg: theme.muted }), children: ` ${row.label}` })
687
+ ] }, `group-${row.label}`);
688
+ }
689
+ const { item, index: itemIndex } = row;
690
+ const isHighlighted = !disabled && itemIndex === state.cursor;
691
+ const isSelected = currentSelected.has(item.value);
692
+ const isItemDisabled = disabled || !!item.disabled;
693
+ const itemColor = isItemDisabled ? theme.muted : isHighlighted ? resolvedHighlight : isSelected ? resolvedCheckbox : theme.foreground;
694
+ return /* @__PURE__ */ jsxs6("text", { children: [
695
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
696
+ BAR2,
697
+ " "
698
+ ] }),
699
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: isHighlighted ? resolvedHighlight : void 0 }), children: [
700
+ isHighlighted ? CURSOR2 : " ",
701
+ " "
702
+ ] }),
703
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: isSelected && !isItemDisabled ? resolvedCheckbox : theme.muted, dim: isItemDisabled }), children: [
704
+ isSelected ? CHECKED : UNCHECKED,
705
+ " "
706
+ ] }),
707
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: itemColor, dim: isItemDisabled }), children: item.label })
708
+ ] }, item.key ?? String(item.value));
709
+ }),
710
+ hasSubmitRow && /* @__PURE__ */ jsxs6("text", { children: [
711
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
712
+ BAR2,
713
+ " "
714
+ ] }),
715
+ /* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: isOnSubmit ? resolvedHighlight : void 0 }), children: [
716
+ isOnSubmit ? CURSOR2 : " ",
717
+ " "
718
+ ] }),
719
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: isOnSubmit ? resolvedHighlight : theme.muted }), children: "\u21B3 " }),
720
+ /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: isOnSubmit ? resolvedHighlight : theme.foreground }), children: "Submit" })
721
+ ] })
722
+ ] });
723
+ }
724
+
725
+ // ../ui/components/table/table.tsx
726
+ import { Fragment } from "react";
727
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
728
+ function getColumns(data, columnsProp) {
729
+ if (columnsProp) return columnsProp;
730
+ const keys = /* @__PURE__ */ new Set();
731
+ for (const row of data) {
732
+ for (const key in row) keys.add(key);
733
+ }
734
+ return Array.from(keys);
735
+ }
736
+ function calculateColumnWidths(columns, data, padding) {
737
+ return columns.map((field) => {
738
+ const headerWidth = String(field).length;
739
+ const maxDataWidth = data.reduce((max, row) => {
740
+ const val = row[field];
741
+ return Math.max(max, val == null ? 0 : String(val).length);
742
+ }, 0);
743
+ return { field, width: Math.max(headerWidth, maxDataWidth) + padding * 2 };
744
+ });
745
+ }
746
+ function padCell(value, width, padding) {
747
+ const rightPad = width - value.length - padding;
748
+ return " ".repeat(padding) + value + " ".repeat(Math.max(0, rightPad));
749
+ }
750
+ function Table({
751
+ data,
752
+ columns: columnsProp,
753
+ padding = 1,
754
+ headerColor,
755
+ borderColor
756
+ }) {
757
+ const theme = useTheme();
758
+ const resolvedHeaderColor = headerColor ?? theme.primary;
759
+ const resolvedBorderColor = borderColor ?? theme.border;
760
+ const columns = getColumns(data, columnsProp);
761
+ const colInfo = calculateColumnWidths(columns, data, padding);
762
+ const borderLine = (left, mid, right) => {
763
+ const inner = colInfo.map((c) => "\u2500".repeat(c.width)).join(mid);
764
+ return /* @__PURE__ */ jsx12("text", { children: /* @__PURE__ */ jsxs7("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: [
765
+ left,
766
+ inner,
767
+ right
768
+ ] }) });
769
+ };
770
+ const contentRow = (rowData, isHeader) => {
771
+ const parts = [];
772
+ parts.push(
773
+ /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: "\u2502" }, "left-border")
774
+ );
775
+ colInfo.forEach((col, i) => {
776
+ const val = rowData[col.field];
777
+ const str = val == null ? "" : String(val);
778
+ const padded = padCell(str, col.width, padding);
779
+ if (isHeader) {
780
+ parts.push(
781
+ /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedHeaderColor, bold: true }), children: padded }, `cell-${i}`)
782
+ );
783
+ } else {
784
+ parts.push(
785
+ /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: theme.foreground }), children: padded }, `cell-${i}`)
786
+ );
787
+ }
788
+ if (i < colInfo.length - 1) {
789
+ parts.push(
790
+ /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: "\u2502" }, `sep-${i}`)
791
+ );
792
+ }
793
+ });
794
+ parts.push(
795
+ /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: "\u2502" }, "right-border")
796
+ );
797
+ return /* @__PURE__ */ jsx12("text", { children: parts });
798
+ };
799
+ const headerData = columns.reduce(
800
+ (acc, col) => ({ ...acc, [col]: col }),
801
+ {}
802
+ );
803
+ return /* @__PURE__ */ jsxs7("box", { children: [
804
+ borderLine("\u250C", "\u252C", "\u2510"),
805
+ contentRow(headerData, true),
806
+ data.map((row, index) => /* @__PURE__ */ jsxs7(Fragment, { children: [
807
+ borderLine("\u251C", "\u253C", "\u2524"),
808
+ contentRow(row, false)
809
+ ] }, index)),
810
+ borderLine("\u2514", "\u2534", "\u2518")
811
+ ] });
812
+ }
813
+
814
+ // ../ui/components/gradient/gradient.tsx
815
+ import { Fragment as Fragment2, jsx as jsx13 } from "react/jsx-runtime";
816
+ var GRADIENTS = {
817
+ cristal: ["#bdfff3", "#4ac29a"],
818
+ teen: ["#77a1d3", "#79cbca", "#e684ae"],
819
+ mind: ["#473b7b", "#3584a7", "#30d2be"],
820
+ morning: ["#ff5f6d", "#ffc371"],
821
+ vice: ["#5ee7df", "#b490ca"],
822
+ passion: ["#f43b47", "#453a94"],
823
+ fruit: ["#ff4e50", "#f9d423"],
824
+ instagram: ["#833ab4", "#fd1d1d", "#fcb045"],
825
+ atlas: ["#feac5e", "#c779d0", "#4bc0c8"],
826
+ retro: ["#3f51b1", "#5a55ae", "#7b5fac", "#8f6aae", "#a86aa4", "#cc6b8e", "#f18271", "#f3a469", "#f7c978"],
827
+ summer: ["#fdbb2d", "#22c1c3"],
828
+ rainbow: ["#ff0000", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#ff00ff", "#ff0000"],
829
+ pastel: ["#74ebd5", "#ACB6E5"]
830
+ };
831
+ function hexToRgb(hex) {
832
+ const normalized = hex.replace("#", "");
833
+ return {
834
+ r: parseInt(normalized.substring(0, 2), 16),
835
+ g: parseInt(normalized.substring(2, 4), 16),
836
+ b: parseInt(normalized.substring(4, 6), 16)
837
+ };
838
+ }
839
+ function rgbToHex(rgb) {
840
+ const r = rgb.r.toString(16).padStart(2, "0");
841
+ const g = rgb.g.toString(16).padStart(2, "0");
842
+ const b = rgb.b.toString(16).padStart(2, "0");
843
+ return `#${r}${g}${b}`;
844
+ }
845
+ function lerp(a, b, t) {
846
+ return a + (b - a) * t;
847
+ }
848
+ function interpolateColor(color1, color2, t) {
849
+ return {
850
+ r: Math.round(lerp(color1.r, color2.r, t)),
851
+ g: Math.round(lerp(color1.g, color2.g, t)),
852
+ b: Math.round(lerp(color1.b, color2.b, t))
853
+ };
854
+ }
855
+ function generateGradient(colors, steps) {
856
+ if (colors.length === 0) throw new Error("At least one color is required");
857
+ if (colors.length === 1 || steps <= 1) return Array(steps).fill(colors[0]);
858
+ const rgbColors = colors.map(hexToRgb);
859
+ const result = [];
860
+ const segmentLength = (steps - 1) / (rgbColors.length - 1);
861
+ for (let i = 0; i < steps; i++) {
862
+ const segmentIndex = Math.min(Math.floor(i / segmentLength), rgbColors.length - 2);
863
+ const segmentProgress = segmentLength > 0 ? (i - segmentIndex * segmentLength) / segmentLength : 0;
864
+ const color = interpolateColor(rgbColors[segmentIndex], rgbColors[segmentIndex + 1], Math.min(segmentProgress, 1));
865
+ result.push(rgbToHex(color));
866
+ }
867
+ return result;
868
+ }
869
+ function Gradient({ children, name, colors }) {
870
+ if (name && colors) throw new Error("The `name` and `colors` props are mutually exclusive");
871
+ if (!name && !colors) throw new Error("Either `name` or `colors` prop must be provided");
872
+ const gradientColors = name ? GRADIENTS[name] : colors;
873
+ const lines = children.split("\n");
874
+ const maxLength = Math.max(...lines.map((l) => l.length));
875
+ if (maxLength === 0) return /* @__PURE__ */ jsx13("text", { children });
876
+ const hexColors = generateGradient(gradientColors, maxLength);
877
+ return /* @__PURE__ */ jsx13(Fragment2, { children: lines.map((line, lineIndex) => /* @__PURE__ */ jsx13("text", { children: line.split("").map((char, charIndex) => /* @__PURE__ */ jsx13("span", { style: { fg: hexColors[charIndex] }, children: char }, charIndex)) }, lineIndex)) });
878
+ }
879
+
880
+ // ../ui/components/tab-bar/tab-bar.tsx
881
+ import { createContext as createContext3, useContext as useContext3, useState as useState5, Children, isValidElement } from "react";
882
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
883
+ var TabsContext = createContext3(null);
884
+ function useTabsContext() {
885
+ const ctx = useContext3(TabsContext);
886
+ if (!ctx) throw new Error("Tabs compound components must be used within <Tabs>");
887
+ return ctx;
888
+ }
889
+ function Tabs({
890
+ value: controlledValue,
891
+ defaultValue = "",
892
+ onValueChange,
893
+ children
894
+ }) {
895
+ const [internalValue, setInternalValue] = useState5(defaultValue);
896
+ const value = controlledValue ?? internalValue;
897
+ const handleChange = onValueChange ?? setInternalValue;
898
+ return /* @__PURE__ */ jsx14(TabsContext.Provider, { value: { value, onValueChange: handleChange }, children: /* @__PURE__ */ jsx14("box", { flexDirection: "column", children }) });
899
+ }
900
+ function TabsList({
901
+ label,
902
+ focused = true,
903
+ activeColor,
904
+ separator = true,
905
+ children
906
+ }) {
907
+ const theme = useTheme();
908
+ const { value } = useTabsContext();
909
+ const color = activeColor ?? theme.accent;
910
+ const triggers = [];
911
+ Children.forEach(children, (child) => {
912
+ if (isValidElement(child) && "value" in child.props) {
913
+ triggers.push({ value: child.props.value, label: child.props.children });
914
+ }
915
+ });
916
+ const parts = [];
917
+ if (label !== void 0) {
918
+ parts.push(
919
+ /* @__PURE__ */ jsxs8(
920
+ "span",
921
+ {
922
+ style: focused ? textStyle({ bold: true, fg: theme.foreground }) : textStyle({ dim: true, fg: theme.muted }),
923
+ children: [
924
+ label,
925
+ " "
926
+ ]
927
+ },
928
+ "label"
929
+ )
930
+ );
931
+ }
932
+ triggers.forEach((trigger, i) => {
933
+ const isSelected = trigger.value === value;
934
+ const padded = ` ${trigger.label} `;
935
+ const style = isSelected ? focused ? textStyle({ bold: true, fg: theme.background, bg: color }) : textStyle({ bold: true, fg: theme.background, bg: theme.muted, dim: true }) : focused ? textStyle({ fg: theme.foreground }) : textStyle({ dim: true, fg: theme.muted });
936
+ if (i > 0) {
937
+ parts.push(
938
+ /* @__PURE__ */ jsx14("span", { style: textStyle({ dim: true, fg: theme.muted }), children: "\u2502" }, `div-${i}`)
939
+ );
940
+ }
941
+ parts.push(
942
+ /* @__PURE__ */ jsx14("span", { style, children: padded }, `opt-${i}`)
943
+ );
944
+ });
945
+ return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", children: [
946
+ /* @__PURE__ */ jsx14("text", { children: parts }),
947
+ separator && /* @__PURE__ */ jsx14("text", { wrapMode: "none", style: textStyle({ dim: true, fg: theme.muted }), children: "\u2500".repeat(200) })
948
+ ] });
949
+ }
950
+ function TabsTrigger(_props) {
951
+ return null;
952
+ }
953
+ function TabBar({
954
+ label,
955
+ options,
956
+ selectedIndex,
957
+ focused = true,
958
+ activeColor,
959
+ separator = true
960
+ }) {
961
+ return /* @__PURE__ */ jsx14(Tabs, { value: options[selectedIndex], children: /* @__PURE__ */ jsx14(TabsList, { label, focused, activeColor, separator, children: options.map((option) => /* @__PURE__ */ jsx14(TabsTrigger, { value: option, children: option }, option)) }) });
962
+ }
963
+
964
+ // ../ui/components/modal/modal.tsx
965
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
966
+ function Modal({
967
+ children,
968
+ title,
969
+ borderColor,
970
+ borderStyle = "rounded",
971
+ onClose,
972
+ useKeyboard: useKeyboardProp
973
+ }) {
974
+ const theme = useTheme();
975
+ const useKeyboard3 = useKeyboardContext(useKeyboardProp);
976
+ const resolvedBorderColor = borderColor ?? theme.muted;
977
+ useKeyboard3?.((event) => {
978
+ if (event.name === "escape" && onClose) {
979
+ onClose();
980
+ }
981
+ });
982
+ return /* @__PURE__ */ jsx15("box", { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx15(
983
+ "box",
984
+ {
985
+ flexDirection: "column",
986
+ flexGrow: 1,
987
+ border: true,
988
+ borderStyle,
989
+ borderColor: resolvedBorderColor,
990
+ children: title ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
991
+ /* @__PURE__ */ jsx15("box", { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsx15("text", { style: textStyle({ bold: true, fg: theme.primary }), children: title }) }),
992
+ children
993
+ ] }) : children
994
+ }
995
+ ) });
996
+ }
997
+
998
+ // ../ui/components/prompt-input/prompt-input.tsx
999
+ import {
1000
+ useState as useState6,
1001
+ useRef as useRef4,
1002
+ useCallback as useCallback2,
1003
+ useMemo as useMemo3,
1004
+ createContext as createContext4,
1005
+ useContext as useContext4
1006
+ } from "react";
1007
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
1008
+ var PromptInputControllerCtx = createContext4(null);
1009
+ var useOptionalController = () => useContext4(PromptInputControllerCtx);
1010
+ var PromptInputContext = createContext4(null);
1011
+ function usePromptInput() {
1012
+ const ctx = useContext4(PromptInputContext);
1013
+ if (!ctx) {
1014
+ throw new Error("usePromptInput must be used within a <PromptInput> component");
1015
+ }
1016
+ return ctx;
1017
+ }
1018
+ function computeDefaultSuggestions(input, commands, files) {
1019
+ if (input.startsWith("/") && commands.length > 0) {
1020
+ return commands.filter((c) => c.cmd.startsWith(input)).map((c) => ({ text: c.cmd, desc: c.desc }));
1021
+ }
1022
+ if (input.includes("@") && files.length > 0) {
1023
+ const query = input.split("@").pop() ?? "";
1024
+ return files.filter((f) => f.toLowerCase().includes(query.toLowerCase())).map((f) => ({ text: "@" + f }));
1025
+ }
1026
+ return [];
1027
+ }
1028
+ function resolveStatusHintText(status, submittedText, streamingText, errorText, disabledText) {
1029
+ if (status === "submitted") return submittedText;
1030
+ if (status === "streaming") return streamingText;
1031
+ if (status === "error") return errorText;
1032
+ return disabledText;
1033
+ }
1034
+ function PromptInputDivider() {
1035
+ const { theme } = usePromptInput();
1036
+ return /* @__PURE__ */ jsx16("text", { wrapMode: "none", children: /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.muted }), children: "\u2500".repeat(500) }) });
1037
+ }
1038
+ function PromptInputSuggestions() {
1039
+ const { suggestions, sugIdx, maxSuggestions, theme } = usePromptInput();
1040
+ const visible = suggestions.slice(0, maxSuggestions);
1041
+ if (visible.length === 0) return null;
1042
+ return /* @__PURE__ */ jsx16("box", { flexDirection: "column", marginLeft: 2, children: visible.map((sug, i) => {
1043
+ const active = i === sugIdx;
1044
+ return /* @__PURE__ */ jsxs10("text", { children: [
1045
+ /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: active ? theme.primary : theme.muted }), children: active ? "\u25B8 " : " " }),
1046
+ /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: active ? theme.primary : theme.muted, bold: active }), children: sug.text }),
1047
+ sug.desc && /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: " " + sug.desc })
1048
+ ] }, sug.text);
1049
+ }) });
1050
+ }
1051
+ var CURSOR_CHAR = "\u258D";
1052
+ function PromptInputTextarea() {
1053
+ const { value, disabled, statusHintText, placeholder, prompt, promptColor, theme } = usePromptInput();
1054
+ return /* @__PURE__ */ jsxs10("text", { children: [
1055
+ /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: promptColor }), children: prompt }),
1056
+ value.length === 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
1057
+ !disabled && /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.muted }), children: CURSOR_CHAR }),
1058
+ /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: disabled ? statusHintText : " " + placeholder })
1059
+ ] }) : /* @__PURE__ */ jsxs10(Fragment5, { children: [
1060
+ /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.foreground }), children: value }),
1061
+ !disabled && /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.muted }), children: CURSOR_CHAR })
1062
+ ] })
1063
+ ] });
1064
+ }
1065
+ function PromptInputSubmit(props) {
1066
+ const ctx = usePromptInput();
1067
+ const status = props.status ?? ctx.status;
1068
+ const onStop = props.onStop ?? ctx.onStop;
1069
+ const { disabled, theme } = ctx;
1070
+ const isGenerating = status === "submitted" || status === "streaming";
1071
+ const icon = status === "submitted" ? "\u25D0" : status === "streaming" ? onStop ? "\u25A0" : "\u25D0" : status === "error" ? "\u2715" : "\u23CE";
1072
+ const color = status === "error" ? theme.error : isGenerating ? theme.muted : disabled ? theme.muted : theme.primary;
1073
+ return /* @__PURE__ */ jsx16("text", { children: /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: color }), children: " " + icon }) });
1074
+ }
1075
+ function PromptInputStatusText() {
1076
+ const { status, errorText, theme } = usePromptInput();
1077
+ if (status !== "error") return null;
1078
+ return /* @__PURE__ */ jsx16("text", { children: /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.error }), children: errorText }) });
1079
+ }
1080
+ function PromptInput({
1081
+ value: controlledValue,
1082
+ defaultValue = "",
1083
+ onSubmit,
1084
+ onChange,
1085
+ placeholder = "Type a message...",
1086
+ prompt = "\u276F ",
1087
+ promptColor,
1088
+ status,
1089
+ onStop,
1090
+ submittedText = "Thinking...",
1091
+ streamingText: streamingLabel = "Generating...",
1092
+ errorText = "An error occurred. Try again.",
1093
+ disabled: disabledProp = false,
1094
+ disabledText = "Generating...",
1095
+ commands = [],
1096
+ files = [],
1097
+ getSuggestions: customGetSuggestions,
1098
+ maxSuggestions = 5,
1099
+ enableHistory = true,
1100
+ showDividers = false,
1101
+ useKeyboard: useKeyboardProp,
1102
+ children
1103
+ }) {
1104
+ const theme = useTheme();
1105
+ const useKeyboard3 = useKeyboardContext(useKeyboardProp);
1106
+ const resolvedPromptColor = promptColor ?? theme.muted;
1107
+ const disabled = status ? status === "submitted" || status === "streaming" : disabledProp;
1108
+ const statusHintText = resolveStatusHintText(status, submittedText, streamingLabel, errorText, disabledText);
1109
+ const controller = useOptionalController();
1110
+ const usingProvider = !!controller;
1111
+ const isControlled = controlledValue !== void 0;
1112
+ const controlledRef = useRef4(isControlled);
1113
+ if (controlledRef.current !== isControlled) {
1114
+ console.warn("PromptInput: switching between controlled and uncontrolled is not supported.");
1115
+ }
1116
+ const [localValue, setLocalValue] = useState6(defaultValue);
1117
+ const [localSuggestions, setLocalSuggestions] = useState6([]);
1118
+ const [localSugIdx, setLocalSugIdx] = useState6(0);
1119
+ const [history, setHistory] = useState6([]);
1120
+ const [histIdx, setHistIdx] = useState6(-1);
1121
+ const value = isControlled ? controlledValue : usingProvider ? controller.textInput.value : localValue;
1122
+ const suggestions = usingProvider ? controller.suggestions.suggestions : localSuggestions;
1123
+ const sugIdx = usingProvider ? controller.suggestions.selectedIndex : localSugIdx;
1124
+ const valueRef = useRef4(defaultValue);
1125
+ if (isControlled) valueRef.current = controlledValue;
1126
+ else if (usingProvider) valueRef.current = controller.textInput.value;
1127
+ else valueRef.current = localValue;
1128
+ const suggestionsRef = useRef4([]);
1129
+ suggestionsRef.current = suggestions;
1130
+ const sugIdxRef = useRef4(0);
1131
+ sugIdxRef.current = sugIdx;
1132
+ const historyRef = useRef4([]);
1133
+ historyRef.current = history;
1134
+ const histIdxRef = useRef4(-1);
1135
+ histIdxRef.current = histIdx;
1136
+ const setSug = useCallback2((next) => {
1137
+ suggestionsRef.current = next;
1138
+ if (usingProvider) {
1139
+ controller.suggestions.setSuggestions(next);
1140
+ } else {
1141
+ setLocalSuggestions(next);
1142
+ }
1143
+ }, [usingProvider, controller]);
1144
+ const setSugI = useCallback2((next) => {
1145
+ sugIdxRef.current = next;
1146
+ if (usingProvider) {
1147
+ controller.suggestions.setSelectedIndex(next);
1148
+ } else {
1149
+ setLocalSugIdx(next);
1150
+ }
1151
+ }, [usingProvider, controller]);
1152
+ const setHist = useCallback2((next) => {
1153
+ historyRef.current = next;
1154
+ setHistory(next);
1155
+ }, []);
1156
+ const setHistI = useCallback2((next) => {
1157
+ histIdxRef.current = next;
1158
+ setHistIdx(next);
1159
+ }, []);
1160
+ const computeSuggestions = useCallback2((input) => {
1161
+ if (customGetSuggestions) return customGetSuggestions(input);
1162
+ return computeDefaultSuggestions(input, commands, files);
1163
+ }, [customGetSuggestions, commands, files]);
1164
+ const updateValue = useCallback2((next) => {
1165
+ valueRef.current = next;
1166
+ if (isControlled) {
1167
+ } else if (usingProvider) {
1168
+ controller.textInput.setValue(next);
1169
+ } else {
1170
+ setLocalValue(next);
1171
+ }
1172
+ onChange?.(next);
1173
+ const sug = computeSuggestions(next);
1174
+ setSug(sug);
1175
+ setSugI(0);
1176
+ }, [isControlled, usingProvider, controller, onChange, computeSuggestions, setSug, setSugI]);
1177
+ const clearInput = useCallback2(() => {
1178
+ if (usingProvider) {
1179
+ controller.textInput.clear();
1180
+ } else if (!isControlled) {
1181
+ setLocalValue("");
1182
+ }
1183
+ onChange?.("");
1184
+ }, [usingProvider, controller, isControlled, onChange]);
1185
+ const handleSubmit = useCallback2((text) => {
1186
+ if (!onSubmit) return;
1187
+ const result = onSubmit({ text });
1188
+ if (result instanceof Promise) {
1189
+ result.then(
1190
+ () => clearInput(),
1191
+ () => {
1192
+ }
1193
+ );
1194
+ } else {
1195
+ clearInput();
1196
+ }
1197
+ }, [onSubmit, clearInput]);
1198
+ useKeyboard3?.((event) => {
1199
+ if (event.name === "escape" && (status === "streaming" || status === "submitted") && onStop) {
1200
+ onStop();
1201
+ return;
1202
+ }
1203
+ if (disabled) return;
1204
+ if (event.name === "return") {
1205
+ if (suggestionsRef.current.length > 0) {
1206
+ const sel = suggestionsRef.current[sugIdxRef.current];
1207
+ if (sel) {
1208
+ if (valueRef.current.startsWith("/")) {
1209
+ setSug([]);
1210
+ updateValue("");
1211
+ if (enableHistory) {
1212
+ setHist([sel.text, ...historyRef.current]);
1213
+ }
1214
+ setHistI(-1);
1215
+ handleSubmit(sel.text);
1216
+ } else {
1217
+ const base = valueRef.current.slice(0, valueRef.current.lastIndexOf("@"));
1218
+ updateValue(base + sel.text + " ");
1219
+ setSug([]);
1220
+ }
1221
+ }
1222
+ } else {
1223
+ const trimmed = valueRef.current.trim();
1224
+ if (!trimmed) return;
1225
+ if (enableHistory) {
1226
+ setHist([trimmed, ...historyRef.current]);
1227
+ }
1228
+ updateValue("");
1229
+ setHistI(-1);
1230
+ setSug([]);
1231
+ handleSubmit(trimmed);
1232
+ }
1233
+ return;
1234
+ }
1235
+ if (event.name === "tab" && suggestionsRef.current.length > 0) {
1236
+ setSugI((sugIdxRef.current + 1) % suggestionsRef.current.length);
1237
+ return;
1238
+ }
1239
+ if (event.name === "up") {
1240
+ if (suggestionsRef.current.length > 0) {
1241
+ setSugI(Math.max(0, sugIdxRef.current - 1));
1242
+ } else if (enableHistory && historyRef.current.length > 0) {
1243
+ const idx = Math.min(historyRef.current.length - 1, histIdxRef.current + 1);
1244
+ setHistI(idx);
1245
+ updateValue(historyRef.current[idx]);
1246
+ }
1247
+ return;
1248
+ }
1249
+ if (event.name === "down") {
1250
+ if (suggestionsRef.current.length > 0) {
1251
+ setSugI(Math.min(suggestionsRef.current.length - 1, sugIdxRef.current + 1));
1252
+ } else if (enableHistory && histIdxRef.current > 0) {
1253
+ const nextIdx = histIdxRef.current - 1;
1254
+ setHistI(nextIdx);
1255
+ updateValue(historyRef.current[nextIdx]);
1256
+ } else if (enableHistory && histIdxRef.current === 0) {
1257
+ setHistI(-1);
1258
+ updateValue("");
1259
+ }
1260
+ return;
1261
+ }
1262
+ if (event.name === "escape") {
1263
+ if (suggestionsRef.current.length > 0) {
1264
+ setSug([]);
1265
+ }
1266
+ return;
1267
+ }
1268
+ if (event.name === "backspace" || event.name === "delete") {
1269
+ updateValue(valueRef.current.slice(0, -1));
1270
+ return;
1271
+ }
1272
+ if (event.ctrl || event.meta) return;
1273
+ if (event.name === "space") {
1274
+ updateValue(valueRef.current + " ");
1275
+ return;
1276
+ }
1277
+ if (event.name && event.name.length === 1) {
1278
+ updateValue(valueRef.current + event.name);
1279
+ }
1280
+ });
1281
+ const visibleSuggestions = suggestions.slice(0, maxSuggestions);
1282
+ const ctxValue = {
1283
+ value,
1284
+ disabled,
1285
+ status,
1286
+ onStop,
1287
+ statusHintText,
1288
+ placeholder,
1289
+ prompt,
1290
+ promptColor: resolvedPromptColor,
1291
+ suggestions: visibleSuggestions,
1292
+ sugIdx,
1293
+ maxSuggestions,
1294
+ errorText,
1295
+ theme
1296
+ };
1297
+ if (children) {
1298
+ return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx16("box", { flexDirection: "column", children }) });
1299
+ }
1300
+ return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxs10("box", { flexDirection: "column", children: [
1301
+ showDividers && /* @__PURE__ */ jsx16(PromptInputDivider, {}),
1302
+ /* @__PURE__ */ jsx16(PromptInputSuggestions, {}),
1303
+ /* @__PURE__ */ jsx16(PromptInputTextarea, {}),
1304
+ /* @__PURE__ */ jsx16(PromptInputStatusText, {}),
1305
+ showDividers && /* @__PURE__ */ jsx16(PromptInputDivider, {})
1306
+ ] }) });
1307
+ }
1308
+ PromptInput.Textarea = PromptInputTextarea;
1309
+ PromptInput.Suggestions = PromptInputSuggestions;
1310
+ PromptInput.Submit = PromptInputSubmit;
1311
+ PromptInput.Divider = PromptInputDivider;
1312
+ PromptInput.StatusText = PromptInputStatusText;
1313
+
1314
+ // ../ui/components/chat/chat.tsx
1315
+ import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
1316
+ var STATUS_ICONS = {
1317
+ pending: "\u2022",
1318
+ in_progress: "\u280B",
1319
+ completed: "\u2713",
1320
+ failed: "\u2717"
1321
+ };
1322
+ function getStatusColors(theme) {
1323
+ return {
1324
+ pending: theme.muted,
1325
+ in_progress: theme.warning,
1326
+ completed: theme.success,
1327
+ failed: theme.error
1328
+ };
1329
+ }
1330
+ function MessageBubble({
1331
+ message,
1332
+ userColor,
1333
+ assistantColor,
1334
+ foregroundColor
1335
+ }) {
1336
+ const isUser = message.role === "user";
1337
+ const prefix = isUser ? "> " : "< ";
1338
+ const color = isUser ? userColor : assistantColor;
1339
+ return /* @__PURE__ */ jsxs11("text", { wrapMode: "word", children: [
1340
+ /* @__PURE__ */ jsx17("span", { style: textStyle({ bold: true, fg: color }), children: prefix }),
1341
+ /* @__PURE__ */ jsx17("span", { style: { fg: foregroundColor }, children: message.content })
1342
+ ] });
1343
+ }
1344
+ function StreamingTextDisplay({
1345
+ text,
1346
+ assistantColor,
1347
+ foregroundColor,
1348
+ cursorChar = "_"
1349
+ }) {
1350
+ if (!text) return null;
1351
+ return /* @__PURE__ */ jsxs11("text", { wrapMode: "word", children: [
1352
+ /* @__PURE__ */ jsx17("span", { style: textStyle({ bold: true, fg: assistantColor }), children: "< " }),
1353
+ /* @__PURE__ */ jsx17("span", { style: { fg: foregroundColor }, children: text }),
1354
+ /* @__PURE__ */ jsx17("span", { style: textStyle({ dim: true, fg: foregroundColor }), children: cursorChar })
1355
+ ] });
1356
+ }
1357
+ function ToolCallCard({ toolCall, statusColors }) {
1358
+ const icon = STATUS_ICONS[toolCall.status] || "\u2022";
1359
+ const color = statusColors[toolCall.status] || "gray";
1360
+ const showEllipsis = toolCall.status === "pending" || toolCall.status === "in_progress";
1361
+ return /* @__PURE__ */ jsxs11("text", { children: [
1362
+ /* @__PURE__ */ jsx17("span", { children: " " }),
1363
+ /* @__PURE__ */ jsx17("span", { style: textStyle({ fg: color }), children: icon }),
1364
+ /* @__PURE__ */ jsx17("span", { children: " " }),
1365
+ /* @__PURE__ */ jsx17("span", { style: textStyle({ fg: color }), children: toolCall.title }),
1366
+ showEllipsis && /* @__PURE__ */ jsx17("span", { style: textStyle({ dim: true, fg: color }), children: " ..." })
1367
+ ] });
1368
+ }
1369
+ function ChatPanel({
1370
+ messages,
1371
+ streamingText = "",
1372
+ status,
1373
+ isLoading = false,
1374
+ activeToolCalls = [],
1375
+ onSendMessage,
1376
+ onStop,
1377
+ onCancel,
1378
+ placeholder = "Type a message...",
1379
+ promptChar = "> ",
1380
+ promptColor,
1381
+ userColor,
1382
+ assistantColor,
1383
+ loadingText = "Thinking...",
1384
+ useKeyboard: useKeyboardProp
1385
+ }) {
1386
+ const theme = useTheme();
1387
+ const resolvedUserColor = userColor ?? theme.secondary;
1388
+ const resolvedAssistantColor = assistantColor ?? theme.primary;
1389
+ const resolvedPromptColor = promptColor ?? theme.secondary;
1390
+ const statusColors = getStatusColors(theme);
1391
+ const chatStatus = status ? status : isLoading && !streamingText ? "submitted" : streamingText ? "streaming" : void 0;
1392
+ const isSubmitted = chatStatus === "submitted";
1393
+ const isStreaming = chatStatus === "streaming";
1394
+ const stopHandler = onStop ?? onCancel;
1395
+ return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", paddingX: 1, children: [
1396
+ messages.map((msg) => /* @__PURE__ */ jsx17(
1397
+ MessageBubble,
1398
+ {
1399
+ message: msg,
1400
+ userColor: resolvedUserColor,
1401
+ assistantColor: resolvedAssistantColor,
1402
+ foregroundColor: theme.foreground
1403
+ },
1404
+ msg.id
1405
+ )),
1406
+ activeToolCalls.map((tc) => /* @__PURE__ */ jsx17(ToolCallCard, { toolCall: tc, statusColors }, tc.id)),
1407
+ isStreaming && streamingText ? /* @__PURE__ */ jsx17(
1408
+ StreamingTextDisplay,
1409
+ {
1410
+ text: streamingText,
1411
+ assistantColor: resolvedAssistantColor,
1412
+ foregroundColor: theme.foreground
1413
+ }
1414
+ ) : isSubmitted ? /* @__PURE__ */ jsxs11("text", { style: textStyle({ dim: true, fg: theme.muted }), children: [
1415
+ " ",
1416
+ loadingText
1417
+ ] }) : null,
1418
+ /* @__PURE__ */ jsx17("box", { marginTop: 1, children: /* @__PURE__ */ jsx17(
1419
+ PromptInput,
1420
+ {
1421
+ onSubmit: (msg) => onSendMessage(msg.text),
1422
+ onStop: stopHandler,
1423
+ status: chatStatus,
1424
+ placeholder,
1425
+ prompt: promptChar,
1426
+ promptColor: resolvedPromptColor,
1427
+ foregroundColor: theme.foreground,
1428
+ submittedText: loadingText,
1429
+ useKeyboard: useKeyboardProp
1430
+ }
1431
+ ) })
1432
+ ] });
1433
+ }
1434
+
1435
+ // ../ui/components/timeline/timeline.tsx
1436
+ import { useState as useState7, useEffect as useEffect2, useRef as useRef5 } from "react";
1437
+ import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1438
+ var DOTS = ["\u25CB", "\u25D4", "\u25D1", "\u25D5", "\u25CF"];
1439
+ function getStepDot(status) {
1440
+ return status === "pending" ? "\u25CB" : "\u25CF";
1441
+ }
1442
+ function getStepColor(status, theme) {
1443
+ switch (status) {
1444
+ case "done":
1445
+ return theme.success;
1446
+ case "running":
1447
+ return theme.primary;
1448
+ case "pending":
1449
+ return theme.muted;
1450
+ case "error":
1451
+ return theme.error;
1452
+ default:
1453
+ return theme.muted;
1454
+ }
1455
+ }
1456
+ function StepRow({ step, isLast, theme, frame }) {
1457
+ const color = getStepColor(step.status, theme);
1458
+ const isActive = step.status === "running";
1459
+ const isPending = step.status === "pending";
1460
+ const pipe = "\u2502";
1461
+ const dot = isActive ? DOTS[frame % DOTS.length] : getStepDot(step.status);
1462
+ const dashIdx = step.label.indexOf(" \u2014 ");
1463
+ const mainLabel = dashIdx >= 0 ? step.label.slice(0, dashIdx) : step.label;
1464
+ const detail = dashIdx >= 0 ? step.label.slice(dashIdx) : "";
1465
+ return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", marginLeft: 1, children: [
1466
+ /* @__PURE__ */ jsxs12("text", { children: [
1467
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color }), children: dot }),
1468
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.foreground }), children: " " }),
1469
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: isPending ? theme.muted : color, dim: isPending, bold: isActive }), children: mainLabel }),
1470
+ detail && /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: detail })
1471
+ ] }),
1472
+ step.output && /* @__PURE__ */ jsxs12("text", { children: [
1473
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color, dim: true }), children: pipe + " " }),
1474
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: step.status === "error" ? theme.error : theme.accent }), children: step.output })
1475
+ ] }),
1476
+ !isLast && /* @__PURE__ */ jsx18("text", { children: /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color, dim: true }), children: pipe }) })
1477
+ ] });
1478
+ }
1479
+ function Timeline({
1480
+ steps,
1481
+ duration,
1482
+ collapsed = true,
1483
+ headerLabel = "Thought for"
1484
+ }) {
1485
+ const theme = useTheme();
1486
+ const arrow = collapsed ? "\u25B6" : "\u25BC";
1487
+ const durationStr = duration ?? "0ms";
1488
+ const hasRunning = steps?.some((s) => s.status === "running") ?? false;
1489
+ const [frame, setFrame] = useState7(0);
1490
+ const alive = useRef5(true);
1491
+ useEffect2(() => {
1492
+ alive.current = true;
1493
+ return () => {
1494
+ alive.current = false;
1495
+ };
1496
+ }, []);
1497
+ useEffect2(() => {
1498
+ if (!hasRunning) return;
1499
+ const id = setInterval(() => {
1500
+ if (alive.current) setFrame((f) => f + 1);
1501
+ }, 150);
1502
+ return () => clearInterval(id);
1503
+ }, [hasRunning]);
1504
+ return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", children: [
1505
+ /* @__PURE__ */ jsxs12("text", { children: [
1506
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.muted }), children: arrow }),
1507
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " " + headerLabel + " " + durationStr })
1508
+ ] }),
1509
+ !collapsed && steps && steps.map((step, i) => /* @__PURE__ */ jsx18(
1510
+ StepRow,
1511
+ {
1512
+ step,
1513
+ isLast: i === steps.length - 1,
1514
+ theme,
1515
+ frame
1516
+ },
1517
+ `step-${i}`
1518
+ ))
1519
+ ] });
1520
+ }
1521
+
1522
+ // ../ui/components/message/message.tsx
1523
+ import { createContext as createContext5, useContext as useContext5 } from "react";
1524
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
1525
+ var MessageContext = createContext5(null);
1526
+ function useMessage() {
1527
+ const ctx = useContext5(MessageContext);
1528
+ if (!ctx) throw new Error("useMessage must be used within <Message>");
1529
+ return ctx;
1530
+ }
1531
+ function getBubbleColors(theme) {
1532
+ const isDark = theme.background !== "#FFFFFF";
1533
+ return isDark ? { assistantBg: "#2a2a4a", userBg: "#2a3a3a" } : { assistantBg: "#F1F5F9", userBg: "#E2E8F0" };
1534
+ }
1535
+ var TOOL_STATE_ICONS = {
1536
+ "partial-call": "\u2022",
1537
+ // •
1538
+ "call": "\u280B",
1539
+ // ⠋
1540
+ "result": "\u2713"
1541
+ // ✓
1542
+ };
1543
+ function getToolStateColor(state, theme) {
1544
+ switch (state) {
1545
+ case "partial-call":
1546
+ return theme.muted;
1547
+ case "call":
1548
+ return theme.warning;
1549
+ case "result":
1550
+ return theme.success;
1551
+ default:
1552
+ return theme.muted;
1553
+ }
1554
+ }
1555
+ function MessageContent({ children }) {
1556
+ const { role, backgroundColor } = useMessage();
1557
+ const isUser = role === "user";
1558
+ return /* @__PURE__ */ jsx19(
1559
+ "box",
1560
+ {
1561
+ flexDirection: "column",
1562
+ backgroundColor,
1563
+ paddingX: 2,
1564
+ paddingY: 1,
1565
+ ...isUser ? { maxWidth: "85%" } : { width: "85%" },
1566
+ children
1567
+ }
1568
+ );
1569
+ }
1570
+ function MessageText({ children, isLast = false }) {
1571
+ const { isStreaming, streamingCursor, backgroundColor, textColor } = useMessage();
1572
+ return /* @__PURE__ */ jsxs13("text", { wrapMode: "word", children: [
1573
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, bg: backgroundColor }), children }),
1574
+ isLast && isStreaming && /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: streamingCursor })
1575
+ ] });
1576
+ }
1577
+ function MessageReasoning({ part }) {
1578
+ return /* @__PURE__ */ jsx19(
1579
+ Timeline,
1580
+ {
1581
+ steps: part.steps,
1582
+ duration: part.duration,
1583
+ collapsed: part.collapsed
1584
+ }
1585
+ );
1586
+ }
1587
+ function MessageToolInvocation({ part, toolColors }) {
1588
+ const theme = useTheme();
1589
+ const { backgroundColor, textColor } = useMessage();
1590
+ const { toolName, state, result } = part.toolInvocation;
1591
+ const icon = TOOL_STATE_ICONS[state] || "\u2022";
1592
+ const stateColor = toolColors?.[toolName] ?? getToolStateColor(state, theme);
1593
+ const isActive = state === "partial-call" || state === "call";
1594
+ return /* @__PURE__ */ jsxs13("box", { flexDirection: "column", children: [
1595
+ /* @__PURE__ */ jsxs13("text", { children: [
1596
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bg: backgroundColor }), children: icon }),
1597
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, bg: backgroundColor }), children: " " }),
1598
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bold: isActive, bg: backgroundColor }), children: toolName }),
1599
+ isActive && /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: " ..." })
1600
+ ] }),
1601
+ state === "result" && result !== void 0 && /* @__PURE__ */ jsxs13("text", { children: [
1602
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: " \u2514\u2500 " }),
1603
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: String(result).slice(0, 120) })
1604
+ ] })
1605
+ ] });
1606
+ }
1607
+ function MessageSource({ part, index }) {
1608
+ const theme = useTheme();
1609
+ const { backgroundColor, textColor } = useMessage();
1610
+ const title = part.source.title || part.source.url || "source";
1611
+ return /* @__PURE__ */ jsxs13("text", { children: [
1612
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: "[" }),
1613
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: String(index + 1) }),
1614
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: "] " }),
1615
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: title })
1616
+ ] });
1617
+ }
1618
+ function MessageFooter({ model, timestamp }) {
1619
+ const theme = useTheme();
1620
+ if (!model && !timestamp) return null;
1621
+ return /* @__PURE__ */ jsxs13("text", { children: [
1622
+ model && /* @__PURE__ */ jsx19("span", { style: textStyle({ dim: true, fg: theme.muted }), children: model }),
1623
+ model && timestamp && /* @__PURE__ */ jsx19("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " \xB7 " }),
1624
+ timestamp && /* @__PURE__ */ jsx19("span", { style: textStyle({ dim: true, fg: theme.muted }), children: timestamp })
1625
+ ] });
1626
+ }
1627
+ function Message({
1628
+ role,
1629
+ isStreaming = false,
1630
+ streamingCursor = "\u258E",
1631
+ backgroundColor,
1632
+ children
1633
+ }) {
1634
+ const theme = useTheme();
1635
+ const { assistantBg, userBg } = getBubbleColors(theme);
1636
+ const isUser = role === "user";
1637
+ const bg = backgroundColor ?? (isUser ? userBg : assistantBg);
1638
+ return /* @__PURE__ */ jsx19(MessageContext.Provider, { value: { role, isStreaming, streamingCursor, backgroundColor: bg, textColor: theme.foreground }, children: /* @__PURE__ */ jsx19(
1639
+ "box",
1640
+ {
1641
+ flexDirection: "column",
1642
+ flexShrink: 0,
1643
+ alignItems: isUser ? "flex-end" : "flex-start",
1644
+ children
1645
+ }
1646
+ ) });
1647
+ }
1648
+ Message.Content = MessageContent;
1649
+ Message.Text = MessageText;
1650
+ Message.Reasoning = MessageReasoning;
1651
+ Message.ToolInvocation = MessageToolInvocation;
1652
+ Message.Source = MessageSource;
1653
+ Message.Footer = MessageFooter;
1654
+
1655
+ // ../ui/components/terminal-window/terminal-window.tsx
1656
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
1657
+
1658
+ // ../ui/components/breakpoints/use-breakpoints.ts
1659
+ import { useTerminalDimensions } from "@opentui/react";
1660
+ var BREAKPOINTS = {
1661
+ tiny: 40,
1662
+ narrow: 60,
1663
+ mobile: 70
1664
+ };
1665
+ function useBreakpoints() {
1666
+ const { width, height } = useTerminalDimensions();
1667
+ return {
1668
+ isTiny: width < BREAKPOINTS.tiny,
1669
+ isNarrow: width < BREAKPOINTS.narrow,
1670
+ isMobile: width < BREAKPOINTS.mobile,
1671
+ isDesktop: width >= BREAKPOINTS.mobile,
1672
+ width,
1673
+ height
1674
+ };
1675
+ }
1676
+
1677
+ // ../docs/components/landing/landing-app.tsx
1678
+ import { useState as useState10 } from "react";
1679
+
1680
+ // ../docs/components/landing/logo.tsx
1681
+ import { useState as useState8, useEffect as useEffect3, useRef as useRef6, useMemo as useMemo4 } from "react";
28
1682
  import figlet from "figlet";
29
1683
  import ansiShadow from "figlet/importable-fonts/ANSI Shadow.js";
1684
+ import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
1685
+ figlet.parseFont("ANSI Shadow", ansiShadow);
1686
+ function makeArt(text) {
1687
+ return figlet.textSync(text, { font: "ANSI Shadow" }).split("\n").filter((l) => l.trimEnd().length > 0).join("\n");
1688
+ }
1689
+ var fullArt = makeArt("gridland");
1690
+ var gridArt = makeArt("grid");
1691
+ var landArt = makeArt("land");
1692
+ var ART_HEIGHT = 6;
1693
+ function useAnimation(duration = 1e3) {
1694
+ const isBrowser = typeof document !== "undefined";
1695
+ const [progress, setProgress] = useState8(isBrowser ? 0 : 1);
1696
+ const startTime = useRef6(null);
1697
+ useEffect3(() => {
1698
+ if (!isBrowser) return;
1699
+ let raf;
1700
+ const tick = (time) => {
1701
+ if (startTime.current === null) startTime.current = time;
1702
+ const elapsed = time - startTime.current;
1703
+ const t = Math.min(1, elapsed / duration);
1704
+ const eased = 1 - Math.pow(1 - t, 3);
1705
+ setProgress(eased);
1706
+ if (t < 1) raf = requestAnimationFrame(tick);
1707
+ };
1708
+ raf = requestAnimationFrame(tick);
1709
+ return () => cancelAnimationFrame(raf);
1710
+ }, []);
1711
+ return progress;
1712
+ }
1713
+ function RevealGradient({ children, revealCol }) {
1714
+ const gradientColors = GRADIENTS.instagram;
1715
+ const lines = children.split("\n");
1716
+ const maxLength = Math.max(...lines.map((l) => l.length));
1717
+ if (maxLength === 0) return /* @__PURE__ */ jsx21("text", { children });
1718
+ const hexColors = useMemo4(() => generateGradient(gradientColors, maxLength), [maxLength]);
1719
+ return /* @__PURE__ */ jsx21("box", { position: "relative", width: maxLength, height: lines.length, shouldFill: false, children: lines.map((line, lineIndex) => {
1720
+ const runs = [];
1721
+ let current = null;
1722
+ for (let i = 0; i < line.length; i++) {
1723
+ const revealed = i <= revealCol;
1724
+ const char = line[i];
1725
+ const isVisible = revealed && char !== " ";
1726
+ if (isVisible) {
1727
+ if (!current) {
1728
+ current = { start: i, chars: [] };
1729
+ }
1730
+ current.chars.push(char);
1731
+ } else {
1732
+ if (current) {
1733
+ runs.push(current);
1734
+ current = null;
1735
+ }
1736
+ }
1737
+ }
1738
+ if (current) runs.push(current);
1739
+ return runs.map((run, runIndex) => /* @__PURE__ */ jsx21(
1740
+ "box",
1741
+ {
1742
+ position: "absolute",
1743
+ top: lineIndex,
1744
+ left: run.start,
1745
+ shouldFill: false,
1746
+ children: /* @__PURE__ */ jsx21("text", { shouldFill: false, children: run.chars.map((char, ci) => /* @__PURE__ */ jsx21(
1747
+ "span",
1748
+ {
1749
+ style: { fg: hexColors[run.start + ci] },
1750
+ children: char
1751
+ },
1752
+ ci
1753
+ )) })
1754
+ },
1755
+ `${lineIndex}-${runIndex}`
1756
+ ));
1757
+ }) });
1758
+ }
1759
+ function Logo({ compact, narrow, mobile }) {
1760
+ const isBrowser = typeof document !== "undefined";
1761
+ const progress = useAnimation(900);
1762
+ const artHeight = compact ? 1 : narrow ? ART_HEIGHT * 2 : ART_HEIGHT;
1763
+ const dropOffset = Math.round((1 - progress) * -artHeight);
1764
+ const revealProgress = Math.max(0, Math.min(1, (progress - 0.1) / 0.7));
1765
+ const maxWidth = compact ? 8 : narrow ? 40 : 62;
1766
+ const revealCol = Math.round(revealProgress * (maxWidth + 4)) - 2;
1767
+ const taglineOpacity = Math.max(0, Math.min(1, (progress - 0.7) / 0.3));
1768
+ const subtitle = /* @__PURE__ */ jsxs15(Fragment6, { children: [
1769
+ /* @__PURE__ */ jsx21("text", { children: " " }),
1770
+ /* @__PURE__ */ jsx21("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs15("text", { style: textStyle({ fg: "#d4b0e8" }), opacity: taglineOpacity, wrapMode: "word", textAlign: "center", width: "100%", shouldFill: false, children: [
1771
+ "A framework for building terminal apps, built on ",
1772
+ /* @__PURE__ */ jsx21("a", { href: "https://opentui.com", style: { attributes: 72, fg: "#d4b0e8" }, children: "OpenTUI" }),
1773
+ " + React." + (mobile ? " " : "\n") + "(Gridland apps, like this website, work in the browser and terminal.)"
1774
+ ] }) })
1775
+ ] });
1776
+ if (!isBrowser) {
1777
+ const art = compact ? "gridland" : narrow ? gridArt + "\n" + landArt : fullArt;
1778
+ return /* @__PURE__ */ jsxs15("box", { flexDirection: "column", flexShrink: 0, width: "100%", alignItems: "center", children: [
1779
+ /* @__PURE__ */ jsx21(Gradient, { name: "instagram", children: art }),
1780
+ /* @__PURE__ */ jsx21("text", { children: " " }),
1781
+ /* @__PURE__ */ jsx21("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs15("text", { style: textStyle({ fg: "#d4b0e8" }), shouldFill: false, children: [
1782
+ "A framework for building terminal apps, built on OpenTUI + React.",
1783
+ "\n",
1784
+ "(Gridland apps, like this website, work in the browser and terminal.)"
1785
+ ] }) })
1786
+ ] });
1787
+ }
1788
+ if (compact) {
1789
+ return /* @__PURE__ */ jsxs15("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
1790
+ /* @__PURE__ */ jsx21("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx21("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx21(RevealGradient, { revealCol, children: "gridland" }) }) }),
1791
+ subtitle
1792
+ ] });
1793
+ }
1794
+ if (narrow) {
1795
+ return /* @__PURE__ */ jsxs15("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
1796
+ /* @__PURE__ */ jsx21("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsxs15("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: [
1797
+ /* @__PURE__ */ jsx21(RevealGradient, { revealCol, children: gridArt }),
1798
+ /* @__PURE__ */ jsx21(RevealGradient, { revealCol, children: landArt })
1799
+ ] }) }),
1800
+ subtitle
1801
+ ] });
1802
+ }
1803
+ return /* @__PURE__ */ jsxs15("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
1804
+ /* @__PURE__ */ jsx21("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx21("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx21(RevealGradient, { revealCol, children: fullArt }) }) }),
1805
+ subtitle
1806
+ ] });
1807
+ }
1808
+
1809
+ // ../docs/components/landing/install-box.tsx
1810
+ import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
1811
+ function InstallBox() {
1812
+ const theme = useTheme();
1813
+ return /* @__PURE__ */ jsx22(
1814
+ "box",
1815
+ {
1816
+ border: true,
1817
+ borderStyle: "rounded",
1818
+ borderColor: theme.border,
1819
+ paddingX: 1,
1820
+ flexDirection: "column",
1821
+ flexShrink: 0,
1822
+ children: /* @__PURE__ */ jsxs16("text", { children: [
1823
+ /* @__PURE__ */ jsx22("span", { style: textStyle({ dim: true }), children: "$ " }),
1824
+ /* @__PURE__ */ jsx22("span", { style: textStyle({ bold: true }), children: "bun create " }),
1825
+ /* @__PURE__ */ jsx22("span", { style: textStyle({ fg: theme.accent }), children: "gridland" })
1826
+ ] })
1827
+ }
1828
+ );
1829
+ }
1830
+
1831
+ // ../docs/components/landing/links-box.tsx
1832
+ import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
1833
+ var UNDERLINE3 = 1 << 3;
1834
+ function LinksBox() {
1835
+ const theme = useTheme();
1836
+ return /* @__PURE__ */ jsx23(
1837
+ "box",
1838
+ {
1839
+ border: true,
1840
+ borderStyle: "rounded",
1841
+ borderColor: theme.border,
1842
+ paddingX: 1,
1843
+ flexDirection: "column",
1844
+ flexShrink: 0,
1845
+ children: /* @__PURE__ */ jsxs17("text", { children: [
1846
+ /* @__PURE__ */ jsx23("span", { children: "\u{1F431}" }),
1847
+ /* @__PURE__ */ jsx23("a", { href: "https://github.com/cjroth/gridland", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " GitHub" }),
1848
+ /* @__PURE__ */ jsx23("span", { children: " " }),
1849
+ /* @__PURE__ */ jsx23("span", { children: "\u{1F4D6}" }),
1850
+ /* @__PURE__ */ jsx23("a", { href: "/docs", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " Docs" })
1851
+ ] })
1852
+ }
1853
+ );
1854
+ }
1855
+
1856
+ // ../docs/components/landing/matrix-background.tsx
1857
+ import { useMemo as useMemo5 } from "react";
1858
+
1859
+ // ../docs/components/landing/use-matrix.ts
1860
+ import { useState as useState9, useEffect as useEffect4, useRef as useRef7 } from "react";
1861
+ var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789@#$%^&*(){}[]|;:<>,.?/~`";
1862
+ function randomChar() {
1863
+ return CHARS[Math.floor(Math.random() * CHARS.length)];
1864
+ }
1865
+ function createDrop(height, seeded = false) {
1866
+ const length = Math.floor(Math.random() * Math.floor(height * 0.6)) + 4;
1867
+ return {
1868
+ y: seeded ? Math.floor(Math.random() * (height + length)) : -Math.floor(Math.random() * height),
1869
+ speed: 1,
1870
+ length,
1871
+ chars: Array.from({ length }, randomChar)
1872
+ };
1873
+ }
1874
+ function buildGrid(columns, width, height) {
1875
+ const grid = Array.from({ length: height }, () => Array(width).fill(" "));
1876
+ const brightness = Array.from({ length: height }, () => Array(width).fill(0));
1877
+ for (let x = 0; x < width; x++) {
1878
+ const drop = columns[x];
1879
+ if (!drop) continue;
1880
+ for (let i = 0; i < drop.length; i++) {
1881
+ const row = Math.floor(drop.y) - i;
1882
+ if (row < 0 || row >= height) continue;
1883
+ grid[row][x] = drop.chars[i];
1884
+ if (i === 0) {
1885
+ brightness[row][x] = 1;
1886
+ } else {
1887
+ brightness[row][x] = Math.max(0.15, 1 - i / drop.length);
1888
+ }
1889
+ }
1890
+ }
1891
+ return { grid, brightness };
1892
+ }
1893
+ function useMatrix(width, height) {
1894
+ const columnsRef = useRef7([]);
1895
+ const [state, setState] = useState9(() => {
1896
+ const columns = Array.from(
1897
+ { length: width },
1898
+ () => Math.random() < 0.5 ? createDrop(height, true) : null
1899
+ );
1900
+ columnsRef.current = columns;
1901
+ return buildGrid(columns, width, height);
1902
+ });
1903
+ useEffect4(() => {
1904
+ if (width < 2 || height < 2) return;
1905
+ const id = setInterval(() => {
1906
+ const columns = columnsRef.current;
1907
+ for (let x = 0; x < width; x++) {
1908
+ if (columns[x] === null || columns[x] === void 0) {
1909
+ if (Math.random() < 0.03) {
1910
+ columns[x] = createDrop(height);
1911
+ }
1912
+ continue;
1913
+ }
1914
+ const drop = columns[x];
1915
+ drop.y += drop.speed;
1916
+ if (Math.random() < 0.1) {
1917
+ const idx = Math.floor(Math.random() * drop.chars.length);
1918
+ drop.chars[idx] = randomChar();
1919
+ }
1920
+ if (drop.y - drop.length > height) {
1921
+ columns[x] = null;
1922
+ }
1923
+ }
1924
+ setState(buildGrid(columns, width, height));
1925
+ }, 80);
1926
+ return () => clearInterval(id);
1927
+ }, [width, height]);
1928
+ useEffect4(() => {
1929
+ columnsRef.current = Array.from(
1930
+ { length: width },
1931
+ () => Math.random() < 0.5 ? createDrop(height, true) : null
1932
+ );
1933
+ setState(buildGrid(columnsRef.current, width, height));
1934
+ }, [width, height]);
1935
+ return state;
1936
+ }
1937
+
1938
+ // ../docs/components/landing/matrix-background.tsx
1939
+ import { jsx as jsx24 } from "react/jsx-runtime";
1940
+ var MUTE_LEVELS = [0.12, 0.18, 0.24, 0.3, 0.38];
1941
+ var BG = hexToRgb("#1a1a2e");
1942
+ function buildMutedColors(baseHex) {
1943
+ const fg = hexToRgb(baseHex);
1944
+ return MUTE_LEVELS.map((factor) => rgbToHex({
1945
+ r: Math.round(BG.r + (fg.r - BG.r) * factor),
1946
+ g: Math.round(BG.g + (fg.g - BG.g) * factor),
1947
+ b: Math.round(BG.b + (fg.b - BG.b) * factor)
1948
+ }));
1949
+ }
1950
+ function colorForCell(mutedColors, b) {
1951
+ if (b >= 1) return mutedColors[4];
1952
+ const idx = Math.min(Math.floor(b * (MUTE_LEVELS.length - 1)), MUTE_LEVELS.length - 2);
1953
+ return mutedColors[idx];
1954
+ }
1955
+ function MatrixBackground({ width, height, clearRect, clearRects }) {
1956
+ const { grid, brightness } = useMatrix(width, height);
1957
+ const theme = useTheme();
1958
+ const columnColors = useMemo5(
1959
+ () => width > 0 ? generateGradient([theme.accent, theme.secondary, theme.primary], width) : [],
1960
+ [width, theme.accent, theme.secondary, theme.primary]
1961
+ );
1962
+ const columnMutedColors = useMemo5(
1963
+ () => columnColors.map(buildMutedColors),
1964
+ [columnColors]
1965
+ );
1966
+ return /* @__PURE__ */ jsx24("box", { flexDirection: "column", children: grid.map((row, y) => /* @__PURE__ */ jsx24("text", { children: row.map((cell, x) => {
1967
+ const inClearRect = clearRect && y >= clearRect.top && y < clearRect.top + clearRect.height && x >= clearRect.left && x < clearRect.left + clearRect.width || clearRects && clearRects.some(
1968
+ (r) => y >= r.top && y < r.top + r.height && x >= r.left && x < r.left + r.width
1969
+ );
1970
+ const mutedColors = columnMutedColors[x];
1971
+ if (cell === " " || inClearRect || !mutedColors) {
1972
+ return /* @__PURE__ */ jsx24("span", { children: " " }, x);
1973
+ }
1974
+ return /* @__PURE__ */ jsx24(
1975
+ "span",
1976
+ {
1977
+ style: {
1978
+ fg: colorForCell(mutedColors, brightness[y][x])
1979
+ },
1980
+ children: cell
1981
+ },
1982
+ x
1983
+ );
1984
+ }) }, y)) });
1985
+ }
1986
+
1987
+ // ../docs/components/landing/about-modal.tsx
1988
+ import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
1989
+ function AboutModal({ onClose, useKeyboard: useKeyboard3 }) {
1990
+ const theme = useTheme();
1991
+ return /* @__PURE__ */ jsx25(Modal, { title: "About Gridland", useKeyboard: useKeyboard3, onClose, children: /* @__PURE__ */ jsxs18("box", { paddingX: 1, flexDirection: "column", gap: 1, children: [
1992
+ /* @__PURE__ */ jsx25("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "What is Gridland?" }),
1993
+ /* @__PURE__ */ jsx25("text", { children: "Gridland renders terminal UIs to HTML5 Canvas with React." }),
1994
+ /* @__PURE__ */ jsx25("text", { children: "No xterm.js. No terminal emulator. Just pixels." }),
1995
+ /* @__PURE__ */ jsx25("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "Features" }),
1996
+ /* @__PURE__ */ jsxs18("text", { children: [
1997
+ /* @__PURE__ */ jsxs18("span", { style: textStyle({ dim: true }), children: [
1998
+ "\u2022",
1999
+ " "
2000
+ ] }),
2001
+ "Canvas-rendered TUI components"
2002
+ ] }),
2003
+ /* @__PURE__ */ jsxs18("text", { children: [
2004
+ /* @__PURE__ */ jsxs18("span", { style: textStyle({ dim: true }), children: [
2005
+ "\u2022",
2006
+ " "
2007
+ ] }),
2008
+ "React reconciler with JSX"
2009
+ ] }),
2010
+ /* @__PURE__ */ jsxs18("text", { children: [
2011
+ /* @__PURE__ */ jsxs18("span", { style: textStyle({ dim: true }), children: [
2012
+ "\u2022",
2013
+ " "
2014
+ ] }),
2015
+ "Yoga flexbox layout engine"
2016
+ ] }),
2017
+ /* @__PURE__ */ jsxs18("text", { children: [
2018
+ /* @__PURE__ */ jsxs18("span", { style: textStyle({ dim: true }), children: [
2019
+ "\u2022",
2020
+ " "
2021
+ ] }),
2022
+ "Keyboard, mouse, and clipboard support"
2023
+ ] }),
2024
+ /* @__PURE__ */ jsxs18("text", { children: [
2025
+ /* @__PURE__ */ jsxs18("span", { style: textStyle({ dim: true }), children: [
2026
+ "\u2022",
2027
+ " "
2028
+ ] }),
2029
+ "Next.js and Vite plugins"
2030
+ ] }),
2031
+ /* @__PURE__ */ jsx25("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "Tech Stack" }),
2032
+ /* @__PURE__ */ jsx25("text", { children: "React + opentui engine + yoga-layout + HTML5 Canvas" }),
2033
+ /* @__PURE__ */ jsx25("text", { style: textStyle({ dim: true }), children: "Press q to close" })
2034
+ ] }) });
2035
+ }
2036
+
2037
+ // ../docs/components/landing/landing-app.tsx
2038
+ import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
2039
+ function LandingApp({ useKeyboard: useKeyboard3 }) {
2040
+ const theme = useTheme();
2041
+ const { width, height, isNarrow, isTiny, isMobile } = useBreakpoints();
2042
+ const [showAbout, setShowAbout] = useState10(false);
2043
+ useKeyboard3((event) => {
2044
+ if (event.name === "a" && !showAbout) {
2045
+ setShowAbout(true);
2046
+ }
2047
+ if (event.name === "q" && showAbout) {
2048
+ setShowAbout(false);
2049
+ }
2050
+ });
2051
+ if (showAbout) {
2052
+ return /* @__PURE__ */ jsxs19("box", { flexDirection: "column", width: "100%", height: "100%", children: [
2053
+ /* @__PURE__ */ jsx26("box", { flexGrow: 1, children: /* @__PURE__ */ jsx26(AboutModal, { onClose: () => setShowAbout(false), useKeyboard: useKeyboard3 }) }),
2054
+ /* @__PURE__ */ jsx26(StatusBar, { items: [{ key: "q", label: "close" }] })
2055
+ ] });
2056
+ }
2057
+ const isBrowser = typeof document !== "undefined";
2058
+ const logoHeight = isTiny ? 2 : isNarrow ? 13 : 7;
2059
+ const logoExtra = isBrowser ? 1 : 0;
2060
+ const gap = isMobile ? 0 : 1;
2061
+ const installLinksTop = 3 + logoHeight + logoExtra + gap;
2062
+ const installLinksHeight = 3;
2063
+ const boxTop = installLinksTop + installLinksHeight + gap + 1;
2064
+ const boxHeight = height - boxTop - 1 - 1;
2065
+ const clearRect = { top: boxTop, left: 1, width: width - 2, height: boxHeight };
2066
+ const installLinksClearRect = { top: installLinksTop, left: 1, width: width - 2, height: installLinksHeight };
2067
+ return /* @__PURE__ */ jsxs19("box", { width: "100%", height: "100%", position: "relative", children: [
2068
+ /* @__PURE__ */ jsx26(MatrixBackground, { width, height, clearRect, clearRects: [installLinksClearRect] }),
2069
+ /* @__PURE__ */ jsxs19(
2070
+ "box",
2071
+ {
2072
+ position: "absolute",
2073
+ top: 0,
2074
+ left: 0,
2075
+ width,
2076
+ height,
2077
+ zIndex: 1,
2078
+ flexDirection: "column",
2079
+ shouldFill: false,
2080
+ children: [
2081
+ /* @__PURE__ */ jsxs19("box", { flexGrow: 1, flexDirection: "column", paddingTop: 3, paddingLeft: 1, paddingRight: 1, paddingBottom: 1, gap: isMobile ? 0 : 1, shouldFill: false, children: [
2082
+ /* @__PURE__ */ jsx26("box", { flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx26(Logo, { compact: isTiny, narrow: isNarrow, mobile: isMobile }) }),
2083
+ /* @__PURE__ */ jsxs19("box", { flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: isMobile ? 0 : 1, flexShrink: 0, shouldFill: false, children: [
2084
+ /* @__PURE__ */ jsx26(
2085
+ "box",
2086
+ {
2087
+ border: true,
2088
+ borderStyle: "rounded",
2089
+ borderColor: theme.border,
2090
+ paddingX: 1,
2091
+ flexDirection: "column",
2092
+ flexShrink: 0,
2093
+ children: /* @__PURE__ */ jsxs19("text", { children: [
2094
+ /* @__PURE__ */ jsx26("span", { style: textStyle({ dim: true }), children: "$ " }),
2095
+ /* @__PURE__ */ jsx26("span", { style: textStyle({ bold: true }), children: "bunx " }),
2096
+ /* @__PURE__ */ jsx26("span", { style: textStyle({ fg: theme.accent }), children: "@gridland/demo landing" })
2097
+ ] })
2098
+ }
2099
+ ),
2100
+ /* @__PURE__ */ jsx26(InstallBox, {}),
2101
+ /* @__PURE__ */ jsx26(LinksBox, {})
2102
+ ] }),
2103
+ /* @__PURE__ */ jsx26(
2104
+ "box",
2105
+ {
2106
+ flexGrow: 1,
2107
+ border: true,
2108
+ borderStyle: "rounded",
2109
+ borderColor: theme.border
2110
+ }
2111
+ )
2112
+ ] }),
2113
+ /* @__PURE__ */ jsx26(
2114
+ StatusBar,
2115
+ {
2116
+ items: [
2117
+ { key: "a", label: "about" }
2118
+ ]
2119
+ }
2120
+ )
2121
+ ]
2122
+ }
2123
+ )
2124
+ ] });
2125
+ }
2126
+
2127
+ // ../docs/components/landing/matrix-rain.tsx
2128
+ import { jsx as jsx27 } from "react/jsx-runtime";
2129
+
2130
+ // ../ui/scripts/demo-apps.tsx
2131
+ import figlet2 from "figlet";
2132
+ import ansiShadow2 from "figlet/importable-fonts/ANSI Shadow.js";
30
2133
  import big from "figlet/importable-fonts/Big.js";
31
2134
  import doom from "figlet/importable-fonts/Doom.js";
32
2135
  import slant from "figlet/importable-fonts/Slant.js";
@@ -34,9 +2137,9 @@ import speed from "figlet/importable-fonts/Speed.js";
34
2137
  import standard from "figlet/importable-fonts/Standard.js";
35
2138
  import block from "figlet/importable-fonts/Block.js";
36
2139
  import colossal from "figlet/importable-fonts/Colossal.js";
37
- import { jsx, jsxs } from "react/jsx-runtime";
2140
+ import { jsx as jsx28, jsxs as jsxs20 } from "react/jsx-runtime";
38
2141
  var fonts = [
39
- { name: "ANSI Shadow", data: ansiShadow },
2142
+ { name: "ANSI Shadow", data: ansiShadow2 },
40
2143
  { name: "Standard", data: standard },
41
2144
  { name: "Big", data: big },
42
2145
  { name: "Doom", data: doom },
@@ -46,47 +2149,47 @@ var fonts = [
46
2149
  { name: "Colossal", data: colossal }
47
2150
  ];
48
2151
  for (const f of fonts) {
49
- figlet.parseFont(f.name, f.data);
2152
+ figlet2.parseFont(f.name, f.data);
50
2153
  }
51
2154
  function getFigletLines(fontName, text = "gridland") {
52
- const art = figlet.textSync(text, { font: fontName });
2155
+ const art = figlet2.textSync(text, { font: fontName });
53
2156
  return art.split("\n").filter((l) => l.trimEnd().length > 0);
54
2157
  }
55
2158
  var gradientNames = Object.keys(GRADIENTS);
56
2159
  function GradientApp() {
57
- const [index, setIndex] = useState(0);
2160
+ const [index, setIndex] = useState11(0);
58
2161
  const name = gradientNames[index];
59
2162
  const lines = getFigletLines("ANSI Shadow");
60
2163
  useKeyboard((event) => {
61
2164
  if (event.name === "left") setIndex((i) => i > 0 ? i - 1 : gradientNames.length - 1);
62
2165
  if (event.name === "right") setIndex((i) => i < gradientNames.length - 1 ? i + 1 : 0);
63
2166
  });
64
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
65
- /* @__PURE__ */ jsx("box", { padding: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: /* @__PURE__ */ jsx(Gradient, { name, children: lines.join("\n") }) }),
66
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(
2167
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2168
+ /* @__PURE__ */ jsx28("box", { padding: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: /* @__PURE__ */ jsx28(Gradient, { name, children: lines.join("\n") }) }),
2169
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(
67
2170
  StatusBar,
68
2171
  {
69
2172
  items: [{ key: "\u2190\u2192", label: "gradient" }, { key: "q", label: "quit" }],
70
- extra: /* @__PURE__ */ jsx("span", { style: textStyle({ fg: "cyan", bold: true }), children: name.padEnd(11) })
2173
+ extra: /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "cyan", bold: true }), children: name.padEnd(11) })
71
2174
  }
72
2175
  ) })
73
2176
  ] });
74
2177
  }
75
2178
  function AsciiApp() {
76
- const [fontIndex, setFontIndex] = useState(0);
2179
+ const [fontIndex, setFontIndex] = useState11(0);
77
2180
  const font = fonts[fontIndex];
78
2181
  const lines = getFigletLines(font.name);
79
2182
  useKeyboard((event) => {
80
2183
  if (event.name === "left") setFontIndex((i) => i > 0 ? i - 1 : fonts.length - 1);
81
2184
  if (event.name === "right") setFontIndex((i) => i < fonts.length - 1 ? i + 1 : 0);
82
2185
  });
83
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
84
- /* @__PURE__ */ jsx("box", { padding: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx("text", { fg: "#88c0d0", bold: true, children: line }, i)) }),
85
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(
2186
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2187
+ /* @__PURE__ */ jsx28("box", { padding: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx28("text", { fg: "#88c0d0", bold: true, children: line }, i)) }),
2188
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(
86
2189
  StatusBar,
87
2190
  {
88
2191
  items: [{ key: "\u2190\u2192", label: "change font" }, { key: "q", label: "quit" }],
89
- extra: /* @__PURE__ */ jsx("span", { style: textStyle({ fg: "cyan", bold: true }), children: font.name.padEnd(11) })
2192
+ extra: /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "cyan", bold: true }), children: font.name.padEnd(11) })
90
2193
  }
91
2194
  ) })
92
2195
  ] });
@@ -97,28 +2200,28 @@ function TableApp() {
97
2200
  { name: "Bob", role: "Designer", status: "Active" },
98
2201
  { name: "Charlie", role: "PM", status: "Away" }
99
2202
  ];
100
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
101
- /* @__PURE__ */ jsx("box", { padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx(Table, { data, headerColor: "cyan", borderColor: "#5e81ac" }) }),
102
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2203
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2204
+ /* @__PURE__ */ jsx28("box", { padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28(Table, { data, headerColor: "cyan", borderColor: "#5e81ac" }) }),
2205
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
103
2206
  ] });
104
2207
  }
105
2208
  function SpinnerApp() {
106
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
107
- /* @__PURE__ */ jsx("box", { flexGrow: 1, children: /* @__PURE__ */ jsx(SpinnerPicker, { useKeyboard }) }),
108
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "\u2190\u2192", label: "change variant" }, { key: "q", label: "quit" }] }) })
2209
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2210
+ /* @__PURE__ */ jsx28("box", { flexGrow: 1, children: /* @__PURE__ */ jsx28(SpinnerPicker, { useKeyboard }) }),
2211
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "\u2190\u2192", label: "change variant" }, { key: "q", label: "quit" }] }) })
109
2212
  ] });
110
2213
  }
111
2214
  function SelectInputApp() {
112
- const [submitted, setSubmitted] = useState(false);
2215
+ const [submitted, setSubmitted] = useState11(false);
113
2216
  const items = [
114
2217
  { label: "TypeScript", value: "ts" },
115
2218
  { label: "JavaScript", value: "js" },
116
2219
  { label: "Python", value: "py" },
117
2220
  { label: "Rust", value: "rs" }
118
2221
  ];
119
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
120
- /* @__PURE__ */ jsx("box", { padding: 1, flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx(SelectInput, { items, title: "Choose a language", useKeyboard, onSubmit: () => setSubmitted(true) }) }),
121
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: submitted ? [{ key: "q", label: "quit" }] : [
2222
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2223
+ /* @__PURE__ */ jsx28("box", { padding: 1, flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx28(SelectInput, { items, title: "Choose a language", useKeyboard, onSubmit: () => setSubmitted(true) }) }),
2224
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: submitted ? [{ key: "q", label: "quit" }] : [
122
2225
  { key: "\u2191\u2193", label: "select" },
123
2226
  { key: "enter", label: "submit" },
124
2227
  { key: "q", label: "quit" }
@@ -126,16 +2229,16 @@ function SelectInputApp() {
126
2229
  ] });
127
2230
  }
128
2231
  function MultiSelectApp() {
129
- const [submitted, setSubmitted] = useState(false);
2232
+ const [submitted, setSubmitted] = useState11(false);
130
2233
  const items = [
131
2234
  { label: "TypeScript", value: "ts" },
132
2235
  { label: "JavaScript", value: "js" },
133
2236
  { label: "Python", value: "py" },
134
2237
  { label: "Rust", value: "rs" }
135
2238
  ];
136
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
137
- /* @__PURE__ */ jsx("box", { padding: 1, flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx(MultiSelect, { items, title: "Select languages", useKeyboard, onSubmit: () => setSubmitted(true) }) }),
138
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: submitted ? [{ key: "q", label: "quit" }] : [
2239
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2240
+ /* @__PURE__ */ jsx28("box", { padding: 1, flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx28(MultiSelect, { items, title: "Select languages", useKeyboard, onSubmit: () => setSubmitted(true) }) }),
2241
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: submitted ? [{ key: "q", label: "quit" }] : [
139
2242
  { key: "\u2191\u2193", label: "move" },
140
2243
  { key: "enter", label: "select" },
141
2244
  { key: "a", label: "all" },
@@ -145,10 +2248,10 @@ function MultiSelectApp() {
145
2248
  ] });
146
2249
  }
147
2250
  function PromptInputApp() {
148
- const [lastMessage, setLastMessage] = useState("");
149
- const [model, setModel] = useState("opus");
150
- const [showModelPicker, setShowModelPicker] = useState(false);
151
- const [resetKey, setResetKey] = useState(0);
2251
+ const [lastMessage, setLastMessage] = useState11("");
2252
+ const [model, setModel] = useState11("opus");
2253
+ const [showModelPicker, setShowModelPicker] = useState11(false);
2254
+ const [resetKey, setResetKey] = useState11(0);
152
2255
  const commands = [
153
2256
  { cmd: "/help", desc: "Show commands" },
154
2257
  { cmd: "/model", desc: "Switch model" },
@@ -174,14 +2277,14 @@ function PromptInputApp() {
174
2277
  setLastMessage(msg.text);
175
2278
  };
176
2279
  if (showModelPicker) {
177
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
178
- /* @__PURE__ */ jsx(
2280
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2281
+ /* @__PURE__ */ jsx28(
179
2282
  Modal,
180
2283
  {
181
2284
  title: "Select Model",
182
2285
  useKeyboard,
183
2286
  onClose: () => setShowModelPicker(false),
184
- children: /* @__PURE__ */ jsx("box", { paddingX: 1, children: /* @__PURE__ */ jsx(
2287
+ children: /* @__PURE__ */ jsx28("box", { paddingX: 1, children: /* @__PURE__ */ jsx28(
185
2288
  SelectInput,
186
2289
  {
187
2290
  items: models,
@@ -195,19 +2298,19 @@ function PromptInputApp() {
195
2298
  ) })
196
2299
  }
197
2300
  ),
198
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [
2301
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [
199
2302
  { key: "\u23CE", label: "select" },
200
2303
  { key: "esc", label: "cancel" },
201
2304
  { key: "q", label: "quit" }
202
2305
  ] }) })
203
2306
  ] });
204
2307
  }
205
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
206
- /* @__PURE__ */ jsx("box", { padding: 1, flexDirection: "column", flexGrow: 1, children: lastMessage ? /* @__PURE__ */ jsxs("text", { children: [
207
- /* @__PURE__ */ jsx("span", { children: "Sent: " }),
208
- /* @__PURE__ */ jsx("span", { children: lastMessage })
209
- ] }) : /* @__PURE__ */ jsx("text", { style: textStyle({ dim: true }), children: "Type a message and press enter" }) }),
210
- /* @__PURE__ */ jsx("box", { paddingX: 1, children: /* @__PURE__ */ jsx(
2308
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2309
+ /* @__PURE__ */ jsx28("box", { padding: 1, flexDirection: "column", flexGrow: 1, children: lastMessage ? /* @__PURE__ */ jsxs20("text", { children: [
2310
+ /* @__PURE__ */ jsx28("span", { children: "Sent: " }),
2311
+ /* @__PURE__ */ jsx28("span", { children: lastMessage })
2312
+ ] }) : /* @__PURE__ */ jsx28("text", { style: textStyle({ dim: true }), children: "Type a message and press enter" }) }),
2313
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, children: /* @__PURE__ */ jsx28(
211
2314
  PromptInput,
212
2315
  {
213
2316
  commands,
@@ -219,11 +2322,11 @@ function PromptInputApp() {
219
2322
  },
220
2323
  resetKey
221
2324
  ) }),
222
- /* @__PURE__ */ jsx("box", { paddingX: 1, children: /* @__PURE__ */ jsxs("text", { children: [
223
- /* @__PURE__ */ jsx("span", { style: textStyle({ fg: "#C4A8FF" }), children: "[\u22A1_\u22A1]" }),
224
- /* @__PURE__ */ jsx("span", { style: textStyle({ dim: true }), children: " " + model })
2325
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, children: /* @__PURE__ */ jsxs20("text", { children: [
2326
+ /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "#C4A8FF" }), children: "[\u22A1_\u22A1]" }),
2327
+ /* @__PURE__ */ jsx28("span", { style: textStyle({ dim: true }), children: " " + model })
225
2328
  ] }) }),
226
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [
2329
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [
227
2330
  { key: "\u23CE", label: "send" },
228
2331
  { key: "/", label: "commands" },
229
2332
  { key: "@", label: "files" },
@@ -239,18 +2342,18 @@ var TEXT_INPUT_FIELDS = [
239
2342
  { label: "API Key", placeholder: "sk-...", maxLength: 60, disabled: true }
240
2343
  ];
241
2344
  function TextInputApp() {
242
- const [activeField, setActiveField] = useState(0);
243
- const [values, setValues] = useState(TEXT_INPUT_FIELDS.map(() => ""));
2345
+ const [activeField, setActiveField] = useState11(0);
2346
+ const [values, setValues] = useState11(TEXT_INPUT_FIELDS.map(() => ""));
244
2347
  useKeyboard((event) => {
245
2348
  if (event.name === "up") setActiveField((i) => Math.max(0, i - 1));
246
2349
  if (event.name === "down" || event.name === "tab") setActiveField((i) => Math.min(TEXT_INPUT_FIELDS.length - 1, i + 1));
247
2350
  });
248
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
249
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingTop: 1, children: /* @__PURE__ */ jsxs("text", { children: [
250
- /* @__PURE__ */ jsx("span", { style: textStyle({ fg: "#FF71CE", bold: true }), children: "TextInput" }),
251
- /* @__PURE__ */ jsx("span", { style: textStyle({ dim: true }), children: " Form with multiple input types" })
2351
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2352
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingTop: 1, children: /* @__PURE__ */ jsxs20("text", { children: [
2353
+ /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "#FF71CE", bold: true }), children: "TextInput" }),
2354
+ /* @__PURE__ */ jsx28("span", { style: textStyle({ dim: true }), children: " Form with multiple input types" })
252
2355
  ] }) }),
253
- /* @__PURE__ */ jsx("box", { flexDirection: "column", paddingX: 1, paddingTop: 1, flexGrow: 1, children: TEXT_INPUT_FIELDS.map((field, i) => /* @__PURE__ */ jsx("box", { marginBottom: 1, children: /* @__PURE__ */ jsx(
2356
+ /* @__PURE__ */ jsx28("box", { flexDirection: "column", paddingX: 1, paddingTop: 1, flexGrow: 1, children: TEXT_INPUT_FIELDS.map((field, i) => /* @__PURE__ */ jsx28("box", { marginBottom: 1, children: /* @__PURE__ */ jsx28(
254
2357
  TextInput,
255
2358
  {
256
2359
  label: field.label,
@@ -265,7 +2368,7 @@ function TextInputApp() {
265
2368
  description: field.description
266
2369
  }
267
2370
  ) }, field.label)) }),
268
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [
2371
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [
269
2372
  { key: "\u2191\u2193", label: "field" },
270
2373
  { key: "\u2190\u2192", label: "cursor" },
271
2374
  { key: "tab", label: "complete" },
@@ -274,24 +2377,24 @@ function TextInputApp() {
274
2377
  ] });
275
2378
  }
276
2379
  function LinkApp() {
277
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
278
- /* @__PURE__ */ jsx("box", { padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx(LinkDemo, { useKeyboard }) }),
279
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2380
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2381
+ /* @__PURE__ */ jsx28("box", { padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28(LinkDemo, { useKeyboard }) }),
2382
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
280
2383
  ] });
281
2384
  }
282
2385
  function TabBarApp() {
283
2386
  const tabs = ["Files", "Search", "Git", "Debug"];
284
- const [selectedIndex, setSelectedIndex] = useState(0);
2387
+ const [selectedIndex, setSelectedIndex] = useState11(0);
285
2388
  useKeyboard((event) => {
286
2389
  if (event.name === "left") setSelectedIndex((i) => i > 0 ? i - 1 : tabs.length - 1);
287
2390
  if (event.name === "right") setSelectedIndex((i) => i < tabs.length - 1 ? i + 1 : 0);
288
2391
  });
289
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
290
- /* @__PURE__ */ jsxs("box", { flexDirection: "column", gap: 1, padding: 1, flexGrow: 1, children: [
291
- /* @__PURE__ */ jsx(TabBar, { label: "View", options: tabs, selectedIndex }),
292
- /* @__PURE__ */ jsx("text", { style: textStyle({ dim: true }), children: "Use \u2190/\u2192 arrow keys to switch tabs" })
2392
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2393
+ /* @__PURE__ */ jsxs20("box", { flexDirection: "column", gap: 1, padding: 1, flexGrow: 1, children: [
2394
+ /* @__PURE__ */ jsx28(TabBar, { label: "View", options: tabs, selectedIndex }),
2395
+ /* @__PURE__ */ jsx28("text", { style: textStyle({ dim: true }), children: "Use \u2190/\u2192 arrow keys to switch tabs" })
293
2396
  ] }),
294
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "\u2190\u2192", label: "switch tab" }, { key: "q", label: "quit" }] }) })
2397
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "\u2190\u2192", label: "switch tab" }, { key: "q", label: "quit" }] }) })
295
2398
  ] });
296
2399
  }
297
2400
  function StatusBarApp() {
@@ -301,7 +2404,7 @@ function StatusBarApp() {
301
2404
  { key: "b", label: "back" },
302
2405
  { key: "z", label: "reset" }
303
2406
  ];
304
- const [lastKey, setLastKey] = useState(null);
2407
+ const [lastKey, setLastKey] = useState11(null);
305
2408
  useKeyboard((event) => {
306
2409
  if (event.name === "tab") setLastKey("switch focus (Tab)");
307
2410
  else if (event.name === "left") setLastKey("cycle (\u2190)");
@@ -309,66 +2412,66 @@ function StatusBarApp() {
309
2412
  else if (event.name === "b") setLastKey("back (b)");
310
2413
  else if (event.name === "z") setLastKey("reset (z)");
311
2414
  });
312
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", gap: 1, padding: 1, children: [
313
- lastKey ? /* @__PURE__ */ jsxs("text", { children: [
314
- /* @__PURE__ */ jsx("span", { children: "Pressed: " }),
315
- /* @__PURE__ */ jsx("span", { style: textStyle({ bold: true, fg: "cyan" }), children: lastKey })
316
- ] }) : /* @__PURE__ */ jsx("text", { style: textStyle({ dim: true }), children: "Press a key to trigger an action" }),
317
- /* @__PURE__ */ jsx(
2415
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", gap: 1, padding: 1, children: [
2416
+ lastKey ? /* @__PURE__ */ jsxs20("text", { children: [
2417
+ /* @__PURE__ */ jsx28("span", { children: "Pressed: " }),
2418
+ /* @__PURE__ */ jsx28("span", { style: textStyle({ bold: true, fg: "cyan" }), children: lastKey })
2419
+ ] }) : /* @__PURE__ */ jsx28("text", { style: textStyle({ dim: true }), children: "Press a key to trigger an action" }),
2420
+ /* @__PURE__ */ jsx28(
318
2421
  StatusBar,
319
2422
  {
320
2423
  items: [...shortcuts, { key: "q", label: "quit" }],
321
- extra: /* @__PURE__ */ jsx("span", { style: textStyle({ fg: "green" }), children: "\u25CF Ready" })
2424
+ extra: /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "green" }), children: "\u25CF Ready" })
322
2425
  }
323
2426
  )
324
2427
  ] });
325
2428
  }
326
2429
  function ModalApp() {
327
- const [isOpen, setIsOpen] = useState(false);
2430
+ const [isOpen, setIsOpen] = useState11(false);
328
2431
  useKeyboard((event) => {
329
2432
  if (!isOpen && event.name === "m") setIsOpen(true);
330
2433
  if (isOpen && (event.name === "q" || event.name === "escape")) setIsOpen(false);
331
2434
  });
332
2435
  if (isOpen) {
333
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
334
- /* @__PURE__ */ jsx(Modal, { title: "Example Modal", borderColor: "blue", useKeyboard, onClose: () => setIsOpen(false), children: /* @__PURE__ */ jsxs("box", { paddingX: 1, flexDirection: "column", children: [
335
- /* @__PURE__ */ jsx("text", { children: "This is a modal overlay component." }),
336
- /* @__PURE__ */ jsx("text", { children: " " }),
337
- /* @__PURE__ */ jsx("text", { style: textStyle({ dim: true }), children: "It stretches to fill the full terminal height." })
2436
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2437
+ /* @__PURE__ */ jsx28(Modal, { title: "Example Modal", borderColor: "blue", useKeyboard, onClose: () => setIsOpen(false), children: /* @__PURE__ */ jsxs20("box", { paddingX: 1, flexDirection: "column", children: [
2438
+ /* @__PURE__ */ jsx28("text", { children: "This is a modal overlay component." }),
2439
+ /* @__PURE__ */ jsx28("text", { children: " " }),
2440
+ /* @__PURE__ */ jsx28("text", { style: textStyle({ dim: true }), children: "It stretches to fill the full terminal height." })
338
2441
  ] }) }),
339
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "q", label: "close" }, { key: "Esc", label: "quit" }] }) })
2442
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "close" }, { key: "Esc", label: "quit" }] }) })
340
2443
  ] });
341
2444
  }
342
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
343
- /* @__PURE__ */ jsx("box", { flexDirection: "column", flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs("text", { children: [
344
- /* @__PURE__ */ jsx("span", { style: textStyle({ dim: true }), children: "Press " }),
345
- /* @__PURE__ */ jsx("span", { style: textStyle({ inverse: true, bold: true }), children: " m " }),
346
- /* @__PURE__ */ jsx("span", { style: textStyle({ dim: true }), children: " to open modal" })
2445
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2446
+ /* @__PURE__ */ jsx28("box", { flexDirection: "column", flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs20("text", { children: [
2447
+ /* @__PURE__ */ jsx28("span", { style: textStyle({ dim: true }), children: "Press " }),
2448
+ /* @__PURE__ */ jsx28("span", { style: textStyle({ inverse: true, bold: true }), children: " m " }),
2449
+ /* @__PURE__ */ jsx28("span", { style: textStyle({ dim: true }), children: " to open modal" })
347
2450
  ] }) }),
348
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "m", label: "open modal" }, { key: "q", label: "quit" }] }) })
2451
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "m", label: "open modal" }, { key: "q", label: "quit" }] }) })
349
2452
  ] });
350
2453
  }
351
2454
  function PrimitivesApp() {
352
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
353
- /* @__PURE__ */ jsxs("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: [
354
- /* @__PURE__ */ jsx("box", { border: true, borderStyle: "rounded", borderColor: "#5e81ac", title: "Layout", titleAlignment: "center", padding: 1, children: /* @__PURE__ */ jsxs("box", { flexDirection: "row", gap: 2, children: [
355
- /* @__PURE__ */ jsx("box", { border: true, borderStyle: "single", borderColor: "#a3be8c", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx("text", { fg: "#a3be8c", bold: true, children: "Box 1" }) }),
356
- /* @__PURE__ */ jsx("box", { border: true, borderStyle: "single", borderColor: "#ebcb8b", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx("text", { fg: "#ebcb8b", bold: true, children: "Box 2" }) }),
357
- /* @__PURE__ */ jsx("box", { border: true, borderStyle: "single", borderColor: "#b48ead", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx("text", { fg: "#b48ead", bold: true, children: "Box 3" }) })
2455
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2456
+ /* @__PURE__ */ jsxs20("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: [
2457
+ /* @__PURE__ */ jsx28("box", { border: true, borderStyle: "rounded", borderColor: "#5e81ac", title: "Layout", titleAlignment: "center", padding: 1, children: /* @__PURE__ */ jsxs20("box", { flexDirection: "row", gap: 2, children: [
2458
+ /* @__PURE__ */ jsx28("box", { border: true, borderStyle: "single", borderColor: "#a3be8c", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28("text", { fg: "#a3be8c", bold: true, children: "Box 1" }) }),
2459
+ /* @__PURE__ */ jsx28("box", { border: true, borderStyle: "single", borderColor: "#ebcb8b", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28("text", { fg: "#ebcb8b", bold: true, children: "Box 2" }) }),
2460
+ /* @__PURE__ */ jsx28("box", { border: true, borderStyle: "single", borderColor: "#b48ead", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28("text", { fg: "#b48ead", bold: true, children: "Box 3" }) })
358
2461
  ] }) }),
359
- /* @__PURE__ */ jsx("text", { fg: "#d8dee9", dim: true, children: " Nested boxes with borders, colors & flexbox layout" })
2462
+ /* @__PURE__ */ jsx28("text", { fg: "#d8dee9", dim: true, children: " Nested boxes with borders, colors & flexbox layout" })
360
2463
  ] }),
361
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2464
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
362
2465
  ] });
363
2466
  }
364
2467
  function TerminalWindowApp() {
365
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
366
- /* @__PURE__ */ jsxs("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: [
367
- /* @__PURE__ */ jsx("text", { style: textStyle({ fg: "green" }), children: '$ echo "Hello from OpenTUI"' }),
368
- /* @__PURE__ */ jsx("text", { children: "Hello from OpenTUI" }),
369
- /* @__PURE__ */ jsx("text", { style: textStyle({ fg: "green" }), children: "$ _" })
2468
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2469
+ /* @__PURE__ */ jsxs20("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: [
2470
+ /* @__PURE__ */ jsx28("text", { style: textStyle({ fg: "green" }), children: '$ echo "Hello from OpenTUI"' }),
2471
+ /* @__PURE__ */ jsx28("text", { children: "Hello from OpenTUI" }),
2472
+ /* @__PURE__ */ jsx28("text", { style: textStyle({ fg: "green" }), children: "$ _" })
370
2473
  ] }),
371
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2474
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
372
2475
  ] });
373
2476
  }
374
2477
  var TIMELINE_STEPS = [
@@ -379,10 +2482,10 @@ var TIMELINE_STEPS = [
379
2482
  { tool: "Edit", label: "Fixing test \u2014 routes.test.ts", status: "done", delay: 1500 }
380
2483
  ];
381
2484
  function TimelineApp() {
382
- const [expanded, setExpanded] = useState(true);
383
- const [phase, setPhase] = useState("running");
384
- const [stepIndex, setStepIndex] = useState(0);
385
- const timerRef = useRef(null);
2485
+ const [expanded, setExpanded] = useState11(true);
2486
+ const [phase, setPhase] = useState11("running");
2487
+ const [stepIndex, setStepIndex] = useState11(0);
2488
+ const timerRef = useRef8(null);
386
2489
  useKeyboard((event) => {
387
2490
  if (event.name === "E" && event.ctrl && event.shift) setExpanded((v) => !v);
388
2491
  if (event.name === "r") timelineRestart();
@@ -392,7 +2495,7 @@ function TimelineApp() {
392
2495
  setPhase("running");
393
2496
  setStepIndex(0);
394
2497
  }
395
- useEffect(() => {
2498
+ useEffect5(() => {
396
2499
  if (phase !== "running") return;
397
2500
  if (stepIndex < TIMELINE_STEPS.length) {
398
2501
  const delay = TIMELINE_STEPS[stepIndex].delay;
@@ -404,7 +2507,7 @@ function TimelineApp() {
404
2507
  if (timerRef.current) clearTimeout(timerRef.current);
405
2508
  };
406
2509
  }, [phase, stepIndex]);
407
- useEffect(() => {
2510
+ useEffect5(() => {
408
2511
  if (phase === "done") {
409
2512
  timerRef.current = setTimeout(() => timelineRestart(), 3e3);
410
2513
  }
@@ -419,8 +2522,8 @@ function TimelineApp() {
419
2522
  });
420
2523
  const elapsedMs = TIMELINE_STEPS.slice(0, stepIndex).reduce((sum, s) => sum + s.delay, 0);
421
2524
  const totalMs = TIMELINE_STEPS.reduce((sum, s) => sum + s.delay, 0);
422
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
423
- /* @__PURE__ */ jsx("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx(
2525
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2526
+ /* @__PURE__ */ jsx28("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28(
424
2527
  Timeline,
425
2528
  {
426
2529
  steps,
@@ -428,7 +2531,7 @@ function TimelineApp() {
428
2531
  collapsed: !expanded
429
2532
  }
430
2533
  ) }),
431
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [
2534
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [
432
2535
  { key: "ctrl+shift+e", label: "toggle" },
433
2536
  { key: "r", label: "restart" },
434
2537
  { key: "q", label: "quit" }
@@ -445,11 +2548,11 @@ var BUBBLE_STEPS = [
445
2548
  var BUBBLE_RESPONSE = "I've refactored the auth module. The changes include extracting the token validation into a shared helper, consolidating the middleware chain, and updating the test suite to match.";
446
2549
  var BUBBLE_TOTAL_MS = BUBBLE_STEPS.reduce((sum, s) => sum + s.delay, 0);
447
2550
  function MessageApp() {
448
- const [expanded, setExpanded] = useState(true);
449
- const [phase, setPhase] = useState("idle");
450
- const [stepIndex, setStepIndex] = useState(0);
451
- const [streamedText, setStreamedText] = useState("");
452
- const timerRef = useRef(null);
2551
+ const [expanded, setExpanded] = useState11(true);
2552
+ const [phase, setPhase] = useState11("idle");
2553
+ const [stepIndex, setStepIndex] = useState11(0);
2554
+ const [streamedText, setStreamedText] = useState11("");
2555
+ const timerRef = useRef8(null);
453
2556
  useKeyboard((event) => {
454
2557
  if (event.name === "E" && event.ctrl && event.shift) setExpanded((v) => !v);
455
2558
  if (event.name === "r") bubbleRestart();
@@ -460,7 +2563,7 @@ function MessageApp() {
460
2563
  setStepIndex(0);
461
2564
  setStreamedText("");
462
2565
  }
463
- useEffect(() => {
2566
+ useEffect5(() => {
464
2567
  if (phase === "idle") {
465
2568
  timerRef.current = setTimeout(() => setPhase("thinking"), 800);
466
2569
  }
@@ -468,7 +2571,7 @@ function MessageApp() {
468
2571
  if (timerRef.current) clearTimeout(timerRef.current);
469
2572
  };
470
2573
  }, [phase]);
471
- useEffect(() => {
2574
+ useEffect5(() => {
472
2575
  if (phase !== "thinking") return;
473
2576
  if (stepIndex < BUBBLE_STEPS.length) {
474
2577
  const delay = BUBBLE_STEPS[stepIndex].delay;
@@ -480,7 +2583,7 @@ function MessageApp() {
480
2583
  if (timerRef.current) clearTimeout(timerRef.current);
481
2584
  };
482
2585
  }, [phase, stepIndex]);
483
- useEffect(() => {
2586
+ useEffect5(() => {
484
2587
  if (phase !== "streaming") return;
485
2588
  if (streamedText.length < BUBBLE_RESPONSE.length) {
486
2589
  timerRef.current = setTimeout(() => {
@@ -493,7 +2596,7 @@ function MessageApp() {
493
2596
  if (timerRef.current) clearTimeout(timerRef.current);
494
2597
  };
495
2598
  }, [phase, streamedText]);
496
- useEffect(() => {
2599
+ useEffect5(() => {
497
2600
  if (phase === "done") {
498
2601
  timerRef.current = setTimeout(() => bubbleRestart(), 3e3);
499
2602
  }
@@ -513,20 +2616,20 @@ function MessageApp() {
513
2616
  const elapsedMs = BUBBLE_STEPS.slice(0, stepIndex).reduce((sum, s) => sum + s.delay, 0);
514
2617
  const reasoningDuration = isThinking ? `${(elapsedMs / 1e3).toFixed(1)}s` : `${(BUBBLE_TOTAL_MS / 1e3).toFixed(1)}s`;
515
2618
  const reasoningSteps = isThinking ? steps : BUBBLE_STEPS.map((s) => ({ ...s, status: "done" }));
516
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
517
- /* @__PURE__ */ jsxs("box", { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1, children: [
518
- /* @__PURE__ */ jsx(Message, { role: "user", children: /* @__PURE__ */ jsx(Message.Content, { children: /* @__PURE__ */ jsx(Message.Text, { children: "Can you refactor the auth module?" }) }) }),
519
- showAssistant && /* @__PURE__ */ jsxs(Message, { role: "assistant", isStreaming, children: [
520
- /* @__PURE__ */ jsx(Message.Reasoning, { part: {
2619
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2620
+ /* @__PURE__ */ jsxs20("box", { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1, children: [
2621
+ /* @__PURE__ */ jsx28(Message, { role: "user", children: /* @__PURE__ */ jsx28(Message.Content, { children: /* @__PURE__ */ jsx28(Message.Text, { children: "Can you refactor the auth module?" }) }) }),
2622
+ showAssistant && /* @__PURE__ */ jsxs20(Message, { role: "assistant", isStreaming, children: [
2623
+ /* @__PURE__ */ jsx28(Message.Reasoning, { part: {
521
2624
  type: "reasoning",
522
2625
  duration: reasoningDuration,
523
2626
  collapsed: !expanded,
524
2627
  steps: reasoningSteps
525
2628
  } }),
526
- (isStreaming || isDone) && /* @__PURE__ */ jsx(Message.Content, { children: /* @__PURE__ */ jsx(Message.Text, { isLast: true, children: isDone ? BUBBLE_RESPONSE : streamedText }) })
2629
+ (isStreaming || isDone) && /* @__PURE__ */ jsx28(Message.Content, { children: /* @__PURE__ */ jsx28(Message.Text, { isLast: true, children: isDone ? BUBBLE_RESPONSE : streamedText }) })
527
2630
  ] })
528
2631
  ] }),
529
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "ctrl+shift+e", label: "toggle timeline" }, { key: "r", label: "restart" }, { key: "q", label: "quit" }] }) })
2632
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "ctrl+shift+e", label: "toggle timeline" }, { key: "r", label: "restart" }, { key: "q", label: "quit" }] }) })
530
2633
  ] });
531
2634
  }
532
2635
  var initialChatMessages = [
@@ -537,12 +2640,12 @@ var initialChatMessages = [
537
2640
  ];
538
2641
  var chatNextId = 5;
539
2642
  function ChatApp() {
540
- const [messages, setMessages] = useState(initialChatMessages);
541
- const [isLoading, setIsLoading] = useState(false);
542
- const [streamingText, setStreamingText] = useState("");
543
- const [activeToolCalls, setActiveToolCalls] = useState([]);
544
- const intervalRef = useRef(null);
545
- const handleSend = useCallback((text) => {
2643
+ const [messages, setMessages] = useState11(initialChatMessages);
2644
+ const [isLoading, setIsLoading] = useState11(false);
2645
+ const [streamingText, setStreamingText] = useState11("");
2646
+ const [activeToolCalls, setActiveToolCalls] = useState11([]);
2647
+ const intervalRef = useRef8(null);
2648
+ const handleSend = useCallback3((text) => {
546
2649
  const userMsg = { id: String(chatNextId++), role: "user", content: text };
547
2650
  setMessages((prev) => [...prev, userMsg]);
548
2651
  setIsLoading(true);
@@ -573,14 +2676,14 @@ function ChatApp() {
573
2676
  }, 50);
574
2677
  }, 1400);
575
2678
  }, []);
576
- const handleCancel = useCallback(() => {
2679
+ const handleCancel = useCallback3(() => {
577
2680
  if (intervalRef.current) clearInterval(intervalRef.current);
578
2681
  setIsLoading(false);
579
2682
  setStreamingText("");
580
2683
  setActiveToolCalls([]);
581
2684
  }, []);
582
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", flexGrow: 1, children: [
583
- /* @__PURE__ */ jsx(
2685
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2686
+ /* @__PURE__ */ jsx28(
584
2687
  ChatPanel,
585
2688
  {
586
2689
  messages,
@@ -593,32 +2696,32 @@ function ChatApp() {
593
2696
  useKeyboard
594
2697
  }
595
2698
  ),
596
- /* @__PURE__ */ jsx("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2699
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
597
2700
  ] });
598
2701
  }
599
2702
  var demos = [
600
- { name: "gradient", app: () => /* @__PURE__ */ jsx(GradientApp, {}) },
601
- { name: "ascii", app: () => /* @__PURE__ */ jsx(AsciiApp, {}) },
602
- { name: "table", app: () => /* @__PURE__ */ jsx(TableApp, {}) },
603
- { name: "spinner", app: () => /* @__PURE__ */ jsx(SpinnerApp, {}) },
604
- { name: "select-input", app: () => /* @__PURE__ */ jsx(SelectInputApp, {}) },
605
- { name: "multi-select", app: () => /* @__PURE__ */ jsx(MultiSelectApp, {}) },
606
- { name: "prompt-input", app: () => /* @__PURE__ */ jsx(PromptInputApp, {}) },
607
- { name: "text-input", app: () => /* @__PURE__ */ jsx(TextInputApp, {}) },
608
- { name: "link", app: () => /* @__PURE__ */ jsx(LinkApp, {}) },
609
- { name: "tabs", app: () => /* @__PURE__ */ jsx(TabBarApp, {}) },
610
- { name: "status-bar", app: () => /* @__PURE__ */ jsx(StatusBarApp, {}) },
611
- { name: "modal", app: () => /* @__PURE__ */ jsx(ModalApp, {}) },
612
- { name: "primitives", app: () => /* @__PURE__ */ jsx(PrimitivesApp, {}) },
613
- { name: "chat", app: () => /* @__PURE__ */ jsx(ChatApp, {}) },
614
- { name: "timeline", app: () => /* @__PURE__ */ jsx(TimelineApp, {}) },
615
- { name: "message", app: () => /* @__PURE__ */ jsx(MessageApp, {}) },
616
- { name: "terminal-window", app: () => /* @__PURE__ */ jsx(TerminalWindowApp, {}) },
617
- { name: "landing", app: () => /* @__PURE__ */ jsx(LandingApp, { useKeyboard }) }
2703
+ { name: "gradient", app: () => /* @__PURE__ */ jsx28(GradientApp, {}) },
2704
+ { name: "ascii", app: () => /* @__PURE__ */ jsx28(AsciiApp, {}) },
2705
+ { name: "table", app: () => /* @__PURE__ */ jsx28(TableApp, {}) },
2706
+ { name: "spinner", app: () => /* @__PURE__ */ jsx28(SpinnerApp, {}) },
2707
+ { name: "select-input", app: () => /* @__PURE__ */ jsx28(SelectInputApp, {}) },
2708
+ { name: "multi-select", app: () => /* @__PURE__ */ jsx28(MultiSelectApp, {}) },
2709
+ { name: "prompt-input", app: () => /* @__PURE__ */ jsx28(PromptInputApp, {}) },
2710
+ { name: "text-input", app: () => /* @__PURE__ */ jsx28(TextInputApp, {}) },
2711
+ { name: "link", app: () => /* @__PURE__ */ jsx28(LinkApp, {}) },
2712
+ { name: "tabs", app: () => /* @__PURE__ */ jsx28(TabBarApp, {}) },
2713
+ { name: "status-bar", app: () => /* @__PURE__ */ jsx28(StatusBarApp, {}) },
2714
+ { name: "modal", app: () => /* @__PURE__ */ jsx28(ModalApp, {}) },
2715
+ { name: "primitives", app: () => /* @__PURE__ */ jsx28(PrimitivesApp, {}) },
2716
+ { name: "chat", app: () => /* @__PURE__ */ jsx28(ChatApp, {}) },
2717
+ { name: "timeline", app: () => /* @__PURE__ */ jsx28(TimelineApp, {}) },
2718
+ { name: "message", app: () => /* @__PURE__ */ jsx28(MessageApp, {}) },
2719
+ { name: "terminal-window", app: () => /* @__PURE__ */ jsx28(TerminalWindowApp, {}) },
2720
+ { name: "landing", app: () => /* @__PURE__ */ jsx28(LandingApp, { useKeyboard }) }
618
2721
  ];
619
2722
 
620
2723
  // src/run.tsx
621
- import { jsx as jsx2 } from "react/jsx-runtime";
2724
+ import { jsx as jsx29 } from "react/jsx-runtime";
622
2725
  var _renderer;
623
2726
  function DemoShell({ children }) {
624
2727
  useKeyboard2((event) => {
@@ -626,7 +2729,7 @@ function DemoShell({ children }) {
626
2729
  _renderer.destroy();
627
2730
  }
628
2731
  });
629
- return /* @__PURE__ */ jsx2("box", { flexDirection: "column", flexGrow: 1, children });
2732
+ return /* @__PURE__ */ jsx29("box", { flexDirection: "column", flexGrow: 1, children });
630
2733
  }
631
2734
  async function runDemo(name) {
632
2735
  const demo = demos.find((d) => d.name === name);
@@ -636,7 +2739,7 @@ async function runDemo(name) {
636
2739
  process.exit(1);
637
2740
  }
638
2741
  _renderer = await createCliRenderer({ exitOnCtrlC: true });
639
- createRoot(_renderer).render(/* @__PURE__ */ jsx2(DemoShell, { children: demo.app() }));
2742
+ createRoot(_renderer).render(/* @__PURE__ */ jsx29(DemoShell, { children: demo.app() }));
640
2743
  }
641
2744
  export {
642
2745
  demos,