@gridland/demo 0.2.49 → 0.2.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/landing.js CHANGED
@@ -52,33 +52,6 @@ function textStyle(opts) {
52
52
 
53
53
  // ../ui/components/status-bar/status-bar.tsx
54
54
  import { jsx as jsx3 } from "react/jsx-runtime";
55
- function StatusBar({ items, extra }) {
56
- const theme = useTheme();
57
- const parts = [];
58
- if (extra !== void 0) {
59
- parts.push(
60
- /* @__PURE__ */ jsx3("span", { children: extra }, "extra")
61
- );
62
- parts.push(
63
- /* @__PURE__ */ jsx3("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: " \u2502 " }, "pipe")
64
- );
65
- }
66
- items.forEach((item, i) => {
67
- if (i > 0) {
68
- parts.push(/* @__PURE__ */ jsx3("span", { children: " " }, `gap-${i}`));
69
- }
70
- parts.push(
71
- /* @__PURE__ */ jsx3("span", { style: textStyle({ bold: true, fg: theme.background, bg: theme.muted }), children: ` ${item.key} ` }, `key-${i}`)
72
- );
73
- parts.push(
74
- /* @__PURE__ */ jsx3("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: ` ${item.label}` }, `label-${i}`)
75
- );
76
- });
77
- if (parts.length === 0) {
78
- return null;
79
- }
80
- return /* @__PURE__ */ jsx3("text", { children: parts });
81
- }
82
55
 
83
56
  // ../ui/components/provider/provider.tsx
84
57
  import { createContext as createContext2, useContext as useContext2 } from "react";
@@ -99,7 +72,7 @@ import { jsx as jsx6 } from "react/jsx-runtime";
99
72
  import { useEffect, useState as useState2 } from "react";
100
73
  import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
101
74
  var VARIANTS = {
102
- dots: { frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"], interval: 83 },
75
+ dots: { frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"], interval: 80 },
103
76
  pulse: { frames: ["\xB7", "\u2219", "\u25CF", "\u2219", "\xB7", "\xB7", "\xB7"], interval: 180 },
104
77
  meter: { frames: ["\u25B1\u25B1\u25B1", "\u25B0\u25B1\u25B1", "\u25B0\u25B0\u25B1", "\u25B0\u25B0\u25B0", "\u25B0\u25B0\u25B1", "\u25B0\u25B1\u25B1", "\u25B1\u25B1\u25B1"], interval: 143 },
105
78
  bloom: { frames: ["\xB7", "\u2726", "\u2727", "\u2739", "\u273A", "\u274B", "\u2738", "\u2735", "\u2738", "\u274B", "\u273A", "\u2739", "\u2727", "\u2726", "\xB7", "\xB7"], interval: 100 },
@@ -124,8 +97,9 @@ import { useReducer as useReducer2, useMemo as useMemo2, useRef as useRef3 } fro
124
97
  import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
125
98
 
126
99
  // ../ui/components/table/table.tsx
127
- import { Fragment } from "react";
100
+ import { createContext as createContext3, useContext as useContext3, Children, isValidElement, Fragment } from "react";
128
101
  import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
102
+ var TableContext = createContext3(null);
129
103
 
130
104
  // ../ui/components/gradient/gradient.tsx
131
105
  import { Fragment as Fragment2, jsx as jsx13 } from "react/jsx-runtime";
@@ -194,59 +168,29 @@ function Gradient({ children, name, colors }) {
194
168
  }
195
169
 
196
170
  // ../ui/components/tab-bar/tab-bar.tsx
197
- import { createContext as createContext3, useContext as useContext3, useState as useState5, Children, isValidElement } from "react";
171
+ import { createContext as createContext4, useContext as useContext4, useState as useState5, Children as Children2, isValidElement as isValidElement2 } from "react";
198
172
  import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
199
- var TabsContext = createContext3(null);
173
+ var TabsContext = createContext4(null);
200
174
 
201
175
  // ../ui/components/modal/modal.tsx
202
176
  import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
203
- function Modal({
204
- children,
205
- title,
206
- borderColor,
207
- borderStyle = "rounded",
208
- onClose,
209
- useKeyboard: useKeyboardProp
210
- }) {
211
- const theme = useTheme();
212
- const useKeyboard = useKeyboardContext(useKeyboardProp);
213
- const resolvedBorderColor = borderColor ?? theme.muted;
214
- useKeyboard?.((event) => {
215
- if (event.name === "escape" && onClose) {
216
- onClose();
217
- }
218
- });
219
- return /* @__PURE__ */ jsx15("box", { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx15(
220
- "box",
221
- {
222
- flexDirection: "column",
223
- flexGrow: 1,
224
- border: true,
225
- borderStyle,
226
- borderColor: resolvedBorderColor,
227
- children: title ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
228
- /* @__PURE__ */ jsx15("box", { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsx15("text", { style: textStyle({ bold: true, fg: theme.primary }), children: title }) }),
229
- children
230
- ] }) : children
231
- }
232
- ) });
233
- }
234
177
 
235
178
  // ../ui/components/prompt-input/prompt-input.tsx
236
179
  import {
237
180
  useState as useState6,
238
181
  useRef as useRef4,
239
182
  useCallback as useCallback2,
183
+ useEffect as useEffect2,
240
184
  useMemo as useMemo3,
241
- createContext as createContext4,
242
- useContext as useContext4
185
+ createContext as createContext5,
186
+ useContext as useContext5
243
187
  } from "react";
244
188
  import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
245
- var PromptInputControllerCtx = createContext4(null);
246
- var useOptionalController = () => useContext4(PromptInputControllerCtx);
247
- var PromptInputContext = createContext4(null);
189
+ var PromptInputControllerCtx = createContext5(null);
190
+ var useOptionalController = () => useContext5(PromptInputControllerCtx);
191
+ var PromptInputContext = createContext5(null);
248
192
  function usePromptInput() {
249
- const ctx = useContext4(PromptInputContext);
193
+ const ctx = useContext5(PromptInputContext);
250
194
  if (!ctx) {
251
195
  throw new Error("usePromptInput must be used within a <PromptInput> component");
252
196
  }
@@ -268,9 +212,10 @@ function resolveStatusHintText(status, submittedText, streamingText, errorText,
268
212
  if (status === "error") return errorText;
269
213
  return disabledText;
270
214
  }
215
+ var DIVIDER_LINE = "\u2500".repeat(500);
271
216
  function PromptInputDivider() {
272
217
  const { theme } = usePromptInput();
273
- return /* @__PURE__ */ jsx16("text", { wrapMode: "none", children: /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.muted }), children: "\u2500".repeat(500) }) });
218
+ return /* @__PURE__ */ jsx16("text", { wrapMode: "none", marginLeft: -1, marginRight: -1, children: /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.muted }), children: DIVIDER_LINE }) });
274
219
  }
