@gridland/demo 0.2.49 → 0.2.50

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.
@@ -1 +1 @@
1
- ["gradient","ascii","table","spinner","select-input","multi-select","prompt-input","text-input","link","tabs","status-bar","modal","primitives","chat","timeline","message","terminal-window","landing"]
1
+ ["gradient","ascii","table","spinner","select-input","multi-select","prompt-input","text-input","link","tabs","status-bar","modal","primitives","chat","chain-of-thought","message","terminal-window","landing"]
package/dist/landing.js CHANGED
@@ -563,13 +563,19 @@ PromptInput.Model = PromptInputModel;
563
563
  // ../ui/components/chat/chat.tsx
564
564
  import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
565
565
 
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";
566
+ // ../ui/components/chain-of-thought/chain-of-thought.tsx
567
+ import { createContext as createContext5, memo, useContext as useContext5, useEffect as useEffect2, useMemo as useMemo4, useState as useState7 } from "react";
568
+ import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
569
569
  var DOTS = ["\u25CB", "\u25D4", "\u25D1", "\u25D5", "\u25CF"];
570
- function getStepDot(status) {
571
- return status === "pending" ? "\u25CB" : "\u25CF";
572
- }
570
+ var SPINNER_INTERVAL = 150;
571
+ var ChainOfThoughtContext = createContext5(null);
572
+ var useChainOfThought = () => {
573
+ const context = useContext5(ChainOfThoughtContext);
574
+ if (!context) {
575
+ throw new Error("ChainOfThought components must be used within <ChainOfThought>");
576
+ }
577
+ return context;
578
+ };
573
579
  function getStepColor(status, theme) {
574
580
  switch (status) {
575
581
  case "done":
@@ -584,78 +590,89 @@ function getStepColor(status, theme) {
584
590
  return theme.muted;
585
591
  }
586
592
  }
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";
593
+ var ChainOfThought = memo(({
594
+ open,
595
+ defaultOpen = false,
596
+ onOpenChange,
597
+ children
598
+ }) => {
599
+ const [internalOpen, setInternalOpen] = useState7(defaultOpen);
600
+ const isOpen = open ?? internalOpen;
601
+ const setIsOpen = onOpenChange ?? setInternalOpen;
602
+ const context = useMemo4(
603
+ () => ({ isOpen, setIsOpen }),
604
+ [isOpen, setIsOpen]
605
+ );
606
+ return /* @__PURE__ */ jsx18(ChainOfThoughtContext.Provider, { value: context, children: /* @__PURE__ */ jsx18("box", { flexDirection: "column", children }) });
607
+ });
608
+ var ChainOfThoughtHeader = memo(({
609
+ duration,
610
+ children = "Thought for"
611
+ }) => {
612
+ const theme = useTheme();
613
+ const { isOpen } = useChainOfThought();
614
+ const arrow = isOpen ? "\u25BC" : "\u25B6";
615
+ return /* @__PURE__ */ jsxs12("text", { children: [
616
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.muted }), children: arrow }),
617
+ /* @__PURE__ */ jsxs12("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
618
+ " ",
619
+ children,
620
+ duration ? " " + duration : ""
621
+ ] })
622
+ ] });
623
+ });
624
+ var ChainOfThoughtContent = memo(({ children }) => {
625
+ const { isOpen } = useChainOfThought();
626
+ if (!isOpen) return null;
627
+ return /* @__PURE__ */ jsx18(Fragment6, { children });
628
+ });
629
+ var ChainOfThoughtStep = memo(({
630
+ label,
631
+ description,
632
+ status = "done",
633
+ isLast = false,
634
+ children
635
+ }) => {
636
+ const theme = useTheme();
637
+ const isActive = status === "running";
638
+ const isPending = status === "pending";
639
+ const color = getStepColor(status, theme);
591
640
  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) : "";
641
+ const [frame, setFrame] = useState7(0);
642
+ useEffect2(() => {
643
+ if (!isActive) {
644
+ setFrame(0);
645
+ return;
646
+ }
647
+ const id = setInterval(() => setFrame((f) => f + 1), SPINNER_INTERVAL);
648
+ return () => clearInterval(id);
649
+ }, [isActive]);
650
+ const dot = isActive ? DOTS[frame % DOTS.length] : isPending ? "\u25CB" : "\u25CF";
596
651
  return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", marginLeft: 1, children: [
597
652
  /* @__PURE__ */ jsxs12("text", { children: [
598
653
  /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color }), children: dot }),
599
654
  /* @__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 })
655
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: isPending ? theme.muted : color, dim: isPending, bold: isActive }), children: label }),
656
+ description && /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " \u2014 " + description })
602
657
  ] }),
603
- step.output && /* @__PURE__ */ jsxs12("text", { children: [
658
+ children && /* @__PURE__ */ jsxs12("text", { children: [
604
659
  /* @__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 })
660
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: status === "error" ? theme.error : theme.accent }), children })
606
661
  ] }),
607
662
  !isLast && /* @__PURE__ */ jsx18("text", { children: /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color, dim: true }), children: pipe }) })
608
663
  ] });
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
- }
664
+ });
665
+ ChainOfThought.displayName = "ChainOfThought";
666
+ ChainOfThoughtHeader.displayName = "ChainOfThoughtHeader";
667
+ ChainOfThoughtContent.displayName = "ChainOfThoughtContent";
668
+ ChainOfThoughtStep.displayName = "ChainOfThoughtStep";
652
669
 
