@gridland/demo 0.2.50 → 0.2.51

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,8 +1,11 @@
1
1
  // src/run.tsx
2
- import { createCliRenderer, createRoot, useKeyboard as useKeyboard2 } from "@gridland/bun";
2
+ import { createCliRenderer, createRoot, useKeyboard as useKeyboard19 } from "@gridland/bun";
3
3
 
4
- // ../ui/scripts/demo-apps.tsx
5
- import { useState as useState11, useCallback as useCallback4, useRef as useRef8, useEffect as useEffect5 } from "react";
4
+ // demos/index.tsx
5
+ import { useKeyboard as useKeyboard18 } from "@gridland/utils";
6
+
7
+ // demos/gradient.tsx
8
+ import { useState as useState8 } from "react";
6
9
  import { useKeyboard } from "@gridland/utils";
7
10
 
8
11
  // ../ui/components/theme/themes.ts
@@ -33,19 +36,6 @@ import { jsx as jsx2 } from "react/jsx-runtime";
33
36
  var UNDERLINE = 1 << 3;
34
37
  var UNDERLINE_DASHED = 1 << 4;
35
38
  var UNDERLINE_DOTTED = 1 << 6;
36
- function Link({ children, url, underline = "solid", color }) {
37
- const theme = useTheme();
38
- const resolvedColor = color ?? theme.accent;
39
- let attributes = 0;
40
- if (underline === "solid") {
41
- attributes = UNDERLINE;
42
- } else if (underline === "dashed") {
43
- attributes = UNDERLINE | UNDERLINE_DASHED;
44
- } else if (underline === "dotted") {
45
- attributes = UNDERLINE | UNDERLINE_DOTTED;
46
- }
47
- return /* @__PURE__ */ jsx2("text", { children: /* @__PURE__ */ jsx2("a", { href: url, style: { attributes, fg: resolvedColor }, children }) });
48
- }
49
39
 
50
40
  // ../ui/components/link/link-demo.tsx
51
41
  import { useState } from "react";
@@ -72,7 +62,7 @@ function textStyle(opts) {
72
62
 
73
63
  // ../ui/components/status-bar/status-bar.tsx
74
64
  import { jsx as jsx3 } from "react/jsx-runtime";
75
- function StatusBar({ items, extra }) {
65
+ function StatusBar({ items: items3, extra }) {
76
66
  const theme = useTheme();
77
67
  const parts = [];
78
68
  if (extra !== void 0) {
@@ -83,7 +73,7 @@ function StatusBar({ items, extra }) {
83
73
  /* @__PURE__ */ jsx3("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: " \u2502 " }, "pipe")
84
74
  );
85
75
  }
86
- items.forEach((item, i) => {
76
+ items3.forEach((item, i) => {
87
77
  if (i > 0) {
88
78
  parts.push(/* @__PURE__ */ jsx3("span", { children: " " }, `gap-${i}`));
89
79
  }
@@ -111,35 +101,6 @@ function useKeyboardContext(propOverride) {
111
101
 
112
102
  // ../ui/components/link/link-demo.tsx
113
103
  import { jsx as jsx5, jsxs } from "react/jsx-runtime";
114
- var MODES = ["solid", "dashed", "dotted", "none"];
115
- function LinkDemo({
116
- url = "https://opentui.com",
117
- label = "Visit opentui.com",
118
- useKeyboard: useKeyboardProp
119
- }) {
120
- const useKeyboard3 = useKeyboardContext(useKeyboardProp);
121
- const [modeIndex, setModeIndex] = useState(0);
122
- const mode = MODES[modeIndex];
123
- useKeyboard3?.((event) => {
124
- if (event.name === "right") {
125
- setModeIndex((i) => (i + 1) % MODES.length);
126
- } else if (event.name === "left") {
127
- setModeIndex((i) => (i - 1 + MODES.length) % MODES.length);
128
- }
129
- });
130
- return /* @__PURE__ */ jsxs("box", { flexDirection: "column", gap: 1, children: [
131
- /* @__PURE__ */ jsx5(Link, { url, underline: mode, children: label }),
132
- /* @__PURE__ */ jsx5(
133
- StatusBar,
134
- {
135
- extra: /* @__PURE__ */ jsx5("span", { style: textStyle({ bold: true }), children: mode.padEnd(6) }),
136
- items: [
137
- { key: "\u2190\u2192", label: "underline style" }
138
- ]
139
- }
140
- )
141
- ] });
142
- }
143
104
 
144
105
  // ../ui/components/ascii/ascii.tsx
145
106
  import { jsx as jsx6 } from "react/jsx-runtime";
@@ -148,25 +109,43 @@ import { jsx as jsx6 } from "react/jsx-runtime";
148
109
  import { useEffect, useState as useState2 } from "react";
149
110
  import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
150
111
  var VARIANTS = {
151
- dots: { frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"], interval: 83 },
112
+ dots: { frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"], interval: 80 },
152
113
  pulse: { frames: ["\xB7", "\u2219", "\u25CF", "\u2219", "\xB7", "\xB7", "\xB7"], interval: 180 },
153
114
  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 },
154
115
  bloom: { frames: ["\xB7", "\u2726", "\u2727", "\u2739", "\u273A", "\u274B", "\u2738", "\u2735", "\u2738", "\u274B", "\u273A", "\u2739", "\u2727", "\u2726", "\xB7", "\xB7"], interval: 100 },
155
116
  ellipsis: { frames: [" ", ". ", ".. ", "..."], interval: 333 }
156
117
  };
157
118
  var VARIANT_NAMES = Object.keys(VARIANTS);
158
- function Spinner({ variant = "dots", text, color }) {
119
+ var STATUS_SYMBOLS = {
120
+ success: "\u2714",
121
+ error: "\u2716",
122
+ warning: "\u26A0",
123
+ info: "\u2139"
124
+ };
125
+ function Spinner({ variant = "dots", text, color, status = "spinning" }) {
159
126
  const theme = useTheme();
160
- const resolvedColor = color ?? theme.accent;
161
127
  const { frames, interval } = VARIANTS[variant];
162
128
  const [frame, setFrame] = useState2(0);
163
129
  useEffect(() => {
130
+ if (status !== "spinning") return;
164
131
  setFrame(0);
165
132
  const timer = setInterval(() => {
166
133
  setFrame((prev) => (prev + 1) % frames.length);
167
134
  }, interval);
168
135
  return () => clearInterval(timer);
169
- }, [variant]);
136
+ }, [variant, status]);
137
+ if (status !== "spinning") {
138
+ const symbol = STATUS_SYMBOLS[status];
139
+ const statusColor = status === "success" ? theme.success : status === "error" ? theme.error : status === "warning" ? theme.warning : theme.accent;
140
+ return /* @__PURE__ */ jsxs2("text", { children: [
141
+ /* @__PURE__ */ jsx7("span", { style: { fg: statusColor }, children: symbol }),
142
+ text ? /* @__PURE__ */ jsxs2("span", { style: { fg: theme.foreground }, children: [
143
+ " ",
144
+ text
145
+ ] }) : null
146
+ ] });
147
+ }
148
+ const resolvedColor = color ?? theme.accent;
170
149
  return /* @__PURE__ */ jsxs2("text", { children: [
171
150
  /* @__PURE__ */ jsx7("span", { style: { fg: resolvedColor }, children: frames[frame] }),
172
151
  text ? /* @__PURE__ */ jsxs2("span", { style: { fg: theme.foreground }, children: [
@@ -181,9 +160,9 @@ import { useState as useState3 } from "react";
181
160
  import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
182
161
  function SpinnerPicker({ useKeyboard: useKeyboardProp }) {
183
162
  const theme = useTheme();
184
- const useKeyboard3 = useKeyboardContext(useKeyboardProp);
163
+ const useKeyboard20 = useKeyboardContext(useKeyboardProp);
185
164
  const [selected, setSelected] = useState3(0);
186
- useKeyboard3?.((event) => {
165
+ useKeyboard20?.((event) => {
187
166
  if (event.name === "left") {
188
167
  setSelected((s) => s > 0 ? s - 1 : VARIANT_NAMES.length - 1);
189
168
  } else if (event.name === "right") {
@@ -296,7 +275,7 @@ var RADIO = "\u25CB";
296
275
  var CURSOR = "\u25B8";
297
276
  var SEPARATOR = "\u2500";
298
277
  function SelectInput({
299
- items = [],
278
+ items: items3 = [],
300
279
  defaultValue,
301
280
  value: controlledValue,
302
281
  onChange,
@@ -312,14 +291,14 @@ function SelectInput({
312
291
  useKeyboard: useKeyboardProp
313
292
  }) {
314
293
  const theme = useTheme();
315
- const useKeyboard3 = useKeyboardContext(useKeyboardProp);
294
+ const useKeyboard20 = useKeyboardContext(useKeyboardProp);
316
295
  const resolvedHighlight = highlightColor ?? theme.primary;
317
296
  const isControlled = controlledValue !== void 0;
318
297
  const controlledRef = useRef2(isControlled);
319
298
  if (controlledRef.current !== isControlled) {
320
299
  console.warn("SelectInput: switching between controlled and uncontrolled is not supported.");
321
300
  }
322
- const initialIndex = items.findIndex((i) => i.value === (isControlled ? controlledValue : defaultValue));
301
+ const initialIndex = items3.findIndex((i) => i.value === (isControlled ? controlledValue : defaultValue));
323
302
  const [state, dispatch] = useReducer(reducer, {
324
303
  cursor: Math.max(0, initialIndex),
325
304
  submitted: false
@@ -329,7 +308,7 @@ function SelectInput({
329
308
  const selectable = [];
330
309
  let index = 0;
331
310
  const grouped = /* @__PURE__ */ new Map();
332
- for (const item of items) {
311
+ for (const item of items3) {
333
312
  const group = item.group ?? "";
334
313
  const list = grouped.get(group) ?? [];
335
314
  list.push(item);
@@ -349,13 +328,13 @@ function SelectInput({
349
328
  }
350
329
  }
351
330
  return { flatRows: rows, selectableItems: selectable };
352
- }, [items]);
331
+ }, [items3]);
353
332
  const visibleCount = limit ?? VISIBLE;
354
333
  const cursorRowIndex = flatRows.findIndex((r) => r.type === "item" && r.index === state.cursor);
355
334
  const scrollOffset = Math.max(0, Math.min(cursorRowIndex - Math.floor(visibleCount / 2), flatRows.length - visibleCount));
356
335
  const visibleRows = flatRows.slice(scrollOffset, scrollOffset + visibleCount);
357
336
  const diamondColor = invalid ? theme.error : disabled ? theme.muted : theme.accent;
358
- useKeyboard3?.((event) => {
337
+ useKeyboard20?.((event) => {
359
338
  if (state.submitted || disabled) return;
360
339
  if (event.name === "up" || event.name === "k") {
361
340
  const direction = -1;
@@ -384,7 +363,7 @@ function SelectInput({
384
363
  }
385
364
  });
386
365
  if (state.submitted) {
387
- const selectedItem = isControlled ? items.find((i) => i.value === controlledValue) : selectableItems[state.cursor]?.item;
366
+ const selectedItem = isControlled ? items3.find((i) => i.value === controlledValue) : selectableItems[state.cursor]?.item;
388
367
  return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
389
368
  /* @__PURE__ */ jsxs5("text", { children: [
390
369
  /* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.success }), children: "\u25C6 " }),
@@ -498,7 +477,7 @@ var UNCHECKED = "\u25CB";
498
477
  var CURSOR2 = "\u25B8";
499
478
  var SEPARATOR2 = "\u2500";
500
479
  function MultiSelect({
501
- items = [],
480
+ items: items3 = [],
502
481
  defaultSelected = [],
503
482
  selected: controlledSelected,
504
483
  onChange,
@@ -519,7 +498,7 @@ function MultiSelect({
519
498
  useKeyboard: useKeyboardProp
520
499
  }) {
521
500
  const theme = useTheme();
522
- const useKeyboard3 = useKeyboardContext(useKeyboardProp);
501
+ const useKeyboard20 = useKeyboardContext(useKeyboardProp);
523
502
  const resolvedHighlight = highlightColor ?? theme.primary;
524
503
  const resolvedCheckbox = checkboxColor ?? theme.accent;
525
504
  const isControlled = controlledSelected !== void 0;
@@ -539,7 +518,7 @@ function MultiSelect({
539
518
  const selectable = [];
540
519
  let index = 0;
541
520
  const grouped = /* @__PURE__ */ new Map();
542
- for (const item of items) {
521
+ for (const item of items3) {
543
522
  const group = item.group ?? "";
544
523
  const list = grouped.get(group) ?? [];
545
524
  list.push(item);
@@ -559,7 +538,7 @@ function MultiSelect({
559
538
  }
560
539
  }
561
540
  return { flatRows: rows, selectableItems: selectable };
562
- }, [items]);
541
+ }, [items3]);
563
542
  const hasSubmitRow = allowEmpty || currentSelected.size > 0;
564
543
  const totalPositions = selectableItems.length + (hasSubmitRow ? 1 : 0);
565
544
  const isOnSubmit = hasSubmitRow && state.cursor === selectableItems.length;
@@ -575,7 +554,7 @@ function MultiSelect({
575
554
  }
576
555
  };
577
556
  const diamondColor = invalid ? theme.error : disabled ? theme.muted : theme.accent;
578
- useKeyboard3?.((event) => {
557
+ useKeyboard20?.((event) => {
579
558
  if (state.submitted || disabled) return;
580
559
  const move = (direction) => {
581
560
  let next = cursorRef.current + direction;
@@ -605,14 +584,14 @@ function MultiSelect({
605
584
  }
606
585
  }
607
586
  } else if (event.name === "a" && enableSelectAll) {
608
- const enabledValues = items.filter((i) => !i.disabled).map((i) => i.value);
587
+ const enabledValues = items3.filter((i) => !i.disabled).map((i) => i.value);
609
588
  setSelected(maxCount !== void 0 ? enabledValues.slice(0, maxCount) : enabledValues);
610
589
  } else if (event.name === "x" && enableClear) {
611
590
  setSelected([]);
612
591
  }
613
592
  });
614
593
  if (state.submitted) {
615
- const selectedItems = items.filter((i) => currentSelected.has(i.value));
594
+ const selectedItems = items3.filter((i) => currentSelected.has(i.value));
616
595
  return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
617
596
  /* @__PURE__ */ jsxs6("text", { children: [
618
597
  /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: theme.success }), children: "\u25C6 " }),
@@ -722,91 +701,145 @@ function MultiSelect({
722
701
  }
723
702
 
724
703
  // ../ui/components/table/table.tsx
725
- import { Fragment } from "react";
704
+ import { createContext as createContext3, useContext as useContext3, Children, isValidElement, Fragment } from "react";
726
705
  import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
727
- function getColumns(data, columnsProp) {
706
+ var TableContext = createContext3(null);
707
+ function useTableContext() {
708
+ const ctx = useContext3(TableContext);
709
+ if (!ctx) throw new Error("Table compound components must be used within <TableRoot>");
710
+ return ctx;
711
+ }
712
+ function getColumns(data2, columnsProp) {
728
713
  if (columnsProp) return columnsProp;
729
714
  const keys = /* @__PURE__ */ new Set();
730
- for (const row of data) {
715
+ for (const row of data2) {
731
716
  for (const key in row) keys.add(key);
732
717
  }
733
718
  return Array.from(keys);
734
719
  }
735
- function calculateColumnWidths(columns, data, padding) {
736
- return columns.map((field) => {
737
- const headerWidth = String(field).length;
738
- const maxDataWidth = data.reduce((max, row) => {
739
- const val = row[field];
740
- return Math.max(max, val == null ? 0 : String(val).length);
741
- }, 0);
742
- return { field, width: Math.max(headerWidth, maxDataWidth) + padding * 2 };
743
- });
744
- }
745
720
  function padCell(value, width, padding) {
746
721
  const rightPad = width - value.length - padding;
747
722
  return " ".repeat(padding) + value + " ".repeat(Math.max(0, rightPad));
748
723
  }
724
+ function extractCellText(node) {
725
+ if (node == null) return "";
726
+ if (typeof node === "string") return node;
727
+ if (typeof node === "number" || typeof node === "boolean") return String(node);
728
+ if (Array.isArray(node)) return node.map(extractCellText).join("");
729
+ return "";
730
+ }
731
+ function collectColumnWidths(children, padding) {
732
+ const columnMaxWidths = [];
733
+ Children.forEach(children, (section) => {
734
+ if (!isValidElement(section)) return;
735
+ if (section.type === TableCaption) return;
736
+ Children.forEach(section.props.children, (row) => {
737
+ if (!isValidElement(row)) return;
738
+ let colIdx = 0;
739
+ Children.forEach(row.props.children, (cell) => {
740
+ if (!isValidElement(cell)) return;
741
+ const text = extractCellText(cell.props.children);
742
+ const width = text.length + padding * 2;
743
+ if (colIdx >= columnMaxWidths.length) {
744
+ columnMaxWidths.push(width);
745
+ } else {
746
+ columnMaxWidths[colIdx] = Math.max(columnMaxWidths[colIdx], width);
747
+ }
748
+ colIdx++;
749
+ });
750
+ });
751
+ });
752
+ return columnMaxWidths;
753
+ }
754
+ function getTotalWidth(columnWidths) {
755
+ if (columnWidths.length === 0) return 0;
756
+ return columnWidths.reduce((sum, w) => sum + w, 0) + (columnWidths.length - 1);
757
+ }
758
+ function TableRoot({ children, padding = 1, headerColor, borderColor }) {
759
+ const theme = useTheme();
760
+ const resolvedHeaderColor = headerColor ?? theme.foreground;
761
+ const resolvedBorderColor = borderColor ?? theme.muted;
762
+ const columnWidths = collectColumnWidths(children, padding);
763
+ return /* @__PURE__ */ jsx12(
764
+ TableContext.Provider,
765
+ {
766
+ value: {
767
+ columnWidths,
768
+ padding,
769
+ headerColor: resolvedHeaderColor,
770
+ borderColor: resolvedBorderColor,
771
+ foregroundColor: theme.foreground
772
+ },
773
+ children: /* @__PURE__ */ jsx12("box", { children })
774
+ }
775
+ );
776
+ }
777
+ function TableHeader({ children }) {
778
+ const ctx = useTableContext();
779
+ const totalWidth = getTotalWidth(ctx.columnWidths);
780
+ return /* @__PURE__ */ jsxs7("box", { children: [
781
+ children,
782
+ /* @__PURE__ */ jsx12("text", { children: /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: ctx.borderColor }), children: "\u2500".repeat(totalWidth) }) })
783
+ ] });
784
+ }
785
+ function TableBody({ children }) {
786
+ const ctx = useTableContext();
787
+ const totalWidth = getTotalWidth(ctx.columnWidths);
788
+ const rows = Children.toArray(children);
789
+ return /* @__PURE__ */ jsx12("box", { children: rows.map((row, index) => /* @__PURE__ */ jsxs7(Fragment, { children: [
790
+ row,
791
+ index < rows.length - 1 && /* @__PURE__ */ jsx12("text", { children: /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: ctx.borderColor, dim: true }), children: "\u2500".repeat(totalWidth) }) })
792
+ ] }, index)) });
793
+ }
794
+ function TableRow({ children }) {
795
+ const ctx = useTableContext();
796
+ const parts = [];
797
+ let colIdx = 0;
798
+ Children.forEach(children, (child) => {
799
+ if (!isValidElement(child)) return;
800
+ const text = extractCellText(child.props.children);
801
+ const width = ctx.columnWidths[colIdx] ?? text.length + ctx.padding * 2;
802
+ const padded = padCell(text, width, ctx.padding);
803
+ const isHead = child.type === TableHead;
804
+ if (colIdx > 0) {
805
+ parts.push(
806
+ /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: ctx.borderColor, dim: true }), children: " " }, `sep-${colIdx}`)
807
+ );
808
+ }
809
+ if (isHead) {
810
+ parts.push(
811
+ /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: ctx.headerColor }), children: padded }, `cell-${colIdx}`)
812
+ );
813
+ } else {
814
+ parts.push(
815
+ /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: ctx.foregroundColor, dim: true }), children: padded }, `cell-${colIdx}`)
816
+ );
817
+ }
818
+ colIdx++;
819
+ });
820
+ return /* @__PURE__ */ jsx12("text", { children: parts });
821
+ }
822
+ function TableHead(_props) {
823
+ return null;
824
+ }
825
+ function TableCell(_props) {
826
+ return null;
827
+ }
828
+ function TableCaption({ children }) {
829
+ const ctx = useTableContext();
830
+ return /* @__PURE__ */ jsx12("text", { children: /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: ctx.borderColor, dim: true }), children: extractCellText(children) }) });
831
+ }
749
832
  function Table({
750
- data,
833
+ data: data2,
751
834
  columns: columnsProp,
752
835
  padding = 1,
753
836
  headerColor,
754
837
  borderColor
755
838
  }) {
756
- const theme = useTheme();
757
- const resolvedHeaderColor = headerColor ?? theme.primary;
758
- const resolvedBorderColor = borderColor ?? theme.border;
759
- const columns = getColumns(data, columnsProp);
760
- const colInfo = calculateColumnWidths(columns, data, padding);
761
- const borderLine = (left, mid, right) => {
762
- const inner = colInfo.map((c) => "\u2500".repeat(c.width)).join(mid);
763
- return /* @__PURE__ */ jsx12("text", { children: /* @__PURE__ */ jsxs7("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: [
764
- left,
765
- inner,
766
- right
767
- ] }) });
768
- };
769
- const contentRow = (rowData, isHeader) => {
770
- const parts = [];
771
- parts.push(
772
- /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: "\u2502" }, "left-border")
773
- );
774
- colInfo.forEach((col, i) => {
775
- const val = rowData[col.field];
776
- const str = val == null ? "" : String(val);
777
- const padded = padCell(str, col.width, padding);
778
- if (isHeader) {
779
- parts.push(
780
- /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedHeaderColor, bold: true }), children: padded }, `cell-${i}`)
781
- );
782
- } else {
783
- parts.push(
784
- /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: theme.foreground }), children: padded }, `cell-${i}`)
785
- );
786
- }
787
- if (i < colInfo.length - 1) {
788
- parts.push(
789
- /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: "\u2502" }, `sep-${i}`)
790
- );
791
- }
792
- });
793
- parts.push(
794
- /* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: "\u2502" }, "right-border")
795
- );
796
- return /* @__PURE__ */ jsx12("text", { children: parts });
797
- };
798
- const headerData = columns.reduce(
799
- (acc, col) => ({ ...acc, [col]: col }),
800
- {}
801
- );
802
- return /* @__PURE__ */ jsxs7("box", { children: [
803
- borderLine("\u250C", "\u252C", "\u2510"),
804
- contentRow(headerData, true),
805
- data.map((row, index) => /* @__PURE__ */ jsxs7(Fragment, { children: [
806
- borderLine("\u251C", "\u253C", "\u2524"),
807
- contentRow(row, false)
808
- ] }, index)),
809
- borderLine("\u2514", "\u2534", "\u2518")
839
+ const cols = getColumns(data2, columnsProp);
840
+ return /* @__PURE__ */ jsxs7(TableRoot, { padding, headerColor, borderColor, children: [
841
+ /* @__PURE__ */ jsx12(TableHeader, { children: /* @__PURE__ */ jsx12(TableRow, { children: cols.map((col) => /* @__PURE__ */ jsx12(TableHead, { children: String(col) }, String(col))) }) }),
842
+ /* @__PURE__ */ jsx12(TableBody, { children: data2.map((row, index) => /* @__PURE__ */ jsx12(TableRow, { children: cols.map((col) => /* @__PURE__ */ jsx12(TableCell, { children: row[col] == null ? "" : String(row[col]) }, String(col))) }, index)) })
810
843
  ] });
811
844
  }
812
845
 
@@ -865,23 +898,23 @@ function generateGradient(colors, steps) {
865
898
  }
866
899
  return result;
867
900
  }
868
- function Gradient({ children, name, colors }) {
869
- if (name && colors) throw new Error("The `name` and `colors` props are mutually exclusive");
870
- if (!name && !colors) throw new Error("Either `name` or `colors` prop must be provided");
871
- const gradientColors = name ? GRADIENTS[name] : colors;
872
- const lines = children.split("\n");
873
- const maxLength = Math.max(...lines.map((l) => l.length));
901
+ function Gradient({ children, name: name2, colors }) {
902
+ if (name2 && colors) throw new Error("The `name` and `colors` props are mutually exclusive");
903
+ if (!name2 && !colors) throw new Error("Either `name` or `colors` prop must be provided");
904
+ const gradientColors = name2 ? GRADIENTS[name2] : colors;
905
+ const lines2 = children.split("\n");
906
+ const maxLength = Math.max(...lines2.map((l) => l.length));
874
907
  if (maxLength === 0) return /* @__PURE__ */ jsx13("text", { children });
875
908
  const hexColors = generateGradient(gradientColors, maxLength);
876
- 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)) });
909
+ return /* @__PURE__ */ jsx13(Fragment2, { children: lines2.map((line, lineIndex) => /* @__PURE__ */ jsx13("text", { children: line.split("").map((char, charIndex) => /* @__PURE__ */ jsx13("span", { style: { fg: hexColors[charIndex] }, children: char }, charIndex)) }, lineIndex)) });
877
910
  }