275
220
  function PromptInputSuggestions() {
276
221
  const { suggestions, sugIdx, maxSuggestions, theme } = usePromptInput();
@@ -344,11 +289,20 @@ function PromptInput({
344
289
  enableHistory = true,
345
290
  model,
346
291
  showDividers = true,
292
+ autoFocus = false,
347
293
  useKeyboard: useKeyboardProp,
348
294
  children
349
295
  }) {
350
296
  const theme = useTheme();
351
297
  const useKeyboard = useKeyboardContext(useKeyboardProp);
298
+ useEffect2(() => {
299
+ if (!autoFocus) return;
300
+ if (typeof document === "undefined") return;
301
+ const canvas = document.querySelector("canvas");
302
+ if (canvas && document.activeElement !== canvas) {
303
+ canvas.focus();
304
+ }
305
+ }, [autoFocus]);
352
306
  const resolvedPromptColor = promptColor ?? theme.muted;
353
307
  const disabled = status ? status === "submitted" || status === "streaming" : disabledProp;
354
308
  const statusHintText = resolveStatusHintText(status, submittedText, streamingLabel, errorText, disabledText);
@@ -542,14 +496,16 @@ function PromptInput({
542
496
  theme
543
497
  };
544
498
  if (children) {
545
- return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx16("box", { flexDirection: "column", children }) });
499
+ return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx16("box", { flexDirection: "column", flexShrink: 0, children }) });
546
500
  }
547
- return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxs10("box", { flexDirection: "column", children: [
501
+ return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxs10("box", { flexDirection: "column", flexShrink: 0, children: [
548
502
  showDividers && /* @__PURE__ */ jsx16(PromptInputDivider, {}),
549
- /* @__PURE__ */ jsx16(PromptInputSuggestions, {}),
550
- /* @__PURE__ */ jsx16(PromptInputTextarea, {}),
551
- /* @__PURE__ */ jsx16(PromptInputStatusText, {}),
552
- /* @__PURE__ */ jsx16(PromptInputModel, {}),
503
+ /* @__PURE__ */ jsxs10("box", { flexDirection: "column", paddingX: 1, children: [
504
+ /* @__PURE__ */ jsx16(PromptInputSuggestions, {}),
505
+ /* @__PURE__ */ jsx16(PromptInputTextarea, {}),
506
+ /* @__PURE__ */ jsx16(PromptInputStatusText, {}),
507
+ /* @__PURE__ */ jsx16(PromptInputModel, {})
508
+ ] }),
553
509
  showDividers && /* @__PURE__ */ jsx16(PromptInputDivider, {})
554
510
  ] }) });
555
511
  }
@@ -563,13 +519,19 @@ PromptInput.Model = PromptInputModel;
563
519
  // ../ui/components/chat/chat.tsx
564
520
  import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
565
521
 
566
- // ../ui/components/timeline/timeline.tsx
567
- import { useState as useState7, useEffect as useEffect2, useRef as useRef5 } from "react";
568
- import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
522
+ // ../ui/components/chain-of-thought/chain-of-thought.tsx
523
+ import { createContext as createContext6, memo, useContext as useContext6, useEffect as useEffect3, useMemo as useMemo4, useState as useState7 } from "react";
524
+ import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
569
525
  var DOTS = ["\u25CB", "\u25D4", "\u25D1", "\u25D5", "\u25CF"];
570
- function getStepDot(status) {
571
- return status === "pending" ? "\u25CB" : "\u25CF";
572
- }
526
+ var SPINNER_INTERVAL = 150;
527
+ var ChainOfThoughtContext = createContext6(null);
528
+ var useChainOfThought = () => {
529
+ const context = useContext6(ChainOfThoughtContext);
530
+ if (!context) {
531
+ throw new Error("ChainOfThought components must be used within <ChainOfThought>");
532
+ }
533
+ return context;
534
+ };
573
535
  function getStepColor(status, theme) {
574
536
  switch (status) {
575
537
  case "done":
@@ -584,78 +546,89 @@ function getStepColor(status, theme) {
584
546
  return theme.muted;
585
547
  }
586
548
  }
587
- function StepRow({ step, isLast, theme, frame }) {
588
- const color = getStepColor(step.status, theme);
589
- const isActive = step.status === "running";
590
- const isPending = step.status === "pending";
549
+ var ChainOfThought = memo(({
550
+ open,
551
+ defaultOpen = false,
552
+ onOpenChange,
553
+ children
554
+ }) => {
555
+ const [internalOpen, setInternalOpen] = useState7(defaultOpen);
556
+ const isOpen = open ?? internalOpen;
557
+ const setIsOpen = onOpenChange ?? setInternalOpen;
558
+ const context = useMemo4(
559
+ () => ({ isOpen, setIsOpen }),
560
+ [isOpen, setIsOpen]
561
+ );
562
+ return /* @__PURE__ */ jsx18(ChainOfThoughtContext.Provider, { value: context, children: /* @__PURE__ */ jsx18("box", { flexDirection: "column", children }) });
563
+ });
564
+ var ChainOfThoughtHeader = memo(({
565
+ duration,
566
+ children = "Thought for"
567
+ }) => {
568
+ const theme = useTheme();
569
+ const { isOpen } = useChainOfThought();
570
+ const arrow = isOpen ? "\u25BC" : "\u25B6";
571
+ return /* @__PURE__ */ jsxs12("text", { children: [
572
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.muted }), children: arrow }),
573
+ /* @__PURE__ */ jsxs12("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
574
+ " ",
575
+ children,
576
+ duration ? " " + duration : ""
577
+ ] })
578
+ ] });
579
+ });
580
+ var ChainOfThoughtContent = memo(({ children }) => {
581
+ const { isOpen } = useChainOfThought();
582
+ if (!isOpen) return null;
583
+ return /* @__PURE__ */ jsx18(Fragment6, { children });
584
+ });
585
+ var ChainOfThoughtStep = memo(({
586
+ label,
587
+ description,
588
+ status = "done",
589
+ isLast = false,
590
+ children
591
+ }) => {
592
+ const theme = useTheme();
593
+ const isActive = status === "running";
594
+ const isPending = status === "pending";
595
+ const color = getStepColor(status, theme);
591
596
  const pipe = "\u2502";
592
- const dot = isActive ? DOTS[frame % DOTS.length] : getStepDot(step.status);
593
- const dashIdx = step.label.indexOf(" \u2014 ");
594
- const mainLabel = dashIdx >= 0 ? step.label.slice(0, dashIdx) : step.label;
595
- const detail = dashIdx >= 0 ? step.label.slice(dashIdx) : "";
597
+ const [frame, setFrame] = useState7(0);
598
+ useEffect3(() => {
599
+ if (!isActive) {
600
+ setFrame(0);
601
+ return;
602
+ }
603
+ const id = setInterval(() => setFrame((f) => f + 1), SPINNER_INTERVAL);
604
+ return () => clearInterval(id);
605
+ }, [isActive]);
606
+ const dot = isActive ? DOTS[frame % DOTS.length] : isPending ? "\u25CB" : "\u25CF";
596
607
  return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", marginLeft: 1, children: [
597
608
  /* @__PURE__ */ jsxs12("text", { children: [
598
609
  /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color }), children: dot }),
599
610
  /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.foreground }), children: " " }),
