@gridland/demo 0.2.52 → 0.2.53

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,8 @@
1
1
  // src/run.tsx
2
- import { createCliRenderer, createRoot, useKeyboard as useKeyboard19 } from "@gridland/bun";
2
+ import { createCliRenderer, createRoot, useKeyboard as useKeyboard22 } from "@gridland/bun";
3
3
 
4
4
  // demos/index.tsx
5
- import { useKeyboard as useKeyboard18 } from "@gridland/utils";
5
+ import { useKeyboard as useKeyboard21 } from "@gridland/utils";
6
6
 
7
7
  // demos/gradient.tsx
8
8
  import { useState as useState8 } from "react";
@@ -160,9 +160,9 @@ import { useState as useState3 } from "react";
160
160
  import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
161
161
  function SpinnerPicker({ useKeyboard: useKeyboardProp }) {
162
162
  const theme = useTheme();
163
- const useKeyboard20 = useKeyboardContext(useKeyboardProp);
163
+ const useKeyboard23 = useKeyboardContext(useKeyboardProp);
164
164
  const [selected, setSelected] = useState3(0);
165
- useKeyboard20?.((event) => {
165
+ useKeyboard23?.((event) => {
166
166
  if (event.name === "left") {
167
167
  setSelected((s) => s > 0 ? s - 1 : VARIANT_NAMES.length - 1);
168
168
  } else if (event.name === "right") {
@@ -291,7 +291,7 @@ function SelectInput({
291
291
  useKeyboard: useKeyboardProp
292
292
  }) {
293
293
  const theme = useTheme();
294
- const useKeyboard20 = useKeyboardContext(useKeyboardProp);
294
+ const useKeyboard23 = useKeyboardContext(useKeyboardProp);
295
295
  const resolvedHighlight = highlightColor ?? theme.primary;
296
296
  const isControlled = controlledValue !== void 0;
297
297
  const controlledRef = useRef2(isControlled);
@@ -334,7 +334,7 @@ function SelectInput({
334
334
  const scrollOffset = Math.max(0, Math.min(cursorRowIndex - Math.floor(visibleCount / 2), flatRows.length - visibleCount));
335
335
  const visibleRows = flatRows.slice(scrollOffset, scrollOffset + visibleCount);
336
336
  const diamondColor = invalid ? theme.error : disabled ? theme.muted : theme.accent;
337
- useKeyboard20?.((event) => {
337
+ useKeyboard23?.((event) => {
338
338
  if (state.submitted || disabled) return;
339
339
  if (event.name === "up" || event.name === "k") {
340
340
  const direction = -1;
@@ -498,7 +498,7 @@ function MultiSelect({
498
498
  useKeyboard: useKeyboardProp
499
499
  }) {
500
500
  const theme = useTheme();
501
- const useKeyboard20 = useKeyboardContext(useKeyboardProp);
501
+ const useKeyboard23 = useKeyboardContext(useKeyboardProp);
502
502
  const resolvedHighlight = highlightColor ?? theme.primary;
503
503
  const resolvedCheckbox = checkboxColor ?? theme.accent;
504
504
  const isControlled = controlledSelected !== void 0;
@@ -554,7 +554,7 @@ function MultiSelect({
554
554
  }
555
555
  };
556
556
  const diamondColor = invalid ? theme.error : disabled ? theme.muted : theme.accent;
557
- useKeyboard20?.((event) => {
557
+ useKeyboard23?.((event) => {
558
558
  if (state.submitted || disabled) return;
559
559
  const move = (direction) => {
560
560
  let next = cursorRef.current + direction;
@@ -1004,9 +1004,9 @@ function Modal({
1004
1004
  useKeyboard: useKeyboardProp
1005
1005
  }) {
1006
1006
  const theme = useTheme();
1007
- const useKeyboard20 = useKeyboardContext(useKeyboardProp);
1007
+ const useKeyboard23 = useKeyboardContext(useKeyboardProp);
1008
1008
  const resolvedBorderColor = borderColor ?? theme.muted;
1009
- useKeyboard20?.((event) => {
1009
+ useKeyboard23?.((event) => {
1010
1010
  if (event.name === "escape" && onClose) {
1011
1011
  onClose();
1012
1012
  }
@@ -1037,7 +1037,7 @@ import {
1037
1037
  createContext as createContext5,
1038
1038
  useContext as useContext5
1039
1039
  } from "react";
1040
- import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
1040
+ import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
1041
1041
  var PromptInputControllerCtx = createContext5(null);
1042
1042
  var useOptionalController = () => useContext5(PromptInputControllerCtx);
1043
1043
  var PromptInputContext = createContext5(null);
@@ -1064,10 +1064,11 @@ function resolveStatusHintText(status, submittedText, streamingText, errorText,
1064
1064
  if (status === "error") return errorText;
1065
1065
  return disabledText;
1066
1066
  }
1067
- var DIVIDER_LINE = "\u2500".repeat(500);
1068
1067
  function PromptInputDivider() {
1069
- const { theme } = usePromptInput();
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 }) });
1068
+ const { dividerColor, dividerDashed, theme } = usePromptInput();
1069
+ const color = dividerColor ?? theme.muted;
1070
+ const char = dividerDashed ? "\u254C" : "\u2500";
1071
+ return /* @__PURE__ */ jsx16("text", { wrapMode: "none", marginLeft: -1, marginRight: -1, children: /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: !dividerColor, fg: color }), children: char.repeat(500) }) });
1071
1072
  }
1072
1073
  function PromptInputSuggestions() {
1073
1074
  const { suggestions, sugIdx, maxSuggestions, theme } = usePromptInput();
@@ -1082,18 +1083,25 @@ function PromptInputSuggestions() {
1082
1083
  ] }, sug.text);
1083
1084
  }) });
1084
1085
  }
1085
- var CURSOR_CHAR = "\u258D";
1086
1086
  function PromptInputTextarea() {
1087
- const { value, disabled, statusHintText, placeholder, prompt, promptColor, theme } = usePromptInput();
1088
- return /* @__PURE__ */ jsxs10("text", { children: [
1089
- /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: promptColor }), children: prompt }),
1090
- value.length === 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
1091
- !disabled && /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.muted }), children: CURSOR_CHAR }),
1092
- /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: disabled ? statusHintText : " " + placeholder })
1093
- ] }) : /* @__PURE__ */ jsxs10(Fragment5, { children: [
1094
- /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.foreground }), children: value }),
1095
- !disabled && /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.muted }), children: CURSOR_CHAR })
1096
- ] })
1087
+ const { value, isFocused, disabled, statusHintText, placeholder, prompt, promptColor, theme, handleInput, handleInputSubmit, handleInputKeyDown } = usePromptInput();
1088
+ return /* @__PURE__ */ jsxs10("box", { flexDirection: "row", children: [
1089
+ /* @__PURE__ */ jsx16("text", { children: /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: promptColor }), children: prompt }) }),
1090
+ isFocused ? /* @__PURE__ */ jsx16(
1091
+ "input",
1092
+ {
1093
+ value,
1094
+ placeholder,
1095
+ focused: true,
1096
+ onInput: handleInput,
1097
+ onSubmit: handleInputSubmit,
1098
+ onKeyDown: handleInputKeyDown,
1099
+ cursorColor: theme.muted,
1100
+ cursorStyle: { style: "line", blinking: !value },
1101
+ placeholderColor: theme.placeholder,
1102
+ textColor: theme.foreground
1103
+ }
1104
+ ) : disabled && value.length === 0 ? /* @__PURE__ */ jsx16("text", { children: /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: statusHintText }) }) : /* @__PURE__ */ jsx16("text", { children: /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: value ? theme.foreground : theme.placeholder, dim: !value }), children: value || placeholder }) })
1097
1105
  ] });
1098
1106
  }