878
911
 
879
912
  // ../ui/components/tab-bar/tab-bar.tsx
880
- import { createContext as createContext3, useContext as useContext3, useState as useState5, Children, isValidElement } from "react";
913
+ import { createContext as createContext4, useContext as useContext4, useState as useState5, Children as Children2, isValidElement as isValidElement2 } from "react";
881
914
  import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
882
- var TabsContext = createContext3(null);
915
+ var TabsContext = createContext4(null);
883
916
  function useTabsContext() {
884
- const ctx = useContext3(TabsContext);
917
+ const ctx = useContext4(TabsContext);
885
918
  if (!ctx) throw new Error("Tabs compound components must be used within <Tabs>");
886
919
  return ctx;
887
920
  }
@@ -907,8 +940,8 @@ function TabsList({
907
940
  const { value } = useTabsContext();
908
941
  const color = activeColor ?? theme.accent;
909
942
  const triggers = [];
910
- Children.forEach(children, (child) => {
911
- if (isValidElement(child) && "value" in child.props) {
943
+ Children2.forEach(children, (child) => {
944
+ if (isValidElement2(child) && "value" in child.props) {
912
945
  triggers.push({ value: child.props.value, label: child.props.children });
913
946
  }
914
947
  });
@@ -971,9 +1004,9 @@ function Modal({
971
1004
  useKeyboard: useKeyboardProp
972
1005
  }) {
973
1006
  const theme = useTheme();
974
- const useKeyboard3 = useKeyboardContext(useKeyboardProp);
1007
+ const useKeyboard20 = useKeyboardContext(useKeyboardProp);
975
1008
  const resolvedBorderColor = borderColor ?? theme.muted;
976
- useKeyboard3?.((event) => {
1009
+ useKeyboard20?.((event) => {
977
1010
  if (event.name === "escape" && onClose) {
978
1011
  onClose();
979
1012
  }
@@ -999,28 +1032,29 @@ import {
999
1032
  useState as useState6,
1000
1033
  useRef as useRef4,
1001
1034
  useCallback as useCallback2,
1035
+ useEffect as useEffect2,
1002
1036
  useMemo as useMemo3,
1003
- createContext as createContext4,
1004
- useContext as useContext4
1037
+ createContext as createContext5,
1038
+ useContext as useContext5
1005
1039
  } from "react";
1006
1040
  import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
1007
- var PromptInputControllerCtx = createContext4(null);
1008
- var useOptionalController = () => useContext4(PromptInputControllerCtx);
1009
- var PromptInputContext = createContext4(null);
1041
+ var PromptInputControllerCtx = createContext5(null);
1042
+ var useOptionalController = () => useContext5(PromptInputControllerCtx);
1043
+ var PromptInputContext = createContext5(null);
1010
1044
  function usePromptInput() {
1011
- const ctx = useContext4(PromptInputContext);
1045
+ const ctx = useContext5(PromptInputContext);
1012
1046
  if (!ctx) {
1013
1047
  throw new Error("usePromptInput must be used within a <PromptInput> component");
1014
1048
  }
1015
1049
  return ctx;
1016
1050
  }
1017
- function computeDefaultSuggestions(input, commands, files) {
1018
- if (input.startsWith("/") && commands.length > 0) {
1019
- return commands.filter((c) => c.cmd.startsWith(input)).map((c) => ({ text: c.cmd, desc: c.desc }));
1051
+ function computeDefaultSuggestions(input, commands2, files2) {
1052
+ if (input.startsWith("/") && commands2.length > 0) {
1053
+ return commands2.filter((c) => c.cmd.startsWith(input)).map((c) => ({ text: c.cmd, desc: c.desc }));
1020
1054
  }
1021
- if (input.includes("@") && files.length > 0) {
1055
+ if (input.includes("@") && files2.length > 0) {
1022
1056
  const query = input.split("@").pop() ?? "";
1023
- return files.filter((f) => f.toLowerCase().includes(query.toLowerCase())).map((f) => ({ text: "@" + f }));
1057
+ return files2.filter((f) => f.toLowerCase().includes(query.toLowerCase())).map((f) => ({ text: "@" + f }));
1024
1058
  }
1025
1059
  return [];
1026
1060
  }
@@ -1030,9 +1064,10 @@ function resolveStatusHintText(status, submittedText, streamingText, errorText,
1030
1064
  if (status === "error") return errorText;
1031
1065
  return disabledText;
1032
1066
  }
1067
+ var DIVIDER_LINE = "\u2500".repeat(500);
1033
1068
  function PromptInputDivider() {
1034
1069
  const { theme } = usePromptInput();
1035
- return /* @__PURE__ */ jsx16("text", { wrapMode: "none", children: /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.muted }), children: "\u2500".repeat(500) }) });
1070
+ return /* @__PURE__ */ jsx16("text", { wrapMode: "none", marginLeft: -1, marginRight: -1, children: /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.muted }), children: DIVIDER_LINE }) });
1036
1071
  }
1037
1072
  function PromptInputSuggestions() {
1038
1073
  const { suggestions, sugIdx, maxSuggestions, theme } = usePromptInput();
@@ -1099,18 +1134,27 @@ function PromptInput({
1099
1134
  errorText = "An error occurred. Try again.",
1100
1135
  disabled: disabledProp = false,
1101
1136
  disabledText = "Generating...",
1102
- commands = [],
1103
- files = [],
1137
+ commands: commands2 = [],
1138
+ files: files2 = [],
1104
1139
  getSuggestions: customGetSuggestions,
1105
1140
  maxSuggestions = 5,
1106
1141
  enableHistory = true,
1107
1142
  model,
1108
1143
  showDividers = true,
1144
+ autoFocus = false,
1109
1145
  useKeyboard: useKeyboardProp,
1110
1146
  children
1111
1147
  }) {
1112
1148
  const theme = useTheme();
1113
- const useKeyboard3 = useKeyboardContext(useKeyboardProp);
1149
+ const useKeyboard20 = useKeyboardContext(useKeyboardProp);
1150
+ useEffect2(() => {
1151
+ if (!autoFocus) return;
1152
+ if (typeof document === "undefined") return;
1153
+ const canvas = document.querySelector("canvas");
1154
+ if (canvas && document.activeElement !== canvas) {
1155
+ canvas.focus();
1156
+ }
1157
+ }, [autoFocus]);
1114
1158
  const resolvedPromptColor = promptColor ?? theme.muted;
1115
1159
  const disabled = status ? status === "submitted" || status === "streaming" : disabledProp;
1116
1160
  const statusHintText = resolveStatusHintText(status, submittedText, streamingLabel, errorText, disabledText);
@@ -1167,8 +1211,8 @@ function PromptInput({
1167
1211
  }, []);
1168
1212
  const computeSuggestions = useCallback2((input) => {
1169
1213
  if (customGetSuggestions) return customGetSuggestions(input);
1170
- return computeDefaultSuggestions(input, commands, files);
1171
- }, [customGetSuggestions, commands, files]);
1214
+ return computeDefaultSuggestions(input, commands2, files2);
1215
+ }, [customGetSuggestions, commands2, files2]);
1172
1216
  const updateValue = useCallback2((next) => {
1173
1217
  valueRef.current = next;
1174
1218
  if (isControlled) {
@@ -1203,7 +1247,7 @@ function PromptInput({
1203
1247
  clearInput();
1204
1248
  }
1205
1249
  }, [onSubmit, clearInput]);
1206
- useKeyboard3?.((event) => {
1250
+ useKeyboard20?.((event) => {
1207
1251
  if (event.name === "escape" && (status === "streaming" || status === "submitted") && onStop) {
1208
1252
  onStop();
1209
1253
  return;
@@ -1304,14 +1348,16 @@ function PromptInput({
1304
1348
  theme
1305
1349
  };
1306
1350
  if (children) {
1307
- return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx16("box", { flexDirection: "column", children }) });
1351
+ return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx16("box", { flexDirection: "column", flexShrink: 0, children }) });
1308
1352
  }
1309
- return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxs10("box", { flexDirection: "column", children: [
1353
+ return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxs10("box", { flexDirection: "column", flexShrink: 0, children: [
1310
1354
  showDividers && /* @__PURE__ */ jsx16(PromptInputDivider, {}),
1311
- /* @__PURE__ */ jsx16(PromptInputSuggestions, {}),
1312
- /* @__PURE__ */ jsx16(PromptInputTextarea, {}),
1313
- /* @__PURE__ */ jsx16(PromptInputStatusText, {}),
1314
- /* @__PURE__ */ jsx16(PromptInputModel, {}),
1355
+ /* @__PURE__ */ jsxs10("box", { flexDirection: "column", paddingX: 1, children: [
1356
+ /* @__PURE__ */ jsx16(PromptInputSuggestions, {}),
1357
+ /* @__PURE__ */ jsx16(PromptInputTextarea, {}),
1358
+ /* @__PURE__ */ jsx16(PromptInputStatusText, {}),
1359
+ /* @__PURE__ */ jsx16(PromptInputModel, {})
1360
+ ] }),
1315
1361
  showDividers && /* @__PURE__ */ jsx16(PromptInputDivider, {})
1316
1362
  ] }) });
1317
1363
  }
@@ -1444,13 +1490,13 @@ function ChatPanel({
1444
1490
  }
1445
1491
 
1446
1492
  // ../ui/components/chain-of-thought/chain-of-thought.tsx
1447
- import { createContext as createContext5, memo, useContext as useContext5, useEffect as useEffect2, useMemo as useMemo4, useState as useState7 } from "react";
1493
+ import { createContext as createContext6, memo, useContext as useContext6, useEffect as useEffect3, useMemo as useMemo4, useState as useState7 } from "react";
1448
1494
  import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1449
1495
  var DOTS = ["\u25CB", "\u25D4", "\u25D1", "\u25D5", "\u25CF"];
1450
1496
  var SPINNER_INTERVAL = 150;
1451
- var ChainOfThoughtContext = createContext5(null);
1497
+ var ChainOfThoughtContext = createContext6(null);
1452
1498
  var useChainOfThought = () => {
1453
- const context = useContext5(ChainOfThoughtContext);
1499
+ const context = useContext6(ChainOfThoughtContext);
1454
1500
  if (!context) {
1455
1501
  throw new Error("ChainOfThought components must be used within <ChainOfThought>");
1456
1502
  }
@@ -1519,7 +1565,7 @@ var ChainOfThoughtStep = memo(({
1519
1565
  const color = getStepColor(status, theme);
1520
1566
  const pipe = "\u2502";
1521
1567
  const [frame, setFrame] = useState7(0);
1522
- useEffect2(() => {
1568
+ useEffect3(() => {
1523
1569
  if (!isActive) {
1524
1570
  setFrame(0);
1525
1571
  return;
@@ -1548,11 +1594,11 @@ ChainOfThoughtContent.displayName = "ChainOfThoughtContent";
1548
1594
  ChainOfThoughtStep.displayName = "ChainOfThoughtStep";
1549
1595
 
1550
1596
  // ../ui/components/message/message.tsx
1551
- import { createContext as createContext6, useContext as useContext6 } from "react";
1597
+ import { createContext as createContext7, useContext as useContext7 } from "react";
1552
1598
  import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
1553
- var MessageContext = createContext6(null);
1599
+ var MessageContext = createContext7(null);
1554
1600
  function useMessage() {
1555
- const ctx = useContext6(MessageContext);
1601
+ const ctx = useContext7(MessageContext);
1556
1602
  if (!ctx) throw new Error("useMessage must be used within <Message>");
1557
1603
  return ctx;
1558
1604
  }
@@ -1561,23 +1607,25 @@ function getBubbleColors(theme) {
1561
1607
  return isDark ? { assistantBg: "#2a2a4a", userBg: "#2a3a3a" } : { assistantBg: "#F1F5F9", userBg: "#E2E8F0" };
1562
1608
  }
1563
1609
  var TOOL_STATE_ICONS = {
1564
- "partial-call": "\u2022",
1610
+ pending: "\u2022",
1565
1611
  // •
1566
- "call": "\u280B",
1612
+ running: "\u280B",
1567
1613
  // ⠋
1568
- "result": "\u2713"
1614
+ completed: "\u2713",
1569
1615
  // ✓
1616
+ error: "\u2715"
1617
+ // ✕
1570
1618
  };
1571
1619
  function getToolStateColor(state, theme) {
1572
1620
  switch (state) {
1573
- case "partial-call":
1621
+ case "pending":
1574
1622
  return theme.muted;
1575
- case "call":
1623
+ case "running":
1576
1624
  return theme.warning;
1577
- case "result":
1625
+ case "completed":
1578
1626
  return theme.success;
1579
- default:
1580
- return theme.muted;
1627
+ case "error":
1628
+ return theme.error;
1581
1629
  }
1582
1630
  }
1583
1631
  function MessageContent({ children }) {
@@ -1602,51 +1650,57 @@ function MessageText({ children, isLast = false }) {
1602
1650
  isLast && isStreaming && /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: streamingCursor })
1603
1651
  ] });
1604
1652
  }
1605
- function MessageReasoning({ part }) {
1606
- return /* @__PURE__ */ jsxs13(ChainOfThought, { defaultOpen: part.collapsed === false, children: [
1607
- /* @__PURE__ */ jsx19(ChainOfThoughtHeader, { duration: part.duration }),
1608
- /* @__PURE__ */ jsx19(ChainOfThoughtContent, { children: part.steps?.map((step, i) => /* @__PURE__ */ jsx19(
1609
- ChainOfThoughtStep,
1610
- {
1611
- label: step.label,
1612
- description: step.description,
1613
- status: step.status,
1614
- isLast: i === (part.steps?.length ?? 0) - 1,
1615
- children: step.output
1616
- },
1617
- i
1618
- )) })
1653
+ function MessageReasoning({ duration, steps, collapsed = true, children }) {
1654
+ return /* @__PURE__ */ jsxs13(ChainOfThought, { defaultOpen: !collapsed, children: [
1655
+ /* @__PURE__ */ jsx19(ChainOfThoughtHeader, { duration }),
1656
+ /* @__PURE__ */ jsxs13(ChainOfThoughtContent, { children: [
1657
+ steps?.map((step, i) => /* @__PURE__ */ jsx19(
1658
+ ChainOfThoughtStep,
1659
+ {
1660
+ label: step.label,
1661
+ description: step.description,
1662
+ status: step.status,
1663
+ isLast: i === (steps?.length ?? 0) - 1,
1664
+ children: step.output
1665
+ },
1666
+ i
1667
+ )),
1668
+ children
1669
+ ] })
1619
1670
  ] });
1620
1671
  }
1621
- function MessageToolInvocation({ part, toolColors }) {
1672
+ function MessageToolCall({ name: name2, state = "pending", result, color }) {
1622
1673
  const theme = useTheme();
1623
1674
  const { backgroundColor, textColor } = useMessage();
1624
- const { toolName, state, result } = part.toolInvocation;
1625
- const icon = TOOL_STATE_ICONS[state] || "\u2022";
1626
- const stateColor = toolColors?.[toolName] ?? getToolStateColor(state, theme);
1627
- const isActive = state === "partial-call" || state === "call";
1675
+ const icon = TOOL_STATE_ICONS[state];
1676
+ const stateColor = color ?? getToolStateColor(state, theme);
1677
+ const isActive = state === "pending" || state === "running";
1628
1678
  return /* @__PURE__ */ jsxs13("box", { flexDirection: "column", children: [
1629
1679
  /* @__PURE__ */ jsxs13("text", { children: [
1630
1680
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bg: backgroundColor }), children: icon }),
1631
1681
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, bg: backgroundColor }), children: " " }),
1632
- /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bold: isActive, bg: backgroundColor }), children: toolName }),
1682
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bold: isActive, bg: backgroundColor }), children: name2 }),
1633
1683
  isActive && /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: " ..." })
1634
1684
  ] }),
1635
- state === "result" && result !== void 0 && /* @__PURE__ */ jsxs13("text", { children: [
1685
+ state === "completed" && result !== void 0 && /* @__PURE__ */ jsxs13("text", { children: [
1636
1686
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: " \u2514\u2500 " }),
1637
1687
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: String(result).slice(0, 120) })
1688
+ ] }),
1689
+ state === "error" && result !== void 0 && /* @__PURE__ */ jsxs13("text", { children: [
1690
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.error, dim: true, bg: backgroundColor }), children: " \u2514\u2500 " }),
1691
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.error, dim: true, bg: backgroundColor }), children: String(result).slice(0, 120) })
1638
1692
  ] })