600
- /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: isPending ? theme.muted : color, dim: isPending, bold: isActive }), children: mainLabel }),
601
- detail && /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: detail })
611
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: isPending ? theme.muted : color, dim: isPending, bold: isActive }), children: label }),
612
+ description && /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " \u2014 " + description })
602
613
  ] }),
603
- step.output && /* @__PURE__ */ jsxs12("text", { children: [
614
+ children && /* @__PURE__ */ jsxs12("text", { children: [
604
615
  /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color, dim: true }), children: pipe + " " }),
605
- /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: step.status === "error" ? theme.error : theme.accent }), children: step.output })
616
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: status === "error" ? theme.error : theme.accent }), children })
606
617
  ] }),
607
618
  !isLast && /* @__PURE__ */ jsx18("text", { children: /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color, dim: true }), children: pipe }) })
608
619
  ] });
609
- }
610
- function Timeline({
611
- steps,
612
- duration,
613
- collapsed = true,
614
- headerLabel = "Thought for"
615
- }) {
616
- const theme = useTheme();
617
- const arrow = collapsed ? "\u25B6" : "\u25BC";
618
- const durationStr = duration ?? "0ms";
619
- const hasRunning = steps?.some((s) => s.status === "running") ?? false;
620
- const [frame, setFrame] = useState7(0);
621
- const alive = useRef5(true);
622
- useEffect2(() => {
623
- alive.current = true;
624
- return () => {
625
- alive.current = false;
626
- };
627
- }, []);
628
- useEffect2(() => {
629
- if (!hasRunning) return;
630
- const id = setInterval(() => {
631
- if (alive.current) setFrame((f) => f + 1);
632
- }, 150);
633
- return () => clearInterval(id);
634
- }, [hasRunning]);
635
- return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", children: [
636
- /* @__PURE__ */ jsxs12("text", { children: [
637
- /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.muted }), children: arrow }),
638
- /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " " + headerLabel + " " + durationStr })
639
- ] }),
640
- !collapsed && steps && steps.map((step, i) => /* @__PURE__ */ jsx18(
641
- StepRow,
642
- {
643
- step,
644
- isLast: i === steps.length - 1,
645
- theme,
646
- frame
647
- },
648
- `step-${i}`
649
- ))
650
- ] });
651
- }
620
+ });
621
+ ChainOfThought.displayName = "ChainOfThought";
622
+ ChainOfThoughtHeader.displayName = "ChainOfThoughtHeader";
623
+ ChainOfThoughtContent.displayName = "ChainOfThoughtContent";
624
+ ChainOfThoughtStep.displayName = "ChainOfThoughtStep";
652
625
 
653
626
  // ../ui/components/message/message.tsx
654
- import { createContext as createContext5, useContext as useContext5 } from "react";
627
+ import { createContext as createContext7, useContext as useContext7 } from "react";
655
628
  import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
656
- var MessageContext = createContext5(null);
629
+ var MessageContext = createContext7(null);
657
630
  function useMessage() {
658
- const ctx = useContext5(MessageContext);
631
+ const ctx = useContext7(MessageContext);
659
632
  if (!ctx) throw new Error("useMessage must be used within <Message>");
660
633
  return ctx;
661
634
  }
@@ -664,23 +637,25 @@ function getBubbleColors(theme) {
664
637
  return isDark ? { assistantBg: "#2a2a4a", userBg: "#2a3a3a" } : { assistantBg: "#F1F5F9", userBg: "#E2E8F0" };
665
638
  }
666
639
  var TOOL_STATE_ICONS = {
667
- "partial-call": "\u2022",
640
+ pending: "\u2022",
668
641
  // •
669
- "call": "\u280B",
642
+ running: "\u280B",
670
643
  // ⠋
671
- "result": "\u2713"
644
+ completed: "\u2713",
672
645
  // ✓
646
+ error: "\u2715"
647
+ // ✕
673
648
  };
674
649
  function getToolStateColor(state, theme) {
675
650
  switch (state) {
676
- case "partial-call":
651
+ case "pending":
677
652
  return theme.muted;
678
- case "call":
653
+ case "running":
679
654
  return theme.warning;
680
- case "result":
655
+ case "completed":
681
656
  return theme.success;
682
- default:
683
- return theme.muted;
657
+ case "error":
658
+ return theme.error;
684
659
  }
685
660
  }
686
661
  function MessageContent({ children }) {
@@ -705,45 +680,57 @@ function MessageText({ children, isLast = false }) {
705
680
  isLast && isStreaming && /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: streamingCursor })
706
681
  ] });
707
682
  }