653
670
  // ../ui/components/message/message.tsx
654
- import { createContext as createContext5, useContext as useContext5 } from "react";
671
+ import { createContext as createContext6, useContext as useContext6 } from "react";
655
672
  import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
656
- var MessageContext = createContext5(null);
673
+ var MessageContext = createContext6(null);
657
674
  function useMessage() {
658
- const ctx = useContext5(MessageContext);
675
+ const ctx = useContext6(MessageContext);
659
676
  if (!ctx) throw new Error("useMessage must be used within <Message>");
660
677
  return ctx;
661
678
  }
@@ -706,14 +723,20 @@ function MessageText({ children, isLast = false }) {
706
723
  ] });
707
724
  }
708
725
  function MessageReasoning({ part }) {
709
- return /* @__PURE__ */ jsx19(
710
- Timeline,
711
- {
712
- steps: part.steps,
713
- duration: part.duration,
714
- collapsed: part.collapsed
715
- }
716
- );
726
+ return /* @__PURE__ */ jsxs13(ChainOfThought, { defaultOpen: part.collapsed === false, children: [
727
+ /* @__PURE__ */ jsx19(ChainOfThoughtHeader, { duration: part.duration }),
728
+ /* @__PURE__ */ jsx19(ChainOfThoughtContent, { children: part.steps?.map((step, i) => /* @__PURE__ */ jsx19(
729
+ ChainOfThoughtStep,
730
+ {
731
+ label: step.label,
732
+ description: step.description,
733
+ status: step.status,
734
+ isLast: i === (part.steps?.length ?? 0) - 1,
735
+ children: step.output
736
+ },
737
+ i
738
+ )) })
739
+ ] });
717
740
  }