1639
1693
  ] });
1640
1694
  }
1641
- function MessageSource({ part, index }) {
1695
+ function MessageSource({ title, url, index }) {
1642
1696
  const theme = useTheme();
1643
1697
  const { backgroundColor, textColor } = useMessage();
1644
- const title = part.source.title || part.source.url || "source";
1698
+ const displayTitle = title || url || "source";
1645
1699
  return /* @__PURE__ */ jsxs13("text", { children: [
1646
1700
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: "[" }),
1647
1701
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: String(index + 1) }),
1648
1702
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: "] " }),
1649
- /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: title })
1703
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: displayTitle })
1650
1704
  ] });
1651
1705
  }
1652
1706
  function MessageFooter({ model, timestamp }) {
@@ -1682,7 +1736,7 @@ function Message({
1682
1736
  Message.Content = MessageContent;
1683
1737
  Message.Text = MessageText;
1684
1738
  Message.Reasoning = MessageReasoning;
1685
- Message.ToolInvocation = MessageToolInvocation;
1739
+ Message.ToolCall = MessageToolCall;
1686
1740
  Message.Source = MessageSource;
1687
1741
  Message.Footer = MessageFooter;
1688
1742
 
@@ -1708,124 +1762,836 @@ function useBreakpoints() {
1708
1762
  };
1709
1763
  }
1710
1764
 
1711
- // ../docs/components/landing/landing-app.tsx
1712
- import { useCallback as useCallback3, useRef as useRef7, useState as useState10 } from "react";
1765
+ // demos/gradient.tsx
1766
+ import figlet from "figlet";
1767
+ import ansiShadow from "figlet/importable-fonts/ANSI Shadow.js";
1768
+ figlet.parseFont("ANSI Shadow", ansiShadow);
1769
+ var art = figlet.textSync("gridland", { font: "ANSI Shadow" });
1770
+ var lines = art.split("\n").filter((l) => l.trimEnd().length > 0);
1771
+ var gradientNames = Object.keys(GRADIENTS);
1772
+ function GradientApp() {
1773
+ const theme = useTheme();
1774
+ const [index, setIndex] = useState8(gradientNames.indexOf("instagram"));
1775
+ const name2 = gradientNames[index];
1776
+ useKeyboard((event) => {
1777
+ if (event.name === "left") setIndex((i) => i > 0 ? i - 1 : gradientNames.length - 1);
1778
+ if (event.name === "right") setIndex((i) => i < gradientNames.length - 1 ? i + 1 : 0);
1779
+ });
1780
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement("box", { padding: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1 }, /* @__PURE__ */ React.createElement(Gradient, { name: name2 }, lines.join("\n"))), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(
1781
+ StatusBar,
1782
+ {
1783
+ items: [{ key: "\u2190\u2192", label: "gradient" }],
1784
+ extra: /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.accent, bold: true }) }, name2.padEnd(11))
1785
+ }
1786
+ )));
1787
+ }
1713
1788
 
1714
- // ../docs/components/landing/about-modal.tsx
1715
- import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
1716
- function AboutModal({ onClose, useKeyboard: useKeyboard3 }) {
1789
+ // demos/ascii.tsx
1790
+ import { useState as useState9 } from "react";
1791
+ import { useKeyboard as useKeyboard2 } from "@gridland/utils";
1792
+ import figlet2 from "figlet";
1793
+ import ansiShadow2 from "figlet/importable-fonts/ANSI Shadow.js";
1794
+ import big from "figlet/importable-fonts/Big.js";
1795
+ import doom from "figlet/importable-fonts/Doom.js";
1796
+ import slant from "figlet/importable-fonts/Slant.js";
1797
+ import speed from "figlet/importable-fonts/Speed.js";
1798
+ import standard from "figlet/importable-fonts/Standard.js";
1799
+ import block from "figlet/importable-fonts/Block.js";
1800
+ import colossal from "figlet/importable-fonts/Colossal.js";
1801
+ var fonts = [
1802
+ { name: "ANSI Shadow", data: ansiShadow2 },
1803
+ { name: "Standard", data: standard },
1804
+ { name: "Big", data: big },
1805
+ { name: "Doom", data: doom },
1806
+ { name: "Slant", data: slant },
1807
+ { name: "Speed", data: speed },
1808
+ { name: "Block", data: block },
1809
+ { name: "Colossal", data: colossal }
1810
+ ];
1811
+ for (const f of fonts) {
1812
+ figlet2.parseFont(f.name, f.data);
1813
+ }
1814
+ function getLines(fontName) {
1815
+ const art2 = figlet2.textSync("gridland", { font: fontName });
1816
+ return art2.split("\n").filter((l) => l.trimEnd().length > 0);
1817
+ }
1818
+ function AsciiApp() {
1717
1819
  const theme = useTheme();
1718
- return /* @__PURE__ */ jsx21(Modal, { title: "About Gridland", useKeyboard: useKeyboard3, onClose, children: /* @__PURE__ */ jsxs15("box", { paddingX: 1, flexDirection: "column", gap: 1, children: [
1719
- /* @__PURE__ */ jsx21("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "What is Gridland?" }),
1720
- /* @__PURE__ */ jsx21("text", { children: "Gridland renders terminal UIs to HTML5 Canvas with React." }),
1721
- /* @__PURE__ */ jsx21("text", { children: "No xterm.js. No terminal emulator. Just pixels." }),
1722
- /* @__PURE__ */ jsx21("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "Features" }),
1723
- /* @__PURE__ */ jsxs15("text", { children: [
1724
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
1725
- "\u2022",
1726
- " "
1727
- ] }),
1728
- "Canvas-rendered TUI components"
1729
- ] }),
1730
- /* @__PURE__ */ jsxs15("text", { children: [
1731
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
1732
- "\u2022",
1733
- " "
1734
- ] }),
1735
- "React reconciler with JSX"
1736
- ] }),
1737
- /* @__PURE__ */ jsxs15("text", { children: [
1738
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
1739
- "\u2022",
1740
- " "
1741
- ] }),
1742
- "Yoga flexbox layout engine"
1743
- ] }),
1744
- /* @__PURE__ */ jsxs15("text", { children: [
1745
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
1746
- "\u2022",
1747
- " "
1748
- ] }),
1749
- "Keyboard, mouse, and clipboard support"
1750
- ] }),
1751
- /* @__PURE__ */ jsxs15("text", { children: [
1752
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
1753
- "\u2022",
1754
- " "
1755
- ] }),
1756
- "Next.js and Vite plugins"
1757
- ] }),
1758
- /* @__PURE__ */ jsx21("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "Tech Stack" }),
1759
- /* @__PURE__ */ jsx21("text", { children: "React + opentui engine + yoga-layout + HTML5 Canvas" }),
1760
- /* @__PURE__ */ jsx21("text", { style: textStyle({ dim: true }), children: "Press q to close" })
1761
- ] }) });
1820
+ const [fontIndex, setFontIndex] = useState9(fonts.findIndex((f) => f.name === "Colossal"));
1821
+ const font = fonts[fontIndex];
1822
+ const lines2 = getLines(font.name);
1823
+ useKeyboard2((event) => {
1824
+ if (event.name === "left") setFontIndex((i) => i > 0 ? i - 1 : fonts.length - 1);
1825
+ if (event.name === "right") setFontIndex((i) => i < fonts.length - 1 ? i + 1 : 0);
1826
+ });
1827
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement("box", { padding: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1 }, lines2.map((line, i) => /* @__PURE__ */ React.createElement("text", { key: i, fg: theme.accent, bold: true }, line))), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(
1828
+ StatusBar,
1829
+ {
1830
+ items: [{ key: "\u2190\u2192", label: "change font" }],
1831
+ extra: /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.accent, bold: true }) }, font.name.padEnd(11))
1832
+ }
1833
+ )));
1762
1834
  }
1763
1835
 
1764
- // ../docs/components/landing/install-box.tsx
1765
- import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
1766
- function InstallBox() {
1836
+ // demos/table.tsx
1837
+ function TableApp() {
1838
+ const data2 = [
1839
+ { name: "Alice", role: "Engineer", status: "Active" },
1840
+ { name: "Bob", role: "Designer", status: "Active" },
1841
+ { name: "Charlie", role: "PM", status: "Away" }
1842
+ ];
1843
+ return /* @__PURE__ */ React.createElement("box", { padding: 1, flexGrow: 1 }, /* @__PURE__ */ React.createElement(Table, { data: data2, headerColor: "cyan", borderColor: "#5e81ac" }));
1844
+ }
1845
+
1846
+ // demos/spinner.tsx
1847
+ import { useKeyboard as useKeyboard3 } from "@gridland/utils";
1848
+ function SpinnerApp() {
1849
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement("box", { flexGrow: 1 }, /* @__PURE__ */ React.createElement(SpinnerPicker, { useKeyboard: useKeyboard3 })), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(StatusBar, { items: [{ key: "\u2190\u2192", label: "change variant" }] })));
1850
+ }
1851
+
1852
+ // demos/select-input.tsx
1853
+ import { useState as useState10 } from "react";
1854
+ import { useKeyboard as useKeyboard4 } from "@gridland/utils";
1855
+ var items = [
1856
+ { label: "TypeScript", value: "ts" },
1857
+ { label: "JavaScript", value: "js" },
1858
+ { label: "Python", value: "py" },
1859
+ { label: "Rust", value: "rs" }
1860
+ ];
1861
+ function SelectInputApp() {
1862
+ const [submitted, setSubmitted] = useState10(false);
1863
+ const [resetKey, setResetKey] = useState10(0);
1864
+ useKeyboard4((event) => {
1865
+ if (submitted && event.name === "r") {
1866
+ setSubmitted(false);
1867
+ setResetKey((k) => k + 1);
1868
+ }
1869
+ });
1870
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1, padding: 1 }, /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement(
1871
+ SelectInput,
1872
+ {
1873
+ key: resetKey,
1874
+ items,
1875
+ title: "Choose a language",
1876
+ useKeyboard: useKeyboard4,
1877
+ onSubmit: () => setSubmitted(true)
1878
+ }
1879
+ )), /* @__PURE__ */ React.createElement(StatusBar, { items: submitted ? [{ key: "r", label: "reset demo" }] : [
1880
+ { key: "\u2191\u2193", label: "select" },
1881
+ { key: "enter", label: "submit" }
1882
+ ] }));
1883
+ }
1884
+
1885
+ // demos/multi-select.tsx
1886
+ import { useState as useState11 } from "react";
1887
+ import { useKeyboard as useKeyboard5 } from "@gridland/utils";
1888
+ var items2 = [
1889
+ { label: "TypeScript", value: "ts" },
1890
+ { label: "JavaScript", value: "js" },
1891
+ { label: "Python", value: "py" },
1892
+ { label: "Rust", value: "rs" }
1893
+ ];
1894
+ function MultiSelectApp() {
1895
+ const [submitted, setSubmitted] = useState11(false);
1896
+ const [resetKey, setResetKey] = useState11(0);
1897
+ useKeyboard5((event) => {
1898
+ if (submitted && event.name === "r") {
1899
+ setSubmitted(false);
1900
+ setResetKey((k) => k + 1);
1901
+ }
1902
+ });
1903
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1, padding: 1 }, /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement(
1904
+ MultiSelect,
1905
+ {
1906
+ key: resetKey,
1907
+ items: items2,
1908
+ title: "Select languages",
1909
+ useKeyboard: useKeyboard5,
1910
+ onSubmit: () => setSubmitted(true)
1911
+ }
1912
+ )), /* @__PURE__ */ React.createElement(StatusBar, { items: submitted ? [{ key: "r", label: "reset demo" }] : [
1913
+ { key: "\u2191\u2193", label: "move" },
1914
+ { key: "enter", label: "select" },
1915
+ { key: "a", label: "all" },
1916
+ { key: "x", label: "clear" }
1917
+ ] }));
1918
+ }
1919
+
1920
+ // demos/prompt-input.tsx
1921
+ import { useState as useState12 } from "react";
1922
+ import { useKeyboard as useKeyboard6 } from "@gridland/utils";
1923
+ var commands = [{ cmd: "/model", desc: "Switch model" }];
1924
+ var files = ["src/index.ts", "src/routes.ts", "src/auth.ts", "package.json"];
1925
+ var models = [
1926
+ { label: "Claude Opus", value: "opus" },
1927
+ { label: "Claude Sonnet", value: "sonnet" },
1928
+ { label: "Claude Haiku", value: "haiku" }
1929
+ ];
1930
+ function PromptInputApp() {
1767
1931
  const theme = useTheme();
1768
- return /* @__PURE__ */ jsx22(
1769
- "box",
1932
+ const [lastMessage, setLastMessage] = useState12("");
1933
+ const [model, setModel] = useState12("opus");
1934
+ const [showModelPicker, setShowModelPicker] = useState12(false);
1935
+ const [resetKey, setResetKey] = useState12(0);
1936
+ const handleSubmit = (msg) => {
1937
+ if (msg.text === "/model") {
1938
+ setShowModelPicker(true);
1939
+ setResetKey((k) => k + 1);
1940
+ return;
1941
+ }
1942
+ setLastMessage(msg.text);
1943
+ };
1944
+ if (showModelPicker) {
1945
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1, padding: 1 }, /* @__PURE__ */ React.createElement(Modal, { title: "Select Model", useKeyboard: useKeyboard6, onClose: () => setShowModelPicker(false) }, /* @__PURE__ */ React.createElement("box", { paddingX: 1 }, /* @__PURE__ */ React.createElement(
1946
+ SelectInput,
1947
+ {
1948
+ items: models,
1949
+ defaultValue: model,
1950
+ useKeyboard: useKeyboard6,
1951
+ onSubmit: (value) => {
1952
+ setModel(value);
1953
+ setShowModelPicker(false);
1954
+ }
1955
+ }
1956
+ ))));
1957
+ }
1958
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1, padding: 1 }, /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, lastMessage ? /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.muted }) }, "Sent: "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.foreground }) }, lastMessage)) : /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", null, " "))), /* @__PURE__ */ React.createElement(
1959
+ PromptInput,
1770
1960
  {
1771
- border: true,
1772
- borderStyle: "rounded",
1773
- borderColor: theme.border,
1774
- paddingX: 1,
1775
- flexDirection: "column",
1776
- flexShrink: 0,
1777
- children: /* @__PURE__ */ jsxs16("text", { children: [
1778
- /* @__PURE__ */ jsx22("span", { style: textStyle({ dim: true }), children: "$ " }),
1779
- /* @__PURE__ */ jsx22("span", { style: textStyle({ bold: true }), children: "bun create " }),
1780
- /* @__PURE__ */ jsx22("span", { style: textStyle({ fg: theme.accent }), children: "gridland" })
1781
- ] })
1961
+ key: resetKey,
1962
+ commands,
1963
+ files,
1964
+ placeholder: "Message Claude...",
1965
+ showDividers: true,
1966
+ useKeyboard: useKeyboard6,
1967
+ onSubmit: handleSubmit
1782
1968
  }
1783
- );
1969
+ ), /* @__PURE__ */ React.createElement("box", null, /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ dim: true }) }, "model: " + model))), /* @__PURE__ */ React.createElement("box", { paddingTop: 1, paddingLeft: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(StatusBar, { items: [
1970
+ { key: "\u23CE enter", label: "send" },
1971
+ { key: "/model", label: "change model" },
1972
+ { key: "\u2191", label: "history" }
1973
+ ] })));
1784
1974
  }
1785
1975
 