708
- function MessageReasoning({ part }) {
709
- return /* @__PURE__ */ jsx19(
710
- Timeline,
711
- {
712
- steps: part.steps,
713
- duration: part.duration,
714
- collapsed: part.collapsed
715
- }
716
- );
683
+ function MessageReasoning({ duration, steps, collapsed = true, children }) {
684
+ return /* @__PURE__ */ jsxs13(ChainOfThought, { defaultOpen: !collapsed, children: [
685
+ /* @__PURE__ */ jsx19(ChainOfThoughtHeader, { duration }),
686
+ /* @__PURE__ */ jsxs13(ChainOfThoughtContent, { children: [
687
+ steps?.map((step, i) => /* @__PURE__ */ jsx19(
688
+ ChainOfThoughtStep,
689
+ {
690
+ label: step.label,
691
+ description: step.description,
692
+ status: step.status,
693
+ isLast: i === (steps?.length ?? 0) - 1,
694
+ children: step.output
695
+ },
696
+ i
697
+ )),
698
+ children
699
+ ] })
700
+ ] });
717
701
  }
718
- function MessageToolInvocation({ part, toolColors }) {
702
+ function MessageToolCall({ name, state = "pending", result, color }) {
719
703
  const theme = useTheme();
720
704
  const { backgroundColor, textColor } = useMessage();
721
- const { toolName, state, result } = part.toolInvocation;
722
- const icon = TOOL_STATE_ICONS[state] || "\u2022";
723
- const stateColor = toolColors?.[toolName] ?? getToolStateColor(state, theme);
724
- const isActive = state === "partial-call" || state === "call";
705
+ const icon = TOOL_STATE_ICONS[state];
706
+ const stateColor = color ?? getToolStateColor(state, theme);
707
+ const isActive = state === "pending" || state === "running";
725
708
  return /* @__PURE__ */ jsxs13("box", { flexDirection: "column", children: [
726
709
  /* @__PURE__ */ jsxs13("text", { children: [
727
710
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bg: backgroundColor }), children: icon }),
728
711
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, bg: backgroundColor }), children: " " }),
729
- /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bold: isActive, bg: backgroundColor }), children: toolName }),
712
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bold: isActive, bg: backgroundColor }), children: name }),
730
713
  isActive && /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: " ..." })
731
714
  ] }),
732
- state === "result" && result !== void 0 && /* @__PURE__ */ jsxs13("text", { children: [
715
+ state === "completed" && result !== void 0 && /* @__PURE__ */ jsxs13("text", { children: [
733
716
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: " \u2514\u2500 " }),
734
717
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: String(result).slice(0, 120) })
718
+ ] }),
719
+ state === "error" && result !== void 0 && /* @__PURE__ */ jsxs13("text", { children: [
720
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.error, dim: true, bg: backgroundColor }), children: " \u2514\u2500 " }),
721
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.error, dim: true, bg: backgroundColor }), children: String(result).slice(0, 120) })
735
722
  ] })
736
723
  ] });
737
724
  }
738
- function MessageSource({ part, index }) {
725
+ function MessageSource({ title, url, index }) {
739
726
  const theme = useTheme();
740
727
  const { backgroundColor, textColor } = useMessage();
741
- const title = part.source.title || part.source.url || "source";
728
+ const displayTitle = title || url || "source";
742
729
  return /* @__PURE__ */ jsxs13("text", { children: [
743
730
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: "[" }),
744
731
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: String(index + 1) }),
745
732
  /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: "] " }),
746
- /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: title })
733
+ /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: displayTitle })
747
734
  ] });
748
735
  }