718
741
  function MessageToolInvocation({ part, toolColors }) {
719
742
  const theme = useTheme();
@@ -806,7 +829,7 @@ function useBreakpoints() {
806
829
  }
807
830
 
808
831
  // ../docs/components/landing/landing-app.tsx
809
- import { useCallback as useCallback3, useRef as useRef8, useState as useState10 } from "react";
832
+ import { useCallback as useCallback3, useRef as useRef7, useState as useState10 } from "react";
810
833
 
811
834
  // ../docs/components/landing/about-modal.tsx
812
835
  import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
@@ -906,10 +929,10 @@ function LinksBox() {
906
929
  }
907
930
 
908
931
  // ../docs/components/landing/logo.tsx
909
- import { useState as useState8, useEffect as useEffect3, useRef as useRef6, useMemo as useMemo4 } from "react";
932
+ import { useState as useState8, useEffect as useEffect3, useRef as useRef5, useMemo as useMemo5 } from "react";
910
933
  import figlet from "figlet";
911
934
  import ansiShadow from "figlet/importable-fonts/ANSI Shadow.js";
912
- import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
935
+ import { Fragment as Fragment7, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
913
936
  figlet.parseFont("ANSI Shadow", ansiShadow);
914
937
  function makeArt(text) {
915
938
  return figlet.textSync(text, { font: "ANSI Shadow" }).split("\n").filter((l) => l.trimEnd().length > 0).join("\n");
@@ -921,7 +944,7 @@ var ART_HEIGHT = 6;
921
944
  function useAnimation(duration = 1e3) {
922
945
  const isBrowser = typeof document !== "undefined";
923
946
  const [progress, setProgress] = useState8(isBrowser ? 0 : 1);
924
- const startTime = useRef6(null);
947
+ const startTime = useRef5(null);
925
948
  useEffect3(() => {
926
949
  if (!isBrowser) return;
927
950
  let raf;
@@ -943,7 +966,7 @@ function RevealGradient({ children, revealCol }) {
943
966
  const lines = children.split("\n");
944
967
  const maxLength = Math.max(...lines.map((l) => l.length));
945
968
  if (maxLength === 0) return /* @__PURE__ */ jsx24("text", { children });
946
- const hexColors = useMemo4(() => generateGradient(gradientColors, maxLength), [maxLength]);
969
+ const hexColors = useMemo5(() => generateGradient(gradientColors, maxLength), [maxLength]);
947
970
  return /* @__PURE__ */ jsx24("box", { position: "relative", width: maxLength, height: lines.length, shouldFill: false, children: lines.map((line, lineIndex) => {
948
971
  const runs = [];
949
972
  let current = null;
@@ -993,7 +1016,7 @@ function Logo({ compact, narrow, mobile }) {
993
1016
  const maxWidth = compact ? 8 : narrow ? 40 : 62;
994
1017
  const revealCol = Math.round(revealProgress * (maxWidth + 4)) - 2;
995
1018
  const taglineOpacity = Math.max(0, Math.min(1, (progress - 0.7) / 0.3));
996
- const subtitle = /* @__PURE__ */ jsxs18(Fragment6, { children: [
1019
+ const subtitle = /* @__PURE__ */ jsxs18(Fragment7, { children: [
997
1020
  /* @__PURE__ */ jsx24("text", { children: " " }),
998
1021
  /* @__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: [
999
1022
  "A framework for building terminal apps, built on ",
@@ -1035,10 +1058,10 @@ function Logo({ compact, narrow, mobile }) {
1035
1058
  }
1036
1059
 
1037
1060
  // ../docs/components/landing/matrix-background.tsx
1038
- import { useMemo as useMemo5 } from "react";
1061
+ import { useMemo as useMemo6 } from "react";
1039
1062
 
1040
1063
  // ../docs/components/landing/use-matrix.ts
1041
- import { useState as useState9, useEffect as useEffect4, useRef as useRef7 } from "react";
1064
+ import { useState as useState9, useEffect as useEffect4, useRef as useRef6 } from "react";
1042
1065
  var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789@#$%^&*(){}[]|;:<>,.?/~`";
1043
1066
  function randomChar() {
1044
1067
  return CHARS[Math.floor(Math.random() * CHARS.length)];
@@ -1072,7 +1095,7 @@ function buildGrid(columns, width, height) {
1072
1095
  return { grid, brightness };
1073
1096
  }
1074
1097
  function useMatrix(width, height) {
1075
- const columnsRef = useRef7([]);
1098
+ const columnsRef = useRef6([]);
1076
1099
  const [state, setState] = useState9(() => {
1077
1100
  const columns = Array.from(
1078
1101
  { length: width },
@@ -1136,11 +1159,11 @@ function colorForCell(mutedColors, b) {
1136
1159
  function MatrixBackground({ width, height, clearRect, clearRects }) {
1137
1160
  const { grid, brightness } = useMatrix(width, height);
1138
1161
  const theme = useTheme();
1139
- const columnColors = useMemo5(
1162
+ const columnColors = useMemo6(
1140
1163
  () => width > 0 ? generateGradient([theme.accent, theme.secondary, theme.primary], width) : [],
1141
1164
  [width, theme.accent, theme.secondary, theme.primary]
1142
1165
  );
1143
- const columnMutedColors = useMemo5(
1166
+ const columnMutedColors = useMemo6(
1144
1167
  () => columnColors.map(buildMutedColors),
1145
1168
  [columnColors]
1146
1169
  );
@@ -1180,7 +1203,7 @@ function LandingApp({ useKeyboard }) {
1180
1203
  const [showAbout, setShowAbout] = useState10(false);
1181
1204
  const [messages, setMessages] = useState10([]);
1182
1205
  const [chatStatus, setChatStatus] = useState10("ready");
1183
- const responseIdx = useRef8(0);
1206
+ const responseIdx = useRef7(0);
1184
1207
  const handleChatSubmit = useCallback3(({ text }) => {
1185
1208
  const userMsg = { id: `u-${Date.now()}`, role: "user", content: text };
1186
1209
  setMessages((prev) => [...prev, userMsg]);
package/dist/run.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { createCliRenderer, createRoot, useKeyboard as useKeyboard2 } from "@gridland/bun";
3
3
 
4
4
  // ../ui/scripts/demo-apps.tsx
5
- import { useState as useState11, useCallback as useCallback4, useRef as useRef9, useEffect as useEffect5 } from "react";
5
+ import { useState as useState11, useCallback as useCallback4, useRef as useRef8, useEffect as useEffect5 } from "react";
6
6
  import { useKeyboard } from "@gridland/utils";
7
7
 
8
8
  // ../ui/components/theme/themes.ts
@@ -1443,13 +1443,19 @@ function ChatPanel({
1443
1443
  ] });
1444
1444
  }
1445
1445
 
1446
- // ../ui/components/timeline/timeline.tsx
1447
- import { useState as useState7, useEffect as useEffect2, useRef as useRef5 } from "react";
1448
- import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1446
+ // ../ui/components/chain-of-thought/chain-of-thought.tsx
1447
+ import { createContext as createContext5, memo, useContext as useContext5, useEffect as useEffect2, useMemo as useMemo4, useState as useState7 } from "react";
1448
+ import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1449
1449
  var DOTS = ["\u25CB", "\u25D4", "\u25D1", "\u25D5", "\u25CF"];
1450
- function getStepDot(status) {
1451
- return status === "pending" ? "\u25CB" : "\u25CF";
1452
- }
1450
+ var SPINNER_INTERVAL = 150;
1451
+ var ChainOfThoughtContext = createContext5(null);
1452
+ var useChainOfThought = () => {
1453
+ const context = useContext5(ChainOfThoughtContext);
1454
+ if (!context) {
1455
+ throw new Error("ChainOfThought components must be used within <ChainOfThought>");
1456
+ }
1457
+ return context;
1458
+ };
1453
1459
  function getStepColor(status, theme) {
1454
1460
  switch (status) {
1455
1461
  case "done":
@@ -1464,78 +1470,89 @@ function getStepColor(status, theme) {
1464
1470
  return theme.muted;
1465
1471
  }
1466
1472
  }
1467
- function StepRow({ step, isLast, theme, frame }) {
1468
- const color = getStepColor(step.status, theme);
1469
- const isActive = step.status === "running";
1470
- const isPending = step.status === "pending";
1473
+ var ChainOfThought = memo(({
1474
+ open,
1475
+ defaultOpen = false,
1476
+ onOpenChange,
1477
+ children
1478
+ }) => {
1479
+ const [internalOpen, setInternalOpen] = useState7(defaultOpen);
1480
+ const isOpen = open ?? internalOpen;
1481
+ const setIsOpen = onOpenChange ?? setInternalOpen;
1482
+ const context = useMemo4(
1483
+ () => ({ isOpen, setIsOpen }),
1484
+ [isOpen, setIsOpen]
1485
+ );
1486
+ return /* @__PURE__ */ jsx18(ChainOfThoughtContext.Provider, { value: context, children: /* @__PURE__ */ jsx18("box", { flexDirection: "column", children }) });
1487
+ });
1488
+ var ChainOfThoughtHeader = memo(({
1489
+ duration,
1490
+ children = "Thought for"
1491
+ }) => {
1492
+ const theme = useTheme();
1493
+ const { isOpen } = useChainOfThought();
1494
+ const arrow = isOpen ? "\u25BC" : "\u25B6";
1495
+ return /* @__PURE__ */ jsxs12("text", { children: [
1496
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.muted }), children: arrow }),
1497
+ /* @__PURE__ */ jsxs12("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
1498
+ " ",
1499
+ children,
1500
+ duration ? " " + duration : ""
1501
+ ] })
1502
+ ] });
1503
+ });
1504
+ var ChainOfThoughtContent = memo(({ children }) => {
1505
+ const { isOpen } = useChainOfThought();
1506
+ if (!isOpen) return null;
1507
+ return /* @__PURE__ */ jsx18(Fragment6, { children });
1508
+ });
1509
+ var ChainOfThoughtStep = memo(({
1510
+ label,
1511
+ description,
1512
+ status = "done",
1513
+ isLast = false,
1514
+ children
1515
+ }) => {
1516
+ const theme = useTheme();
1517
+ const isActive = status === "running";
1518
+ const isPending = status === "pending";
1519
+ const color = getStepColor(status, theme);
1471
1520
  const pipe = "\u2502";
1472
- const dot = isActive ? DOTS[frame % DOTS.length] : getStepDot(step.status);
1473
- const dashIdx = step.label.indexOf(" \u2014 ");
1474
- const mainLabel = dashIdx >= 0 ? step.label.slice(0, dashIdx) : step.label;
1475
- const detail = dashIdx >= 0 ? step.label.slice(dashIdx) : "";
1521
+ const [frame, setFrame] = useState7(0);
1522
+ useEffect2(() => {
1523
+ if (!isActive) {
1524
+ setFrame(0);
1525
+ return;
1526
+ }
1527
+ const id = setInterval(() => setFrame((f) => f + 1), SPINNER_INTERVAL);
1528
+ return () => clearInterval(id);
1529
+ }, [isActive]);
1530
+ const dot = isActive ? DOTS[frame % DOTS.length] : isPending ? "\u25CB" : "\u25CF";
1476
1531
  return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", marginLeft: 1, children: [
1477
1532
  /* @__PURE__ */ jsxs12("text", { children: [
1478
1533
  /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color }), children: dot }),
1479
1534
  /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.foreground }), children: " " }),
1480
- /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: isPending ? theme.muted : color, dim: isPending, bold: isActive }), children: mainLabel }),
1481
- detail && /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: detail })
1535
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: isPending ? theme.muted : color, dim: isPending, bold: isActive }), children: label }),
1536
+ description && /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " \u2014 " + description })
1482
1537
  ] }),