1786
- // ../docs/components/landing/links-box.tsx
1787
- import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
1788
- var UNDERLINE3 = 1 << 3;
1789
- function LinksBox() {
1976
+ // demos/text-input.tsx
1977
+ import { useState as useState13 } from "react";
1978
+ import { useKeyboard as useKeyboard7 } from "@gridland/utils";
1979
+ var FIELDS = [
1980
+ { label: "Username", placeholder: "enter your name", maxLength: 30, required: true },
1981
+ { label: "Email", placeholder: "user@example.com", maxLength: 50, required: true, description: "We'll never share your email" },
1982
+ { label: "Password", placeholder: "enter password", maxLength: 40 },
1983
+ { label: "API Key", placeholder: "sk-...", maxLength: 60, disabled: true }
1984
+ ];
1985
+ function TextInputApp() {
1986
+ const [activeField, setActiveField] = useState13(0);
1987
+ const [values, setValues] = useState13(FIELDS.map(() => ""));
1988
+ useKeyboard7((event) => {
1989
+ if (event.name === "up") setActiveField((i) => Math.max(0, i - 1));
1990
+ if (event.name === "down") setActiveField((i) => Math.min(FIELDS.length - 1, i + 1));
1991
+ });
1992
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement("box", { flexDirection: "column", paddingX: 1, paddingTop: 1, flexGrow: 1 }, FIELDS.map((field, i) => /* @__PURE__ */ React.createElement("box", { key: field.label, marginBottom: 1 }, /* @__PURE__ */ React.createElement(
1993
+ TextInput,
1994
+ {
1995
+ label: field.label,
1996
+ placeholder: field.placeholder,
1997
+ prompt: "> ",
1998
+ focus: i === activeField,
1999
+ maxLength: field.maxLength,
2000
+ value: values[i],
2001
+ onChange: (v) => setValues((prev) => prev.map((old, j) => j === i ? v : old)),
2002
+ required: field.required,
2003
+ disabled: field.disabled,
2004
+ description: field.description
2005
+ }
2006
+ )))), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(StatusBar, { items: [{ key: "\u2191\u2193", label: "field" }] })));
2007
+ }
2008
+
2009
+ // demos/link.tsx
2010
+ import { useState as useState14 } from "react";
2011
+ import { useKeyboard as useKeyboard8 } from "@gridland/utils";
2012
+ var MODES = ["solid", "dashed", "dotted", "none"];
2013
+ function LinkApp() {
1790
2014
  const theme = useTheme();
1791
- return /* @__PURE__ */ jsx23(
1792
- "box",
2015
+ const [modeIndex, setModeIndex] = useState14(0);
2016
+ const mode = MODES[modeIndex];
2017
+ useKeyboard8((event) => {
2018
+ if (event.name === "right") setModeIndex((i) => (i + 1) % MODES.length);
2019
+ if (event.name === "left") setModeIndex((i) => (i - 1 + MODES.length) % MODES.length);
2020
+ });
2021
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement("box", { padding: 1, flexGrow: 1 }, /* @__PURE__ */ React.createElement("text", { style: textStyle({ fg: theme.foreground }) }, "Made by ", /* @__PURE__ */ React.createElement("a", { href: "https://cjroth.com", style: { attributes: mode === "solid" ? 8 : mode === "dashed" ? 24 : mode === "dotted" ? 72 : 0, fg: theme.accent } }, "Chris Roth"), " and ", /* @__PURE__ */ React.createElement("a", { href: "https://jessicacheng.studio", style: { attributes: mode === "solid" ? 8 : mode === "dashed" ? 24 : mode === "dotted" ? 72 : 0, fg: theme.accent } }, "Jessica Cheng"), ".")), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(
2022
+ StatusBar,
1793
2023
  {
1794
- border: true,
1795
- borderStyle: "rounded",
1796
- borderColor: theme.border,
1797
- paddingX: 1,
1798
- flexDirection: "column",
1799
- flexShrink: 0,
1800
- children: /* @__PURE__ */ jsxs17("text", { children: [
1801
- /* @__PURE__ */ jsx23("span", { children: "\u{1F431}" }),
1802
- /* @__PURE__ */ jsx23("a", { href: "https://github.com/thoughtfulllc/gridland", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " GitHub" }),
1803
- /* @__PURE__ */ jsx23("span", { children: " " }),
1804
- /* @__PURE__ */ jsx23("span", { children: "\u{1F4D6}" }),
1805
- /* @__PURE__ */ jsx23("a", { href: "https://gridland.io/docs", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " Docs" })
1806
- ] })
2024
+ extra: /* @__PURE__ */ React.createElement("span", { style: textStyle({ bold: true, fg: theme.foreground }) }, mode.padEnd(6)),
2025
+ items: [{ key: "\u2190\u2192", label: "underline style" }]
1807
2026
  }
1808
- );
2027
+ )));
1809
2028
  }
1810
2029
 
1811
- // ../docs/components/landing/logo.tsx
1812
- import { useState as useState8, useEffect as useEffect3, useRef as useRef5, useMemo as useMemo5 } from "react";
1813
- import figlet from "figlet";
1814
- import ansiShadow from "figlet/importable-fonts/ANSI Shadow.js";
1815
- import { Fragment as Fragment7, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
1816
- figlet.parseFont("ANSI Shadow", ansiShadow);
1817
- function makeArt(text) {
1818
- return figlet.textSync(text, { font: "ANSI Shadow" }).split("\n").filter((l) => l.trimEnd().length > 0).join("\n");
2030
+ // demos/tabs.tsx
2031
+ import { useState as useState15 } from "react";
2032
+ import { useKeyboard as useKeyboard9 } from "@gridland/utils";
2033
+ var tabs = ["Files", "Search", "Git", "Debug"];
2034
+ function TabBarApp() {
2035
+ const [selectedIndex, setSelectedIndex] = useState15(0);
2036
+ useKeyboard9((event) => {
2037
+ if (event.name === "left") setSelectedIndex((i) => i > 0 ? i - 1 : tabs.length - 1);
2038
+ if (event.name === "right") setSelectedIndex((i) => i < tabs.length - 1 ? i + 1 : 0);
2039
+ });
2040
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement("box", { padding: 1 }, /* @__PURE__ */ React.createElement(TabBar, { options: tabs, selectedIndex })), /* @__PURE__ */ React.createElement("box", { flexGrow: 1 }), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(StatusBar, { items: [{ key: "\u2190\u2192", label: "switch tab" }] })));
1819
2041
  }
