@claude-code-kit/ui 0.1.0 → 0.2.0

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/index.mjs CHANGED
@@ -1,11 +1,138 @@
1
1
  // src/index.ts
2
2
  export * from "@claude-code-kit/ink-renderer";
3
3
 
4
+ // src/DiffView.tsx
5
+ import { useMemo } from "react";
6
+ import { Box, Text } from "@claude-code-kit/ink-renderer";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ function parseUnifiedDiff(diff) {
9
+ const rawLines = diff.split("\n");
10
+ let filename = "";
11
+ const lines = [];
12
+ let oldLine = 0;
13
+ let newLine = 0;
14
+ for (const line of rawLines) {
15
+ if (line.startsWith("+++ ")) {
16
+ const path = line.slice(4).trim();
17
+ filename = path.startsWith("b/") ? path.slice(2) : path;
18
+ continue;
19
+ }
20
+ if (line.startsWith("--- ")) continue;
21
+ const hunkMatch = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
22
+ if (hunkMatch) {
23
+ oldLine = parseInt(hunkMatch[1], 10);
24
+ newLine = parseInt(hunkMatch[2], 10);
25
+ continue;
26
+ }
27
+ if (line.startsWith("diff ") || line.startsWith("index ") || line.startsWith("\\")) {
28
+ continue;
29
+ }
30
+ if (oldLine === 0 && newLine === 0) continue;
31
+ if (line.startsWith("+")) {
32
+ lines.push({
33
+ type: "added",
34
+ content: line.slice(1),
35
+ newLineNumber: newLine
36
+ });
37
+ newLine++;
38
+ } else if (line.startsWith("-")) {
39
+ lines.push({
40
+ type: "removed",
41
+ content: line.slice(1),
42
+ oldLineNumber: oldLine
43
+ });
44
+ oldLine++;
45
+ } else {
46
+ lines.push({
47
+ type: "context",
48
+ content: line.startsWith(" ") ? line.slice(1) : line,
49
+ oldLineNumber: oldLine,
50
+ newLineNumber: newLine
51
+ });
52
+ oldLine++;
53
+ newLine++;
54
+ }
55
+ }
56
+ return { filename: filename || "unknown", lines };
57
+ }
58
+ function DiffView({
59
+ filename,
60
+ lines: propLines,
61
+ diff,
62
+ showLineNumbers = true,
63
+ maxHeight,
64
+ color: colorOverrides
65
+ }) {
66
+ const parsed = useMemo(() => {
67
+ if (diff) return parseUnifiedDiff(diff);
68
+ return null;
69
+ }, [diff]);
70
+ const resolvedFilename = parsed?.filename ?? filename;
71
+ const resolvedLines = parsed?.lines ?? propLines;
72
+ const addedColor = colorOverrides?.added ?? "green";
73
+ const removedColor = colorOverrides?.removed ?? "red";
74
+ const headerColor = colorOverrides?.header ?? "cyan";
75
+ const contextColor = colorOverrides?.context;
76
+ const maxLineNum = useMemo(() => {
77
+ let max = 0;
78
+ for (const line of resolvedLines) {
79
+ if (line.oldLineNumber !== void 0 && line.oldLineNumber > max) max = line.oldLineNumber;
80
+ if (line.newLineNumber !== void 0 && line.newLineNumber > max) max = line.newLineNumber;
81
+ }
82
+ return max;
83
+ }, [resolvedLines]);
84
+ const gutterWidth = Math.max(2, String(maxLineNum).length);
85
+ const visibleLines = maxHeight && resolvedLines.length > maxHeight ? resolvedLines.slice(0, maxHeight) : resolvedLines;
86
+ const truncated = maxHeight && resolvedLines.length > maxHeight ? resolvedLines.length - maxHeight : 0;
87
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
88
+ /* @__PURE__ */ jsxs(Text, { bold: true, color: headerColor, children: [
89
+ " ",
90
+ resolvedFilename
91
+ ] }),
92
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
93
+ " ",
94
+ "\u2500".repeat(30)
95
+ ] }),
96
+ visibleLines.map((line, i) => /* @__PURE__ */ jsx(
97
+ DiffLineRow,
98
+ {
99
+ line,
100
+ gutterWidth,
101
+ showLineNumbers,
102
+ addedColor,
103
+ removedColor,
104
+ contextColor
105
+ },
106
+ i
107
+ )),
108
+ truncated > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
109
+ " ... ",
110
+ truncated,
111
+ " more lines"
112
+ ] })
113
+ ] });
114
+ }
115
+ function DiffLineRow({ line, gutterWidth, showLineNumbers, addedColor, removedColor, contextColor }) {
116
+ const lineNum = line.type === "removed" ? line.oldLineNumber : line.newLineNumber ?? line.oldLineNumber;
117
+ const prefix = line.type === "added" ? "+ " : line.type === "removed" ? "- " : " ";
118
+ const gutterStr = showLineNumbers && lineNum !== void 0 ? String(lineNum).padStart(gutterWidth) : " ".repeat(gutterWidth);
119
+ const contentColor = line.type === "added" ? addedColor : line.type === "removed" ? removedColor : contextColor;
120
+ const dim = line.type === "context" && !contextColor;
121
+ return /* @__PURE__ */ jsxs(Text, { children: [
122
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: gutterStr }),
123
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
124
+ /* @__PURE__ */ jsxs(Text, { dimColor: dim, color: contentColor, children: [
125
+ prefix,
126
+ line.content
127
+ ] })
128
+ ] });
129
+ }
130
+
4
131
  // src/Divider.tsx
5
132
  import { useContext } from "react";
6
- import { Text, Ansi, TerminalSizeContext, stringWidth } from "@claude-code-kit/ink-renderer";
7
- import { jsx, jsxs } from "react/jsx-runtime";
8
- function Divider({ width, color, char = "\u2500", padding = 0, title }) {
133
+ import { Text as Text2, Ansi, TerminalSizeContext, stringWidth } from "@claude-code-kit/ink-renderer";
134
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
135
+ function Divider({ width, color: color2, char = "\u2500", padding = 0, title }) {
9
136
  const terminalSize = useContext(TerminalSizeContext);
10
137
  const terminalWidth = terminalSize?.columns ?? 80;
11
138
  const effectiveWidth = Math.max(0, (width ?? terminalWidth - 2) - padding);
@@ -14,20 +141,20 @@ function Divider({ width, color, char = "\u2500", padding = 0, title }) {
14
141
  const sideWidth = Math.max(0, effectiveWidth - titleWidth);
15
142
  const leftWidth = Math.floor(sideWidth / 2);
16
143
  const rightWidth = sideWidth - leftWidth;
17
- return /* @__PURE__ */ jsxs(Text, { color, dimColor: !color, children: [
144
+ return /* @__PURE__ */ jsxs2(Text2, { color: color2, dimColor: !color2, children: [
18
145
  char.repeat(leftWidth),
19
146
  " ",
20
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: /* @__PURE__ */ jsx(Ansi, { children: title }) }),
147
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: /* @__PURE__ */ jsx2(Ansi, { children: title }) }),
21
148
  " ",
22
149
  char.repeat(rightWidth)
23
150
  ] });
24
151
  }
25
- return /* @__PURE__ */ jsx(Text, { color, dimColor: !color, children: char.repeat(effectiveWidth) });
152
+ return /* @__PURE__ */ jsx2(Text2, { color: color2, dimColor: !color2, children: char.repeat(effectiveWidth) });
26
153
  }
27
154
 
28
155
  // src/ProgressBar.tsx
29
- import { Text as Text2 } from "@claude-code-kit/ink-renderer";
30
- import { jsx as jsx2 } from "react/jsx-runtime";
156
+ import { Text as Text3 } from "@claude-code-kit/ink-renderer";
157
+ import { jsx as jsx3 } from "react/jsx-runtime";
31
158
  var BLOCKS = [" ", "\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u258A", "\u2589", "\u2588"];
32
159
  function ProgressBar({ ratio: inputRatio, width, fillColor, emptyColor }) {
33
160
  const ratio = Math.min(1, Math.max(0, inputRatio));
@@ -42,13 +169,13 @@ function ProgressBar({ ratio: inputRatio, width, fillColor, emptyColor }) {
42
169
  segments.push(BLOCKS[0].repeat(empty));
43
170
  }
44
171
  }
45
- return /* @__PURE__ */ jsx2(Text2, { color: fillColor, backgroundColor: emptyColor, children: segments.join("") });
172
+ return /* @__PURE__ */ jsx3(Text3, { color: fillColor, backgroundColor: emptyColor, children: segments.join("") });
46
173
  }
47
174
 
48
175
  // src/StatusIcon.tsx
49
176
  import figures from "figures";
