@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/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/index.d.mts +669 -24
- package/dist/index.d.ts +669 -24
- package/dist/index.js +3333 -350
- package/dist/index.mjs +3264 -312
- package/package.json +18 -9
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__ */
|
|
144
|
+
return /* @__PURE__ */ jsxs2(Text2, { color: color2, dimColor: !color2, children: [
|
|
18
145
|
char.repeat(leftWidth),
|
|
19
146
|
" ",
|
|
20
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
30
|
-
import { jsx as
|
|
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__ */
|
|
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
|
|
51
|
-
import { jsxs as
|
|
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__ */
|
|
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
|
|
71
|
-
import { jsx as
|
|
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
|
-
|
|
206
|
+
separator = " \xB7 ",
|
|
80
207
|
borderStyle = "none",
|
|
81
208
|
borderColor
|
|
82
209
|
}) {
|
|
83
210
|
const border = borderStyle === "none" ? void 0 : borderStyle;
|
|
84
|
-
return /* @__PURE__ */
|
|
85
|
-
|
|
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__ */
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
|
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 =
|
|
604
|
+
const getDisplayText = useMemo2(
|
|
472
605
|
() => (action, context) => getBindingDisplayText(action, context, bindings),
|
|
473
606
|
[bindings]
|
|
474
607
|
);
|
|
475
|
-
const registerHandler =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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 {
|
|
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
|
|
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
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
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
|
-
|
|
1737
|
-
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
)
|
|
1741
|
-
|
|
1742
|
-
(
|
|
1743
|
-
if (
|
|
1744
|
-
if (
|
|
1745
|
-
|
|
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 (
|
|
1760
|
-
|
|
1761
|
-
|
|
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.
|
|
1766
|
-
|
|
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.
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
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
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
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 (
|
|
1790
|
-
if (
|
|
1791
|
-
|
|
2050
|
+
if (input === "d") {
|
|
2051
|
+
if (!pendingD) {
|
|
2052
|
+
setPendingD(true);
|
|
1792
2053
|
return;
|
|
1793
2054
|
}
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
const
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
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.
|
|
1808
|
-
|
|
2068
|
+
if (key.upArrow || input === "k" && !key.ctrl) {
|
|
2069
|
+
if (!moveLine(-1)) historyUp();
|
|
1809
2070
|
return;
|
|
1810
2071
|
}
|
|
1811
|
-
if (key.
|
|
1812
|
-
|
|
2072
|
+
if (key.downArrow || input === "j" && !key.ctrl) {
|
|
2073
|
+
if (!moveLine(1)) historyDown();
|
|
1813
2074
|
return;
|
|
1814
2075
|
}
|
|
1815
|
-
if (key.
|
|
1816
|
-
|
|
2076
|
+
if (key.return && value.length > 0) {
|
|
2077
|
+
onSubmit(value);
|
|
1817
2078
|
return;
|
|
1818
2079
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
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 (
|
|
1824
|
-
|
|
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 (
|
|
1834
|
-
|
|
1835
|
-
|
|
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 (
|
|
1839
|
-
|
|
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
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
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 (
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
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
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
return
|
|
1863
|
-
|
|
1864
|
-
|
|
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 =
|
|
1868
|
-
const
|
|
1869
|
-
const after =
|
|
1870
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
2190
|
+
/* @__PURE__ */ jsx7(Text6, { inverse: true, children: at }),
|
|
1873
2191
|
after
|
|
1874
2192
|
] });
|
|
1875
2193
|
};
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
/* @__PURE__ */
|
|
1894
|
-
|
|
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
|
|
1900
|
-
import { Text as
|
|
1901
|
-
import { jsx as
|
|
1902
|
-
var
|
|
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 =
|
|
2254
|
+
const startRef = useRef3(Date.now());
|
|
1918
2255
|
const allVerbs = verbs ?? (verb ? [verb] : ["Thinking"]);
|
|
1919
|
-
|
|
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
|
-
|
|
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__ */
|
|
1938
|
-
/* @__PURE__ */
|
|
1939
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2281
|
+
label && /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
1945
2282
|
" ",
|
|
1946
2283
|
label
|
|
1947
2284
|
] }),
|
|
1948
|
-
showTime && /* @__PURE__ */
|
|
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/
|
|
1957
|
-
import {
|
|
1958
|
-
import {
|
|
1959
|
-
import {
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
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
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2485
|
+
},
|
|
2486
|
+
cancelPreview: () => {
|
|
2487
|
+
if (previewTheme !== null) {
|
|
2488
|
+
setPreviewTheme(null);
|
|
2489
|
+
}
|
|
2490
|
+
},
|
|
2491
|
+
currentTheme
|
|
2492
|
+
}),
|
|
2493
|
+
[themeSetting, previewTheme, currentTheme, onThemeSave]
|
|
1990
2494
|
);
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
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
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
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 =
|
|
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__ */
|
|
2036
|
-
title && /* @__PURE__ */
|
|
2037
|
-
scrollOffset > 0 && /* @__PURE__ */
|
|
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__ */
|
|
2044
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2049
|
-
|
|
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__ */
|
|
2062
|
-
opt.description && /* @__PURE__ */
|
|
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__ */
|
|
2069
|
-
/* @__PURE__ */
|
|
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] =
|
|
2082
|
-
const handleConfirm =
|
|
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 =
|
|
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__ */
|
|
2110
|
-
title && /* @__PURE__ */
|
|
2111
|
-
scrollOffset > 0 && /* @__PURE__ */
|
|
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__ */
|
|
2118
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2123
|
-
|
|
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__ */
|
|
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__ */
|
|
2144
|
-
/* @__PURE__ */
|
|
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 {
|
|
2150
|
-
import {
|
|
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
|
-
|
|
2166
|
-
/* @__PURE__ */
|
|
2167
|
-
/* @__PURE__ */
|
|
2168
|
-
|
|
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.
|
|
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__ */
|
|
2182
|
-
messages.map((message, i) => /* @__PURE__ */
|
|
2183
|
-
streamingContent != null && streamingContent.length > 0 && /* @__PURE__ */
|
|
2184
|
-
/* @__PURE__ */
|
|
2185
|
-
/* @__PURE__ */
|
|
2186
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
2201
|
-
import { Text as
|
|
2202
|
-
import { jsx as
|
|
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] =
|
|
2211
|
-
const onCompleteRef =
|
|
3646
|
+
const [revealed, setRevealed] = useState8(0);
|
|
3647
|
+
const onCompleteRef = useRef6(onComplete);
|
|
2212
3648
|
onCompleteRef.current = onComplete;
|
|
2213
|
-
|
|
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__ */
|
|
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
|
|
2231
|
-
import { Box as
|
|
2232
|
-
|
|
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] =
|
|
2250
|
-
const [internalHistory, setInternalHistory] =
|
|
2251
|
-
const
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
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__ */
|
|
3914
|
+
isLoading && !streamingContent && /* @__PURE__ */ jsx17(Box10, { marginTop: messages.length > 0 ? 1 : 0, children: spinner ?? /* @__PURE__ */ jsx17(Spinner, {}) })
|
|
2316
3915
|
] }),
|
|
2317
|
-
/* @__PURE__ */
|
|
2318
|
-
|
|
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__ */
|
|
2332
|
-
resolvedSegments.length > 0 && /* @__PURE__ */
|
|
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
|
-
|
|
5308
|
+
usePreviewTheme,
|
|
5309
|
+
useSearch,
|
|
5310
|
+
useStatusLine,
|
|
5311
|
+
useTabsWidth,
|
|
5312
|
+
useTerminalSize,
|
|
5313
|
+
useTheme,
|
|
5314
|
+
useThemeSetting,
|
|
5315
|
+
useVirtualScroll
|
|
2364
5316
|
};
|