1820
- var fullArt = makeArt("gridland");
1821
- var gridArt = makeArt("grid");
2042
+
2043
+ // demos/status-bar.tsx
2044
+ import { useState as useState16 } from "react";
2045
+ import { useKeyboard as useKeyboard10 } from "@gridland/utils";
2046
+ var shortcuts = [
2047
+ { key: "Tab", label: "switch focus" },
2048
+ { key: "\u2190\u2192", label: "cycle" },
2049
+ { key: "b", label: "back" },
2050
+ { key: "z", label: "reset" }
2051
+ ];
2052
+ function StatusBarApp() {
2053
+ const theme = useTheme();
2054
+ const [lastKey, setLastKey] = useState16(null);
2055
+ useKeyboard10((event) => {
2056
+ if (event.name === "tab") setLastKey("switch focus (Tab)");
2057
+ else if (event.name === "left") setLastKey("cycle (\u2190)");
2058
+ else if (event.name === "right") setLastKey("cycle (\u2192)");
2059
+ else if (event.name === "b") setLastKey("back (b)");
2060
+ else if (event.name === "z") setLastKey("reset (z)");
2061
+ });
2062
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", gap: 1, padding: 1 }, lastKey ? /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", null, "Pressed: "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ bold: true, fg: theme.accent }) }, lastKey)) : /* @__PURE__ */ React.createElement("text", { style: textStyle({ dim: true }) }, "Press a key to trigger an action"), /* @__PURE__ */ React.createElement(
2063
+ StatusBar,
2064
+ {
2065
+ items: shortcuts,
2066
+ extra: /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.success }) }, "\u25CF Ready")
2067
+ }
2068
+ ));
2069
+ }
2070
+
2071
+ // demos/modal.tsx
2072
+ import { useState as useState17 } from "react";
2073
+ import { useKeyboard as useKeyboard11 } from "@gridland/utils";
2074
+ function ModalApp() {
2075
+ const theme = useTheme();
2076
+ const [isOpen, setIsOpen] = useState17(false);
2077
+ useKeyboard11((event) => {
2078
+ if (!isOpen && event.name === "m") setIsOpen(true);
2079
+ if (isOpen && (event.name === "q" || event.name === "escape")) setIsOpen(false);
2080
+ });
2081
+ if (isOpen) {
2082
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement(Modal, { title: "Example Modal", useKeyboard: useKeyboard11, onClose: () => setIsOpen(false) }, /* @__PURE__ */ React.createElement("box", { paddingX: 1, flexDirection: "column" }, /* @__PURE__ */ React.createElement("text", { style: textStyle({ fg: theme.foreground }) }, "This is a modal overlay component."), /* @__PURE__ */ React.createElement("text", null, " "), /* @__PURE__ */ React.createElement("text", { style: textStyle({ dim: true, fg: theme.muted }) }, "It stretches to fill the full terminal height."))), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(StatusBar, { items: [{ key: "q", label: "close" }] })));
2083
+ }
2084
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1, alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ dim: true, fg: theme.muted }) }, "Press "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ bold: true, fg: theme.background, bg: theme.muted }) }, " m "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ dim: true, fg: theme.muted }) }, " to open modal")));
2085
+ }
2086
+
2087
+ // demos/primitives.tsx
2088
+ function PrimitivesApp() {
2089
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(
2090
+ "box",
2091
+ {
2092
+ border: true,
2093
+ borderStyle: "rounded",
2094
+ borderColor: "#75715e",
2095
+ title: "Layout",
2096
+ titleAlignment: "center",
2097
+ padding: 1
2098
+ },
2099
+ /* @__PURE__ */ React.createElement("box", { flexDirection: "row", gap: 2 }, /* @__PURE__ */ React.createElement("box", { border: true, borderStyle: "single", borderColor: "#a6e22e", padding: 1, flexGrow: 1 }, /* @__PURE__ */ React.createElement("text", { fg: "#a6e22e", bold: true }, "Box 1")), /* @__PURE__ */ React.createElement("box", { border: true, borderStyle: "single", borderColor: "#f92672", padding: 1, flexGrow: 1 }, /* @__PURE__ */ React.createElement("text", { fg: "#f92672", bold: true }, "Box 2")), /* @__PURE__ */ React.createElement("box", { border: true, borderStyle: "single", borderColor: "#66d9ef", padding: 1, flexGrow: 1 }, /* @__PURE__ */ React.createElement("text", { fg: "#66d9ef", bold: true }, "Box 3")))
2100
+ ), /* @__PURE__ */ React.createElement("text", { dim: true, fg: "#75715e" }, " Nested boxes with borders, colors & flexbox layout"));
2101
+ }
2102
+
2103
+ // demos/chat.tsx
2104
+ import { useState as useState18, useCallback as useCallback3, useRef as useRef5 } from "react";
2105
+ import { useKeyboard as useKeyboard12 } from "@gridland/utils";
2106
+ var initialMessages = [
2107
+ { id: "1", role: "user", content: "Show me my portfolio" },
2108
+ { id: "2", role: "assistant", content: "Here's your current portfolio allocation:" },
2109
+ { id: "3", role: "user", content: "Calculate rebalancing trades" },
2110
+ { id: "4", role: "assistant", content: "I've calculated the optimal trades to rebalance your portfolio." }
2111
+ ];
2112
+ var nextId = 5;
2113
+ function ChatApp() {
2114
+ const [messages, setMessages] = useState18(initialMessages);
2115
+ const [isLoading, setIsLoading] = useState18(false);
2116
+ const [streamingText, setStreamingText] = useState18("");
2117
+ const [activeToolCalls, setActiveToolCalls] = useState18([]);
2118
+ const intervalRef = useRef5(null);
2119
+ const handleSend = useCallback3((text) => {
2120
+ const userMsg = { id: String(nextId++), role: "user", content: text };
2121
+ setMessages((prev) => [...prev, userMsg]);
2122
+ setIsLoading(true);
2123
+ const toolCallId = `tc-${nextId}`;
2124
+ setTimeout(() => {
2125
+ setIsLoading(false);
2126
+ setActiveToolCalls([{ id: toolCallId, title: "process_request", status: "in_progress" }]);
2127
+ }, 500);
2128
+ setTimeout(() => {
2129
+ setActiveToolCalls([{ id: toolCallId, title: "process_request", status: "completed" }]);
2130
+ }, 1200);
2131
+ const response = `You said: "${text}". This is a demo response.`;
2132
+ let charIndex = 0;
2133
+ setTimeout(() => {
2134
+ intervalRef.current = setInterval(() => {
2135
+ charIndex = Math.min(charIndex + 3, response.length);
2136
+ if (charIndex < response.length) {
2137
+ setStreamingText(response.slice(0, charIndex));
2138
+ } else {
2139
+ if (intervalRef.current) clearInterval(intervalRef.current);
2140
+ setStreamingText("");
2141
+ setActiveToolCalls([]);
2142
+ setMessages((prev) => [
2143
+ ...prev,
2144
+ { id: String(nextId++), role: "assistant", content: response }
2145
+ ]);
2146
+ }
2147
+ }, 50);
2148
+ }, 1400);
2149
+ }, []);
2150
+ const handleCancel = useCallback3(() => {
2151
+ if (intervalRef.current) clearInterval(intervalRef.current);
2152
+ setIsLoading(false);
2153
+ setStreamingText("");
2154
+ setActiveToolCalls([]);
2155
+ }, []);
2156
+ return /* @__PURE__ */ React.createElement(
2157
+ ChatPanel,
2158
+ {
2159
+ messages,
2160
+ streamingText,
2161
+ isLoading,
2162
+ activeToolCalls,
2163
+ onSendMessage: handleSend,
2164
+ onCancel: handleCancel,
2165
+ placeholder: "Ask about your portfolio...",
2166
+ useKeyboard: useKeyboard12
2167
+ }
2168
+ );
2169
+ }
2170
+
2171
+ // demos/chain-of-thought.tsx
2172
+ import { useState as useState19, useEffect as useEffect4, useRef as useRef6 } from "react";
2173
+ import { useKeyboard as useKeyboard13 } from "@gridland/utils";
2174
+ var ALL_STEPS = [
2175
+ { tool: "Read", label: "Reading codebase", description: "src/", status: "done", delay: 1800 },
2176
+ { tool: "Think", label: "Planning changes", description: "auth module", status: "done", delay: 2500 },
2177
+ { tool: "Edit", label: "Editing files", description: "4 files", status: "done", delay: 3200 },
2178
+ { tool: "Bash", label: "Running tests", description: "vitest", status: "done", delay: 2e3 },
2179
+ { tool: "Edit", label: "Fixing test", description: "routes.test.ts", status: "done", delay: 1500 }
2180
+ ];
2181
+ function ChainOfThoughtApp() {
2182
+ const [expanded, setExpanded] = useState19(true);
2183
+ const [phase, setPhase] = useState19("running");
2184
+ const [stepIndex, setStepIndex] = useState19(0);
2185
+ const timerRef = useRef6(null);
2186
+ useKeyboard13((event) => {
2187
+ if (event.name === "E" && event.ctrl && event.shift) setExpanded((v) => !v);
2188
+ if (event.name === "r") restart();
2189
+ });
2190
+ function restart() {
2191
+ if (timerRef.current) clearTimeout(timerRef.current);
2192
+ setPhase("running");
2193
+ setStepIndex(0);
2194
+ }
2195
+ useEffect4(() => {
2196
+ if (phase !== "running") return;
2197
+ if (stepIndex < ALL_STEPS.length) {
2198
+ const delay = ALL_STEPS[stepIndex].delay;
2199
+ timerRef.current = setTimeout(() => setStepIndex((i) => i + 1), delay);
2200
+ } else {
2201
+ timerRef.current = setTimeout(() => setPhase("done"), 500);
2202
+ }
2203
+ return () => {
2204
+ if (timerRef.current) clearTimeout(timerRef.current);
2205
+ };
2206
+ }, [phase, stepIndex]);
2207
+ useEffect4(() => {
2208
+ if (phase === "done") {
2209
+ timerRef.current = setTimeout(() => restart(), 3e3);
2210
+ }
2211
+ return () => {
2212
+ if (timerRef.current) clearTimeout(timerRef.current);
2213
+ };
2214
+ }, [phase]);
2215
+ const steps = ALL_STEPS.map((s, i) => {
2216
+ if (i < stepIndex) return { ...s, status: "done" };
2217
+ if (i === stepIndex && phase === "running") return { ...s, status: "running" };
2218
+ return { ...s, status: phase === "done" ? "done" : "pending" };
2219
+ });
2220
+ const elapsedMs = ALL_STEPS.slice(0, stepIndex).reduce((sum, s) => sum + s.delay, 0);
2221
+ const totalMs = ALL_STEPS.reduce((sum, s) => sum + s.delay, 0);
2222
+ const durationStr = phase === "done" ? `${(totalMs / 1e3).toFixed(1)}s` : `${(elapsedMs / 1e3).toFixed(1)}s`;
2223
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement("box", { flexDirection: "column", padding: 1, flexGrow: 1 }, /* @__PURE__ */ React.createElement(ChainOfThought, { open: expanded, onOpenChange: setExpanded }, /* @__PURE__ */ React.createElement(ChainOfThoughtHeader, { duration: durationStr }), /* @__PURE__ */ React.createElement(ChainOfThoughtContent, null, steps.map((step, i) => /* @__PURE__ */ React.createElement(
2224
+ ChainOfThoughtStep,
2225
+ {
2226
+ key: i,
2227
+ label: step.label,
2228
+ description: step.description,
2229
+ status: step.status,
2230
+ isLast: i === steps.length - 1
2231
+ },
2232
+ step.output
2233
+ ))))), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(StatusBar, { items: [
2234
+ { key: "ctrl+shift+e", label: "toggle" },
2235
+ { key: "r", label: "restart" }
2236
+ ] })));
2237
+ }
2238
+
2239
+ // demos/message.tsx
2240
+ import { useState as useState20, useEffect as useEffect5, useRef as useRef7 } from "react";
2241
+ import { useKeyboard as useKeyboard14 } from "@gridland/utils";
2242
+ var 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.";
2243
+ function MessageApp() {
2244
+ const [phase, setPhase] = useState20("idle");
2245
+ const [streamedText, setStreamedText] = useState20("");
2246
+ const timerRef = useRef7(null);
2247
+ useKeyboard14((event) => {
2248
+ if (event.name === "r") restart();
2249
+ });
2250
+ function restart() {
2251
+ if (timerRef.current) clearTimeout(timerRef.current);
2252
+ setPhase("idle");
2253
+ setStreamedText("");
2254
+ }
2255
+ useEffect5(() => {
2256
+ if (phase === "idle") {
2257
+ timerRef.current = setTimeout(() => setPhase("streaming"), 800);
2258
+ }
2259
+ return () => {
2260
+ if (timerRef.current) clearTimeout(timerRef.current);
2261
+ };
2262
+ }, [phase]);
2263
+ useEffect5(() => {
2264
+ if (phase !== "streaming") return;
2265
+ if (streamedText.length < RESPONSE.length) {
2266
+ timerRef.current = setTimeout(() => {
2267
+ setStreamedText(RESPONSE.slice(0, streamedText.length + 2));
2268
+ }, 25);
2269
+ } else {
2270
+ timerRef.current = setTimeout(() => setPhase("done"), 500);
2271
+ }
2272
+ return () => {
2273
+ if (timerRef.current) clearTimeout(timerRef.current);
2274
+ };
2275
+ }, [phase, streamedText]);
2276
+ useEffect5(() => {
2277
+ if (phase === "done") {
2278
+ timerRef.current = setTimeout(() => restart(), 3e3);
2279
+ }
2280
+ return () => {
2281
+ if (timerRef.current) clearTimeout(timerRef.current);
2282
+ };
2283
+ }, [phase]);
2284
+ const isStreaming = phase === "streaming";
2285
+ const isDone = phase === "done";
2286
+ const showAssistant = phase !== "idle";
2287
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement("box", { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1 }, /* @__PURE__ */ React.createElement(Message, { role: "user" }, /* @__PURE__ */ React.createElement(Message.Content, null, /* @__PURE__ */ React.createElement(Message.Text, null, "Can you refactor the auth module?"))), showAssistant && /* @__PURE__ */ React.createElement(Message, { role: "assistant", isStreaming }, /* @__PURE__ */ React.createElement(Message.Content, null, /* @__PURE__ */ React.createElement(Message.Text, { isLast: true }, isDone ? RESPONSE : streamedText)))), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(StatusBar, { items: [{ key: "r", label: "restart" }] })));
2288
+ }
2289
+
2290
+ // demos/terminal-window.tsx
2291
+ function TerminalWindowApp() {
2292
+ const theme = useTheme();
2293
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement("text", { style: textStyle({ fg: theme.secondary }) }, '$ echo "Hello from TerminalWindow"'), /* @__PURE__ */ React.createElement("text", { style: textStyle({ fg: theme.foreground }) }, "Hello from TerminalWindow"), /* @__PURE__ */ React.createElement("text", { style: textStyle({ fg: theme.secondary }) }, "$ _"));
2294
+ }
2295
+
2296
+ // demos/focus.tsx
2297
+ import { useState as useState21, useRef as useRef8 } from "react";
2298
+ import { useKeyboard as useKeyboard15 } from "@gridland/utils";
2299
+ var focusPanels = [
2300
+ {
2301
+ label: "Language",
2302
+ items: [
2303
+ { label: "TypeScript", value: "ts" },
2304
+ { label: "JavaScript", value: "js" },
2305
+ { label: "Python", value: "py" }
2306
+ ]
2307
+ },
2308
+ {
2309
+ label: "Framework",
2310
+ items: [
2311
+ { label: "React", value: "react" },
2312
+ { label: "Vue", value: "vue" },
2313
+ { label: "Svelte", value: "svelte" }
2314
+ ]
2315
+ },
2316
+ {
2317
+ label: "Runtime",
2318
+ items: [
2319
+ { label: "Bun", value: "bun" },
2320
+ { label: "Node", value: "node" },
2321
+ { label: "Deno", value: "deno" }
2322
+ ]
2323
+ }
2324
+ ];
2325
+ function FocusApp() {
2326
+ const [panelIndex, setPanelIndex] = useState21(0);
2327
+ const [entered, setEntered] = useState21(false);
2328
+ const [cursors, setCursors] = useState21([0, 0, 0]);
2329
+ const [selections, setSelections] = useState21([null, null, null]);
2330
+ const panelRef = useRef8(0);
2331
+ const enteredRef = useRef8(false);
2332
+ const cursorsRef = useRef8([0, 0, 0]);
2333
+ panelRef.current = panelIndex;
2334
+ enteredRef.current = entered;
2335
+ cursorsRef.current = cursors;
2336
+ useKeyboard15((event) => {
2337
+ const pi = panelRef.current;
2338
+ if (enteredRef.current) {
2339
+ const items3 = focusPanels[pi].items;
2340
+ const cur = cursorsRef.current[pi];
2341
+ if (event.name === "down" || event.name === "j") {
2342
+ const next = (cur + 1) % items3.length;
2343
+ cursorsRef.current = [...cursorsRef.current];
2344
+ cursorsRef.current[pi] = next;
2345
+ setCursors([...cursorsRef.current]);
2346
+ } else if (event.name === "up" || event.name === "k") {
2347
+ const next = (cur - 1 + items3.length) % items3.length;
2348
+ cursorsRef.current = [...cursorsRef.current];
2349
+ cursorsRef.current[pi] = next;
2350
+ setCursors([...cursorsRef.current]);
2351
+ } else if (event.name === "return") {
2352
+ const selected = items3[cursorsRef.current[pi]].label;
2353
+ setSelections((s) => {
2354
+ const n = [...s];
2355
+ n[pi] = selected;
2356
+ return n;
2357
+ });
2358
+ enteredRef.current = false;
2359
+ setEntered(false);
2360
+ } else if (event.name === "escape") {
2361
+ enteredRef.current = false;
2362
+ setEntered(false);
2363
+ }
2364
+ } else {
2365
+ if (event.name === "right" || event.name === "tab") {
2366
+ const next = (pi + 1) % focusPanels.length;
2367
+ panelRef.current = next;
2368
+ setPanelIndex(next);
2369
+ } else if (event.name === "left") {
2370
+ const next = (pi - 1 + focusPanels.length) % focusPanels.length;
2371
+ panelRef.current = next;
2372
+ setPanelIndex(next);
2373
+ } else if (event.name === "return") {
2374
+ enteredRef.current = true;
2375
+ setEntered(true);
2376
+ }
2377
+ }
2378
+ event.preventDefault();
2379
+ });
2380
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, /* @__PURE__ */ React.createElement("box", { flexDirection: "row", gap: 1, padding: 1, flexGrow: 1 }, focusPanels.map((panel, i) => {
2381
+ const focused = i === panelIndex;
2382
+ const active = focused && entered;
2383
+ const selected = selections[i];
2384
+ return /* @__PURE__ */ React.createElement(
2385
+ "box",
2386
+ {
2387
+ key: panel.label,
2388
+ border: true,
2389
+ borderStyle: "rounded",
2390
+ borderColor: active ? "#22c55e" : focused ? "#3b82f6" : "#555",
2391
+ flexGrow: 1
2392
+ },
2393
+ /* @__PURE__ */ React.createElement("box", { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement("text", { style: {
2394
+ fg: active ? "#22c55e" : focused ? "#3b82f6" : "#888",
2395
+ bold: focused
2396
+ } }, focused ? "\u25B8 " : " ", panel.label, selected ? `: ${selected}` : ""), (active || !entered && focused) && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("box", { height: 1 }), /* @__PURE__ */ React.createElement("box", { flexDirection: "column" }, panel.items.map((item, j) => {
2397
+ const highlighted = active && j === cursors[i];
2398
+ return /* @__PURE__ */ React.createElement("text", { key: item.value, style: {
2399
+ fg: highlighted ? "#22c55e" : active ? "#ccc" : "#666",
2400
+ bold: highlighted
2401
+ } }, highlighted ? " \u25B8 " : " ", item.label);
2402
+ }))))
2403
+ );
2404
+ })), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(
2405
+ StatusBar,
2406
+ {
2407
+ items: entered ? [{ key: "\u2191\u2193", label: "select" }, { key: "enter", label: "confirm" }, { key: "esc", label: "back" }] : [{ key: "\u2190\u2192", label: "navigate" }, { key: "enter", label: "select" }, { key: "tab", label: "next" }]
2408
+ }
2409
+ )));
2410
+ }
2411
+
2412
+ // demos/pointer.tsx
2413
+ import { useState as useState22, useRef as useRef9 } from "react";
2414
+ import { useKeyboard as useKeyboard16 } from "@gridland/utils";
2415
+ var pointerColors = ["#ef4444", "#f97316", "#eab308", "#22c55e", "#3b82f6", "#8b5cf6"];
2416
+ var pointerColorNames = ["Red", "Orange", "Yellow", "Green", "Blue", "Purple"];
2417
+ function HoverBox() {
2418
+ const [hovering, setHovering] = useState22(false);
2419
+ return /* @__PURE__ */ React.createElement(
2420
+ "box",
2421
+ {
2422
+ border: true,
2423
+ borderStyle: "rounded",
2424
+ borderColor: hovering ? "#22c55e" : "#555",
2425
+ width: 20,
2426
+ height: 5,
2427
+ onMouseOver: () => setHovering(true),
2428
+ onMouseOut: () => setHovering(false)
2429
+ },
2430
+ /* @__PURE__ */ React.createElement("box", { padding: 1 }, /* @__PURE__ */ React.createElement("text", { style: { fg: hovering ? "#22c55e" : "#888", bold: hovering } }, hovering ? "Mouse inside!" : "Hover me"))
2431
+ );
2432
+ }
2433
+ function PointerApp() {
2434
+ const [selected, setSelected] = useState22(null);
2435
+ const [clickCount, setClickCount] = useState22(0);
2436
+ const [mousePos, setMousePos] = useState22(null);
2437
+ const selectedRef = useRef9(null);
2438
+ const clickCountRef = useRef9(0);
2439
+ selectedRef.current = selected;
2440
+ clickCountRef.current = clickCount;
2441
+ useKeyboard16((event) => {
2442
+ const cur = selectedRef.current ?? -1;
2443
+ if (event.name === "right" || event.name === "tab") {
2444
+ const next = (cur + 1) % pointerColors.length;
2445
+ selectedRef.current = next;
2446
+ setSelected(next);
2447
+ } else if (event.name === "left") {
2448
+ const next = (cur - 1 + pointerColors.length) % pointerColors.length;
2449
+ selectedRef.current = next;
2450
+ setSelected(next);
2451
+ }
2452
+ event.preventDefault();
2453
+ });
2454
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1, padding: 1 }, /* @__PURE__ */ React.createElement("box", { flexDirection: "row", gap: 1 }, pointerColors.map((color, i) => /* @__PURE__ */ React.createElement(
2455
+ "box",
2456
+ {
2457
+ key: color,
2458
+ flexGrow: 1,
2459
+ height: 3,
2460
+ border: true,
2461
+ borderStyle: "rounded",
2462
+ borderColor: i === selected ? color : "#555",
2463
+ onMouseDown: (e) => {
2464
+ clickCountRef.current++;
2465
+ setClickCount(clickCountRef.current);
2466
+ selectedRef.current = i;
2467
+ setSelected(i);
2468
+ setMousePos({ x: e.x, y: e.y });
2469
+ }
2470
+ },
2471
+ /* @__PURE__ */ React.createElement("text", { style: { fg: color, bold: i === selected } }, i === selected ? `\u25B8 ${pointerColorNames[i]}` : ` ${pointerColorNames[i]}`)
2472
+ ))), /* @__PURE__ */ React.createElement("box", { height: 1 }), /* @__PURE__ */ React.createElement("box", { flexDirection: "row", gap: 2 }, /* @__PURE__ */ React.createElement(HoverBox, null), /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1, paddingTop: 1 }, /* @__PURE__ */ React.createElement("text", { style: { fg: selected !== null ? pointerColors[selected] : "#888" } }, selected !== null ? `Clicked ${pointerColorNames[selected]}` : "Click a color", clickCount > 0 ? ` (${clickCount} clicks)` : ""), /* @__PURE__ */ React.createElement("text", { style: { dim: true, fg: "#888" } }, mousePos ? `mouse: ${mousePos.x}, ${mousePos.y}` : ""))), /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(StatusBar, { items: [
2473
+ { key: "click", label: "select" },
2474
+ { key: "\u2190\u2192", label: "keyboard nav" }
2475
+ ] })));
2476
+ }
2477
+
2478
+ // demos/cursor-highlight.tsx
2479
+ import { useState as useState23 } from "react";
2480
+ function CursorHighlightApp() {
2481
+ const [pos, setPos] = useState23(null);
2482
+ return /* @__PURE__ */ React.createElement(
2483
+ "box",
2484
+ {
2485
+ flexDirection: "column",
2486
+ flexGrow: 1,
2487
+ onMouseMove: (e) => {
2488
+ setPos({ x: e.x, y: e.y });
2489
+ }
2490
+ },
2491
+ /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1, padding: 1 }, /* @__PURE__ */ React.createElement("text", { style: { bold: true, fg: "#fff" } }, "Cursor Highlight"), /* @__PURE__ */ React.createElement("text", { style: { dim: true, fg: "#888" } }, "Move your mouse over the grid"), /* @__PURE__ */ React.createElement("box", { height: 1 }), /* @__PURE__ */ React.createElement("box", { flexDirection: "column" }, Array.from({ length: 6 }, (_, row) => /* @__PURE__ */ React.createElement("text", { key: row }, Array.from({ length: 40 }, (_2, col) => {
2492
+ const isEven = (row + col) % 2 === 0;
2493
+ return /* @__PURE__ */ React.createElement("span", { key: col, style: {
2494
+ fg: isEven ? "#3b82f6" : "#8b5cf6",
2495
+ dim: !isEven
2496
+ } }, isEven ? "\u2591\u2591" : "\u2593\u2593");
2497
+ }))))),
2498
+ /* @__PURE__ */ React.createElement("box", { paddingX: 1, paddingBottom: 1 }, /* @__PURE__ */ React.createElement(
2499
+ StatusBar,
2500
+ {
2501
+ items: [],
2502
+ extra: /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ bold: true, fg: "#1e1e2e", bg: "#888" }) }, " x "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ dim: true, fg: "#888" }) }, ` ${pos ? String(pos.x).padStart(3) : " -"} `), /* @__PURE__ */ React.createElement("span", { style: textStyle({ bold: true, fg: "#1e1e2e", bg: "#888" }) }, " y "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ dim: true, fg: "#888" }) }, ` ${pos ? String(pos.y).padStart(3) : " -"}`))
2503
+ }
2504
+ ))
2505
+ );
2506
+ }
2507
+
2508
+ // demos/text-style.tsx
2509
+ function TextStyleApp() {
2510
+ const theme = useTheme();
2511
+ const desc = textStyle({ fg: theme.muted });
2512
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", padding: 1, gap: 0 }, /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.foreground, bold: true }) }, "bold "), /* @__PURE__ */ React.createElement("span", { style: desc }, "textStyle(", "{", " bold: true ", "}", ")")), /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.foreground, dim: true }) }, "dim "), /* @__PURE__ */ React.createElement("span", { style: desc }, "textStyle(", "{", " dim: true ", "}", ")")), /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.foreground, italic: true }) }, "italic "), /* @__PURE__ */ React.createElement("span", { style: desc }, "textStyle(", "{", " italic: true ", "}", ")")), /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.foreground, underline: true }) }, "underline "), /* @__PURE__ */ React.createElement("span", { style: desc }, "textStyle(", "{", " underline: true ", "}", ")")), /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ inverse: true }) }, "inverse "), /* @__PURE__ */ React.createElement("span", { style: desc }, "textStyle(", "{", " inverse: true ", "}", ")")), /* @__PURE__ */ React.createElement("text", null, " "), /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.primary }) }, "fg color "), /* @__PURE__ */ React.createElement("span", { style: desc }, "textStyle(", "{", " fg: theme.primary ", "}", ")")), /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.foreground, bg: theme.secondary }) }, "bg color "), /* @__PURE__ */ React.createElement("span", { style: desc }, "textStyle(", "{", " fg: theme.foreground, bg: theme.secondary ", "}", ")")), /* @__PURE__ */ React.createElement("text", null, " "), /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.accent, bold: true, underline: true }) }, "combined "), /* @__PURE__ */ React.createElement("span", { style: desc }, "textStyle(", "{", " fg: theme.accent, bold: true, underline: true ", "}", ")")));
2513
+ }
2514
+
2515
+ // demos/headless.tsx
2516
+ var data = [
2517
+ { name: "Alice", role: "Engineer", status: "Active" },
2518
+ { name: "Bob", role: "Designer", status: "Active" },
2519
+ { name: "Charlie", role: "PM", status: "Away" }
2520
+ ];
2521
+ function HeadlessApp() {
2522
+ return /* @__PURE__ */ React.createElement("box", { padding: 1 }, /* @__PURE__ */ React.createElement(Table, { data }));
2523
+ }
2524
+
2525
+ // demos/theming.tsx
2526
+ import { useKeyboard as useKeyboard17 } from "@gridland/utils";
2527
+ var tableData = [
2528
+ { name: "Alice", role: "Engineer", status: "Active" },
2529
+ { name: "Bob", role: "Designer", status: "Away" }
2530
+ ];
2531
+ var selectItems = [
2532
+ { label: "TypeScript", value: "ts" },
2533
+ { label: "JavaScript", value: "js" },
2534
+ { label: "Python", value: "py" }
2535
+ ];
2536
+ function ThemingApp() {
2537
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1 }, /* @__PURE__ */ React.createElement(Spinner, { text: "Loading data..." }), /* @__PURE__ */ React.createElement(Table, { data: tableData }), /* @__PURE__ */ React.createElement(MultiSelect, { items: selectItems, useKeyboard: useKeyboard17 }));
2538
+ }
2539
+
2540
+ // src/landing/landing-app.tsx
2541
+ import { useMemo as useMemo7 } from "react";
2542
+
2543
+ // src/landing/install-box.tsx
2544
+ function InstallBox() {
2545
+ const theme = useTheme();
2546
+ return /* @__PURE__ */ React.createElement(
2547
+ "box",
2548
+ {
2549
+ border: true,
2550
+ borderStyle: "rounded",
2551
+ borderColor: theme.border,
2552
+ paddingX: 1,
2553
+ flexDirection: "column",
2554
+ flexShrink: 0
2555
+ },
2556
+ /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ dim: true }) }, "$ "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ bold: true }) }, "bun create "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.accent }) }, "gridland"))
2557
+ );
2558
+ }
2559
+
2560
+ // src/landing/links-box.tsx
2561
+ var UNDERLINE3 = 1 << 3;
2562
+ function LinksBox() {
2563
+ const theme = useTheme();
2564
+ return /* @__PURE__ */ React.createElement(
2565
+ "box",
2566
+ {
2567
+ border: true,
2568
+ borderStyle: "rounded",
2569
+ borderColor: theme.border,
2570
+ paddingX: 1,
2571
+ flexDirection: "column",
2572
+ flexShrink: 0
2573
+ },
2574
+ /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", null, "\u{1F431}"), /* @__PURE__ */ React.createElement("a", { href: "https://github.com/thoughtfulllc/gridland", style: { attributes: UNDERLINE3, fg: theme.accent } }, " GitHub"), /* @__PURE__ */ React.createElement("span", null, " "), /* @__PURE__ */ React.createElement("span", null, "\u{1F4D6}"), /* @__PURE__ */ React.createElement("a", { href: "https://gridland.io/docs", style: { attributes: UNDERLINE3, fg: theme.accent } }, " Docs"))
2575
+ );
2576
+ }
2577
+
2578
+ // src/landing/logo.tsx
2579
+ import { useState as useState24, useEffect as useEffect6, useRef as useRef10, useMemo as useMemo5 } from "react";
2580
+ import figlet3 from "figlet";
2581
+ import ansiShadow3 from "figlet/importable-fonts/ANSI Shadow.js";
2582
+ figlet3.parseFont("ANSI Shadow", ansiShadow3);
2583
+ function makeArt(text) {
2584
+ return figlet3.textSync(text, { font: "ANSI Shadow" }).split("\n").filter((l) => l.trimEnd().length > 0).join("\n");
2585
+ }
2586
+ var fullArt = makeArt("gridland");
2587
+ var gridArt = makeArt("grid");
1822
2588
  var landArt = makeArt("land");
1823
2589
  var ART_HEIGHT = 6;