50
- import { Text as Text3 } from "@claude-code-kit/ink-renderer";
51
- import { jsxs as jsxs2 } from "react/jsx-runtime";
177
+ import { Text as Text4 } from "@claude-code-kit/ink-renderer";
178
+ import { jsxs as jsxs3 } from "react/jsx-runtime";
52
179
  var STATUS_CONFIG = {
53
180
  success: { icon: figures.tick, color: "green" },
54
181
  error: { icon: figures.cross, color: "red" },
@@ -59,16 +186,16 @@ var STATUS_CONFIG = {
59
186
  };
60
187
  function StatusIcon({ status, withSpace = false }) {
61
188
  const config = STATUS_CONFIG[status];
62
- return /* @__PURE__ */ jsxs2(Text3, { color: config.color, dimColor: !config.color, children: [
189
+ return /* @__PURE__ */ jsxs3(Text4, { color: config.color, dimColor: !config.color, children: [
63
190
  config.icon,
64
191
  withSpace && " "
65
192
  ] });
66
193
  }
67
194
 
68
195
  // src/StatusLine.tsx
69
- import { useEffect, useState } from "react";
70
- import { Box, Text as Text4, Ansi as Ansi2 } from "@claude-code-kit/ink-renderer";
71
- import { jsx as jsx3 } from "react/jsx-runtime";
196
+ import React3, { useEffect, useState } from "react";
197
+ import { Box as Box2, Text as Text5, Ansi as Ansi2 } from "@claude-code-kit/ink-renderer";
198
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
72
199
  function hasAnsi(s) {
73
200
  return /\x1b\[/.test(s);
74
201
  }
@@ -76,19 +203,22 @@ function StatusLine({
76
203
  segments,
77
204
  text,
78
205
  paddingX = 1,
79
- gap = 1,
206
+ separator = " \xB7 ",
80
207
  borderStyle = "none",
81
208
  borderColor
82
209
  }) {
83
210
  const border = borderStyle === "none" ? void 0 : borderStyle;
84
- return /* @__PURE__ */ jsx3(
85
- Box,
211
+ return /* @__PURE__ */ jsx4(
212
+ Box2,
86
213
  {
87
214
  flexDirection: "row",
88
215
  paddingX,
89
216
  borderStyle: border,
90
217
  borderColor,
91
- children: text !== void 0 ? hasAnsi(text) ? /* @__PURE__ */ jsx3(Ansi2, { children: text }) : /* @__PURE__ */ jsx3(Text4, { dimColor: true, children: text }) : segments?.map((seg, i) => /* @__PURE__ */ jsx3(Box, { flexGrow: seg.flex ? 1 : 0, marginRight: i < segments.length - 1 ? gap : 0, children: hasAnsi(seg.content) ? /* @__PURE__ */ jsx3(Ansi2, { children: seg.content }) : /* @__PURE__ */ jsx3(Text4, { dimColor: true, color: seg.color, children: seg.content }) }, i))
218
+ children: text !== void 0 ? hasAnsi(text) ? /* @__PURE__ */ jsx4(Ansi2, { children: text }) : /* @__PURE__ */ jsx4(Text5, { dimColor: true, children: text }) : segments?.map((seg, i) => /* @__PURE__ */ jsxs4(React3.Fragment, { children: [
219
+ i > 0 && /* @__PURE__ */ jsx4(Text5, { dimColor: true, children: separator }),
220
+ /* @__PURE__ */ jsx4(Box2, { flexGrow: seg.flex ? 1 : 0, children: hasAnsi(seg.content) ? /* @__PURE__ */ jsx4(Ansi2, { children: seg.content }) : /* @__PURE__ */ jsx4(Text5, { dimColor: true, color: seg.color, children: seg.content }) })
221
+ ] }, i))
92
222
  }
93
223
  );
94
224
  }
@@ -210,7 +340,7 @@ import {
210
340
  createContext,
211
341
  useContext as useContext2,
212
342
  useLayoutEffect,
213
- useMemo
343
+ useMemo as useMemo2
214
344
  } from "react";
215
345
 
216
346
  // src/keybindings/match.ts
@@ -365,10 +495,13 @@ function parseBindings(blocks) {
365
495
 
366
496
  // src/keybindings/resolver.ts
367
497
  function getBindingDisplayText(action, context, bindings) {
368
- const binding = bindings.findLast(
369
- (b) => b.action === action && b.context === context
370
- );
371
- return binding ? chordToString(binding.chord) : void 0;
498
+ for (let i = bindings.length - 1; i >= 0; i--) {
499
+ const binding = bindings[i];
500
+ if (binding && binding.action === action && binding.context === context) {
501
+ return chordToString(binding.chord);
502
+ }
503
+ }
504
+ return void 0;
372
505
  }
373
506
  function buildKeystroke(input, key) {
374
507
  const keyName = getKeyName(input, key);
@@ -455,7 +588,7 @@ function resolveKeyWithChordState(input, key, activeContexts, bindings, pending)
455
588
  }
456
589
 
457
590
  // src/keybindings/KeybindingContext.tsx
458
- import { jsx as jsx4 } from "react/jsx-runtime";
591
+ import { jsx as jsx5 } from "react/jsx-runtime";
459
592
  var KeybindingContext = createContext(null);
460
593
  function KeybindingProvider({
461
594
  bindings,
@@ -468,11 +601,11 @@ function KeybindingProvider({
468
601
  handlerRegistryRef,
469
602
  children
470
603
  }) {
471
- const getDisplayText = useMemo(
604
+ const getDisplayText = useMemo2(
472
605
  () => (action, context) => getBindingDisplayText(action, context, bindings),
473
606
  [bindings]
474
607
  );
475
- const registerHandler = useMemo(
608
+ const registerHandler = useMemo2(
476
609
  () => (registration) => {
477
610
  const registry = handlerRegistryRef.current;
478
611
  if (!registry) return () => {
@@ -493,7 +626,7 @@ function KeybindingProvider({
493
626
  },
494
627
  [handlerRegistryRef]
495
628
  );
496
- const invokeAction = useMemo(
629
+ const invokeAction = useMemo2(
497
630
  () => (action) => {
498
631
  const registry = handlerRegistryRef.current;
499
632
  if (!registry) return false;
@@ -509,7 +642,7 @@ function KeybindingProvider({
509
642
  },
510
643
  [activeContexts, handlerRegistryRef]
511
644
  );
512
- const resolve = useMemo(
645
+ const resolve = useMemo2(
513
646
  () => (input, key, contexts) => resolveKeyWithChordState(
514
647
  input,
515
648
  key,
@@ -519,7 +652,7 @@ function KeybindingProvider({
519
652
  ),
520
653
  [bindings, pendingChordRef]
521
654
  );
522
- const value = useMemo(
655
+ const value = useMemo2(
523
656
  () => ({
524
657
  resolve,
525
658
  setPendingChord,
@@ -545,7 +678,7 @@ function KeybindingProvider({
545
678
  invokeAction
546
679
  ]
547
680
  );
548
- return /* @__PURE__ */ jsx4(KeybindingContext.Provider, { value, children });
681
+ return /* @__PURE__ */ jsx5(KeybindingContext.Provider, { value, children });
549
682
  }
550
683
  function useOptionalKeybindingContext() {
551
684
  return useContext2(KeybindingContext);
@@ -1524,7 +1657,7 @@ function handleDelete(path) {
1524
1657
  }
1525
1658
 
1526
1659
  // src/keybindings/KeybindingProviderSetup.tsx
1527
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1660
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1528
1661
  var plural = (n, s) => n === 1 ? s : s + "s";
1529
1662
  function logForDebugging2(msg) {
1530
1663
  if (process.env.DEBUG_KEYBINDINGS) console.error(msg);
@@ -1604,7 +1737,7 @@ function KeybindingSetup({ children, onWarnings }) {
1604
1737
  clearChordTimeout();
1605
1738
  };
1606
1739
  }, [clearChordTimeout]);
1607
- return /* @__PURE__ */ jsxs3(
1740
+ return /* @__PURE__ */ jsxs5(
1608
1741
  KeybindingProvider,
1609
1742
  {
1610
1743
  bindings,
@@ -1616,7 +1749,7 @@ function KeybindingSetup({ children, onWarnings }) {
1616
1749
  unregisterActiveContext,
1617
1750
  handlerRegistryRef,
1618
1751
  children: [
1619
- /* @__PURE__ */ jsx5(
1752
+ /* @__PURE__ */ jsx6(
1620
1753
  ChordInterceptor,
1621
1754
  {
1622
1755
  bindings,
@@ -1706,10 +1839,98 @@ function ChordInterceptor({
1706
1839
  return null;
1707
1840
  }
1708
1841
 
1842
+ // src/hooks/useDoublePress.ts
1843
+ import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef2 } from "react";
1844
+ var DOUBLE_PRESS_TIMEOUT_MS = 800;
1845
+ function useDoublePress(setPending, onDoublePress, onFirstPress) {
1846
+ const lastPressRef = useRef2(0);
1847
+ const timeoutRef = useRef2(void 0);
1848
+ const clearTimeoutSafe = useCallback3(() => {
1849
+ if (timeoutRef.current) {
1850
+ clearTimeout(timeoutRef.current);
1851
+ timeoutRef.current = void 0;
1852
+ }
1853
+ }, []);
1854
+ useEffect4(() => {
1855
+ return () => {
1856
+ clearTimeoutSafe();
1857
+ };
1858
+ }, [clearTimeoutSafe]);
1859
+ return useCallback3(() => {
1860
+ const now = Date.now();
1861
+ const timeSinceLastPress = now - lastPressRef.current;
1862
+ const isDoublePress = timeSinceLastPress <= DOUBLE_PRESS_TIMEOUT_MS && timeoutRef.current !== void 0;
1863
+ if (isDoublePress) {
1864
+ clearTimeoutSafe();
1865
+ setPending(false);
1866
+ onDoublePress();
1867
+ } else {
1868
+ onFirstPress?.();
1869
+ setPending(true);
1870
+ clearTimeoutSafe();
1871
+ timeoutRef.current = setTimeout(
1872
+ (setPending2, timeoutRef2) => {
1873
+ setPending2(false);
1874
+ timeoutRef2.current = void 0;
1875
+ },
1876
+ DOUBLE_PRESS_TIMEOUT_MS,
1877
+ setPending,
1878
+ timeoutRef
1879
+ );
1880
+ }
1881
+ lastPressRef.current = now;
1882
+ }, [setPending, onDoublePress, onFirstPress, clearTimeoutSafe]);
1883
+ }
1884
+
1885
+ // src/hooks/useTerminalSize.ts
1886
+ import { useContext as useContext3 } from "react";
1887
+ import { TerminalSizeContext as TerminalSizeContext2 } from "@claude-code-kit/ink-renderer";
1888
+ function useTerminalSize() {
1889
+ const size = useContext3(TerminalSizeContext2);
1890
+ if (!size) {
1891
+ throw new Error("useTerminalSize must be used within an Ink App component");
1892
+ }
1893
+ return size;
1894
+ }
1895
+
1896
+ // src/PromptInput.tsx
1897
+ import { useState as useState3, useCallback as useCallback4 } from "react";
1898
+ import { Text as Text6, Box as Box3, useInput as useInput3 } from "@claude-code-kit/ink-renderer";
1899
+
1900
+ // src/utils/promptInputLogic.ts
1901
+ function wordFwd(s, p) {
1902
+ let i = p;
1903
+ while (i < s.length && s[i] !== " ") i++;
1904
+ while (i < s.length && s[i] === " ") i++;
1905
+ return i;
1906
+ }
1907
+ function wordBwd(s, p) {
1908
+ let i = p;
1909
+ if (i > 0) i--;
1910
+ while (i > 0 && s[i] === " ") i--;
1911
+ while (i > 0 && s[i - 1] !== " ") i--;
1912
+ return i;
1913
+ }
1914
+ function lineOffset(lines, line) {
1915
+ let pos = 0;
1916
+ for (let i = 0; i < line; i++) pos += lines[i].length + 1;
1917
+ return pos;
1918
+ }
1919
+ function cursorLineIndex(lines, cursor) {
1920
+ let pos = 0;
1921
+ for (let i = 0; i < lines.length; i++) {
1922
+ if (cursor <= pos + lines[i].length) return i;
1923
+ pos += lines[i].length + 1;
1924
+ }
1925
+ return lines.length - 1;
1926
+ }
1927
+ function filterCommands(commands, value) {
1928
+ if (!value.startsWith("/")) return [];
1929
+ return commands.filter((cmd) => `/${cmd.name}`.startsWith(value));
1930
+ }
1931
+
1709
1932
  // src/PromptInput.tsx
1710
- import { useState as useState3, useCallback as useCallback3 } from "react";
1711
- import { Text as Text5, Box as Box2, useInput as useInput3 } from "@claude-code-kit/ink-renderer";
1712
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1933
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1713
1934
  function PromptInput({
1714
1935
  value,
1715
1936
  onChange,
@@ -1720,186 +1941,302 @@ function PromptInput({
1720
1941
  disabled = false,
1721
1942
  commands = [],
1722
1943
  onCommandSelect,
1723
- history = []
1944
+ history = [],
1945
+ vimMode = false,
1946
+ multiline = false
1724
1947
  }) {
1725
1948
  const [cursor, setCursor] = useState3(0);
1726
1949
  const [historyIndex, setHistoryIndex] = useState3(-1);
1727
1950
  const [suggestionIndex, setSuggestionIndex] = useState3(0);
1728
1951
  const [showSuggestions, setShowSuggestions] = useState3(false);
1729
- const suggestions = value.startsWith("/") && commands.length > 0 ? commands.filter((cmd) => `/${cmd.name}`.startsWith(value)) : [];
1952
+ const [vim, setVim] = useState3("INSERT");
1953
+ const [pendingD, setPendingD] = useState3(false);
1954
+ const isVimNormal = vimMode && vim === "NORMAL";
1955
+ const suggestions = commands.length > 0 ? filterCommands(commands, value) : [];
1730
1956
  const hasSuggestions = showSuggestions && suggestions.length > 0;
1731
- const updateValue = useCallback3(
1732
- (newValue, newCursor) => {
1733
- onChange(newValue);
1734
- setCursor(newCursor ?? newValue.length);
1957
+ const lines = multiline ? value.split("\n") : [value];
1958
+ const cursorLine = multiline ? cursorLineIndex(lines, cursor) : 0;
1959
+ const lineOffset2 = (line) => lineOffset(lines, line);
1960
+ const updateValue = useCallback4((nv, nc) => {
1961
+ onChange(nv);
1962
+ setCursor(nc ?? nv.length);
1963
+ setHistoryIndex(-1);
1964
+ setShowSuggestions(nv.startsWith("/"));
1965
+ setSuggestionIndex(0);
1966
+ }, [onChange]);
1967
+ const insertNewline = () => {
1968
+ updateValue(value.slice(0, cursor) + "\n" + value.slice(cursor), cursor + 1);
1969
+ };
1970
+ const moveLine = (dir) => {
1971
+ const target = cursorLine + dir;
1972
+ if (multiline && target >= 0 && target < lines.length) {
1973
+ const col = cursor - lineOffset2(cursorLine);
1974
+ setCursor(lineOffset2(target) + Math.min(col, lines[target].length));
1975
+ return true;
1976
+ }
1977
+ return false;
1978
+ };
1979
+ const historyUp = () => {
1980
+ if (history.length > 0 && historyIndex + 1 < history.length) {
1981
+ const ni = historyIndex + 1;
1982
+ setHistoryIndex(ni);
1983
+ const hv = history[ni];
1984
+ onChange(hv);
1985
+ setCursor(hv.length);
1986
+ }
1987
+ };
1988
+ const historyDown = () => {
1989
+ if (historyIndex > 0) {
1990
+ const ni = historyIndex - 1;
1991
+ setHistoryIndex(ni);
1992
+ const hv = history[ni];
1993
+ onChange(hv);
1994
+ setCursor(hv.length);
1995
+ } else if (historyIndex === 0) {
1735
1996
  setHistoryIndex(-1);
1736
- setShowSuggestions(newValue.startsWith("/"));
1737
- setSuggestionIndex(0);
1738
- },
1739
- [onChange]
1740
- );
1741
- useInput3(
1742
- (input, key) => {
1743
- if (disabled) return;
1744
- if (key.return) {
1745
- if (hasSuggestions) {
1746
- const cmd = suggestions[suggestionIndex];
1747
- const cmdValue = `/${cmd.name}`;
1748
- onCommandSelect?.(cmd.name);
1749
- onChange(cmdValue);
1750
- setCursor(cmdValue.length);
1751
- setShowSuggestions(false);
1752
- return;
1753
- }
1754
- if (value.length > 0) {
1755
- onSubmit(value);
1756
- }
1997
+ onChange("");
1998
+ setCursor(0);
1999
+ }
2000
+ };
2001
+ useInput3((input, key) => {
2002
+ if (disabled) return;
2003
+ if (isVimNormal) {
2004
+ if (input !== "d") setPendingD(false);
2005
+ if (input === "i") {
2006
+ setVim("INSERT");
1757
2007
  return;
1758
2008
  }
1759
- if (key.escape) {
1760
- if (hasSuggestions) {
1761
- setShowSuggestions(false);
1762
- }
2009
+ if (input === "a") {
2010
+ setVim("INSERT");
2011
+ setCursor((c) => Math.min(value.length, c + 1));
1763
2012
  return;
1764
2013
  }
1765
- if (key.tab) {
1766
- if (hasSuggestions) {
1767
- const cmd = suggestions[suggestionIndex];
1768
- const cmdValue = `/${cmd.name} `;
1769
- updateValue(cmdValue);
1770
- }
2014
+ if (input === "h" || key.leftArrow) {
2015
+ setCursor((c) => Math.max(0, c - 1));
1771
2016
  return;
1772
2017
  }
1773
- if (key.upArrow) {
1774
- if (hasSuggestions) {
1775
- setSuggestionIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
1776
- return;
2018
+ if (input === "l" || key.rightArrow) {
2019
+ setCursor((c) => Math.min(Math.max(0, value.length - 1), c + 1));
2020
+ return;
2021
+ }
2022
+ if (input === "0" || key.home) {
2023
+ setCursor(multiline ? lineOffset2(cursorLine) : 0);
2024
+ return;
2025
+ }
2026
+ if (input === "$" || key.end) {
2027
+ if (multiline) {
2028
+ const endOfLine = lineOffset2(cursorLine) + lines[cursorLine].length;
2029
+ setCursor(Math.max(lineOffset2(cursorLine), endOfLine - 1));
2030
+ } else {
2031
+ setCursor(Math.max(0, value.length - 1));
1777
2032
  }
1778
- if (history.length > 0) {
1779
- const nextIndex = historyIndex + 1;
1780
- if (nextIndex < history.length) {
1781
- setHistoryIndex(nextIndex);
1782
- const histValue = history[nextIndex];
1783
- onChange(histValue);
1784
- setCursor(histValue.length);
1785
- }
2033
+ return;
2034
+ }
2035
+ if (input === "w") {
2036
+ setCursor(wordFwd(value, cursor));
2037
+ return;
2038
+ }
2039
+ if (input === "b") {
2040
+ setCursor(wordBwd(value, cursor));
2041
+ return;
2042
+ }
2043
+ if (input === "x") {
2044
+ if (cursor < value.length) {
2045
+ const nv = value.slice(0, cursor) + value.slice(cursor + 1);
2046
+ updateValue(nv, Math.min(cursor, Math.max(0, nv.length - 1)));
1786
2047
  }
1787
2048
  return;
1788
2049
  }
1789
- if (key.downArrow) {
1790
- if (hasSuggestions) {
1791
- setSuggestionIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
2050
+ if (input === "d") {
2051
+ if (!pendingD) {
2052
+ setPendingD(true);
1792
2053
  return;
1793
2054
  }
1794
- if (historyIndex > 0) {
1795
- const nextIndex = historyIndex - 1;
1796
- setHistoryIndex(nextIndex);
1797
- const histValue = history[nextIndex];
1798
- onChange(histValue);
1799
- setCursor(histValue.length);
1800
- } else if (historyIndex === 0) {
1801
- setHistoryIndex(-1);
1802
- onChange("");
1803
- setCursor(0);
2055
+ setPendingD(false);
2056
+ if (multiline && lines.length > 1) {
2057
+ const pos = lineOffset2(cursorLine);
2058
+ const end = pos + lines[cursorLine].length;
2059
+ const from = cursorLine === 0 ? pos : pos - 1;
2060
+ const to = cursorLine === 0 ? Math.min(end + 1, value.length) : end;
2061
+ const nv = value.slice(0, from) + value.slice(to);
2062
+ updateValue(nv, Math.min(from, Math.max(0, nv.length - 1)));
2063
+ } else {
2064
+ updateValue("", 0);
1804
2065
  }
1805
2066
  return;
1806
2067
  }
1807
- if (key.leftArrow) {
1808
- setCursor((c) => Math.max(0, c - 1));
2068
+ if (key.upArrow || input === "k" && !key.ctrl) {
2069
+ if (!moveLine(-1)) historyUp();
1809
2070
  return;
1810
2071
  }
1811
- if (key.rightArrow) {
1812
- setCursor((c) => Math.min(value.length, c + 1));
2072
+ if (key.downArrow || input === "j" && !key.ctrl) {
2073
+ if (!moveLine(1)) historyDown();
1813
2074
  return;
1814
2075
  }
1815
- if (key.home || key.ctrl && input === "a") {
1816
- setCursor(0);
2076
+ if (key.return && value.length > 0) {
2077
+ onSubmit(value);
1817
2078
  return;
1818
2079
  }
1819
- if (key.end || key.ctrl && input === "e") {
1820
- setCursor(value.length);
2080
+ return;
2081
+ }
2082
+ if (key.return) {
2083
+ if (hasSuggestions) {
2084
+ const cmd = suggestions[suggestionIndex];
2085
+ const cv = `/${cmd.name}`;
2086
+ onCommandSelect?.(cmd.name);
2087
+ onChange(cv);
2088
+ setCursor(cv.length);
2089
+ setShowSuggestions(false);
1821
2090
  return;
1822
2091
  }
1823
- if (key.ctrl && input === "w") {
1824
- if (cursor > 0) {
1825
- let i = cursor - 1;
1826
- while (i > 0 && value[i - 1] === " ") i--;
1827
- while (i > 0 && value[i - 1] !== " ") i--;
1828
- const newValue = value.slice(0, i) + value.slice(cursor);
1829
- updateValue(newValue, i);
1830
- }
2092
+ if (multiline && key.shift) {
2093
+ insertNewline();
1831
2094
  return;
1832
2095
  }
1833
- if (key.ctrl && input === "u") {
1834
- const newValue = value.slice(cursor);
1835
- updateValue(newValue, 0);
2096
+ if (value.length > 0) onSubmit(value);
2097
+ return;
2098
+ }
2099
+ if (key.escape) {
2100
+ if (hasSuggestions) {
2101
+ setShowSuggestions(false);
1836
2102
  return;
1837
2103
  }
1838
- if (key.backspace) {
1839
- if (cursor > 0) {
1840
- const newValue = value.slice(0, cursor - 1) + value.slice(cursor);
1841
- updateValue(newValue, cursor - 1);
1842
- }
2104
+ if (vimMode) {
2105
+ setVim("NORMAL");
1843
2106
  return;
1844
2107
  }
1845
- if (key.delete) {
1846
- if (cursor < value.length) {
1847
- const newValue = value.slice(0, cursor) + value.slice(cursor + 1);
1848
- updateValue(newValue, cursor);
1849
- }
2108
+ return;
2109
+ }
2110
+ if (multiline && key.ctrl && input === "j") {
2111
+ insertNewline();
2112
+ return;
2113
+ }
2114
+ if (key.tab) {
2115
+ if (hasSuggestions) {
2116
+ updateValue(`/${suggestions[suggestionIndex].name} `);
2117
+ }
2118
+ ;
2119
+ return;
2120
+ }
2121
+ if (key.upArrow) {
2122
+ if (hasSuggestions) {
2123
+ setSuggestionIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
1850
2124
  return;
1851
2125
  }
1852
- if (key.ctrl || key.meta) return;
1853
- if (input.length > 0) {
1854
- const newValue = value.slice(0, cursor) + input + value.slice(cursor);
1855
- updateValue(newValue, cursor + input.length);
2126
+ if (!moveLine(-1)) historyUp();
2127
+ return;
2128
+ }
2129
+ if (key.downArrow) {
2130
+ if (hasSuggestions) {
2131
+ setSuggestionIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
2132
+ return;
1856
2133
  }
1857
- },
1858
- { isActive: !disabled }
1859
- );
1860
- const renderTextWithCursor = () => {
1861
- if (value.length === 0 && placeholder) {
1862
- return /* @__PURE__ */ jsxs4(Text5, { children: [
1863
- /* @__PURE__ */ jsx6(Text5, { inverse: true, children: " " }),
1864
- /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: placeholder })
2134
+ if (!moveLine(1)) historyDown();
2135
+ return;
2136
+ }
2137
+ if (key.leftArrow) {
2138
+ setCursor((c) => Math.max(0, c - 1));
2139
+ return;
2140
+ }
2141
+ if (key.rightArrow) {
2142
+ setCursor((c) => Math.min(value.length, c + 1));
2143
+ return;
2144
+ }
2145
+ if (key.home || key.ctrl && input === "a") {
2146
+ setCursor(0);
2147
+ return;
2148
+ }
2149
+ if (key.end || key.ctrl && input === "e") {
2150
+ setCursor(value.length);
2151
+ return;
2152
+ }
2153
+ if (key.ctrl && input === "w") {
2154
+ if (cursor > 0) {
2155
+ let i = cursor - 1;
2156
+ while (i > 0 && value[i - 1] === " ") i--;
2157
+ while (i > 0 && value[i - 1] !== " ") i--;
2158
+ updateValue(value.slice(0, i) + value.slice(cursor), i);
2159
+ }
2160
+ ;
2161
+ return;
2162
+ }
2163
+ if (key.ctrl && input === "u") {
2164
+ updateValue(value.slice(cursor), 0);
2165
+ return;
2166
+ }
2167
+ if (key.backspace) {
2168
+ if (cursor > 0) updateValue(value.slice(0, cursor - 1) + value.slice(cursor), cursor - 1);
2169
+ return;
2170
+ }
2171
+ if (key.delete) {
2172
+ if (cursor < value.length) updateValue(value.slice(0, cursor) + value.slice(cursor + 1), cursor);
2173
+ return;
2174
+ }
2175
+ if (key.ctrl || key.meta) return;
2176
+ if (input.length > 0) updateValue(value.slice(0, cursor) + input + value.slice(cursor), cursor + input.length);
2177
+ }, { isActive: !disabled });
2178
+ const renderCursor = (text, cur) => {
2179
+ if (text.length === 0 && placeholder && cursor === 0) {
2180
+ return /* @__PURE__ */ jsxs6(Text6, { children: [
2181
+ /* @__PURE__ */ jsx7(Text6, { inverse: true, children: " " }),
2182
+ /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: placeholder })
1865
2183
  ] });
1866
2184
  }
1867
- const before = value.slice(0, cursor);
1868
- const atCursor = cursor < value.length ? value[cursor] : " ";
1869
- const after = cursor < value.length ? value.slice(cursor + 1) : "";
1870
- return /* @__PURE__ */ jsxs4(Text5, { children: [
2185
+ const before = text.slice(0, cur);
2186
+ const at = cur < text.length ? text[cur] : " ";
2187
+ const after = cur < text.length ? text.slice(cur + 1) : "";
2188
+ return /* @__PURE__ */ jsxs6(Text6, { children: [
1871
2189
  before,
1872
- /* @__PURE__ */ jsx6(Text5, { inverse: true, children: atCursor }),
2190
+ /* @__PURE__ */ jsx7(Text6, { inverse: true, children: at }),
1873
2191
  after
1874
2192
  ] });
1875
2193
  };
1876
- return /* @__PURE__ */ jsxs4(Box2, { flexDirection: "column", children: [
1877
- /* @__PURE__ */ jsxs4(Box2, { children: [
1878
- /* @__PURE__ */ jsxs4(Text5, { color: prefixColor, children: [
1879
- prefix,
1880
- " "
1881
- ] }),
1882
- renderTextWithCursor()
1883
- ] }),
1884
- hasSuggestions && /* @__PURE__ */ jsx6(Box2, { flexDirection: "column", marginLeft: 2, children: suggestions.map((cmd, i) => /* @__PURE__ */ jsxs4(Box2, { children: [
1885
- /* @__PURE__ */ jsx6(
1886
- Text5,
1887
- {
1888
- inverse: i === suggestionIndex,
1889
- color: i === suggestionIndex ? "cyan" : void 0,
1890
- children: ` /${cmd.name}`
1891
- }
1892
- ),
1893
- /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: ` ${cmd.description}` })
1894
- ] }, cmd.name)) })
2194
+ const vimTag = vimMode ? /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: ` -- ${vim} --` }) : null;
2195
+ const renderContent = () => {
2196
+ if (!multiline || lines.length <= 1) {
2197
+ return /* @__PURE__ */ jsxs6(Box3, { children: [
2198
+ /* @__PURE__ */ jsxs6(Text6, { color: prefixColor, children: [
2199
+ prefix,
2200
+ " "
2201
+ ] }),
2202
+ renderCursor(value, cursor),
2203
+ vimTag
2204
+ ] });
2205
+ }
2206
+ let off = 0;
2207
+ return /* @__PURE__ */ jsx7(Box3, { flexDirection: "column", children: lines.map((line, i) => {
2208
+ const ls = off;
2209
+ off += line.length + 1;
2210
+ const active = i === cursorLine;
2211
+ return /* @__PURE__ */ jsxs6(Box3, { children: [
2212
+ /* @__PURE__ */ jsx7(Text6, { color: prefixColor, children: i === 0 ? `${prefix} ` : "\u2219 " }),
2213
+ active ? renderCursor(line, cursor - ls) : /* @__PURE__ */ jsx7(Text6, { children: line }),
2214
+ i === lines.length - 1 && vimTag
2215
+ ] }, i);
2216
+ }) });
2217
+ };
2218
+ return /* @__PURE__ */ jsxs6(Box3, { flexDirection: "column", children: [
2219
+ renderContent(),
2220
+ hasSuggestions && /* @__PURE__ */ jsx7(Box3, { flexDirection: "column", marginLeft: 2, children: suggestions.map((cmd, i) => {
2221
+ const isFocused = i === suggestionIndex;
2222
+ return /* @__PURE__ */ jsxs6(Box3, { children: [
2223
+ /* @__PURE__ */ jsxs6(Text6, { color: isFocused ? "cyan" : void 0, children: [
2224
+ isFocused ? "\u276F" : " ",
2225
+ " "
2226
+ ] }),
2227
+ /* @__PURE__ */ jsx7(Text6, { color: isFocused ? "cyan" : void 0, bold: isFocused, children: `/${cmd.name}` }),
2228
+ /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: ` ${cmd.description}` })
2229
+ ] }, cmd.name);
2230
+ }) })
1895
2231
  ] });
1896
2232
  }
1897
2233
 
1898
2234
  // src/Spinner.tsx
1899
- import { useState as useState4, useEffect as useEffect4, useRef as useRef2 } from "react";
1900
- import { Text as Text6, Box as Box3 } from "@claude-code-kit/ink-renderer";
1901
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1902
- var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
2235
+ import { useState as useState4, useEffect as useEffect5, useRef as useRef3 } from "react";
2236
+ import { Text as Text7, Box as Box4 } from "@claude-code-kit/ink-renderer";
2237
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2238
+ var DEFAULT_CHARACTERS = process.platform === "darwin" ? ["\xB7", "\u2722", "\u2733", "\u2736", "\u273B", "\u273D"] : ["\xB7", "\u2722", "*", "\u2736", "\u273B", "\u273D"];
2239
+ var FRAMES = [...DEFAULT_CHARACTERS, ...[...DEFAULT_CHARACTERS].reverse()];
1903
2240
  var SPINNER_INTERVAL = 80;
1904
2241
  var VERB_ROTATE_INTERVAL = 4e3;
1905
2242
  var ELAPSED_SHOW_AFTER = 1e3;
@@ -1908,22 +2245,22 @@ function Spinner({
1908
2245
  label,
1909
2246
  verb,
1910
2247
  verbs,
1911
- color = DEFAULT_COLOR,
2248
+ color: color2 = DEFAULT_COLOR,
1912
2249
  showElapsed = true
1913
2250
  }) {
1914
2251
  const [frameIndex, setFrameIndex] = useState4(0);
1915
2252
  const [verbIndex, setVerbIndex] = useState4(0);
1916
2253
  const [elapsed, setElapsed] = useState4(0);
1917
- const startRef = useRef2(Date.now());
2254
+ const startRef = useRef3(Date.now());
1918
2255
  const allVerbs = verbs ?? (verb ? [verb] : ["Thinking"]);
1919
- useEffect4(() => {
2256
+ useEffect5(() => {
1920
2257
  const id = setInterval(() => {
1921
2258
  setFrameIndex((i) => (i + 1) % FRAMES.length);
1922
2259
  setElapsed(Date.now() - startRef.current);
1923
2260
  }, SPINNER_INTERVAL);
1924
2261
  return () => clearInterval(id);
1925
2262
  }, []);
1926
- useEffect4(() => {
2263
+ useEffect5(() => {
1927
2264
  if (allVerbs.length <= 1) return;
1928
2265
  const id = setInterval(() => {
1929
2266
  setVerbIndex((i) => (i + 1) % allVerbs.length);
@@ -1934,18 +2271,18 @@ function Spinner({
1934
2271
  const currentVerb = allVerbs[verbIndex % allVerbs.length];
1935
2272
  const elapsedSec = Math.floor(elapsed / 1e3);
1936
2273
  const showTime = showElapsed && elapsed >= ELAPSED_SHOW_AFTER;
1937
- return /* @__PURE__ */ jsxs5(Box3, { children: [
1938
- /* @__PURE__ */ jsx7(Text6, { color, children: frame }),
1939
- /* @__PURE__ */ jsxs5(Text6, { children: [
2274
+ return /* @__PURE__ */ jsxs7(Box4, { children: [
2275
+ /* @__PURE__ */ jsx8(Text7, { color: color2, children: frame }),
2276
+ /* @__PURE__ */ jsxs7(Text7, { children: [
1940
2277
  " ",
1941
2278
  currentVerb,
1942
2279
  "..."
1943
2280
  ] }),
1944
- label && /* @__PURE__ */ jsxs5(Text6, { children: [
2281
+ label && /* @__PURE__ */ jsxs7(Text7, { children: [
1945
2282
  " ",
1946
2283
  label
1947
2284
  ] }),
1948
- showTime && /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
2285
+ showTime && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1949
2286
  " (",
1950
2287
  elapsedSec,
1951
2288
  "s)"
@@ -1953,100 +2290,952 @@ function Spinner({
1953
2290
  ] });
1954
2291
  }
1955
2292
 
1956
- // src/Select.tsx
1957
- import { useState as useState5, useRef as useRef3, useMemo as useMemo2, useCallback as useCallback4 } from "react";
1958
- import { Box as Box4, Text as Text7, useInput as useInput4 } from "@claude-code-kit/ink-renderer";
1959
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1960
- function useListNavigation(opts) {
1961
- const { options, maxVisible, onCancel, onSelect, extraHandler } = opts;
1962
- const [focusIndex, setFocusIndex] = useState5(0);
1963
- const focusRef = useRef3(focusIndex);
1964
- focusRef.current = focusIndex;
1965
- const total = options.length;
1966
- const max = maxVisible ?? total;
1967
- const scrollOffset = useMemo2(() => {
1968
- if (total <= max) return 0;
1969
- const half = Math.floor(max / 2);
1970
- if (focusIndex <= half) return 0;
1971
- if (focusIndex >= total - max + half) return total - max;
1972
- return focusIndex - half;
1973
- }, [focusIndex, total, max]);
1974
- const visibleOptions = useMemo2(
1975
- () => options.slice(scrollOffset, scrollOffset + max),
1976
- [options, scrollOffset, max]
1977
- );
1978
- const moveFocus = useCallback4(
1979
- (dir) => {
1980
- setFocusIndex((prev) => {
1981
- let next = prev;
1982
- for (let i = 0; i < total; i++) {
1983
- next = (next + dir + total) % total;
1984
- if (!options[next].disabled) return next;
2293
+ // src/Markdown.tsx
2294
+ import { marked as marked2 } from "marked";
2295
+ import { Suspense, use, useMemo as useMemo4, useRef as useRef4 } from "react";
2296
+ import { Ansi as Ansi4, Box as Box5 } from "@claude-code-kit/ink-renderer";
2297
+
2298
+ // src/design-system/ThemeProvider.tsx
2299
+ import { createContext as createContext2, useContext as useContext4, useMemo as useMemo3, useState as useState5 } from "react";
2300
+ import { jsx as jsx9 } from "react/jsx-runtime";
2301
+ var themes = {
2302
+ dark: {
2303
+ text: "#E0E0E0",
2304
+ dimText: "#666666",
2305
+ border: "#444444",
2306
+ accent: "#5B9BD5",
2307
+ success: "#6BC76B",
2308
+ warning: "#E5C07B",
2309
+ error: "#E06C75",
2310
+ assistant: "#DA7756",
2311
+ inactive: "#666666",
2312
+ inverseText: "#1E1E1E",
2313
+ permission: "#5B9BD5",
2314
+ diffAdded: "#1a3a1a",
2315
+ diffRemoved: "#3a1a1a",
2316
+ diffAddedWord: "#2d5a2d",
2317
+ diffRemovedWord: "#5a2d2d",
2318
+ diffHeader: "#1e2d3d",
2319
+ userMessage: "#2B4A6F",
2320
+ assistantMessage: "#3D2614",
2321
+ systemMessage: "#2D2D2D",
2322
+ toolUseMessage: "#1E3A2D",
2323
+ permissionAllow: "#1B4332",
2324
+ permissionDeny: "#3B1014",
2325
+ permissionAlways: "#1B2F4D",
2326
+ focus: "#5B9BD5",
2327
+ selection: "#264F78",
2328
+ placeholder: "#555555",
2329
+ link: "#79B8FF",
2330
+ code: "#F8BFB0",
2331
+ codeBackground: "#2D2D2D",
2332
+ blockquote: "#444444",
2333
+ info: "#5B9BD5",
2334
+ spinnerColor: "#DA7756",
2335
+ shimmer: "#3A3A3A"
2336
+ },
2337
+ light: {
2338
+ text: "#1E1E1E",
2339
+ dimText: "#999999",
2340
+ border: "#CCCCCC",
2341
+ accent: "#0066CC",
2342
+ success: "#2E7D32",
2343
+ warning: "#F57C00",
2344
+ error: "#C62828",
2345
+ assistant: "#DA7756",
2346
+ inactive: "#999999",
2347
+ inverseText: "#FFFFFF",
2348
+ permission: "#0066CC",
2349
+ diffAdded: "#E6FFEC",
2350
+ diffRemoved: "#FFEBE9",
2351
+ diffAddedWord: "#CCFFD8",
2352
+ diffRemovedWord: "#FFD7D5",
2353
+ diffHeader: "#DDF4FF",
2354
+ userMessage: "#E8F0FE",
2355
+ assistantMessage: "#FDF2EE",
2356
+ systemMessage: "#F5F5F5",
2357
+ toolUseMessage: "#EAF5EE",
2358
+ permissionAllow: "#E6F4EA",
2359
+ permissionDeny: "#FCE8E6",
2360
+ permissionAlways: "#E8F0FE",
2361
+ focus: "#0066CC",
2362
+ selection: "#B3D4FF",
2363
+ placeholder: "#AAAAAA",
2364
+ link: "#0066CC",
2365
+ code: "#C7522A",
2366
+ codeBackground: "#F5F5F5",
2367
+ blockquote: "#EEEEEE",
2368
+ info: "#0066CC",
2369
+ spinnerColor: "#DA7756",
2370
+ shimmer: "#E8E8E8"
2371
+ },
2372
+ "light-high-contrast": {
2373
+ text: "#000000",
2374
+ dimText: "#595959",
2375
+ border: "#767676",
2376
+ accent: "#0000EE",
2377
+ success: "#006400",
2378
+ warning: "#7A4000",
2379
+ error: "#AE1818",
2380
+ assistant: "#B55530",
2381
+ inactive: "#767676",
2382
+ inverseText: "#FFFFFF",
2383
+ permission: "#0000EE",
2384
+ diffAdded: "#CCF0D0",
2385
+ diffRemoved: "#F5C6C6",
2386
+ diffAddedWord: "#99E0A0",
2387
+ diffRemovedWord: "#EBA0A0",
2388
+ diffHeader: "#B8DEFF",
2389
+ userMessage: "#C8DCFF",
2390
+ assistantMessage: "#FCDAC8",
2391
+ systemMessage: "#E0E0E0",
2392
+ toolUseMessage: "#C4EED0",
2393
+ permissionAllow: "#C4EED0",
2394
+ permissionDeny: "#F5C6C6",
2395
+ permissionAlways: "#C8DCFF",
2396
+ focus: "#0000EE",
2397
+ selection: "#80BFFF",
2398
+ placeholder: "#767676",
2399
+ link: "#0000EE",
2400
+ code: "#8B0000",
2401
+ codeBackground: "#E8E8E8",
2402
+ blockquote: "#D0D0D0",
2403
+ info: "#0000EE",
2404
+ spinnerColor: "#B55530",
2405
+ shimmer: "#D0D0D0"
2406
+ },
2407
+ "dark-dimmed": {
2408
+ text: "#ADBAC7",
2409
+ dimText: "#545D68",
2410
+ border: "#373E47",
2411
+ accent: "#539BF5",
2412
+ success: "#57AB5A",
2413
+ warning: "#C69026",
2414
+ error: "#E5534B",
2415
+ assistant: "#DA7756",
2416
+ inactive: "#545D68",
2417
+ inverseText: "#22272E",
2418
+ permission: "#539BF5",
2419
+ diffAdded: "#1B2F23",
2420
+ diffRemoved: "#2F1B1E",
2421
+ diffAddedWord: "#264D30",
2422
+ diffRemovedWord: "#4D2628",
2423
+ diffHeader: "#1C2B3A",
2424
+ userMessage: "#1C2B3A",
2425
+ assistantMessage: "#2F211A",
2426
+ systemMessage: "#2D333B",
2427
+ toolUseMessage: "#1B2B23",
2428
+ permissionAllow: "#1B2B23",
2429
+ permissionDeny: "#2F1B1E",
2430
+ permissionAlways: "#1C2B3A",
2431
+ focus: "#539BF5",
2432
+ selection: "#1C4066",
2433
+ placeholder: "#545D68",
2434
+ link: "#6CB6FF",
2435
+ code: "#F0A070",
2436
+ codeBackground: "#2D333B",
2437
+ blockquote: "#373E47",
2438
+ info: "#539BF5",
2439
+ spinnerColor: "#DA7756",
2440
+ shimmer: "#373E47"
2441
+ }
2442
+ };
2443
+ function getTheme(name) {
2444
+ return themes[name] ?? themes.dark;
2445
+ }
2446
+ var DEFAULT_THEME = "dark";
2447
+ var ThemeContext = createContext2({
2448
+ themeSetting: DEFAULT_THEME,
2449
+ setThemeSetting: () => {
2450
+ },
2451
+ setPreviewTheme: () => {
2452
+ },
2453
+ savePreview: () => {
2454
+ },
2455
+ cancelPreview: () => {
2456
+ },
2457
+ currentTheme: DEFAULT_THEME
2458
+ });
2459
+ function ThemeProvider({
2460
+ children,
2461
+ initialState = "dark",
2462
+ onThemeSave
2463
+ }) {
2464
+ const [themeSetting, setThemeSetting] = useState5(initialState);
2465
+ const [previewTheme, setPreviewTheme] = useState5(null);
2466
+ const activeSetting = previewTheme ?? themeSetting;
2467
+ const currentTheme = activeSetting === "auto" ? "dark" : activeSetting;
2468
+ const value = useMemo3(
2469
+ () => ({
2470
+ themeSetting,
2471
+ setThemeSetting: (newSetting) => {
2472
+ setThemeSetting(newSetting);
2473
+ setPreviewTheme(null);
2474
+ onThemeSave?.(newSetting);
2475
+ },
2476
+ setPreviewTheme: (newSetting) => {
2477
+ setPreviewTheme(newSetting);
2478
+ },
2479
+ savePreview: () => {
2480
+ if (previewTheme !== null) {
2481
+ setThemeSetting(previewTheme);
2482
+ setPreviewTheme(null);
2483
+ onThemeSave?.(previewTheme);
1985
2484
  }
1986
- return prev;
1987
- });
1988
- },
1989
- [options, total]
2485
+ },
2486
+ cancelPreview: () => {
2487
+ if (previewTheme !== null) {
2488
+ setPreviewTheme(null);
2489
+ }
2490
+ },
2491
+ currentTheme
2492
+ }),
2493
+ [themeSetting, previewTheme, currentTheme, onThemeSave]
1990
2494
  );
1991
- useInput4((input, key) => {
1992
- if (extraHandler?.(input, key, focusRef.current)) return;
1993
- if (key.upArrow || input === "k") {
1994
- moveFocus(-1);
1995
- } else if (key.downArrow || input === "j") {
1996
- moveFocus(1);
1997
- } else if (key.return) {
1998
- if (!options[focusRef.current]?.disabled) {
1999
- onSelect(focusRef.current);
2000
- }
2001
- } else if (key.escape) {
2002
- onCancel?.();
2003
- } else if (input >= "1" && input <= "9") {
2004
- const idx = parseInt(input, 10) - 1;
2005
- if (idx < total && !options[idx].disabled) {
2006
- setFocusIndex(idx);
2007
- onSelect(idx);
2495
+ return /* @__PURE__ */ jsx9(ThemeContext.Provider, { value, children });
2496
+ }
2497
+ function useTheme() {
2498
+ const { currentTheme, setThemeSetting } = useContext4(ThemeContext);
2499
+ return [currentTheme, setThemeSetting];
2500
+ }
2501
+ function useThemeSetting() {
2502
+ return useContext4(ThemeContext).themeSetting;
2503
+ }
2504
+ function usePreviewTheme() {
2505
+ const { setPreviewTheme, savePreview, cancelPreview } = useContext4(ThemeContext);
2506
+ return { setPreviewTheme, savePreview, cancelPreview };
2507
+ }
2508
+
2509
+ // src/utils/optional/cliHighlight.ts
2510
+ var cliHighlightPromise;
2511
+ async function loadCliHighlight() {
2512
+ try {
2513
+ const mod = await import("cli-highlight");
2514
+ const cliHighlight = mod;
2515
+ return {
2516
+ highlight: cliHighlight.highlight,
2517
+ supportsLanguage: cliHighlight.supportsLanguage
2518
+ };
2519
+ } catch {
2520
+ return null;
2521
+ }
2522
+ }
2523
+ function getCliHighlightPromise() {
2524
+ cliHighlightPromise ?? (cliHighlightPromise = loadCliHighlight());
2525
+ return cliHighlightPromise;
2526
+ }
2527
+
2528
+ // src/utils/hash.ts
2529
+ function hashContent(content) {
2530
+ let h = 2166136261 | 0;
2531
+ for (let i = 0; i < content.length; i++) {
2532
+ h ^= content.charCodeAt(i);
2533
+ h = Math.imul(h, 16777619);
2534
+ }
2535
+ let h2 = 26499749718 | 0;
2536
+ for (let i = 0; i < content.length; i++) {
2537
+ h2 ^= content.charCodeAt(i);
2538
+ h2 = Math.imul(h2, 16777619);
2539
+ }
2540
+ return ((h >>> 0) * 1048576 + (h2 >>> 0)).toString(36) + content.length.toString(36);
2541
+ }
2542
+
2543
+ // src/utils/markdown.ts
2544
+ import chalk from "chalk";
2545
+ import { marked } from "marked";
2546
+ import stripAnsi from "strip-ansi";
2547
+ import { stringWidth as stringWidth2 } from "@claude-code-kit/ink-renderer";
2548
+
2549
+ // src/design-system/color.ts
2550
+ import { colorize } from "@claude-code-kit/ink-renderer";
2551
+ function color(c, theme, type = "foreground") {
2552
+ return (text) => {
2553
+ if (!c) {
2554
+ return text;
2555
+ }
2556
+ if (c.startsWith("rgb(") || c.startsWith("#") || c.startsWith("ansi256(") || c.startsWith("ansi:")) {
2557
+ return colorize(text, c, type);
2558
+ }
2559
+ return colorize(text, getTheme(theme)[c], type);
2560
+ };
2561
+ }
2562
+
2563
+ // src/utils/markdown.ts
2564
+ function logForDebugging3(...args) {
2565
+ if (process.env["DEBUG"]) {
2566
+ console.debug(...args);
2567
+ }
2568
+ }
2569
+ var EOL = "\n";
2570
+ var BLOCKQUOTE_BAR = "\u258E";
2571
+ var OSC8_START = "\x1B]8;;";
2572
+ var OSC8_END = "\x07";
2573
+ function supportsHyperlinks() {
2574
+ const termProgram = process.env["TERM_PROGRAM"];
2575
+ const lcTerminal = process.env["LC_TERMINAL"];
2576
+ const term = process.env["TERM"];
2577
+ const supported = ["ghostty", "Hyper", "kitty", "alacritty", "iTerm.app", "iTerm2"];
2578
+ return !!(termProgram && supported.includes(termProgram)) || !!(lcTerminal && supported.includes(lcTerminal)) || !!term?.includes("kitty");
2579
+ }
2580
+ function createHyperlink(url, content) {
2581
+ if (!supportsHyperlinks()) {
2582
+ return url;
2583
+ }
2584
+ const displayText = content ?? url;
2585
+ const coloredText = chalk.blue(displayText);
2586
+ return `${OSC8_START}${url}${OSC8_END}${coloredText}${OSC8_START}${OSC8_END}`;
2587
+ }
2588
+ var markedConfigured = false;
2589
+ function configureMarked() {
2590
+ if (markedConfigured) return;
2591
+ markedConfigured = true;
2592
+ marked.use({
2593
+ tokenizer: {
2594
+ del() {
2595
+ return void 0;
2008
2596
  }
2009
2597
  }
2010
2598
  });
2011
- return { focusIndex, scrollOffset, visibleOptions, max, total };
2012
- }
2013
- function ScrollHint({ count, direction }) {
2014
- return /* @__PURE__ */ jsxs6(Text7, { dimColor: true, children: [
2015
- " ",
2016
- direction === "up" ? "\u2191" : "\u2193",
2017
- " ",
2018
- count,
2019
- " more"
2020
- ] });
2021
2599
  }
2022
- function Select({
2023
- options,
2024
- defaultValue,
2025
- onChange,
2026
- onCancel,
2600
+ function formatToken(token, theme, listDepth = 0, orderedListNumber = null, parent = null, highlight = null) {
2601
+ switch (token.type) {
2602
+ case "blockquote": {
2603
+ const inner = (token.tokens ?? []).map((_) => formatToken(_, theme, 0, null, null, highlight)).join("");
2604
+ const bar = chalk.dim(BLOCKQUOTE_BAR);
2605
+ return inner.split(EOL).map(
2606
+ (line) => stripAnsi(line).trim() ? `${bar} ${chalk.italic(line)}` : line
2607
+ ).join(EOL);
2608
+ }
2609
+ case "code": {
2610
+ if (!highlight) {
2611
+ return token.text + EOL;
2612
+ }
2613
+ let language = "plaintext";
2614
+ if (token.lang) {
2615
+ if (highlight.supportsLanguage(token.lang)) {
2616
+ language = token.lang;
2617
+ } else {
2618
+ logForDebugging3(
2619
+ `Language not supported while highlighting code, falling back to plaintext: ${token.lang}`
2620
+ );
2621
+ }
2622
+ }
2623
+ return highlight.highlight(token.text, { language }) + EOL;
2624
+ }
2625
+ case "codespan": {
2626
+ return color("permission", theme)(token.text);
2627
+ }
2628
+ case "em":
2629
+ return chalk.italic(
2630
+ (token.tokens ?? []).map((_) => formatToken(_, theme, 0, null, parent, highlight)).join("")
2631
+ );
2632
+ case "strong":
2633
+ return chalk.bold(
2634
+ (token.tokens ?? []).map((_) => formatToken(_, theme, 0, null, parent, highlight)).join("")
2635
+ );
2636
+ case "heading":
2637
+ switch (token.depth) {
2638
+ case 1:
2639
+ return chalk.bold.italic.underline(
2640
+ (token.tokens ?? []).map((_) => formatToken(_, theme, 0, null, null, highlight)).join("")
2641
+ ) + EOL + EOL;
2642
+ case 2:
2643
+ return chalk.bold(
2644
+ (token.tokens ?? []).map((_) => formatToken(_, theme, 0, null, null, highlight)).join("")
2645
+ ) + EOL + EOL;
2646
+ default:
2647
+ return chalk.bold(
2648
+ (token.tokens ?? []).map((_) => formatToken(_, theme, 0, null, null, highlight)).join("")
2649
+ ) + EOL + EOL;
2650
+ }
2651
+ case "hr":
2652
+ return "---";
2653
+ case "image":
2654
+ return token.href;
2655
+ case "link": {
2656
+ if (token.href.startsWith("mailto:")) {
2657
+ const email = token.href.replace(/^mailto:/, "");
2658
+ return email;
2659
+ }
2660
+ const linkText = (token.tokens ?? []).map((_) => formatToken(_, theme, 0, null, token, highlight)).join("");
2661
+ const plainLinkText = stripAnsi(linkText);
2662
+ if (plainLinkText && plainLinkText !== token.href) {
2663
+ return createHyperlink(token.href, linkText);
2664
+ }
2665
+ return createHyperlink(token.href);
2666
+ }
2667
+ case "list": {
2668
+ return token.items.map(
2669
+ (_, index) => formatToken(
2670
+ _,
2671
+ theme,
2672
+ listDepth,
2673
+ token.ordered ? token.start + index : null,
2674
+ token,
2675
+ highlight
2676
+ )
2677
+ ).join("");
2678
+ }
2679
+ case "list_item":
2680
+ return (token.tokens ?? []).map(
2681
+ (_) => `${" ".repeat(listDepth)}${formatToken(_, theme, listDepth + 1, orderedListNumber, token, highlight)}`
2682
+ ).join("");
2683
+ case "paragraph":
2684
+ return (token.tokens ?? []).map((_) => formatToken(_, theme, 0, null, null, highlight)).join("") + EOL;
2685
+ case "space":
2686
+ return EOL;
2687
+ case "br":
2688
+ return EOL;
2689
+ case "text":
2690
+ if (parent?.type === "link") {
2691
+ return token.text;
2692
+ }
2693
+ if (parent?.type === "list_item") {
2694
+ return `${orderedListNumber === null ? "-" : getListNumber(listDepth, orderedListNumber) + "."} ${token.tokens ? token.tokens.map((_) => formatToken(_, theme, listDepth, orderedListNumber, token, highlight)).join("") : linkifyIssueReferences(token.text)}${EOL}`;
2695
+ }
2696
+ return linkifyIssueReferences(token.text);
2697
+ case "table": {
2698
+ let getDisplayText2 = function(tokens) {
2699
+ return stripAnsi(
2700
+ tokens?.map((_) => formatToken(_, theme, 0, null, null, highlight)).join("") ?? ""
2701
+ );
2702
+ };
2703
+ var getDisplayText = getDisplayText2;
2704
+ const tableToken = token;
2705
+ const columnWidths = tableToken.header.map((header, index) => {
2706
+ let maxWidth = stringWidth2(getDisplayText2(header.tokens));
2707
+ for (const row of tableToken.rows) {
2708
+ const cellLength = stringWidth2(getDisplayText2(row[index]?.tokens));
2709
+ maxWidth = Math.max(maxWidth, cellLength);
2710
+ }
2711
+ return Math.max(maxWidth, 3);
2712
+ });
2713
+ let tableOutput = "| ";
2714
+ tableToken.header.forEach((header, index) => {
2715
+ const content = header.tokens?.map((_) => formatToken(_, theme, 0, null, null, highlight)).join("") ?? "";
2716
+ const displayText = getDisplayText2(header.tokens);
2717
+ const width = columnWidths[index];
2718
+ const align = tableToken.align?.[index];
2719
+ tableOutput += padAligned(content, stringWidth2(displayText), width, align) + " | ";
2720
+ });
2721
+ tableOutput = tableOutput.trimEnd() + EOL;
2722
+ tableOutput += "|";
2723
+ columnWidths.forEach((width) => {
2724
+ const separator = "-".repeat(width + 2);
2725
+ tableOutput += separator + "|";
2726
+ });
2727
+ tableOutput += EOL;
2728
+ tableToken.rows.forEach((row) => {
2729
+ tableOutput += "| ";
2730
+ row.forEach((cell, index) => {
2731
+ const content = cell.tokens?.map((_) => formatToken(_, theme, 0, null, null, highlight)).join("") ?? "";
2732
+ const displayText = getDisplayText2(cell.tokens);
2733
+ const width = columnWidths[index];
2734
+ const align = tableToken.align?.[index];
2735
+ tableOutput += padAligned(content, stringWidth2(displayText), width, align) + " | ";
2736
+ });
2737
+ tableOutput = tableOutput.trimEnd() + EOL;
2738
+ });
2739
+ return tableOutput + EOL;
2740
+ }
2741
+ case "escape":
2742
+ return token.text;
2743
+ case "def":
2744
+ case "del":
2745
+ case "html":
2746
+ return "";
2747
+ }
2748
+ return "";
2749
+ }
2750
+ var ISSUE_REF_PATTERN = /(^|[^\w./-])([A-Za-z0-9][\w-]*\/[A-Za-z0-9][\w.-]*)#(\d+)\b/g;
2751
+ function linkifyIssueReferences(text) {
2752
+ if (!supportsHyperlinks()) {
2753
+ return text;
2754
+ }
2755
+ return text.replace(
2756
+ ISSUE_REF_PATTERN,
2757
+ (_match, prefix, repo, num) => prefix + createHyperlink(
2758
+ `https://github.com/${repo}/issues/${num}`,
2759
+ `${repo}#${num}`
2760
+ )
2761
+ );
2762
+ }
2763
+ function numberToLetter(n) {
2764
+ let result = "";
2765
+ while (n > 0) {
2766
+ n--;
2767
+ result = String.fromCharCode(97 + n % 26) + result;
2768
+ n = Math.floor(n / 26);
2769
+ }
2770
+ return result;
2771
+ }
2772
+ var ROMAN_VALUES = [
2773
+ [1e3, "m"],
2774
+ [900, "cm"],
2775
+ [500, "d"],
2776
+ [400, "cd"],
2777
+ [100, "c"],
2778
+ [90, "xc"],
2779
+ [50, "l"],
2780
+ [40, "xl"],
2781
+ [10, "x"],
2782
+ [9, "ix"],
2783
+ [5, "v"],
2784
+ [4, "iv"],
2785
+ [1, "i"]
2786
+ ];
2787
+ function numberToRoman(n) {
2788
+ let result = "";
2789
+ for (const [value, numeral] of ROMAN_VALUES) {
2790
+ while (n >= value) {
2791
+ result += numeral;
2792
+ n -= value;
2793
+ }
2794
+ }
2795
+ return result;
2796
+ }
2797
+ function getListNumber(listDepth, orderedListNumber) {
2798
+ switch (listDepth) {
2799
+ case 0:
2800
+ case 1:
2801
+ return orderedListNumber.toString();
2802
+ case 2:
2803
+ return numberToLetter(orderedListNumber);
2804
+ case 3:
2805
+ return numberToRoman(orderedListNumber);
2806
+ default:
2807
+ return orderedListNumber.toString();
2808
+ }
2809
+ }
2810
+ function padAligned(content, displayWidth, targetWidth, align) {
2811
+ const padding = Math.max(0, targetWidth - displayWidth);
2812
+ if (align === "center") {
2813
+ const leftPad = Math.floor(padding / 2);
2814
+ return " ".repeat(leftPad) + content + " ".repeat(padding - leftPad);
2815
+ }
2816
+ if (align === "right") {
2817
+ return " ".repeat(padding) + content;
2818
+ }
2819
+ return content + " ".repeat(padding);
2820
+ }
2821
+
2822
+ // src/MarkdownTable.tsx
2823
+ import { useContext as useContext5 } from "react";
2824
+ import stripAnsi2 from "strip-ansi";
2825
+ import { TerminalSizeContext as TerminalSizeContext3 } from "@claude-code-kit/ink-renderer";
2826
+ import { stringWidth as stringWidth3 } from "@claude-code-kit/ink-renderer";
2827
+ import { wrapAnsi } from "@claude-code-kit/ink-renderer";
2828
+ import { Ansi as Ansi3 } from "@claude-code-kit/ink-renderer";
2829
+ import { jsx as jsx10 } from "react/jsx-runtime";
2830
+ var SAFETY_MARGIN = 4;
2831
+ var MIN_COLUMN_WIDTH = 3;
2832
+ var MAX_ROW_LINES = 4;
2833
+ var ANSI_BOLD_START = "\x1B[1m";
2834
+ var ANSI_BOLD_END = "\x1B[22m";
2835
+ function wrapText(text, width, options) {
2836
+ if (width <= 0) return [text];
2837
+ const trimmedText = text.trimEnd();
2838
+ const wrapped = wrapAnsi(trimmedText, width, {
2839
+ hard: options?.hard ?? false,
2840
+ trim: false,
2841
+ wordWrap: true
2842
+ });
2843
+ const lines = wrapped.split("\n").filter((line) => line.length > 0);
2844
+ return lines.length > 0 ? lines : [""];
2845
+ }
2846
+ function MarkdownTable({
2847
+ token,
2848
+ highlight,
2849
+ forceWidth
2850
+ }) {
2851
+ const [theme] = useTheme();
2852
+ const terminalSize = useContext5(TerminalSizeContext3);
2853
+ const actualTerminalWidth = terminalSize?.columns ?? 80;
2854
+ const terminalWidth = forceWidth ?? actualTerminalWidth;
2855
+ function formatCell(tokens) {
2856
+ return tokens?.map((_) => formatToken(_, theme, 0, null, null, highlight)).join("") ?? "";
2857
+ }
2858
+ function getPlainText(tokens_0) {
2859
+ return stripAnsi2(formatCell(tokens_0));
2860
+ }
2861
+ function getMinWidth(tokens_1) {
2862
+ const text = getPlainText(tokens_1);
2863
+ const words = text.split(/\s+/).filter((w) => w.length > 0);
2864
+ if (words.length === 0) return MIN_COLUMN_WIDTH;
2865
+ return Math.max(...words.map((w_0) => stringWidth3(w_0)), MIN_COLUMN_WIDTH);
2866
+ }
2867
+ function getIdealWidth(tokens_2) {
2868
+ return Math.max(stringWidth3(getPlainText(tokens_2)), MIN_COLUMN_WIDTH);
2869
+ }
2870
+ const minWidths = token.header.map((header, colIndex) => {
2871
+ let maxMinWidth = getMinWidth(header.tokens);
2872
+ for (const row of token.rows) {
2873
+ maxMinWidth = Math.max(maxMinWidth, getMinWidth(row[colIndex]?.tokens));
2874
+ }
2875
+ return maxMinWidth;
2876
+ });
2877
+ const idealWidths = token.header.map((header_0, colIndex_0) => {
2878
+ let maxIdeal = getIdealWidth(header_0.tokens);
2879
+ for (const row_0 of token.rows) {
2880
+ maxIdeal = Math.max(maxIdeal, getIdealWidth(row_0[colIndex_0]?.tokens));
2881
+ }
2882
+ return maxIdeal;
2883
+ });
2884
+ const numCols = token.header.length;
2885
+ const borderOverhead = 1 + numCols * 3;
2886
+ const availableWidth = Math.max(terminalWidth - borderOverhead - SAFETY_MARGIN, numCols * MIN_COLUMN_WIDTH);
2887
+ const totalMin = minWidths.reduce((sum, w_1) => sum + w_1, 0);
2888
+ const totalIdeal = idealWidths.reduce((sum_0, w_2) => sum_0 + w_2, 0);
2889
+ let needsHardWrap = false;
2890
+ let columnWidths;
2891
+ if (totalIdeal <= availableWidth) {
2892
+ columnWidths = idealWidths;
2893
+ } else if (totalMin <= availableWidth) {
2894
+ const extraSpace = availableWidth - totalMin;
2895
+ const overflows = idealWidths.map((ideal, i) => ideal - minWidths[i]);
2896
+ const totalOverflow = overflows.reduce((sum_1, o) => sum_1 + o, 0);
2897
+ columnWidths = minWidths.map((min, i_0) => {
2898
+ if (totalOverflow === 0) return min;
2899
+ const extra = Math.floor(overflows[i_0] / totalOverflow * extraSpace);
2900
+ return min + extra;
2901
+ });
2902
+ } else {
2903
+ needsHardWrap = true;
2904
+ const scaleFactor = availableWidth / totalMin;
2905
+ columnWidths = minWidths.map((w_3) => Math.max(Math.floor(w_3 * scaleFactor), MIN_COLUMN_WIDTH));
2906
+ }
2907
+ function calculateMaxRowLines() {
2908
+ let maxLines = 1;
2909
+ for (let i_1 = 0; i_1 < token.header.length; i_1++) {
2910
+ const content = formatCell(token.header[i_1].tokens);
2911
+ const wrapped = wrapText(content, columnWidths[i_1], {
2912
+ hard: needsHardWrap
2913
+ });
2914
+ maxLines = Math.max(maxLines, wrapped.length);
2915
+ }
2916
+ for (const row_1 of token.rows) {
2917
+ for (let i_2 = 0; i_2 < row_1.length; i_2++) {
2918
+ const content_0 = formatCell(row_1[i_2]?.tokens);
2919
+ const wrapped_0 = wrapText(content_0, columnWidths[i_2], {
2920
+ hard: needsHardWrap
2921
+ });
2922
+ maxLines = Math.max(maxLines, wrapped_0.length);
2923
+ }
2924
+ }
2925
+ return maxLines;
2926
+ }
2927
+ const maxRowLines = calculateMaxRowLines();
2928
+ const useVerticalFormat = maxRowLines > MAX_ROW_LINES;
2929
+ function renderRowLines(cells, isHeader) {
2930
+ const cellLines = cells.map((cell, colIndex_1) => {
2931
+ const formattedText = formatCell(cell.tokens);
2932
+ const width = columnWidths[colIndex_1];
2933
+ return wrapText(formattedText, width, {
2934
+ hard: needsHardWrap
2935
+ });
2936
+ });
2937
+ const maxLines_0 = Math.max(...cellLines.map((lines) => lines.length), 1);
2938
+ const verticalOffsets = cellLines.map((lines_0) => Math.floor((maxLines_0 - lines_0.length) / 2));
2939
+ const result = [];
2940
+ for (let lineIdx = 0; lineIdx < maxLines_0; lineIdx++) {
2941
+ let line = "\u2502";
2942
+ for (let colIndex_2 = 0; colIndex_2 < cells.length; colIndex_2++) {
2943
+ const lines_1 = cellLines[colIndex_2];
2944
+ const offset = verticalOffsets[colIndex_2];
2945
+ const contentLineIdx = lineIdx - offset;
2946
+ const lineText = contentLineIdx >= 0 && contentLineIdx < lines_1.length ? lines_1[contentLineIdx] : "";
2947
+ const width_0 = columnWidths[colIndex_2];
2948
+ const align = isHeader ? "center" : token.align?.[colIndex_2] ?? "left";
2949
+ line += " " + padAligned(lineText, stringWidth3(lineText), width_0, align) + " \u2502";
2950
+ }
2951
+ result.push(line);
2952
+ }
2953
+ return result;
2954
+ }
2955
+ function renderBorderLine(type) {
2956
+ const [left, mid, cross, right] = {
2957
+ top: ["\u250C", "\u2500", "\u252C", "\u2510"],
2958
+ middle: ["\u251C", "\u2500", "\u253C", "\u2524"],
2959
+ bottom: ["\u2514", "\u2500", "\u2534", "\u2518"]
2960
+ }[type];
2961
+ let line_0 = left;
2962
+ columnWidths.forEach((width_1, colIndex_3) => {
2963
+ line_0 += mid.repeat(width_1 + 2);
2964
+ line_0 += colIndex_3 < columnWidths.length - 1 ? cross : right;
2965
+ });
2966
+ return line_0;
2967
+ }
2968
+ function renderVerticalFormat() {
2969
+ const lines_2 = [];
2970
+ const headers = token.header.map((h) => getPlainText(h.tokens));
2971
+ const separatorWidth = Math.min(terminalWidth - 1, 40);
2972
+ const separator = "\u2500".repeat(separatorWidth);
2973
+ const wrapIndent = " ";
2974
+ token.rows.forEach((row_2, rowIndex) => {
2975
+ if (rowIndex > 0) {
2976
+ lines_2.push(separator);
2977
+ }
2978
+ row_2.forEach((cell_0, colIndex_4) => {
2979
+ const label = headers[colIndex_4] || `Column ${colIndex_4 + 1}`;
2980
+ const rawValue = formatCell(cell_0.tokens).trimEnd();
2981
+ const value = rawValue.replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
2982
+ const firstLineWidth = terminalWidth - stringWidth3(label) - 3;
2983
+ const subsequentLineWidth = terminalWidth - wrapIndent.length - 1;
2984
+ const firstPassLines = wrapText(value, Math.max(firstLineWidth, 10));
2985
+ const firstLine = firstPassLines[0] || "";
2986
+ let wrappedValue;
2987
+ if (firstPassLines.length <= 1 || subsequentLineWidth <= firstLineWidth) {
2988
+ wrappedValue = firstPassLines;
2989
+ } else {
2990
+ const remainingText = firstPassLines.slice(1).map((l) => l.trim()).join(" ");
2991
+ const rewrapped = wrapText(remainingText, subsequentLineWidth);
2992
+ wrappedValue = [firstLine, ...rewrapped];
2993
+ }
2994
+ lines_2.push(`${ANSI_BOLD_START}${label}:${ANSI_BOLD_END} ${wrappedValue[0] || ""}`);
2995
+ for (let i_3 = 1; i_3 < wrappedValue.length; i_3++) {
2996
+ const line_1 = wrappedValue[i_3];
2997
+ if (!line_1.trim()) continue;
2998
+ lines_2.push(`${wrapIndent}${line_1}`);
2999
+ }
3000
+ });
3001
+ });
3002
+ return lines_2.join("\n");
3003
+ }
3004
+ if (useVerticalFormat) {
3005
+ return /* @__PURE__ */ jsx10(Ansi3, { children: renderVerticalFormat() });
3006
+ }
3007
+ const tableLines = [];
3008
+ tableLines.push(renderBorderLine("top"));
3009
+ tableLines.push(...renderRowLines(token.header, true));
3010
+ tableLines.push(renderBorderLine("middle"));
3011
+ token.rows.forEach((row_3, rowIndex_0) => {
3012
+ tableLines.push(...renderRowLines(row_3, false));
3013
+ if (rowIndex_0 < token.rows.length - 1) {
3014
+ tableLines.push(renderBorderLine("middle"));
3015
+ }
3016
+ });
3017
+ tableLines.push(renderBorderLine("bottom"));
3018
+ const maxLineWidth = Math.max(...tableLines.map((line_2) => stringWidth3(stripAnsi2(line_2))));
3019
+ if (maxLineWidth > terminalWidth - SAFETY_MARGIN) {
3020
+ return /* @__PURE__ */ jsx10(Ansi3, { children: renderVerticalFormat() });
3021
+ }
3022
+ return /* @__PURE__ */ jsx10(Ansi3, { children: tableLines.join("\n") });
3023
+ }
3024
+
3025
+ // src/Markdown.tsx
3026
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
3027
+ var TOKEN_CACHE_MAX = 500;
3028
+ var tokenCache = /* @__PURE__ */ new Map();
3029
+ var MD_SYNTAX_RE = /[#*`|[>\-_~]|\n\n|^\d+\. |\n\d+\. /;
3030
+ function hasMarkdownSyntax(s) {
3031
+ return MD_SYNTAX_RE.test(s.length > 500 ? s.slice(0, 500) : s);
3032
+ }
3033
+ function stripPromptXMLTags(content) {
3034
+ return content.replace(/<(commit_analysis|context|function_analysis|pr_analysis)>.*?<\/\1>\n?/gs, "").trim();
3035
+ }
3036
+ function cachedLexer(content) {
3037
+ if (!hasMarkdownSyntax(content)) {
3038
+ return [
3039
+ {
3040
+ type: "paragraph",
3041
+ raw: content,
3042
+ text: content,
3043
+ tokens: [{ type: "text", raw: content, text: content }]
3044
+ }
3045
+ ];
3046
+ }
3047
+ const key = hashContent(content);
3048
+ const hit = tokenCache.get(key);
3049
+ if (hit) {
3050
+ tokenCache.delete(key);
3051
+ tokenCache.set(key, hit);
3052
+ return hit;
3053
+ }
3054
+ const tokens = marked2.lexer(content);
3055
+ if (tokenCache.size >= TOKEN_CACHE_MAX) {
3056
+ const first = tokenCache.keys().next().value;
3057
+ if (first !== void 0) tokenCache.delete(first);
3058
+ }
3059
+ tokenCache.set(key, tokens);
3060
+ return tokens;
3061
+ }
3062
+ function Markdown(props) {
3063
+ const settings = {};
3064
+ if (settings.syntaxHighlightingDisabled) {
3065
+ return /* @__PURE__ */ jsx11(MarkdownBody, { ...props, highlight: null });
3066
+ }
3067
+ return /* @__PURE__ */ jsx11(Suspense, { fallback: /* @__PURE__ */ jsx11(MarkdownBody, { ...props, highlight: null }), children: /* @__PURE__ */ jsx11(MarkdownWithHighlight, { ...props }) });
3068
+ }
3069
+ function MarkdownWithHighlight(props) {
3070
+ const highlight = use(getCliHighlightPromise());
3071
+ return /* @__PURE__ */ jsx11(MarkdownBody, { ...props, highlight });
3072
+ }
3073
+ function MarkdownBody({
3074
+ children,
3075
+ dimColor,
3076
+ highlight
3077
+ }) {
3078
+ const [theme] = useTheme();
3079
+ configureMarked();
3080
+ const elements = useMemo4(() => {
3081
+ const tokens = cachedLexer(stripPromptXMLTags(children));
3082
+ const elements2 = [];
3083
+ let nonTableContent = "";
3084
+ function flushNonTableContent() {
3085
+ if (nonTableContent) {
3086
+ elements2.push(
3087
+ /* @__PURE__ */ jsx11(Ansi4, { dimColor, children: nonTableContent.trim() }, elements2.length)
3088
+ );
3089
+ nonTableContent = "";
3090
+ }
3091
+ }
3092
+ for (const token of tokens) {
3093
+ if (token.type === "table") {
3094
+ flushNonTableContent();
3095
+ elements2.push(
3096
+ /* @__PURE__ */ jsx11(
3097
+ MarkdownTable,
3098
+ {
3099
+ token,
3100
+ highlight
3101
+ },
3102
+ elements2.length
3103
+ )
3104
+ );
3105
+ } else {
3106
+ nonTableContent += formatToken(token, theme, 0, null, null, highlight);
3107
+ }
3108
+ }
3109
+ flushNonTableContent();
3110
+ return elements2;
3111
+ }, [children, dimColor, highlight, theme]);
3112
+ return /* @__PURE__ */ jsx11(Box5, { flexDirection: "column", gap: 1, children: elements });
3113
+ }
3114
+ function StreamingMarkdown({
3115
+ children
3116
+ }) {
3117
+ "use no memo";
3118
+ configureMarked();
3119
+ const stripped = stripPromptXMLTags(children);
3120
+ const stablePrefixRef = useRef4("");
3121
+ if (!stripped.startsWith(stablePrefixRef.current)) {
3122
+ stablePrefixRef.current = "";
3123
+ }
3124
+ const boundary = stablePrefixRef.current.length;
3125
+ const tokens = marked2.lexer(stripped.substring(boundary));
3126
+ let lastContentIdx = tokens.length - 1;
3127
+ while (lastContentIdx >= 0 && tokens[lastContentIdx].type === "space") {
3128
+ lastContentIdx--;
3129
+ }
3130
+ let advance = 0;
3131
+ for (let i = 0; i < lastContentIdx; i++) {
3132
+ advance += tokens[i].raw.length;
3133
+ }
3134
+ if (advance > 0) {
3135
+ stablePrefixRef.current = stripped.substring(0, boundary + advance);
3136
+ }
3137
+ const stablePrefix = stablePrefixRef.current;
3138
+ const unstableSuffix = stripped.substring(stablePrefix.length);
3139
+ return /* @__PURE__ */ jsxs8(Box5, { flexDirection: "column", gap: 1, children: [
3140
+ stablePrefix && /* @__PURE__ */ jsx11(Markdown, { children: stablePrefix }),
3141
+ unstableSuffix && /* @__PURE__ */ jsx11(Markdown, { children: unstableSuffix })
3142
+ ] });
3143
+ }
3144
+
3145
+ // src/Select.tsx
3146
+ import { useState as useState6, useRef as useRef5, useMemo as useMemo5, useCallback as useCallback5 } from "react";
3147
+ import { Box as Box6, Text as Text8, useInput as useInput4 } from "@claude-code-kit/ink-renderer";
3148
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
3149
+ function useListNavigation(opts) {
3150
+ const { options, maxVisible, onCancel, onSelect, extraHandler } = opts;
3151
+ const [focusIndex, setFocusIndex] = useState6(0);
3152
+ const focusRef = useRef5(focusIndex);
3153
+ focusRef.current = focusIndex;
3154
+ const total = options.length;
3155
+ const max = maxVisible ?? total;
3156
+ const scrollOffset = useMemo5(() => {
3157
+ if (total <= max) return 0;
3158
+ const half = Math.floor(max / 2);
3159
+ if (focusIndex <= half) return 0;
3160
+ if (focusIndex >= total - max + half) return total - max;
3161
+ return focusIndex - half;
3162
+ }, [focusIndex, total, max]);
3163
+ const visibleOptions = useMemo5(
3164
+ () => options.slice(scrollOffset, scrollOffset + max),
3165
+ [options, scrollOffset, max]
3166
+ );
3167
+ const moveFocus = useCallback5(
3168
+ (dir) => {
3169
+ setFocusIndex((prev) => {
3170
+ let next = prev;
3171
+ for (let i = 0; i < total; i++) {
3172
+ next = (next + dir + total) % total;
3173
+ if (!options[next].disabled) return next;
3174
+ }
3175
+ return prev;
3176
+ });
3177
+ },
3178
+ [options, total]
3179
+ );
3180
+ useInput4((input, key) => {
3181
+ if (extraHandler?.(input, key, focusRef.current)) return;
3182
+ if (key.upArrow || input === "k") {
3183
+ moveFocus(-1);
3184
+ } else if (key.downArrow || input === "j") {
3185
+ moveFocus(1);
3186
+ } else if (key.return) {
3187
+ if (!options[focusRef.current]?.disabled) {
3188
+ onSelect(focusRef.current);
3189
+ }
3190
+ } else if (key.escape) {
3191
+ onCancel?.();
3192
+ } else if (input >= "1" && input <= "9") {
3193
+ const idx = parseInt(input, 10) - 1;
3194
+ if (idx < total && !options[idx].disabled) {
3195
+ setFocusIndex(idx);
3196
+ onSelect(idx);
3197
+ }
3198
+ }
3199
+ });
3200
+ return { focusIndex, scrollOffset, visibleOptions, max, total };
3201
+ }
3202
+ function ScrollHint({ count, direction }) {
3203
+ return /* @__PURE__ */ jsxs9(Text8, { dimColor: true, children: [
3204
+ " ",
3205
+ direction === "up" ? "\u2191" : "\u2193",
3206
+ " ",
3207
+ count,
3208
+ " more"
3209
+ ] });
3210
+ }
3211
+ function Select({
3212
+ options,
3213
+ defaultValue,
3214
+ onChange,
3215
+ onCancel,
2027
3216
  title,
2028
3217
  maxVisible
2029
3218
  }) {
2030
- const handleSelect = useCallback4(
3219
+ const handleSelect = useCallback5(
2031
3220
  (index) => onChange(options[index].value),
2032
3221
  [onChange, options]
2033
3222
  );
2034
3223
  const { focusIndex, scrollOffset, visibleOptions, max, total } = useListNavigation({ options, maxVisible, onCancel, onSelect: handleSelect });
2035
- return /* @__PURE__ */ jsxs6(Box4, { flexDirection: "column", children: [
2036
- title && /* @__PURE__ */ jsx8(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx8(Text7, { bold: true, children: title }) }),
2037
- scrollOffset > 0 && /* @__PURE__ */ jsx8(ScrollHint, { count: scrollOffset, direction: "up" }),
3224
+ return /* @__PURE__ */ jsxs9(Box6, { flexDirection: "column", children: [
3225
+ title && /* @__PURE__ */ jsx12(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text8, { bold: true, children: title }) }),
3226
+ scrollOffset > 0 && /* @__PURE__ */ jsx12(ScrollHint, { count: scrollOffset, direction: "up" }),
2038
3227
  visibleOptions.map((opt, i) => {
2039
3228
  const realIndex = scrollOffset + i;
2040
3229
  const isFocused = realIndex === focusIndex;
2041
3230
  const isSelected = opt.value === defaultValue;
2042
3231
  const isDisabled = opt.disabled === true;
2043
- return /* @__PURE__ */ jsxs6(Box4, { children: [
2044
- /* @__PURE__ */ jsxs6(Text7, { color: isFocused ? "cyan" : void 0, children: [
3232
+ return /* @__PURE__ */ jsxs9(Box6, { children: [
3233
+ /* @__PURE__ */ jsxs9(Text8, { color: isFocused ? "cyan" : void 0, children: [
2045
3234
  isFocused ? "\u276F" : " ",
2046
3235
  " "
2047
3236
  ] }),
2048
- /* @__PURE__ */ jsxs6(
2049
- Text7,
3237
+ /* @__PURE__ */ jsxs9(
3238
+ Text8,
2050
3239
  {
2051
3240
  color: isDisabled ? "gray" : isFocused ? "cyan" : void 0,
2052
3241
  bold: isFocused,
@@ -2058,15 +3247,15 @@ function Select({
2058
3247
  ]
2059
3248
  }
2060
3249
  ),
2061
- isSelected && /* @__PURE__ */ jsx8(Text7, { color: "green", children: " \u2713" }),
2062
- opt.description && /* @__PURE__ */ jsxs6(Text7, { dimColor: true, children: [
3250
+ isSelected && /* @__PURE__ */ jsx12(Text8, { color: "green", children: " \u2713" }),
3251
+ opt.description && /* @__PURE__ */ jsxs9(Text8, { dimColor: true, children: [
2063
3252
  " ",
2064
3253
  opt.description
2065
3254
  ] })
2066
3255
  ] }, realIndex);
2067
3256
  }),
2068
- scrollOffset + max < total && /* @__PURE__ */ jsx8(ScrollHint, { count: total - scrollOffset - max, direction: "down" }),
2069
- /* @__PURE__ */ jsx8(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "Enter to confirm \xB7 Esc to exit" }) })
3257
+ scrollOffset + max < total && /* @__PURE__ */ jsx12(ScrollHint, { count: total - scrollOffset - max, direction: "down" }),
3258
+ /* @__PURE__ */ jsx12(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text8, { dimColor: true, children: "Enter to confirm \xB7 Esc to exit" }) })
2070
3259
  ] });
2071
3260
  }
2072
3261
  function MultiSelect({
@@ -2078,12 +3267,12 @@ function MultiSelect({
2078
3267
  title,
2079
3268
  maxVisible
2080
3269
  }) {
2081
- const [selected, setSelected] = useState5(() => new Set(selectedValues));
2082
- const handleConfirm = useCallback4(
3270
+ const [selected, setSelected] = useState6(() => new Set(selectedValues));
3271
+ const handleConfirm = useCallback5(
2083
3272
  () => onConfirm(Array.from(selected)),
2084
3273
  [onConfirm, selected]
2085
3274
  );
2086
- const handleSpace = useCallback4(
3275
+ const handleSpace = useCallback5(
2087
3276
  (input, _key, focusIndex2) => {
2088
3277
  if (input !== " ") return false;
2089
3278
  const opt = options[focusIndex2];
@@ -2106,21 +3295,21 @@ function MultiSelect({
2106
3295
  onSelect: handleConfirm,
2107
3296
  extraHandler: handleSpace
2108
3297
  });
2109
- return /* @__PURE__ */ jsxs6(Box4, { flexDirection: "column", children: [
2110
- title && /* @__PURE__ */ jsx8(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx8(Text7, { bold: true, children: title }) }),
2111
- scrollOffset > 0 && /* @__PURE__ */ jsx8(ScrollHint, { count: scrollOffset, direction: "up" }),
3298
+ return /* @__PURE__ */ jsxs9(Box6, { flexDirection: "column", children: [
3299
+ title && /* @__PURE__ */ jsx12(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text8, { bold: true, children: title }) }),
3300
+ scrollOffset > 0 && /* @__PURE__ */ jsx12(ScrollHint, { count: scrollOffset, direction: "up" }),
2112
3301
  visibleOptions.map((opt, i) => {
2113
3302
  const realIndex = scrollOffset + i;
2114
3303
  const isFocused = realIndex === focusIndex;
2115
3304
  const isChecked = selected.has(opt.value);
2116
3305
  const isDisabled = opt.disabled === true;
2117
- return /* @__PURE__ */ jsxs6(Box4, { children: [
2118
- /* @__PURE__ */ jsxs6(Text7, { color: isFocused ? "cyan" : void 0, children: [
3306
+ return /* @__PURE__ */ jsxs9(Box6, { children: [
3307
+ /* @__PURE__ */ jsxs9(Text8, { color: isFocused ? "cyan" : void 0, children: [
2119
3308
  isFocused ? "\u276F" : " ",
2120
3309
  " "
2121
3310
  ] }),
2122
- /* @__PURE__ */ jsxs6(
2123
- Text7,
3311
+ /* @__PURE__ */ jsxs9(
3312
+ Text8,
2124
3313
  {
2125
3314
  color: isDisabled ? "gray" : isFocused ? "cyan" : void 0,
2126
3315
  bold: isFocused,
@@ -2134,25 +3323,263 @@ function MultiSelect({
2134
3323
  ]
2135
3324
  }
2136
3325
  ),
2137
- opt.description && /* @__PURE__ */ jsxs6(Text7, { dimColor: true, children: [
3326
+ opt.description && /* @__PURE__ */ jsxs9(Text8, { dimColor: true, children: [
2138
3327
  " ",
2139
3328
  opt.description
2140
3329
  ] })
2141
3330
  ] }, realIndex);
2142
3331
  }),
2143
- scrollOffset + max < total && /* @__PURE__ */ jsx8(ScrollHint, { count: total - scrollOffset - max, direction: "down" }),
2144
- /* @__PURE__ */ jsx8(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "Space to toggle \xB7 Enter to confirm \xB7 Esc to exit" }) })
3332
+ scrollOffset + max < total && /* @__PURE__ */ jsx12(ScrollHint, { count: total - scrollOffset - max, direction: "down" }),
3333
+ /* @__PURE__ */ jsx12(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text8, { dimColor: true, children: "Space to toggle \xB7 Enter to confirm \xB7 Esc to exit" }) })
3334
+ ] });
3335
+ }
3336
+
3337
+ // src/PermissionRequest.tsx
3338
+ import React12, { useMemo as useMemo6, useCallback as useCallback6, useContext as useContext6 } from "react";
3339
+ import { Box as Box7, Text as Text9, useInput as useInput5, TerminalSizeContext as TerminalSizeContext4 } from "@claude-code-kit/ink-renderer";
3340
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
3341
+ function PermissionHeader({ toolName, width }) {
3342
+ const label = ` ${toolName} `;
3343
+ const labelLen = toolName.length + 2;
3344
+ const leftLen = 3;
3345
+ const rightLen = Math.max(0, width - leftLen - labelLen);
3346
+ return /* @__PURE__ */ jsxs10(Text9, { children: [
3347
+ /* @__PURE__ */ jsx13(Text9, { dimColor: true, children: "\u2500".repeat(leftLen) }),
3348
+ /* @__PURE__ */ jsx13(Text9, { bold: true, color: "cyan", children: label }),
3349
+ /* @__PURE__ */ jsx13(Text9, { dimColor: true, children: "\u2500".repeat(rightLen) })
3350
+ ] });
3351
+ }
3352
+ function HorizontalRule({ width }) {
3353
+ return /* @__PURE__ */ jsx13(Text9, { dimColor: true, children: "\u2500".repeat(width) });
3354
+ }
3355
+ function BashPermissionContent({ command }) {
3356
+ return /* @__PURE__ */ jsxs10(Box7, { flexDirection: "column", children: [
3357
+ /* @__PURE__ */ jsx13(Text9, { children: "Run command:" }),
3358
+ /* @__PURE__ */ jsx13(Box7, { marginLeft: 2, children: /* @__PURE__ */ jsx13(Text9, { color: "yellow", children: command }) })
3359
+ ] });
3360
+ }
3361
+ function FileEditPermissionContent({
3362
+ filename,
3363
+ diff
3364
+ }) {
3365
+ return /* @__PURE__ */ jsxs10(Box7, { flexDirection: "column", children: [
3366
+ /* @__PURE__ */ jsxs10(Text9, { children: [
3367
+ "Edit file: ",
3368
+ /* @__PURE__ */ jsx13(Text9, { color: "cyan", bold: true, children: filename })
3369
+ ] }),
3370
+ diff && /* @__PURE__ */ jsx13(Box7, { marginTop: 1, flexDirection: "column", children: diff.split("\n").map((line, i) => {
3371
+ let color2;
3372
+ if (line.startsWith("+")) color2 = "green";
3373
+ else if (line.startsWith("-")) color2 = "red";
3374
+ else if (line.startsWith("@")) color2 = "cyan";
3375
+ return /* @__PURE__ */ jsx13(Text9, { color: color2, dimColor: !color2 && !line.startsWith("+"), children: line }, i);
3376
+ }) })
3377
+ ] });
3378
+ }
3379
+ function PermissionRequest({
3380
+ toolName,
3381
+ description,
3382
+ details,
3383
+ showAlwaysAllow = true,
3384
+ onDecision,
3385
+ children,
3386
+ preview
3387
+ }) {
3388
+ const terminalSize = useContext6(TerminalSizeContext4);
3389
+ const terminalWidth = Math.min((terminalSize?.columns ?? 80) - 2, 80);
3390
+ const options = useMemo6(() => {
3391
+ const opts = [
3392
+ { value: "allow", label: "Yes, allow this action" }
3393
+ ];
3394
+ if (showAlwaysAllow) {
3395
+ opts.push({ value: "always_allow", label: `Yes, and always allow ${toolName}` });
3396
+ }
3397
+ opts.push({ value: "deny", label: "No, deny" });
3398
+ return opts;
3399
+ }, [showAlwaysAllow, toolName]);
3400
+ const [focusIndex, setFocusIndex] = React12.useState(0);
3401
+ const focusRef = React12.useRef(focusIndex);
3402
+ focusRef.current = focusIndex;
3403
+ const decide = useCallback6(
3404
+ (action) => {
3405
+ onDecision(action);
3406
+ },
3407
+ [onDecision]
3408
+ );
3409
+ useInput5((input, key) => {
3410
+ if (input === "y") {
3411
+ decide("allow");
3412
+ return;
3413
+ }
3414
+ if (input === "a" && showAlwaysAllow) {
3415
+ decide("always_allow");
3416
+ return;
3417
+ }
3418
+ if (input === "n" || key.escape) {
3419
+ decide("deny");
3420
+ return;
3421
+ }
3422
+ if (key.upArrow || input === "k") {
3423
+ setFocusIndex((prev) => (prev - 1 + options.length) % options.length);
3424
+ } else if (key.downArrow || input === "j") {
3425
+ setFocusIndex((prev) => (prev + 1) % options.length);
3426
+ } else if (key.return) {
3427
+ decide(options[focusRef.current].value);
3428
+ } else if (input >= "1" && input <= "9") {
3429
+ const idx = parseInt(input, 10) - 1;
3430
+ if (idx < options.length) {
3431
+ setFocusIndex(idx);
3432
+ decide(options[idx].value);
3433
+ }
3434
+ }
3435
+ });
3436
+ return /* @__PURE__ */ jsxs10(Box7, { flexDirection: "column", children: [
3437
+ /* @__PURE__ */ jsx13(PermissionHeader, { toolName, width: terminalWidth }),
3438
+ /* @__PURE__ */ jsx13(Box7, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: /* @__PURE__ */ jsx13(Text9, { children: description }) }),
3439
+ details && /* @__PURE__ */ jsx13(Box7, { marginTop: 1, marginLeft: 4, children: /* @__PURE__ */ jsx13(Text9, { color: "yellow", children: details }) }),
3440
+ children && /* @__PURE__ */ jsx13(Box7, { marginTop: 1, marginLeft: 2, flexDirection: "column", children }),
3441
+ preview && /* @__PURE__ */ jsx13(Box7, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: preview }),
3442
+ /* @__PURE__ */ jsx13(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx13(HorizontalRule, { width: terminalWidth }) }),
3443
+ /* @__PURE__ */ jsx13(Box7, { marginTop: 1, flexDirection: "column", children: options.map((opt, i) => {
3444
+ const isFocused = i === focusIndex;
3445
+ return /* @__PURE__ */ jsxs10(Box7, { children: [
3446
+ /* @__PURE__ */ jsxs10(Text9, { color: isFocused ? "cyan" : void 0, children: [
3447
+ isFocused ? "\u276F" : " ",
3448
+ " "
3449
+ ] }),
3450
+ /* @__PURE__ */ jsxs10(
3451
+ Text9,
3452
+ {
3453
+ color: isFocused ? "cyan" : void 0,
3454
+ bold: isFocused,
3455
+ children: [
3456
+ i + 1,
3457
+ ". ",
3458
+ opt.label
3459
+ ]
3460
+ }
3461
+ )
3462
+ ] }, opt.value);
3463
+ }) }),
3464
+ /* @__PURE__ */ jsx13(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text9, { dimColor: true, children: "Enter to confirm \xB7 Esc to deny" }) })
2145
3465
  ] });
2146
3466
  }
2147
3467
 
2148
3468
  // src/MessageList.tsx
2149
- import { Box as Box5, Text as Text8 } from "@claude-code-kit/ink-renderer";
2150
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
3469
+ import { useState as useState7 } from "react";
3470
+ import { Box as Box8, Text as Text10 } from "@claude-code-kit/ink-renderer";
3471
+ import { Fragment, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
2151
3472
  var ROLE_CONFIG = {
2152
3473
  user: { icon: "\u276F", label: "You", color: "cyan" },
2153
3474
  assistant: { icon: "\u25CF", label: "Claude", color: "#DA7756" },
2154
3475
  system: { icon: "\u273B", label: "System", color: void 0 }
2155
3476
  };
3477
+ var GUTTER = "\u23BF";
3478
+ function TextBlock({ text, dim }) {
3479
+ return /* @__PURE__ */ jsx14(Fragment, { children: text.split("\n").map((line, i) => /* @__PURE__ */ jsx14(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx14(Text10, { dimColor: dim, children: line }) }, i)) });
3480
+ }
3481
+ function ToolUseBlock({ content }) {
3482
+ const statusColor = content.status === "error" ? "red" : content.status === "success" ? "green" : void 0;
3483
+ return /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", marginLeft: 2, children: [
3484
+ /* @__PURE__ */ jsxs11(Box8, { children: [
3485
+ /* @__PURE__ */ jsxs11(Text10, { dimColor: true, children: [
3486
+ GUTTER,
3487
+ " "
3488
+ ] }),
3489
+ /* @__PURE__ */ jsx14(Text10, { bold: true, children: content.toolName })
3490
+ ] }),
3491
+ content.input.split("\n").map((line, i) => /* @__PURE__ */ jsx14(Box8, { marginLeft: 4, children: /* @__PURE__ */ jsx14(Text10, { dimColor: true, children: line }) }, i)),
3492
+ content.status === "running" && /* @__PURE__ */ jsx14(Box8, { marginLeft: 4, children: /* @__PURE__ */ jsx14(Spinner, { label: content.toolName, showElapsed: true }) }),
3493
+ content.result != null && /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", marginLeft: 4, children: [
3494
+ /* @__PURE__ */ jsxs11(Box8, { children: [
3495
+ /* @__PURE__ */ jsxs11(Text10, { dimColor: true, children: [
3496
+ GUTTER,
3497
+ " "
3498
+ ] }),
3499
+ /* @__PURE__ */ jsxs11(Text10, { color: statusColor, children: [
3500
+ "result (",
3501
+ content.status ?? "done",
3502
+ ")"
3503
+ ] })
3504
+ ] }),
3505
+ content.result.split("\n").map((line, i) => /* @__PURE__ */ jsx14(Box8, { marginLeft: 6, children: /* @__PURE__ */ jsx14(Text10, { color: statusColor, dimColor: !statusColor, children: line }) }, i))
3506
+ ] })
3507
+ ] });
3508
+ }
3509
+ function ThinkingBlock({ content }) {
3510
+ const [collapsed, setCollapsed] = useState7(content.collapsed ?? true);
3511
+ return /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", marginLeft: 2, children: [
3512
+ /* @__PURE__ */ jsxs11(Box8, { onClick: () => setCollapsed((c) => !c), children: [
3513
+ /* @__PURE__ */ jsxs11(Text10, { color: "#DA7756", children: [
3514
+ "\u273B",
3515
+ " "
3516
+ ] }),
3517
+ /* @__PURE__ */ jsxs11(Text10, { dimColor: true, children: [
3518
+ "Thinking...",
3519
+ collapsed ? " (click to expand)" : ""
3520
+ ] })
3521
+ ] }),
3522
+ !collapsed && content.text.split("\n").map((line, i) => /* @__PURE__ */ jsx14(Box8, { marginLeft: 4, children: /* @__PURE__ */ jsx14(Text10, { dimColor: true, children: line }) }, i))
3523
+ ] });
3524
+ }
3525
+ function DiffBlock({ content }) {
3526
+ return /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", marginLeft: 2, children: [
3527
+ /* @__PURE__ */ jsxs11(Box8, { children: [
3528
+ /* @__PURE__ */ jsxs11(Text10, { dimColor: true, children: [
3529
+ GUTTER,
3530
+ " "
3531
+ ] }),
3532
+ /* @__PURE__ */ jsx14(Text10, { bold: true, children: content.filename })
3533
+ ] }),
3534
+ content.diff.split("\n").map((line, i) => {
3535
+ let color2;
3536
+ if (line.startsWith("+")) color2 = "green";
3537
+ else if (line.startsWith("-")) color2 = "red";
3538
+ else if (line.startsWith("@")) color2 = "cyan";
3539
+ return /* @__PURE__ */ jsx14(Box8, { marginLeft: 4, children: /* @__PURE__ */ jsx14(Text10, { color: color2, dimColor: !color2, children: line }) }, i);
3540
+ })
3541
+ ] });
3542
+ }
3543
+ function CodeBlock({ content }) {
3544
+ return /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", marginLeft: 2, children: [
3545
+ /* @__PURE__ */ jsxs11(Text10, { dimColor: true, children: [
3546
+ "```",
3547
+ content.language ?? ""
3548
+ ] }),
3549
+ content.code.split("\n").map((line, i) => /* @__PURE__ */ jsx14(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx14(Text10, { children: line }) }, i)),
3550
+ /* @__PURE__ */ jsx14(Text10, { dimColor: true, children: "```" })
3551
+ ] });
3552
+ }
3553
+ function ErrorBlock({ content }) {
3554
+ return /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", marginLeft: 2, children: [
3555
+ /* @__PURE__ */ jsxs11(Box8, { children: [
3556
+ /* @__PURE__ */ jsxs11(Text10, { color: "red", children: [
3557
+ "\u2716",
3558
+ " Error: "
3559
+ ] }),
3560
+ /* @__PURE__ */ jsx14(Text10, { color: "red", children: content.message })
3561
+ ] }),
3562
+ content.details?.split("\n").map((line, i) => /* @__PURE__ */ jsx14(Box8, { marginLeft: 4, children: /* @__PURE__ */ jsx14(Text10, { color: "red", dimColor: true, children: line }) }, i))
3563
+ ] });
3564
+ }
3565
+ function ContentBlock({ block }) {
3566
+ switch (block.type) {
3567
+ case "text":
3568
+ return /* @__PURE__ */ jsx14(TextBlock, { text: block.text });
3569
+ case "tool_use":
3570
+ return /* @__PURE__ */ jsx14(ToolUseBlock, { content: block });
3571
+ case "thinking":
3572
+ return /* @__PURE__ */ jsx14(ThinkingBlock, { content: block });
3573
+ case "diff":
3574
+ return /* @__PURE__ */ jsx14(DiffBlock, { content: block });
3575
+ case "code":
3576
+ return /* @__PURE__ */ jsx14(CodeBlock, { content: block });
3577
+ case "error":
3578
+ return /* @__PURE__ */ jsx14(ErrorBlock, { content: block });
3579
+ default:
3580
+ return null;
3581
+ }
3582
+ }
2156
3583
  function MessageItem({
2157
3584
  message,
2158
3585
  renderMessage
@@ -2162,15 +3589,27 @@ function MessageItem({
2162
3589
  }
2163
3590
  const config = ROLE_CONFIG[message.role];
2164
3591
  const isSystem = message.role === "system";
2165
- return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
2166
- /* @__PURE__ */ jsxs7(Box5, { children: [
2167
- /* @__PURE__ */ jsx9(Text8, { color: config.color, dimColor: isSystem, children: config.icon }),
2168
- /* @__PURE__ */ jsxs7(Text8, { color: config.color, dimColor: isSystem, bold: !isSystem, children: [
3592
+ if (typeof message.content === "string") {
3593
+ return /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", children: [
3594
+ /* @__PURE__ */ jsxs11(Box8, { children: [
3595
+ /* @__PURE__ */ jsx14(Text10, { color: config.color, dimColor: isSystem, children: config.icon }),
3596
+ /* @__PURE__ */ jsxs11(Text10, { color: config.color, dimColor: isSystem, bold: !isSystem, children: [
3597
+ " ",
3598
+ config.label
3599
+ ] })
3600
+ ] }),
3601
+ message.content.split("\n").map((line, i) => /* @__PURE__ */ jsx14(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx14(Text10, { dimColor: isSystem, children: line }) }, i))
3602
+ ] });
3603
+ }
3604
+ return /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", children: [
3605
+ /* @__PURE__ */ jsxs11(Box8, { children: [
3606
+ /* @__PURE__ */ jsx14(Text10, { color: config.color, dimColor: isSystem, children: config.icon }),
3607
+ /* @__PURE__ */ jsxs11(Text10, { color: config.color, dimColor: isSystem, bold: !isSystem, children: [
2169
3608
  " ",
2170
3609
  config.label
2171
3610
  ] })
2172
3611
  ] }),
2173
- message.content.split("\n").map((line, i) => /* @__PURE__ */ jsx9(Box5, { marginLeft: 2, children: /* @__PURE__ */ jsx9(Text8, { dimColor: isSystem, children: line }) }, i))
3612
+ message.content.map((block, i) => /* @__PURE__ */ jsx14(ContentBlock, { block }, i))
2174
3613
  ] });
2175
3614
  }
2176
3615
  function MessageList({
@@ -2178,39 +3617,36 @@ function MessageList({
2178
3617
  streamingContent,
2179
3618
  renderMessage
2180
3619
  }) {
2181
- return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
2182
- messages.map((message, i) => /* @__PURE__ */ jsx9(Box5, { flexDirection: "column", marginTop: i > 0 ? 1 : 0, children: /* @__PURE__ */ jsx9(MessageItem, { message, renderMessage }) }, message.id)),
2183
- streamingContent != null && streamingContent.length > 0 && /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", marginTop: messages.length > 0 ? 1 : 0, children: [
2184
- /* @__PURE__ */ jsxs7(Box5, { children: [
2185
- /* @__PURE__ */ jsx9(Text8, { color: "#DA7756", children: "\u25CF" }),
2186
- /* @__PURE__ */ jsxs7(Text8, { color: "#DA7756", bold: true, children: [
2187
- " ",
2188
- "Claude"
2189
- ] })
3620
+ return /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", children: [
3621
+ messages.map((message, i) => /* @__PURE__ */ jsx14(Box8, { flexDirection: "column", marginTop: i > 0 ? 1 : 0, children: /* @__PURE__ */ jsx14(MessageItem, { message, renderMessage }) }, message.id)),
3622
+ streamingContent != null && streamingContent.length > 0 && /* @__PURE__ */ jsxs11(Box8, { flexDirection: "column", marginTop: messages.length > 0 ? 1 : 0, children: [
3623
+ /* @__PURE__ */ jsxs11(Box8, { children: [
3624
+ /* @__PURE__ */ jsx14(Text10, { color: "#DA7756", children: "\u25CF" }),
3625
+ /* @__PURE__ */ jsx14(Text10, { color: "#DA7756", bold: true, children: " Claude" })
2190
3626
  ] }),
2191
- streamingContent.split("\n").map((line, i) => /* @__PURE__ */ jsx9(Box5, { marginLeft: 2, children: /* @__PURE__ */ jsxs7(Text8, { children: [
3627
+ streamingContent.split("\n").map((line, i) => /* @__PURE__ */ jsx14(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsxs11(Text10, { children: [
2192
3628
  line,
2193
- i === streamingContent.split("\n").length - 1 && /* @__PURE__ */ jsx9(Text8, { color: "#DA7756", children: "\u2588" })
3629
+ i === streamingContent.split("\n").length - 1 && /* @__PURE__ */ jsx14(Text10, { color: "#DA7756", children: "\u2588" })
2194
3630
  ] }) }, i))
2195
3631
  ] })
2196
3632
  ] });
2197
3633
  }
2198
3634
 
2199
3635
  // src/StreamingText.tsx
2200
- import { useState as useState6, useEffect as useEffect5, useRef as useRef4 } from "react";
2201
- import { Text as Text9 } from "@claude-code-kit/ink-renderer";
2202
- import { jsx as jsx10 } from "react/jsx-runtime";
3636
+ import { useState as useState8, useEffect as useEffect6, useRef as useRef6 } from "react";
3637
+ import { Text as Text11 } from "@claude-code-kit/ink-renderer";
3638
+ import { jsx as jsx15 } from "react/jsx-runtime";
2203
3639
  function StreamingText({
2204
3640
  text,
2205
3641
  speed = 3,
2206
3642
  interval = 20,
2207
3643
  onComplete,
2208
- color
3644
+ color: color2
2209
3645
  }) {
2210
- const [revealed, setRevealed] = useState6(0);
2211
- const onCompleteRef = useRef4(onComplete);
3646
+ const [revealed, setRevealed] = useState8(0);
3647
+ const onCompleteRef = useRef6(onComplete);
2212
3648
  onCompleteRef.current = onComplete;
2213
- useEffect5(() => {
3649
+ useEffect6(() => {
2214
3650
  if (revealed >= text.length) return;
2215
3651
  const id = setInterval(() => {
2216
3652
  setRevealed((prev) => {
@@ -2223,19 +3659,171 @@ function StreamingText({
2223
3659
  }, interval);
2224
3660
  return () => clearInterval(id);
2225
3661
  }, [text.length, speed, interval, revealed >= text.length]);
2226
- return /* @__PURE__ */ jsx10(Text9, { color, children: text.slice(0, revealed) });
3662
+ return /* @__PURE__ */ jsx15(Text11, { color: color2, children: text.slice(0, revealed) });
2227
3663
  }
2228
3664
 
2229
3665
  // src/REPL.tsx
2230
- import { useState as useState7, useCallback as useCallback5, useRef as useRef5 } from "react";
2231
- import { Box as Box6, useInput as useInput5, useApp } from "@claude-code-kit/ink-renderer";
2232
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
3666
+ import { useState as useState10, useCallback as useCallback8, useRef as useRef8 } from "react";
3667
+ import { Box as Box10, useInput as useInput7, useApp } from "@claude-code-kit/ink-renderer";
3668
+
3669
+ // src/SearchOverlay.tsx
3670
+ import { useState as useState9, useCallback as useCallback7, useEffect as useEffect7, useRef as useRef7 } from "react";
3671
+ import { Box as Box9, Text as Text12, useInput as useInput6 } from "@claude-code-kit/ink-renderer";
3672
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
3673
+ function computeMatches(content, query) {
3674
+ if (!query) return [];
3675
+ const lower = query.toLowerCase();
3676
+ const found = [];
3677
+ for (let i = 0; i < content.length; i++) {
3678
+ const text = content[i] ?? "";
3679
+ let offset = 0;
3680
+ let pos = text.toLowerCase().indexOf(lower, offset);
3681
+ while (pos !== -1) {
3682
+ found.push({ index: i, offset: pos, length: query.length });
3683
+ offset = pos + 1;
3684
+ pos = text.toLowerCase().indexOf(lower, offset);
3685
+ }
3686
+ }
3687
+ return found;
3688
+ }
3689
+ function useSearch({ content, isActive }) {
3690
+ const [query, setQueryState] = useState9("");
3691
+ const [matches, setMatches] = useState9([]);
3692
+ const [currentIndex, setCurrentIndex] = useState9(0);
3693
+ const setQuery = useCallback7((q) => {
3694
+ setQueryState(q);
3695
+ const found = computeMatches(content, q);
3696
+ setMatches(found);
3697
+ setCurrentIndex(found.length > 0 ? 0 : -1);
3698
+ }, [content]);
3699
+ const next = useCallback7(() => {
3700
+ if (matches.length === 0) return;
3701
+ setCurrentIndex((i) => (i + 1) % matches.length);
3702
+ }, [matches.length]);
3703
+ const previous = useCallback7(() => {
3704
+ if (matches.length === 0) return;
3705
+ setCurrentIndex((i) => (i - 1 + matches.length) % matches.length);
3706
+ }, [matches.length]);
3707
+ useEffect7(() => {
3708
+ if (!isActive) {
3709
+ setQueryState("");
3710
+ setMatches([]);
3711
+ setCurrentIndex(0);
3712
+ }
3713
+ }, [isActive]);
3714
+ return { query, matches, currentIndex, next, previous, setQuery };
3715
+ }
3716
+ function SearchOverlay({
3717
+ isOpen,
3718
+ onClose,
3719
+ onSearch,
3720
+ onNavigate
3721
+ }) {
3722
+ const [query, setQueryState] = useState9("");
3723
+ const [cursor, setCursor] = useState9(0);
3724
+ const [matches, setMatches] = useState9([]);
3725
+ const matchIndexRef = useRef7(0);
3726
+ const [matchIndex, setMatchIndexState] = useState9(0);
3727
+ const setMatchIndex = useCallback7((next) => {
3728
+ matchIndexRef.current = next;
3729
+ setMatchIndexState(next);
3730
+ }, []);
3731
+ useEffect7(() => {
3732
+ if (!isOpen) {
3733
+ setQueryState("");
3734
+ setCursor(0);
3735
+ setMatches([]);
3736
+ setMatchIndex(0);
3737
+ }
3738
+ }, [isOpen, setMatchIndex]);
3739
+ const runSearch = useCallback7((q) => {
3740
+ const found = onSearch(q);
3741
+ setMatches(found);
3742
+ setMatchIndex(0);
3743
+ if (found.length > 0) onNavigate(found[0]);
3744
+ }, [onSearch, onNavigate, setMatchIndex]);
3745
+ const navigate = useCallback7((delta) => {
3746
+ setMatches((currentMatches) => {
3747
+ if (currentMatches.length === 0) return currentMatches;
3748
+ const next = (matchIndexRef.current + delta + currentMatches.length) % currentMatches.length;
3749
+ setMatchIndex(next);
3750
+ onNavigate(currentMatches[next]);
3751
+ return currentMatches;
3752
+ });
3753
+ }, [onNavigate, setMatchIndex]);
3754
+ useInput6(
3755
+ (input, key) => {
3756
+ if (key.escape) {
3757
+ onClose();
3758
+ return;
3759
+ }
3760
+ if (key.return) {
3761
+ navigate(1);
3762
+ return;
3763
+ }
3764
+ if (input === "n" && key.ctrl) {
3765
+ navigate(1);
3766
+ return;
3767
+ }
3768
+ if (input === "p" && key.ctrl) {
3769
+ navigate(-1);
3770
+ return;
3771
+ }
3772
+ if (key.backspace) {
3773
+ if (cursor > 0) {
3774
+ const next = query.slice(0, cursor - 1) + query.slice(cursor);
3775
+ setQueryState(next);
3776
+ setCursor(cursor - 1);
3777
+ runSearch(next);
3778
+ }
3779
+ return;
3780
+ }
3781
+ if (key.leftArrow) {
3782
+ setCursor((c) => Math.max(0, c - 1));
3783
+ return;
3784
+ }
3785
+ if (key.rightArrow) {
3786
+ setCursor((c) => Math.min(query.length, c + 1));
3787
+ return;
3788
+ }
3789
+ if (key.ctrl || key.meta) return;
3790
+ if (input.length > 0) {
3791
+ const next = query.slice(0, cursor) + input + query.slice(cursor);
3792
+ setQueryState(next);
3793
+ setCursor(cursor + input.length);
3794
+ runSearch(next);
3795
+ }
3796
+ },
3797
+ { isActive: isOpen }
3798
+ );
3799
+ if (!isOpen) return null;
3800
+ const total = matches.length;
3801
+ const current = total > 0 ? matchIndex + 1 : 0;
3802
+ const countLabel = total > 0 ? `${current}/${total} matches` : "no matches";
3803
+ const before = query.slice(0, cursor);
3804
+ const atCursor = cursor < query.length ? query[cursor] : " ";
3805
+ const after = cursor < query.length ? query.slice(cursor + 1) : "";
3806
+ return /* @__PURE__ */ jsxs12(Box9, { flexDirection: "row", paddingX: 1, children: [
3807
+ /* @__PURE__ */ jsx16(Text12, { color: "cyan", children: "Search: " }),
3808
+ /* @__PURE__ */ jsx16(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsxs12(Text12, { children: [
3809
+ before,
3810
+ /* @__PURE__ */ jsx16(Text12, { inverse: true, children: atCursor }),
3811
+ after
3812
+ ] }) }),
3813
+ /* @__PURE__ */ jsx16(Text12, { dimColor: true, children: countLabel })
3814
+ ] });
3815
+ }
3816
+
3817
+ // src/REPL.tsx
3818
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
2233
3819
  function REPL({
2234
3820
  onSubmit,
2235
3821
  onExit,
2236
3822
  messages,
2237
3823
  isLoading = false,
2238
3824
  streamingContent,
3825
+ welcome,
3826
+ permissionRequest,
2239
3827
  commands = [],
2240
3828
  model,
2241
3829
  statusSegments,
@@ -2246,15 +3834,19 @@ function REPL({
2246
3834
  spinner
2247
3835
  }) {
2248
3836
  const { exit } = useApp();
2249
- const [inputValue, setInputValue] = useState7("");
2250
- const [internalHistory, setInternalHistory] = useState7([]);
2251
- const submittingRef = useRef5(false);
3837
+ const [inputValue, setInputValue] = useState10("");
3838
+ const [internalHistory, setInternalHistory] = useState10([]);
3839
+ const [searchOpen, setSearchOpen] = useState10(false);
3840
+ const submittingRef = useRef8(false);
2252
3841
  const history = externalHistory ?? internalHistory;
3842
+ const messageContents = messages.map(
3843
+ (m) => typeof m.content === "string" ? m.content : m.content.map((b) => "text" in b ? b.text : "").join(" ")
3844
+ );
2253
3845
  const promptCommands = commands.map((c) => ({
2254
3846
  name: c.name,
2255
3847
  description: c.description
2256
3848
  }));
2257
- const handleSubmit = useCallback5(
3849
+ const handleSubmit = useCallback8(
2258
3850
  (value) => {
2259
3851
  if (submittingRef.current) return;
2260
3852
  const trimmed = value.trim();
@@ -2286,7 +3878,7 @@ function REPL({
2286
3878
  },
2287
3879
  [commands, onSubmit, externalHistory]
2288
3880
  );
2289
- useInput5(
3881
+ useInput7(
2290
3882
  (_input, key) => {
2291
3883
  if (key.ctrl && _input === "c" && isLoading) {
2292
3884
  return;
@@ -2298,13 +3890,20 @@ function REPL({
2298
3890
  exit();
2299
3891
  }
2300
3892
  }
3893
+ if (key.ctrl && _input === "f") {
3894
+ setSearchOpen(true);
3895
+ }
2301
3896
  },
2302
- { isActive: true }
3897
+ // Deactivate when search overlay is open so only SearchOverlay handles input.
3898
+ { isActive: !searchOpen }
2303
3899
  );
2304
3900
  const resolvedSegments = statusSegments ?? buildDefaultSegments(model);
2305
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", flexGrow: 1, children: [
2306
- /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", flexGrow: 1, children: [
2307
- /* @__PURE__ */ jsx11(
3901
+ const showWelcome = welcome && messages.length === 0 && !isLoading;
3902
+ const showPermission = !!permissionRequest;
3903
+ return /* @__PURE__ */ jsxs13(Box10, { flexDirection: "column", flexGrow: 1, children: [
3904
+ /* @__PURE__ */ jsxs13(Box10, { flexDirection: "column", flexGrow: 1, children: [
3905
+ showWelcome && /* @__PURE__ */ jsx17(Box10, { marginBottom: 1, children: welcome }),
3906
+ /* @__PURE__ */ jsx17(
2308
3907
  MessageList,
2309
3908
  {
2310
3909
  messages,
@@ -2312,10 +3911,29 @@ function REPL({
2312
3911
  renderMessage
2313
3912
  }
2314
3913
  ),
2315
- isLoading && !streamingContent && /* @__PURE__ */ jsx11(Box6, { marginTop: messages.length > 0 ? 1 : 0, children: spinner ?? /* @__PURE__ */ jsx11(Spinner, {}) })
3914
+ isLoading && !streamingContent && /* @__PURE__ */ jsx17(Box10, { marginTop: messages.length > 0 ? 1 : 0, children: spinner ?? /* @__PURE__ */ jsx17(Spinner, {}) })
2316
3915
  ] }),
2317
- /* @__PURE__ */ jsx11(Divider, {}),
2318
- /* @__PURE__ */ jsx11(
3916
+ searchOpen && /* @__PURE__ */ jsx17(
3917
+ SearchOverlay,
3918
+ {
3919
+ isOpen: searchOpen,
3920
+ onClose: () => setSearchOpen(false),
3921
+ onSearch: (q) => computeMatches(messageContents, q),
3922
+ onNavigate: () => {
3923
+ }
3924
+ }
3925
+ ),
3926
+ /* @__PURE__ */ jsx17(Divider, {}),
3927
+ showPermission ? /* @__PURE__ */ jsx17(
3928
+ PermissionRequest,
3929
+ {
3930
+ toolName: permissionRequest.toolName,
3931
+ description: permissionRequest.description,
3932
+ details: permissionRequest.details,
3933
+ preview: permissionRequest.preview,
3934
+ onDecision: permissionRequest.onDecision
3935
+ }
3936
+ ) : /* @__PURE__ */ jsx17(
2319
3937
  PromptInput,
2320
3938
  {
2321
3939
  value: inputValue,
@@ -2323,42 +3941,1376 @@ function REPL({
2323
3941
  onSubmit: handleSubmit,
2324
3942
  prefix,
2325
3943
  placeholder,
2326
- disabled: isLoading,
3944
+ disabled: isLoading || searchOpen,
2327
3945
  commands: promptCommands,
2328
3946
  history
2329
3947
  }
2330
3948
  ),
2331
- /* @__PURE__ */ jsx11(Divider, {}),
2332
- resolvedSegments.length > 0 && /* @__PURE__ */ jsx11(StatusLine, { segments: resolvedSegments })
3949
+ /* @__PURE__ */ jsx17(Divider, {}),
3950
+ resolvedSegments.length > 0 && /* @__PURE__ */ jsx17(StatusLine, { segments: resolvedSegments })
2333
3951
  ] });
2334
3952
  }
2335
3953
  function buildDefaultSegments(model) {
2336
3954
  if (!model) return [];
2337
3955
  return [{ content: model, color: "green" }];
2338
3956
  }
3957
+
3958
+ // src/design-system/ThemedBox.tsx
3959
+ import { Box as Box11 } from "@claude-code-kit/ink-renderer";
3960
+ import { jsx as jsx18 } from "react/jsx-runtime";
3961
+ function resolveColor(color2, theme) {
3962
+ if (!color2) return void 0;
3963
+ if (color2.startsWith("rgb(") || color2.startsWith("#") || color2.startsWith("ansi256(") || color2.startsWith("ansi:")) {
3964
+ return color2;
3965
+ }
3966
+ return theme[color2];
3967
+ }
3968
+ function ThemedBox({
3969
+ borderColor,
3970
+ borderTopColor,
3971
+ borderBottomColor,
3972
+ borderLeftColor,
3973
+ borderRightColor,
3974
+ backgroundColor,
3975
+ children,
3976
+ ref,
3977
+ ...rest
3978
+ }) {
3979
+ const [themeName] = useTheme();
3980
+ const theme = getTheme(themeName);
3981
+ const resolvedBorderColor = resolveColor(borderColor, theme);
3982
+ const resolvedBorderTopColor = resolveColor(borderTopColor, theme);
3983
+ const resolvedBorderBottomColor = resolveColor(borderBottomColor, theme);
3984
+ const resolvedBorderLeftColor = resolveColor(borderLeftColor, theme);
3985
+ const resolvedBorderRightColor = resolveColor(borderRightColor, theme);
3986
+ const resolvedBackgroundColor = resolveColor(backgroundColor, theme);
3987
+ return /* @__PURE__ */ jsx18(
3988
+ Box11,
3989
+ {
3990
+ ref,
3991
+ borderColor: resolvedBorderColor,
3992
+ borderTopColor: resolvedBorderTopColor,
3993
+ borderBottomColor: resolvedBorderBottomColor,
3994
+ borderLeftColor: resolvedBorderLeftColor,
3995
+ borderRightColor: resolvedBorderRightColor,
3996
+ backgroundColor: resolvedBackgroundColor,
3997
+ ...rest,
3998
+ children
3999
+ }
4000
+ );
4001
+ }
4002
+ var ThemedBox_default = ThemedBox;
4003
+
4004
+ // src/design-system/ThemedText.tsx
4005
+ import React17, { useContext as useContext7 } from "react";
4006
+ import { Text as Text13 } from "@claude-code-kit/ink-renderer";
4007
+ import { jsx as jsx19 } from "react/jsx-runtime";
4008
+ var TextHoverColorContext = React17.createContext(void 0);
4009
+ function resolveColor2(color2, theme) {
4010
+ if (!color2) return void 0;
4011
+ if (color2.startsWith("rgb(") || color2.startsWith("#") || color2.startsWith("ansi256(") || color2.startsWith("ansi:")) {
4012
+ return color2;
4013
+ }
4014
+ return theme[color2];
4015
+ }
4016
+ function ThemedText({
4017
+ color: color2,
4018
+ backgroundColor,
4019
+ dimColor = false,
4020
+ bold = false,
4021
+ italic = false,
4022
+ underline = false,
4023
+ strikethrough = false,
4024
+ inverse = false,
4025
+ wrap = "wrap",
4026
+ children
4027
+ }) {
4028
+ const [themeName] = useTheme();
4029
+ const theme = getTheme(themeName);
4030
+ const hoverColor = useContext7(TextHoverColorContext);
4031
+ const resolvedColor = !color2 && hoverColor ? resolveColor2(hoverColor, theme) : dimColor ? theme.inactive : resolveColor2(color2, theme);
4032
+ const resolvedBackgroundColor = backgroundColor ? theme[backgroundColor] : void 0;
4033
+ return /* @__PURE__ */ jsx19(
4034
+ Text13,
4035
+ {
4036
+ color: resolvedColor,
4037
+ backgroundColor: resolvedBackgroundColor,
4038
+ bold,
4039
+ italic,
4040
+ underline,
4041
+ strikethrough,
4042
+ inverse,
4043
+ wrap,
4044
+ children
4045
+ }
4046
+ );
4047
+ }
4048
+
4049
+ // src/design-system/Dialog.tsx
4050
+ import { useCallback as useCallback9 } from "react";
4051
+ import { Box as Box13, Text as Text16, useInput as useInput8 } from "@claude-code-kit/ink-renderer";
4052
+
4053
+ // src/design-system/Byline.tsx
4054
+ import React18, { Children, isValidElement } from "react";
4055
+ import { Text as Text14 } from "@claude-code-kit/ink-renderer";
4056
+ import { Fragment as Fragment2, jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
4057
+ function Byline({ children }) {
4058
+ const validChildren = Children.toArray(children);
4059
+ if (validChildren.length === 0) {
4060
+ return null;
4061
+ }
4062
+ return /* @__PURE__ */ jsx20(Fragment2, { children: validChildren.map((child, index) => /* @__PURE__ */ jsxs14(
4063
+ React18.Fragment,
4064
+ {
4065
+ children: [
4066
+ index > 0 && /* @__PURE__ */ jsx20(Text14, { dimColor: true, children: " \xB7 " }),
4067
+ child
4068
+ ]
4069
+ },
4070
+ isValidElement(child) ? child.key ?? index : index
4071
+ )) });
4072
+ }
4073
+
4074
+ // src/design-system/KeyboardShortcutHint.tsx
4075
+ import { Text as Text15 } from "@claude-code-kit/ink-renderer";
4076
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
4077
+ function KeyboardShortcutHint({
4078
+ shortcut,
4079
+ action,
4080
+ parens = false,
4081
+ bold = false
4082
+ }) {
4083
+ const shortcutText = bold ? /* @__PURE__ */ jsx21(Text15, { bold: true, children: shortcut }) : shortcut;
4084
+ if (parens) {
4085
+ return /* @__PURE__ */ jsxs15(Text15, { children: [
4086
+ "(",
4087
+ shortcutText,
4088
+ " to ",
4089
+ action,
4090
+ ")"
4091
+ ] });
4092
+ }
4093
+ return /* @__PURE__ */ jsxs15(Text15, { children: [
4094
+ shortcutText,
4095
+ " to ",
4096
+ action
4097
+ ] });
4098
+ }
4099
+
4100
+ // src/design-system/Pane.tsx
4101
+ import { Box as Box12 } from "@claude-code-kit/ink-renderer";
4102
+ import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
4103
+ function Pane({ children, color: color2 }) {
4104
+ return /* @__PURE__ */ jsxs16(Box12, { flexDirection: "column", paddingTop: 1, children: [
4105
+ /* @__PURE__ */ jsx22(Divider, { color: color2 }),
4106
+ /* @__PURE__ */ jsx22(Box12, { flexDirection: "column", paddingX: 2, children })
4107
+ ] });
4108
+ }
4109
+
4110
+ // src/design-system/Dialog.tsx
4111
+ import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
4112
+ function Dialog({
4113
+ title,
4114
+ subtitle,
4115
+ children,
4116
+ onCancel,
4117
+ color: color2 = "permission",
4118
+ hideInputGuide,
4119
+ hideBorder
4120
+ }) {
4121
+ useInput8(
4122
+ useCallback9(
4123
+ (_input, key) => {
4124
+ if (key.escape) {
4125
+ onCancel();
4126
+ }
4127
+ },
4128
+ [onCancel]
4129
+ )
4130
+ );
4131
+ const defaultInputGuide = /* @__PURE__ */ jsxs17(Byline, { children: [
4132
+ /* @__PURE__ */ jsx23(KeyboardShortcutHint, { shortcut: "Enter", action: "confirm" }),
4133
+ /* @__PURE__ */ jsx23(KeyboardShortcutHint, { shortcut: "Esc", action: "cancel" })
4134
+ ] });
4135
+ const content = /* @__PURE__ */ jsxs17(Fragment3, { children: [
4136
+ /* @__PURE__ */ jsxs17(Box13, { flexDirection: "column", gap: 1, children: [
4137
+ /* @__PURE__ */ jsxs17(Box13, { flexDirection: "column", children: [
4138
+ /* @__PURE__ */ jsx23(Text16, { bold: true, color: color2, children: title }),
4139
+ subtitle && /* @__PURE__ */ jsx23(Text16, { dimColor: true, children: subtitle })
4140
+ ] }),
4141
+ children
4142
+ ] }),
4143
+ !hideInputGuide && /* @__PURE__ */ jsx23(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx23(Text16, { dimColor: true, italic: true, children: defaultInputGuide }) })
4144
+ ] });
4145
+ if (hideBorder) {
4146
+ return content;
4147
+ }
4148
+ return /* @__PURE__ */ jsx23(Pane, { color: color2, children: content });
4149
+ }
4150
+
4151
+ // src/design-system/FuzzyPicker.tsx
4152
+ import { useCallback as useCallback10, useContext as useContext8, useEffect as useEffect8, useState as useState11 } from "react";
4153
+ import {
4154
+ clamp,
4155
+ Box as Box15,
4156
+ Text as Text18,
4157
+ useInput as useInput9,
4158
+ TerminalSizeContext as TerminalSizeContext5
4159
+ } from "@claude-code-kit/ink-renderer";
4160
+
4161
+ // src/design-system/ListItem.tsx
4162
+ import figures2 from "figures";
4163
+ import { useDeclaredCursor } from "@claude-code-kit/ink-renderer";
4164
+ import { Box as Box14, Text as Text17 } from "@claude-code-kit/ink-renderer";
4165
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
4166
+ function ListItem({
4167
+ isFocused,
4168
+ isSelected = false,
4169
+ children,
4170
+ description,
4171
+ showScrollDown,
4172
+ showScrollUp,
4173
+ styled = true,
4174
+ disabled = false,
4175
+ declareCursor
4176
+ }) {
4177
+ function renderIndicator() {
4178
+ if (disabled) {
4179
+ return /* @__PURE__ */ jsx24(Text17, { children: " " });
4180
+ }
4181
+ if (isFocused) {
4182
+ return /* @__PURE__ */ jsx24(Text17, { color: "cyan", children: figures2.pointer });
4183
+ }
4184
+ if (showScrollDown) {
4185
+ return /* @__PURE__ */ jsx24(Text17, { dimColor: true, children: figures2.arrowDown });
4186
+ }
4187
+ if (showScrollUp) {
4188
+ return /* @__PURE__ */ jsx24(Text17, { dimColor: true, children: figures2.arrowUp });
4189
+ }
4190
+ return /* @__PURE__ */ jsx24(Text17, { children: " " });
4191
+ }
4192
+ function getTextColor() {
4193
+ if (disabled) {
4194
+ return void 0;
4195
+ }
4196
+ if (!styled) {
4197
+ return void 0;
4198
+ }
4199
+ if (isSelected) {
4200
+ return "green";
4201
+ }
4202
+ if (isFocused) {
4203
+ return "cyan";
4204
+ }
4205
+ return void 0;
4206
+ }
4207
+ const textColor = getTextColor();
4208
+ const cursorRef = useDeclaredCursor({
4209
+ line: 0,
4210
+ column: 0,
4211
+ active: isFocused && !disabled && declareCursor !== false
4212
+ });
4213
+ return /* @__PURE__ */ jsxs18(Box14, { ref: cursorRef, flexDirection: "column", children: [
4214
+ /* @__PURE__ */ jsxs18(Box14, { flexDirection: "row", gap: 1, children: [
4215
+ renderIndicator(),
4216
+ styled ? /* @__PURE__ */ jsx24(Text17, { color: textColor, dimColor: disabled, children }) : children,
4217
+ isSelected && !disabled && /* @__PURE__ */ jsx24(Text17, { color: "green", children: figures2.tick })
4218
+ ] }),
4219
+ description && /* @__PURE__ */ jsx24(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx24(Text17, { dimColor: true, children: description }) })
4220
+ ] });
4221
+ }
4222
+
4223
+ // src/design-system/FuzzyPicker.tsx
4224
+ import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
4225
+ var DEFAULT_VISIBLE = 8;
4226
+ var CHROME_ROWS = 10;
4227
+ var MIN_VISIBLE = 2;
4228
+ function FuzzyPicker({
4229
+ title,
4230
+ placeholder = "Type to search...",
4231
+ initialQuery,
4232
+ items,
4233
+ getKey,
4234
+ renderItem,
4235
+ renderPreview,
4236
+ previewPosition = "bottom",
4237
+ visibleCount: requestedVisible = DEFAULT_VISIBLE,
4238
+ direction = "down",
4239
+ onQueryChange,
4240
+ onSelect,
4241
+ onTab,
4242
+ onShiftTab,
4243
+ onFocus,
4244
+ onCancel,
4245
+ emptyMessage = "No results",
4246
+ matchLabel,
4247
+ selectAction = "select",
4248
+ extraHints
4249
+ }) {
4250
+ const terminalSize = useContext8(TerminalSizeContext5);
4251
+ const rows = terminalSize?.rows ?? 24;
4252
+ const columns = terminalSize?.columns ?? 80;
4253
+ const [focusedIndex, setFocusedIndex] = useState11(0);
4254
+ const [query, setQuery] = useState11(initialQuery ?? "");
4255
+ const visibleCount = Math.max(
4256
+ MIN_VISIBLE,
4257
+ Math.min(requestedVisible, rows - CHROME_ROWS - (matchLabel ? 1 : 0))
4258
+ );
4259
+ const compact = columns < 120;
4260
+ const step = useCallback10(
4261
+ (delta) => {
4262
+ setFocusedIndex((i) => clamp(i + delta, 0, items.length - 1));
4263
+ },
4264
+ [items.length]
4265
+ );
4266
+ useInput9(
4267
+ useCallback10(
4268
+ (input, key) => {
4269
+ if (key.escape) {
4270
+ onCancel();
4271
+ return;
4272
+ }
4273
+ if (key.upArrow || key.ctrl && input === "p") {
4274
+ step(direction === "up" ? 1 : -1);
4275
+ return;
4276
+ }
4277
+ if (key.downArrow || key.ctrl && input === "n") {
4278
+ step(direction === "up" ? -1 : 1);
4279
+ return;
4280
+ }
4281
+ if (key.return) {
4282
+ const selected = items[focusedIndex];
4283
+ if (selected) onSelect(selected);
4284
+ return;
4285
+ }
4286
+ if (key.tab) {
4287
+ const selected = items[focusedIndex];
4288
+ if (!selected) return;
4289
+ const tabAction = key.shift ? onShiftTab ?? onTab : onTab;
4290
+ if (tabAction) {
4291
+ tabAction.handler(selected);
4292
+ } else {
4293
+ onSelect(selected);
4294
+ }
4295
+ return;
4296
+ }
4297
+ if (key.backspace) {
4298
+ setQuery((q) => q.slice(0, -1));
4299
+ return;
4300
+ }
4301
+ if (input && !key.ctrl) {
4302
+ setQuery((q) => q + input);
4303
+ }
4304
+ },
4305
+ [onCancel, step, direction, items, focusedIndex, onSelect, onShiftTab, onTab]
4306
+ )
4307
+ );
4308
+ useEffect8(() => {
4309
+ onQueryChange(query);
4310
+ setFocusedIndex(0);
4311
+ }, [query]);
4312
+ useEffect8(() => {
4313
+ setFocusedIndex((i) => clamp(i, 0, items.length - 1));
4314
+ }, [items.length]);
4315
+ const focused = items[focusedIndex];
4316
+ useEffect8(() => {
4317
+ onFocus?.(focused);
4318
+ }, [focused]);
4319
+ const windowStart = clamp(
4320
+ focusedIndex - visibleCount + 1,
4321
+ 0,
4322
+ items.length - visibleCount
4323
+ );
4324
+ const visible = items.slice(windowStart, windowStart + visibleCount);
4325
+ const emptyText = typeof emptyMessage === "function" ? emptyMessage(query) : emptyMessage;
4326
+ const searchInput = /* @__PURE__ */ jsx25(Box15, { borderStyle: "round", paddingX: 1, children: /* @__PURE__ */ jsx25(Text18, { dimColor: !query, children: query || placeholder }) });
4327
+ const listBlock = /* @__PURE__ */ jsx25(
4328
+ List,
4329
+ {
4330
+ visible,
4331
+ windowStart,
4332
+ visibleCount,
4333
+ total: items.length,
4334
+ focusedIndex,
4335
+ direction,
4336
+ getKey,
4337
+ renderItem,
4338
+ emptyText
4339
+ }
4340
+ );
4341
+ const preview = renderPreview && focused ? /* @__PURE__ */ jsx25(Box15, { flexDirection: "column", flexGrow: 1, children: renderPreview(focused) }) : null;
4342
+ const listGroup = renderPreview && previewPosition === "right" ? /* @__PURE__ */ jsxs19(
4343
+ Box15,
4344
+ {
4345
+ flexDirection: "row",
4346
+ gap: 2,
4347
+ height: visibleCount + (matchLabel ? 1 : 0),
4348
+ children: [
4349
+ /* @__PURE__ */ jsxs19(Box15, { flexDirection: "column", flexShrink: 0, children: [
4350
+ listBlock,
4351
+ matchLabel && /* @__PURE__ */ jsx25(Text18, { dimColor: true, children: matchLabel })
4352
+ ] }),
4353
+ preview ?? /* @__PURE__ */ jsx25(Box15, { flexGrow: 1 })
4354
+ ]
4355
+ }
4356
+ ) : /* @__PURE__ */ jsxs19(Box15, { flexDirection: "column", children: [
4357
+ listBlock,
4358
+ matchLabel && /* @__PURE__ */ jsx25(Text18, { dimColor: true, children: matchLabel }),
4359
+ preview
4360
+ ] });
4361
+ const inputAbove = direction !== "up";
4362
+ return /* @__PURE__ */ jsx25(Pane, { color: "permission", children: /* @__PURE__ */ jsxs19(Box15, { flexDirection: "column", gap: 1, children: [
4363
+ /* @__PURE__ */ jsx25(Text18, { bold: true, color: "permission", children: title }),
4364
+ inputAbove && searchInput,
4365
+ listGroup,
4366
+ !inputAbove && searchInput,
4367
+ /* @__PURE__ */ jsx25(Text18, { dimColor: true, children: /* @__PURE__ */ jsxs19(Byline, { children: [
4368
+ /* @__PURE__ */ jsx25(
4369
+ KeyboardShortcutHint,
4370
+ {
4371
+ shortcut: "up/dn",
4372
+ action: compact ? "nav" : "navigate"
4373
+ }
4374
+ ),
4375
+ /* @__PURE__ */ jsx25(KeyboardShortcutHint, { shortcut: "Enter", action: selectAction }),
4376
+ onTab && /* @__PURE__ */ jsx25(KeyboardShortcutHint, { shortcut: "Tab", action: onTab.action }),
4377
+ onShiftTab && !compact && /* @__PURE__ */ jsx25(
4378
+ KeyboardShortcutHint,
4379
+ {
4380
+ shortcut: "shift+tab",
4381
+ action: onShiftTab.action
4382
+ }
4383
+ ),
4384
+ /* @__PURE__ */ jsx25(KeyboardShortcutHint, { shortcut: "Esc", action: "cancel" }),
4385
+ extraHints
4386
+ ] }) })
4387
+ ] }) });
4388
+ }
4389
+ function List({
4390
+ visible,
4391
+ windowStart,
4392
+ visibleCount,
4393
+ total,
4394
+ focusedIndex,
4395
+ direction,
4396
+ getKey,
4397
+ renderItem,
4398
+ emptyText
4399
+ }) {
4400
+ if (visible.length === 0) {
4401
+ return /* @__PURE__ */ jsx25(Box15, { height: visibleCount, flexShrink: 0, children: /* @__PURE__ */ jsx25(Text18, { dimColor: true, children: emptyText }) });
4402
+ }
4403
+ const rows = visible.map((item, i) => {
4404
+ const actualIndex = windowStart + i;
4405
+ const isFocused = actualIndex === focusedIndex;
4406
+ const atLowEdge = i === 0 && windowStart > 0;
4407
+ const atHighEdge = i === visible.length - 1 && windowStart + visibleCount < total;
4408
+ return /* @__PURE__ */ jsx25(
4409
+ ListItem,
4410
+ {
4411
+ isFocused,
4412
+ showScrollUp: direction === "up" ? atHighEdge : atLowEdge,
4413
+ showScrollDown: direction === "up" ? atLowEdge : atHighEdge,
4414
+ styled: false,
4415
+ children: renderItem(item, isFocused)
4416
+ },
4417
+ getKey(item)
4418
+ );
4419
+ });
4420
+ return /* @__PURE__ */ jsx25(
4421
+ Box15,
4422
+ {
4423
+ height: visibleCount,
4424
+ flexShrink: 0,
4425
+ flexDirection: direction === "up" ? "column-reverse" : "column",
4426
+ children: rows
4427
+ }
4428
+ );
4429
+ }
4430
+
4431
+ // src/design-system/LoadingState.tsx
4432
+ import { Box as Box16, Text as Text19 } from "@claude-code-kit/ink-renderer";
4433
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
4434
+ function LoadingState({
4435
+ message,
4436
+ bold = false,
4437
+ dimColor = false,
4438
+ subtitle
4439
+ }) {
4440
+ return /* @__PURE__ */ jsxs20(Box16, { flexDirection: "column", children: [
4441
+ /* @__PURE__ */ jsxs20(Box16, { flexDirection: "row", children: [
4442
+ /* @__PURE__ */ jsx26(Spinner, {}),
4443
+ /* @__PURE__ */ jsxs20(Text19, { bold, dimColor, children: [
4444
+ " ",
4445
+ message
4446
+ ] })
4447
+ ] }),
4448
+ subtitle && /* @__PURE__ */ jsx26(Text19, { dimColor: true, children: subtitle })
4449
+ ] });
4450
+ }
4451
+
4452
+ // src/design-system/Ratchet.tsx
4453
+ import { useCallback as useCallback11, useContext as useContext9, useLayoutEffect as useLayoutEffect2, useRef as useRef9, useState as useState12 } from "react";
4454
+ import { TerminalSizeContext as TerminalSizeContext6 } from "@claude-code-kit/ink-renderer";
4455
+ import { useTerminalViewport } from "@claude-code-kit/ink-renderer";
4456
+ import { Box as Box17, measureElement } from "@claude-code-kit/ink-renderer";
4457
+ import { jsx as jsx27 } from "react/jsx-runtime";
4458
+ function Ratchet({
4459
+ children,
4460
+ lock = "always"
4461
+ }) {
4462
+ const [viewportRef, { isVisible }] = useTerminalViewport();
4463
+ const terminalSize = useContext9(TerminalSizeContext6);
4464
+ const rows = terminalSize?.rows ?? 24;
4465
+ const innerRef = useRef9(null);
4466
+ const maxHeight = useRef9(0);
4467
+ const [minHeight, setMinHeight] = useState12(0);
4468
+ const outerRef = useCallback11(
4469
+ (el) => {
4470
+ viewportRef(el);
4471
+ },
4472
+ [viewportRef]
4473
+ );
4474
+ const engaged = lock === "always" || !isVisible;
4475
+ useLayoutEffect2(() => {
4476
+ if (!innerRef.current) {
4477
+ return;
4478
+ }
4479
+ const { height } = measureElement(innerRef.current);
4480
+ if (height > maxHeight.current) {
4481
+ maxHeight.current = Math.min(height, rows);
4482
+ setMinHeight(maxHeight.current);
4483
+ }
4484
+ });
4485
+ return /* @__PURE__ */ jsx27(Box17, { minHeight: engaged ? minHeight : void 0, ref: outerRef, children: /* @__PURE__ */ jsx27(Box17, { ref: innerRef, flexDirection: "column", children }) });
4486
+ }
4487
+
4488
+ // src/design-system/Tabs.tsx
4489
+ import {
4490
+ createContext as createContext3,
4491
+ useCallback as useCallback12,
4492
+ useContext as useContext10,
4493
+ useState as useState13
4494
+ } from "react";
4495
+ import { TerminalSizeContext as TerminalSizeContext7 } from "@claude-code-kit/ink-renderer";
4496
+ import { stringWidth as stringWidth4 } from "@claude-code-kit/ink-renderer";
4497
+ import { Box as Box18, Text as Text20, useInput as useInput10 } from "@claude-code-kit/ink-renderer";
4498
+ import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
4499
+ var TabsContext = createContext3({
4500
+ selectedTab: void 0,
4501
+ width: void 0
4502
+ });
4503
+ function Tabs({
4504
+ title,
4505
+ color: color2,
4506
+ defaultTab,
4507
+ children,
4508
+ hidden,
4509
+ useFullWidth,
4510
+ selectedTab: controlledSelectedTab,
4511
+ onTabChange,
4512
+ banner,
4513
+ disableNavigation
4514
+ }) {
4515
+ const terminalSize = useContext10(TerminalSizeContext7);
4516
+ const terminalWidth = terminalSize?.columns ?? 80;
4517
+ const tabs = children.map((child) => [
4518
+ child.props.id ?? child.props.title,
4519
+ child.props.title
4520
+ ]);
4521
+ const defaultTabIndex = defaultTab ? tabs.findIndex((tab) => defaultTab === tab[0]) : 0;
4522
+ const isControlled = controlledSelectedTab !== void 0;
4523
+ const [internalSelectedTab, setInternalSelectedTab] = useState13(
4524
+ defaultTabIndex !== -1 ? defaultTabIndex : 0
4525
+ );
4526
+ const controlledTabIndex = isControlled ? tabs.findIndex((tab) => tab[0] === controlledSelectedTab) : -1;
4527
+ const selectedTabIndex = isControlled ? controlledTabIndex !== -1 ? controlledTabIndex : 0 : internalSelectedTab;
4528
+ const handleTabChange = useCallback12(
4529
+ (offset) => {
4530
+ const newIndex = (selectedTabIndex + tabs.length + offset) % tabs.length;
4531
+ const newTabId = tabs[newIndex]?.[0];
4532
+ if (isControlled && onTabChange && newTabId) {
4533
+ onTabChange(newTabId);
4534
+ } else {
4535
+ setInternalSelectedTab(newIndex);
4536
+ }
4537
+ },
4538
+ [selectedTabIndex, tabs, isControlled, onTabChange]
4539
+ );
4540
+ useInput10(
4541
+ useCallback12(
4542
+ (_input, key) => {
4543
+ if (hidden || disableNavigation) return;
4544
+ if (key.tab && !key.shift) {
4545
+ handleTabChange(1);
4546
+ } else if (key.tab && key.shift) {
4547
+ handleTabChange(-1);
4548
+ } else if (key.leftArrow) {
4549
+ handleTabChange(-1);
4550
+ } else if (key.rightArrow) {
4551
+ handleTabChange(1);
4552
+ }
4553
+ },
4554
+ [hidden, disableNavigation, handleTabChange]
4555
+ )
4556
+ );
4557
+ const titleWidth = title ? stringWidth4(title) + 1 : 0;
4558
+ const tabsWidth = tabs.reduce(
4559
+ (sum, [, tabTitle]) => sum + (tabTitle ? stringWidth4(tabTitle) : 0) + 3,
4560
+ 0
4561
+ );
4562
+ const usedWidth = titleWidth + tabsWidth;
4563
+ const spacerWidth = useFullWidth ? Math.max(0, terminalWidth - usedWidth) : 0;
4564
+ const contentWidth = useFullWidth ? terminalWidth : void 0;
4565
+ return /* @__PURE__ */ jsx28(
4566
+ TabsContext.Provider,
4567
+ {
4568
+ value: {
4569
+ selectedTab: tabs[selectedTabIndex]?.[0],
4570
+ width: contentWidth
4571
+ },
4572
+ children: /* @__PURE__ */ jsxs21(Box18, { flexDirection: "column", children: [
4573
+ !hidden && /* @__PURE__ */ jsxs21(Box18, { flexDirection: "row", gap: 1, children: [
4574
+ title !== void 0 && /* @__PURE__ */ jsx28(Text20, { bold: true, color: color2, children: title }),
4575
+ tabs.map(([id, tabTitle], i) => {
4576
+ const isCurrent = selectedTabIndex === i;
4577
+ return /* @__PURE__ */ jsxs21(
4578
+ Text20,
4579
+ {
4580
+ inverse: isCurrent,
4581
+ bold: isCurrent,
4582
+ children: [
4583
+ " ",
4584
+ tabTitle,
4585
+ " "
4586
+ ]
4587
+ },
4588
+ id
4589
+ );
4590
+ }),
4591
+ spacerWidth > 0 && /* @__PURE__ */ jsx28(Text20, { children: " ".repeat(spacerWidth) })
4592
+ ] }),
4593
+ banner,
4594
+ /* @__PURE__ */ jsx28(
4595
+ Box18,
4596
+ {
4597
+ width: contentWidth,
4598
+ marginTop: hidden ? 0 : 1,
4599
+ children
4600
+ }
4601
+ )
4602
+ ] })
4603
+ }
4604
+ );
4605
+ }
4606
+ function Tab({ title, id, children }) {
4607
+ const { selectedTab, width } = useContext10(TabsContext);
4608
+ if (selectedTab !== (id ?? title)) {
4609
+ return null;
4610
+ }
4611
+ return /* @__PURE__ */ jsx28(Box18, { width, children });
4612
+ }
4613
+ function useTabsWidth() {
4614
+ const { width } = useContext10(TabsContext);
4615
+ return width;
4616
+ }
4617
+
4618
+ // src/useVirtualScroll.ts
4619
+ import React22, { useState as useState14, useCallback as useCallback13, useMemo as useMemo7 } from "react";
4620
+ import { Box as Box19 } from "@claude-code-kit/ink-renderer";
4621
+ function clamp2(value, min, max) {
4622
+ return Math.max(min, Math.min(max, value));
4623
+ }
4624
+ function useVirtualScroll(options) {
4625
+ const {
4626
+ itemCount,
4627
+ estimatedItemHeight = 3,
4628
+ overscan = 20,
4629
+ viewportHeight
4630
+ } = options;
4631
+ const totalHeight = itemCount * estimatedItemHeight;
4632
+ const maxOffset = Math.max(0, totalHeight - viewportHeight);
4633
+ const [scrollOffset, setScrollOffset] = useState14(0);
4634
+ const clampedOffset = clamp2(scrollOffset, 0, maxOffset);
4635
+ const rawStart = Math.floor(clampedOffset / estimatedItemHeight);
4636
+ const rawEnd = Math.ceil((clampedOffset + viewportHeight) / estimatedItemHeight);
4637
+ const startIndex = clamp2(rawStart - overscan, 0, itemCount);
4638
+ const endIndex = clamp2(rawEnd + overscan, 0, itemCount);
4639
+ const visibleItems = endIndex - startIndex;
4640
+ const scrollTo = useCallback13(
4641
+ (index) => {
4642
+ const targetOffset = clamp2(index * estimatedItemHeight, 0, maxOffset);
4643
+ setScrollOffset(targetOffset);
4644
+ },
4645
+ [estimatedItemHeight, maxOffset]
4646
+ );
4647
+ const scrollToEnd = useCallback13(() => {
4648
+ setScrollOffset(maxOffset);
4649
+ }, [maxOffset]);
4650
+ const onScroll = useCallback13(
4651
+ (delta) => {
4652
+ setScrollOffset((prev) => clamp2(prev + delta * estimatedItemHeight, 0, maxOffset));
4653
+ },
4654
+ [estimatedItemHeight, maxOffset]
4655
+ );
4656
+ const isAtTop = clampedOffset <= 0;
4657
+ const isAtEnd = clampedOffset >= maxOffset;
4658
+ return useMemo7(
4659
+ () => ({
4660
+ startIndex,
4661
+ endIndex,
4662
+ visibleItems,
4663
+ totalHeight,
4664
+ scrollOffset: clampedOffset,
4665
+ scrollTo,
4666
+ scrollToEnd,
4667
+ onScroll,
4668
+ isAtTop,
4669
+ isAtEnd
4670
+ }),
4671
+ [
4672
+ startIndex,
4673
+ endIndex,
4674
+ visibleItems,
4675
+ totalHeight,
4676
+ clampedOffset,
4677
+ scrollTo,
4678
+ scrollToEnd,
4679
+ onScroll,
4680
+ isAtTop,
4681
+ isAtEnd
4682
+ ]
4683
+ );
4684
+ }
4685
+ function VirtualList(props) {
4686
+ const { items, renderItem, viewportHeight, estimatedItemHeight = 3, overscan = 20 } = props;
4687
+ const { startIndex, endIndex, totalHeight } = useVirtualScroll({
4688
+ itemCount: items.length,
4689
+ estimatedItemHeight,
4690
+ overscan,
4691
+ viewportHeight
4692
+ });
4693
+ const topPad = startIndex * estimatedItemHeight;
4694
+ const renderedHeight = (endIndex - startIndex) * estimatedItemHeight;
4695
+ const bottomPad = Math.max(0, totalHeight - topPad - renderedHeight);
4696
+ const visibleSlice = [];
4697
+ for (let i = startIndex; i < endIndex && i < items.length; i++) {
4698
+ visibleSlice.push(renderItem(items[i], i));
4699
+ }
4700
+ return React22.createElement(
4701
+ Box19,
4702
+ {
4703
+ flexDirection: "column",
4704
+ height: viewportHeight,
4705
+ overflow: "hidden"
4706
+ },
4707
+ topPad > 0 ? React22.createElement(Box19, { height: topPad, key: "__virtual-top" }) : null,
4708
+ ...visibleSlice,
4709
+ bottomPad > 0 ? React22.createElement(Box19, { height: bottomPad, key: "__virtual-bottom" }) : null
4710
+ );
4711
+ }
4712
+
4713
+ // src/WelcomeScreen.tsx
4714
+ import { Box as Box20, Text as Text21 } from "@claude-code-kit/ink-renderer";
4715
+ import { jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
4716
+ var DEFAULT_COLOR2 = "#DA7756";
4717
+ function ClawdLogo({ color: color2 = DEFAULT_COLOR2 }) {
4718
+ return /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
4719
+ /* @__PURE__ */ jsx29(Text21, { color: color2, children: " \u2590\u259B\u2588\u2588\u2588\u259C\u258C" }),
4720
+ /* @__PURE__ */ jsx29(Text21, { color: color2, children: "\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u259B\u2598" }),
4721
+ /* @__PURE__ */ jsx29(Text21, { color: color2, children: " \u2598\u2598 \u259D\u259D " })
4722
+ ] });
4723
+ }
4724
+ function WelcomeScreen({
4725
+ appName,
4726
+ subtitle,
4727
+ version,
4728
+ tips,
4729
+ logo,
4730
+ model,
4731
+ color: color2 = DEFAULT_COLOR2
4732
+ }) {
4733
+ const logoNode = logo ?? /* @__PURE__ */ jsx29(ClawdLogo, { color: color2 });
4734
+ return /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", gap: 1, marginTop: 1, marginLeft: 1, children: [
4735
+ /* @__PURE__ */ jsxs22(Box20, { flexDirection: "row", gap: 2, alignItems: "flex-start", children: [
4736
+ logoNode,
4737
+ /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", justifyContent: "center", children: [
4738
+ /* @__PURE__ */ jsxs22(Box20, { flexDirection: "row", gap: 1, children: [
4739
+ /* @__PURE__ */ jsx29(Text21, { bold: true, color: color2, children: appName }),
4740
+ version && /* @__PURE__ */ jsx29(Text21, { dimColor: true, children: `v${version}` })
4741
+ ] }),
4742
+ subtitle && /* @__PURE__ */ jsx29(Text21, { dimColor: true, children: subtitle }),
4743
+ model && /* @__PURE__ */ jsx29(Text21, { dimColor: true, children: model })
4744
+ ] })
4745
+ ] }),
4746
+ tips && tips.length > 0 && /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", gap: 0, children: [
4747
+ /* @__PURE__ */ jsx29(Text21, { dimColor: true, children: "Tips:" }),
4748
+ tips.map((tip, i) => /* @__PURE__ */ jsx29(Text21, { dimColor: true, children: ` - ${tip}` }, i))
4749
+ ] })
4750
+ ] });
4751
+ }
4752
+
4753
+ // src/agent/useAgent.ts
4754
+ import { useState as useState15, useCallback as useCallback14, useRef as useRef10, useEffect as useEffect9 } from "react";
4755
+ var _msgId = 0;
4756
+ function nextId() {
4757
+ return `msg-${++_msgId}-${Date.now()}`;
4758
+ }
4759
+ function toolCallToContent(tc) {
4760
+ return {
4761
+ type: "tool_use",
4762
+ toolName: tc.name,
4763
+ input: JSON.stringify(tc.input, null, 2),
4764
+ status: "running"
4765
+ };
4766
+ }
4767
+ function useAgent({ agent, onError }) {
4768
+ const [messages, setMessages] = useState15([]);
4769
+ const [isLoading, setIsLoading] = useState15(false);
4770
+ const [streamingContent, setStreamingContent] = useState15(null);
4771
+ const [permissionRequest, setPermissionRequest] = useState15(null);
4772
+ const isRunningRef = useRef10(false);
4773
+ const toolMsgMap = useRef10(/* @__PURE__ */ new Map());
4774
+ useEffect9(() => {
4775
+ agent.setPermissionHandler(async (request) => {
4776
+ return new Promise((resolve) => {
4777
+ setPermissionRequest({
4778
+ toolName: request.tool,
4779
+ description: `Tool "${request.tool}" wants to execute`,
4780
+ details: JSON.stringify(request.input, null, 2),
4781
+ resolve: (decision) => {
4782
+ setPermissionRequest(null);
4783
+ resolve({ decision });
4784
+ }
4785
+ });
4786
+ });
4787
+ });
4788
+ }, [agent]);
4789
+ const cancel = useCallback14(() => {
4790
+ agent.abort();
4791
+ isRunningRef.current = false;
4792
+ setIsLoading(false);
4793
+ setStreamingContent(null);
4794
+ setPermissionRequest(null);
4795
+ }, [agent]);
4796
+ const clearMessages = useCallback14(() => {
4797
+ agent.clearMessages();
4798
+ setMessages([]);
4799
+ setStreamingContent(null);
4800
+ setPermissionRequest(null);
4801
+ }, [agent]);
4802
+ const submit = useCallback14(
4803
+ (input) => {
4804
+ if (isRunningRef.current) return;
4805
+ const trimmed = input.trim();
4806
+ if (!trimmed) return;
4807
+ const userMsg = {
4808
+ id: nextId(),
4809
+ role: "user",
4810
+ content: trimmed,
4811
+ timestamp: Date.now()
4812
+ };
4813
+ setMessages((prev) => [...prev, userMsg]);
4814
+ isRunningRef.current = true;
4815
+ setIsLoading(true);
4816
+ setStreamingContent(null);
4817
+ (async () => {
4818
+ let accumulated = "";
4819
+ try {
4820
+ for await (const event of agent.run(trimmed)) {
4821
+ switch (event.type) {
4822
+ case "text": {
4823
+ accumulated += event.text;
4824
+ setStreamingContent(accumulated);
4825
+ break;
4826
+ }
4827
+ case "tool_call": {
4828
+ const msgId = nextId();
4829
+ toolMsgMap.current.set(event.toolCall.id, msgId);
4830
+ const toolMsg = {
4831
+ id: msgId,
4832
+ role: "assistant",
4833
+ content: [toolCallToContent(event.toolCall)],
4834
+ timestamp: Date.now()
4835
+ };
4836
+ setMessages((prev) => [...prev, toolMsg]);
4837
+ break;
4838
+ }
4839
+ case "tool_result": {
4840
+ const targetId = toolMsgMap.current.get(event.toolCallId);
4841
+ if (targetId) {
4842
+ setMessages(
4843
+ (prev) => prev.map((m) => {
4844
+ if (m.id !== targetId) return m;
4845
+ const contents = Array.isArray(m.content) ? m.content : [];
4846
+ return {
4847
+ ...m,
4848
+ content: contents.map((c) => {
4849
+ if (c.type !== "tool_use") return c;
4850
+ return {
4851
+ ...c,
4852
+ result: event.result.content,
4853
+ status: event.result.isError ? "error" : "success"
4854
+ };
4855
+ })
4856
+ };
4857
+ })
4858
+ );
4859
+ toolMsgMap.current.delete(event.toolCallId);
4860
+ }
4861
+ break;
4862
+ }
4863
+ case "error": {
4864
+ onError?.(event.error);
4865
+ break;
4866
+ }
4867
+ case "done": {
4868
+ if (accumulated.length > 0) {
4869
+ const assistantMsg = {
4870
+ id: nextId(),
4871
+ role: "assistant",
4872
+ content: accumulated,
4873
+ timestamp: Date.now()
4874
+ };
4875
+ setMessages((prev) => [...prev, assistantMsg]);
4876
+ }
4877
+ accumulated = "";
4878
+ setStreamingContent(null);
4879
+ break;
4880
+ }
4881
+ }
4882
+ }
4883
+ } catch (err) {
4884
+ const error = err instanceof Error ? err : new Error(String(err));
4885
+ onError?.(error);
4886
+ } finally {
4887
+ isRunningRef.current = false;
4888
+ setIsLoading(false);
4889
+ setStreamingContent(null);
4890
+ }
4891
+ })();
4892
+ },
4893
+ [agent, onError]
4894
+ );
4895
+ return {
4896
+ messages,
4897
+ isLoading,
4898
+ streamingContent,
4899
+ permissionRequest,
4900
+ submit,
4901
+ cancel,
4902
+ clearMessages
4903
+ };
4904
+ }
4905
+
4906
+ // src/agent/AgentProvider.tsx
4907
+ import { createContext as createContext4, useContext as useContext11, useMemo as useMemo8 } from "react";
4908
+ import { jsx as jsx30 } from "react/jsx-runtime";
4909
+ var AgentContext = createContext4(null);
4910
+ function AgentProvider({
4911
+ agent,
4912
+ model = "unknown",
4913
+ onError,
4914
+ children
4915
+ }) {
4916
+ const agentState = useAgent({ agent, onError });
4917
+ const value = useMemo8(
4918
+ () => ({
4919
+ ...agentState,
4920
+ agent,
4921
+ model
4922
+ }),
4923
+ [agentState, agent, model]
4924
+ );
4925
+ return /* @__PURE__ */ jsx30(AgentContext.Provider, { value, children });
4926
+ }
4927
+ function useAgentContext() {
4928
+ const ctx = useContext11(AgentContext);
4929
+ if (!ctx) {
4930
+ throw new Error(
4931
+ "useAgentContext must be used within an <AgentProvider>. Wrap your component tree with <AgentProvider agent={agent}>."
4932
+ );
4933
+ }
4934
+ return ctx;
4935
+ }
4936
+
4937
+ // src/agent/AgentREPL.tsx
4938
+ import { useCallback as useCallback15, useMemo as useMemo9 } from "react";
4939
+ import { jsx as jsx31 } from "react/jsx-runtime";
4940
+ function AgentREPLInner({
4941
+ commands,
4942
+ welcome,
4943
+ placeholder,
4944
+ onExit
4945
+ }) {
4946
+ const {
4947
+ messages,
4948
+ isLoading,
4949
+ streamingContent,
4950
+ permissionRequest,
4951
+ submit,
4952
+ model,
4953
+ clearMessages
4954
+ } = useAgentContext();
4955
+ const permissionState = useMemo9(() => {
4956
+ if (!permissionRequest) return void 0;
4957
+ return {
4958
+ toolName: permissionRequest.toolName,
4959
+ description: permissionRequest.description,
4960
+ details: permissionRequest.details,
4961
+ onDecision: (action) => {
4962
+ permissionRequest.resolve(action === "deny" ? "deny" : "allow");
4963
+ }
4964
+ };
4965
+ }, [permissionRequest]);
4966
+ const allCommands = useMemo9(() => {
4967
+ const builtIn = [
4968
+ {
4969
+ name: "clear",
4970
+ description: "Clear conversation history",
4971
+ onExecute: () => clearMessages()
4972
+ }
4973
+ ];
4974
+ return [...builtIn, ...commands ?? []];
4975
+ }, [commands, clearMessages]);
4976
+ const handleSubmit = useCallback15(
4977
+ async (input) => {
4978
+ submit(input);
4979
+ },
4980
+ [submit]
4981
+ );
4982
+ return /* @__PURE__ */ jsx31(
4983
+ REPL,
4984
+ {
4985
+ onSubmit: handleSubmit,
4986
+ onExit,
4987
+ messages,
4988
+ isLoading,
4989
+ streamingContent,
4990
+ permissionRequest: permissionState,
4991
+ commands: allCommands,
4992
+ model,
4993
+ welcome,
4994
+ placeholder
4995
+ }
4996
+ );
4997
+ }
4998
+ function AgentREPL({
4999
+ agent,
5000
+ model,
5001
+ commands,
5002
+ welcome,
5003
+ placeholder,
5004
+ onError,
5005
+ onExit
5006
+ }) {
5007
+ return /* @__PURE__ */ jsx31(AgentProvider, { agent, model, onError, children: /* @__PURE__ */ jsx31(
5008
+ AgentREPLInner,
5009
+ {
5010
+ commands,
5011
+ welcome,
5012
+ placeholder,
5013
+ onExit
5014
+ }
5015
+ ) });
5016
+ }
5017
+
5018
+ // src/AuthFlow.tsx
5019
+ import { useState as useState16, useCallback as useCallback16 } from "react";
5020
+ import { Box as Box21, Text as Text22, useInput as useInput11 } from "@claude-code-kit/ink-renderer";
5021
+ import { jsx as jsx32, jsxs as jsxs23 } from "react/jsx-runtime";
5022
+ function CredentialInput({
5023
+ label,
5024
+ masked,
5025
+ onSubmit,
5026
+ onCancel
5027
+ }) {
5028
+ const [value, setValue] = useState16("");
5029
+ const [cursor, setCursor] = useState16(0);
5030
+ useInput11((input, key) => {
5031
+ if (key.escape) {
5032
+ onCancel?.();
5033
+ return;
5034
+ }
5035
+ if (key.return) {
5036
+ if (value.length > 0) onSubmit(value);
5037
+ return;
5038
+ }
5039
+ if (key.backspace) {
5040
+ if (cursor > 0) {
5041
+ setValue((v) => v.slice(0, cursor - 1) + v.slice(cursor));
5042
+ setCursor((c) => c - 1);
5043
+ }
5044
+ return;
5045
+ }
5046
+ if (key.leftArrow) {
5047
+ setCursor((c) => Math.max(0, c - 1));
5048
+ return;
5049
+ }
5050
+ if (key.rightArrow) {
5051
+ setCursor((c) => Math.min(value.length, c + 1));
5052
+ return;
5053
+ }
5054
+ if (key.ctrl || key.meta) return;
5055
+ if (input.length > 0) {
5056
+ setValue((v) => v.slice(0, cursor) + input + v.slice(cursor));
5057
+ setCursor((c) => c + input.length);
5058
+ }
5059
+ });
5060
+ const display = masked ? "*".repeat(value.length) : value;
5061
+ const before = display.slice(0, cursor);
5062
+ const at = cursor < display.length ? display[cursor] : " ";
5063
+ const after = cursor < display.length ? display.slice(cursor + 1) : "";
5064
+ return /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", children: [
5065
+ /* @__PURE__ */ jsx32(Text22, { bold: true, children: label }),
5066
+ /* @__PURE__ */ jsxs23(Box21, { children: [
5067
+ /* @__PURE__ */ jsx32(Text22, { color: "cyan", children: "> " }),
5068
+ /* @__PURE__ */ jsxs23(Text22, { children: [
5069
+ before,
5070
+ /* @__PURE__ */ jsx32(Text22, { inverse: true, children: at }),
5071
+ after
5072
+ ] })
5073
+ ] }),
5074
+ value.length === 0 && /* @__PURE__ */ jsx32(Text22, { dimColor: true, children: " Type your credential and press Enter" })
5075
+ ] });
5076
+ }
5077
+ function AuthFlowUI({
5078
+ auth,
5079
+ onComplete,
5080
+ onCancel,
5081
+ title = "Authentication"
5082
+ }) {
5083
+ const [phase, setPhase] = useState16({ type: "select-provider" });
5084
+ const [baseURL, setBaseURL] = useState16();
5085
+ const [error, setError] = useState16();
5086
+ const flowState = auth.interactive();
5087
+ const providerOptions = (flowState.providers ?? []).map((p) => ({
5088
+ value: p.name,
5089
+ label: `${p.displayName}`,
5090
+ description: p.description
5091
+ }));
5092
+ const handleProviderSelect = useCallback16((providerName) => {
5093
+ setError(void 0);
5094
+ try {
5095
+ const state = auth.selectProvider(providerName);
5096
+ if (state.step === "done" && state.result) {
5097
+ onComplete(state.result.provider, state.result.providerName, state.result.model);
5098
+ setPhase({ type: "done" });
5099
+ return;
5100
+ }
5101
+ if (state.step === "input-credentials" && state.currentAuthMethod) {
5102
+ const method = state.currentAuthMethod;
5103
+ const needsBaseURL = method.type === "base-url-key" && !method.defaultBaseURL;
5104
+ setPhase({
5105
+ type: "input-credentials",
5106
+ providerName,
5107
+ method,
5108
+ needsBaseURL
5109
+ });
5110
+ return;
5111
+ }
5112
+ if (state.step === "select-model" && state.models) {
5113
+ setPhase({
5114
+ type: "select-model",
5115
+ providerName,
5116
+ method: state.currentAuthMethod,
5117
+ models: state.models,
5118
+ defaultModel: state.currentModel
5119
+ });
5120
+ return;
5121
+ }
5122
+ if (state.step === "select-auth-method" && state.authMethods) {
5123
+ const method = state.authMethods[0];
5124
+ const needsBaseURL = method.type === "base-url-key" && !("defaultBaseURL" in method && method.defaultBaseURL);
5125
+ setPhase({
5126
+ type: "input-credentials",
5127
+ providerName,
5128
+ method,
5129
+ needsBaseURL: needsBaseURL && method.type === "base-url-key"
5130
+ });
5131
+ return;
5132
+ }
5133
+ } catch (err) {
5134
+ setError(err instanceof Error ? err.message : String(err));
5135
+ }
5136
+ }, [auth, onComplete]);
5137
+ const handleBaseURLSubmit = useCallback16((url) => {
5138
+ setBaseURL(url);
5139
+ }, []);
5140
+ const handleCredentialSubmit = useCallback16(async (apiKey) => {
5141
+ if (phase.type !== "input-credentials") return;
5142
+ setError(void 0);
5143
+ try {
5144
+ const credentials = {
5145
+ apiKey,
5146
+ baseURL: baseURL || (phase.method.type === "base-url-key" ? phase.method.defaultBaseURL : void 0)
5147
+ };
5148
+ const state = await auth.inputCredentials(phase.providerName, phase.method, credentials);
5149
+ if (state.step === "done" && state.result) {
5150
+ onComplete(state.result.provider, state.result.providerName, state.result.model);
5151
+ setPhase({ type: "done" });
5152
+ return;
5153
+ }
5154
+ if (state.step === "select-model" && state.models) {
5155
+ setPhase({
5156
+ type: "select-model",
5157
+ providerName: phase.providerName,
5158
+ method: phase.method,
5159
+ models: state.models,
5160
+ defaultModel: state.currentModel
5161
+ });
5162
+ return;
5163
+ }
5164
+ } catch (err) {
5165
+ setError(err instanceof Error ? err.message : String(err));
5166
+ }
5167
+ }, [auth, phase, baseURL, onComplete]);
5168
+ const handleModelSelect = useCallback16(async (model) => {
5169
+ if (phase.type !== "select-model") return;
5170
+ setError(void 0);
5171
+ try {
5172
+ const state = await auth.selectModel(phase.providerName, phase.method, model);
5173
+ if (state.step === "done" && state.result) {
5174
+ onComplete(state.result.provider, state.result.providerName, state.result.model);
5175
+ setPhase({ type: "done" });
5176
+ }
5177
+ } catch (err) {
5178
+ setError(err instanceof Error ? err.message : String(err));
5179
+ }
5180
+ }, [auth, phase, onComplete]);
5181
+ const handleCancel = useCallback16(() => {
5182
+ if (phase.type === "select-provider") {
5183
+ onCancel?.();
5184
+ } else {
5185
+ setPhase({ type: "select-provider" });
5186
+ setBaseURL(void 0);
5187
+ setError(void 0);
5188
+ }
5189
+ }, [phase, onCancel]);
5190
+ if (phase.type === "done") {
5191
+ return null;
5192
+ }
5193
+ return /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", marginTop: 1, marginLeft: 1, children: [
5194
+ /* @__PURE__ */ jsx32(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx32(Text22, { bold: true, color: "#DA7756", children: title }) }),
5195
+ /* @__PURE__ */ jsx32(Divider, {}),
5196
+ error && /* @__PURE__ */ jsx32(Box21, { marginTop: 1, children: /* @__PURE__ */ jsxs23(Text22, { color: "red", children: [
5197
+ "Error: ",
5198
+ error
5199
+ ] }) }),
5200
+ /* @__PURE__ */ jsxs23(Box21, { marginTop: 1, children: [
5201
+ phase.type === "select-provider" && /* @__PURE__ */ jsx32(
5202
+ Select,
5203
+ {
5204
+ title: "Select a provider:",
5205
+ options: providerOptions,
5206
+ onChange: handleProviderSelect,
5207
+ onCancel: handleCancel
5208
+ }
5209
+ ),
5210
+ phase.type === "input-credentials" && phase.method.type === "base-url-key" && phase.needsBaseURL && !baseURL && /* @__PURE__ */ jsx32(
5211
+ CredentialInput,
5212
+ {
5213
+ label: "Enter Base URL:",
5214
+ masked: false,
5215
+ onSubmit: handleBaseURLSubmit,
5216
+ onCancel: handleCancel
5217
+ }
5218
+ ),
5219
+ phase.type === "input-credentials" && !(phase.method.type === "base-url-key" && phase.needsBaseURL && !baseURL) && /* @__PURE__ */ jsx32(
5220
+ CredentialInput,
5221
+ {
5222
+ label: phase.method.type === "api-key" || phase.method.type === "base-url-key" ? phase.method.inputLabel ?? "Enter API Key:" : "Enter API Key:",
5223
+ masked: true,
5224
+ onSubmit: handleCredentialSubmit,
5225
+ onCancel: handleCancel
5226
+ }
5227
+ ),
5228
+ phase.type === "select-model" && /* @__PURE__ */ jsx32(
5229
+ Select,
5230
+ {
5231
+ title: "Select a model:",
5232
+ options: phase.models.map((m) => ({
5233
+ value: m,
5234
+ label: m,
5235
+ description: m === phase.defaultModel ? "(default)" : void 0
5236
+ })),
5237
+ defaultValue: phase.defaultModel,
5238
+ onChange: handleModelSelect,
5239
+ onCancel: handleCancel
5240
+ }
5241
+ )
5242
+ ] }),
5243
+ /* @__PURE__ */ jsx32(Box21, { marginTop: 1, children: /* @__PURE__ */ jsxs23(Text22, { dimColor: true, children: [
5244
+ "Esc to ",
5245
+ phase.type === "select-provider" ? "cancel" : "go back"
5246
+ ] }) })
5247
+ ] });
5248
+ }
2339
5249
  export {
5250
+ AgentContext,
5251
+ AgentProvider,
5252
+ AgentREPL,
5253
+ AuthFlowUI,
5254
+ BashPermissionContent,
5255
+ Byline,
5256
+ ClawdLogo,
2340
5257
  CommandRegistry,
2341
5258
  DEFAULT_BINDINGS,
5259
+ Dialog,
5260
+ DiffView,
2342
5261
  Divider,
5262
+ FileEditPermissionContent,
5263
+ FuzzyPicker,
2343
5264
  KeybindingSetup,
5265
+ KeyboardShortcutHint,
5266
+ ListItem,
5267
+ LoadingState,
5268
+ Markdown,
5269
+ MarkdownTable,
2344
5270
  MessageList,
2345
5271
  MultiSelect,
5272
+ Pane,
5273
+ PermissionRequest,
2346
5274
  ProgressBar,
2347
5275
  PromptInput,
2348
5276
  REPL,
5277
+ Ratchet,
5278
+ SearchOverlay,
2349
5279
  Select,
2350
5280
  Spinner,
2351
5281
  StatusIcon,
2352
5282
  StatusLine,
5283
+ StreamingMarkdown,
2353
5284
  StreamingText,
5285
+ Tab,
5286
+ Tabs,
5287
+ TextHoverColorContext,
5288
+ ThemeProvider,
5289
+ ThemedBox_default as ThemedBox,
5290
+ ThemedText,
5291
+ VirtualList,
5292
+ WelcomeScreen,
2354
5293
  clearCommand,
5294
+ color,
2355
5295
  createCommandRegistry,
2356
5296
  defineCommand,
2357
5297
  defineJSXCommand,
2358
5298
  defineLocalCommand,
2359
5299
  exitCommand,
5300
+ getTheme,
2360
5301
  helpCommand,
5302
+ parseUnifiedDiff,
5303
+ useAgent,
5304
+ useAgentContext,
5305
+ useDoublePress,
2361
5306
  useKeybinding,
2362
5307
  useKeybindings,
2363
- useStatusLine
5308
+ usePreviewTheme,
5309
+ useSearch,
5310
+ useStatusLine,
5311
+ useTabsWidth,
5312
+ useTerminalSize,
5313
+ useTheme,
5314
+ useThemeSetting,
5315
+ useVirtualScroll
2364
5316
  };