1099
1107
  function PromptInputSubmit(props) {
@@ -1140,13 +1148,16 @@ function PromptInput({
1140
1148
  maxSuggestions = 5,
1141
1149
  enableHistory = true,
1142
1150
  model,
1151
+ focus = true,
1143
1152
  showDividers = true,
1144
1153
  autoFocus = false,
1154
+ dividerColor,
1155
+ dividerDashed,
1145
1156
  useKeyboard: useKeyboardProp,
1146
1157
  children
1147
1158
  }) {
1148
1159
  const theme = useTheme();
1149
- const useKeyboard20 = useKeyboardContext(useKeyboardProp);
1160
+ const useKeyboard23 = useKeyboardContext(useKeyboardProp);
1150
1161
  useEffect2(() => {
1151
1162
  if (!autoFocus) return;
1152
1163
  if (typeof document === "undefined") return;
@@ -1157,6 +1168,7 @@ function PromptInput({
1157
1168
  }, [autoFocus]);
1158
1169
  const resolvedPromptColor = promptColor ?? theme.muted;
1159
1170
  const disabled = status ? status === "submitted" || status === "streaming" : disabledProp;
1171
+ const isFocused = focus && !disabled;
1160
1172
  const statusHintText = resolveStatusHintText(status, submittedText, streamingLabel, errorText, disabledText);
1161
1173
  const controller = useOptionalController();
1162
1174
  const usingProvider = !!controller;
@@ -1247,18 +1259,86 @@ function PromptInput({
1247
1259
  clearInput();
1248
1260
  }
1249
1261
  }, [onSubmit, clearInput]);
1250
- useKeyboard20?.((event) => {
1262
+ const handleInputSubmit = (text) => {
1263
+ const trimmed = text.trim();
1264
+ if (!trimmed) return;
1265
+ if (enableHistory) {
1266
+ setHist([trimmed, ...historyRef.current]);
1267
+ }
1268
+ updateValue("");
1269
+ setHistI(-1);
1270
+ handleSubmit(trimmed);
1271
+ };
1272
+ const handleInputKeyDown = (key) => {
1273
+ if (key.name === "return" && suggestionsRef.current.length > 0) {
1274
+ const sel = suggestionsRef.current[sugIdxRef.current];
1275
+ if (sel) {
1276
+ if (valueRef.current.startsWith("/")) {
1277
+ updateValue("");
1278
+ if (enableHistory) {
1279
+ setHist([sel.text, ...historyRef.current]);
1280
+ }
1281
+ setHistI(-1);
1282
+ handleSubmit(sel.text);
1283
+ } else {
1284
+ const base = valueRef.current.slice(0, valueRef.current.lastIndexOf("@"));
1285
+ updateValue(base + sel.text + " ");
1286
+ setSug([]);
1287
+ }
1288
+ }
1289
+ key.preventDefault();
1290
+ return;
1291
+ }
1292
+ if (key.name === "tab" && suggestionsRef.current.length > 0) {
1293
+ setSugI((sugIdxRef.current + 1) % suggestionsRef.current.length);
1294
+ key.preventDefault();
1295
+ return;
1296
+ }
1297
+ if (key.name === "up") {
1298
+ if (suggestionsRef.current.length > 0) {
1299
+ setSugI(Math.max(0, sugIdxRef.current - 1));
1300
+ } else if (enableHistory && historyRef.current.length > 0) {
1301
+ const idx = Math.min(historyRef.current.length - 1, histIdxRef.current + 1);
1302
+ setHistI(idx);
1303
+ updateValue(historyRef.current[idx]);
1304
+ }
1305
+ key.preventDefault();
1306
+ return;
1307
+ }
1308
+ if (key.name === "down") {
1309
+ if (suggestionsRef.current.length > 0) {
1310
+ setSugI(Math.min(suggestionsRef.current.length - 1, sugIdxRef.current + 1));
1311
+ } else if (enableHistory && histIdxRef.current > 0) {
1312
+ const nextIdx = histIdxRef.current - 1;
1313
+ setHistI(nextIdx);
1314
+ updateValue(historyRef.current[nextIdx]);
1315
+ } else if (enableHistory && histIdxRef.current === 0) {
1316
+ setHistI(-1);
1317
+ updateValue("");
1318
+ }
1319
+ key.preventDefault();
1320
+ return;
1321
+ }
1322
+ if (key.name === "escape") {
1323
+ if (suggestionsRef.current.length > 0) {
1324
+ setSug([]);
1325
+ key.preventDefault();
1326
+ }
1327
+ return;
1328
+ }
1329
+ };
1330
+ useKeyboard23?.((event) => {
1251
1331
  if (event.name === "escape" && (status === "streaming" || status === "submitted") && onStop) {
1252
1332
  onStop();
1253
1333
  return;
1254
1334
  }
1335
+ if (isFocused) return;
1255
1336
  if (disabled) return;
1256
1337
  if (event.name === "return") {
1257
1338
  if (suggestionsRef.current.length > 0) {
1258
1339
  const sel = suggestionsRef.current[sugIdxRef.current];
1259
1340
  if (sel) {
1260
1341
  if (valueRef.current.startsWith("/")) {
1261
- setSug([]);
1262
1342
  updateValue("");
1263
1343
  if (enableHistory) {
1264
1344
  setHist([sel.text, ...historyRef.current]);
@@ -1279,7 +1359,6 @@ function PromptInput({
1279
1359
  }
1280
1360
  updateValue("");
1281
1361
  setHistI(-1);
1282
- setSug([]);
1283
1362
  handleSubmit(trimmed);
1284
1363
  }
1285
1364
  return;
@@ -1333,6 +1412,7 @@ function PromptInput({
1333
1412
  const visibleSuggestions = suggestions.slice(0, maxSuggestions);
1334
1413
  const ctxValue = {
1335
1414
  value,
1415
+ isFocused,
1336
1416
  disabled,
1337
1417
  status,
1338
1418
  onStop,
@@ -1345,7 +1425,12 @@ function PromptInput({
1345
1425
  maxSuggestions,
1346
1426
  errorText,
1347
1427
  model,
1348
- theme
1428
+ dividerColor,
1429
+ dividerDashed,
1430
+ theme,
1431
+ handleInput: updateValue,
1432
+ handleInputSubmit,
1433
+ handleInputKeyDown
1349
1434
  };
1350
1435
  if (children) {
1351
1436
  return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx16("box", { flexDirection: "column", flexShrink: 0, children }) });
@@ -1438,6 +1523,7 @@ function ChatPanel({
1438
1523
  userColor,
1439
1524
  assistantColor,
1440
1525
  loadingText = "Thinking...",
1526
+ focus,
1441
1527
  useKeyboard: useKeyboardProp
1442
1528
  }) {
1443
1529
  const theme = useTheme();
@@ -1483,6 +1569,7 @@ function ChatPanel({
1483
1569
  promptColor: resolvedPromptColor,
1484
1570
  foregroundColor: theme.foreground,
1485
1571
  submittedText: loadingText,
1572
+ focus,
1486
1573
  useKeyboard: useKeyboardProp
1487
1574
  }
1488
1575
  ) })
@@ -1491,7 +1578,7 @@ function ChatPanel({
1491
1578
 
1492
1579
  // ../ui/components/chain-of-thought/chain-of-thought.tsx
1493
1580
  import { createContext as createContext6, memo, useContext as useContext6, useEffect as useEffect3, useMemo as useMemo4, useState as useState7 } from "react";
1494
- import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1581
+ import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1495
1582
  var DOTS = ["\u25CB", "\u25D4", "\u25D1", "\u25D5", "\u25CF"];
1496
1583
  var SPINNER_INTERVAL = 150;
1497
1584
  var ChainOfThoughtContext = createContext6(null);
@@ -1550,7 +1637,7 @@ var ChainOfThoughtHeader = memo(({
1550
1637
  var ChainOfThoughtContent = memo(({ children }) => {
1551
1638
  const { isOpen } = useChainOfThought();
1552
1639
  if (!isOpen) return null;
1553
- return /* @__PURE__ */ jsx18(Fragment6, { children });
1640
+ return /* @__PURE__ */ jsx18(Fragment5, { children });
1554
1641
  });
1555
1642
  var ChainOfThoughtStep = memo(({
1556
1643
  label,
@@ -1854,18 +1941,15 @@ function TableApp() {
1854
1941
 
1855
1942
  // demos/spinner.tsx
1856
1943
  import { useKeyboard as useKeyboard3 } from "@gridland/utils";
1857
- import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
1944
+ import { jsx as jsx24 } from "react/jsx-runtime";
1858
1945
  function SpinnerApp() {
1859
- return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexGrow: 1, children: [
1860
- /* @__PURE__ */ jsx24("box", { flexGrow: 1, children: /* @__PURE__ */ jsx24(SpinnerPicker, { useKeyboard: useKeyboard3 }) }),
1861
- /* @__PURE__ */ jsx24("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx24(StatusBar, { items: [{ key: "\u2190\u2192", label: "change variant" }] }) })
1862
- ] });
1946
+ return /* @__PURE__ */ jsx24("box", { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx24(SpinnerPicker, { useKeyboard: useKeyboard3 }) });
1863
1947
  }
1864
1948
 
1865
1949
  // demos/select-input.tsx
1866
1950
  import { useState as useState10 } from "react";
1867
1951
  import { useKeyboard as useKeyboard4 } from "@gridland/utils";
1868
- import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
1952
+ import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
1869
1953
  var items = [
1870
1954
  { label: "TypeScript", value: "ts" },
1871
1955
  { label: "JavaScript", value: "js" },
@@ -1881,7 +1965,7 @@ function SelectInputApp() {
1881
1965
  setResetKey((k) => k + 1);
1882
1966
  }
1883
1967
  });
1884
- return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
1968
+ return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
1885
1969
  /* @__PURE__ */ jsx25("box", { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx25(
1886
1970
  SelectInput,
1887
1971
  {
@@ -1902,7 +1986,7 @@ function SelectInputApp() {
1902
1986
  // demos/multi-select.tsx
1903
1987
  import { useState as useState11 } from "react";
1904
1988
  import { useKeyboard as useKeyboard5 } from "@gridland/utils";
1905
- import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
1989
+ import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
1906
1990
  var items2 = [
1907
1991
  { label: "TypeScript", value: "ts" },
1908
1992
  { label: "JavaScript", value: "js" },
@@ -1918,7 +2002,7 @@ function MultiSelectApp() {
1918
2002
  setResetKey((k) => k + 1);
1919
2003
  }
1920
2004
  });
1921
- return /* @__PURE__ */ jsxs19("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
2005
+ return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
1922
2006
  /* @__PURE__ */ jsx26("box", { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx26(
1923
2007
  MultiSelect,
1924
2008
  {
@@ -1941,7 +2025,7 @@ function MultiSelectApp() {
1941
2025
  // demos/prompt-input.tsx
1942
2026
  import { useState as useState12 } from "react";
1943
2027
  import { useKeyboard as useKeyboard6 } from "@gridland/utils";
1944
- import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
2028
+ import { jsx as jsx27, jsxs as jsxs19 } from "react/jsx-runtime";
1945
2029
  var commands = [{ cmd: "/model", desc: "Switch model" }];
1946
2030
  var files = ["src/index.ts", "src/routes.ts", "src/auth.ts", "package.json"];
1947
2031
  var models = [
@@ -1977,8 +2061,8 @@ function PromptInputApp() {
1977
2061
  }
1978
2062
  ) }) }) });
1979
2063
  }
1980
- return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
1981
- /* @__PURE__ */ jsx27("box", { flexDirection: "column", flexGrow: 1, children: lastMessage ? /* @__PURE__ */ jsxs20("text", { children: [
2064
+ return /* @__PURE__ */ jsxs19("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
2065
+ /* @__PURE__ */ jsx27("box", { flexDirection: "column", flexGrow: 1, children: lastMessage ? /* @__PURE__ */ jsxs19("text", { children: [
1982
2066
  /* @__PURE__ */ jsx27("span", { style: textStyle({ fg: theme.muted }), children: "Sent: " }),
1983
2067
  /* @__PURE__ */ jsx27("span", { style: textStyle({ fg: theme.foreground }), children: lastMessage })
1984
2068
  ] }) : /* @__PURE__ */ jsx27("text", { children: /* @__PURE__ */ jsx27("span", { children: " " }) }) }),
@@ -1989,6 +2073,7 @@ function PromptInputApp() {
1989
2073
  files,
1990
2074
  placeholder: "Message Claude...",
1991
2075
  showDividers: true,
2076
+ autoFocus: true,
1992
2077
  useKeyboard: useKeyboard6,
1993
2078
  onSubmit: handleSubmit
1994
2079
  },
@@ -2006,7 +2091,7 @@ function PromptInputApp() {
2006
2091
  // demos/text-input.tsx
2007
2092
  import { useState as useState13 } from "react";
2008
2093
  import { useKeyboard as useKeyboard7 } from "@gridland/utils";
2009
- import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
2094
+ import { jsx as jsx28, jsxs as jsxs20 } from "react/jsx-runtime";
2010
2095
  var FIELDS = [
2011
2096
  { label: "Username", placeholder: "enter your name", maxLength: 30, required: true },
2012
2097
  { label: "Email", placeholder: "user@example.com", maxLength: 50, required: true, description: "We'll never share your email" },
@@ -2020,7 +2105,7 @@ function TextInputApp() {
2020
2105
  if (event.name === "up") setActiveField((i) => Math.max(0, i - 1));
2021
2106
  if (event.name === "down") setActiveField((i) => Math.min(FIELDS.length - 1, i + 1));
2022
2107
  });
2023
- return /* @__PURE__ */ jsxs21("box", { flexDirection: "column", flexGrow: 1, children: [
2108
+ return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2024
2109
  /* @__PURE__ */ jsx28("box", { flexDirection: "column", paddingX: 1, paddingTop: 1, flexGrow: 1, children: FIELDS.map((field, i) => /* @__PURE__ */ jsx28("box", { marginBottom: 1, children: /* @__PURE__ */ jsx28(
2025
2110
  TextInput,
2026
2111
  {
@@ -2043,7 +2128,7 @@ function TextInputApp() {
2043
2128
  // demos/link.tsx
2044
2129
  import { useState as useState14 } from "react";
2045
2130
  import { useKeyboard as useKeyboard8 } from "@gridland/utils";
2046
- import { jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
2131
+ import { jsx as jsx29, jsxs as jsxs21 } from "react/jsx-runtime";
2047
2132
  var MODES = ["solid", "dashed", "dotted", "none"];
2048
2133
  function LinkApp() {
2049
2134
  const theme = useTheme();
@@ -2053,8 +2138,8 @@ function LinkApp() {
2053
2138
  if (event.name === "right") setModeIndex((i) => (i + 1) % MODES.length);
2054
2139
  if (event.name === "left") setModeIndex((i) => (i - 1 + MODES.length) % MODES.length);
2055
2140
  });
2056
- return /* @__PURE__ */ jsxs22("box", { flexDirection: "column", flexGrow: 1, children: [
2057
- /* @__PURE__ */ jsx29("box", { padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs22("text", { style: textStyle({ fg: theme.foreground }), children: [
2141
+ return /* @__PURE__ */ jsxs21("box", { flexDirection: "column", flexGrow: 1, children: [
2142
+ /* @__PURE__ */ jsx29("box", { padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs21("text", { style: textStyle({ fg: theme.foreground }), children: [
2058
2143
  "Made by ",
2059
2144
  /* @__PURE__ */ jsx29("a", { href: "https://cjroth.com", style: { attributes: mode === "solid" ? 8 : mode === "dashed" ? 24 : mode === "dotted" ? 72 : 0, fg: theme.accent }, children: "Chris Roth" }),
2060
2145
  " and ",
@@ -2074,7 +2159,7 @@ function LinkApp() {
2074
2159
  // demos/tabs.tsx
2075
2160
  import { useState as useState15 } from "react";
2076
2161
  import { useKeyboard as useKeyboard9 } from "@gridland/utils";
2077
- import { jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
2162
+ import { jsx as jsx30, jsxs as jsxs22 } from "react/jsx-runtime";
2078
2163
  var tabs = ["Files", "Search", "Git", "Debug"];
2079
2164
  function TabBarApp() {
2080
2165
  const [selectedIndex, setSelectedIndex] = useState15(0);
@@ -2082,7 +2167,7 @@ function TabBarApp() {
2082
2167
  if (event.name === "left") setSelectedIndex((i) => i > 0 ? i - 1 : tabs.length - 1);
2083
2168
  if (event.name === "right") setSelectedIndex((i) => i < tabs.length - 1 ? i + 1 : 0);
2084
2169
  });
2085
- return /* @__PURE__ */ jsxs23("box", { flexDirection: "column", flexGrow: 1, children: [
2170
+ return /* @__PURE__ */ jsxs22("box", { flexDirection: "column", flexGrow: 1, children: [
2086
2171
  /* @__PURE__ */ jsx30("box", { padding: 1, children: /* @__PURE__ */ jsx30(TabBar, { options: tabs, selectedIndex }) }),
2087
2172
  /* @__PURE__ */ jsx30("box", { flexGrow: 1 }),
2088
2173
  /* @__PURE__ */ jsx30("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx30(StatusBar, { items: [{ key: "\u2190\u2192", label: "switch tab" }] }) })
@@ -2092,7 +2177,7 @@ function TabBarApp() {
2092
2177
  // demos/status-bar.tsx
2093
2178
  import { useState as useState16 } from "react";
2094
2179
  import { useKeyboard as useKeyboard10 } from "@gridland/utils";
2095
- import { jsx as jsx31, jsxs as jsxs24 } from "react/jsx-runtime";
2180
+ import { jsx as jsx31, jsxs as jsxs23 } from "react/jsx-runtime";
2096
2181
  var shortcuts = [
2097
2182
  { key: "Tab", label: "switch focus" },
2098
2183
  { key: "\u2190\u2192", label: "cycle" },
@@ -2109,8 +2194,8 @@ function StatusBarApp() {
2109
2194
  else if (event.name === "b") setLastKey("back (b)");
2110
2195
  else if (event.name === "z") setLastKey("reset (z)");
2111
2196
  });
2112
- return /* @__PURE__ */ jsxs24("box", { flexDirection: "column", gap: 1, padding: 1, children: [
2113
- lastKey ? /* @__PURE__ */ jsxs24("text", { children: [
2197
+ return /* @__PURE__ */ jsxs23("box", { flexDirection: "column", gap: 1, padding: 1, children: [
2198
+ lastKey ? /* @__PURE__ */ jsxs23("text", { children: [
2114
2199
  /* @__PURE__ */ jsx31("span", { children: "Pressed: " }),
2115
2200
  /* @__PURE__ */ jsx31("span", { style: textStyle({ bold: true, fg: theme.accent }), children: lastKey })
2116
2201
  ] }) : /* @__PURE__ */ jsx31("text", { style: textStyle({ dim: true }), children: "Press a key to trigger an action" }),
@@ -2127,7 +2212,7 @@ function StatusBarApp() {
2127
2212
  // demos/modal.tsx
2128
2213
  import { useState as useState17 } from "react";
2129
2214
  import { useKeyboard as useKeyboard11 } from "@gridland/utils";
2130
- import { jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
2215
+ import { jsx as jsx32, jsxs as jsxs24 } from "react/jsx-runtime";
2131
2216
  function ModalApp() {
2132
2217
  const theme = useTheme();
2133
2218
  const [isOpen, setIsOpen] = useState17(false);
@@ -2136,8 +2221,8 @@ function ModalApp() {
2136
2221
  if (isOpen && (event.name === "q" || event.name === "escape")) setIsOpen(false);
2137
2222
  });
2138
2223
  if (isOpen) {
2139
- return /* @__PURE__ */ jsxs25("box", { flexDirection: "column", flexGrow: 1, children: [
2140
- /* @__PURE__ */ jsx32(Modal, { title: "Example Modal", useKeyboard: useKeyboard11, onClose: () => setIsOpen(false), children: /* @__PURE__ */ jsxs25("box", { paddingX: 1, flexDirection: "column", children: [
2224
+ return /* @__PURE__ */ jsxs24("box", { flexDirection: "column", flexGrow: 1, children: [
2225
+ /* @__PURE__ */ jsx32(Modal, { title: "Example Modal", useKeyboard: useKeyboard11, onClose: () => setIsOpen(false), children: /* @__PURE__ */ jsxs24("box", { paddingX: 1, flexDirection: "column", children: [
2141
2226
  /* @__PURE__ */ jsx32("text", { style: textStyle({ fg: theme.foreground }), children: "This is a modal overlay component." }),
2142
2227
  /* @__PURE__ */ jsx32("text", { children: " " }),
2143
2228
  /* @__PURE__ */ jsx32("text", { style: textStyle({ dim: true, fg: theme.muted }), children: "It stretches to fill the full terminal height." })
@@ -2145,7 +2230,7 @@ function ModalApp() {
2145
2230
  /* @__PURE__ */ jsx32("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx32(StatusBar, { items: [{ key: "q", label: "close" }] }) })
2146
2231
  ] });
2147
2232
  }
2148
- return /* @__PURE__ */ jsx32("box", { flexDirection: "column", flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs25("text", { children: [
2233
+ return /* @__PURE__ */ jsx32("box", { flexDirection: "column", flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs24("text", { children: [
2149
2234
  /* @__PURE__ */ jsx32("span", { style: textStyle({ dim: true, fg: theme.muted }), children: "Press " }),
2150
2235
  /* @__PURE__ */ jsx32("span", { style: textStyle({ bold: true, fg: theme.background, bg: theme.muted }), children: " m " }),
2151
2236
  /* @__PURE__ */ jsx32("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " to open modal" })
@@ -2153,9 +2238,9 @@ function ModalApp() {
2153
2238
  }
2154
2239
 
2155
2240
  // demos/primitives.tsx
2156
- import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
2241
+ import { jsx as jsx33, jsxs as jsxs25 } from "react/jsx-runtime";
2157
2242
  function PrimitivesApp() {
2158
- return /* @__PURE__ */ jsxs26("box", { flexDirection: "column", padding: 1, children: [
2243
+ return /* @__PURE__ */ jsxs25("box", { flexDirection: "column", padding: 1, children: [
2159
2244
  /* @__PURE__ */ jsx33(
2160
2245
  "box",
2161
2246
  {
@@ -2165,7 +2250,7 @@ function PrimitivesApp() {
2165
2250
  title: "Layout",
2166
2251
  titleAlignment: "center",
2167
2252
  padding: 1,
2168
- children: /* @__PURE__ */ jsxs26("box", { flexDirection: "row", gap: 2, children: [
2253
+ children: /* @__PURE__ */ jsxs25("box", { flexDirection: "row", gap: 2, children: [
2169
2254
  /* @__PURE__ */ jsx33("box", { border: true, borderStyle: "single", borderColor: "#a6e22e", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx33("text", { fg: "#a6e22e", bold: true, children: "Box 1" }) }),
2170
2255
  /* @__PURE__ */ jsx33("box", { border: true, borderStyle: "single", borderColor: "#f92672", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx33("text", { fg: "#f92672", bold: true, children: "Box 2" }) }),
2171
2256
  /* @__PURE__ */ jsx33("box", { border: true, borderStyle: "single", borderColor: "#66d9ef", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx33("text", { fg: "#66d9ef", bold: true, children: "Box 3" }) })
@@ -2248,7 +2333,7 @@ function ChatApp() {
2248
2333
  // demos/chain-of-thought.tsx
2249
2334
  import { useState as useState19, useEffect as useEffect4, useRef as useRef6 } from "react";
2250
2335
  import { useKeyboard as useKeyboard13 } from "@gridland/utils";
2251
- import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
2336
+ import { jsx as jsx35, jsxs as jsxs26 } from "react/jsx-runtime";
2252
2337
  var ALL_STEPS = [
2253
2338
  { tool: "Read", label: "Reading codebase", description: "src/", status: "done", delay: 1800 },
2254
2339
  { tool: "Think", label: "Planning changes", description: "auth module", status: "done", delay: 2500 },
@@ -2298,8 +2383,8 @@ function ChainOfThoughtApp() {
2298
2383
  const elapsedMs = ALL_STEPS.slice(0, stepIndex).reduce((sum, s) => sum + s.delay, 0);
2299
2384
  const totalMs = ALL_STEPS.reduce((sum, s) => sum + s.delay, 0);
2300
2385
  const durationStr = phase === "done" ? `${(totalMs / 1e3).toFixed(1)}s` : `${(elapsedMs / 1e3).toFixed(1)}s`;
2301
- return /* @__PURE__ */ jsxs27("box", { flexDirection: "column", flexGrow: 1, children: [
2302
- /* @__PURE__ */ jsx35("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs27(ChainOfThought, { open: expanded, onOpenChange: setExpanded, children: [
2386
+ return /* @__PURE__ */ jsxs26("box", { flexDirection: "column", flexGrow: 1, children: [
2387
+ /* @__PURE__ */ jsx35("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs26(ChainOfThought, { open: expanded, onOpenChange: setExpanded, children: [
2303
2388
  /* @__PURE__ */ jsx35(ChainOfThoughtHeader, { duration: durationStr }),
2304
2389
  /* @__PURE__ */ jsx35(ChainOfThoughtContent, { children: steps.map((step, i) => /* @__PURE__ */ jsx35(
2305
2390
  ChainOfThoughtStep,
@@ -2323,7 +2408,7 @@ function ChainOfThoughtApp() {
2323
2408
  // demos/message.tsx
2324
2409
  import { useState as useState20, useEffect as useEffect5, useRef as useRef7 } from "react";
2325
2410
  import { useKeyboard as useKeyboard14 } from "@gridland/utils";
2326
- import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
2411
+ import { jsx as jsx36, jsxs as jsxs27 } from "react/jsx-runtime";
2327
2412
  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.";
2328
2413
  function MessageApp() {
2329
2414
  const [phase, setPhase] = useState20("idle");
@@ -2369,8 +2454,8 @@ function MessageApp() {
2369
2454
  const isStreaming = phase === "streaming";
2370
2455
  const isDone = phase === "done";
2371
2456
  const showAssistant = phase !== "idle";
2372
- return /* @__PURE__ */ jsxs28("box", { flexDirection: "column", flexGrow: 1, children: [
2373
- /* @__PURE__ */ jsxs28("box", { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1, children: [
2457
+ return /* @__PURE__ */ jsxs27("box", { flexDirection: "column", flexGrow: 1, children: [
2458
+ /* @__PURE__ */ jsxs27("box", { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1, children: [
2374
2459
  /* @__PURE__ */ jsx36(Message, { role: "user", children: /* @__PURE__ */ jsx36(Message.Content, { children: /* @__PURE__ */ jsx36(Message.Text, { children: "Can you refactor the auth module?" }) }) }),
2375
2460
  showAssistant && /* @__PURE__ */ jsx36(Message, { role: "assistant", isStreaming, children: /* @__PURE__ */ jsx36(Message.Content, { children: /* @__PURE__ */ jsx36(Message.Text, { isLast: true, children: isDone ? RESPONSE : streamedText }) }) })
2376
2461
  ] }),
@@ -2379,10 +2464,10 @@ function MessageApp() {
2379
2464
  }
2380
2465
 
2381
2466
  // demos/terminal-window.tsx
2382
- import { jsx as jsx37, jsxs as jsxs29 } from "react/jsx-runtime";
2467
+ import { jsx as jsx37, jsxs as jsxs28 } from "react/jsx-runtime";
2383
2468
  function TerminalWindowApp() {
2384
2469
  const theme = useTheme();
2385
- return /* @__PURE__ */ jsxs29("box", { flexDirection: "column", padding: 1, children: [
2470
+ return /* @__PURE__ */ jsxs28("box", { flexDirection: "column", padding: 1, children: [
2386
2471
  /* @__PURE__ */ jsx37("text", { style: textStyle({ fg: theme.secondary }), children: '$ echo "Hello from TerminalWindow"' }),
2387
2472
  /* @__PURE__ */ jsx37("text", { style: textStyle({ fg: theme.foreground }), children: "Hello from TerminalWindow" }),
2388
2473
  /* @__PURE__ */ jsx37("text", { style: textStyle({ fg: theme.secondary }), children: "$ _" })
@@ -2390,28 +2475,33 @@ function TerminalWindowApp() {
2390
2475
  }
2391
2476
 
2392
2477
  // demos/focus.tsx
2393
- import { useState as useState21, useRef as useRef8 } from "react";
2394
- import { useKeyboard as useKeyboard15 } from "@gridland/utils";
2395
- import { Fragment as Fragment7, jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
2396
- var focusPanels = [
2478
+ import { useRef as useRef8, useCallback as useCallback4 } from "react";
2479
+ import { useKeyboard as useKeyboard15, useFocus, FocusProvider, useShortcuts, useFocusedShortcuts } from "@gridland/utils";
2480
+ import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
2481
+ var focusMultiSelects = [
2397
2482
  {
2398
- label: "Language",
2483
+ id: "language",
2484
+ title: "Language",
2399
2485
  items: [
2400
2486
  { label: "TypeScript", value: "ts" },
2401
2487
  { label: "JavaScript", value: "js" },
2402
- { label: "Python", value: "py" }
2488
+ { label: "Python", value: "py" },
2489
+ { label: "Rust", value: "rs" }
2403
2490
  ]
2404
2491
  },
2405
2492
  {
2406
- label: "Framework",
2493
+ id: "framework",
2494
+ title: "Framework",
2407
2495
  items: [
2408
2496
  { label: "React", value: "react" },
2409
2497
  { label: "Vue", value: "vue" },
2410
- { label: "Svelte", value: "svelte" }
2498
+ { label: "Svelte", value: "svelte" },
2499
+ { label: "Solid", value: "solid" }
2411
2500
  ]
2412
2501
  },
2413
2502
  {
2414
- label: "Runtime",
2503
+ id: "runtime",
2504
+ title: "Runtime",
2415
2505
  items: [
2416
2506
  { label: "Bun", value: "bun" },
2417
2507
  { label: "Node", value: "node" },
@@ -2419,118 +2509,168 @@ var focusPanels = [
2419
2509
  ]
2420
2510
  }
2421
2511
  ];
2422
- function FocusApp() {
2423
- const [panelIndex, setPanelIndex] = useState21(0);
2424
- const [entered, setEntered] = useState21(false);
2425
- const [cursors, setCursors] = useState21([0, 0, 0]);
2426
- const [selections, setSelections] = useState21([null, null, null]);
2427
- const panelRef = useRef8(0);
2428
- const enteredRef = useRef8(false);
2429
- const cursorsRef = useRef8([0, 0, 0]);
2430
- panelRef.current = panelIndex;
2431
- enteredRef.current = entered;
2432
- cursorsRef.current = cursors;
2512
+ function FocusMultiSelectPanel({ id, title, items: items3, autoFocus }) {
2513
+ const { isFocused, isSelected, isAnySelected, focusId, focusRef } = useFocus({ id, autoFocus });
2514
+ const multiSelectHandlerRef = useRef8(null);
2515
+ const captureKeyboard = useCallback4((handler) => {
2516
+ multiSelectHandlerRef.current = handler;
2517
+ }, []);
2433
2518
  useKeyboard15((event) => {
2434
- const pi = panelRef.current;
2435
- if (enteredRef.current) {
2436
- const items3 = focusPanels[pi].items;
2437
- const cur = cursorsRef.current[pi];
2438
- if (event.name === "down" || event.name === "j") {
2439
- const next = (cur + 1) % items3.length;
2440
- cursorsRef.current = [...cursorsRef.current];
2441
- cursorsRef.current[pi] = next;
2442
- setCursors([...cursorsRef.current]);
2443
- } else if (event.name === "up" || event.name === "k") {
2444
- const next = (cur - 1 + items3.length) % items3.length;
2445
- cursorsRef.current = [...cursorsRef.current];
2446
- cursorsRef.current[pi] = next;
2447
- setCursors([...cursorsRef.current]);
2448
- } else if (event.name === "return") {
2449
- const selected = items3[cursorsRef.current[pi]].label;
2450
- setSelections((s) => {
2451
- const n = [...s];
2452
- n[pi] = selected;
2453
- return n;
2454
- });
2455
- enteredRef.current = false;
2456
- setEntered(false);
2457
- } else if (event.name === "escape") {
2458
- enteredRef.current = false;
2459
- setEntered(false);
2460
- }
2461
- } else {
2462
- if (event.name === "right" || event.name === "tab") {
2463
- const next = (pi + 1) % focusPanels.length;
2464
- panelRef.current = next;
2465
- setPanelIndex(next);
2466
- } else if (event.name === "left") {
2467
- const next = (pi - 1 + focusPanels.length) % focusPanels.length;
2468
- panelRef.current = next;
2469
- setPanelIndex(next);
2470
- } else if (event.name === "return") {
2471
- enteredRef.current = true;
2472
- setEntered(true);
2473
- }
2519
+ multiSelectHandlerRef.current?.(event);
2520
+ }, { focusId, selectedOnly: true });
2521
+ useShortcuts(
2522
+ isSelected ? [{ key: "\u2191\u2193", label: "move" }, { key: "enter", label: "toggle" }, { key: "esc", label: "back" }] : [{ key: "\u2190\u2192", label: "navigate" }, { key: "tab", label: "cycle" }, { key: "enter", label: "select" }],
2523
+ focusId
2524
+ );
2525
+ const borderStyle = isSelected ? "rounded" : isFocused ? "dashed" : "rounded";
2526
+ const borderColor = isSelected ? "#818cf8" : isAnySelected ? "transparent" : isFocused ? "#6366f1" : "#3b3466";
2527
+ return /* @__PURE__ */ jsx38("box", { ref: focusRef, border: true, borderStyle, borderColor, flexGrow: 1, children: /* @__PURE__ */ jsx38("box", { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx38(
2528
+ MultiSelect,
2529
+ {
2530
+ items: items3,
2531
+ title,
2532
+ allowEmpty: true,
2533
+ enableSelectAll: false,
2534
+ enableClear: false,
2535
+ highlightColor: isSelected ? "#a5b4fc" : "#6366f1",
2536
+ checkboxColor: "#818cf8",
2537
+ useKeyboard: captureKeyboard
2474
2538
  }
2475
- event.preventDefault();
2476
- });
2477
- return /* @__PURE__ */ jsxs30("box", { flexDirection: "column", flexGrow: 1, children: [
2478
- /* @__PURE__ */ jsx38("box", { flexDirection: "row", gap: 1, padding: 1, flexGrow: 1, children: focusPanels.map((panel, i) => {
2479
- const focused = i === panelIndex;
2480
- const active = focused && entered;
2481
- const selected = selections[i];
2482
- return /* @__PURE__ */ jsx38(
2483
- "box",
2484
- {
2485
- border: true,
2486
- borderStyle: "rounded",
2487
- borderColor: active ? "#22c55e" : focused ? "#3b82f6" : "#555",
2488
- flexGrow: 1,
2489
- children: /* @__PURE__ */ jsxs30("box", { flexDirection: "column", padding: 1, children: [
2490
- /* @__PURE__ */ jsxs30("text", { style: {
2491
- fg: active ? "#22c55e" : focused ? "#3b82f6" : "#888",
2492
- bold: focused
2493
- }, children: [
2494
- focused ? "\u25B8 " : " ",
2495
- panel.label,
2496
- selected ? `: ${selected}` : ""
2497
- ] }),
2498
- (active || !entered && focused) && /* @__PURE__ */ jsxs30(Fragment7, { children: [
2499
- /* @__PURE__ */ jsx38("box", { height: 1 }),
2500
- /* @__PURE__ */ jsx38("box", { flexDirection: "column", children: panel.items.map((item, j) => {
2501
- const highlighted = active && j === cursors[i];
2502
- return /* @__PURE__ */ jsxs30("text", { style: {
2503
- fg: highlighted ? "#22c55e" : active ? "#ccc" : "#666",
2504
- bold: highlighted
2505
- }, children: [
2506
- highlighted ? " \u25B8 " : " ",
2507
- item.label
2508
- ] }, item.value);
2509
- }) })
2510
- ] })
2511
- ] })
2512
- },
2513
- panel.label
2514
- );
2515
- }) }),
2516
- /* @__PURE__ */ jsx38("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx38(
2517
- StatusBar,
2539
+ ) }) });
2540
+ }
2541
+ function FocusNavStatusBar() {
2542
+ const shortcuts2 = useFocusedShortcuts();
2543
+ return /* @__PURE__ */ jsx38("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx38(StatusBar, { items: shortcuts2 }) });
2544
+ }
2545
+ function FocusApp() {
2546
+ return /* @__PURE__ */ jsx38(FocusProvider, { selectable: true, children: /* @__PURE__ */ jsxs29("box", { flexDirection: "column", flexGrow: 1, children: [
2547
+ /* @__PURE__ */ jsx38("box", { flexDirection: "row", gap: 1, padding: 1, flexGrow: 1, children: focusMultiSelects.map((panel, i) => /* @__PURE__ */ jsx38(
2548
+ FocusMultiSelectPanel,
2518
2549
  {
2519
- 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" }]
2520
- }
2521
- ) })
2522
- ] });
2550
+ id: panel.id,
2551
+ title: panel.title,
2552
+ items: panel.items,
2553
+ autoFocus: i === 0
2554
+ },
2555
+ panel.id
2556
+ )) }),
2557
+ /* @__PURE__ */ jsx38(FocusNavStatusBar, {})
2558
+ ] }) });
2559
+ }
2560
+
2561
+ // demos/focus-grid.tsx
2562
+ import { useFocus as useFocus2, FocusProvider as FocusProvider2, useShortcuts as useShortcuts2, useFocusedShortcuts as useFocusedShortcuts2 } from "@gridland/utils";
2563
+ import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
2564
+ var gridItems = [
2565
+ { id: "cell-1" },
2566
+ { id: "cell-2" },
2567
+ { id: "cell-3" },
2568
+ { id: "cell-4" },
2569
+ { id: "cell-5" },
2570
+ { id: "cell-6" }
2571
+ ];
2572
+ function GridCell({ id, autoFocus }) {
2573
+ const { isFocused, isSelected, isAnySelected, focusId, focusRef } = useFocus2({ id, autoFocus });
2574
+ useShortcuts2(
2575
+ isSelected ? [{ key: "esc", label: "back" }] : [{ key: "\u2191\u2193\u2190\u2192", label: "navigate" }, { key: "enter", label: "select" }, { key: "tab", label: "cycle" }],
2576
+ focusId
2577
+ );
2578
+ const borderStyle = isSelected ? "rounded" : isFocused ? "dashed" : "rounded";
2579
+ const borderColor = isSelected ? "#818cf8" : isAnySelected ? "transparent" : isFocused ? "#6366f1" : "#3b3466";
2580
+ const fg = isSelected ? "#a5b4fc" : isFocused ? "#a5b4fc" : "#888";
2581
+ return /* @__PURE__ */ jsx39("box", { ref: focusRef, border: true, borderStyle, borderColor, width: 16, height: 5, children: /* @__PURE__ */ jsx39("box", { flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: /* @__PURE__ */ jsx39("text", { style: { fg, bold: isFocused || isSelected }, children: isSelected ? "selected" : "not selected" }) }) });
2582
+ }
2583
+ function FocusGridStatusBar() {
2584
+ const shortcuts2 = useFocusedShortcuts2();
2585
+ return /* @__PURE__ */ jsx39("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx39(StatusBar, { items: shortcuts2 }) });
2586
+ }
2587
+ function FocusGridApp() {
2588
+ return /* @__PURE__ */ jsx39(FocusProvider2, { selectable: true, children: /* @__PURE__ */ jsxs30("box", { flexDirection: "column", flexGrow: 1, children: [
2589
+ /* @__PURE__ */ jsxs30("box", { flexDirection: "column", gap: 1, padding: 1, flexGrow: 1, alignItems: "center", children: [
2590
+ /* @__PURE__ */ jsx39("box", { flexDirection: "row", gap: 1, children: gridItems.slice(0, 3).map((item, i) => /* @__PURE__ */ jsx39(GridCell, { ...item, autoFocus: i === 0 }, item.id)) }),
2591
+ /* @__PURE__ */ jsx39("box", { flexDirection: "row", gap: 1, children: gridItems.slice(3, 6).map((item) => /* @__PURE__ */ jsx39(GridCell, { ...item }, item.id)) })
2592
+ ] }),
2593
+ /* @__PURE__ */ jsx39(FocusGridStatusBar, {})
2594
+ ] }) });
2595
+ }
2596
+
2597
+ // demos/focus-chat.tsx
2598
+ import { useState as useState22, useRef as useRef9, useCallback as useCallback5 } from "react";
2599
+ import { useKeyboard as useKeyboard16, useFocus as useFocus3, FocusProvider as FocusProvider3, useShortcuts as useShortcuts3, useFocusedShortcuts as useFocusedShortcuts3 } from "@gridland/utils";
2600
+ import { jsx as jsx40, jsxs as jsxs31 } from "react/jsx-runtime";
2601
+ function CotSection() {
2602
+ const { isFocused, isSelected, isAnySelected, focusId, focusRef } = useFocus3({ id: "cot", autoFocus: true });
2603
+ const [cotOpen, setCotOpen] = useState22(false);
2604
+ useKeyboard16((event) => {
2605
+ if (event.name === "return") {
2606
+ setCotOpen((v) => !v);
2607
+ event.preventDefault();
2608
+ }
2609
+ }, { focusId, selectedOnly: true });
2610
+ useShortcuts3(
2611
+ isSelected ? [{ key: "enter", label: "expand/collapse" }, { key: "esc", label: "back" }] : [{ key: "\u2191\u2193", label: "navigate" }, { key: "tab", label: "cycle" }, { key: "enter", label: "select" }],
2612
+ focusId
2613
+ );
2614
+ const borderStyle = isSelected ? "rounded" : isFocused ? "dashed" : "rounded";
2615
+ const borderColor = isSelected ? "#818cf8" : isAnySelected ? "transparent" : isFocused ? "#6366f1" : "#3b3466";
2616
+ return /* @__PURE__ */ jsx40("box", { ref: focusRef, marginTop: 1, border: true, borderStyle, borderColor, children: /* @__PURE__ */ jsxs31(ChainOfThought, { open: cotOpen, onOpenChange: setCotOpen, children: [
2617
+ /* @__PURE__ */ jsx40(ChainOfThoughtHeader, { duration: "1.2s" }),
2618
+ /* @__PURE__ */ jsx40(ChainOfThoughtContent, { children: /* @__PURE__ */ jsx40(ChainOfThoughtStep, { label: "Searched docs", status: "done", isLast: true }) })
2619
+ ] }) });
2620
+ }
2621
+ function PromptSection() {
2622
+ const { isFocused, isSelected, isAnySelected, focusId, focusRef } = useFocus3({ id: "prompt" });
2623
+ const promptHandlerRef = useRef9(null);
2624
+ const captureKeyboard = useCallback5((handler) => {
2625
+ promptHandlerRef.current = handler;
2626
+ }, []);
2627
+ useKeyboard16((event) => {
2628
+ promptHandlerRef.current?.(event);
2629
+ }, { focusId, selectedOnly: true });
2630
+ useShortcuts3(
2631
+ isSelected ? [{ key: "\u23CE", label: "send" }, { key: "esc", label: "back" }] : [{ key: "\u2191\u2193", label: "navigate" }, { key: "tab", label: "cycle" }, { key: "enter", label: "select" }],
2632
+ focusId
2633
+ );
2634
+ const dividerColor = isSelected ? "#818cf8" : isAnySelected ? void 0 : isFocused ? "#6366f1" : "#3b3466";
2635
+ const dividerDashed = isFocused && !isSelected && !isAnySelected;
2636
+ return /* @__PURE__ */ jsx40("box", { ref: focusRef, children: /* @__PURE__ */ jsx40(
2637
+ PromptInput,
2638
+ {
2639
+ placeholder: "Type a message...",
2640
+ status: "ready",
2641
+ dividerColor,
2642
+ dividerDashed,
2643
+ useKeyboard: captureKeyboard
2644
+ }
2645
+ ) });
2646
+ }
2647
+ function FocusChatStatusBar() {
2648
+ const shortcuts2 = useFocusedShortcuts3();
2649
+ return /* @__PURE__ */ jsx40("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx40(StatusBar, { items: shortcuts2 }) });
2650
+ }
2651
+ function FocusChatApp() {
2652
+ return /* @__PURE__ */ jsx40(FocusProvider3, { selectable: true, children: /* @__PURE__ */ jsxs31("box", { flexDirection: "column", flexGrow: 1, children: [
2653
+ /* @__PURE__ */ jsxs31("box", { flexDirection: "column", flexGrow: 1, children: [
2654
+ /* @__PURE__ */ jsxs31("box", { flexDirection: "column", paddingX: 1, paddingTop: 1, flexGrow: 1, children: [
2655
+ /* @__PURE__ */ jsx40(Message, { role: "user", children: /* @__PURE__ */ jsx40(Message.Content, { children: /* @__PURE__ */ jsx40(Message.Text, { children: "How do I set up keyboard navigation?" }) }) }),
2656
+ /* @__PURE__ */ jsx40(CotSection, {}),
2657
+ /* @__PURE__ */ jsx40(Message, { role: "assistant", children: /* @__PURE__ */ jsx40(Message.Content, { children: /* @__PURE__ */ jsx40(Message.Text, { children: "Use the useKeyboard hook to listen for key events. Wrap your app in a FocusProvider to enable tab navigation between focusable components." }) }) })
2658
+ ] }),
2659
+ /* @__PURE__ */ jsx40(PromptSection, {})
2660
+ ] }),
2661
+ /* @__PURE__ */ jsx40(FocusChatStatusBar, {})
2662
+ ] }) });
2523
2663
  }
2524
2664
 
2525
2665
  // demos/pointer.tsx
2526
- import { useState as useState22, useRef as useRef9 } from "react";
2527
- import { useKeyboard as useKeyboard16 } from "@gridland/utils";
2528
- import { jsx as jsx39, jsxs as jsxs31 } from "react/jsx-runtime";
2666
+ import { useState as useState23, useRef as useRef10 } from "react";
2667
+ import { useKeyboard as useKeyboard17 } from "@gridland/utils";
2668
+ import { jsx as jsx41, jsxs as jsxs32 } from "react/jsx-runtime";
2529
2669
  var pointerColors = ["#ef4444", "#f97316", "#eab308", "#22c55e", "#3b82f6", "#8b5cf6"];
2530
2670
  var pointerColorNames = ["Red", "Orange", "Yellow", "Green", "Blue", "Purple"];
2531
2671
  function HoverBox() {
2532
- const [hovering, setHovering] = useState22(false);
2533
- return /* @__PURE__ */ jsx39(
2672
+ const [hovering, setHovering] = useState23(false);
2673
+ return /* @__PURE__ */ jsx41(
2534
2674
  "box",
2535
2675
  {
2536
2676
  border: true,
@@ -2540,19 +2680,19 @@ function HoverBox() {
2540
2680
  height: 5,
2541
2681
  onMouseOver: () => setHovering(true),
2542
2682
  onMouseOut: () => setHovering(false),
2543
- children: /* @__PURE__ */ jsx39("box", { padding: 1, children: /* @__PURE__ */ jsx39("text", { style: { fg: hovering ? "#22c55e" : "#888", bold: hovering }, children: hovering ? "Mouse inside!" : "Hover me" }) })
2683
+ children: /* @__PURE__ */ jsx41("box", { padding: 1, children: /* @__PURE__ */ jsx41("text", { style: { fg: hovering ? "#22c55e" : "#888", bold: hovering }, children: hovering ? "Mouse inside!" : "Hover me" }) })
2544
2684
  }
2545
2685
  );
2546
2686
  }
2547
2687
  function PointerApp() {
2548
- const [selected, setSelected] = useState22(null);
2549
- const [clickCount, setClickCount] = useState22(0);
2550
- const [mousePos, setMousePos] = useState22(null);
2551
- const selectedRef = useRef9(null);
2552
- const clickCountRef = useRef9(0);
2688
+ const [selected, setSelected] = useState23(null);
2689
+ const [clickCount, setClickCount] = useState23(0);
2690
+ const [mousePos, setMousePos] = useState23(null);
2691
+ const selectedRef = useRef10(null);
2692
+ const clickCountRef = useRef10(0);
2553
2693
  selectedRef.current = selected;
2554
2694
  clickCountRef.current = clickCount;
2555
- useKeyboard16((event) => {
2695
+ useKeyboard17((event) => {
2556
2696
  const cur = selectedRef.current ?? -1;
2557
2697
  if (event.name === "right" || event.name === "tab") {
2558
2698
  const next = (cur + 1) % pointerColors.length;
@@ -2565,8 +2705,8 @@ function PointerApp() {
2565
2705
  }
2566
2706
  event.preventDefault();
2567
2707
  });
2568
- return /* @__PURE__ */ jsxs31("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
2569
- /* @__PURE__ */ jsx39("box", { flexDirection: "row", gap: 1, children: pointerColors.map((color, i) => /* @__PURE__ */ jsx39(
2708
+ return /* @__PURE__ */ jsxs32("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
2709
+ /* @__PURE__ */ jsx41("box", { flexDirection: "row", gap: 1, children: pointerColors.map((color, i) => /* @__PURE__ */ jsx41(
2570
2710
  "box",
2571
2711
  {
2572
2712
  flexGrow: 1,
@@ -2581,22 +2721,22 @@ function PointerApp() {
2581
2721
  setSelected(i);
2582
2722
  setMousePos({ x: e.x, y: e.y });
2583
2723
  },
2584
- children: /* @__PURE__ */ jsx39("text", { style: { fg: color, bold: i === selected }, children: i === selected ? `\u25B8 ${pointerColorNames[i]}` : ` ${pointerColorNames[i]}` })
2724
+ children: /* @__PURE__ */ jsx41("text", { style: { fg: color, bold: i === selected }, children: i === selected ? `\u25B8 ${pointerColorNames[i]}` : ` ${pointerColorNames[i]}` })
2585
2725
  },
2586
2726
  color
2587
2727
  )) }),
2588
- /* @__PURE__ */ jsx39("box", { height: 1 }),
2589
- /* @__PURE__ */ jsxs31("box", { flexDirection: "row", gap: 2, children: [
2590
- /* @__PURE__ */ jsx39(HoverBox, {}),
2591
- /* @__PURE__ */ jsxs31("box", { flexDirection: "column", flexGrow: 1, paddingTop: 1, children: [
2592
- /* @__PURE__ */ jsxs31("text", { style: { fg: selected !== null ? pointerColors[selected] : "#888" }, children: [
2728
+ /* @__PURE__ */ jsx41("box", { height: 1 }),
2729
+ /* @__PURE__ */ jsxs32("box", { flexDirection: "row", gap: 2, children: [
2730
+ /* @__PURE__ */ jsx41(HoverBox, {}),
2731
+ /* @__PURE__ */ jsxs32("box", { flexDirection: "column", flexGrow: 1, paddingTop: 1, children: [
2732
+ /* @__PURE__ */ jsxs32("text", { style: { fg: selected !== null ? pointerColors[selected] : "#888" }, children: [
2593
2733
  selected !== null ? `Clicked ${pointerColorNames[selected]}` : "Click a color",
2594
2734
  clickCount > 0 ? ` (${clickCount} clicks)` : ""
2595
2735
  ] }),
2596
- /* @__PURE__ */ jsx39("text", { style: { dim: true, fg: "#888" }, children: mousePos ? `mouse: ${mousePos.x}, ${mousePos.y}` : "" })
2736
+ /* @__PURE__ */ jsx41("text", { style: { dim: true, fg: "#888" }, children: mousePos ? `mouse: ${mousePos.x}, ${mousePos.y}` : "" })
2597
2737
  ] })
2598
2738
  ] }),
2599
- /* @__PURE__ */ jsx39("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx39(StatusBar, { items: [
2739
+ /* @__PURE__ */ jsx41("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx41(StatusBar, { items: [
2600
2740
  { key: "click", label: "select" },
2601
2741
  { key: "\u2190\u2192", label: "keyboard nav" }
2602
2742
  ] }) })
@@ -2604,11 +2744,11 @@ function PointerApp() {
2604
2744
  }
2605
2745
 
2606
2746
  // demos/cursor-highlight.tsx
2607
- import { useState as useState23 } from "react";
2608
- import { jsx as jsx40, jsxs as jsxs32 } from "react/jsx-runtime";
2747
+ import { useState as useState24 } from "react";
2748
+ import { jsx as jsx42, jsxs as jsxs33 } from "react/jsx-runtime";
2609
2749
  function CursorHighlightApp() {
2610
- const [pos, setPos] = useState23(null);
2611
- return /* @__PURE__ */ jsxs32(
2750
+ const [pos, setPos] = useState24(null);
2751
+ return /* @__PURE__ */ jsxs33(
2612
2752
  "box",
2613
2753
  {
2614
2754
  flexDirection: "column",
@@ -2617,27 +2757,27 @@ function CursorHighlightApp() {
2617
2757
  setPos({ x: e.x, y: e.y });
2618
2758
  },
2619
2759
  children: [
2620
- /* @__PURE__ */ jsxs32("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
2621
- /* @__PURE__ */ jsx40("text", { style: { bold: true, fg: "#fff" }, children: "Cursor Highlight" }),
2622
- /* @__PURE__ */ jsx40("text", { style: { dim: true, fg: "#888" }, children: "Move your mouse over the grid" }),
2623
- /* @__PURE__ */ jsx40("box", { height: 1 }),
2624
- /* @__PURE__ */ jsx40("box", { flexDirection: "column", children: Array.from({ length: 6 }, (_, row) => /* @__PURE__ */ jsx40("text", { children: Array.from({ length: 40 }, (_2, col) => {
2760
+ /* @__PURE__ */ jsxs33("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
2761
+ /* @__PURE__ */ jsx42("text", { style: { bold: true, fg: "#fff" }, children: "Cursor Highlight" }),
2762
+ /* @__PURE__ */ jsx42("text", { style: { dim: true, fg: "#888" }, children: "Move your mouse over the grid" }),
2763
+ /* @__PURE__ */ jsx42("box", { height: 1 }),
2764
+ /* @__PURE__ */ jsx42("box", { flexDirection: "column", children: Array.from({ length: 6 }, (_, row) => /* @__PURE__ */ jsx42("text", { children: Array.from({ length: 40 }, (_2, col) => {
2625
2765
  const isEven = (row + col) % 2 === 0;
2626
- return /* @__PURE__ */ jsx40("span", { style: {
2766
+ return /* @__PURE__ */ jsx42("span", { style: {
2627
2767
  fg: isEven ? "#3b82f6" : "#8b5cf6",
2628
2768
  dim: !isEven
2629
2769
  }, children: isEven ? "\u2591\u2591" : "\u2593\u2593" }, col);
2630
2770
  }) }, row)) })
2631
2771
  ] }),
2632
- /* @__PURE__ */ jsx40("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx40(
2772
+ /* @__PURE__ */ jsx42("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx42(
2633
2773
  StatusBar,
2634
2774
  {
2635
2775
  items: [],
2636
- extra: /* @__PURE__ */ jsxs32("span", { children: [
2637
- /* @__PURE__ */ jsx40("span", { style: textStyle({ bold: true, fg: "#1e1e2e", bg: "#888" }), children: " x " }),
2638
- /* @__PURE__ */ jsx40("span", { style: textStyle({ dim: true, fg: "#888" }), children: ` ${pos ? String(pos.x).padStart(3) : " -"} ` }),
2639
- /* @__PURE__ */ jsx40("span", { style: textStyle({ bold: true, fg: "#1e1e2e", bg: "#888" }), children: " y " }),
2640
- /* @__PURE__ */ jsx40("span", { style: textStyle({ dim: true, fg: "#888" }), children: ` ${pos ? String(pos.y).padStart(3) : " -"}` })
2776
+ extra: /* @__PURE__ */ jsxs33("span", { children: [
2777
+ /* @__PURE__ */ jsx42("span", { style: textStyle({ bold: true, fg: "#1e1e2e", bg: "#888" }), children: " x " }),
2778
+ /* @__PURE__ */ jsx42("span", { style: textStyle({ dim: true, fg: "#888" }), children: ` ${pos ? String(pos.x).padStart(3) : " -"} ` }),
2779
+ /* @__PURE__ */ jsx42("span", { style: textStyle({ bold: true, fg: "#1e1e2e", bg: "#888" }), children: " y " }),
2780
+ /* @__PURE__ */ jsx42("span", { style: textStyle({ dim: true, fg: "#888" }), children: ` ${pos ? String(pos.y).padStart(3) : " -"}` })
2641
2781
  ] })
2642
2782
  }
2643
2783
  ) })
@@ -2647,14 +2787,14 @@ function CursorHighlightApp() {
2647
2787
  }
2648
2788
 
2649
2789
  // demos/text-style.tsx
2650
- import { jsx as jsx41, jsxs as jsxs33 } from "react/jsx-runtime";
2790
+ import { jsx as jsx43, jsxs as jsxs34 } from "react/jsx-runtime";
2651
2791
  function TextStyleApp() {
2652
2792
  const theme = useTheme();
2653
2793
  const desc = textStyle({ fg: theme.muted });
2654
- return /* @__PURE__ */ jsxs33("box", { flexDirection: "column", padding: 1, gap: 0, children: [
2655
- /* @__PURE__ */ jsxs33("text", { children: [
2656
- /* @__PURE__ */ jsx41("span", { style: textStyle({ fg: theme.foreground, bold: true }), children: "bold " }),
2657
- /* @__PURE__ */ jsxs33("span", { style: desc, children: [
2794
+ return /* @__PURE__ */ jsxs34("box", { flexDirection: "column", padding: 1, gap: 0, children: [
2795
+ /* @__PURE__ */ jsxs34("text", { children: [
2796
+ /* @__PURE__ */ jsx43("span", { style: textStyle({ fg: theme.foreground, bold: true }), children: "bold " }),
2797
+ /* @__PURE__ */ jsxs34("span", { style: desc, children: [
2658
2798
  "textStyle(",
2659
2799
  "{",
2660
2800
  " bold: true ",
@@ -2662,9 +2802,9 @@ function TextStyleApp() {
2662
2802
  ")"
2663
2803
  ] })
2664
2804
  ] }),
2665
- /* @__PURE__ */ jsxs33("text", { children: [
2666
- /* @__PURE__ */ jsx41("span", { style: textStyle({ fg: theme.foreground, dim: true }), children: "dim " }),
2667
- /* @__PURE__ */ jsxs33("span", { style: desc, children: [
2805
+ /* @__PURE__ */ jsxs34("text", { children: [
2806
+ /* @__PURE__ */ jsx43("span", { style: textStyle({ fg: theme.foreground, dim: true }), children: "dim " }),
2807
+ /* @__PURE__ */ jsxs34("span", { style: desc, children: [
2668
2808
  "textStyle(",
2669
2809
  "{",
2670
2810
  " dim: true ",
@@ -2672,9 +2812,9 @@ function TextStyleApp() {
2672
2812
  ")"
2673
2813
  ] })
2674
2814
  ] }),
2675
- /* @__PURE__ */ jsxs33("text", { children: [
2676
- /* @__PURE__ */ jsx41("span", { style: textStyle({ fg: theme.foreground, italic: true }), children: "italic " }),
2677
- /* @__PURE__ */ jsxs33("span", { style: desc, children: [
2815
+ /* @__PURE__ */ jsxs34("text", { children: [
2816
+ /* @__PURE__ */ jsx43("span", { style: textStyle({ fg: theme.foreground, italic: true }), children: "italic " }),
2817
+ /* @__PURE__ */ jsxs34("span", { style: desc, children: [
2678
2818
  "textStyle(",
2679
2819
  "{",
2680
2820
  " italic: true ",
@@ -2682,9 +2822,9 @@ function TextStyleApp() {
2682
2822
  ")"
2683
2823
  ] })
2684
2824
  ] }),
2685
- /* @__PURE__ */ jsxs33("text", { children: [
2686
- /* @__PURE__ */ jsx41("span", { style: textStyle({ fg: theme.foreground, underline: true }), children: "underline " }),
2687
- /* @__PURE__ */ jsxs33("span", { style: desc, children: [
2825
+ /* @__PURE__ */ jsxs34("text", { children: [
2826
+ /* @__PURE__ */ jsx43("span", { style: textStyle({ fg: theme.foreground, underline: true }), children: "underline " }),
2827
+ /* @__PURE__ */ jsxs34("span", { style: desc, children: [
2688
2828
  "textStyle(",
2689
2829
  "{",
2690
2830
  " underline: true ",
@@ -2692,9 +2832,9 @@ function TextStyleApp() {
2692
2832
  ")"
2693
2833
  ] })
2694
2834
  ] }),
2695
- /* @__PURE__ */ jsxs33("text", { children: [
2696
- /* @__PURE__ */ jsx41("span", { style: textStyle({ inverse: true }), children: "inverse " }),
2697
- /* @__PURE__ */ jsxs33("span", { style: desc, children: [
2835
+ /* @__PURE__ */ jsxs34("text", { children: [
2836
+ /* @__PURE__ */ jsx43("span", { style: textStyle({ inverse: true }), children: "inverse " }),
2837
+ /* @__PURE__ */ jsxs34("span", { style: desc, children: [
2698
2838
  "textStyle(",
2699
2839
  "{",
2700
2840
  " inverse: true ",
@@ -2702,10 +2842,10 @@ function TextStyleApp() {
2702
2842
  ")"
2703
2843
  ] })
2704
2844
  ] }),
2705
- /* @__PURE__ */ jsx41("text", { children: " " }),
2706
- /* @__PURE__ */ jsxs33("text", { children: [
2707
- /* @__PURE__ */ jsx41("span", { style: textStyle({ fg: theme.primary }), children: "fg color " }),
2708
- /* @__PURE__ */ jsxs33("span", { style: desc, children: [
2845
+ /* @__PURE__ */ jsx43("text", { children: " " }),
2846
+ /* @__PURE__ */ jsxs34("text", { children: [
2847
+ /* @__PURE__ */ jsx43("span", { style: textStyle({ fg: theme.primary }), children: "fg color " }),
2848
+ /* @__PURE__ */ jsxs34("span", { style: desc, children: [
2709
2849
  "textStyle(",
2710
2850
  "{",
2711
2851
  " fg: theme.primary ",
@@ -2713,9 +2853,9 @@ function TextStyleApp() {
2713
2853
  ")"
2714
2854
  ] })
2715
2855
  ] }),
2716
- /* @__PURE__ */ jsxs33("text", { children: [
2717
- /* @__PURE__ */ jsx41("span", { style: textStyle({ fg: theme.foreground, bg: theme.secondary }), children: "bg color " }),
2718
- /* @__PURE__ */ jsxs33("span", { style: desc, children: [
2856
+ /* @__PURE__ */ jsxs34("text", { children: [
2857
+ /* @__PURE__ */ jsx43("span", { style: textStyle({ fg: theme.foreground, bg: theme.secondary }), children: "bg color " }),
2858
+ /* @__PURE__ */ jsxs34("span", { style: desc, children: [
2719
2859
  "textStyle(",
2720
2860
  "{",
2721
2861
  " fg: theme.foreground, bg: theme.secondary ",
@@ -2723,10 +2863,10 @@ function TextStyleApp() {
2723
2863
  ")"
2724
2864
  ] })
2725
2865
  ] }),
2726
- /* @__PURE__ */ jsx41("text", { children: " " }),
2727
- /* @__PURE__ */ jsxs33("text", { children: [
2728
- /* @__PURE__ */ jsx41("span", { style: textStyle({ fg: theme.accent, bold: true, underline: true }), children: "combined " }),
2729
- /* @__PURE__ */ jsxs33("span", { style: desc, children: [
2866
+ /* @__PURE__ */ jsx43("text", { children: " " }),
2867
+ /* @__PURE__ */ jsxs34("text", { children: [
2868
+ /* @__PURE__ */ jsx43("span", { style: textStyle({ fg: theme.accent, bold: true, underline: true }), children: "combined " }),
2869
+ /* @__PURE__ */ jsxs34("span", { style: desc, children: [
2730
2870
  "textStyle(",
2731
2871
  "{",
2732
2872
  " fg: theme.accent, bold: true, underline: true ",
@@ -2738,19 +2878,19 @@ function TextStyleApp() {
2738
2878
  }
2739
2879
 
2740
2880
  // demos/headless.tsx
2741
- import { jsx as jsx42 } from "react/jsx-runtime";
2881
+ import { jsx as jsx44 } from "react/jsx-runtime";
2742
2882
  var data = [
2743
2883
  { name: "Alice", role: "Engineer", status: "Active" },
2744
2884
  { name: "Bob", role: "Designer", status: "Active" },
2745
2885
  { name: "Charlie", role: "PM", status: "Away" }
2746
2886
  ];
2747
2887
  function HeadlessApp() {
2748
- return /* @__PURE__ */ jsx42("box", { padding: 1, children: /* @__PURE__ */ jsx42(Table, { data }) });
2888
+ return /* @__PURE__ */ jsx44("box", { padding: 1, children: /* @__PURE__ */ jsx44(Table, { data }) });
2749
2889
  }
2750
2890
 
2751
2891
  // demos/theming.tsx
2752
- import { useKeyboard as useKeyboard17 } from "@gridland/utils";
2753
- import { jsx as jsx43, jsxs as jsxs34 } from "react/jsx-runtime";
2892
+ import { useKeyboard as useKeyboard18 } from "@gridland/utils";
2893
+ import { jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
2754
2894
  var tableData = [
2755
2895
  { name: "Alice", role: "Engineer", status: "Active" },
2756
2896
  { name: "Bob", role: "Designer", status: "Away" }
@@ -2761,21 +2901,21 @@ var selectItems = [
2761
2901
  { label: "Python", value: "py" }
2762
2902
  ];
2763
2903
  function ThemingApp() {
2764
- return /* @__PURE__ */ jsxs34("box", { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1, children: [
2765
- /* @__PURE__ */ jsx43(Spinner, { text: "Loading data..." }),
2766
- /* @__PURE__ */ jsx43(Table, { data: tableData }),
2767
- /* @__PURE__ */ jsx43(MultiSelect, { items: selectItems, useKeyboard: useKeyboard17 })
2904
+ return /* @__PURE__ */ jsxs35("box", { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1, children: [
2905
+ /* @__PURE__ */ jsx45(Spinner, { text: "Loading data..." }),
2906
+ /* @__PURE__ */ jsx45(Table, { data: tableData }),
2907
+ /* @__PURE__ */ jsx45(MultiSelect, { items: selectItems, useKeyboard: useKeyboard18 })
2768
2908
  ] });
2769
2909
  }
2770
2910
 
2771
2911
  // src/landing/landing-app.tsx
2772
- import { useMemo as useMemo7 } from "react";
2912
+ import { useMemo as useMemo8, useRef as useRef15, useState as useState29 } from "react";
2773
2913
 
2774
2914
  // src/landing/install-box.tsx
2775
- import { jsx as jsx44, jsxs as jsxs35 } from "react/jsx-runtime";
2915
+ import { jsx as jsx46, jsxs as jsxs36 } from "react/jsx-runtime";
2776
2916
  function InstallBox() {
2777
2917
  const theme = useTheme();
2778
- return /* @__PURE__ */ jsx44(
2918
+ return /* @__PURE__ */ jsx46(
2779
2919
  "box",
2780
2920
  {
2781
2921
  border: true,
@@ -2784,21 +2924,23 @@ function InstallBox() {
2784
2924
  paddingX: 1,
2785
2925
  flexDirection: "column",
2786
2926
  flexShrink: 0,
2787
- children: /* @__PURE__ */ jsxs35("text", { children: [
2788
- /* @__PURE__ */ jsx44("span", { style: textStyle({ dim: true }), children: "$ " }),
2789
- /* @__PURE__ */ jsx44("span", { style: textStyle({ bold: true }), children: "bun create " }),
2790
- /* @__PURE__ */ jsx44("span", { style: textStyle({ fg: theme.accent }), children: "gridland" })
2927
+ children: /* @__PURE__ */ jsxs36("text", { children: [
2928
+ /* @__PURE__ */ jsx46("span", { style: textStyle({ dim: true }), children: "$ " }),
2929
+ /* @__PURE__ */ jsx46("span", { style: textStyle({ bold: true }), children: "bun create " }),
2930
+ /* @__PURE__ */ jsx46("span", { style: textStyle({ fg: theme.accent }), children: "gridland" })
2791
2931
  ] })
2792
2932
  }
2793
2933
  );
2794
2934
  }
2795
2935
 
2796
2936
  // src/landing/links-box.tsx
2797
- import { jsx as jsx45, jsxs as jsxs36 } from "react/jsx-runtime";
2937
+ import { isBrowser } from "@gridland/utils";
2938
+ import { jsx as jsx47, jsxs as jsxs37 } from "react/jsx-runtime";
2798
2939
  var UNDERLINE3 = 1 << 3;
2799
2940
  function LinksBox() {
2800
2941
  const theme = useTheme();
2801
- return /* @__PURE__ */ jsx45(
2942
+ const docsHref = isBrowser() ? `${window.location.origin}/docs` : "https://gridland.io/docs";
2943
+ return /* @__PURE__ */ jsx47(
2802
2944
  "box",
2803
2945
  {
2804
2946
  border: true,
@@ -2807,36 +2949,36 @@ function LinksBox() {
2807
2949
  paddingX: 1,
2808
2950
  flexDirection: "column",
2809
2951
  flexShrink: 0,
2810
- children: /* @__PURE__ */ jsxs36("text", { children: [
2811
- /* @__PURE__ */ jsx45("span", { children: "\u{1F431}" }),
2812
- /* @__PURE__ */ jsx45("a", { href: "https://github.com/thoughtfulllc/gridland", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " GitHub" }),
2813
- /* @__PURE__ */ jsx45("span", { children: " " }),
2814
- /* @__PURE__ */ jsx45("span", { children: "\u{1F4D6}" }),
2815
- /* @__PURE__ */ jsx45("a", { href: "https://gridland.io/docs", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " Docs" })
2952
+ children: /* @__PURE__ */ jsxs37("text", { children: [
2953
+ /* @__PURE__ */ jsx47("span", { children: "\u{1F431}" }),
2954
+ /* @__PURE__ */ jsx47("a", { href: "https://github.com/thoughtfulllc/gridland", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " GitHub" }),
2955
+ /* @__PURE__ */ jsx47("span", { children: " " }),
2956
+ /* @__PURE__ */ jsx47("span", { children: "\u{1F4D6}" }),
2957
+ /* @__PURE__ */ jsx47("a", { href: docsHref, style: { attributes: UNDERLINE3, fg: theme.accent }, children: " Docs" })
2816
2958
  ] })
2817
2959
  }
2818
2960
  );
2819
2961
  }
2820
2962
 
2821
2963
  // src/landing/logo.tsx
2822
- import { useState as useState24, useEffect as useEffect6, useRef as useRef10, useMemo as useMemo5 } from "react";
2964
+ import { useState as useState25, useEffect as useEffect6, useRef as useRef11, useMemo as useMemo5 } from "react";
2823
2965
  import figlet3 from "figlet";
2824
- import ansiShadow3 from "figlet/importable-fonts/ANSI Shadow.js";
2825
- import { Fragment as Fragment8, jsx as jsx46, jsxs as jsxs37 } from "react/jsx-runtime";
2826
- figlet3.parseFont("ANSI Shadow", ansiShadow3);
2827
- function makeArt(text) {
2828
- return figlet3.textSync(text, { font: "ANSI Shadow" }).split("\n").filter((l) => l.trimEnd().length > 0).join("\n");
2829
- }
2830
- var fullArt = makeArt("gridland");
2831
- var gridArt = makeArt("grid");
2832
- var landArt = makeArt("land");
2833
- var ART_HEIGHT = 6;
2966
+ import blockFont from "figlet/importable-fonts/Block.js";
2967
+ import { Fragment as Fragment6, jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
2968
+ figlet3.parseFont("Block", blockFont);
2969
+ function makeArt(text, font = "Block") {
2970
+ return figlet3.textSync(text, { font }).split("\n").filter((l) => l.trimEnd().length > 0).join("\n");
2971
+ }
2972
+ var fullArt = makeArt("gridland", "Block");
2973
+ var gridArt = makeArt("grid", "Block");
2974
+ var landArt = makeArt("land", "Block");
2975
+ var ART_HEIGHT = fullArt.split("\n").length;
2834
2976
  function useAnimation(duration = 1e3) {
2835
- const isBrowser = typeof document !== "undefined";
2836
- const [progress, setProgress] = useState24(isBrowser ? 0 : 1);
2837
- const startTime = useRef10(null);
2977
+ const isBrowser2 = typeof document !== "undefined";
2978
+ const [progress, setProgress] = useState25(isBrowser2 ? 0 : 1);
2979
+ const startTime = useRef11(null);
2838
2980
  useEffect6(() => {
2839
- if (!isBrowser) return;
2981
+ if (!isBrowser2) return;
2840
2982
  let raf;
2841
2983
  const tick = (time) => {
2842
2984
  if (startTime.current === null) startTime.current = time;
@@ -2851,13 +2993,20 @@ function useAnimation(duration = 1e3) {
2851
2993
  }, []);
2852
2994
  return progress;
2853
2995
  }
2996
+ function darkenHex(hex, factor = 0.4) {
2997
+ const r = Math.round(parseInt(hex.slice(1, 3), 16) * factor);
2998
+ const g = Math.round(parseInt(hex.slice(3, 5), 16) * factor);
2999
+ const b = Math.round(parseInt(hex.slice(5, 7), 16) * factor);
3000
+ return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
3001
+ }
2854
3002
  function RevealGradient({ children, revealCol }) {
2855
3003
  const gradientColors = GRADIENTS.instagram;
2856
3004
  const lines2 = children.split("\n");
2857
3005
  const maxLength = Math.max(...lines2.map((l) => l.length));
2858
- if (maxLength === 0) return /* @__PURE__ */ jsx46("text", { children });
3006
+ if (maxLength === 0) return /* @__PURE__ */ jsx48("text", { children });
2859
3007
  const hexColors = useMemo5(() => generateGradient(gradientColors, maxLength), [maxLength]);
2860
- return /* @__PURE__ */ jsx46("box", { position: "relative", width: maxLength, height: lines2.length, shouldFill: false, children: lines2.map((line, lineIndex) => {
3008
+ const bgColors = useMemo5(() => hexColors.map((c) => darkenHex(c)), [hexColors]);
3009
+ return /* @__PURE__ */ jsx48("box", { position: "relative", width: maxLength, height: lines2.length, shouldFill: false, children: lines2.map((line, lineIndex) => {
2861
3010
  const runs = [];
2862
3011
  let current = null;
2863
3012
  for (let i = 0; i < line.length; i++) {
@@ -2877,17 +3026,17 @@ function RevealGradient({ children, revealCol }) {
2877
3026
  }
2878
3027
  }
2879
3028
  if (current) runs.push(current);
2880
- return runs.map((run, runIndex) => /* @__PURE__ */ jsx46(
3029
+ return runs.map((run, runIndex) => /* @__PURE__ */ jsx48(
2881
3030
  "box",
2882
3031
  {
2883
3032
  position: "absolute",
2884
3033
  top: lineIndex,
2885
3034
  left: run.start,
2886
3035
  shouldFill: false,
2887
- children: /* @__PURE__ */ jsx46("text", { shouldFill: false, children: run.chars.map((char, ci) => /* @__PURE__ */ jsx46(
3036
+ children: /* @__PURE__ */ jsx48("text", { shouldFill: false, children: run.chars.map((char, ci) => /* @__PURE__ */ jsx48(
2888
3037
  "span",
2889
3038
  {
2890
- style: { fg: hexColors[run.start + ci] },
3039
+ style: { fg: hexColors[run.start + ci], bg: bgColors[run.start + ci] },
2891
3040
  children: char
2892
3041
  },
2893
3042
  ci
@@ -2898,28 +3047,28 @@ function RevealGradient({ children, revealCol }) {
2898
3047
  }) });
2899
3048
  }
2900
3049
  function Logo({ compact, narrow, mobile }) {
2901
- const isBrowser = typeof document !== "undefined";
3050
+ const isBrowser2 = typeof document !== "undefined";
2902
3051
  const progress = useAnimation(900);
2903
- const artHeight = compact ? 1 : narrow ? ART_HEIGHT * 2 : ART_HEIGHT;
3052
+ const artHeight = compact ? 1 : narrow && !mobile ? ART_HEIGHT * 2 : ART_HEIGHT;
2904
3053
  const dropOffset = Math.round((1 - progress) * -artHeight);
2905
3054
  const revealProgress = Math.max(0, Math.min(1, (progress - 0.1) / 0.7));
2906
- const maxWidth = compact ? 8 : narrow ? 40 : 62;
3055
+ const maxWidth = compact ? 8 : narrow ? 35 : 69;
2907
3056
  const revealCol = Math.round(revealProgress * (maxWidth + 4)) - 2;
2908
3057
  const taglineOpacity = Math.max(0, Math.min(1, (progress - 0.7) / 0.3));
2909
- const subtitle = /* @__PURE__ */ jsxs37(Fragment8, { children: [
2910
- /* @__PURE__ */ jsx46("text", { children: " " }),
2911
- /* @__PURE__ */ jsx46("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs37("text", { style: textStyle({ fg: "#d4b0e8" }), opacity: taglineOpacity, wrapMode: "word", textAlign: "center", width: "100%", shouldFill: false, children: [
3058
+ const subtitle = /* @__PURE__ */ jsxs38(Fragment6, { children: [
3059
+ /* @__PURE__ */ jsx48("text", { children: " " }),
3060
+ /* @__PURE__ */ jsx48("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs38("text", { style: textStyle({ fg: "#d4b0e8" }), opacity: taglineOpacity, wrapMode: "word", textAlign: "center", width: "100%", shouldFill: false, children: [
2912
3061
  "A framework for building terminal apps, built on ",
2913
- /* @__PURE__ */ jsx46("a", { href: "https://opentui.com", style: { attributes: 72, fg: "#d4b0e8" }, children: "OpenTUI" }),
3062
+ /* @__PURE__ */ jsx48("a", { href: "https://opentui.com", style: { attributes: 72, fg: "#d4b0e8" }, children: "OpenTUI" }),
2914
3063
  " + React." + (mobile ? " " : "\n") + "(Gridland apps, like this website, work in the browser and terminal.)"
2915
3064
  ] }) })
2916
3065
  ] });
2917
- if (!isBrowser) {
2918
- const art2 = compact ? "gridland" : narrow ? gridArt + "\n" + landArt : fullArt;
2919
- return /* @__PURE__ */ jsxs37("box", { flexDirection: "column", flexShrink: 0, width: "100%", alignItems: "center", shouldFill: false, children: [
2920
- /* @__PURE__ */ jsx46(Gradient, { name: "instagram", children: art2 }),
2921
- /* @__PURE__ */ jsx46("text", { children: " " }),
2922
- /* @__PURE__ */ jsx46("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs37("text", { style: textStyle({ fg: "#d4b0e8" }), shouldFill: false, children: [
3066
+ if (!isBrowser2) {
3067
+ const art2 = compact ? "gridland" : narrow && !mobile ? gridArt + "\n" + landArt : fullArt;
3068
+ return /* @__PURE__ */ jsxs38("box", { flexDirection: "column", flexShrink: 0, width: "100%", alignItems: "center", shouldFill: false, children: [
3069
+ /* @__PURE__ */ jsx48(Gradient, { name: "instagram", children: art2 }),
3070
+ /* @__PURE__ */ jsx48("text", { children: " " }),
3071
+ /* @__PURE__ */ jsx48("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs38("text", { style: textStyle({ fg: "#d4b0e8" }), shouldFill: false, children: [
2923
3072
  "A framework for building terminal apps, built on OpenTUI + React.",
2924
3073
  "\n",
2925
3074
  "(Gridland apps, like this website, work in the browser and terminal.)"
@@ -2927,22 +3076,22 @@ function Logo({ compact, narrow, mobile }) {
2927
3076
  ] });
2928
3077
  }
2929
3078
  if (compact) {
2930
- return /* @__PURE__ */ jsxs37("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
2931
- /* @__PURE__ */ jsx46("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx46("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx46(RevealGradient, { revealCol, children: "gridland" }) }) }),
3079
+ return /* @__PURE__ */ jsxs38("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
3080
+ /* @__PURE__ */ jsx48("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx48("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx48(RevealGradient, { revealCol, children: "gridland" }) }) }),
2932
3081
  subtitle
2933
3082
  ] });
2934
3083
  }
2935
- if (narrow) {
2936
- return /* @__PURE__ */ jsxs37("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
2937
- /* @__PURE__ */ jsx46("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsxs37("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: [
2938
- /* @__PURE__ */ jsx46(RevealGradient, { revealCol, children: gridArt }),
2939
- /* @__PURE__ */ jsx46(RevealGradient, { revealCol, children: landArt })
3084
+ if (narrow && !mobile) {
3085
+ return /* @__PURE__ */ jsxs38("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
3086
+ /* @__PURE__ */ jsx48("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsxs38("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: [
3087
+ /* @__PURE__ */ jsx48(RevealGradient, { revealCol, children: gridArt }),
3088
+ /* @__PURE__ */ jsx48(RevealGradient, { revealCol, children: landArt })
2940
3089
  ] }) }),
2941
3090
  subtitle
2942
3091
  ] });
2943
3092
  }
2944
- return /* @__PURE__ */ jsxs37("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
2945
- /* @__PURE__ */ jsx46("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx46("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx46(RevealGradient, { revealCol, children: fullArt }) }) }),
3093
+ return /* @__PURE__ */ jsxs38("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
3094
+ /* @__PURE__ */ jsx48("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx48("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx48(RevealGradient, { revealCol, children: fullArt }) }) }),
2946
3095
  subtitle
2947
3096
  ] });
2948
3097
  }
@@ -2951,7 +3100,7 @@ function Logo({ compact, narrow, mobile }) {
2951
3100
  import { useMemo as useMemo6 } from "react";
2952
3101
 
2953
3102
  // src/landing/use-matrix.ts
2954
- import { useState as useState25, useEffect as useEffect7, useRef as useRef11 } from "react";
3103
+ import { useState as useState26, useEffect as useEffect7, useLayoutEffect, useRef as useRef12 } from "react";
2955
3104
  var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789@#$%^&*(){}[]|;:<>,.?/~`";
2956
3105
  function randomChar() {
2957
3106
  return CHARS[Math.floor(Math.random() * CHARS.length)];
@@ -2965,7 +3114,11 @@ function createDrop(height, seeded = false) {
2965
3114
  chars: Array.from({ length }, randomChar)
2966
3115
  };
2967
3116
  }
2968
- function buildGrid(columns, width, height) {
3117
+ var PULL_RADIUS = 18;
3118
+ var PULL_STRENGTH = 7;
3119
+ var RIPPLE_DURATION_MS = 3200;
3120
+ var RIPPLE_SPEED = 8e-3;
3121
+ function buildGrid(columns, width, height, mousePos, ripples, now = Date.now()) {
2969
3122
  const grid = Array.from({ length: height }, () => Array(width).fill(" "));
2970
3123
  const brightness = Array.from({ length: height }, () => Array(width).fill(0));
2971
3124
  for (let x = 0; x < width; x++) {
@@ -2974,30 +3127,67 @@ function buildGrid(columns, width, height) {
2974
3127
  for (let i = 0; i < drop.length; i++) {
2975
3128
  const row = Math.floor(drop.y) - i;
2976
3129
  if (row < 0 || row >= height) continue;
2977
- grid[row][x] = drop.chars[i];
2978
- if (i === 0) {
2979
- brightness[row][x] = 1;
2980
- } else {
2981
- brightness[row][x] = Math.max(0.15, 1 - i / drop.length);
3130
+ let renderX = x;
3131
+ if (mousePos) {
3132
+ const dx = mousePos.x - x;
3133
+ const dy = mousePos.y - row;
3134
+ const dist = Math.sqrt(dx * dx + dy * dy);
3135
+ if (dist < PULL_RADIUS && dist > 0.5) {
3136
+ const t = 1 - dist / PULL_RADIUS;
3137
+ const strength = t * t * PULL_STRENGTH;
3138
+ renderX = Math.round(x + dx / dist * strength);
3139
+ renderX = Math.max(0, Math.min(width - 1, renderX));
3140
+ }
3141
+ }
3142
+ const b = i === 0 ? 1 : Math.max(0.15, 1 - i / drop.length);
3143
+ if (brightness[row][renderX] < b) {
3144
+ grid[row][renderX] = drop.chars[i];
3145
+ brightness[row][renderX] = b;
3146
+ }
3147
+ }
3148
+ }
3149
+ for (const ripple of ripples) {
3150
+ const elapsed = now - ripple.createdAt;
3151
+ if (elapsed > RIPPLE_DURATION_MS || elapsed < 0) continue;
3152
+ const radius = elapsed * RIPPLE_SPEED;
3153
+ const fade = 1 - elapsed / RIPPLE_DURATION_MS;
3154
+ const maxR = Math.ceil(radius) + 2;
3155
+ const rx = Math.round(ripple.x);
3156
+ const ry = Math.round(ripple.y);
3157
+ for (let dy = -maxR; dy <= maxR; dy++) {
3158
+ for (let dx = -maxR; dx <= maxR; dx++) {
3159
+ const cy = ry + dy;
3160
+ const cx = rx + dx;
3161
+ if (cy < 0 || cy >= height || cx < 0 || cx >= width) continue;
3162
+ const dist = Math.sqrt(dx * dx + dy * dy);
3163
+ const ringDist = Math.abs(dist - radius);
3164
+ if (ringDist < 2) {
3165
+ const boost = (1 - ringDist / 2) * fade * 0.7;
3166
+ brightness[cy][cx] = Math.min(1, brightness[cy][cx] + boost);
3167
+ if (grid[cy][cx] === " " && boost > 0.2) {
3168
+ grid[cy][cx] = randomChar();
3169
+ }
3170
+ }
2982
3171
  }
2983
3172
  }
2984
3173
  }
2985
3174
  return { grid, brightness };
2986
3175
  }
2987
- function useMatrix(width, height) {
2988
- const columnsRef = useRef11([]);
2989
- const [state, setState] = useState25(() => {
3176
+ function useMatrix(width, height, mousePosRef, ripplesRef) {
3177
+ const columnsRef = useRef12([]);
3178
+ const [state, setState] = useState26(() => {
2990
3179
  const columns = Array.from(
2991
3180
  { length: width },
2992
3181
  () => Math.random() < 0.5 ? createDrop(height, true) : null
2993
3182
  );
2994
3183
  columnsRef.current = columns;
2995
- return buildGrid(columns, width, height);
3184
+ return buildGrid(columns, width, height, null, []);
2996
3185
  });
2997
3186
  useEffect7(() => {
2998
3187
  if (width < 2 || height < 2) return;
2999
3188
  const id = setInterval(() => {
3000
3189
  const columns = columnsRef.current;
3190
+ const now = Date.now();
3001
3191
  for (let x = 0; x < width; x++) {
3002
3192
  if (columns[x] === null || columns[x] === void 0) {
3003
3193
  if (Math.random() < 0.03) {
@@ -3015,22 +3205,29 @@ function useMatrix(width, height) {
3015
3205
  columns[x] = null;
3016
3206
  }
3017
3207
  }
3018
- setState(buildGrid(columns, width, height));
3208
+ if (ripplesRef?.current) {
3209
+ ripplesRef.current = ripplesRef.current.filter(
3210
+ (r) => now - r.createdAt < RIPPLE_DURATION_MS
3211
+ );
3212
+ }
3213
+ const mousePos = mousePosRef?.current ?? null;
3214
+ const ripples = ripplesRef?.current ?? [];
3215
+ setState(buildGrid(columns, width, height, mousePos, ripples, now));
3019
3216
  }, 80);
3020
3217
  return () => clearInterval(id);
3021
3218
  }, [width, height]);
3022
- useEffect7(() => {
3219
+ useLayoutEffect(() => {
3023
3220
  columnsRef.current = Array.from(
3024
3221
  { length: width },
3025
3222
  () => Math.random() < 0.5 ? createDrop(height, true) : null
3026
3223
  );
3027
- setState(buildGrid(columnsRef.current, width, height));
3224
+ setState(buildGrid(columnsRef.current, width, height, null, []));
3028
3225
  }, [width, height]);
3029
3226
  return state;
3030
3227
  }
3031
3228
 
3032
3229
  // src/landing/matrix-background.tsx
3033
- import { jsx as jsx47 } from "react/jsx-runtime";
3230
+ import { jsx as jsx49 } from "react/jsx-runtime";
3034
3231
  var MUTE_LEVELS = [0.12, 0.18, 0.24, 0.3, 0.38];
3035
3232
  var BG = hexToRgb("#1a1a2e");
3036
3233
  function buildMutedColors(baseHex) {
@@ -3046,8 +3243,8 @@ function colorForCell(mutedColors, b) {
3046
3243
  const idx = Math.min(Math.floor(b * (MUTE_LEVELS.length - 1)), MUTE_LEVELS.length - 2);
3047
3244
  return mutedColors[idx];
3048
3245
  }
3049
- function MatrixBackground({ width, height, clearRect, clearRects }) {
3050
- const { grid, brightness } = useMatrix(width, height);
3246
+ function MatrixBackground({ width, height, clearRect, clearRects, mousePosRef, ripplesRef }) {
3247
+ const { grid, brightness } = useMatrix(width, height, mousePosRef, ripplesRef);
3051
3248
  const theme = useTheme();
3052
3249
  const columnColors = useMemo6(
3053
3250
  () => width > 0 ? generateGradient([theme.accent, theme.secondary, theme.primary], width) : [],
@@ -3057,15 +3254,15 @@ function MatrixBackground({ width, height, clearRect, clearRects }) {
3057
3254
  () => columnColors.map(buildMutedColors),
3058
3255
  [columnColors]
3059
3256
  );
3060
- return /* @__PURE__ */ jsx47("box", { flexDirection: "column", children: grid.map((row, y) => /* @__PURE__ */ jsx47("text", { children: row.map((cell, x) => {
3257
+ return /* @__PURE__ */ jsx49("box", { flexDirection: "column", children: grid.map((row, y) => /* @__PURE__ */ jsx49("text", { children: row.map((cell, x) => {
3061
3258
  const inClearRect = clearRect && y >= clearRect.top && y < clearRect.top + clearRect.height && x >= clearRect.left && x < clearRect.left + clearRect.width || clearRects && clearRects.some(
3062
3259
  (r) => y >= r.top && y < r.top + r.height && x >= r.left && x < r.left + r.width
3063
3260
  );
3064
3261
  const mutedColors = columnMutedColors[x];
3065
3262
  if (cell === " " || inClearRect || !mutedColors) {
3066
- return /* @__PURE__ */ jsx47("span", { children: " " }, x);
3263
+ return /* @__PURE__ */ jsx49("span", { children: " " }, x);
3067
3264
  }
3068
- return /* @__PURE__ */ jsx47(
3265
+ return /* @__PURE__ */ jsx49(
3069
3266
  "span",
3070
3267
  {
3071
3268
  style: {
@@ -3078,88 +3275,540 @@ function MatrixBackground({ width, height, clearRect, clearRects }) {
3078
3275
  }) }, y)) });
3079
3276
  }
3080
3277
 
3278
+ // demos/ripple.tsx
3279
+ import { useState as useState27, useEffect as useEffect8, useRef as useRef13, useCallback as useCallback6 } from "react";
3280
+ import { useKeyboard as useKeyboard19 } from "@gridland/utils";
3281
+ import { jsx as jsx50, jsxs as jsxs39 } from "react/jsx-runtime";
3282
+ var DEFAULT_COLS = 40;
3283
+ var DEFAULT_ROWS = 10;
3284
+ var CHARS2 = ["\xB7", "\u2591", "\u2592", "\u2593", "\u2588"];
3285
+ function hexToRgb2(hex) {
3286
+ const h = hex.replace("#", "");
3287
+ return [
3288
+ parseInt(h.slice(0, 2), 16),
3289
+ parseInt(h.slice(2, 4), 16),
3290
+ parseInt(h.slice(4, 6), 16)
3291
+ ];
3292
+ }
3293
+ function rgbToHex2(r, g, b) {
3294
+ const clamp = (v) => Math.max(0, Math.min(255, Math.round(v)));
3295
+ return "#" + clamp(r).toString(16).padStart(2, "0") + clamp(g).toString(16).padStart(2, "0") + clamp(b).toString(16).padStart(2, "0");
3296
+ }
3297
+ function lerp2(a, b, t) {
3298
+ return a + (b - a) * t;
3299
+ }
3300
+ function RippleApp({ mouseOffset = { x: 0, y: 0 }, containerWidth, containerHeight } = {}) {
3301
+ const theme = useTheme();
3302
+ const [, setTick] = useState27(0);
3303
+ const COLS2 = containerWidth ? containerWidth - 2 : DEFAULT_COLS;
3304
+ const ROWS2 = containerHeight ? Math.max(3, containerHeight - 2 - 2) : DEFAULT_ROWS;
3305
+ const cursorRef = useRef13({ x: Math.floor(COLS2 / 2), y: Math.floor(ROWS2 / 2) });
3306
+ const ripplesRef = useRef13([]);
3307
+ const frameRef = useRef13(0);
3308
+ const mousePosRef = useRef13(null);
3309
+ const accentRgb = hexToRgb2(theme.accent);
3310
+ const dimRgb = [40, 40, 50];
3311
+ const baseRgb = [60, 60, 70];
3312
+ const addRipple = useCallback6((x, y) => {
3313
+ ripplesRef.current = [
3314
+ ...ripplesRef.current,
3315
+ { x, y, time: frameRef.current }
3316
+ ];
3317
+ }, []);
3318
+ useEffect8(() => {
3319
+ const interval = setInterval(() => {
3320
+ frameRef.current++;
3321
+ ripplesRef.current = ripplesRef.current.filter(
3322
+ (r) => frameRef.current - r.time < 30
3323
+ );
3324
+ setTick((t) => t + 1);
3325
+ }, 60);
3326
+ return () => clearInterval(interval);
3327
+ }, []);
3328
+ useKeyboard19((event) => {
3329
+ const cursor2 = cursorRef.current;
3330
+ if (event.name === "up") {
3331
+ cursorRef.current = { ...cursor2, y: Math.max(0, cursor2.y - 1) };
3332
+ } else if (event.name === "down") {
3333
+ cursorRef.current = { ...cursor2, y: Math.min(ROWS2 - 1, cursor2.y + 1) };
3334
+ } else if (event.name === "left") {
3335
+ cursorRef.current = { ...cursor2, x: Math.max(0, cursor2.x - 1) };
3336
+ } else if (event.name === "right") {
3337
+ cursorRef.current = { ...cursor2, x: Math.min(COLS2 - 1, cursor2.x + 1) };
3338
+ } else if (event.name === "return") {
3339
+ addRipple(cursorRef.current.x, cursorRef.current.y);
3340
+ }
3341
+ event.preventDefault();
3342
+ });
3343
+ const cursor = cursorRef.current;
3344
+ const frame = frameRef.current;
3345
+ const ripples = ripplesRef.current;
3346
+ const grid = Array.from({ length: ROWS2 }, (_, row) => /* @__PURE__ */ jsx50("text", { children: Array.from({ length: COLS2 }, (_2, col) => {
3347
+ const isCursor = col === cursor.x && row === cursor.y;
3348
+ const baseWave = Math.sin(frame * 0.08 + col * 0.3 + row * 0.5) * 0.5 + 0.5;
3349
+ let intensity = baseWave * 0.15;
3350
+ for (const ripple of ripples) {
3351
+ const dx = col - ripple.x;
3352
+ const dy = row - ripple.y;
3353
+ const dist = Math.sqrt(dx * dx + dy * dy);
3354
+ const age = frame - ripple.time;
3355
+ const radius = age * 0.5;
3356
+ const fade = 1 - age / 30;
3357
+ const ringDist = Math.abs(dist - radius);
3358
+ if (ringDist < 1.5) {
3359
+ const ringIntensity = (1 - ringDist / 1.5) * fade;
3360
+ intensity = Math.max(intensity, ringIntensity);
3361
+ } else if (dist < radius) {
3362
+ const innerIntensity = fade * 0.3 * (1 - dist / radius);
3363
+ intensity = Math.max(intensity, innerIntensity);
3364
+ }
3365
+ }
3366
+ intensity = Math.max(0, Math.min(1, intensity));
3367
+ const charIndex = Math.min(
3368
+ CHARS2.length - 1,
3369
+ Math.floor(intensity * CHARS2.length)
3370
+ );
3371
+ const char = isCursor ? "\u25C6" : CHARS2[charIndex];
3372
+ let fg;
3373
+ if (isCursor) {
3374
+ fg = theme.primary;
3375
+ } else {
3376
+ const r = lerp2(dimRgb[0], accentRgb[0], intensity);
3377
+ const g = lerp2(dimRgb[1], accentRgb[1], intensity);
3378
+ const b = lerp2(dimRgb[2], accentRgb[2], intensity);
3379
+ fg = rgbToHex2(r, g, b);
3380
+ }
3381
+ return /* @__PURE__ */ jsx50("span", { style: { fg, bold: isCursor || intensity > 0.7 }, children: char }, col);
3382
+ }) }, row));
3383
+ return /* @__PURE__ */ jsxs39("box", { flexDirection: "column", flexGrow: 1, children: [
3384
+ /* @__PURE__ */ jsxs39(
3385
+ "box",
3386
+ {
3387
+ flexDirection: "column",
3388
+ flexGrow: 1,
3389
+ paddingX: 1,
3390
+ onMouseMove: (e) => {
3391
+ const gx = e.x - mouseOffset.x - 1;
3392
+ const gy = e.y - mouseOffset.y - 2;
3393
+ if (gx >= 0 && gx < COLS2 && gy >= 0 && gy < ROWS2) {
3394
+ mousePosRef.current = { x: gx, y: gy };
3395
+ cursorRef.current = { x: gx, y: gy };
3396
+ }
3397
+ },
3398
+ onMouseDown: (e) => {
3399
+ const gx = e.x - mouseOffset.x - 1;
3400
+ const gy = e.y - mouseOffset.y - 2;
3401
+ if (gx >= 0 && gx < COLS2 && gy >= 0 && gy < ROWS2) {
3402
+ addRipple(gx, gy);
3403
+ }
3404
+ },
3405
+ children: [
3406
+ /* @__PURE__ */ jsx50("text", { style: { dim: true, fg: theme.muted }, children: "Click or press Enter to create ripples" }),
3407
+ /* @__PURE__ */ jsx50("box", { flexDirection: "column", children: grid })
3408
+ ]
3409
+ }
3410
+ ),
3411
+ /* @__PURE__ */ jsx50("box", { flexGrow: 1 }),
3412
+ /* @__PURE__ */ jsx50("box", { paddingX: 1, children: /* @__PURE__ */ jsx50(
3413
+ StatusBar,
3414
+ {
3415
+ items: [
3416
+ { key: "\u2191\u2193\u2190\u2192", label: "move" },
3417
+ { key: "enter/click", label: "ripple" }
3418
+ ]
3419
+ }
3420
+ ) })
3421
+ ] });
3422
+ }
3423
+
3424
+ // demos/puzzle.tsx
3425
+ import { useState as useState28, useEffect as useEffect9, useRef as useRef14, useMemo as useMemo7 } from "react";
3426
+ import { useKeyboard as useKeyboard20 } from "@gridland/utils";
3427
+ import { jsx as jsx51, jsxs as jsxs40 } from "react/jsx-runtime";
3428
+ var COLS = 4;
3429
+ var ROWS = 3;
3430
+ var TILE_COUNT = COLS * ROWS;
3431
+ var DEFAULT_TILE_WIDTH = 8;
3432
+ var DEFAULT_TILE_HEIGHT = 3;
3433
+ var SOLVED = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0];
3434
+ var tileColors = [
3435
+ "#ef4444",
3436
+ "#f97316",
3437
+ "#eab308",
3438
+ "#22c55e",
3439
+ "#3b82f6",
3440
+ "#8b5cf6",
3441
+ "#ec4899",
3442
+ "#14b8a6",
3443
+ "#f43f5e",
3444
+ "#6366f1",
3445
+ "#84cc16"
3446
+ ];
3447
+ function isSolved(board) {
3448
+ for (let i = 0; i < TILE_COUNT; i++) {
3449
+ if (board[i] !== SOLVED[i]) return false;
3450
+ }
3451
+ return true;
3452
+ }
3453
+ function getEmptyIndex(board) {
3454
+ return board.indexOf(0);
3455
+ }
3456
+ function getNeighbor(emptyIdx, direction) {
3457
+ const row = Math.floor(emptyIdx / COLS);
3458
+ const col = emptyIdx % COLS;
3459
+ switch (direction) {
3460
+ case "up":
3461
+ return row > 0 ? emptyIdx - COLS : null;
3462
+ case "down":
3463
+ return row < ROWS - 1 ? emptyIdx + COLS : null;
3464
+ case "left":
3465
+ return col > 0 ? emptyIdx - 1 : null;
3466
+ case "right":
3467
+ return col < COLS - 1 ? emptyIdx + 1 : null;
3468
+ default:
3469
+ return null;
3470
+ }
3471
+ }
3472
+ function swap(board, a, b) {
3473
+ const next = [...board];
3474
+ next[a] = board[b];
3475
+ next[b] = board[a];
3476
+ return next;
3477
+ }
3478
+ function shuffle(board, count) {
3479
+ let current = [...board];
3480
+ const directions = ["up", "down", "left", "right"];
3481
+ let lastDir = "";
3482
+ for (let i = 0; i < count; i++) {
3483
+ const emptyIdx = getEmptyIndex(current);
3484
+ const validMoves = directions.filter((d) => {
3485
+ if (d === lastDir) return false;
3486
+ return getNeighbor(emptyIdx, d) !== null;
3487
+ });
3488
+ const dir = validMoves[Math.floor(Math.random() * validMoves.length)];
3489
+ const neighbor = getNeighbor(emptyIdx, dir);
3490
+ current = swap(current, emptyIdx, neighbor);
3491
+ const opposites = { up: "down", down: "up", left: "right", right: "left" };
3492
+ lastDir = opposites[dir];
3493
+ }
3494
+ return current;
3495
+ }
3496
+ function isAdjacentToEmpty(board, tileIdx) {
3497
+ const emptyIdx = getEmptyIndex(board);
3498
+ const eRow = Math.floor(emptyIdx / COLS);
3499
+ const eCol = emptyIdx % COLS;
3500
+ const tRow = Math.floor(tileIdx / COLS);
3501
+ const tCol = tileIdx % COLS;
3502
+ return Math.abs(eRow - tRow) === 1 && eCol === tCol || Math.abs(eCol - tCol) === 1 && eRow === tRow;
3503
+ }
3504
+ function PuzzleApp({ containerWidth, containerHeight } = {}) {
3505
+ const theme = useTheme();
3506
+ const TILE_WIDTH = containerWidth ? Math.floor((containerWidth - 2) / COLS) : DEFAULT_TILE_WIDTH;
3507
+ const TILE_HEIGHT = containerHeight ? Math.max(3, Math.floor((containerHeight - 2 - 4) / ROWS)) : DEFAULT_TILE_HEIGHT;
3508
+ const [board, setBoard] = useState28(SOLVED);
3509
+ const [moves, setMoves] = useState28(0);
3510
+ const [solved, setSolved] = useState28(false);
3511
+ const boardRef = useRef14(board);
3512
+ const movesRef = useRef14(moves);
3513
+ boardRef.current = board;
3514
+ movesRef.current = moves;
3515
+ const doMove = (direction) => {
3516
+ const current = boardRef.current;
3517
+ if (isSolved(current) && movesRef.current > 0) return;
3518
+ const emptyIdx = getEmptyIndex(current);
3519
+ const neighbor = getNeighbor(emptyIdx, direction);
3520
+ if (neighbor === null) return;
3521
+ const next = swap(current, emptyIdx, neighbor);
3522
+ boardRef.current = next;
3523
+ movesRef.current++;
3524
+ setBoard(next);
3525
+ setMoves(movesRef.current);
3526
+ setSolved(isSolved(next));
3527
+ };
3528
+ const doShuffle = () => {
3529
+ const shuffled = shuffle(SOLVED, 30);
3530
+ boardRef.current = shuffled;
3531
+ movesRef.current = 0;
3532
+ setBoard(shuffled);
3533
+ setMoves(0);
3534
+ setSolved(false);
3535
+ };
3536
+ useEffect9(() => {
3537
+ doShuffle();
3538
+ }, []);
3539
+ useKeyboard20((event) => {
3540
+ if (event.name === "up") {
3541
+ doMove("down");
3542
+ event.preventDefault();
3543
+ } else if (event.name === "down") {
3544
+ doMove("up");
3545
+ event.preventDefault();
3546
+ } else if (event.name === "left") {
3547
+ doMove("right");
3548
+ event.preventDefault();
3549
+ } else if (event.name === "right") {
3550
+ doMove("left");
3551
+ event.preventDefault();
3552
+ } else if (event.key === "r") {
3553
+ doShuffle();
3554
+ event.preventDefault();
3555
+ }
3556
+ });
3557
+ const rows = useMemo7(() => {
3558
+ const result = [];
3559
+ for (let r = 0; r < ROWS; r++) {
3560
+ result.push(board.slice(r * COLS, r * COLS + COLS));
3561
+ }
3562
+ return result;
3563
+ }, [board]);
3564
+ return /* @__PURE__ */ jsxs40("box", { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
3565
+ /* @__PURE__ */ jsx51("text", { style: textStyle({ dim: true, fg: theme.muted }), children: "Slide tiles to solve the puzzle" }),
3566
+ /* @__PURE__ */ jsx51("box", { flexDirection: "column", children: rows.map((row, rowIdx) => /* @__PURE__ */ jsx51("box", { flexDirection: "row", children: row.map((tile, colIdx) => {
3567
+ const idx = rowIdx * COLS + colIdx;
3568
+ if (tile === 0) {
3569
+ return /* @__PURE__ */ jsx51("box", { width: TILE_WIDTH, height: TILE_HEIGHT }, idx);
3570
+ }
3571
+ const color = tileColors[tile - 1];
3572
+ return /* @__PURE__ */ jsx51(
3573
+ "box",
3574
+ {
3575
+ width: TILE_WIDTH,
3576
+ height: TILE_HEIGHT,
3577
+ border: true,
3578
+ borderStyle: "rounded",
3579
+ borderColor: solved ? theme.success : color,
3580
+ onMouseDown: () => {
3581
+ if (isAdjacentToEmpty(boardRef.current, idx)) {
3582
+ const emptyIdx = getEmptyIndex(boardRef.current);
3583
+ const next = swap(boardRef.current, emptyIdx, idx);
3584
+ boardRef.current = next;
3585
+ movesRef.current++;
3586
+ setBoard(next);
3587
+ setMoves(movesRef.current);
3588
+ setSolved(isSolved(next));
3589
+ }
3590
+ },
3591
+ children: /* @__PURE__ */ jsx51("text", { style: textStyle({
3592
+ fg: solved ? theme.success : color,
3593
+ bold: true
3594
+ }), children: String(tile).padStart(2) })
3595
+ },
3596
+ idx
3597
+ );
3598
+ }) }, rowIdx)) }),
3599
+ /* @__PURE__ */ jsx51("box", { height: 1 }),
3600
+ /* @__PURE__ */ jsxs40("box", { flexDirection: "row", gap: 2, children: [
3601
+ /* @__PURE__ */ jsxs40("text", { style: textStyle({ dim: true, fg: theme.muted }), children: [
3602
+ "Moves: ",
3603
+ moves
3604
+ ] }),
3605
+ solved && moves > 0 && /* @__PURE__ */ jsx51("text", { style: textStyle({ fg: theme.success, bold: true }), children: "Solved!" })
3606
+ ] }),
3607
+ /* @__PURE__ */ jsx51("box", { flexGrow: 1 }),
3608
+ /* @__PURE__ */ jsx51("box", { paddingX: 1, children: /* @__PURE__ */ jsx51(StatusBar, { items: [
3609
+ { key: "\u2191\u2193\u2190\u2192", label: "slide" },
3610
+ { key: "click", label: "slide tile" },
3611
+ { key: "r", label: "shuffle" }
3612
+ ] }) })
3613
+ ] });
3614
+ }
3615
+
3081
3616
  // src/landing/landing-app.tsx
3082
- import { jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
3083
- function LandingApp({ useKeyboard: useKeyboard20 }) {
3617
+ import { Fragment as Fragment7, jsx as jsx52, jsxs as jsxs41 } from "react/jsx-runtime";
3618
+ var DEMOS = ["ripple", "puzzle"];
3619
+ var TAB_HEIGHT = 2;
3620
+ var TAB_WIDTHS = DEMOS.map((n) => n.length + 4);
3621
+ var TAB_POSITIONS = [];
3622
+ var _pos = 0;
3623
+ for (const w of TAB_WIDTHS) {
3624
+ TAB_POSITIONS.push(_pos);
3625
+ _pos += w;
3626
+ }
3627
+ function LandingApp({ useKeyboard: useKeyboard23 }) {
3084
3628
  const theme = useTheme();
3085
3629
  const { width, height, isNarrow, isTiny, isMobile } = useBreakpoints();
3086
- const isBrowser = typeof document !== "undefined";
3087
- const { clearRect, installLinksClearRect } = useMemo7(() => {
3088
- const logoHeight = isTiny ? 2 : isNarrow ? 13 : 7;
3089
- const logoExtra = isBrowser ? 1 : 0;
3630
+ const [activeIndex, setActiveIndex] = useState29(0);
3631
+ const mousePosRef = useRef15(null);
3632
+ const matrixRipplesRef = useRef15([]);
3633
+ useKeyboard23((event) => {
3634
+ if (event.name === "tab") {
3635
+ setActiveIndex((prev) => (prev + 1) % DEMOS.length);
3636
+ event.preventDefault();
3637
+ }
3638
+ });
3639
+ const isBrowser2 = typeof document !== "undefined";
3640
+ const { clearRect, installLinksClearRect, boxTop } = useMemo8(() => {
3641
+ const logoHeight = isTiny ? 2 : isMobile ? 7 : isNarrow ? 13 : 7;
3642
+ const logoExtra = isBrowser2 ? 1 : 0;
3090
3643
  const gap = isMobile ? 0 : 1;
3091
- const installLinksTop = 3 + logoHeight + logoExtra + gap;
3644
+ const paddingTop = isMobile ? 1 : 3;
3645
+ const installLinksTop = paddingTop + logoHeight + logoExtra + gap;
3092
3646
  const installLinksHeight = 3;
3093
- const boxTop = installLinksTop + installLinksHeight + gap + 1;
3094
- const bh = height - boxTop - 1;
3647
+ const boxTop2 = installLinksTop + installLinksHeight + gap + 1;
3648
+ const bh = height - boxTop2 - 1;
3649
+ const bw = Math.min(82, width - 2);
3650
+ const bl = Math.floor((width - bw) / 2);
3095
3651
  return {
3096
- clearRect: { top: boxTop, left: 1, width: width - 2, height: bh },
3097
- installLinksClearRect: { top: installLinksTop, left: 1, width: width - 2, height: installLinksHeight }
3652
+ clearRect: { top: boxTop2, left: bl, width: bw, height: bh },
3653
+ installLinksClearRect: { top: installLinksTop, left: 1, width: width - 2, height: installLinksHeight },
3654
+ boxTop: boxTop2
3098
3655
  };
3099
- }, [width, height, isTiny, isNarrow, isMobile, isBrowser]);
3100
- return /* @__PURE__ */ jsxs38("box", { width: "100%", height: "100%", position: "relative", children: [
3101
- /* @__PURE__ */ jsx48(MatrixBackground, { width, height, clearRect, clearRects: isBrowser ? void 0 : [installLinksClearRect] }),
3102
- /* @__PURE__ */ jsx48("box", { position: "absolute", top: 0, left: 0, width, height, zIndex: 1, flexDirection: "column", shouldFill: false, children: /* @__PURE__ */ jsxs38("box", { flexGrow: 1, flexDirection: "column", paddingTop: 3, paddingLeft: 1, paddingRight: 1, paddingBottom: 1, gap: isMobile ? 0 : 1, shouldFill: false, children: [
3103
- /* @__PURE__ */ jsx48("box", { flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx48(Logo, { compact: isTiny, narrow: isNarrow, mobile: isMobile }) }),
3104
- /* @__PURE__ */ jsxs38("box", { flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: isMobile ? 0 : 1, flexShrink: 0, shouldFill: false, children: [
3105
- /* @__PURE__ */ jsx48("box", { border: true, borderStyle: "rounded", borderColor: theme.border, paddingX: 1, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsxs38("text", { children: [
3106
- /* @__PURE__ */ jsx48("span", { style: textStyle({ dim: true }), children: "$ " }),
3107
- /* @__PURE__ */ jsx48("span", { style: textStyle({ bold: true }), children: "bunx " }),
3108
- /* @__PURE__ */ jsx48("span", { style: textStyle({ fg: theme.accent }), children: "@gridland/demo landing" })
3109
- ] }) }),
3110
- /* @__PURE__ */ jsx48(InstallBox, {}),
3111
- /* @__PURE__ */ jsx48(LinksBox, {})
3112
- ] }),
3113
- /* @__PURE__ */ jsx48("box", { flexGrow: 1, border: true, borderStyle: "rounded", borderColor: theme.border, flexDirection: "column", overflow: "hidden" })
3114
- ] }) })
3656
+ }, [width, height, isTiny, isNarrow, isMobile, isBrowser2]);
3657
+ const MAX_BOX_WIDTH = 82;
3658
+ const availableWidth = width - 2;
3659
+ const boxWidth = Math.min(MAX_BOX_WIDTH, availableWidth);
3660
+ const boxLeft = Math.floor((width - boxWidth) / 2);
3661
+ const mouseOffset = useMemo8(() => ({
3662
+ x: boxLeft + 1,
3663
+ // box left edge + border(1)
3664
+ y: boxTop + TAB_HEIGHT + 1
3665
+ // boxTop + tab rows + box top border(1)
3666
+ }), [boxTop, boxLeft]);
3667
+ const containerWidth = boxWidth - 2;
3668
+ const maxBoxHeight = 20;
3669
+ const containerHeight = Math.min(height - boxTop - 1 - TAB_HEIGHT, maxBoxHeight - TAB_HEIGHT);
3670
+ const activeStart = TAB_POSITIONS[activeIndex];
3671
+ const activeWidth = TAB_WIDTHS[activeIndex];
3672
+ const activeEnd = activeStart + activeWidth;
3673
+ const connectParts = [];
3674
+ if (activeStart === 0) {
3675
+ connectParts.push(/* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: "\u2502" }, "cl"));
3676
+ connectParts.push(/* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: " ".repeat(activeWidth - 2) }, "gap"));
3677
+ connectParts.push(/* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: "\u2570" }, "cr"));
3678
+ } else {
3679
+ connectParts.push(
3680
+ /* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: "\u256D" + "\u2500".repeat(activeStart - 1) }, "left")
3681
+ );
3682
+ connectParts.push(/* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: "\u256F" }, "cl"));
3683
+ connectParts.push(/* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: " ".repeat(activeWidth - 2) }, "gap"));
3684
+ connectParts.push(/* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: "\u2570" }, "cr"));
3685
+ }
3686
+ const rightFill = boxWidth - activeEnd - 1;
3687
+ if (rightFill > 0) {
3688
+ connectParts.push(
3689
+ /* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: "\u2500".repeat(rightFill) }, "right")
3690
+ );
3691
+ }
3692
+ connectParts.push(/* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: "\u256E" }, "corner-r"));
3693
+ return /* @__PURE__ */ jsxs41("box", { width: "100%", height: "100%", position: "relative", children: [
3694
+ /* @__PURE__ */ jsx52(MatrixBackground, { width, height, clearRect, clearRects: isBrowser2 ? void 0 : [installLinksClearRect], mousePosRef, ripplesRef: matrixRipplesRef }),
3695
+ /* @__PURE__ */ jsx52(
3696
+ "box",
3697
+ {
3698
+ position: "absolute",
3699
+ top: 0,
3700
+ left: 0,
3701
+ width,
3702
+ height,
3703
+ zIndex: 1,
3704
+ flexDirection: "column",
3705
+ shouldFill: false,
3706
+ onMouseMove: (e) => {
3707
+ mousePosRef.current = { x: e.x, y: e.y };
3708
+ },
3709
+ onMouseDown: (e) => {
3710
+ matrixRipplesRef.current = [
3711
+ ...matrixRipplesRef.current,
3712
+ { x: e.x, y: e.y, createdAt: Date.now() }
3713
+ ];
3714
+ },
3715
+ children: /* @__PURE__ */ jsxs41("box", { flexGrow: 1, flexDirection: "column", paddingTop: isMobile ? 1 : 3, paddingLeft: 1, paddingRight: 1, paddingBottom: 1, gap: isMobile ? 0 : 1, shouldFill: false, children: [
3716
+ /* @__PURE__ */ jsx52("box", { flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx52(Logo, { compact: isTiny, narrow: isNarrow, mobile: isMobile }) }),
3717
+ /* @__PURE__ */ jsxs41("box", { flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: isMobile ? 0 : 1, flexShrink: 0, shouldFill: false, children: [
3718
+ !isMobile && /* @__PURE__ */ jsx52("box", { border: true, borderStyle: "rounded", borderColor: theme.border, paddingX: 1, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsxs41("text", { children: [
3719
+ /* @__PURE__ */ jsx52("span", { style: textStyle({ dim: true }), children: "$ " }),
3720
+ /* @__PURE__ */ jsx52("span", { style: textStyle({ bold: true }), children: "bunx " }),
3721
+ /* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.accent }), children: "@gridland/demo landing" })
3722
+ ] }) }),
3723
+ !isMobile && /* @__PURE__ */ jsx52(InstallBox, {}),
3724
+ /* @__PURE__ */ jsx52(LinksBox, {})
3725
+ ] }),
3726
+ /* @__PURE__ */ jsxs41("box", { flexDirection: "column", width: boxWidth, maxWidth: MAX_BOX_WIDTH, maxHeight: 20, alignSelf: "center", flexGrow: 1, children: [
3727
+ /* @__PURE__ */ jsx52("box", { height: 1, flexShrink: 0, flexDirection: "row", shouldFill: false, children: DEMOS.map((name2, i) => {
3728
+ const isActive = i === activeIndex;
3729
+ const w = TAB_WIDTHS[i];
3730
+ return /* @__PURE__ */ jsx52("box", { width: w, onMouseDown: () => setActiveIndex(i), children: /* @__PURE__ */ jsx52("text", { style: textStyle({ fg: theme.border }), children: isActive ? "\u256D" + "\u2500".repeat(w - 2) + "\u256E" : " ".repeat(w) }) }, name2);
3731
+ }) }),
3732
+ /* @__PURE__ */ jsx52("box", { height: 1, flexShrink: 0, flexDirection: "row", shouldFill: false, children: DEMOS.map((name2, i) => {
3733
+ const isActive = i === activeIndex;
3734
+ const w = TAB_WIDTHS[i];
3735
+ return /* @__PURE__ */ jsx52("box", { width: w, onMouseDown: () => setActiveIndex(i), children: /* @__PURE__ */ jsx52("text", { children: isActive ? /* @__PURE__ */ jsxs41(Fragment7, { children: [
3736
+ /* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: "\u2502" }),
3737
+ /* @__PURE__ */ jsx52("span", { style: textStyle({ bold: true, fg: theme.foreground }), children: ` ${name2} ` }),
3738
+ /* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.border }), children: "\u2502" })
3739
+ ] }) : /* @__PURE__ */ jsx52("span", { style: textStyle({ fg: theme.muted }), children: ` ${name2} ` }) }) }, name2);
3740
+ }) }),
3741
+ /* @__PURE__ */ jsxs41("box", { position: "relative", flexGrow: 1, children: [
3742
+ /* @__PURE__ */ jsx52("box", { position: "absolute", top: 0, left: 0, width: boxWidth, height: 1, zIndex: 2, children: /* @__PURE__ */ jsx52("text", { children: connectParts }) }),
3743
+ /* @__PURE__ */ jsxs41("box", { border: true, borderStyle: "rounded", borderColor: theme.border, flexGrow: 1, flexDirection: "column", overflow: "hidden", children: [
3744
+ activeIndex === 0 && /* @__PURE__ */ jsx52(RippleApp, { mouseOffset, containerWidth, containerHeight }),
3745
+ activeIndex === 1 && /* @__PURE__ */ jsx52(PuzzleApp, { containerWidth, containerHeight })
3746
+ ] })
3747
+ ] }),
3748
+ /* @__PURE__ */ jsx52("box", { height: 1 }),
3749
+ /* @__PURE__ */ jsx52("box", { width: "100%", alignItems: "center", flexDirection: "column", shouldFill: false, children: /* @__PURE__ */ jsxs41("text", { style: textStyle({ dim: true, fg: theme.muted }), children: [
3750
+ "Made with \u2764\uFE0F by ",
3751
+ /* @__PURE__ */ jsx52("a", { href: "https://cjroth.com", style: { attributes: 1 << 3, fg: theme.muted }, children: "Chris Roth" }),
3752
+ " + ",
3753
+ /* @__PURE__ */ jsx52("a", { href: "https://jessicacheng.studio", style: { attributes: 1 << 3, fg: theme.muted }, children: "Jessica Cheng" })
3754
+ ] }) })
3755
+ ] })
3756
+ ] })
3757
+ }
3758
+ )
3115
3759
  ] });
3116
3760
  }
3117
3761
 
3118
3762
  // src/landing/matrix-rain.tsx
3119
- import { jsx as jsx49 } from "react/jsx-runtime";
3763
+ import { jsx as jsx53 } from "react/jsx-runtime";
3120
3764
 
3121
3765
  // src/landing/about-modal.tsx
3122
- import { jsx as jsx50, jsxs as jsxs39 } from "react/jsx-runtime";
3766
+ import { jsx as jsx54, jsxs as jsxs42 } from "react/jsx-runtime";
3123
3767
 
3124
3768
  // demos/index.tsx
3125
- import { jsx as jsx51 } from "react/jsx-runtime";
3769
+ import { jsx as jsx55 } from "react/jsx-runtime";
3126
3770
  var demos = [
3127
- { name: "gradient", app: () => /* @__PURE__ */ jsx51(GradientApp, {}) },
3128
- { name: "ascii", app: () => /* @__PURE__ */ jsx51(AsciiApp, {}) },
3129
- { name: "table", app: () => /* @__PURE__ */ jsx51(TableApp, {}) },
3130
- { name: "spinner", app: () => /* @__PURE__ */ jsx51(SpinnerApp, {}) },
3131
- { name: "select-input", app: () => /* @__PURE__ */ jsx51(SelectInputApp, {}) },
3132
- { name: "multi-select", app: () => /* @__PURE__ */ jsx51(MultiSelectApp, {}) },
3133
- { name: "prompt-input", app: () => /* @__PURE__ */ jsx51(PromptInputApp, {}) },
3134
- { name: "text-input", app: () => /* @__PURE__ */ jsx51(TextInputApp, {}) },
3135
- { name: "link", app: () => /* @__PURE__ */ jsx51(LinkApp, {}) },
3136
- { name: "tabs", app: () => /* @__PURE__ */ jsx51(TabBarApp, {}) },
3137
- { name: "status-bar", app: () => /* @__PURE__ */ jsx51(StatusBarApp, {}) },
3138
- { name: "modal", app: () => /* @__PURE__ */ jsx51(ModalApp, {}) },
3139
- { name: "primitives", app: () => /* @__PURE__ */ jsx51(PrimitivesApp, {}) },
3140
- { name: "chat", app: () => /* @__PURE__ */ jsx51(ChatApp, {}) },
3141
- { name: "chain-of-thought", app: () => /* @__PURE__ */ jsx51(ChainOfThoughtApp, {}) },
3142
- { name: "message", app: () => /* @__PURE__ */ jsx51(MessageApp, {}) },
3143
- { name: "terminal-window", app: () => /* @__PURE__ */ jsx51(TerminalWindowApp, {}) },
3144
- { name: "focus", app: () => /* @__PURE__ */ jsx51(FocusApp, {}) },
3145
- { name: "pointer", app: () => /* @__PURE__ */ jsx51(PointerApp, {}) },
3146
- { name: "cursor-highlight", app: () => /* @__PURE__ */ jsx51(CursorHighlightApp, {}) },
3147
- { name: "text-style", app: () => /* @__PURE__ */ jsx51(TextStyleApp, {}) },
3148
- { name: "headless", app: () => /* @__PURE__ */ jsx51(HeadlessApp, {}) },
3149
- { name: "theming", app: () => /* @__PURE__ */ jsx51(ThemingApp, {}) },
3150
- { name: "landing", app: () => /* @__PURE__ */ jsx51(LandingApp, { useKeyboard: useKeyboard18 }) }
3771
+ { name: "gradient", app: () => /* @__PURE__ */ jsx55(GradientApp, {}) },
3772
+ { name: "ascii", app: () => /* @__PURE__ */ jsx55(AsciiApp, {}) },
3773
+ { name: "table", app: () => /* @__PURE__ */ jsx55(TableApp, {}) },
3774
+ { name: "spinner", app: () => /* @__PURE__ */ jsx55(SpinnerApp, {}) },
3775
+ { name: "select-input", app: () => /* @__PURE__ */ jsx55(SelectInputApp, {}) },
3776
+ { name: "multi-select", app: () => /* @__PURE__ */ jsx55(MultiSelectApp, {}) },
3777
+ { name: "prompt-input", app: () => /* @__PURE__ */ jsx55(PromptInputApp, {}) },
3778
+ { name: "text-input", app: () => /* @__PURE__ */ jsx55(TextInputApp, {}) },
3779
+ { name: "link", app: () => /* @__PURE__ */ jsx55(LinkApp, {}) },
3780
+ { name: "tabs", app: () => /* @__PURE__ */ jsx55(TabBarApp, {}) },
3781
+ { name: "status-bar", app: () => /* @__PURE__ */ jsx55(StatusBarApp, {}) },
3782
+ { name: "modal", app: () => /* @__PURE__ */ jsx55(ModalApp, {}) },
3783
+ { name: "primitives", app: () => /* @__PURE__ */ jsx55(PrimitivesApp, {}) },
3784
+ { name: "chat", app: () => /* @__PURE__ */ jsx55(ChatApp, {}) },
3785
+ { name: "chain-of-thought", app: () => /* @__PURE__ */ jsx55(ChainOfThoughtApp, {}) },
3786
+ { name: "message", app: () => /* @__PURE__ */ jsx55(MessageApp, {}) },
3787
+ { name: "terminal-window", app: () => /* @__PURE__ */ jsx55(TerminalWindowApp, {}) },
3788
+ { name: "focus-grid", app: () => /* @__PURE__ */ jsx55(FocusGridApp, {}) },
3789
+ { name: "focus-chat", app: () => /* @__PURE__ */ jsx55(FocusChatApp, {}) },
3790
+ { name: "focus", app: () => /* @__PURE__ */ jsx55(FocusApp, {}) },
3791
+ { name: "pointer", app: () => /* @__PURE__ */ jsx55(PointerApp, {}) },
3792
+ { name: "cursor-highlight", app: () => /* @__PURE__ */ jsx55(CursorHighlightApp, {}) },
3793
+ { name: "text-style", app: () => /* @__PURE__ */ jsx55(TextStyleApp, {}) },
3794
+ { name: "headless", app: () => /* @__PURE__ */ jsx55(HeadlessApp, {}) },
3795
+ { name: "theming", app: () => /* @__PURE__ */ jsx55(ThemingApp, {}) },
3796
+ { name: "landing", app: () => /* @__PURE__ */ jsx55(LandingApp, { useKeyboard: useKeyboard21 }) },
3797
+ { name: "ripple", app: () => /* @__PURE__ */ jsx55(RippleApp, {}) },
3798
+ { name: "puzzle", app: () => /* @__PURE__ */ jsx55(PuzzleApp, {}) }
3151
3799
  ];
3152
3800
 
3153
3801
  // src/run.tsx
3154
- import { jsx as jsx52 } from "react/jsx-runtime";
3802
+ import { jsx as jsx56 } from "react/jsx-runtime";
3155
3803
  var _renderer;
3156
3804
  function DemoShell({ children }) {
3157
- useKeyboard19((event) => {
3158
- if (event.name === "q" || event.name === "escape") {
3805
+ useKeyboard22((event) => {
3806
+ if (event.defaultPrevented) return;
3807
+ if (event.name === "escape") {
3159
3808
  _renderer.destroy();
3160
3809
  }
3161
3810
  });
3162
- return /* @__PURE__ */ jsx52("box", { flexDirection: "column", flexGrow: 1, children });
3811
+ return /* @__PURE__ */ jsx56("box", { flexDirection: "column", flexGrow: 1, children });
3163
3812
  }
3164
3813
  async function runDemo(name2) {
3165
3814
  const demo = demos.find((d) => d.name === name2);
@@ -3169,7 +3818,7 @@ async function runDemo(name2) {
3169
3818
  process.exit(1);
3170
3819
  }
3171
3820
  _renderer = await createCliRenderer({ exitOnCtrlC: true });
3172
- createRoot(_renderer).render(/* @__PURE__ */ jsx52(DemoShell, { children: demo.app() }));
3821
+ createRoot(_renderer).render(/* @__PURE__ */ jsx56(DemoShell, { children: demo.app() }));
3173
3822
  }
3174
3823
  var name = process.argv[2];
3175
3824
  if (name) {