1824
2590
  function useAnimation(duration = 1e3) {
1825
2591
  const isBrowser = typeof document !== "undefined";
1826
- const [progress, setProgress] = useState8(isBrowser ? 0 : 1);
1827
- const startTime = useRef5(null);
1828
- useEffect3(() => {
2592
+ const [progress, setProgress] = useState24(isBrowser ? 0 : 1);
2593
+ const startTime = useRef10(null);
2594
+ useEffect6(() => {
1829
2595
  if (!isBrowser) return;
1830
2596
  let raf;
1831
2597
  const tick = (time) => {
@@ -1843,11 +2609,11 @@ function useAnimation(duration = 1e3) {
1843
2609
  }
1844
2610
  function RevealGradient({ children, revealCol }) {
1845
2611
  const gradientColors = GRADIENTS.instagram;
1846
- const lines = children.split("\n");
1847
- const maxLength = Math.max(...lines.map((l) => l.length));
1848
- if (maxLength === 0) return /* @__PURE__ */ jsx24("text", { children });
2612
+ const lines2 = children.split("\n");
2613
+ const maxLength = Math.max(...lines2.map((l) => l.length));
2614
+ if (maxLength === 0) return /* @__PURE__ */ React.createElement("text", null, children);
1849
2615
  const hexColors = useMemo5(() => generateGradient(gradientColors, maxLength), [maxLength]);
1850
- return /* @__PURE__ */ jsx24("box", { position: "relative", width: maxLength, height: lines.length, shouldFill: false, children: lines.map((line, lineIndex) => {
2616
+ return /* @__PURE__ */ React.createElement("box", { position: "relative", width: maxLength, height: lines2.length, shouldFill: false }, lines2.map((line, lineIndex) => {
1851
2617
  const runs = [];
1852
2618
  let current = null;
1853
2619
  for (let i = 0; i < line.length; i++) {
@@ -1867,25 +2633,25 @@ function RevealGradient({ children, revealCol }) {
1867
2633
  }
1868
2634
  }
1869
2635
  if (current) runs.push(current);
1870
- return runs.map((run, runIndex) => /* @__PURE__ */ jsx24(
2636
+ return runs.map((run, runIndex) => /* @__PURE__ */ React.createElement(
1871
2637
  "box",
1872
2638
  {
2639
+ key: `${lineIndex}-${runIndex}`,
1873
2640
  position: "absolute",
1874
2641
  top: lineIndex,
1875
2642
  left: run.start,
1876
- shouldFill: false,
1877
- children: /* @__PURE__ */ jsx24("text", { shouldFill: false, children: run.chars.map((char, ci) => /* @__PURE__ */ jsx24(
1878
- "span",
1879
- {
1880
- style: { fg: hexColors[run.start + ci] },
1881
- children: char
1882
- },
1883
- ci
1884
- )) })
2643
+ shouldFill: false
1885
2644
  },
1886
- `${lineIndex}-${runIndex}`
2645
+ /* @__PURE__ */ React.createElement("text", { shouldFill: false }, run.chars.map((char, ci) => /* @__PURE__ */ React.createElement(
2646
+ "span",
2647
+ {
2648
+ key: ci,
2649
+ style: { fg: hexColors[run.start + ci] }
2650
+ },
2651
+ char
2652
+ )))
1887
2653
  ));
1888
- }) });
2654
+ }));
1889
2655
  }
1890
2656
  function Logo({ compact, narrow, mobile }) {
1891
2657
  const isBrowser = typeof document !== "undefined";
@@ -1896,52 +2662,25 @@ function Logo({ compact, narrow, mobile }) {
1896
2662
  const maxWidth = compact ? 8 : narrow ? 40 : 62;
1897
2663
  const revealCol = Math.round(revealProgress * (maxWidth + 4)) - 2;
1898
2664
  const taglineOpacity = Math.max(0, Math.min(1, (progress - 0.7) / 0.3));
1899
- const subtitle = /* @__PURE__ */ jsxs18(Fragment7, { children: [
1900
- /* @__PURE__ */ jsx24("text", { children: " " }),
1901
- /* @__PURE__ */ jsx24("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs18("text", { style: textStyle({ fg: "#d4b0e8" }), opacity: taglineOpacity, wrapMode: "word", textAlign: "center", width: "100%", shouldFill: false, children: [
1902
- "A framework for building terminal apps, built on ",
1903
- /* @__PURE__ */ jsx24("a", { href: "https://opentui.com", style: { attributes: 72, fg: "#d4b0e8" }, children: "OpenTUI" }),
1904
- " + React." + (mobile ? " " : "\n") + "(Gridland apps, like this website, work in the browser and terminal.)"
1905
- ] }) })
1906
- ] });
2665
+ const subtitle = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("text", null, " "), /* @__PURE__ */ React.createElement("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false }, /* @__PURE__ */ React.createElement("text", { style: textStyle({ fg: "#d4b0e8" }), opacity: taglineOpacity, wrapMode: "word", textAlign: "center", width: "100%", shouldFill: false }, "A framework for building terminal apps, built on ", /* @__PURE__ */ React.createElement("a", { href: "https://opentui.com", style: { attributes: 72, fg: "#d4b0e8" } }, "OpenTUI"), " + React." + (mobile ? " " : "\n") + "(Gridland apps, like this website, work in the browser and terminal.)")));
1907
2666
  if (!isBrowser) {
1908
- const art = compact ? "gridland" : narrow ? gridArt + "\n" + landArt : fullArt;
1909
- return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexShrink: 0, width: "100%", alignItems: "center", children: [
1910
- /* @__PURE__ */ jsx24(Gradient, { name: "instagram", children: art }),
1911
- /* @__PURE__ */ jsx24("text", { children: " " }),
1912
- /* @__PURE__ */ jsx24("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs18("text", { style: textStyle({ fg: "#d4b0e8" }), shouldFill: false, children: [
1913
- "A framework for building terminal apps, built on OpenTUI + React.",
1914
- "\n",
1915
- "(Gridland apps, like this website, work in the browser and terminal.)"
1916
- ] }) })
1917
- ] });
2667
+ const art2 = compact ? "gridland" : narrow ? gridArt + "\n" + landArt : fullArt;
2668
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexShrink: 0, width: "100%", alignItems: "center", shouldFill: false }, /* @__PURE__ */ React.createElement(Gradient, { name: "instagram" }, art2), /* @__PURE__ */ React.createElement("text", null, " "), /* @__PURE__ */ React.createElement("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false }, /* @__PURE__ */ React.createElement("text", { style: textStyle({ fg: "#d4b0e8" }), shouldFill: false }, "A framework for building terminal apps, built on OpenTUI + React.", "\n", "(Gridland apps, like this website, work in the browser and terminal.)")));
1918
2669
  }
1919
2670
  if (compact) {
1920
- return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
1921
- /* @__PURE__ */ jsx24("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx24("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx24(RevealGradient, { revealCol, children: "gridland" }) }) }),
1922
- subtitle
1923
- ] });
2671
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false }, /* @__PURE__ */ React.createElement("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false }, /* @__PURE__ */ React.createElement("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false }, /* @__PURE__ */ React.createElement(RevealGradient, { revealCol }, "gridland"))), subtitle);
1924
2672
  }
1925
2673
  if (narrow) {
1926
- return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
1927
- /* @__PURE__ */ jsx24("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsxs18("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: [
1928
- /* @__PURE__ */ jsx24(RevealGradient, { revealCol, children: gridArt }),
1929
- /* @__PURE__ */ jsx24(RevealGradient, { revealCol, children: landArt })
1930
- ] }) }),
1931
- subtitle
1932
- ] });
2674
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false }, /* @__PURE__ */ React.createElement("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false }, /* @__PURE__ */ React.createElement("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false }, /* @__PURE__ */ React.createElement(RevealGradient, { revealCol }, gridArt), /* @__PURE__ */ React.createElement(RevealGradient, { revealCol }, landArt))), subtitle);
1933
2675
  }
1934
- return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
1935
- /* @__PURE__ */ jsx24("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx24("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx24(RevealGradient, { revealCol, children: fullArt }) }) }),
1936
- subtitle
1937
- ] });
2676
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false }, /* @__PURE__ */ React.createElement("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false }, /* @__PURE__ */ React.createElement("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false }, /* @__PURE__ */ React.createElement(RevealGradient, { revealCol }, fullArt))), subtitle);
1938
2677
  }
1939
2678
 
1940
- // ../docs/components/landing/matrix-background.tsx
2679
+ // src/landing/matrix-background.tsx
1941
2680
  import { useMemo as useMemo6 } from "react";
1942
2681
 
1943
- // ../docs/components/landing/use-matrix.ts
1944
- import { useState as useState9, useEffect as useEffect4, useRef as useRef6 } from "react";
2682
+ // src/landing/use-matrix.ts
2683
+ import { useState as useState25, useEffect as useEffect7, useRef as useRef11 } from "react";
1945
2684
  var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789@#$%^&*(){}[]|;:<>,.?/~`";