1483
- step.output && /* @__PURE__ */ jsxs12("text", { children: [
1538
+ children && /* @__PURE__ */ jsxs12("text", { children: [
1484
1539
  /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color, dim: true }), children: pipe + " " }),
1485
- /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: step.status === "error" ? theme.error : theme.accent }), children: step.output })
1540
+ /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: status === "error" ? theme.error : theme.accent }), children })
1486
1541
  ] }),
1487
1542
  !isLast && /* @__PURE__ */ jsx18("text", { children: /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color, dim: true }), children: pipe }) })
1488
1543
  ] });
1489
- }
1490
- function Timeline({
1491
- steps,
1492
- duration,
1493
- collapsed = true,
1494
- headerLabel = "Thought for"
1495
- }) {
1496
- const theme = useTheme();
1497
- const arrow = collapsed ? "\u25B6" : "\u25BC";
1498
- const durationStr = duration ?? "0ms";
1499
- const hasRunning = steps?.some((s) => s.status === "running") ?? false;
1500
- const [frame, setFrame] = useState7(0);
1501
- const alive = useRef5(true);
1502
- useEffect2(() => {
1503
- alive.current = true;
1504
- return () => {
1505
- alive.current = false;
1506
- };
1507
- }, []);
1508
- useEffect2(() => {
1509
- if (!hasRunning) return;
1510
- const id = setInterval(() => {
1511
- if (alive.current) setFrame((f) => f + 1);
1512
- }, 150);
1513
- return () => clearInterval(id);
1514
- }, [hasRunning]);
1515
- return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", children: [
1516
- /* @__PURE__ */ jsxs12("text", { children: [
1517
- /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.muted }), children: arrow }),
1518
- /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " " + headerLabel + " " + durationStr })
1519
- ] }),
1520
- !collapsed && steps && steps.map((step, i) => /* @__PURE__ */ jsx18(
1521
- StepRow,
1522
- {
1523
- step,
1524
- isLast: i === steps.length - 1,
1525
- theme,
1526
- frame
1527
- },
1528
- `step-${i}`
1529
- ))
1530
- ] });
1531
- }
1544
+ });
1545
+ ChainOfThought.displayName = "ChainOfThought";
1546
+ ChainOfThoughtHeader.displayName = "ChainOfThoughtHeader";
1547
+ ChainOfThoughtContent.displayName = "ChainOfThoughtContent";
1548
+ ChainOfThoughtStep.displayName = "ChainOfThoughtStep";
1532
1549
 