749
736
  function MessageFooter({ model, timestamp }) {
@@ -779,7 +766,7 @@ function Message({
779
766
  Message.Content = MessageContent;
780
767
  Message.Text = MessageText;
781
768
  Message.Reasoning = MessageReasoning;
782
- Message.ToolInvocation = MessageToolInvocation;
769
+ Message.ToolCall = MessageToolCall;
783
770
  Message.Source = MessageSource;
784
771
  Message.Footer = MessageFooter;
785
772
 
@@ -805,64 +792,14 @@ function useBreakpoints() {
805
792
  };
806
793
  }
807
794
 
808
- // ../docs/components/landing/landing-app.tsx
809
- import { useCallback as useCallback3, useRef as useRef8, useState as useState10 } from "react";
795
+ // src/landing/landing-app.tsx
796
+ import { useMemo as useMemo7 } from "react";
810
797
 
811
- // ../docs/components/landing/about-modal.tsx
798
+ // src/landing/install-box.tsx
812
799
  import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
813
- function AboutModal({ onClose, useKeyboard }) {
814
- const theme = useTheme();
815
- return /* @__PURE__ */ jsx21(Modal, { title: "About Gridland", useKeyboard, onClose, children: /* @__PURE__ */ jsxs15("box", { paddingX: 1, flexDirection: "column", gap: 1, children: [
816
- /* @__PURE__ */ jsx21("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "What is Gridland?" }),
817
- /* @__PURE__ */ jsx21("text", { children: "Gridland renders terminal UIs to HTML5 Canvas with React." }),
818
- /* @__PURE__ */ jsx21("text", { children: "No xterm.js. No terminal emulator. Just pixels." }),
819
- /* @__PURE__ */ jsx21("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "Features" }),
820
- /* @__PURE__ */ jsxs15("text", { children: [
821
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
822
- "\u2022",
823
- " "
824
- ] }),
825
- "Canvas-rendered TUI components"
826
- ] }),
827
- /* @__PURE__ */ jsxs15("text", { children: [
828
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
829
- "\u2022",
830
- " "
831
- ] }),
832
- "React reconciler with JSX"
833
- ] }),
834
- /* @__PURE__ */ jsxs15("text", { children: [
835
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
836
- "\u2022",
837
- " "
838
- ] }),
839
- "Yoga flexbox layout engine"
840
- ] }),
841
- /* @__PURE__ */ jsxs15("text", { children: [
842
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
843
- "\u2022",
844
- " "
845
- ] }),
846
- "Keyboard, mouse, and clipboard support"
847
- ] }),
848
- /* @__PURE__ */ jsxs15("text", { children: [
849
- /* @__PURE__ */ jsxs15("span", { style: textStyle({ dim: true }), children: [
850
- "\u2022",
851
- " "
852
- ] }),
853
- "Next.js and Vite plugins"
854
- ] }),
855
- /* @__PURE__ */ jsx21("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "Tech Stack" }),
856
- /* @__PURE__ */ jsx21("text", { children: "React + opentui engine + yoga-layout + HTML5 Canvas" }),
857
- /* @__PURE__ */ jsx21("text", { style: textStyle({ dim: true }), children: "Press q to close" })
858
- ] }) });
859
- }
860
-
861
- // ../docs/components/landing/install-box.tsx
862
- import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
863
800
  function InstallBox() {
864
801
  const theme = useTheme();
865
- return /* @__PURE__ */ jsx22(
802
+ return /* @__PURE__ */ jsx21(
866
803
  "box",
867
804
  {
868
805
  border: true,
@@ -871,21 +808,21 @@ function InstallBox() {
871
808
  paddingX: 1,
872
809
  flexDirection: "column",
873
810
  flexShrink: 0,
874
- children: /* @__PURE__ */ jsxs16("text", { children: [
875
- /* @__PURE__ */ jsx22("span", { style: textStyle({ dim: true }), children: "$ " }),
876
- /* @__PURE__ */ jsx22("span", { style: textStyle({ bold: true }), children: "bun create " }),
877
- /* @__PURE__ */ jsx22("span", { style: textStyle({ fg: theme.accent }), children: "gridland" })
811
+ children: /* @__PURE__ */ jsxs15("text", { children: [
812
+ /* @__PURE__ */ jsx21("span", { style: textStyle({ dim: true }), children: "$ " }),
813
+ /* @__PURE__ */ jsx21("span", { style: textStyle({ bold: true }), children: "bun create " }),
814
+ /* @__PURE__ */ jsx21("span", { style: textStyle({ fg: theme.accent }), children: "gridland" })
878
815
  ] })
879
816
  }
880
817
  );
881
818
  }
882
819
 
883
- // ../docs/components/landing/links-box.tsx
884
- import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
820
+ // src/landing/links-box.tsx
821
+ import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
885
822
  var UNDERLINE3 = 1 << 3;
886
823
  function LinksBox() {
887
824
  const theme = useTheme();
888
- return /* @__PURE__ */ jsx23(
825
+ return /* @__PURE__ */ jsx22(
889
826
  "box",
890
827
  {
891
828
  border: true,
@@ -894,22 +831,22 @@ function LinksBox() {
894
831
  paddingX: 1,
895
832
  flexDirection: "column",
896
833
  flexShrink: 0,
897
- children: /* @__PURE__ */ jsxs17("text", { children: [
898
- /* @__PURE__ */ jsx23("span", { children: "\u{1F431}" }),
899
- /* @__PURE__ */ jsx23("a", { href: "https://github.com/thoughtfulllc/gridland", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " GitHub" }),
900
- /* @__PURE__ */ jsx23("span", { children: " " }),
901
- /* @__PURE__ */ jsx23("span", { children: "\u{1F4D6}" }),
902
- /* @__PURE__ */ jsx23("a", { href: "https://gridland.io/docs", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " Docs" })
834
+ children: /* @__PURE__ */ jsxs16("text", { children: [
835
+ /* @__PURE__ */ jsx22("span", { children: "\u{1F431}" }),
836
+ /* @__PURE__ */ jsx22("a", { href: "https://github.com/thoughtfulllc/gridland", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " GitHub" }),
837
+ /* @__PURE__ */ jsx22("span", { children: " " }),
838
+ /* @__PURE__ */ jsx22("span", { children: "\u{1F4D6}" }),
839
+ /* @__PURE__ */ jsx22("a", { href: "https://gridland.io/docs", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " Docs" })
903
840
  ] })
904
841
  }
905
842
  );
906
843
  }
907
844
 
908
- // ../docs/components/landing/logo.tsx
909
- import { useState as useState8, useEffect as useEffect3, useRef as useRef6, useMemo as useMemo4 } from "react";
845
+ // src/landing/logo.tsx
846
+ import { useState as useState8, useEffect as useEffect4, useRef as useRef5, useMemo as useMemo5 } from "react";
910
847
  import figlet from "figlet";
911
848
  import ansiShadow from "figlet/importable-fonts/ANSI Shadow.js";
912
- import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
849
+ import { Fragment as Fragment7, jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
913
850
  figlet.parseFont("ANSI Shadow", ansiShadow);
914
851
  function makeArt(text) {
915
852
  return figlet.textSync(text, { font: "ANSI Shadow" }).split("\n").filter((l) => l.trimEnd().length > 0).join("\n");
@@ -921,8 +858,8 @@ var ART_HEIGHT = 6;
921
858
  function useAnimation(duration = 1e3) {
922
859
  const isBrowser = typeof document !== "undefined";
923
860
  const [progress, setProgress] = useState8(isBrowser ? 0 : 1);
924
- const startTime = useRef6(null);
925
- useEffect3(() => {
861
+ const startTime = useRef5(null);
862
+ useEffect4(() => {
926
863
  if (!isBrowser) return;
927
864
  let raf;
928
865
  const tick = (time) => {
@@ -942,9 +879,9 @@ function RevealGradient({ children, revealCol }) {
942
879
  const gradientColors = GRADIENTS.instagram;
943
880
  const lines = children.split("\n");
944
881
  const maxLength = Math.max(...lines.map((l) => l.length));
945
- if (maxLength === 0) return /* @__PURE__ */ jsx24("text", { children });
946
- const hexColors = useMemo4(() => generateGradient(gradientColors, maxLength), [maxLength]);
947
- return /* @__PURE__ */ jsx24("box", { position: "relative", width: maxLength, height: lines.length, shouldFill: false, children: lines.map((line, lineIndex) => {
882
+ if (maxLength === 0) return /* @__PURE__ */ jsx23("text", { children });
883
+ const hexColors = useMemo5(() => generateGradient(gradientColors, maxLength), [maxLength]);
884
+ return /* @__PURE__ */ jsx23("box", { position: "relative", width: maxLength, height: lines.length, shouldFill: false, children: lines.map((line, lineIndex) => {
948
885
  const runs = [];
949
886
  let current = null;
950
887
  for (let i = 0; i < line.length; i++) {
@@ -964,14 +901,14 @@ function RevealGradient({ children, revealCol }) {
964
901
  }
965
902
  }
966
903
  if (current) runs.push(current);
967
- return runs.map((run, runIndex) => /* @__PURE__ */ jsx24(
904
+ return runs.map((run, runIndex) => /* @__PURE__ */ jsx23(
968
905
  "box",
969
906
  {
970
907
  position: "absolute",
971
908
  top: lineIndex,
972
909
  left: run.start,
973
910
  shouldFill: false,
974
- children: /* @__PURE__ */ jsx24("text", { shouldFill: false, children: run.chars.map((char, ci) => /* @__PURE__ */ jsx24(
911
+ children: /* @__PURE__ */ jsx23("text", { shouldFill: false, children: run.chars.map((char, ci) => /* @__PURE__ */ jsx23(
975
912
  "span",
976
913
  {
977
914
  style: { fg: hexColors[run.start + ci] },
@@ -993,20 +930,20 @@ function Logo({ compact, narrow, mobile }) {
993
930
  const maxWidth = compact ? 8 : narrow ? 40 : 62;
994
931
  const revealCol = Math.round(revealProgress * (maxWidth + 4)) - 2;
995
932
  const taglineOpacity = Math.max(0, Math.min(1, (progress - 0.7) / 0.3));
996
- const subtitle = /* @__PURE__ */ jsxs18(Fragment6, { children: [
997
- /* @__PURE__ */ jsx24("text", { children: " " }),
998
- /* @__PURE__ */ jsx24("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs18("text", { style: textStyle({ fg: "#d4b0e8" }), opacity: taglineOpacity, wrapMode: "word", textAlign: "center", width: "100%", shouldFill: false, children: [
933
+ const subtitle = /* @__PURE__ */ jsxs17(Fragment7, { children: [
934
+ /* @__PURE__ */ jsx23("text", { children: " " }),
935
+ /* @__PURE__ */ jsx23("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs17("text", { style: textStyle({ fg: "#d4b0e8" }), opacity: taglineOpacity, wrapMode: "word", textAlign: "center", width: "100%", shouldFill: false, children: [
999
936
  "A framework for building terminal apps, built on ",
1000
- /* @__PURE__ */ jsx24("a", { href: "https://opentui.com", style: { attributes: 72, fg: "#d4b0e8" }, children: "OpenTUI" }),
937
+ /* @__PURE__ */ jsx23("a", { href: "https://opentui.com", style: { attributes: 72, fg: "#d4b0e8" }, children: "OpenTUI" }),
1001
938
  " + React." + (mobile ? " " : "\n") + "(Gridland apps, like this website, work in the browser and terminal.)"
1002
939
  ] }) })
1003
940
  ] });
1004
941
  if (!isBrowser) {
1005
942
  const art = compact ? "gridland" : narrow ? gridArt + "\n" + landArt : fullArt;
1006
- return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexShrink: 0, width: "100%", alignItems: "center", children: [
1007
- /* @__PURE__ */ jsx24(Gradient, { name: "instagram", children: art }),
1008
- /* @__PURE__ */ jsx24("text", { children: " " }),
1009
- /* @__PURE__ */ jsx24("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs18("text", { style: textStyle({ fg: "#d4b0e8" }), shouldFill: false, children: [
943
+ return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexShrink: 0, width: "100%", alignItems: "center", shouldFill: false, children: [
944
+ /* @__PURE__ */ jsx23(Gradient, { name: "instagram", children: art }),
945
+ /* @__PURE__ */ jsx23("text", { children: " " }),
946
+ /* @__PURE__ */ jsx23("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs17("text", { style: textStyle({ fg: "#d4b0e8" }), shouldFill: false, children: [
1010
947
  "A framework for building terminal apps, built on OpenTUI + React.",
1011
948
  "\n",
1012
949
  "(Gridland apps, like this website, work in the browser and terminal.)"
@@ -1014,31 +951,31 @@ function Logo({ compact, narrow, mobile }) {
1014
951
  ] });
1015
952
  }
1016
953
  if (compact) {
1017
- return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
1018
- /* @__PURE__ */ jsx24("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx24("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx24(RevealGradient, { revealCol, children: "gridland" }) }) }),
954
+ return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
955
+ /* @__PURE__ */ jsx23("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx23("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx23(RevealGradient, { revealCol, children: "gridland" }) }) }),
1019
956
  subtitle
1020
957
  ] });
1021
958
  }
1022
959
  if (narrow) {
1023
- return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
1024
- /* @__PURE__ */ jsx24("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsxs18("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: [
1025
- /* @__PURE__ */ jsx24(RevealGradient, { revealCol, children: gridArt }),
1026
- /* @__PURE__ */ jsx24(RevealGradient, { revealCol, children: landArt })
960
+ return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
961
+ /* @__PURE__ */ jsx23("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsxs17("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: [
962
+ /* @__PURE__ */ jsx23(RevealGradient, { revealCol, children: gridArt }),
963
+ /* @__PURE__ */ jsx23(RevealGradient, { revealCol, children: landArt })
1027
964
  ] }) }),
1028
965
  subtitle
1029
966
  ] });
1030
967
  }
1031
- return /* @__PURE__ */ jsxs18("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
1032
- /* @__PURE__ */ jsx24("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx24("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx24(RevealGradient, { revealCol, children: fullArt }) }) }),
968
+ return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
969
+ /* @__PURE__ */ jsx23("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx23("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx23(RevealGradient, { revealCol, children: fullArt }) }) }),
1033
970
  subtitle
1034
971
  ] });
1035
972
  }
1036
973
 
1037
- // ../docs/components/landing/matrix-background.tsx
1038
- import { useMemo as useMemo5 } from "react";
974
+ // src/landing/matrix-background.tsx
975
+ import { useMemo as useMemo6 } from "react";
1039
976
 
1040
- // ../docs/components/landing/use-matrix.ts
1041
- import { useState as useState9, useEffect as useEffect4, useRef as useRef7 } from "react";
977
+ // src/landing/use-matrix.ts
978
+ import { useState as useState9, useEffect as useEffect5, useRef as useRef6 } from "react";
1042
979
  var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789@#$%^&*(){}[]|;:<>,.?/~`";
1043
980
  function randomChar() {
1044
981
  return CHARS[Math.floor(Math.random() * CHARS.length)];
@@ -1072,7 +1009,7 @@ function buildGrid(columns, width, height) {
1072
1009
  return { grid, brightness };
1073
1010
  }
1074
1011
  function useMatrix(width, height) {
1075
- const columnsRef = useRef7([]);
1012
+ const columnsRef = useRef6([]);
1076
1013
  const [state, setState] = useState9(() => {
1077
1014
  const columns = Array.from(
1078
1015
  { length: width },
@@ -1081,7 +1018,7 @@ function useMatrix(width, height) {
1081
1018
  columnsRef.current = columns;
1082
1019
  return buildGrid(columns, width, height);
1083
1020
  });
1084
- useEffect4(() => {
1021
+ useEffect5(() => {
1085
1022
  if (width < 2 || height < 2) return;
1086
1023
  const id = setInterval(() => {
1087
1024
  const columns = columnsRef.current;
@@ -1106,7 +1043,7 @@ function useMatrix(width, height) {
1106
1043
  }, 80);
1107
1044
  return () => clearInterval(id);
1108
1045
  }, [width, height]);
1109
- useEffect4(() => {
1046
+ useEffect5(() => {
1110
1047
  columnsRef.current = Array.from(
1111
1048
  { length: width },
1112
1049
  () => Math.random() < 0.5 ? createDrop(height, true) : null
@@ -1116,8 +1053,8 @@ function useMatrix(width, height) {
1116
1053
  return state;
1117
1054
  }
1118
1055
 
1119
- // ../docs/components/landing/matrix-background.tsx
1120
- import { jsx as jsx25 } from "react/jsx-runtime";
1056
+ // src/landing/matrix-background.tsx
1057
+ import { jsx as jsx24 } from "react/jsx-runtime";
1121
1058
  var MUTE_LEVELS = [0.12, 0.18, 0.24, 0.3, 0.38];
1122
1059
  var BG = hexToRgb("#1a1a2e");
1123
1060
  function buildMutedColors(baseHex) {
@@ -1136,23 +1073,23 @@ function colorForCell(mutedColors, b) {
1136
1073
  function MatrixBackground({ width, height, clearRect, clearRects }) {
1137
1074
  const { grid, brightness } = useMatrix(width, height);
1138
1075
  const theme = useTheme();
1139
- const columnColors = useMemo5(
1076
+ const columnColors = useMemo6(
1140
1077
  () => width > 0 ? generateGradient([theme.accent, theme.secondary, theme.primary], width) : [],
1141
1078
  [width, theme.accent, theme.secondary, theme.primary]
1142
1079
  );
1143
- const columnMutedColors = useMemo5(
1080
+ const columnMutedColors = useMemo6(
1144
1081
  () => columnColors.map(buildMutedColors),
1145
1082
  [columnColors]
1146
1083
  );
1147
- return /* @__PURE__ */ jsx25("box", { flexDirection: "column", children: grid.map((row, y) => /* @__PURE__ */ jsx25("text", { children: row.map((cell, x) => {
1084
+ return /* @__PURE__ */ jsx24("box", { flexDirection: "column", children: grid.map((row, y) => /* @__PURE__ */ jsx24("text", { children: row.map((cell, x) => {
1148
1085
  const inClearRect = clearRect && y >= clearRect.top && y < clearRect.top + clearRect.height && x >= clearRect.left && x < clearRect.left + clearRect.width || clearRects && clearRects.some(
1149
1086
  (r) => y >= r.top && y < r.top + r.height && x >= r.left && x < r.left + r.width
1150
1087
  );
1151
1088
  const mutedColors = columnMutedColors[x];
1152
1089
  if (cell === " " || inClearRect || !mutedColors) {
1153
- return /* @__PURE__ */ jsx25("span", { children: " " }, x);
1090
+ return /* @__PURE__ */ jsx24("span", { children: " " }, x);
1154
1091
  }
1155
- return /* @__PURE__ */ jsx25(
1092
+ return /* @__PURE__ */ jsx24(
1156
1093
  "span",
1157
1094
  {
1158
1095
  style: {
@@ -1165,84 +1102,48 @@ function MatrixBackground({ width, height, clearRect, clearRects }) {
1165
1102
  }) }, y)) });
1166
1103
  }
1167
1104
 
1168
- // ../docs/components/landing/landing-app.tsx
1169
- import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
1170
- var DEMO_RESPONSES = [
1171
- "Gridland is a framework for building terminal apps with React. It works in both the browser and terminal!",
1172
- "You can get started with `bun create gridland` to scaffold a new project.",
1173
- "OpenTUI provides the layout primitives \u2014 flexbox, borders, text styling \u2014 while React handles the component model.",
1174
- "Yes! Gridland apps are universal \u2014 the same code renders in a terminal emulator and in the browser.",
1175
- "Check out the docs for examples of interactive components like inputs, selects, and tables."
1176
- ];
1105
+ // src/landing/landing-app.tsx
1106
+ import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
1177
1107
  function LandingApp({ useKeyboard }) {
1178
1108
  const theme = useTheme();
1179
1109
  const { width, height, isNarrow, isTiny, isMobile } = useBreakpoints();
1180
- const [showAbout, setShowAbout] = useState10(false);
1181
- const [messages, setMessages] = useState10([]);
1182
- const [chatStatus, setChatStatus] = useState10("ready");
1183
- const responseIdx = useRef8(0);
1184
- const handleChatSubmit = useCallback3(({ text }) => {
1185
- const userMsg = { id: `u-${Date.now()}`, role: "user", content: text };
1186
- setMessages((prev) => [...prev, userMsg]);
1187
- setChatStatus("streaming");
1188
- setTimeout(() => {
1189
- const response = DEMO_RESPONSES[responseIdx.current % DEMO_RESPONSES.length];
1190
- responseIdx.current += 1;
1191
- const assistantMsg = { id: `a-${Date.now()}`, role: "assistant", content: response };
1192
- setMessages((prev) => [...prev, assistantMsg]);
1193
- setChatStatus("ready");
1194
- }, 1200);
1195
- }, []);
1196
- useKeyboard((event) => {
1197
- if (event.name === "a" && !showAbout) {
1198
- setShowAbout(true);
1199
- }
1200
- if (event.name === "q" && showAbout) {
1201
- setShowAbout(false);
1202
- }
1203
- });
1204
- if (showAbout) {
1205
- return /* @__PURE__ */ jsxs19("box", { flexDirection: "column", width: "100%", height: "100%", children: [
1206
- /* @__PURE__ */ jsx26("box", { flexGrow: 1, children: /* @__PURE__ */ jsx26(AboutModal, { onClose: () => setShowAbout(false), useKeyboard }) }),
1207
- /* @__PURE__ */ jsx26(StatusBar, { items: [{ key: "q", label: "close" }] })
1208
- ] });
1209
- }
1210
1110
  const isBrowser = typeof document !== "undefined";
1211
- const logoHeight = isTiny ? 2 : isNarrow ? 13 : 7;
1212
- const logoExtra = isBrowser ? 1 : 0;
1213
- const gap = isMobile ? 0 : 1;
1214
- const installLinksTop = 3 + logoHeight + logoExtra + gap;
1215
- const installLinksHeight = 3;
1216
- const boxTop = installLinksTop + installLinksHeight + gap + 1;
1217
- const boxHeight = height - boxTop - 1 - 1;
1218
- const clearRect = { top: boxTop, left: 1, width: width - 2, height: boxHeight };
1219
- const installLinksClearRect = { top: installLinksTop, left: 1, width: width - 2, height: installLinksHeight };
1220
- return /* @__PURE__ */ jsxs19("box", { width: "100%", height: "100%", position: "relative", children: [
1221
- /* @__PURE__ */ jsx26(MatrixBackground, { width, height, clearRect, clearRects: [installLinksClearRect] }),
1222
- /* @__PURE__ */ jsxs19("box", { position: "absolute", top: 0, left: 0, width, height, zIndex: 1, flexDirection: "column", shouldFill: false, children: [
1223
- /* @__PURE__ */ jsxs19("box", { flexGrow: 1, flexDirection: "column", paddingTop: 3, paddingLeft: 1, paddingRight: 1, paddingBottom: 1, gap: isMobile ? 0 : 1, shouldFill: false, children: [
1224
- /* @__PURE__ */ jsx26("box", { flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx26(Logo, { compact: isTiny, narrow: isNarrow, mobile: isMobile }) }),
1225
- /* @__PURE__ */ jsxs19("box", { flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: isMobile ? 0 : 1, flexShrink: 0, shouldFill: false, children: [
1226
- /* @__PURE__ */ jsx26("box", { border: true, borderStyle: "rounded", borderColor: theme.border, paddingX: 1, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsxs19("text", { children: [
1227
- /* @__PURE__ */ jsx26("span", { style: textStyle({ dim: true }), children: "$ " }),
1228
- /* @__PURE__ */ jsx26("span", { style: textStyle({ bold: true }), children: "bunx " }),
1229
- /* @__PURE__ */ jsx26("span", { style: textStyle({ fg: theme.accent }), children: "@gridland/demo landing" })
1230
- ] }) }),
1231
- /* @__PURE__ */ jsx26(InstallBox, {}),
1232
- /* @__PURE__ */ jsx26(LinksBox, {})
1233
- ] }),
1234
- /* @__PURE__ */ jsxs19("box", { flexGrow: 1, border: true, borderStyle: "rounded", borderColor: theme.border, flexDirection: "column", overflow: "hidden", children: [
1235
- /* @__PURE__ */ jsx26("box", { flexGrow: 1, flexDirection: "column", paddingX: 1, overflow: "hidden", children: messages.map((msg) => /* @__PURE__ */ jsx26(Message, { role: msg.role, children: /* @__PURE__ */ jsx26(Message.Content, { children: /* @__PURE__ */ jsx26(Message.Text, { children: msg.content }) }) }, msg.id)) }),
1236
- /* @__PURE__ */ jsx26("box", { flexShrink: 0, paddingX: 1, paddingBottom: 0, children: /* @__PURE__ */ jsx26(PromptInput, { splaceholder: "Ask about Gridland...", status: chatStatus, onSubmit: handleChatSubmit, useKeyboard }) })
1237
- ] })
1111
+ const { clearRect, installLinksClearRect } = useMemo7(() => {
1112
+ const logoHeight = isTiny ? 2 : isNarrow ? 13 : 7;
1113
+ const logoExtra = isBrowser ? 1 : 0;
1114
+ const gap = isMobile ? 0 : 1;
1115
+ const installLinksTop = 3 + logoHeight + logoExtra + gap;
1116
+ const installLinksHeight = 3;
1117
+ const boxTop = installLinksTop + installLinksHeight + gap + 1;
1118
+ const bh = height - boxTop - 1;
1119
+ return {
1120
+ clearRect: { top: boxTop, left: 1, width: width - 2, height: bh },
1121
+ installLinksClearRect: { top: installLinksTop, left: 1, width: width - 2, height: installLinksHeight }
1122
+ };
1123
+ }, [width, height, isTiny, isNarrow, isMobile, isBrowser]);
1124
+ return /* @__PURE__ */ jsxs18("box", { width: "100%", height: "100%", position: "relative", children: [
1125
+ /* @__PURE__ */ jsx25(MatrixBackground, { width, height, clearRect, clearRects: isBrowser ? void 0 : [installLinksClearRect] }),
1126
+ /* @__PURE__ */ jsx25("box", { position: "absolute", top: 0, left: 0, width, height, zIndex: 1, flexDirection: "column", shouldFill: false, children: /* @__PURE__ */ jsxs18("box", { flexGrow: 1, flexDirection: "column", paddingTop: 3, paddingLeft: 1, paddingRight: 1, paddingBottom: 1, gap: isMobile ? 0 : 1, shouldFill: false, children: [
1127
+ /* @__PURE__ */ jsx25("box", { flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx25(Logo, { compact: isTiny, narrow: isNarrow, mobile: isMobile }) }),
1128
+ /* @__PURE__ */ jsxs18("box", { flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: isMobile ? 0 : 1, flexShrink: 0, shouldFill: false, children: [
1129
+ /* @__PURE__ */ jsx25("box", { border: true, borderStyle: "rounded", borderColor: theme.border, paddingX: 1, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsxs18("text", { children: [
1130
+ /* @__PURE__ */ jsx25("span", { style: textStyle({ dim: true }), children: "$ " }),
1131
+ /* @__PURE__ */ jsx25("span", { style: textStyle({ bold: true }), children: "bunx " }),
1132
+ /* @__PURE__ */ jsx25("span", { style: textStyle({ fg: theme.accent }), children: "@gridland/demo landing" })
1133
+ ] }) }),
1134
+ /* @__PURE__ */ jsx25(InstallBox, {}),
1135
+ /* @__PURE__ */ jsx25(LinksBox, {})
1238
1136
  ] }),
1239
- /* @__PURE__ */ jsx26(StatusBar, { items: [{ key: "a", label: "about" }] })
1240
- ] })
1137
+ /* @__PURE__ */ jsx25("box", { flexGrow: 1, border: true, borderStyle: "rounded", borderColor: theme.border, flexDirection: "column", overflow: "hidden" })
1138
+ ] }) })
1241
1139
  ] });
1242
1140
  }
1243
1141
 
1244
- // ../docs/components/landing/matrix-rain.tsx
1245
- import { jsx as jsx27 } from "react/jsx-runtime";
1142
+ // src/landing/matrix-rain.tsx
1143
+ import { jsx as jsx26 } from "react/jsx-runtime";
1144
+
1145
+ // src/landing/about-modal.tsx
1146
+ import { jsx as jsx27, jsxs as jsxs19 } from "react/jsx-runtime";
1246
1147
  export {
1247
1148
  LandingApp
1248
1149
  };