1946
2685
  function randomChar() {
1947
2686
  return CHARS[Math.floor(Math.random() * CHARS.length)];
@@ -1975,8 +2714,8 @@ function buildGrid(columns, width, height) {
1975
2714
  return { grid, brightness };
1976
2715
  }
1977
2716
  function useMatrix(width, height) {
1978
- const columnsRef = useRef6([]);
1979
- const [state, setState] = useState9(() => {
2717
+ const columnsRef = useRef11([]);
2718
+ const [state, setState] = useState25(() => {
1980
2719
  const columns = Array.from(
1981
2720
  { length: width },
1982
2721
  () => Math.random() < 0.5 ? createDrop(height, true) : null
@@ -1984,7 +2723,7 @@ function useMatrix(width, height) {
1984
2723
  columnsRef.current = columns;
1985
2724
  return buildGrid(columns, width, height);
1986
2725
  });
1987
- useEffect4(() => {
2726
+ useEffect7(() => {
1988
2727
  if (width < 2 || height < 2) return;
1989
2728
  const id = setInterval(() => {
1990
2729
  const columns = columnsRef.current;
@@ -2009,7 +2748,7 @@ function useMatrix(width, height) {
2009
2748
  }, 80);
2010
2749
  return () => clearInterval(id);
2011
2750
  }, [width, height]);
2012
- useEffect4(() => {
2751
+ useEffect7(() => {
2013
2752
  columnsRef.current = Array.from(
2014
2753
  { length: width },
2015
2754
  () => Math.random() < 0.5 ? createDrop(height, true) : null
@@ -2019,8 +2758,7 @@ function useMatrix(width, height) {
2019
2758
  return state;
2020
2759
  }
2021
2760
 
2022
- // ../docs/components/landing/matrix-background.tsx
2023
- import { jsx as jsx25 } from "react/jsx-runtime";
2761
+ // src/landing/matrix-background.tsx
2024
2762
  var MUTE_LEVELS = [0.12, 0.18, 0.24, 0.3, 0.38];
2025
2763
  var BG = hexToRgb("#1a1a2e");
2026
2764
  function buildMutedColors(baseHex) {
@@ -2047,726 +2785,99 @@ function MatrixBackground({ width, height, clearRect, clearRects }) {
2047
2785
  () => columnColors.map(buildMutedColors),
2048
2786
  [columnColors]
2049
2787
  );
2050
- return /* @__PURE__ */ jsx25("box", { flexDirection: "column", children: grid.map((row, y) => /* @__PURE__ */ jsx25("text", { children: row.map((cell, x) => {
2788
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column" }, grid.map((row, y) => /* @__PURE__ */ React.createElement("text", { key: y }, row.map((cell, x) => {
2051
2789
  const inClearRect = clearRect && y >= clearRect.top && y < clearRect.top + clearRect.height && x >= clearRect.left && x < clearRect.left + clearRect.width || clearRects && clearRects.some(
2052
2790
  (r) => y >= r.top && y < r.top + r.height && x >= r.left && x < r.left + r.width
2053
2791
  );
2054
2792
  const mutedColors = columnMutedColors[x];
2055
2793
  if (cell === " " || inClearRect || !mutedColors) {
2056
- return /* @__PURE__ */ jsx25("span", { children: " " }, x);
2794
+ return /* @__PURE__ */ React.createElement("span", { key: x }, " ");
2057
2795
  }
2058
- return /* @__PURE__ */ jsx25(
2796
+ return /* @__PURE__ */ React.createElement(
2059
2797
  "span",
2060
2798
  {
2799
+ key: x,
2061
2800
  style: {
2062
2801
  fg: colorForCell(mutedColors, brightness[y][x])
2063
- },
2064
- children: cell
2802
+ }
2065
2803
  },
2066
- x
2804
+ cell
2067
2805
  );
2068
- }) }, y)) });
2806
+ }))));
2069
2807
  }
2070
2808
 
2071
- // ../docs/components/landing/landing-app.tsx
2072
- import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
2073
- var DEMO_RESPONSES = [
2074
- "Gridland is a framework for building terminal apps with React. It works in both the browser and terminal!",
2075
- "You can get started with `bun create gridland` to scaffold a new project.",
2076
- "OpenTUI provides the layout primitives \u2014 flexbox, borders, text styling \u2014 while React handles the component model.",
2077
- "Yes! Gridland apps are universal \u2014 the same code renders in a terminal emulator and in the browser.",
2078
- "Check out the docs for examples of interactive components like inputs, selects, and tables."
2079
- ];
2080
- function LandingApp({ useKeyboard: useKeyboard3 }) {
2809
+ // src/landing/landing-app.tsx
2810
+ function LandingApp({ useKeyboard: useKeyboard20 }) {
2081
2811
  const theme = useTheme();
2082
2812
  const { width, height, isNarrow, isTiny, isMobile } = useBreakpoints();
2083
- const [showAbout, setShowAbout] = useState10(false);
2084
- const [messages, setMessages] = useState10([]);
2085
- const [chatStatus, setChatStatus] = useState10("ready");
2086
- const responseIdx = useRef7(0);
2087
- const handleChatSubmit = useCallback3(({ text }) => {
2088
- const userMsg = { id: `u-${Date.now()}`, role: "user", content: text };
2089
- setMessages((prev) => [...prev, userMsg]);
2090
- setChatStatus("streaming");
2091
- setTimeout(() => {
2092
- const response = DEMO_RESPONSES[responseIdx.current % DEMO_RESPONSES.length];
2093
- responseIdx.current += 1;
2094
- const assistantMsg = { id: `a-${Date.now()}`, role: "assistant", content: response };
2095
- setMessages((prev) => [...prev, assistantMsg]);
2096
- setChatStatus("ready");
2097
- }, 1200);
2098
- }, []);
2099
- useKeyboard3((event) => {
2100
- if (event.name === "a" && !showAbout) {
2101
- setShowAbout(true);
2102
- }
2103
- if (event.name === "q" && showAbout) {
2104
- setShowAbout(false);
2105
- }
2106
- });
2107
- if (showAbout) {
2108
- return /* @__PURE__ */ jsxs19("box", { flexDirection: "column", width: "100%", height: "100%", children: [
2109
- /* @__PURE__ */ jsx26("box", { flexGrow: 1, children: /* @__PURE__ */ jsx26(AboutModal, { onClose: () => setShowAbout(false), useKeyboard: useKeyboard3 }) }),
2110
- /* @__PURE__ */ jsx26(StatusBar, { items: [{ key: "q", label: "close" }] })
2111
- ] });
2112
- }
2113
2813
  const isBrowser = typeof document !== "undefined";
2114
- const logoHeight = isTiny ? 2 : isNarrow ? 13 : 7;
2115
- const logoExtra = isBrowser ? 1 : 0;
2116
- const gap = isMobile ? 0 : 1;
2117
- const installLinksTop = 3 + logoHeight + logoExtra + gap;
2118
- const installLinksHeight = 3;
2119
- const boxTop = installLinksTop + installLinksHeight + gap + 1;
2120
- const boxHeight = height - boxTop - 1 - 1;
2121
- const clearRect = { top: boxTop, left: 1, width: width - 2, height: boxHeight };
2122
- const installLinksClearRect = { top: installLinksTop, left: 1, width: width - 2, height: installLinksHeight };
2123
- return /* @__PURE__ */ jsxs19("box", { width: "100%", height: "100%", position: "relative", children: [
2124
- /* @__PURE__ */ jsx26(MatrixBackground, { width, height, clearRect, clearRects: [installLinksClearRect] }),
2125
- /* @__PURE__ */ jsxs19("box", { position: "absolute", top: 0, left: 0, width, height, zIndex: 1, flexDirection: "column", shouldFill: false, children: [
2126
- /* @__PURE__ */ jsxs19("box", { flexGrow: 1, flexDirection: "column", paddingTop: 3, paddingLeft: 1, paddingRight: 1, paddingBottom: 1, gap: isMobile ? 0 : 1, shouldFill: false, children: [
2127
- /* @__PURE__ */ jsx26("box", { flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx26(Logo, { compact: isTiny, narrow: isNarrow, mobile: isMobile }) }),
2128
- /* @__PURE__ */ jsxs19("box", { flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: isMobile ? 0 : 1, flexShrink: 0, shouldFill: false, children: [
2129
- /* @__PURE__ */ jsx26("box", { border: true, borderStyle: "rounded", borderColor: theme.border, paddingX: 1, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsxs19("text", { children: [
2130
- /* @__PURE__ */ jsx26("span", { style: textStyle({ dim: true }), children: "$ " }),
2131
- /* @__PURE__ */ jsx26("span", { style: textStyle({ bold: true }), children: "bunx " }),
2132
- /* @__PURE__ */ jsx26("span", { style: textStyle({ fg: theme.accent }), children: "@gridland/demo landing" })
2133
- ] }) }),
2134
- /* @__PURE__ */ jsx26(InstallBox, {}),
2135
- /* @__PURE__ */ jsx26(LinksBox, {})
2136
- ] }),
2137
- /* @__PURE__ */ jsxs19("box", { flexGrow: 1, border: true, borderStyle: "rounded", borderColor: theme.border, flexDirection: "column", overflow: "hidden", children: [
2138
- /* @__PURE__ */ jsx26("box", { flexGrow: 1, flexDirection: "column", paddingX: 1, overflow: "hidden", children: messages.map((msg) => /* @__PURE__ */ jsx26(Message, { role: msg.role, children: /* @__PURE__ */ jsx26(Message.Content, { children: /* @__PURE__ */ jsx26(Message.Text, { children: msg.content }) }) }, msg.id)) }),
2139
- /* @__PURE__ */ jsx26("box", { flexShrink: 0, paddingX: 1, paddingBottom: 0, children: /* @__PURE__ */ jsx26(PromptInput, { splaceholder: "Ask about Gridland...", status: chatStatus, onSubmit: handleChatSubmit, useKeyboard: useKeyboard3 }) })
2140
- ] })
2141
- ] }),
2142
- /* @__PURE__ */ jsx26(StatusBar, { items: [{ key: "a", label: "about" }] })
2143
- ] })
2144
- ] });
2145
- }
2146
-
2147
- // ../docs/components/landing/matrix-rain.tsx
2148
- import { jsx as jsx27 } from "react/jsx-runtime";
2149
-
2150
- // ../ui/scripts/demo-apps.tsx
2151
- import figlet2 from "figlet";
2152
- import ansiShadow2 from "figlet/importable-fonts/ANSI Shadow.js";
2153
- import big from "figlet/importable-fonts/Big.js";
2154
- import doom from "figlet/importable-fonts/Doom.js";
2155
- import slant from "figlet/importable-fonts/Slant.js";
2156
- import speed from "figlet/importable-fonts/Speed.js";
2157
- import standard from "figlet/importable-fonts/Standard.js";
2158
- import block from "figlet/importable-fonts/Block.js";
2159
- import colossal from "figlet/importable-fonts/Colossal.js";
2160
- import { jsx as jsx28, jsxs as jsxs20 } from "react/jsx-runtime";
2161
- var fonts = [
2162
- { name: "ANSI Shadow", data: ansiShadow2 },
2163
- { name: "Standard", data: standard },
2164
- { name: "Big", data: big },
2165
- { name: "Doom", data: doom },
2166
- { name: "Slant", data: slant },
2167
- { name: "Speed", data: speed },
2168
- { name: "Block", data: block },
2169
- { name: "Colossal", data: colossal }
2170
- ];
2171
- for (const f of fonts) {
2172
- figlet2.parseFont(f.name, f.data);
2173
- }
2174
- function getFigletLines(fontName, text = "gridland") {
2175
- const art = figlet2.textSync(text, { font: fontName });
2176
- return art.split("\n").filter((l) => l.trimEnd().length > 0);
2177
- }
2178
- var gradientNames = Object.keys(GRADIENTS);
2179
- function GradientApp() {
2180
- const [index, setIndex] = useState11(0);
2181
- const name = gradientNames[index];
2182
- const lines = getFigletLines("ANSI Shadow");
2183
- useKeyboard((event) => {
2184
- if (event.name === "left") setIndex((i) => i > 0 ? i - 1 : gradientNames.length - 1);
2185
- if (event.name === "right") setIndex((i) => i < gradientNames.length - 1 ? i + 1 : 0);
2186
- });
2187
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2188
- /* @__PURE__ */ jsx28("box", { padding: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: /* @__PURE__ */ jsx28(Gradient, { name, children: lines.join("\n") }) }),
2189
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(
2190
- StatusBar,
2191
- {
2192
- items: [{ key: "\u2190\u2192", label: "gradient" }, { key: "q", label: "quit" }],
2193
- extra: /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "cyan", bold: true }), children: name.padEnd(11) })
2194
- }
2195
- ) })
2196
- ] });
2197
- }
2198
- function AsciiApp() {
2199
- const [fontIndex, setFontIndex] = useState11(0);
2200
- const font = fonts[fontIndex];
2201
- const lines = getFigletLines(font.name);
2202
- useKeyboard((event) => {
2203
- if (event.name === "left") setFontIndex((i) => i > 0 ? i - 1 : fonts.length - 1);
2204
- if (event.name === "right") setFontIndex((i) => i < fonts.length - 1 ? i + 1 : 0);
2205
- });
2206
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2207
- /* @__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)) }),
2208
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(
2209
- StatusBar,
2210
- {
2211
- items: [{ key: "\u2190\u2192", label: "change font" }, { key: "q", label: "quit" }],
2212
- extra: /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "cyan", bold: true }), children: font.name.padEnd(11) })
2213
- }
2214
- ) })
2215
- ] });
2216
- }
2217
- function TableApp() {
2218
- const data = [
2219
- { name: "Alice", role: "Engineer", status: "Active" },
2220
- { name: "Bob", role: "Designer", status: "Active" },
2221
- { name: "Charlie", role: "PM", status: "Away" }
2222
- ];
2223
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2224
- /* @__PURE__ */ jsx28("box", { padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28(Table, { data, headerColor: "cyan", borderColor: "#5e81ac" }) }),
2225
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2226
- ] });
2227
- }
2228
- function SpinnerApp() {
2229
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2230
- /* @__PURE__ */ jsx28("box", { flexGrow: 1, children: /* @__PURE__ */ jsx28(SpinnerPicker, { useKeyboard }) }),
2231
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "\u2190\u2192", label: "change variant" }, { key: "q", label: "quit" }] }) })
2232
- ] });
2233
- }
2234
- function SelectInputApp() {
2235
- const [submitted, setSubmitted] = useState11(false);
2236
- const items = [
2237
- { label: "TypeScript", value: "ts" },
2238
- { label: "JavaScript", value: "js" },
2239
- { label: "Python", value: "py" },
2240
- { label: "Rust", value: "rs" }
2241
- ];
2242
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2243
- /* @__PURE__ */ jsx28("box", { padding: 1, flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx28(SelectInput, { items, title: "Choose a language", useKeyboard, onSubmit: () => setSubmitted(true) }) }),
2244
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: submitted ? [{ key: "q", label: "quit" }] : [
2245
- { key: "\u2191\u2193", label: "select" },
2246
- { key: "enter", label: "submit" },
2247
- { key: "q", label: "quit" }
2248
- ] }) })
2249
- ] });
2250
- }
2251
- function MultiSelectApp() {
2252
- const [submitted, setSubmitted] = useState11(false);
2253
- const items = [
2254
- { label: "TypeScript", value: "ts" },
2255
- { label: "JavaScript", value: "js" },
2256
- { label: "Python", value: "py" },
2257
- { label: "Rust", value: "rs" }
2258
- ];
2259
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2260
- /* @__PURE__ */ jsx28("box", { padding: 1, flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx28(MultiSelect, { items, title: "Select languages", useKeyboard, onSubmit: () => setSubmitted(true) }) }),
2261
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: submitted ? [{ key: "q", label: "quit" }] : [
2262
- { key: "\u2191\u2193", label: "move" },
2263
- { key: "enter", label: "select" },
2264
- { key: "a", label: "all" },
2265
- { key: "x", label: "clear" },
2266
- { key: "q", label: "quit" }
2267
- ] }) })
2268
- ] });
2269
- }
2270
- function PromptInputApp() {
2271
- const [lastMessage, setLastMessage] = useState11("");
2272
- const [model, setModel] = useState11("opus");
2273
- const [showModelPicker, setShowModelPicker] = useState11(false);
2274
- const [resetKey, setResetKey] = useState11(0);
2275
- const commands = [
2276
- { cmd: "/help", desc: "Show commands" },
2277
- { cmd: "/model", desc: "Switch model" },
2278
- { cmd: "/clear", desc: "Clear conversation" }
2279
- ];
2280
- const files = ["src/index.ts", "src/routes.ts", "src/auth.ts", "package.json"];
2281
- const models = [
2282
- { label: "Claude Opus", value: "opus" },
2283
- { label: "Claude Sonnet", value: "sonnet" },
2284
- { label: "Claude Haiku", value: "haiku" }
2285
- ];
2286
- const handleSubmit = (msg) => {
2287
- if (msg.text === "/model") {
2288
- setShowModelPicker(true);
2289
- setResetKey((k) => k + 1);
2290
- return;
2291
- }
2292
- if (msg.text === "/clear") {
2293
- setLastMessage("");
2294
- setResetKey((k) => k + 1);
2295
- return;
2296
- }
2297
- setLastMessage(msg.text);
2298
- };
2299
- if (showModelPicker) {
2300
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2301
- /* @__PURE__ */ jsx28(
2302
- Modal,
2303
- {
2304
- title: "Select Model",
2305
- useKeyboard,
2306
- onClose: () => setShowModelPicker(false),
2307
- children: /* @__PURE__ */ jsx28("box", { paddingX: 1, children: /* @__PURE__ */ jsx28(
2308
- SelectInput,
2309
- {
2310
- items: models,
2311
- defaultValue: model,
2312
- useKeyboard,
2313
- onSubmit: (value) => {
2314
- setModel(value);
2315
- setShowModelPicker(false);
2316
- }
2317
- }
2318
- ) })
2319
- }
2320
- ),
2321
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [
2322
- { key: "\u23CE", label: "select" },
2323
- { key: "esc", label: "cancel" },
2324
- { key: "q", label: "quit" }
2325
- ] }) })
2326
- ] });
2327
- }
2328
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2329
- /* @__PURE__ */ jsx28("box", { padding: 1, flexDirection: "column", flexGrow: 1, children: lastMessage ? /* @__PURE__ */ jsxs20("text", { children: [
2330
- /* @__PURE__ */ jsx28("span", { children: "Sent: " }),
2331
- /* @__PURE__ */ jsx28("span", { children: lastMessage })
2332
- ] }) : /* @__PURE__ */ jsx28("text", { style: textStyle({ dim: true }), children: "Type a message and press enter" }) }),
2333
- /* @__PURE__ */ jsx28("box", { paddingX: 1, children: /* @__PURE__ */ jsx28(
2334
- PromptInput,
2335
- {
2336
- commands,
2337
- files,
2338
- placeholder: "Message Claude...",
2339
- showDividers: true,
2340
- useKeyboard,
2341
- onSubmit: handleSubmit
2342
- },
2343
- resetKey
2344
- ) }),
2345
- /* @__PURE__ */ jsx28("box", { paddingX: 1, children: /* @__PURE__ */ jsxs20("text", { children: [
2346
- /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "#C4A8FF" }), children: "[\u22A1_\u22A1]" }),
2347
- /* @__PURE__ */ jsx28("span", { style: textStyle({ dim: true }), children: " " + model })
2348
- ] }) }),
2349
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [
2350
- { key: "\u23CE", label: "send" },
2351
- { key: "/", label: "commands" },
2352
- { key: "@", label: "files" },
2353
- { key: "\u2191", label: "history" },
2354
- { key: "q", label: "quit" }
2355
- ] }) })
2356
- ] });
2357
- }
2358
- var TEXT_INPUT_FIELDS = [
2359
- { label: "Username", placeholder: "enter your name", maxLength: 30, required: true },
2360
- { label: "Email", placeholder: "user@example.com", maxLength: 50, required: true, description: "We'll never share your email" },
2361
- { label: "Password", placeholder: "enter password", maxLength: 40 },
2362
- { label: "API Key", placeholder: "sk-...", maxLength: 60, disabled: true }
2363
- ];
2364
- function TextInputApp() {
2365
- const [activeField, setActiveField] = useState11(0);
2366
- const [values, setValues] = useState11(TEXT_INPUT_FIELDS.map(() => ""));
2367
- useKeyboard((event) => {
2368
- if (event.name === "up") setActiveField((i) => Math.max(0, i - 1));
2369
- if (event.name === "down" || event.name === "tab") setActiveField((i) => Math.min(TEXT_INPUT_FIELDS.length - 1, i + 1));
2370
- });
2371
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2372
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingTop: 1, children: /* @__PURE__ */ jsxs20("text", { children: [
2373
- /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "#FF71CE", bold: true }), children: "TextInput" }),
2374
- /* @__PURE__ */ jsx28("span", { style: textStyle({ dim: true }), children: " Form with multiple input types" })
2375
- ] }) }),
2376
- /* @__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(
2377
- TextInput,
2378
- {
2379
- label: field.label,
2380
- placeholder: field.placeholder,
2381
- prompt: "> ",
2382
- focus: i === activeField,
2383
- maxLength: field.maxLength,
2384
- value: values[i],
2385
- onChange: (v) => setValues((prev) => prev.map((old, j) => j === i ? v : old)),
2386
- required: field.required,
2387
- disabled: field.disabled,
2388
- description: field.description
2389
- }
2390
- ) }, field.label)) }),
2391
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [
2392
- { key: "\u2191\u2193", label: "field" },
2393
- { key: "\u2190\u2192", label: "cursor" },
2394
- { key: "tab", label: "complete" },
2395
- { key: "^k/^u", label: "kill" }
2396
- ] }) })
2397
- ] });
2398
- }
2399
- function LinkApp() {
2400
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2401
- /* @__PURE__ */ jsx28("box", { padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28(LinkDemo, { useKeyboard }) }),
2402
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2403
- ] });
2404
- }
2405
- function TabBarApp() {
2406
- const tabs = ["Files", "Search", "Git", "Debug"];
2407
- const [selectedIndex, setSelectedIndex] = useState11(0);
2408
- useKeyboard((event) => {
2409
- if (event.name === "left") setSelectedIndex((i) => i > 0 ? i - 1 : tabs.length - 1);
2410
- if (event.name === "right") setSelectedIndex((i) => i < tabs.length - 1 ? i + 1 : 0);
2411
- });
2412
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2413
- /* @__PURE__ */ jsxs20("box", { flexDirection: "column", gap: 1, padding: 1, flexGrow: 1, children: [
2414
- /* @__PURE__ */ jsx28(TabBar, { label: "View", options: tabs, selectedIndex }),
2415
- /* @__PURE__ */ jsx28("text", { style: textStyle({ dim: true }), children: "Use \u2190/\u2192 arrow keys to switch tabs" })
2416
- ] }),
2417
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "\u2190\u2192", label: "switch tab" }, { key: "q", label: "quit" }] }) })
2418
- ] });
2419
- }
2420
- function StatusBarApp() {
2421
- const shortcuts = [
2422
- { key: "Tab", label: "switch focus" },
2423
- { key: "\u2190\u2192", label: "cycle" },
2424
- { key: "b", label: "back" },
2425
- { key: "z", label: "reset" }
2426
- ];
2427
- const [lastKey, setLastKey] = useState11(null);
2428
- useKeyboard((event) => {
2429
- if (event.name === "tab") setLastKey("switch focus (Tab)");
2430
- else if (event.name === "left") setLastKey("cycle (\u2190)");
2431
- else if (event.name === "right") setLastKey("cycle (\u2192)");
2432
- else if (event.name === "b") setLastKey("back (b)");
2433
- else if (event.name === "z") setLastKey("reset (z)");
2434
- });
2435
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", gap: 1, padding: 1, children: [
2436
- lastKey ? /* @__PURE__ */ jsxs20("text", { children: [
2437
- /* @__PURE__ */ jsx28("span", { children: "Pressed: " }),
2438
- /* @__PURE__ */ jsx28("span", { style: textStyle({ bold: true, fg: "cyan" }), children: lastKey })
2439
- ] }) : /* @__PURE__ */ jsx28("text", { style: textStyle({ dim: true }), children: "Press a key to trigger an action" }),
2440
- /* @__PURE__ */ jsx28(
2441
- StatusBar,
2442
- {
2443
- items: [...shortcuts, { key: "q", label: "quit" }],
2444
- extra: /* @__PURE__ */ jsx28("span", { style: textStyle({ fg: "green" }), children: "\u25CF Ready" })
2445
- }
2446
- )
2447
- ] });
2448
- }
2449
- function ModalApp() {
2450
- const [isOpen, setIsOpen] = useState11(false);
2451
- useKeyboard((event) => {
2452
- if (!isOpen && event.name === "m") setIsOpen(true);
2453
- if (isOpen && (event.name === "q" || event.name === "escape")) setIsOpen(false);
2454
- });
2455
- if (isOpen) {
2456
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2457
- /* @__PURE__ */ jsx28(Modal, { title: "Example Modal", borderColor: "blue", useKeyboard, onClose: () => setIsOpen(false), children: /* @__PURE__ */ jsxs20("box", { paddingX: 1, flexDirection: "column", children: [
2458
- /* @__PURE__ */ jsx28("text", { children: "This is a modal overlay component." }),
2459
- /* @__PURE__ */ jsx28("text", { children: " " }),
2460
- /* @__PURE__ */ jsx28("text", { style: textStyle({ dim: true }), children: "It stretches to fill the full terminal height." })
2461
- ] }) }),
2462
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "close" }, { key: "Esc", label: "quit" }] }) })
2463
- ] });
2464
- }
2465
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2466
- /* @__PURE__ */ jsx28("box", { flexDirection: "column", flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs20("text", { children: [
2467
- /* @__PURE__ */ jsx28("span", { style: textStyle({ dim: true }), children: "Press " }),
2468
- /* @__PURE__ */ jsx28("span", { style: textStyle({ inverse: true, bold: true }), children: " m " }),
2469
- /* @__PURE__ */ jsx28("span", { style: textStyle({ dim: true }), children: " to open modal" })
2470
- ] }) }),
2471
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "m", label: "open modal" }, { key: "q", label: "quit" }] }) })
2472
- ] });
2473
- }
2474
- function PrimitivesApp() {
2475
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2476
- /* @__PURE__ */ jsxs20("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: [
2477
- /* @__PURE__ */ jsx28("box", { border: true, borderStyle: "rounded", borderColor: "#5e81ac", title: "Layout", titleAlignment: "center", padding: 1, children: /* @__PURE__ */ jsxs20("box", { flexDirection: "row", gap: 2, children: [
2478
- /* @__PURE__ */ jsx28("box", { border: true, borderStyle: "single", borderColor: "#a3be8c", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28("text", { fg: "#a3be8c", bold: true, children: "Box 1" }) }),
2479
- /* @__PURE__ */ jsx28("box", { border: true, borderStyle: "single", borderColor: "#ebcb8b", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28("text", { fg: "#ebcb8b", bold: true, children: "Box 2" }) }),
2480
- /* @__PURE__ */ jsx28("box", { border: true, borderStyle: "single", borderColor: "#b48ead", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28("text", { fg: "#b48ead", bold: true, children: "Box 3" }) })
2481
- ] }) }),
2482
- /* @__PURE__ */ jsx28("text", { fg: "#d8dee9", dim: true, children: " Nested boxes with borders, colors & flexbox layout" })
2483
- ] }),
2484
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2485
- ] });
2486
- }
2487
- function TerminalWindowApp() {
2488
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2489
- /* @__PURE__ */ jsxs20("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: [
2490
- /* @__PURE__ */ jsx28("text", { style: textStyle({ fg: "green" }), children: '$ echo "Hello from OpenTUI"' }),
2491
- /* @__PURE__ */ jsx28("text", { children: "Hello from OpenTUI" }),
2492
- /* @__PURE__ */ jsx28("text", { style: textStyle({ fg: "green" }), children: "$ _" })
2493
- ] }),
2494
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2495
- ] });
2496
- }
2497
- var COT_STEPS = [
2498
- { tool: "Read", label: "Reading codebase", description: "src/", status: "done", delay: 1800 },
2499
- { tool: "Think", label: "Planning changes", description: "auth module", status: "done", delay: 2500 },
2500
- { tool: "Edit", label: "Editing files", description: "4 files", status: "done", delay: 3200 },
2501
- { tool: "Bash", label: "Running tests", description: "vitest", status: "done", delay: 2e3 },
2502
- { tool: "Edit", label: "Fixing test", description: "routes.test.ts", status: "done", delay: 1500 }
2503
- ];
2504
- function ChainOfThoughtApp() {
2505
- const [expanded, setExpanded] = useState11(true);
2506
- const [phase, setPhase] = useState11("running");
2507
- const [stepIndex, setStepIndex] = useState11(0);
2508
- const timerRef = useRef8(null);
2509
- useKeyboard((event) => {
2510
- if (event.name === "E" && event.ctrl && event.shift) setExpanded((v) => !v);
2511
- if (event.name === "r") cotRestart();
2512
- });
2513
- function cotRestart() {
2514
- if (timerRef.current) clearTimeout(timerRef.current);
2515
- setPhase("running");
2516
- setStepIndex(0);
2517
- }
2518
- useEffect5(() => {
2519
- if (phase !== "running") return;
2520
- if (stepIndex < COT_STEPS.length) {
2521
- const delay = COT_STEPS[stepIndex].delay;
2522
- timerRef.current = setTimeout(() => setStepIndex((i) => i + 1), delay);
2523
- } else {
2524
- timerRef.current = setTimeout(() => setPhase("done"), 500);
2525
- }
2526
- return () => {
2527
- if (timerRef.current) clearTimeout(timerRef.current);
2528
- };
2529
- }, [phase, stepIndex]);
2530
- useEffect5(() => {
2531
- if (phase === "done") {
2532
- timerRef.current = setTimeout(() => cotRestart(), 3e3);
2533
- }
2534
- return () => {
2535
- if (timerRef.current) clearTimeout(timerRef.current);
2814
+ const { clearRect, installLinksClearRect } = useMemo7(() => {
2815
+ const logoHeight = isTiny ? 2 : isNarrow ? 13 : 7;
2816
+ const logoExtra = isBrowser ? 1 : 0;
2817
+ const gap = isMobile ? 0 : 1;
2818
+ const installLinksTop = 3 + logoHeight + logoExtra + gap;
2819
+ const installLinksHeight = 3;
2820
+ const boxTop = installLinksTop + installLinksHeight + gap + 1;
2821
+ const bh = height - boxTop - 1;
2822
+ return {
2823
+ clearRect: { top: boxTop, left: 1, width: width - 2, height: bh },
2824
+ installLinksClearRect: { top: installLinksTop, left: 1, width: width - 2, height: installLinksHeight }
2536
2825
  };
2537
- }, [phase]);
2538
- const steps = COT_STEPS.map((s, i) => {
2539
- if (i < stepIndex) return { ...s, status: "done" };
2540
- if (i === stepIndex && phase === "running") return { ...s, status: "running" };
2541
- return { ...s, status: phase === "done" ? "done" : "pending" };
2542
- });
2543
- const elapsedMs = COT_STEPS.slice(0, stepIndex).reduce((sum, s) => sum + s.delay, 0);
2544
- const totalMs = COT_STEPS.reduce((sum, s) => sum + s.delay, 0);
2545
- const durationStr = phase === "done" ? `${(totalMs / 1e3).toFixed(1)}s` : `${(elapsedMs / 1e3).toFixed(1)}s`;
2546
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2547
- /* @__PURE__ */ jsx28("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs20(ChainOfThought, { open: expanded, onOpenChange: setExpanded, children: [
2548
- /* @__PURE__ */ jsx28(ChainOfThoughtHeader, { duration: durationStr }),
2549
- /* @__PURE__ */ jsx28(ChainOfThoughtContent, { children: steps.map((step, i) => /* @__PURE__ */ jsx28(
2550
- ChainOfThoughtStep,
2551
- {
2552
- label: step.label,
2553
- description: step.description,
2554
- status: step.status,
2555
- isLast: i === steps.length - 1,
2556
- children: step.output
2557
- },
2558
- i
2559
- )) })
2560
- ] }) }),
2561
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [
2562
- { key: "ctrl+shift+e", label: "toggle" },
2563
- { key: "r", label: "restart" },
2564
- { key: "q", label: "quit" }
2565
- ] }) })
2566
- ] });
2567
- }
2568
- var BUBBLE_STEPS = [
2569
- { tool: "Read", label: "Reading codebase", description: "src/", status: "done", delay: 1800 },
2570
- { tool: "Think", label: "Planning changes", description: "auth module", status: "done", delay: 2500 },
2571
- { tool: "Edit", label: "Editing files", description: "4 files", status: "done", delay: 3200 },
2572
- { tool: "Bash", label: "Running tests", description: "vitest", status: "done", delay: 2e3 },
2573
- { tool: "Edit", label: "Fixing test", description: "routes.test.ts", status: "done", delay: 1500 }
2574
- ];
2575
- 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.";
2576
- var BUBBLE_TOTAL_MS = BUBBLE_STEPS.reduce((sum, s) => sum + s.delay, 0);
2577
- function MessageApp() {
2578
- const [expanded, setExpanded] = useState11(true);
2579
- const [phase, setPhase] = useState11("idle");
2580
- const [stepIndex, setStepIndex] = useState11(0);
2581
- const [streamedText, setStreamedText] = useState11("");
2582
- const timerRef = useRef8(null);
2583
- useKeyboard((event) => {
2584
- if (event.name === "E" && event.ctrl && event.shift) setExpanded((v) => !v);
2585
- if (event.name === "r") bubbleRestart();
2586
- });
2587
- function bubbleRestart() {
2588
- if (timerRef.current) clearTimeout(timerRef.current);
2589
- setPhase("idle");
2590
- setStepIndex(0);
2591
- setStreamedText("");
2592
- }
2593
- useEffect5(() => {
2594
- if (phase === "idle") {
2595
- timerRef.current = setTimeout(() => setPhase("thinking"), 800);
2596
- }
2597
- return () => {
2598
- if (timerRef.current) clearTimeout(timerRef.current);
2599
- };
2600
- }, [phase]);
2601
- useEffect5(() => {
2602
- if (phase !== "thinking") return;
2603
- if (stepIndex < BUBBLE_STEPS.length) {
2604
- const delay = BUBBLE_STEPS[stepIndex].delay;
2605
- timerRef.current = setTimeout(() => setStepIndex((i) => i + 1), delay);
2606
- } else {
2607
- timerRef.current = setTimeout(() => setPhase("streaming"), 500);
2608
- }
2609
- return () => {
2610
- if (timerRef.current) clearTimeout(timerRef.current);
2611
- };
2612
- }, [phase, stepIndex]);
2613
- useEffect5(() => {
2614
- if (phase !== "streaming") return;
2615
- if (streamedText.length < BUBBLE_RESPONSE.length) {
2616
- timerRef.current = setTimeout(() => {
2617
- setStreamedText(BUBBLE_RESPONSE.slice(0, streamedText.length + 2));
2618
- }, 25);
2619
- } else {
2620
- timerRef.current = setTimeout(() => setPhase("done"), 500);
2621
- }
2622
- return () => {
2623
- if (timerRef.current) clearTimeout(timerRef.current);
2624
- };
2625
- }, [phase, streamedText]);
2626
- useEffect5(() => {
2627
- if (phase === "done") {
2628
- timerRef.current = setTimeout(() => bubbleRestart(), 3e3);
2629
- }
2630
- return () => {
2631
- if (timerRef.current) clearTimeout(timerRef.current);
2632
- };
2633
- }, [phase]);
2634
- const steps = BUBBLE_STEPS.map((s, i) => {
2635
- if (i < stepIndex) return { ...s, status: "done" };
2636
- if (i === stepIndex) return { ...s, status: "running" };
2637
- return { ...s, status: "pending" };
2638
- });
2639
- const isThinking = phase === "thinking";
2640
- const isStreaming = phase === "streaming";
2641
- const isDone = phase === "done";
2642
- const showAssistant = phase !== "idle";
2643
- const elapsedMs = BUBBLE_STEPS.slice(0, stepIndex).reduce((sum, s) => sum + s.delay, 0);
2644
- const reasoningDuration = isThinking ? `${(elapsedMs / 1e3).toFixed(1)}s` : `${(BUBBLE_TOTAL_MS / 1e3).toFixed(1)}s`;
2645
- const reasoningSteps = isThinking ? steps : BUBBLE_STEPS.map((s) => ({ ...s, status: "done" }));
2646
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2647
- /* @__PURE__ */ jsxs20("box", { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1, children: [
2648
- /* @__PURE__ */ jsx28(Message, { role: "user", children: /* @__PURE__ */ jsx28(Message.Content, { children: /* @__PURE__ */ jsx28(Message.Text, { children: "Can you refactor the auth module?" }) }) }),
2649
- showAssistant && /* @__PURE__ */ jsxs20(Message, { role: "assistant", isStreaming, children: [
2650
- /* @__PURE__ */ jsx28(Message.Reasoning, { part: {
2651
- type: "reasoning",
2652
- duration: reasoningDuration,
2653
- collapsed: !expanded,
2654
- steps: reasoningSteps
2655
- } }),
2656
- (isStreaming || isDone) && /* @__PURE__ */ jsx28(Message.Content, { children: /* @__PURE__ */ jsx28(Message.Text, { isLast: true, children: isDone ? BUBBLE_RESPONSE : streamedText }) })
2657
- ] })
2658
- ] }),
2659
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "ctrl+shift+e", label: "toggle chain of thought" }, { key: "r", label: "restart" }, { key: "q", label: "quit" }] }) })
2660
- ] });
2661
- }
2662
- var initialChatMessages = [
2663
- { id: "1", role: "user", content: "Show me my portfolio" },
2664
- { id: "2", role: "assistant", content: "Here's your current portfolio allocation:" },
2665
- { id: "3", role: "user", content: "Calculate rebalancing trades" },
2666
- { id: "4", role: "assistant", content: "I've calculated the optimal trades to rebalance your portfolio." }
2667
- ];
2668
- var chatNextId = 5;
2669
- function ChatApp() {
2670
- const [messages, setMessages] = useState11(initialChatMessages);
2671
- const [isLoading, setIsLoading] = useState11(false);
2672
- const [streamingText, setStreamingText] = useState11("");
2673
- const [activeToolCalls, setActiveToolCalls] = useState11([]);
2674
- const intervalRef = useRef8(null);
2675
- const handleSend = useCallback4((text) => {
2676
- const userMsg = { id: String(chatNextId++), role: "user", content: text };
2677
- setMessages((prev) => [...prev, userMsg]);
2678
- setIsLoading(true);
2679
- const toolCallId = `tc-${chatNextId}`;
2680
- setTimeout(() => {
2681
- setIsLoading(false);
2682
- setActiveToolCalls([{ id: toolCallId, title: "process_request", status: "in_progress" }]);
2683
- }, 500);
2684
- setTimeout(() => {
2685
- setActiveToolCalls([{ id: toolCallId, title: "process_request", status: "completed" }]);
2686
- }, 1200);
2687
- const response = `You said: "${text}". This is a demo response.`;
2688
- let charIndex = 0;
2689
- setTimeout(() => {
2690
- intervalRef.current = setInterval(() => {
2691
- charIndex = Math.min(charIndex + 3, response.length);
2692
- if (charIndex < response.length) {
2693
- setStreamingText(response.slice(0, charIndex));
2694
- } else {
2695
- if (intervalRef.current) clearInterval(intervalRef.current);
2696
- setStreamingText("");
2697
- setActiveToolCalls([]);
2698
- setMessages((prev) => [
2699
- ...prev,
2700
- { id: String(chatNextId++), role: "assistant", content: response }
2701
- ]);
2702
- }
2703
- }, 50);
2704
- }, 1400);
2705
- }, []);
2706
- const handleCancel = useCallback4(() => {
2707
- if (intervalRef.current) clearInterval(intervalRef.current);
2708
- setIsLoading(false);
2709
- setStreamingText("");
2710
- setActiveToolCalls([]);
2711
- }, []);
2712
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2713
- /* @__PURE__ */ jsx28(
2714
- ChatPanel,
2715
- {
2716
- messages,
2717
- streamingText,
2718
- isLoading,
2719
- activeToolCalls,
2720
- onSendMessage: handleSend,
2721
- onCancel: handleCancel,
2722
- placeholder: "Ask about your portfolio...",
2723
- useKeyboard
2724
- }
2725
- ),
2726
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2727
- ] });
2826
+ }, [width, height, isTiny, isNarrow, isMobile, isBrowser]);
2827
+ return /* @__PURE__ */ React.createElement("box", { width: "100%", height: "100%", position: "relative" }, /* @__PURE__ */ React.createElement(MatrixBackground, { width, height, clearRect, clearRects: isBrowser ? void 0 : [installLinksClearRect] }), /* @__PURE__ */ React.createElement("box", { position: "absolute", top: 0, left: 0, width, height, zIndex: 1, flexDirection: "column", shouldFill: false }, /* @__PURE__ */ React.createElement("box", { flexGrow: 1, flexDirection: "column", paddingTop: 3, paddingLeft: 1, paddingRight: 1, paddingBottom: 1, gap: isMobile ? 0 : 1, shouldFill: false }, /* @__PURE__ */ React.createElement("box", { flexShrink: 0, shouldFill: false }, /* @__PURE__ */ React.createElement(Logo, { compact: isTiny, narrow: isNarrow, mobile: isMobile })), /* @__PURE__ */ React.createElement("box", { flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: isMobile ? 0 : 1, flexShrink: 0, shouldFill: false }, /* @__PURE__ */ React.createElement("box", { border: true, borderStyle: "rounded", borderColor: theme.border, paddingX: 1, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ React.createElement("text", null, /* @__PURE__ */ React.createElement("span", { style: textStyle({ dim: true }) }, "$ "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ bold: true }) }, "bunx "), /* @__PURE__ */ React.createElement("span", { style: textStyle({ fg: theme.accent }) }, "@gridland/demo landing"))), /* @__PURE__ */ React.createElement(InstallBox, null), /* @__PURE__ */ React.createElement(LinksBox, null)), /* @__PURE__ */ React.createElement("box", { flexGrow: 1, border: true, borderStyle: "rounded", borderColor: theme.border, flexDirection: "column", overflow: "hidden" }))));
2728
2828
  }