1533
1550
  // ../ui/components/message/message.tsx
1534
- import { createContext as createContext5, useContext as useContext5 } from "react";
1551
+ import { createContext as createContext6, useContext as useContext6 } from "react";
1535
1552
  import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
1536
- var MessageContext = createContext5(null);
1553
+ var MessageContext = createContext6(null);
1537
1554
  function useMessage() {
1538
- const ctx = useContext5(MessageContext);
1555
+ const ctx = useContext6(MessageContext);
1539
1556
  if (!ctx) throw new Error("useMessage must be used within <Message>");
1540
1557
  return ctx;
1541
1558
  }
@@ -1586,14 +1603,20 @@ function MessageText({ children, isLast = false }) {
1586
1603
  ] });
1587
1604
  }
1588
1605
  function MessageReasoning({ part }) {
1589
- return /* @__PURE__ */ jsx19(
1590
- Timeline,
1591
- {
1592
- steps: part.steps,
1593
- duration: part.duration,
1594
- collapsed: part.collapsed
1595
- }
1596
- );
1606
+ return /* @__PURE__ */ jsxs13(ChainOfThought, { defaultOpen: part.collapsed === false, children: [
1607
+ /* @__PURE__ */ jsx19(ChainOfThoughtHeader, { duration: part.duration }),
1608
+ /* @__PURE__ */ jsx19(ChainOfThoughtContent, { children: part.steps?.map((step, i) => /* @__PURE__ */ jsx19(
1609
+ ChainOfThoughtStep,
1610
+ {
1611
+ label: step.label,
1612
+ description: step.description,
1613
+ status: step.status,
1614
+ isLast: i === (part.steps?.length ?? 0) - 1,
1615
+ children: step.output
1616
+ },
1617
+ i
1618
+ )) })
1619
+ ] });
1597
1620
  }
1598
1621
  function MessageToolInvocation({ part, toolColors }) {
1599
1622
  const theme = useTheme();
@@ -1686,7 +1709,7 @@ function useBreakpoints() {
1686
1709
  }
1687
1710
 
1688
1711
  // ../docs/components/landing/landing-app.tsx
1689
- import { useCallback as useCallback3, useRef as useRef8, useState as useState10 } from "react";
1712
+ import { useCallback as useCallback3, useRef as useRef7, useState as useState10 } from "react";
1690
1713
 
1691
1714
  // ../docs/components/landing/about-modal.tsx
1692
1715
  import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
@@ -1786,10 +1809,10 @@ function LinksBox() {
1786
1809
  }
1787
1810
 
1788
1811
  // ../docs/components/landing/logo.tsx
1789
- import { useState as useState8, useEffect as useEffect3, useRef as useRef6, useMemo as useMemo4 } from "react";
1812
+ import { useState as useState8, useEffect as useEffect3, useRef as useRef5, useMemo as useMemo5 } from "react";
1790
1813
  import figlet from "figlet";
1791
1814
  import ansiShadow from "figlet/importable-fonts/ANSI Shadow.js";
1792
- import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
1815
+ import { Fragment as Fragment7, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
1793
1816
  figlet.parseFont("ANSI Shadow", ansiShadow);
1794
1817
  function makeArt(text) {
1795
1818
  return figlet.textSync(text, { font: "ANSI Shadow" }).split("\n").filter((l) => l.trimEnd().length > 0).join("\n");
@@ -1801,7 +1824,7 @@ var ART_HEIGHT = 6;
1801
1824
  function useAnimation(duration = 1e3) {
1802
1825
  const isBrowser = typeof document !== "undefined";
1803
1826
  const [progress, setProgress] = useState8(isBrowser ? 0 : 1);
1804
- const startTime = useRef6(null);
1827
+ const startTime = useRef5(null);
1805
1828
  useEffect3(() => {
1806
1829
  if (!isBrowser) return;
1807
1830
  let raf;
@@ -1823,7 +1846,7 @@ function RevealGradient({ children, revealCol }) {
1823
1846
  const lines = children.split("\n");
1824
1847
  const maxLength = Math.max(...lines.map((l) => l.length));
1825
1848
  if (maxLength === 0) return /* @__PURE__ */ jsx24("text", { children });
1826
- const hexColors = useMemo4(() => generateGradient(gradientColors, maxLength), [maxLength]);
1849
+ const hexColors = useMemo5(() => generateGradient(gradientColors, maxLength), [maxLength]);
1827
1850
  return /* @__PURE__ */ jsx24("box", { position: "relative", width: maxLength, height: lines.length, shouldFill: false, children: lines.map((line, lineIndex) => {
1828
1851
  const runs = [];
1829
1852
  let current = null;
@@ -1873,7 +1896,7 @@ function Logo({ compact, narrow, mobile }) {
1873
1896
  const maxWidth = compact ? 8 : narrow ? 40 : 62;
1874
1897
  const revealCol = Math.round(revealProgress * (maxWidth + 4)) - 2;
1875
1898
  const taglineOpacity = Math.max(0, Math.min(1, (progress - 0.7) / 0.3));
1876
- const subtitle = /* @__PURE__ */ jsxs18(Fragment6, { children: [
1899
+ const subtitle = /* @__PURE__ */ jsxs18(Fragment7, { children: [
1877
1900
  /* @__PURE__ */ jsx24("text", { children: " " }),
1878
1901
  /* @__PURE__ */ jsx24("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs18("text", { style: textStyle({ fg: "#d4b0e8" }), opacity: taglineOpacity, wrapMode: "word", textAlign: "center", width: "100%", shouldFill: false, children: [
1879
1902
  "A framework for building terminal apps, built on ",
@@ -1915,10 +1938,10 @@ function Logo({ compact, narrow, mobile }) {
1915
1938
  }
1916
1939
 
1917
1940
  // ../docs/components/landing/matrix-background.tsx
1918
- import { useMemo as useMemo5 } from "react";
1941
+ import { useMemo as useMemo6 } from "react";
1919
1942
 
1920
1943
  // ../docs/components/landing/use-matrix.ts
1921
- import { useState as useState9, useEffect as useEffect4, useRef as useRef7 } from "react";
1944
+ import { useState as useState9, useEffect as useEffect4, useRef as useRef6 } from "react";
1922
1945
  var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789@#$%^&*(){}[]|;:<>,.?/~`";
1923
1946
  function randomChar() {
1924
1947
  return CHARS[Math.floor(Math.random() * CHARS.length)];
@@ -1952,7 +1975,7 @@ function buildGrid(columns, width, height) {
1952
1975
  return { grid, brightness };
1953
1976
  }
1954
1977
  function useMatrix(width, height) {
1955
- const columnsRef = useRef7([]);
1978
+ const columnsRef = useRef6([]);
1956
1979
  const [state, setState] = useState9(() => {
1957
1980
  const columns = Array.from(
1958
1981
  { length: width },
@@ -2016,11 +2039,11 @@ function colorForCell(mutedColors, b) {
2016
2039
  function MatrixBackground({ width, height, clearRect, clearRects }) {
2017
2040
  const { grid, brightness } = useMatrix(width, height);
2018
2041
  const theme = useTheme();
2019
- const columnColors = useMemo5(
2042
+ const columnColors = useMemo6(
2020
2043
  () => width > 0 ? generateGradient([theme.accent, theme.secondary, theme.primary], width) : [],
2021
2044
  [width, theme.accent, theme.secondary, theme.primary]
2022
2045
  );
2023
- const columnMutedColors = useMemo5(
2046
+ const columnMutedColors = useMemo6(
2024
2047
  () => columnColors.map(buildMutedColors),
2025
2048
  [columnColors]
2026
2049
  );
@@ -2060,7 +2083,7 @@ function LandingApp({ useKeyboard: useKeyboard3 }) {
2060
2083
  const [showAbout, setShowAbout] = useState10(false);
2061
2084
  const [messages, setMessages] = useState10([]);
2062
2085
  const [chatStatus, setChatStatus] = useState10("ready");
2063
- const responseIdx = useRef8(0);
2086
+ const responseIdx = useRef7(0);
2064
2087
  const handleChatSubmit = useCallback3(({ text }) => {
2065
2088
  const userMsg = { id: `u-${Date.now()}`, role: "user", content: text };
2066
2089
  setMessages((prev) => [...prev, userMsg]);
@@ -2471,31 +2494,31 @@ function TerminalWindowApp() {
2471
2494
  /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "quit" }] }) })
2472
2495
  ] });
2473
2496
  }
2474
- var TIMELINE_STEPS = [
2475
- { tool: "Read", label: "Reading codebase \u2014 src/", status: "done", delay: 1800 },
2476
- { tool: "Think", label: "Planning changes \u2014 auth module", status: "done", delay: 2500 },
2477
- { tool: "Edit", label: "Editing files \u2014 4 files", status: "done", delay: 3200 },
2478
- { tool: "Bash", label: "Running tests \u2014 vitest", status: "done", delay: 2e3 },
2479
- { tool: "Edit", label: "Fixing test \u2014 routes.test.ts", status: "done", delay: 1500 }
2497
+ var COT_STEPS = [
2498
+ { tool: "Read", label: "Reading codebase", description: "src/", status: "done", delay: 1800 },
2499
+ { tool: "Think", label: "Planning changes", description: "auth module", status: "done", delay: 2500 },
2500
+ { tool: "Edit", label: "Editing files", description: "4 files", status: "done", delay: 3200 },
2501
+ { tool: "Bash", label: "Running tests", description: "vitest", status: "done", delay: 2e3 },
2502
+ { tool: "Edit", label: "Fixing test", description: "routes.test.ts", status: "done", delay: 1500 }
2480
2503
  ];
2481
- function TimelineApp() {
2504
+ function ChainOfThoughtApp() {
2482
2505
  const [expanded, setExpanded] = useState11(true);
2483
2506
  const [phase, setPhase] = useState11("running");
2484
2507
  const [stepIndex, setStepIndex] = useState11(0);
2485
- const timerRef = useRef9(null);
2508
+ const timerRef = useRef8(null);
2486
2509
  useKeyboard((event) => {
2487
2510
  if (event.name === "E" && event.ctrl && event.shift) setExpanded((v) => !v);
2488
- if (event.name === "r") timelineRestart();
2511
+ if (event.name === "r") cotRestart();
2489
2512
  });
2490
- function timelineRestart() {
2513
+ function cotRestart() {
2491
2514
  if (timerRef.current) clearTimeout(timerRef.current);
2492
2515
  setPhase("running");
2493
2516
  setStepIndex(0);
2494
2517
  }
2495
2518
  useEffect5(() => {
2496
2519
  if (phase !== "running") return;
2497
- if (stepIndex < TIMELINE_STEPS.length) {
2498
- const delay = TIMELINE_STEPS[stepIndex].delay;
2520
+ if (stepIndex < COT_STEPS.length) {
2521
+ const delay = COT_STEPS[stepIndex].delay;
2499
2522
  timerRef.current = setTimeout(() => setStepIndex((i) => i + 1), delay);
2500
2523
  } else {
2501
2524
  timerRef.current = setTimeout(() => setPhase("done"), 500);
@@ -2506,28 +2529,35 @@ function TimelineApp() {
2506
2529
  }, [phase, stepIndex]);
2507
2530
  useEffect5(() => {
2508
2531
  if (phase === "done") {
2509
- timerRef.current = setTimeout(() => timelineRestart(), 3e3);
2532
+ timerRef.current = setTimeout(() => cotRestart(), 3e3);
2510
2533
  }
2511
2534
  return () => {
2512
2535
  if (timerRef.current) clearTimeout(timerRef.current);
2513
2536
  };
2514
2537
  }, [phase]);
2515
- const steps = TIMELINE_STEPS.map((s, i) => {
2538
+ const steps = COT_STEPS.map((s, i) => {
2516
2539
  if (i < stepIndex) return { ...s, status: "done" };
2517
2540
  if (i === stepIndex && phase === "running") return { ...s, status: "running" };
2518
2541
  return { ...s, status: phase === "done" ? "done" : "pending" };
2519
2542
  });
2520
- const elapsedMs = TIMELINE_STEPS.slice(0, stepIndex).reduce((sum, s) => sum + s.delay, 0);
2521
- const totalMs = TIMELINE_STEPS.reduce((sum, s) => sum + s.delay, 0);
2543
+ const elapsedMs = COT_STEPS.slice(0, stepIndex).reduce((sum, s) => sum + s.delay, 0);
2544
+ const totalMs = COT_STEPS.reduce((sum, s) => sum + s.delay, 0);
2545
+ const durationStr = phase === "done" ? `${(totalMs / 1e3).toFixed(1)}s` : `${(elapsedMs / 1e3).toFixed(1)}s`;
2522
2546
  return /* @__PURE__ */ jsxs20("box", { flexDirection: "column", flexGrow: 1, children: [
2523
- /* @__PURE__ */ jsx28("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsx28(
2524
- Timeline,
2525
- {
2526
- steps,
2527
- duration: phase === "done" ? `${(totalMs / 1e3).toFixed(1)}s` : `${(elapsedMs / 1e3).toFixed(1)}s`,
2528
- collapsed: !expanded
2529
- }
2530
- ) }),
2547
+ /* @__PURE__ */ jsx28("box", { flexDirection: "column", padding: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs20(ChainOfThought, { open: expanded, onOpenChange: setExpanded, children: [
2548
+ /* @__PURE__ */ jsx28(ChainOfThoughtHeader, { duration: durationStr }),
2549
+ /* @__PURE__ */ jsx28(ChainOfThoughtContent, { children: steps.map((step, i) => /* @__PURE__ */ jsx28(
2550
+ ChainOfThoughtStep,
2551
+ {
2552
+ label: step.label,
2553
+ description: step.description,
2554
+ status: step.status,
2555
+ isLast: i === steps.length - 1,
2556
+ children: step.output
2557
+ },
2558
+ i
2559
+ )) })
2560
+ ] }) }),
2531
2561
  /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [
2532
2562
  { key: "ctrl+shift+e", label: "toggle" },
2533
2563
  { key: "r", label: "restart" },
@@ -2536,11 +2566,11 @@ function TimelineApp() {
2536
2566
  ] });
2537
2567
  }
2538
2568
  var BUBBLE_STEPS = [
2539
- { tool: "Read", label: "Reading codebase \u2014 src/", status: "done", delay: 1800 },
2540
- { tool: "Think", label: "Planning changes \u2014 auth module", status: "done", delay: 2500 },
2541
- { tool: "Edit", label: "Editing files \u2014 4 files", status: "done", delay: 3200 },
2542
- { tool: "Bash", label: "Running tests \u2014 vitest", status: "done", delay: 2e3 },
2543
- { tool: "Edit", label: "Fixing test \u2014 routes.test.ts", status: "done", delay: 1500 }
2569
+ { tool: "Read", label: "Reading codebase", description: "src/", status: "done", delay: 1800 },
2570
+ { tool: "Think", label: "Planning changes", description: "auth module", status: "done", delay: 2500 },
2571
+ { tool: "Edit", label: "Editing files", description: "4 files", status: "done", delay: 3200 },
2572
+ { tool: "Bash", label: "Running tests", description: "vitest", status: "done", delay: 2e3 },
2573
+ { tool: "Edit", label: "Fixing test", description: "routes.test.ts", status: "done", delay: 1500 }
2544
2574
  ];
2545
2575
  var BUBBLE_RESPONSE = "I've refactored the auth module. The changes include extracting the token validation into a shared helper, consolidating the middleware chain, and updating the test suite to match.";
2546
2576
  var BUBBLE_TOTAL_MS = BUBBLE_STEPS.reduce((sum, s) => sum + s.delay, 0);
@@ -2549,7 +2579,7 @@ function MessageApp() {
2549
2579
  const [phase, setPhase] = useState11("idle");
2550
2580
  const [stepIndex, setStepIndex] = useState11(0);
2551
2581
  const [streamedText, setStreamedText] = useState11("");
2552
- const timerRef = useRef9(null);
2582
+ const timerRef = useRef8(null);
2553
2583
  useKeyboard((event) => {
2554
2584
  if (event.name === "E" && event.ctrl && event.shift) setExpanded((v) => !v);
2555
2585
  if (event.name === "r") bubbleRestart();
@@ -2626,7 +2656,7 @@ function MessageApp() {
2626
2656
  (isStreaming || isDone) && /* @__PURE__ */ jsx28(Message.Content, { children: /* @__PURE__ */ jsx28(Message.Text, { isLast: true, children: isDone ? BUBBLE_RESPONSE : streamedText }) })
2627
2657
  ] })
2628
2658
  ] }),
2629
- /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "ctrl+shift+e", label: "toggle timeline" }, { key: "r", label: "restart" }, { key: "q", label: "quit" }] }) })
2659
+ /* @__PURE__ */ jsx28("box", { paddingX: 1, paddingBottom: 1, children: /* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "ctrl+shift+e", label: "toggle chain of thought" }, { key: "r", label: "restart" }, { key: "q", label: "quit" }] }) })
2630
2660
  ] });
2631
2661
  }
2632
2662
  var initialChatMessages = [
@@ -2641,7 +2671,7 @@ function ChatApp() {
2641
2671
  const [isLoading, setIsLoading] = useState11(false);
2642
2672
  const [streamingText, setStreamingText] = useState11("");
2643
2673
  const [activeToolCalls, setActiveToolCalls] = useState11([]);
2644
- const intervalRef = useRef9(null);
2674
+ const intervalRef = useRef8(null);
2645
2675
  const handleSend = useCallback4((text) => {
2646
2676
  const userMsg = { id: String(chatNextId++), role: "user", content: text };
2647
2677
  setMessages((prev) => [...prev, userMsg]);
@@ -2711,7 +2741,7 @@ var demos = [
2711
2741
  { name: "modal", app: () => /* @__PURE__ */ jsx28(ModalApp, {}) },
2712
2742
  { name: "primitives", app: () => /* @__PURE__ */ jsx28(PrimitivesApp, {}) },
2713
2743
  { name: "chat", app: () => /* @__PURE__ */ jsx28(ChatApp, {}) },
2714
- { name: "timeline", app: () => /* @__PURE__ */ jsx28(TimelineApp, {}) },
2744
+ { name: "chain-of-thought", app: () => /* @__PURE__ */ jsx28(ChainOfThoughtApp, {}) },
2715
2745
  { name: "message", app: () => /* @__PURE__ */ jsx28(MessageApp, {}) },
2716
2746
  { name: "terminal-window", app: () => /* @__PURE__ */ jsx28(TerminalWindowApp, {}) },
2717
2747
  { name: "landing", app: () => /* @__PURE__ */ jsx28(LandingApp, { useKeyboard }) }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gridland/demo",
3
- "version": "0.2.49",
3
+ "version": "0.2.50",
4
4
  "type": "module",
5
5
  "description": "Run gridland component demos from anywhere",
6
6
  "exports": {
@@ -21,8 +21,8 @@
21
21
  "test": "bun test"
22
22
  },
23
23
  "dependencies": {
24
- "@gridland/bun": "0.2.49",
25
- "@gridland/utils": "0.2.49",
24
+ "@gridland/bun": "0.2.50",
25
+ "@gridland/utils": "0.2.50",
26
26
  "figlet": "^1.10.0",
27
27
  "react": "^19.0.0"
28
28
  },