2829
+
2830
+ // demos/index.tsx
2729
2831
  var demos = [
2730
- { name: "gradient", app: () => /* @__PURE__ */ jsx28(GradientApp, {}) },
2731
- { name: "ascii", app: () => /* @__PURE__ */ jsx28(AsciiApp, {}) },
2732
- { name: "table", app: () => /* @__PURE__ */ jsx28(TableApp, {}) },
2733
- { name: "spinner", app: () => /* @__PURE__ */ jsx28(SpinnerApp, {}) },
2734
- { name: "select-input", app: () => /* @__PURE__ */ jsx28(SelectInputApp, {}) },
2735
- { name: "multi-select", app: () => /* @__PURE__ */ jsx28(MultiSelectApp, {}) },
2736
- { name: "prompt-input", app: () => /* @__PURE__ */ jsx28(PromptInputApp, {}) },
2737
- { name: "text-input", app: () => /* @__PURE__ */ jsx28(TextInputApp, {}) },
2738
- { name: "link", app: () => /* @__PURE__ */ jsx28(LinkApp, {}) },
2739
- { name: "tabs", app: () => /* @__PURE__ */ jsx28(TabBarApp, {}) },
2740
- { name: "status-bar", app: () => /* @__PURE__ */ jsx28(StatusBarApp, {}) },
2741
- { name: "modal", app: () => /* @__PURE__ */ jsx28(ModalApp, {}) },
2742
- { name: "primitives", app: () => /* @__PURE__ */ jsx28(PrimitivesApp, {}) },
2743
- { name: "chat", app: () => /* @__PURE__ */ jsx28(ChatApp, {}) },
2744
- { name: "chain-of-thought", app: () => /* @__PURE__ */ jsx28(ChainOfThoughtApp, {}) },
2745
- { name: "message", app: () => /* @__PURE__ */ jsx28(MessageApp, {}) },
2746
- { name: "terminal-window", app: () => /* @__PURE__ */ jsx28(TerminalWindowApp, {}) },
2747
- { name: "landing", app: () => /* @__PURE__ */ jsx28(LandingApp, { useKeyboard }) }
2832
+ { name: "gradient", app: () => /* @__PURE__ */ React.createElement(GradientApp, null) },
2833
+ { name: "ascii", app: () => /* @__PURE__ */ React.createElement(AsciiApp, null) },
2834
+ { name: "table", app: () => /* @__PURE__ */ React.createElement(TableApp, null) },
2835
+ { name: "spinner", app: () => /* @__PURE__ */ React.createElement(SpinnerApp, null) },
2836
+ { name: "select-input", app: () => /* @__PURE__ */ React.createElement(SelectInputApp, null) },
2837
+ { name: "multi-select", app: () => /* @__PURE__ */ React.createElement(MultiSelectApp, null) },
2838
+ { name: "prompt-input", app: () => /* @__PURE__ */ React.createElement(PromptInputApp, null) },
2839
+ { name: "text-input", app: () => /* @__PURE__ */ React.createElement(TextInputApp, null) },
2840
+ { name: "link", app: () => /* @__PURE__ */ React.createElement(LinkApp, null) },
2841
+ { name: "tabs", app: () => /* @__PURE__ */ React.createElement(TabBarApp, null) },
2842
+ { name: "status-bar", app: () => /* @__PURE__ */ React.createElement(StatusBarApp, null) },
2843
+ { name: "modal", app: () => /* @__PURE__ */ React.createElement(ModalApp, null) },
2844
+ { name: "primitives", app: () => /* @__PURE__ */ React.createElement(PrimitivesApp, null) },
2845
+ { name: "chat", app: () => /* @__PURE__ */ React.createElement(ChatApp, null) },
2846
+ { name: "chain-of-thought", app: () => /* @__PURE__ */ React.createElement(ChainOfThoughtApp, null) },
2847
+ { name: "message", app: () => /* @__PURE__ */ React.createElement(MessageApp, null) },
2848
+ { name: "terminal-window", app: () => /* @__PURE__ */ React.createElement(TerminalWindowApp, null) },
2849
+ { name: "focus", app: () => /* @__PURE__ */ React.createElement(FocusApp, null) },
2850
+ { name: "pointer", app: () => /* @__PURE__ */ React.createElement(PointerApp, null) },
2851
+ { name: "cursor-highlight", app: () => /* @__PURE__ */ React.createElement(CursorHighlightApp, null) },
2852
+ { name: "text-style", app: () => /* @__PURE__ */ React.createElement(TextStyleApp, null) },
2853
+ { name: "headless", app: () => /* @__PURE__ */ React.createElement(HeadlessApp, null) },
2854
+ { name: "theming", app: () => /* @__PURE__ */ React.createElement(ThemingApp, null) },
2855
+ { name: "landing", app: () => /* @__PURE__ */ React.createElement(LandingApp, { useKeyboard: useKeyboard18 }) }
2748
2856
  ];
2749
2857
 
2750
2858
  // src/run.tsx
2751
- import { jsx as jsx29 } from "react/jsx-runtime";
2752
2859
  var _renderer;
2753
2860
  function DemoShell({ children }) {
2754
- useKeyboard2((event) => {
2861
+ useKeyboard19((event) => {
2755
2862
  if (event.name === "q" || event.name === "escape") {
2756
2863
  _renderer.destroy();
2757
2864
  }
2758
2865
  });
2759
- return /* @__PURE__ */ jsx29("box", { flexDirection: "column", flexGrow: 1, children });
2866
+ return /* @__PURE__ */ React.createElement("box", { flexDirection: "column", flexGrow: 1 }, children);
2760
2867
  }
2761
- async function runDemo(name) {
2762
- const demo = demos.find((d) => d.name === name);
2868
+ async function runDemo(name2) {
2869
+ const demo = demos.find((d) => d.name === name2);
2763
2870
  if (!demo) {
2764
- console.error(`Unknown demo: "${name}"`);
2871
+ console.error(`Unknown demo: "${name2}"`);
2765
2872
  console.error(`Available: ${demos.map((d) => d.name).join(", ")}`);
2766
2873
  process.exit(1);
2767
2874
  }
2768
2875
  _renderer = await createCliRenderer({ exitOnCtrlC: true });
2769
- createRoot(_renderer).render(/* @__PURE__ */ jsx29(DemoShell, { children: demo.app() }));
2876
+ createRoot(_renderer).render(/* @__PURE__ */ React.createElement(DemoShell, null, demo.app()));
2877
+ }
2878
+ var name = process.argv[2];
2879
+ if (name) {
2880
+ runDemo(name);
2770
2881
  }
2771
2882
  export {
2772
2883
  demos,