flavor-code 1.2.1
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 +1081 -0
- package/dist/agent/loop.d.ts +31 -0
- package/dist/agent/message-queue.d.ts +19 -0
- package/dist/agent/planner.d.ts +28 -0
- package/dist/agent/subagents.d.ts +50 -0
- package/dist/agent/task-plan.d.ts +60 -0
- package/dist/agent/task-tools.d.ts +11 -0
- package/dist/agent/types.d.ts +117 -0
- package/dist/app-WC2XXGLM.js +3185 -0
- package/dist/app-WC2XXGLM.js.map +1 -0
- package/dist/auth/oauth-config.d.ts +4 -0
- package/dist/auth/oauth.d.ts +21 -0
- package/dist/auth/store.d.ts +15 -0
- package/dist/auth/types.d.ts +33 -0
- package/dist/chunk-BU64GNXE.js +15771 -0
- package/dist/chunk-BU64GNXE.js.map +1 -0
- package/dist/chunk-DM64TM4Z.js +124 -0
- package/dist/chunk-DM64TM4Z.js.map +1 -0
- package/dist/chunk-JZ322RCJ.js +40 -0
- package/dist/chunk-JZ322RCJ.js.map +1 -0
- package/dist/chunk-M744K7MK.js +246 -0
- package/dist/chunk-M744K7MK.js.map +1 -0
- package/dist/chunk-MVIMQEQT.js +692 -0
- package/dist/chunk-MVIMQEQT.js.map +1 -0
- package/dist/chunk-VOCZVFP7.js +153 -0
- package/dist/chunk-VOCZVFP7.js.map +1 -0
- package/dist/chunk-XS2JU6S3.js +35557 -0
- package/dist/chunk-XS2JU6S3.js.map +1 -0
- package/dist/claude-ink-ZL4UZMFY.js +27 -0
- package/dist/claude-ink-ZL4UZMFY.js.map +1 -0
- package/dist/cli.js +915 -0
- package/dist/cli.js.map +1 -0
- package/dist/codeisland/activate.mjs +74 -0
- package/dist/codeisland/bridge.mjs +97 -0
- package/dist/codeisland/flavor-plugin.json +27 -0
- package/dist/config/load.d.ts +19 -0
- package/dist/config/protected-file.d.ts +15 -0
- package/dist/config/schema.d.ts +176 -0
- package/dist/config/secret-envelope.d.ts +8 -0
- package/dist/context/compaction.d.ts +44 -0
- package/dist/context/manager.d.ts +65 -0
- package/dist/context/summarizer.d.ts +15 -0
- package/dist/desktop/main.js +17778 -0
- package/dist/desktop/main.js.map +1 -0
- package/dist/desktop/preload.cjs +90 -0
- package/dist/desktop/preload.cjs.map +1 -0
- package/dist/desktop-renderer/assets/index-C66pDvHs.js +144 -0
- package/dist/desktop-renderer/assets/index-C66pDvHs.js.map +1 -0
- package/dist/desktop-renderer/assets/index-Cm_ktmXm.css +1 -0
- package/dist/desktop-renderer/index.html +16 -0
- package/dist/devtools-SXJBSV2N.js +3 -0
- package/dist/devtools-SXJBSV2N.js.map +1 -0
- package/dist/execution/docker.d.ts +29 -0
- package/dist/execution/factory.d.ts +3 -0
- package/dist/execution/types.d.ts +18 -0
- package/dist/goal/classifier.d.ts +12 -0
- package/dist/goal/orchestrator.d.ts +34 -0
- package/dist/goal/planner.d.ts +9 -0
- package/dist/goal/store.d.ts +9 -0
- package/dist/goal/types.d.ts +190 -0
- package/dist/hallucination/confidence.d.ts +12 -0
- package/dist/hallucination/evidence-ledger.d.ts +29 -0
- package/dist/hallucination/guard.d.ts +27 -0
- package/dist/hallucination/messages.d.ts +10 -0
- package/dist/hallucination/retry-monitor.d.ts +19 -0
- package/dist/hallucination/sliding-window.d.ts +13 -0
- package/dist/hallucination/types.d.ts +49 -0
- package/dist/harness/local.d.ts +54 -0
- package/dist/hooks/bus.d.ts +9 -0
- package/dist/hooks/types.d.ts +105 -0
- package/dist/ide/client.d.ts +35 -0
- package/dist/incidents/reporter.d.ts +38 -0
- package/dist/init/project.d.ts +25 -0
- package/dist/load-WQGNTCHC.js +20 -0
- package/dist/load-WQGNTCHC.js.map +1 -0
- package/dist/loop/budget.d.ts +11 -0
- package/dist/loop/isolation.d.ts +21 -0
- package/dist/loop/orchestrator.d.ts +82 -0
- package/dist/loop/store.d.ts +13 -0
- package/dist/loop/types.d.ts +142 -0
- package/dist/loop/verifier.d.ts +13 -0
- package/dist/mcp/client.d.ts +66 -0
- package/dist/mcp/sdk.d.ts +27 -0
- package/dist/memory/coordinator.d.ts +32 -0
- package/dist/memory/extractor.d.ts +15 -0
- package/dist/memory/intent.d.ts +2 -0
- package/dist/memory/retrieval.d.ts +13 -0
- package/dist/memory/review.d.ts +36 -0
- package/dist/memory/similarity.d.ts +5 -0
- package/dist/memory/store.d.ts +58 -0
- package/dist/memory/types.d.ts +37 -0
- package/dist/models/anthropic.d.ts +24 -0
- package/dist/models/cache-profile.d.ts +23 -0
- package/dist/models/openai.d.ts +24 -0
- package/dist/models/registry.d.ts +16 -0
- package/dist/models/structured.d.ts +75 -0
- package/dist/models/types.d.ts +93 -0
- package/dist/permissions/classifier.d.ts +9 -0
- package/dist/permissions/engine.d.ts +28 -0
- package/dist/plugins/host.d.ts +15 -0
- package/dist/plugins/types.d.ts +136 -0
- package/dist/production.d.ts +59 -0
- package/dist/prompts/system.d.ts +28 -0
- package/dist/rpc/schema.d.ts +61 -0
- package/dist/rpc/server.d.ts +54 -0
- package/dist/sdk/index.d.ts +19 -0
- package/dist/sdk/index.js +53 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/session/checkpoint.d.ts +25 -0
- package/dist/session/store.d.ts +154 -0
- package/dist/session/tree.d.ts +31 -0
- package/dist/skills/builtin-loop.d.ts +12 -0
- package/dist/skills/registry.d.ts +47 -0
- package/dist/skills/tool.d.ts +9 -0
- package/dist/sleep/organizer.d.ts +37 -0
- package/dist/tools/ask-user-question.d.ts +49 -0
- package/dist/tools/file-diff.d.ts +9 -0
- package/dist/tools/files.d.ts +49 -0
- package/dist/tools/lsp.d.ts +77 -0
- package/dist/tools/managed.d.ts +59 -0
- package/dist/tools/runtime.d.ts +58 -0
- package/dist/tools/search.d.ts +76 -0
- package/dist/tools/shell.d.ts +37 -0
- package/dist/tools/task-output.d.ts +53 -0
- package/dist/tools/todo-write.d.ts +35 -0
- package/dist/tools/types.d.ts +56 -0
- package/dist/trace/recorder.d.ts +14 -0
- package/dist/trace/replay.d.ts +3 -0
- package/dist/trace/schema.d.ts +26 -0
- package/dist/ui/commands.d.ts +67 -0
- package/dist/ui/session.d.ts +126 -0
- package/dist/ui/transcript.d.ts +89 -0
- package/dist/utils/async.d.ts +5 -0
- package/dist/utils/envUtils.d.ts +1 -0
- package/dist/utils/error.d.ts +2 -0
- package/dist/utils/execFileNoThrow.d.ts +13 -0
- package/dist/utils/intl.d.ts +18 -0
- package/dist/utils/json.d.ts +44 -0
- package/dist/utils/log.d.ts +28 -0
- package/dist/utils/redact.d.ts +10 -0
- package/dist/utils/usage-summary.d.ts +34 -0
- package/dist/utils/version.d.ts +6 -0
- package/package.json +124 -0
- package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +4797 -0
|
@@ -0,0 +1,3185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
statusPresentation
|
|
5
|
+
} from "./chunk-DM64TM4Z.js";
|
|
6
|
+
import {
|
|
7
|
+
COMMAND_DESCRIPTIONS,
|
|
8
|
+
MVP_COMMANDS,
|
|
9
|
+
createGlobTool,
|
|
10
|
+
createProductionRuntime,
|
|
11
|
+
createTranscriptState,
|
|
12
|
+
isDestructiveTool,
|
|
13
|
+
message,
|
|
14
|
+
modelContentTranscriptText,
|
|
15
|
+
redactErrorText,
|
|
16
|
+
transcriptReducer
|
|
17
|
+
} from "./chunk-BU64GNXE.js";
|
|
18
|
+
import "./chunk-MVIMQEQT.js";
|
|
19
|
+
import {
|
|
20
|
+
Box_default,
|
|
21
|
+
ClockContext,
|
|
22
|
+
OSC,
|
|
23
|
+
ScrollBox_default,
|
|
24
|
+
StdinContext_default,
|
|
25
|
+
TerminalSizeContext,
|
|
26
|
+
TerminalWriteContext,
|
|
27
|
+
Text,
|
|
28
|
+
instances_default,
|
|
29
|
+
osc,
|
|
30
|
+
shiftAnchor,
|
|
31
|
+
stripAnsi,
|
|
32
|
+
useStdout,
|
|
33
|
+
use_app_default,
|
|
34
|
+
use_input_default
|
|
35
|
+
} from "./chunk-XS2JU6S3.js";
|
|
36
|
+
import {
|
|
37
|
+
execFileNoThrow
|
|
38
|
+
} from "./chunk-VOCZVFP7.js";
|
|
39
|
+
import "./chunk-JZ322RCJ.js";
|
|
40
|
+
|
|
41
|
+
// src/ui/app.tsx
|
|
42
|
+
import { basename as basename3 } from "path";
|
|
43
|
+
import { useEffect as useEffect3, useReducer, useRef as useRef4, useState as useState2 } from "react";
|
|
44
|
+
|
|
45
|
+
// src/claude-ink/hooks/use-animation-frame.ts
|
|
46
|
+
import { useContext as useContext2, useEffect, useState } from "react";
|
|
47
|
+
|
|
48
|
+
// src/claude-ink/hooks/use-terminal-viewport.ts
|
|
49
|
+
import { useCallback, useContext, useLayoutEffect, useRef } from "react";
|
|
50
|
+
function useTerminalViewport() {
|
|
51
|
+
const terminalSize = useContext(TerminalSizeContext);
|
|
52
|
+
const elementRef = useRef(null);
|
|
53
|
+
const entryRef = useRef({ isVisible: true });
|
|
54
|
+
const setElement = useCallback((el) => {
|
|
55
|
+
elementRef.current = el;
|
|
56
|
+
}, []);
|
|
57
|
+
useLayoutEffect(() => {
|
|
58
|
+
const element = elementRef.current;
|
|
59
|
+
if (!element?.yogaNode || !terminalSize) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const height = element.yogaNode.getComputedHeight();
|
|
63
|
+
const rows = terminalSize.rows;
|
|
64
|
+
let absoluteTop = element.yogaNode.getComputedTop();
|
|
65
|
+
let parent = element.parentNode;
|
|
66
|
+
let root = element.yogaNode;
|
|
67
|
+
while (parent) {
|
|
68
|
+
if (parent.yogaNode) {
|
|
69
|
+
absoluteTop += parent.yogaNode.getComputedTop();
|
|
70
|
+
root = parent.yogaNode;
|
|
71
|
+
}
|
|
72
|
+
if (parent.scrollTop) absoluteTop -= parent.scrollTop;
|
|
73
|
+
parent = parent.parentNode;
|
|
74
|
+
}
|
|
75
|
+
const screenHeight = root.getComputedHeight();
|
|
76
|
+
const bottom = absoluteTop + height;
|
|
77
|
+
const cursorRestoreScroll = screenHeight > rows ? 1 : 0;
|
|
78
|
+
const viewportY = Math.max(0, screenHeight - rows) + cursorRestoreScroll;
|
|
79
|
+
const viewportBottom = viewportY + rows;
|
|
80
|
+
const visible = bottom > viewportY && absoluteTop < viewportBottom;
|
|
81
|
+
if (visible !== entryRef.current.isVisible) {
|
|
82
|
+
entryRef.current = { isVisible: visible };
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return [setElement, entryRef.current];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/claude-ink/hooks/use-animation-frame.ts
|
|
89
|
+
function useAnimationFrame(intervalMs = 16) {
|
|
90
|
+
const clock = useContext2(ClockContext);
|
|
91
|
+
const [viewportRef, { isVisible }] = useTerminalViewport();
|
|
92
|
+
const [time, setTime] = useState(() => clock?.now() ?? 0);
|
|
93
|
+
const active = isVisible && intervalMs !== null;
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (!clock || !active) return;
|
|
96
|
+
let lastUpdate = clock.now();
|
|
97
|
+
const onChange = () => {
|
|
98
|
+
const now = clock.now();
|
|
99
|
+
if (now - lastUpdate >= intervalMs) {
|
|
100
|
+
lastUpdate = now;
|
|
101
|
+
setTime(now);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
return clock.subscribe(onChange, true);
|
|
105
|
+
}, [clock, intervalMs, active]);
|
|
106
|
+
return [viewportRef, time];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/claude-ink/hooks/use-selection.ts
|
|
110
|
+
import { useContext as useContext3, useMemo, useSyncExternalStore } from "react";
|
|
111
|
+
function useSelection() {
|
|
112
|
+
useContext3(StdinContext_default);
|
|
113
|
+
const ink = instances_default.get(process.stdout);
|
|
114
|
+
return useMemo(() => {
|
|
115
|
+
if (!ink) {
|
|
116
|
+
return {
|
|
117
|
+
copySelection: () => "",
|
|
118
|
+
copySelectionNoClear: () => "",
|
|
119
|
+
clearSelection: () => {
|
|
120
|
+
},
|
|
121
|
+
hasSelection: () => false,
|
|
122
|
+
getState: () => null,
|
|
123
|
+
subscribe: () => () => {
|
|
124
|
+
},
|
|
125
|
+
shiftAnchor: () => {
|
|
126
|
+
},
|
|
127
|
+
shiftSelection: () => {
|
|
128
|
+
},
|
|
129
|
+
moveFocus: () => {
|
|
130
|
+
},
|
|
131
|
+
captureScrolledRows: () => {
|
|
132
|
+
},
|
|
133
|
+
setSelectionBgColor: () => {
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
copySelection: () => ink.copySelection(),
|
|
139
|
+
copySelectionNoClear: () => ink.copySelectionNoClear(),
|
|
140
|
+
clearSelection: () => ink.clearTextSelection(),
|
|
141
|
+
hasSelection: () => ink.hasTextSelection(),
|
|
142
|
+
getState: () => ink.selection,
|
|
143
|
+
subscribe: (cb) => ink.subscribeToSelectionChange(cb),
|
|
144
|
+
shiftAnchor: (dRow, minRow, maxRow) => shiftAnchor(ink.selection, dRow, minRow, maxRow),
|
|
145
|
+
shiftSelection: (dRow, minRow, maxRow) => ink.shiftSelectionForScroll(dRow, minRow, maxRow),
|
|
146
|
+
moveFocus: (move) => ink.moveSelectionFocus(move),
|
|
147
|
+
captureScrolledRows: (firstRow, lastRow, side) => ink.captureScrolledRows(firstRow, lastRow, side),
|
|
148
|
+
setSelectionBgColor: (color) => ink.setSelectionBgColor(color)
|
|
149
|
+
};
|
|
150
|
+
}, [ink]);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/claude-ink/hooks/use-terminal-title.ts
|
|
154
|
+
import { useContext as useContext4, useEffect as useEffect2 } from "react";
|
|
155
|
+
function useTerminalTitle(title) {
|
|
156
|
+
const writeRaw = useContext4(TerminalWriteContext);
|
|
157
|
+
useEffect2(() => {
|
|
158
|
+
if (title === null || !writeRaw) return;
|
|
159
|
+
const clean = stripAnsi(title);
|
|
160
|
+
writeRaw(osc(OSC.SET_TITLE_AND_ICON, clean));
|
|
161
|
+
}, [title, writeRaw]);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/ui/assistant-text.tsx
|
|
165
|
+
import React2 from "react";
|
|
166
|
+
|
|
167
|
+
// src/ui/markdown.tsx
|
|
168
|
+
import React, { useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
169
|
+
import chalk2 from "chalk";
|
|
170
|
+
import { marked } from "marked";
|
|
171
|
+
|
|
172
|
+
// src/ui/highlight.ts
|
|
173
|
+
import chalk from "chalk";
|
|
174
|
+
chalk.level = Math.max(chalk.level, 3);
|
|
175
|
+
var KEYWORD = chalk.hex("#c586c0");
|
|
176
|
+
var STRING = chalk.hex("#ce9178");
|
|
177
|
+
var COMMENT = chalk.gray.italic;
|
|
178
|
+
var NUMBER = chalk.hex("#b5cea8");
|
|
179
|
+
var FUNCTION = chalk.hex("#dcdcaa");
|
|
180
|
+
var ATTRIBUTE = chalk.hex("#d7ba7d");
|
|
181
|
+
var KEYWORDS = {
|
|
182
|
+
javascript: /* @__PURE__ */ new Set([
|
|
183
|
+
"await",
|
|
184
|
+
"async",
|
|
185
|
+
"break",
|
|
186
|
+
"case",
|
|
187
|
+
"catch",
|
|
188
|
+
"class",
|
|
189
|
+
"const",
|
|
190
|
+
"continue",
|
|
191
|
+
"default",
|
|
192
|
+
"delete",
|
|
193
|
+
"do",
|
|
194
|
+
"else",
|
|
195
|
+
"export",
|
|
196
|
+
"extends",
|
|
197
|
+
"finally",
|
|
198
|
+
"for",
|
|
199
|
+
"from",
|
|
200
|
+
"function",
|
|
201
|
+
"if",
|
|
202
|
+
"import",
|
|
203
|
+
"in",
|
|
204
|
+
"instanceof",
|
|
205
|
+
"let",
|
|
206
|
+
"new",
|
|
207
|
+
"of",
|
|
208
|
+
"return",
|
|
209
|
+
"static",
|
|
210
|
+
"super",
|
|
211
|
+
"switch",
|
|
212
|
+
"this",
|
|
213
|
+
"throw",
|
|
214
|
+
"true",
|
|
215
|
+
"false",
|
|
216
|
+
"null",
|
|
217
|
+
"try",
|
|
218
|
+
"typeof",
|
|
219
|
+
"undefined",
|
|
220
|
+
"var",
|
|
221
|
+
"void",
|
|
222
|
+
"while",
|
|
223
|
+
"with",
|
|
224
|
+
"yield"
|
|
225
|
+
]),
|
|
226
|
+
typescript: /* @__PURE__ */ new Set([
|
|
227
|
+
"abstract",
|
|
228
|
+
"any",
|
|
229
|
+
"as",
|
|
230
|
+
"async",
|
|
231
|
+
"await",
|
|
232
|
+
"boolean",
|
|
233
|
+
"break",
|
|
234
|
+
"case",
|
|
235
|
+
"catch",
|
|
236
|
+
"class",
|
|
237
|
+
"const",
|
|
238
|
+
"constructor",
|
|
239
|
+
"continue",
|
|
240
|
+
"declare",
|
|
241
|
+
"default",
|
|
242
|
+
"delete",
|
|
243
|
+
"do",
|
|
244
|
+
"else",
|
|
245
|
+
"enum",
|
|
246
|
+
"export",
|
|
247
|
+
"extends",
|
|
248
|
+
"false",
|
|
249
|
+
"finally",
|
|
250
|
+
"for",
|
|
251
|
+
"from",
|
|
252
|
+
"function",
|
|
253
|
+
"get",
|
|
254
|
+
"if",
|
|
255
|
+
"implements",
|
|
256
|
+
"import",
|
|
257
|
+
"in",
|
|
258
|
+
"instanceof",
|
|
259
|
+
"interface",
|
|
260
|
+
"is",
|
|
261
|
+
"keyof",
|
|
262
|
+
"let",
|
|
263
|
+
"namespace",
|
|
264
|
+
"never",
|
|
265
|
+
"new",
|
|
266
|
+
"null",
|
|
267
|
+
"number",
|
|
268
|
+
"object",
|
|
269
|
+
"of",
|
|
270
|
+
"private",
|
|
271
|
+
"protected",
|
|
272
|
+
"public",
|
|
273
|
+
"readonly",
|
|
274
|
+
"return",
|
|
275
|
+
"set",
|
|
276
|
+
"static",
|
|
277
|
+
"string",
|
|
278
|
+
"super",
|
|
279
|
+
"switch",
|
|
280
|
+
"this",
|
|
281
|
+
"throw",
|
|
282
|
+
"true",
|
|
283
|
+
"try",
|
|
284
|
+
"type",
|
|
285
|
+
"typeof",
|
|
286
|
+
"undefined",
|
|
287
|
+
"unique",
|
|
288
|
+
"unknown",
|
|
289
|
+
"var",
|
|
290
|
+
"void",
|
|
291
|
+
"while",
|
|
292
|
+
"with",
|
|
293
|
+
"yield"
|
|
294
|
+
]),
|
|
295
|
+
python: /* @__PURE__ */ new Set([
|
|
296
|
+
"False",
|
|
297
|
+
"None",
|
|
298
|
+
"True",
|
|
299
|
+
"and",
|
|
300
|
+
"as",
|
|
301
|
+
"assert",
|
|
302
|
+
"async",
|
|
303
|
+
"await",
|
|
304
|
+
"break",
|
|
305
|
+
"class",
|
|
306
|
+
"continue",
|
|
307
|
+
"def",
|
|
308
|
+
"del",
|
|
309
|
+
"elif",
|
|
310
|
+
"else",
|
|
311
|
+
"except",
|
|
312
|
+
"finally",
|
|
313
|
+
"for",
|
|
314
|
+
"from",
|
|
315
|
+
"global",
|
|
316
|
+
"if",
|
|
317
|
+
"import",
|
|
318
|
+
"in",
|
|
319
|
+
"is",
|
|
320
|
+
"lambda",
|
|
321
|
+
"nonlocal",
|
|
322
|
+
"not",
|
|
323
|
+
"or",
|
|
324
|
+
"pass",
|
|
325
|
+
"raise",
|
|
326
|
+
"return",
|
|
327
|
+
"try",
|
|
328
|
+
"while",
|
|
329
|
+
"with",
|
|
330
|
+
"yield",
|
|
331
|
+
"self"
|
|
332
|
+
]),
|
|
333
|
+
bash: /* @__PURE__ */ new Set([
|
|
334
|
+
"if",
|
|
335
|
+
"then",
|
|
336
|
+
"else",
|
|
337
|
+
"elif",
|
|
338
|
+
"fi",
|
|
339
|
+
"for",
|
|
340
|
+
"while",
|
|
341
|
+
"until",
|
|
342
|
+
"do",
|
|
343
|
+
"done",
|
|
344
|
+
"case",
|
|
345
|
+
"esac",
|
|
346
|
+
"in",
|
|
347
|
+
"function",
|
|
348
|
+
"return",
|
|
349
|
+
"export",
|
|
350
|
+
"local",
|
|
351
|
+
"echo",
|
|
352
|
+
"printf",
|
|
353
|
+
"test"
|
|
354
|
+
]),
|
|
355
|
+
json: /* @__PURE__ */ new Set(["true", "false", "null"]),
|
|
356
|
+
rust: /* @__PURE__ */ new Set([
|
|
357
|
+
"as",
|
|
358
|
+
"async",
|
|
359
|
+
"await",
|
|
360
|
+
"break",
|
|
361
|
+
"const",
|
|
362
|
+
"continue",
|
|
363
|
+
"crate",
|
|
364
|
+
"dyn",
|
|
365
|
+
"else",
|
|
366
|
+
"enum",
|
|
367
|
+
"extern",
|
|
368
|
+
"false",
|
|
369
|
+
"fn",
|
|
370
|
+
"for",
|
|
371
|
+
"if",
|
|
372
|
+
"impl",
|
|
373
|
+
"in",
|
|
374
|
+
"let",
|
|
375
|
+
"loop",
|
|
376
|
+
"match",
|
|
377
|
+
"mod",
|
|
378
|
+
"move",
|
|
379
|
+
"mut",
|
|
380
|
+
"pub",
|
|
381
|
+
"ref",
|
|
382
|
+
"return",
|
|
383
|
+
"self",
|
|
384
|
+
"Self",
|
|
385
|
+
"static",
|
|
386
|
+
"struct",
|
|
387
|
+
"super",
|
|
388
|
+
"trait",
|
|
389
|
+
"true",
|
|
390
|
+
"type",
|
|
391
|
+
"unsafe",
|
|
392
|
+
"use",
|
|
393
|
+
"where",
|
|
394
|
+
"while"
|
|
395
|
+
]),
|
|
396
|
+
go: /* @__PURE__ */ new Set([
|
|
397
|
+
"break",
|
|
398
|
+
"case",
|
|
399
|
+
"chan",
|
|
400
|
+
"const",
|
|
401
|
+
"continue",
|
|
402
|
+
"default",
|
|
403
|
+
"defer",
|
|
404
|
+
"else",
|
|
405
|
+
"fallthrough",
|
|
406
|
+
"for",
|
|
407
|
+
"func",
|
|
408
|
+
"go",
|
|
409
|
+
"goto",
|
|
410
|
+
"if",
|
|
411
|
+
"import",
|
|
412
|
+
"interface",
|
|
413
|
+
"map",
|
|
414
|
+
"package",
|
|
415
|
+
"range",
|
|
416
|
+
"return",
|
|
417
|
+
"select",
|
|
418
|
+
"struct",
|
|
419
|
+
"switch",
|
|
420
|
+
"type",
|
|
421
|
+
"var",
|
|
422
|
+
"true",
|
|
423
|
+
"false",
|
|
424
|
+
"nil"
|
|
425
|
+
])
|
|
426
|
+
};
|
|
427
|
+
function normalizeLanguage(input) {
|
|
428
|
+
if (input === void 0) return void 0;
|
|
429
|
+
const lower = input.trim().toLowerCase();
|
|
430
|
+
switch (lower) {
|
|
431
|
+
case "js":
|
|
432
|
+
case "jsx":
|
|
433
|
+
case "javascript":
|
|
434
|
+
return "javascript";
|
|
435
|
+
case "ts":
|
|
436
|
+
case "tsx":
|
|
437
|
+
case "typescript":
|
|
438
|
+
return "typescript";
|
|
439
|
+
case "py":
|
|
440
|
+
case "python":
|
|
441
|
+
return "python";
|
|
442
|
+
case "sh":
|
|
443
|
+
case "shell":
|
|
444
|
+
case "bash":
|
|
445
|
+
case "zsh":
|
|
446
|
+
return "bash";
|
|
447
|
+
case "json":
|
|
448
|
+
return "json";
|
|
449
|
+
case "rs":
|
|
450
|
+
case "rust":
|
|
451
|
+
return "rust";
|
|
452
|
+
case "go":
|
|
453
|
+
case "golang":
|
|
454
|
+
return "go";
|
|
455
|
+
default:
|
|
456
|
+
return void 0;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
function isIdentifierStart(ch) {
|
|
460
|
+
return /[A-Za-z_]/.test(ch) || ch === "@";
|
|
461
|
+
}
|
|
462
|
+
function isIdentifierPart(ch) {
|
|
463
|
+
return /[A-Za-z0-9_]/.test(ch);
|
|
464
|
+
}
|
|
465
|
+
function isDigit(ch) {
|
|
466
|
+
return ch >= "0" && ch <= "9";
|
|
467
|
+
}
|
|
468
|
+
function isNumberPrefix(line, i) {
|
|
469
|
+
const ch = line[i] ?? "";
|
|
470
|
+
if (isDigit(ch)) return true;
|
|
471
|
+
if (ch === "." && isDigit(line[i + 1] ?? "")) return true;
|
|
472
|
+
if (ch === "0" && (line[i + 1] === "x" || line[i + 1] === "X")) return true;
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
function readNumberToken(line, i) {
|
|
476
|
+
let j = i + 1;
|
|
477
|
+
const len = line.length;
|
|
478
|
+
while (j < len) {
|
|
479
|
+
const cj = line[j] ?? "";
|
|
480
|
+
if (isDigit(cj)) {
|
|
481
|
+
j += 1;
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
if (cj === "." && isDigit(line[j + 1] ?? "")) {
|
|
485
|
+
j += 1;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (cj === "_") {
|
|
489
|
+
j += 1;
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
if ((cj === "e" || cj === "E") && /[+\-0-9]/.test(line[j + 1] ?? "")) {
|
|
493
|
+
j += 1;
|
|
494
|
+
if (line[j] === "+" || line[j] === "-") j += 1;
|
|
495
|
+
while (j < len && isDigit(line[j] ?? "")) j += 1;
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
return j;
|
|
501
|
+
}
|
|
502
|
+
function readIdentifierToken(line, i) {
|
|
503
|
+
let j = i + 1;
|
|
504
|
+
while (j < line.length && isIdentifierPart(line[j] ?? "")) j += 1;
|
|
505
|
+
return j;
|
|
506
|
+
}
|
|
507
|
+
function readStringToken(line, i) {
|
|
508
|
+
const quote = line[i] ?? "";
|
|
509
|
+
let j = i + 1;
|
|
510
|
+
const len = line.length;
|
|
511
|
+
while (j < len) {
|
|
512
|
+
const cj = line[j] ?? "";
|
|
513
|
+
if (cj === "\\") {
|
|
514
|
+
j += 2;
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (cj === quote) {
|
|
518
|
+
j += 1;
|
|
519
|
+
return j;
|
|
520
|
+
}
|
|
521
|
+
j += 1;
|
|
522
|
+
}
|
|
523
|
+
return j;
|
|
524
|
+
}
|
|
525
|
+
function readBlockCommentToken(line, i) {
|
|
526
|
+
let j = i + 2;
|
|
527
|
+
const len = line.length;
|
|
528
|
+
while (j < len) {
|
|
529
|
+
if (line[j] === "*" && line[j + 1] === "/") return j + 2;
|
|
530
|
+
j += 1;
|
|
531
|
+
}
|
|
532
|
+
return len;
|
|
533
|
+
}
|
|
534
|
+
function highlightSingleLine(line, keywords) {
|
|
535
|
+
const tokens = [];
|
|
536
|
+
let i = 0;
|
|
537
|
+
const len = line.length;
|
|
538
|
+
const commentStart = line[0] === "#";
|
|
539
|
+
while (i < len) {
|
|
540
|
+
const ch = line[i] ?? "";
|
|
541
|
+
if (ch === "/" && line[i + 1] === "*") {
|
|
542
|
+
const end = readBlockCommentToken(line, i);
|
|
543
|
+
tokens.push({ text: line.slice(i, end), paint: COMMENT });
|
|
544
|
+
i = end;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
if (commentStart && ch === "#") {
|
|
548
|
+
tokens.push({ text: line.slice(i), paint: COMMENT });
|
|
549
|
+
i = len;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
if (ch === "/" && line[i + 1] === "/") {
|
|
553
|
+
tokens.push({ text: line.slice(i), paint: COMMENT });
|
|
554
|
+
i = len;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
558
|
+
const end = readStringToken(line, i);
|
|
559
|
+
tokens.push({ text: line.slice(i, end), paint: STRING });
|
|
560
|
+
i = end;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (isNumberPrefix(line, i)) {
|
|
564
|
+
const end = readNumberToken(line, i);
|
|
565
|
+
tokens.push({ text: line.slice(i, end), paint: NUMBER });
|
|
566
|
+
i = end;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (isIdentifierStart(ch)) {
|
|
570
|
+
const end = readIdentifierToken(line, i);
|
|
571
|
+
const word = line.slice(i, end);
|
|
572
|
+
if (ch === "@") {
|
|
573
|
+
tokens.push({ text: word, paint: ATTRIBUTE });
|
|
574
|
+
} else if (keywords.has(word)) {
|
|
575
|
+
tokens.push({ text: word, paint: KEYWORD });
|
|
576
|
+
} else if (line[end] === "(") {
|
|
577
|
+
tokens.push({ text: word, paint: FUNCTION });
|
|
578
|
+
} else {
|
|
579
|
+
tokens.push({ text: word, paint: (s) => s });
|
|
580
|
+
}
|
|
581
|
+
i = end;
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
let j = i + 1;
|
|
585
|
+
while (j < len) {
|
|
586
|
+
const cj = line[j] ?? "";
|
|
587
|
+
if (isIdentifierStart(cj) || isDigit(cj) || cj === "'" || cj === '"' || cj === "`" || cj === "@" || cj === "/") break;
|
|
588
|
+
j += 1;
|
|
589
|
+
}
|
|
590
|
+
tokens.push({ text: line.slice(i, j), paint: (s) => s });
|
|
591
|
+
i = j;
|
|
592
|
+
}
|
|
593
|
+
let out = "";
|
|
594
|
+
for (const token of tokens) {
|
|
595
|
+
out += token.paint(token.text);
|
|
596
|
+
}
|
|
597
|
+
return out;
|
|
598
|
+
}
|
|
599
|
+
function highlightCode(source, language = void 0) {
|
|
600
|
+
if (source.length === 0) return "";
|
|
601
|
+
const lang = normalizeLanguage(language);
|
|
602
|
+
const keywords = lang !== void 0 ? KEYWORDS[lang] : KEYWORDS.javascript;
|
|
603
|
+
return source.split("\n").map((line) => highlightSingleLine(line, keywords)).join("\n");
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// src/ui/char-width.ts
|
|
607
|
+
function charWidth(codePoint) {
|
|
608
|
+
if (codePoint === 8203 || // ZERO WIDTH SPACE
|
|
609
|
+
codePoint === 8204 || // ZERO WIDTH NON-JOINER
|
|
610
|
+
codePoint === 8205 || // ZERO WIDTH JOINER
|
|
611
|
+
codePoint === 65279 || // BOM / ZERO WIDTH NO-BREAK SPACE
|
|
612
|
+
codePoint >= 768 && codePoint <= 879 || // Combining Diacritical Marks
|
|
613
|
+
codePoint >= 1155 && codePoint <= 1161 || // Cyrillic combining marks
|
|
614
|
+
codePoint >= 1425 && codePoint <= 1469 || // Hebrew combining marks
|
|
615
|
+
codePoint >= 1552 && codePoint <= 1562 || // Arabic combining marks
|
|
616
|
+
codePoint >= 1611 && codePoint <= 1631 || // Arabic combining marks
|
|
617
|
+
codePoint >= 1648 && codePoint <= 1648 || // Arabic combining marks
|
|
618
|
+
codePoint >= 1750 && codePoint <= 1756 || // Arabic combining marks
|
|
619
|
+
codePoint >= 1759 && codePoint <= 1764 || // Arabic combining marks
|
|
620
|
+
codePoint >= 1767 && codePoint <= 1768 || // Arabic combining marks
|
|
621
|
+
codePoint >= 1770 && codePoint <= 1773 || // Arabic combining marks
|
|
622
|
+
codePoint >= 1809 && codePoint <= 1809 || // Syriac combining marks
|
|
623
|
+
codePoint >= 1840 && codePoint <= 1866 || // Syriac combining marks
|
|
624
|
+
codePoint >= 1958 && codePoint <= 1968 || // Thaana combining marks
|
|
625
|
+
codePoint >= 2304 && codePoint <= 2306 || // Devanagari combining marks
|
|
626
|
+
codePoint >= 2362 && codePoint <= 2362 || // Devanagari combining marks
|
|
627
|
+
codePoint >= 2364 && codePoint <= 2364 || // Devanagari combining marks
|
|
628
|
+
codePoint >= 2369 && codePoint <= 2376 || // Devanagari combining marks
|
|
629
|
+
codePoint >= 2381 && codePoint <= 2381 || // Devanagari combining marks
|
|
630
|
+
codePoint >= 2385 && codePoint <= 2391 || // Devanagari combining marks
|
|
631
|
+
codePoint >= 2402 && codePoint <= 2403 || // Devanagari combining marks
|
|
632
|
+
codePoint >= 2433 && codePoint <= 2433 || // Bengali combining marks
|
|
633
|
+
codePoint >= 2492 && codePoint <= 2492 || // Bengali combining marks
|
|
634
|
+
codePoint >= 2497 && codePoint <= 2500 || // Bengali combining marks
|
|
635
|
+
codePoint >= 2509 && codePoint <= 2509 || // Bengali combining marks
|
|
636
|
+
codePoint >= 2530 && codePoint <= 2531 || // Bengali combining marks
|
|
637
|
+
codePoint >= 2561 && codePoint <= 2562 || // Gurmukhi combining marks
|
|
638
|
+
codePoint >= 2620 && codePoint <= 2620 || // Gurmukhi combining marks
|
|
639
|
+
codePoint >= 2625 && codePoint <= 2626 || // Gurmukhi combining marks
|
|
640
|
+
codePoint >= 2631 && codePoint <= 2632 || // Gurmukhi combining marks
|
|
641
|
+
codePoint >= 2635 && codePoint <= 2637 || // Gurmukhi combining marks
|
|
642
|
+
codePoint >= 2672 && codePoint <= 2673 || // Gurmukhi combining marks
|
|
643
|
+
codePoint >= 2689 && codePoint <= 2690 || // Gujarati combining marks
|
|
644
|
+
codePoint >= 2748 && codePoint <= 2748 || // Gujarati combining marks
|
|
645
|
+
codePoint >= 2753 && codePoint <= 2757 || // Gujarati combining marks
|
|
646
|
+
codePoint >= 2759 && codePoint <= 2760 || // Gujarati combining marks
|
|
647
|
+
codePoint >= 2765 && codePoint <= 2765 || // Gujarati combining marks
|
|
648
|
+
codePoint >= 2786 && codePoint <= 2787 || // Gujarati combining marks
|
|
649
|
+
codePoint >= 2817 && codePoint <= 2817 || // Oriya combining marks
|
|
650
|
+
codePoint >= 2876 && codePoint <= 2876 || // Oriya combining marks
|
|
651
|
+
codePoint >= 2879 && codePoint <= 2879 || // Oriya combining marks
|
|
652
|
+
codePoint >= 2881 && codePoint <= 2883 || // Oriya combining marks
|
|
653
|
+
codePoint >= 2893 && codePoint <= 2893 || // Oriya combining marks
|
|
654
|
+
codePoint >= 2902 && codePoint <= 2902 || // Oriya combining marks
|
|
655
|
+
codePoint >= 2946 && codePoint <= 2946 || // Tamil combining marks
|
|
656
|
+
codePoint >= 3008 && codePoint <= 3008 || // Tamil combining marks
|
|
657
|
+
codePoint >= 3021 && codePoint <= 3021 || // Tamil combining marks
|
|
658
|
+
codePoint >= 3134 && codePoint <= 3136 || // Telugu combining marks
|
|
659
|
+
codePoint >= 3142 && codePoint <= 3144 || // Telugu combining marks
|
|
660
|
+
codePoint >= 3146 && codePoint <= 3149 || // Telugu combining marks
|
|
661
|
+
codePoint >= 3157 && codePoint <= 3158 || // Telugu combining marks
|
|
662
|
+
codePoint >= 3260 && codePoint <= 3260 || // Kannada combining marks
|
|
663
|
+
codePoint >= 3263 && codePoint <= 3263 || // Kannada combining marks
|
|
664
|
+
codePoint >= 3270 && codePoint <= 3270 || // Kannada combining marks
|
|
665
|
+
codePoint >= 3276 && codePoint <= 3277 || // Kannada combining marks
|
|
666
|
+
codePoint >= 3298 && codePoint <= 3299 || // Kannada combining marks
|
|
667
|
+
codePoint >= 3393 && codePoint <= 3395 || // Malayalam combining marks
|
|
668
|
+
codePoint >= 3405 && codePoint <= 3405 || // Malayalam combining marks
|
|
669
|
+
codePoint >= 3530 && codePoint <= 3530 || // Sinhala combining marks
|
|
670
|
+
codePoint >= 3538 && codePoint <= 3540 || // Sinhala combining marks
|
|
671
|
+
codePoint >= 3542 && codePoint <= 3542 || // Sinhala combining marks
|
|
672
|
+
codePoint >= 3633 && codePoint <= 3633 || // Thai combining marks
|
|
673
|
+
codePoint >= 3636 && codePoint <= 3642 || // Thai combining marks
|
|
674
|
+
codePoint >= 3655 && codePoint <= 3662 || // Thai combining marks
|
|
675
|
+
codePoint >= 3761 && codePoint <= 3761 || // Lao combining marks
|
|
676
|
+
codePoint >= 3764 && codePoint <= 3769 || // Lao combining marks
|
|
677
|
+
codePoint >= 3771 && codePoint <= 3772 || // Lao combining marks
|
|
678
|
+
codePoint >= 3784 && codePoint <= 3789 || // Lao combining marks
|
|
679
|
+
codePoint >= 3864 && codePoint <= 3865 || // Tibetan combining marks
|
|
680
|
+
codePoint >= 3893 && codePoint <= 3893 || // Tibetan combining marks
|
|
681
|
+
codePoint >= 3895 && codePoint <= 3895 || // Tibetan combining marks
|
|
682
|
+
codePoint >= 3897 && codePoint <= 3897 || // Tibetan combining marks
|
|
683
|
+
codePoint >= 3953 && codePoint <= 3966 || // Tibetan combining marks
|
|
684
|
+
codePoint >= 3968 && codePoint <= 3972 || // Tibetan combining marks
|
|
685
|
+
codePoint >= 3974 && codePoint <= 3975 || // Tibetan combining marks
|
|
686
|
+
codePoint >= 3984 && codePoint <= 3991 || // Tibetan combining marks
|
|
687
|
+
codePoint >= 3993 && codePoint <= 4028 || // Tibetan combining marks
|
|
688
|
+
codePoint >= 4038 && codePoint <= 4038 || // Tibetan combining marks
|
|
689
|
+
codePoint >= 4146 && codePoint <= 4151 || // Myanmar combining marks
|
|
690
|
+
codePoint >= 4153 && codePoint <= 4154 || // Myanmar combining marks
|
|
691
|
+
codePoint >= 4157 && codePoint <= 4158 || // Myanmar combining marks
|
|
692
|
+
codePoint >= 4184 && codePoint <= 4185 || // Myanmar combining marks
|
|
693
|
+
codePoint >= 4190 && codePoint <= 4192 || // Myanmar combining marks
|
|
694
|
+
codePoint >= 4209 && codePoint <= 4212 || // Myanmar combining marks
|
|
695
|
+
codePoint >= 4226 && codePoint <= 4226 || // Myanmar combining marks
|
|
696
|
+
codePoint >= 4229 && codePoint <= 4230 || // Myanmar combining marks
|
|
697
|
+
codePoint >= 4237 && codePoint <= 4237 || // Myanmar combining marks
|
|
698
|
+
codePoint >= 4957 && codePoint <= 4959 || // Ethiopic combining marks
|
|
699
|
+
codePoint >= 5906 && codePoint <= 5908 || // Tagalog combining marks
|
|
700
|
+
codePoint >= 5938 && codePoint <= 5940 || // Hanunoo combining marks
|
|
701
|
+
codePoint >= 5970 && codePoint <= 5971 || // Buhid combining marks
|
|
702
|
+
codePoint >= 6002 && codePoint <= 6003 || // Tagbanwa combining marks
|
|
703
|
+
codePoint >= 6068 && codePoint <= 6069 || // Khmer combining marks
|
|
704
|
+
codePoint >= 6071 && codePoint <= 6077 || // Khmer combining marks
|
|
705
|
+
codePoint >= 6086 && codePoint <= 6086 || // Khmer combining marks
|
|
706
|
+
codePoint >= 6089 && codePoint <= 6099 || // Khmer combining marks
|
|
707
|
+
codePoint >= 6109 && codePoint <= 6109 || // Khmer combining marks
|
|
708
|
+
codePoint >= 6155 && codePoint <= 6157 || // Mongolian combining marks
|
|
709
|
+
codePoint >= 6313 && codePoint <= 6313 || // Mongolian combining marks
|
|
710
|
+
codePoint >= 6432 && codePoint <= 6434 || // Limbu combining marks
|
|
711
|
+
codePoint >= 6439 && codePoint <= 6440 || // Limbu combining marks
|
|
712
|
+
codePoint >= 6450 && codePoint <= 6450 || // Limbu combining marks
|
|
713
|
+
codePoint >= 6457 && codePoint <= 6459 || // Limbu combining marks
|
|
714
|
+
codePoint >= 6679 && codePoint <= 6680 || // Buginese combining marks
|
|
715
|
+
codePoint >= 6912 && codePoint <= 6915 || // Balinese combining marks
|
|
716
|
+
codePoint >= 6964 && codePoint <= 6964 || // Balinese combining marks
|
|
717
|
+
codePoint >= 6966 && codePoint <= 6970 || // Balinese combining marks
|
|
718
|
+
codePoint >= 6972 && codePoint <= 6972 || // Balinese combining marks
|
|
719
|
+
codePoint >= 6978 && codePoint <= 6978 || // Balinese combining marks
|
|
720
|
+
codePoint >= 7019 && codePoint <= 7027 || // Balinese combining marks
|
|
721
|
+
codePoint >= 7616 && codePoint <= 7679 || // Combining Diacritical Marks Supplement
|
|
722
|
+
codePoint >= 8400 && codePoint <= 8432 || // Combining Diacritical Marks for Symbols
|
|
723
|
+
codePoint >= 11503 && codePoint <= 11505 || // Coptic combining marks
|
|
724
|
+
codePoint >= 11647 && codePoint <= 11647 || // Tifinagh combining marks
|
|
725
|
+
codePoint >= 11744 && codePoint <= 11775 || // Cyrillic Extended-A combining marks
|
|
726
|
+
codePoint >= 42607 && codePoint <= 42610 || // Cyrillic Extended-B combining marks
|
|
727
|
+
codePoint >= 42612 && codePoint <= 42621 || // Cyrillic Extended-B combining marks
|
|
728
|
+
codePoint >= 42654 && codePoint <= 42655 || // Cyrillic Extended-B combining marks
|
|
729
|
+
codePoint >= 42736 && codePoint <= 42737 || // Bamum combining marks
|
|
730
|
+
codePoint >= 43010 && codePoint <= 43010 || // Syloti Nagri combining marks
|
|
731
|
+
codePoint >= 43014 && codePoint <= 43014 || // Syloti Nagri combining marks
|
|
732
|
+
codePoint >= 43019 && codePoint <= 43019 || // Syloti Nagri combining marks
|
|
733
|
+
codePoint >= 43045 && codePoint <= 43046 || // Syloti Nagri combining marks
|
|
734
|
+
codePoint >= 43204 && codePoint <= 43204 || // Saurashtra combining marks
|
|
735
|
+
codePoint >= 43232 && codePoint <= 43249 || // Devanagari Extended combining marks
|
|
736
|
+
codePoint >= 43302 && codePoint <= 43309 || // Kayah Li combining marks
|
|
737
|
+
codePoint >= 43335 && codePoint <= 43345 || // Rejang combining marks
|
|
738
|
+
codePoint >= 43392 && codePoint <= 43394 || // Javanese combining marks
|
|
739
|
+
codePoint >= 43443 && codePoint <= 43443 || // Javanese combining marks
|
|
740
|
+
codePoint >= 43446 && codePoint <= 43449 || // Javanese combining marks
|
|
741
|
+
codePoint >= 43452 && codePoint <= 43452 || // Javanese combining marks
|
|
742
|
+
codePoint >= 43561 && codePoint <= 43566 || // Cham combining marks
|
|
743
|
+
codePoint >= 43569 && codePoint <= 43570 || // Cham combining marks
|
|
744
|
+
codePoint >= 43573 && codePoint <= 43574 || // Cham combining marks
|
|
745
|
+
codePoint >= 43587 && codePoint <= 43587 || // Cham combining marks
|
|
746
|
+
codePoint >= 43596 && codePoint <= 43596 || // Cham combining marks
|
|
747
|
+
codePoint >= 43696 && codePoint <= 43696 || // Tai Viet combining marks
|
|
748
|
+
codePoint >= 43698 && codePoint <= 43700 || // Tai Viet combining marks
|
|
749
|
+
codePoint >= 43703 && codePoint <= 43704 || // Tai Viet combining marks
|
|
750
|
+
codePoint >= 43710 && codePoint <= 43711 || // Tai Viet combining marks
|
|
751
|
+
codePoint >= 43713 && codePoint <= 43713 || // Tai Viet combining marks
|
|
752
|
+
codePoint >= 44005 && codePoint <= 44005 || // Meetei Mayek combining marks
|
|
753
|
+
codePoint >= 44008 && codePoint <= 44008 || // Meetei Mayek combining marks
|
|
754
|
+
codePoint >= 44013 && codePoint <= 44013 || // Meetei Mayek combining marks
|
|
755
|
+
codePoint >= 64286 && codePoint <= 64286 || // Hebrew combining marks
|
|
756
|
+
codePoint >= 65024 && codePoint <= 65039 || // Variation Selectors
|
|
757
|
+
codePoint >= 65056 && codePoint <= 65071 || // Combining Half Marks
|
|
758
|
+
codePoint >= 917760 && codePoint <= 917999) {
|
|
759
|
+
return 0;
|
|
760
|
+
}
|
|
761
|
+
if (codePoint >= 4352 && codePoint <= 4447 || // Hangul Jamo
|
|
762
|
+
codePoint >= 9001 && codePoint <= 9002 || // Left/Right-pointing angle bracket
|
|
763
|
+
codePoint >= 11904 && codePoint <= 12350 || // CJK Radicals Supplement, Kangxi Radicals, CJK Symbols
|
|
764
|
+
codePoint >= 12352 && codePoint <= 13247 || // Hiragana, Katakana, Bopomofo, Hangul Compatibility, Kanbun, CJK Strokes, Enclosed CJK
|
|
765
|
+
codePoint >= 13312 && codePoint <= 19903 || // CJK Unified Ideographs Extension A
|
|
766
|
+
codePoint >= 19968 && codePoint <= 42191 || // CJK Unified Ideographs, Yi Syllables, Yi Radicals
|
|
767
|
+
codePoint >= 43360 && codePoint <= 43388 || // Hangul Jamo Extended-A
|
|
768
|
+
codePoint >= 44032 && codePoint <= 55203 || // Hangul Syllables
|
|
769
|
+
codePoint >= 63744 && codePoint <= 64255 || // CJK Compatibility Ideographs
|
|
770
|
+
codePoint >= 65040 && codePoint <= 65049 || // Vertical forms
|
|
771
|
+
codePoint >= 65072 && codePoint <= 65135 || // CJK Compatibility Forms, Small Form Variants
|
|
772
|
+
codePoint >= 65281 && codePoint <= 65376 || // Fullwidth Forms
|
|
773
|
+
codePoint >= 65504 && codePoint <= 65510 || // Fullwidth Signs
|
|
774
|
+
codePoint >= 110592 && codePoint <= 111359 || // Kana Supplement, Kana Extended-A, Small Kana Extension
|
|
775
|
+
codePoint >= 126980 && codePoint <= 126980 || // Mahjong tile
|
|
776
|
+
codePoint >= 127183 && codePoint <= 127183 || // Playing card
|
|
777
|
+
codePoint >= 127232 && codePoint <= 129535 || // Enclosed Alphanumeric Supplement, Emoticons, Transport, etc.
|
|
778
|
+
codePoint >= 129536 && codePoint <= 129647 || // Chess Symbols
|
|
779
|
+
codePoint >= 129648 && codePoint <= 129791 || // Symbols and Pictographs Extended-A
|
|
780
|
+
codePoint >= 131072 && codePoint <= 196605 || // CJK Unified Ideographs Extension B–G, CJK Compatibility Supplement
|
|
781
|
+
codePoint >= 196608 && codePoint <= 262141) {
|
|
782
|
+
return 2;
|
|
783
|
+
}
|
|
784
|
+
return 1;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/ui/markdown.tsx
|
|
788
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
789
|
+
function MarkdownViewInner({ text }) {
|
|
790
|
+
const stablePrefixRef = useRef2("");
|
|
791
|
+
if (!text.startsWith(stablePrefixRef.current)) stablePrefixRef.current = "";
|
|
792
|
+
const boundary = stablePrefixRef.current.length;
|
|
793
|
+
const suffixTokens = useMemo2(() => {
|
|
794
|
+
try {
|
|
795
|
+
return marked.lexer(text.substring(boundary), { gfm: true, async: false });
|
|
796
|
+
} catch {
|
|
797
|
+
return [];
|
|
798
|
+
}
|
|
799
|
+
}, [text, boundary]);
|
|
800
|
+
let lastContentIndex = suffixTokens.length - 1;
|
|
801
|
+
while (lastContentIndex >= 0 && suffixTokens[lastContentIndex]?.type === "space") lastContentIndex--;
|
|
802
|
+
let advance = 0;
|
|
803
|
+
for (let index = 0; index < lastContentIndex; index++) advance += suffixTokens[index]?.raw.length ?? 0;
|
|
804
|
+
if (advance > 0) stablePrefixRef.current = text.substring(0, boundary + advance);
|
|
805
|
+
const stablePrefix = stablePrefixRef.current;
|
|
806
|
+
const unstableSuffix = text.substring(stablePrefix.length);
|
|
807
|
+
return /* @__PURE__ */ jsxs(Box_default, { flexDirection: "column", width: "100%", children: [
|
|
808
|
+
stablePrefix.length > 0 ? /* @__PURE__ */ jsx(MarkdownSegment, { text: stablePrefix }) : null,
|
|
809
|
+
unstableSuffix.length > 0 ? /* @__PURE__ */ jsx(MarkdownSegment, { text: unstableSuffix }) : null
|
|
810
|
+
] });
|
|
811
|
+
}
|
|
812
|
+
var MarkdownSegment = React.memo(function MarkdownSegment2({ text }) {
|
|
813
|
+
const tokens = useMemo2(() => {
|
|
814
|
+
try {
|
|
815
|
+
return marked.lexer(text, { gfm: true, async: false });
|
|
816
|
+
} catch {
|
|
817
|
+
return [];
|
|
818
|
+
}
|
|
819
|
+
}, [text]);
|
|
820
|
+
return /* @__PURE__ */ jsx(Fragment, { children: tokens.map((token, index) => /* @__PURE__ */ jsx(BlockToken, { token }, index)) });
|
|
821
|
+
});
|
|
822
|
+
var MarkdownView = React.memo(MarkdownViewInner);
|
|
823
|
+
var BlockToken = React.memo(function BlockToken2({ token }) {
|
|
824
|
+
switch (token.type) {
|
|
825
|
+
case "space":
|
|
826
|
+
return /* @__PURE__ */ jsx(Box_default, { height: 1 });
|
|
827
|
+
case "heading":
|
|
828
|
+
return /* @__PURE__ */ jsx(HeadingView, { token });
|
|
829
|
+
case "paragraph":
|
|
830
|
+
return /* @__PURE__ */ jsx(Text, { children: /* @__PURE__ */ jsx(InlineTokens, { tokens: token.tokens ?? [] }) });
|
|
831
|
+
case "code":
|
|
832
|
+
return /* @__PURE__ */ jsx(CodeBlock, { token });
|
|
833
|
+
case "blockquote":
|
|
834
|
+
return /* @__PURE__ */ jsx(BlockquoteView, { token });
|
|
835
|
+
case "list":
|
|
836
|
+
return /* @__PURE__ */ jsx(ListView, { token });
|
|
837
|
+
case "hr":
|
|
838
|
+
return /* @__PURE__ */ jsx(HrView, {});
|
|
839
|
+
case "table":
|
|
840
|
+
return /* @__PURE__ */ jsx(TableView, { token });
|
|
841
|
+
case "html":
|
|
842
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "[html]" });
|
|
843
|
+
default:
|
|
844
|
+
if ("text" in token && typeof token.text === "string") {
|
|
845
|
+
return /* @__PURE__ */ jsx(Text, { children: token.text });
|
|
846
|
+
}
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
function HeadingView({ token }) {
|
|
851
|
+
const styled = useMemo2(() => {
|
|
852
|
+
const inner = renderInlineText(token.tokens);
|
|
853
|
+
switch (token.depth) {
|
|
854
|
+
case 1:
|
|
855
|
+
return chalk2.bold.hex("#7dd3fc")(inner);
|
|
856
|
+
case 2:
|
|
857
|
+
return chalk2.bold.hex("#67e8f9")(inner);
|
|
858
|
+
case 3:
|
|
859
|
+
return chalk2.bold.hex("#93c5fd")(inner);
|
|
860
|
+
default:
|
|
861
|
+
return chalk2.italic.hex("#93c5fd")(inner);
|
|
862
|
+
}
|
|
863
|
+
}, [token.depth, token.tokens]);
|
|
864
|
+
return /* @__PURE__ */ jsx(Text, { children: styled });
|
|
865
|
+
}
|
|
866
|
+
function CodeBlock({ token }) {
|
|
867
|
+
const { stdout } = useStdout();
|
|
868
|
+
const columns = stdout?.columns ?? 80;
|
|
869
|
+
const innerWidth = Math.max(20, columns - 6);
|
|
870
|
+
const styledSource = useMemo2(() => highlightCode(token.text, token.lang), [token.text, token.lang]);
|
|
871
|
+
const lines = useMemo2(() => styledSource.split("\n"), [styledSource]);
|
|
872
|
+
const truncated = useMemo2(() => lines.map((line) => truncateToWidth(line, innerWidth - 2)), [lines, innerWidth]);
|
|
873
|
+
const langLabel = token.lang?.trim();
|
|
874
|
+
const label = langLabel && langLabel.length > 0 ? langLabel : "code";
|
|
875
|
+
const topBarWidth = Math.max(0, columns - 4 - label.length - 4);
|
|
876
|
+
return /* @__PURE__ */ jsxs(Box_default, { flexDirection: "column", paddingX: 1, children: [
|
|
877
|
+
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
878
|
+
"\u256D\u2500 ",
|
|
879
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: label }),
|
|
880
|
+
" ",
|
|
881
|
+
"\u2500".repeat(topBarWidth),
|
|
882
|
+
"\u256E"
|
|
883
|
+
] }),
|
|
884
|
+
/* @__PURE__ */ jsx(Box_default, { flexDirection: "column", paddingX: 1, children: truncated.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "gray", children: line }, i)) }),
|
|
885
|
+
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
886
|
+
"\u2570",
|
|
887
|
+
"\u2500".repeat(Math.max(0, columns - 6)),
|
|
888
|
+
"\u256F"
|
|
889
|
+
] })
|
|
890
|
+
] });
|
|
891
|
+
}
|
|
892
|
+
function BlockquoteView({ token }) {
|
|
893
|
+
return /* @__PURE__ */ jsx(Box_default, { flexDirection: "column", children: token.tokens.map((child, i) => {
|
|
894
|
+
const inline = "tokens" in child ? child.tokens ?? [] : [];
|
|
895
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
896
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "\u2502 " }),
|
|
897
|
+
/* @__PURE__ */ jsx(InlineTokens, { tokens: inline })
|
|
898
|
+
] }, i);
|
|
899
|
+
}) });
|
|
900
|
+
}
|
|
901
|
+
function HrView() {
|
|
902
|
+
const { stdout } = useStdout();
|
|
903
|
+
const width = Math.max(8, (stdout?.columns ?? 80) - 4);
|
|
904
|
+
return /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u2500".repeat(width) });
|
|
905
|
+
}
|
|
906
|
+
function ListView({ token }) {
|
|
907
|
+
const start = typeof token.start === "number" ? token.start : 1;
|
|
908
|
+
return /* @__PURE__ */ jsx(Box_default, { flexDirection: "column", children: token.items.map((item, i) => /* @__PURE__ */ jsx(ListItemView, { item, index: start + i, ordered: token.ordered }, i)) });
|
|
909
|
+
}
|
|
910
|
+
function ListItemView({
|
|
911
|
+
item,
|
|
912
|
+
index,
|
|
913
|
+
ordered
|
|
914
|
+
}) {
|
|
915
|
+
const bullet = ordered ? `${index}.` : "-";
|
|
916
|
+
const inlineTokens = [];
|
|
917
|
+
const blockTokens = [];
|
|
918
|
+
let firstParagraphConsumed = false;
|
|
919
|
+
for (const child of item.tokens) {
|
|
920
|
+
if (child.type === "paragraph") {
|
|
921
|
+
const paragraph = child;
|
|
922
|
+
if (!firstParagraphConsumed) {
|
|
923
|
+
inlineTokens.push(paragraph);
|
|
924
|
+
firstParagraphConsumed = true;
|
|
925
|
+
} else {
|
|
926
|
+
blockTokens.push(child);
|
|
927
|
+
}
|
|
928
|
+
} else if (child.type === "text") {
|
|
929
|
+
inlineTokens.push(child);
|
|
930
|
+
} else {
|
|
931
|
+
blockTokens.push(child);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
return /* @__PURE__ */ jsxs(Box_default, { flexDirection: "column", children: [
|
|
935
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
936
|
+
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
937
|
+
bullet,
|
|
938
|
+
" "
|
|
939
|
+
] }),
|
|
940
|
+
inlineTokens.map((child, i) => {
|
|
941
|
+
if (child.type === "paragraph") {
|
|
942
|
+
return /* @__PURE__ */ jsx(InlineTokens, { tokens: child.tokens ?? [] }, i);
|
|
943
|
+
}
|
|
944
|
+
const tokens = child.tokens ?? [];
|
|
945
|
+
return tokens.length > 0 ? /* @__PURE__ */ jsx(InlineTokens, { tokens }, i) : /* @__PURE__ */ jsx(Text, { children: child.text }, i);
|
|
946
|
+
})
|
|
947
|
+
] }),
|
|
948
|
+
blockTokens.map((child, i) => /* @__PURE__ */ jsx(Box_default, { marginLeft: 2, children: /* @__PURE__ */ jsx(BlockToken, { token: child }) }, i))
|
|
949
|
+
] });
|
|
950
|
+
}
|
|
951
|
+
function TableView({ token }) {
|
|
952
|
+
const { stdout } = useStdout();
|
|
953
|
+
const maxWidth = stdout?.columns ?? 80;
|
|
954
|
+
const colCount = token.header.length;
|
|
955
|
+
if (colCount === 0) return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "[empty table]" });
|
|
956
|
+
const colWidth = Math.max(8, Math.floor((maxWidth - colCount * 3) / colCount));
|
|
957
|
+
const pad = (value, w, align) => {
|
|
958
|
+
const stripped = stripAnsi2(value);
|
|
959
|
+
const width = Math.max(0, w);
|
|
960
|
+
if (stripped.length > width) return stripped.slice(0, width - 1) + "\u2026";
|
|
961
|
+
const diff = width - stripped.length;
|
|
962
|
+
if (align === "right") return " ".repeat(diff) + stripped;
|
|
963
|
+
if (align === "center") {
|
|
964
|
+
const left = Math.floor(diff / 2);
|
|
965
|
+
const right = diff - left;
|
|
966
|
+
return " ".repeat(left) + stripped + " ".repeat(right);
|
|
967
|
+
}
|
|
968
|
+
return stripped + " ".repeat(diff);
|
|
969
|
+
};
|
|
970
|
+
const separator = token.header.map((cell) => {
|
|
971
|
+
const align = cell.align;
|
|
972
|
+
if (align === "left") return ":" + "\u2500".repeat(colWidth - 2) + " ";
|
|
973
|
+
if (align === "right") return " " + "\u2500".repeat(colWidth - 2) + ":";
|
|
974
|
+
if (align === "center") return ":" + "\u2500".repeat(colWidth - 2) + ":";
|
|
975
|
+
return "\u2500".repeat(colWidth);
|
|
976
|
+
});
|
|
977
|
+
return /* @__PURE__ */ jsxs(Box_default, { flexDirection: "column", children: [
|
|
978
|
+
/* @__PURE__ */ jsx(Text, { children: token.header.map((cell, i) => pad(cell.text, colWidth, cell.align ?? "left")).join(" \u2502 ") }),
|
|
979
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: separator.join(" \u2502 ") }),
|
|
980
|
+
token.rows.map((row, ri) => /* @__PURE__ */ jsx(Text, { children: row.map((cell, i) => pad(cell.text, colWidth, token.header[i]?.align ?? "left")).join(" \u2502 ") }, ri))
|
|
981
|
+
] });
|
|
982
|
+
}
|
|
983
|
+
function InlineTokens({ tokens }) {
|
|
984
|
+
return /* @__PURE__ */ jsx(Text, { children: tokens.map((token, i) => /* @__PURE__ */ jsx(InlineToken, { token }, i)) });
|
|
985
|
+
}
|
|
986
|
+
function InlineToken({ token }) {
|
|
987
|
+
switch (token.type) {
|
|
988
|
+
case "text":
|
|
989
|
+
return /* @__PURE__ */ jsx(Text, { children: token.raw ?? token.text });
|
|
990
|
+
case "strong":
|
|
991
|
+
return /* @__PURE__ */ jsx(Text, { bold: true, children: renderInlineText(token.tokens) });
|
|
992
|
+
case "em":
|
|
993
|
+
return /* @__PURE__ */ jsx(Text, { italic: true, children: renderInlineText(token.tokens) });
|
|
994
|
+
case "del":
|
|
995
|
+
return /* @__PURE__ */ jsx(Text, { strikethrough: true, children: renderInlineText(token.tokens) });
|
|
996
|
+
case "codespan":
|
|
997
|
+
return /* @__PURE__ */ jsx(Text, { color: "black", backgroundColor: "gray", children: token.text });
|
|
998
|
+
case "link": {
|
|
999
|
+
const link = token;
|
|
1000
|
+
const inner = renderInlineText(link.tokens);
|
|
1001
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
1002
|
+
inner,
|
|
1003
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, color: "blue", children: [
|
|
1004
|
+
" (",
|
|
1005
|
+
link.href,
|
|
1006
|
+
")"
|
|
1007
|
+
] })
|
|
1008
|
+
] });
|
|
1009
|
+
}
|
|
1010
|
+
case "image": {
|
|
1011
|
+
const img = token;
|
|
1012
|
+
return /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1013
|
+
"[image: ",
|
|
1014
|
+
img.text,
|
|
1015
|
+
"]"
|
|
1016
|
+
] });
|
|
1017
|
+
}
|
|
1018
|
+
case "br":
|
|
1019
|
+
return /* @__PURE__ */ jsx(Text, { children: "\n" });
|
|
1020
|
+
case "escape":
|
|
1021
|
+
return /* @__PURE__ */ jsx(Text, { children: token.text });
|
|
1022
|
+
case "html":
|
|
1023
|
+
return null;
|
|
1024
|
+
default:
|
|
1025
|
+
if ("text" in token && typeof token.text === "string") {
|
|
1026
|
+
return /* @__PURE__ */ jsx(Text, { children: token.text });
|
|
1027
|
+
}
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
var ANSI_RE = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
1032
|
+
function stripAnsi2(value) {
|
|
1033
|
+
return value.replace(ANSI_RE, "");
|
|
1034
|
+
}
|
|
1035
|
+
function visibleWidth(value) {
|
|
1036
|
+
let width = 0;
|
|
1037
|
+
for (const ch of stripAnsi2(value)) {
|
|
1038
|
+
width += charWidth(ch.codePointAt(0) ?? 0);
|
|
1039
|
+
}
|
|
1040
|
+
return width;
|
|
1041
|
+
}
|
|
1042
|
+
function truncateToWidth(value, width) {
|
|
1043
|
+
if (width <= 0) return "";
|
|
1044
|
+
const stripped = stripAnsi2(value);
|
|
1045
|
+
if (visibleWidth(stripped) <= width) return value;
|
|
1046
|
+
const prefix = value.match(/^\x1B\[[0-?]*[ -/]*[@-~]*/)?.[0] ?? "";
|
|
1047
|
+
const suffix = "\x1B[0m";
|
|
1048
|
+
let out = "";
|
|
1049
|
+
let visual = 0;
|
|
1050
|
+
for (const ch of stripped) {
|
|
1051
|
+
const cw = charWidth(ch.codePointAt(0) ?? 0);
|
|
1052
|
+
if (visual + cw > width - 1) break;
|
|
1053
|
+
out += ch;
|
|
1054
|
+
visual += cw;
|
|
1055
|
+
}
|
|
1056
|
+
return prefix + out + "\u2026" + suffix;
|
|
1057
|
+
}
|
|
1058
|
+
function renderInlineText(tokens) {
|
|
1059
|
+
let out = "";
|
|
1060
|
+
for (const tok of tokens) {
|
|
1061
|
+
out += inlineTokenToString(tok);
|
|
1062
|
+
}
|
|
1063
|
+
return out;
|
|
1064
|
+
}
|
|
1065
|
+
function inlineTokenToString(token) {
|
|
1066
|
+
switch (token.type) {
|
|
1067
|
+
case "text":
|
|
1068
|
+
return token.raw ?? token.text;
|
|
1069
|
+
case "strong":
|
|
1070
|
+
return chalk2.bold(renderInlineText(token.tokens));
|
|
1071
|
+
case "em":
|
|
1072
|
+
return chalk2.italic(renderInlineText(token.tokens));
|
|
1073
|
+
case "del":
|
|
1074
|
+
return chalk2.strikethrough(renderInlineText(token.tokens));
|
|
1075
|
+
case "codespan":
|
|
1076
|
+
return chalk2.bgGray.black(` ${token.text} `);
|
|
1077
|
+
case "escape":
|
|
1078
|
+
return token.text;
|
|
1079
|
+
case "br":
|
|
1080
|
+
return "\n";
|
|
1081
|
+
case "link": {
|
|
1082
|
+
const link = token;
|
|
1083
|
+
const inner = renderInlineText(link.tokens);
|
|
1084
|
+
return `${inner} ${chalk2.blue.underline.dim(`(${link.href})`)}`;
|
|
1085
|
+
}
|
|
1086
|
+
default:
|
|
1087
|
+
if ("text" in token && typeof token.text === "string") return token.text;
|
|
1088
|
+
return "";
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// src/ui/assistant-text.tsx
|
|
1093
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1094
|
+
function AssistantTextInner({ text }) {
|
|
1095
|
+
return /* @__PURE__ */ jsx2(MarkdownView, { text });
|
|
1096
|
+
}
|
|
1097
|
+
var AssistantText = React2.memo(AssistantTextInner);
|
|
1098
|
+
|
|
1099
|
+
// src/ui/signals.ts
|
|
1100
|
+
function createSessionInterruptHandler(getSession, shutdown) {
|
|
1101
|
+
return () => {
|
|
1102
|
+
if (getSession()?.interrupt() === "cancelled") return;
|
|
1103
|
+
void shutdown();
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
function installSigintHandler(source, handler) {
|
|
1107
|
+
source.on("SIGINT", handler);
|
|
1108
|
+
return () => {
|
|
1109
|
+
source.off("SIGINT", handler);
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// src/ui/wrap-prompt.ts
|
|
1114
|
+
function wrapPromptInput(input, codePointCursor, options) {
|
|
1115
|
+
const indent = Math.max(0, options.indent ?? 2);
|
|
1116
|
+
const columns = Math.max(1, options.columns);
|
|
1117
|
+
const usable = Math.max(1, columns - indent);
|
|
1118
|
+
const points = [...input];
|
|
1119
|
+
const len = points.length;
|
|
1120
|
+
const clamped = Math.max(0, Math.min(len, codePointCursor));
|
|
1121
|
+
const lines = [];
|
|
1122
|
+
let i = 0;
|
|
1123
|
+
while (i < len) {
|
|
1124
|
+
let lineVisualWidth = 0;
|
|
1125
|
+
const lineStart = i;
|
|
1126
|
+
while (i < len) {
|
|
1127
|
+
const cp = points[i].codePointAt(0) ?? 0;
|
|
1128
|
+
const cw = charWidth(cp);
|
|
1129
|
+
if (lineVisualWidth + cw > usable) break;
|
|
1130
|
+
lineVisualWidth += cw;
|
|
1131
|
+
i += 1;
|
|
1132
|
+
}
|
|
1133
|
+
if (i === lineStart) i += 1;
|
|
1134
|
+
lines.push(points.slice(lineStart, i).join(""));
|
|
1135
|
+
}
|
|
1136
|
+
if (lines.length === 0) lines.push("");
|
|
1137
|
+
let cursor = { line: 0, column: 0 };
|
|
1138
|
+
let consumed = 0;
|
|
1139
|
+
for (let line = 0; line < lines.length; line += 1) {
|
|
1140
|
+
const lineLength = [...lines[line] ?? ""].length;
|
|
1141
|
+
const next = consumed + lineLength;
|
|
1142
|
+
if (clamped <= next) {
|
|
1143
|
+
cursor = { line, column: clamped - consumed };
|
|
1144
|
+
break;
|
|
1145
|
+
}
|
|
1146
|
+
consumed = next;
|
|
1147
|
+
}
|
|
1148
|
+
return { lines, cursor, height: lines.length };
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// src/ui/task-progress.tsx
|
|
1152
|
+
import { useRef as useRef3 } from "react";
|
|
1153
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1154
|
+
function TaskStatusLine({ block, interactive }) {
|
|
1155
|
+
const running = block.state === "running";
|
|
1156
|
+
const foreground = interactive && (block.activity === "model" || block.task?.role === "main");
|
|
1157
|
+
const [ref, time] = useAnimationFrame(running && foreground ? 120 : null);
|
|
1158
|
+
const startedAt = useRef3(void 0);
|
|
1159
|
+
const previousState = useRef3(block.state);
|
|
1160
|
+
if (running && (startedAt.current === void 0 || previousState.current !== "running")) startedAt.current = time;
|
|
1161
|
+
if (!running) startedAt.current = void 0;
|
|
1162
|
+
previousState.current = block.state;
|
|
1163
|
+
const elapsed = block.startedAt === void 0 ? startedAt.current === void 0 ? 0 : Math.max(0, time - startedAt.current) : Math.max(0, Date.now() - block.startedAt);
|
|
1164
|
+
const presentation = statusPresentation(block, elapsed, foreground);
|
|
1165
|
+
const { statusLabel, statusColor, text } = presentation;
|
|
1166
|
+
let before = text;
|
|
1167
|
+
let after = "";
|
|
1168
|
+
if (statusLabel !== void 0) {
|
|
1169
|
+
const marker = ` \xB7 ${statusLabel}`;
|
|
1170
|
+
const idx = before.indexOf(marker);
|
|
1171
|
+
if (idx >= 0) {
|
|
1172
|
+
after = before.slice(idx + marker.length);
|
|
1173
|
+
before = before.slice(0, idx);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
return /* @__PURE__ */ jsxs2(Box_default, { ref, flexDirection: "row", children: [
|
|
1177
|
+
/* @__PURE__ */ jsxs2(Text, { ...presentation.color === void 0 ? {} : { color: presentation.color }, children: [
|
|
1178
|
+
presentation.glyph,
|
|
1179
|
+
" "
|
|
1180
|
+
] }),
|
|
1181
|
+
presentation.badge ? /* @__PURE__ */ jsxs2(Text, { color: presentation.badgeColor, children: [
|
|
1182
|
+
presentation.badge,
|
|
1183
|
+
" "
|
|
1184
|
+
] }) : null,
|
|
1185
|
+
/* @__PURE__ */ jsx3(Text, { ...presentation.color === void 0 ? {} : { color: presentation.color }, children: before }),
|
|
1186
|
+
statusLabel !== void 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1187
|
+
/* @__PURE__ */ jsx3(Text, { ...presentation.color === void 0 ? {} : { color: presentation.color }, children: " \xB7 " }),
|
|
1188
|
+
/* @__PURE__ */ jsx3(Text, { color: statusColor ?? presentation.color, children: statusLabel })
|
|
1189
|
+
] }) : null,
|
|
1190
|
+
after ? /* @__PURE__ */ jsx3(Text, { ...presentation.color === void 0 ? {} : { color: presentation.color }, children: after }) : null
|
|
1191
|
+
] });
|
|
1192
|
+
}
|
|
1193
|
+
function TaskProgressPanel({
|
|
1194
|
+
blocks,
|
|
1195
|
+
interactive,
|
|
1196
|
+
maxHeight = 8,
|
|
1197
|
+
scrollRef,
|
|
1198
|
+
onHoverChange
|
|
1199
|
+
}) {
|
|
1200
|
+
if (blocks.length === 0 || maxHeight <= 0) return null;
|
|
1201
|
+
return /* @__PURE__ */ jsxs2(
|
|
1202
|
+
Box_default,
|
|
1203
|
+
{
|
|
1204
|
+
flexDirection: "column",
|
|
1205
|
+
flexShrink: 0,
|
|
1206
|
+
maxHeight,
|
|
1207
|
+
onMouseEnter: () => onHoverChange?.(true),
|
|
1208
|
+
onMouseLeave: () => onHoverChange?.(false),
|
|
1209
|
+
children: [
|
|
1210
|
+
/* @__PURE__ */ jsx3(Text, { dimColor: true, children: "\u2500\u2500 task progress \u2500\u2500" }),
|
|
1211
|
+
maxHeight > 1 ? /* @__PURE__ */ jsx3(
|
|
1212
|
+
ScrollBox_default,
|
|
1213
|
+
{
|
|
1214
|
+
...scrollRef === void 0 ? {} : { ref: scrollRef },
|
|
1215
|
+
flexDirection: "column",
|
|
1216
|
+
flexShrink: 1,
|
|
1217
|
+
maxHeight: maxHeight - 1,
|
|
1218
|
+
children: blocks.map((block) => /* @__PURE__ */ jsx3(TaskStatusLine, { block, interactive }, block.id))
|
|
1219
|
+
}
|
|
1220
|
+
) : null
|
|
1221
|
+
]
|
|
1222
|
+
}
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// src/ui/file-diff-style.ts
|
|
1227
|
+
var CONTENT = "#f8f8f2";
|
|
1228
|
+
function fileDiffLineStyle(kind) {
|
|
1229
|
+
if (kind === "removed") {
|
|
1230
|
+
return { backgroundColor: "#3d0100", markerColor: "#ff5f56", contentColor: CONTENT };
|
|
1231
|
+
}
|
|
1232
|
+
if (kind === "added") {
|
|
1233
|
+
return { backgroundColor: "#022800", markerColor: "#50c878", contentColor: CONTENT };
|
|
1234
|
+
}
|
|
1235
|
+
return { markerColor: CONTENT, contentColor: CONTENT };
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// src/ui/slash-completion.ts
|
|
1239
|
+
function slashCandidatePresentation(selected) {
|
|
1240
|
+
return {
|
|
1241
|
+
marker: selected ? "\u203A " : " ",
|
|
1242
|
+
rowStyle: {},
|
|
1243
|
+
matchStyle: { color: "ansi:cyan", bold: true }
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
function buildSlashCandidates(commands, plugins, skills) {
|
|
1247
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
1248
|
+
for (const command of commands) {
|
|
1249
|
+
candidates.set(command.name, { name: command.name, kind: "command", description: command.description });
|
|
1250
|
+
}
|
|
1251
|
+
for (const name of plugins) {
|
|
1252
|
+
if (!candidates.has(name)) candidates.set(name, { name, kind: "plugin" });
|
|
1253
|
+
}
|
|
1254
|
+
for (const skill of skills) {
|
|
1255
|
+
if (!candidates.has(skill.name)) {
|
|
1256
|
+
candidates.set(skill.name, {
|
|
1257
|
+
name: skill.name,
|
|
1258
|
+
kind: "skill",
|
|
1259
|
+
description: skill.description,
|
|
1260
|
+
source: skill.source
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
return [...candidates.values()];
|
|
1265
|
+
}
|
|
1266
|
+
function completedSlashTokenLength(input, candidates, menuOpen) {
|
|
1267
|
+
if (menuOpen) return 0;
|
|
1268
|
+
const points = [...input];
|
|
1269
|
+
if (points[0] !== "/") return 0;
|
|
1270
|
+
const whitespace = points.findIndex((point) => /\s/u.test(point));
|
|
1271
|
+
let tokenEnd = whitespace < 0 ? points.length : whitespace;
|
|
1272
|
+
const name = points.slice(1, tokenEnd).join("");
|
|
1273
|
+
const matches = candidates.some((candidate) => candidate.name === name);
|
|
1274
|
+
return matches ? tokenEnd : 0;
|
|
1275
|
+
}
|
|
1276
|
+
function completedSlashTokenPresentation() {
|
|
1277
|
+
return { color: "rgb(120,155,255)", bold: true };
|
|
1278
|
+
}
|
|
1279
|
+
function deriveSlashCompletion(input, cursor, candidates, selectedIndex, visibleLimit = 6) {
|
|
1280
|
+
const points = [...input];
|
|
1281
|
+
const safeCursor = Math.max(0, Math.min(points.length, cursor));
|
|
1282
|
+
if (points[0] !== "/") return null;
|
|
1283
|
+
const whitespace = points.findIndex((point) => /\s/u.test(point));
|
|
1284
|
+
const tokenEnd = whitespace < 0 ? points.length : whitespace;
|
|
1285
|
+
if (safeCursor < 1 || safeCursor > tokenEnd) return null;
|
|
1286
|
+
const query = points.slice(1, tokenEnd).join("");
|
|
1287
|
+
const normalized = query.toLowerCase();
|
|
1288
|
+
const items = candidates.filter(({ name }) => name.toLowerCase().includes(normalized)).sort((left, right) => {
|
|
1289
|
+
const leftPrefix = left.name.toLowerCase().startsWith(normalized);
|
|
1290
|
+
const rightPrefix = right.name.toLowerCase().startsWith(normalized);
|
|
1291
|
+
if (leftPrefix !== rightPrefix) return leftPrefix ? -1 : 1;
|
|
1292
|
+
return left.name.localeCompare(right.name);
|
|
1293
|
+
});
|
|
1294
|
+
if (items.length === 0) return null;
|
|
1295
|
+
const selected = Math.max(0, Math.min(items.length - 1, selectedIndex));
|
|
1296
|
+
const limit = Math.max(1, visibleLimit);
|
|
1297
|
+
const windowStart = Math.max(0, Math.min(selected, items.length - limit));
|
|
1298
|
+
return { query, items, selectedIndex: selected, windowStart };
|
|
1299
|
+
}
|
|
1300
|
+
function moveSlashSelection(index, delta, count) {
|
|
1301
|
+
if (count <= 0) return 0;
|
|
1302
|
+
return (index + delta + count) % count;
|
|
1303
|
+
}
|
|
1304
|
+
function completeSlashSelection(input, cursor, name) {
|
|
1305
|
+
const points = [...input];
|
|
1306
|
+
const safeCursor = Math.max(0, Math.min(points.length, cursor));
|
|
1307
|
+
const tokenEndOffset = points.slice(safeCursor).findIndex((point) => /\s/u.test(point));
|
|
1308
|
+
const tokenEnd = tokenEndOffset < 0 ? points.length : safeCursor + tokenEndOffset;
|
|
1309
|
+
const suffix = points.slice(tokenEnd).join("").replace(/^\s*/u, "");
|
|
1310
|
+
const prefix = `/${name} `;
|
|
1311
|
+
return { text: prefix + suffix, cursor: [...prefix].length };
|
|
1312
|
+
}
|
|
1313
|
+
function matchRanges(value, query) {
|
|
1314
|
+
if (query.length === 0) return [];
|
|
1315
|
+
const normalizedValue = value.toLowerCase();
|
|
1316
|
+
const normalizedQuery = query.toLowerCase();
|
|
1317
|
+
const ranges = [];
|
|
1318
|
+
let start = 0;
|
|
1319
|
+
while (start < normalizedValue.length) {
|
|
1320
|
+
const index = normalizedValue.indexOf(normalizedQuery, start);
|
|
1321
|
+
if (index < 0) break;
|
|
1322
|
+
ranges.push([index, index + normalizedQuery.length]);
|
|
1323
|
+
start = index + normalizedQuery.length;
|
|
1324
|
+
}
|
|
1325
|
+
return ranges;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// src/ui/compact-progress.ts
|
|
1329
|
+
var COMPACT_PROGRESS_CELLS = 10;
|
|
1330
|
+
var COMPACT_PROGRESS_COMPLETE = "#5b8cff";
|
|
1331
|
+
var COMPACT_PROGRESS_REMAINING = "#525761";
|
|
1332
|
+
function compactProgressPresentation(progress) {
|
|
1333
|
+
const normalized = Math.max(0, Math.min(100, Math.floor(progress / 10) * 10));
|
|
1334
|
+
const completed = normalized / 10;
|
|
1335
|
+
return {
|
|
1336
|
+
progress: normalized,
|
|
1337
|
+
cells: Array.from({ length: COMPACT_PROGRESS_CELLS }, (_, index) => ({
|
|
1338
|
+
completed: index < completed,
|
|
1339
|
+
color: index < completed ? COMPACT_PROGRESS_COMPLETE : COMPACT_PROGRESS_REMAINING
|
|
1340
|
+
}))
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// src/ui/mention-completion.ts
|
|
1345
|
+
function basename(path) {
|
|
1346
|
+
const normalized = path.replaceAll("\\", "/");
|
|
1347
|
+
const index = normalized.lastIndexOf("/");
|
|
1348
|
+
return index < 0 ? path : normalized.slice(index + 1);
|
|
1349
|
+
}
|
|
1350
|
+
function mentionCandidatePresentation(selected) {
|
|
1351
|
+
if (selected) {
|
|
1352
|
+
const selectedText = { color: "rgb(120,155,255)", bold: true };
|
|
1353
|
+
return {
|
|
1354
|
+
marker: "\u203A ",
|
|
1355
|
+
textStyle: selectedText,
|
|
1356
|
+
highlightMatches: false,
|
|
1357
|
+
matchStyle: { color: "ansi:cyan", bold: true }
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
return {
|
|
1361
|
+
marker: " ",
|
|
1362
|
+
textStyle: {},
|
|
1363
|
+
highlightMatches: true,
|
|
1364
|
+
matchStyle: { color: "ansi:cyan", bold: true }
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
function buildMentionCandidates(paths) {
|
|
1368
|
+
return [...new Set(paths.map((path) => path.replaceAll("\\", "/")))].sort(comparePaths);
|
|
1369
|
+
}
|
|
1370
|
+
function deriveMentionCompletion(input, cursor, candidates, selectedIndex, visibleLimit = 6) {
|
|
1371
|
+
const token = mentionTokenAtCursor(input, cursor);
|
|
1372
|
+
if (token === null) return null;
|
|
1373
|
+
const normalizedQuery = token.query.toLowerCase();
|
|
1374
|
+
const items = candidates.filter((path) => path.toLowerCase().includes(normalizedQuery)).sort((left, right) => {
|
|
1375
|
+
const rankDelta = mentionRank(left, normalizedQuery) - mentionRank(right, normalizedQuery);
|
|
1376
|
+
return rankDelta || comparePaths(left, right);
|
|
1377
|
+
});
|
|
1378
|
+
if (items.length === 0) return null;
|
|
1379
|
+
const selected = Math.max(0, Math.min(items.length - 1, selectedIndex));
|
|
1380
|
+
const limit = Math.max(1, visibleLimit);
|
|
1381
|
+
const windowStart = Math.max(0, Math.min(selected, items.length - limit));
|
|
1382
|
+
return { query: token.query, items, selectedIndex: selected, windowStart };
|
|
1383
|
+
}
|
|
1384
|
+
function moveMentionSelection(index, delta, count) {
|
|
1385
|
+
if (count <= 0) return 0;
|
|
1386
|
+
return (index + delta + count) % count;
|
|
1387
|
+
}
|
|
1388
|
+
function completeMentionSelection(input, cursor, path) {
|
|
1389
|
+
const points = [...input];
|
|
1390
|
+
const token = mentionTokenAtCursor(input, cursor);
|
|
1391
|
+
const safeCursor = Math.max(0, Math.min(points.length, cursor));
|
|
1392
|
+
if (token === null) return { text: input, cursor: safeCursor };
|
|
1393
|
+
const escapedPath = path.replaceAll("\\", "/").replaceAll(" ", "\\ ");
|
|
1394
|
+
const inserted = `@${escapedPath} `;
|
|
1395
|
+
const prefix = points.slice(0, token.start).join("");
|
|
1396
|
+
const suffix = points.slice(token.end).join("").replace(/^\s*/u, "");
|
|
1397
|
+
return {
|
|
1398
|
+
text: prefix + inserted + suffix,
|
|
1399
|
+
cursor: [...prefix + inserted].length,
|
|
1400
|
+
span: { start: [...prefix].length, end: [...prefix + inserted].length }
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
function mentionTokenAtCursor(input, cursor) {
|
|
1404
|
+
const points = [...input];
|
|
1405
|
+
const safeCursor = Math.max(0, Math.min(points.length, cursor));
|
|
1406
|
+
let start = safeCursor;
|
|
1407
|
+
while (start > 0 && !isTokenBoundary(points, start - 1)) start -= 1;
|
|
1408
|
+
if (points[start] !== "@" || safeCursor <= start) return null;
|
|
1409
|
+
let end = safeCursor;
|
|
1410
|
+
while (end < points.length && !isTokenBoundary(points, end)) end += 1;
|
|
1411
|
+
return {
|
|
1412
|
+
start,
|
|
1413
|
+
end,
|
|
1414
|
+
query: unescapeQuery(points.slice(start + 1, end).join(""))
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
function isTokenBoundary(points, index) {
|
|
1418
|
+
if (!/\s/u.test(points[index] ?? "")) return false;
|
|
1419
|
+
let slashCount = 0;
|
|
1420
|
+
for (let cursor = index - 1; cursor >= 0 && points[cursor] === "\\"; cursor -= 1) slashCount += 1;
|
|
1421
|
+
return slashCount % 2 === 0;
|
|
1422
|
+
}
|
|
1423
|
+
function unescapeQuery(value) {
|
|
1424
|
+
return value.replace(/\\(\s)/gu, "$1");
|
|
1425
|
+
}
|
|
1426
|
+
function mentionRank(path, query) {
|
|
1427
|
+
const normalizedPath = path.toLowerCase();
|
|
1428
|
+
const filename = basename(normalizedPath);
|
|
1429
|
+
if (filename.startsWith(query)) return 0;
|
|
1430
|
+
if (normalizedPath.startsWith(query)) return 1;
|
|
1431
|
+
return 2;
|
|
1432
|
+
}
|
|
1433
|
+
function comparePaths(left, right) {
|
|
1434
|
+
return left.localeCompare(right, "en");
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// src/ui/welcome.tsx
|
|
1438
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1439
|
+
var WIDE_WELCOME_COLUMNS = 72;
|
|
1440
|
+
var FLAVOR_ACCENT = "#67D4FF";
|
|
1441
|
+
var FLAVOR_WORDMARK = [
|
|
1442
|
+
"\u250C\u2500\u2510\u252C \u250C\u2500\u2510\u252C \u252C\u250C\u2500\u2510\u252C\u2500\u2510",
|
|
1443
|
+
"\u251C\u2524 \u2502 \u251C\u2500\u2524\u2514\u2510\u250C\u2518\u2502 \u2502\u251C\u252C\u2518",
|
|
1444
|
+
"\u2514 \u2534\u2500\u2518\u2534 \u2534 \u2514\u2518 \u2514\u2500\u2518\u2534\u2514\u2500"
|
|
1445
|
+
].join("\n");
|
|
1446
|
+
function WelcomeCard({ model, serviceName, workspaceName, columns }) {
|
|
1447
|
+
const wide = Math.max(1, Math.floor(columns)) >= WIDE_WELCOME_COLUMNS;
|
|
1448
|
+
return /* @__PURE__ */ jsx4(Box_default, { width: "100%", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: wide ? /* @__PURE__ */ jsxs3(Box_default, { width: "100%", flexDirection: "row", children: [
|
|
1449
|
+
/* @__PURE__ */ jsxs3(
|
|
1450
|
+
Box_default,
|
|
1451
|
+
{
|
|
1452
|
+
width: "36%",
|
|
1453
|
+
flexDirection: "column",
|
|
1454
|
+
alignItems: "center",
|
|
1455
|
+
borderStyle: "single",
|
|
1456
|
+
borderTop: false,
|
|
1457
|
+
borderBottom: false,
|
|
1458
|
+
borderLeft: false,
|
|
1459
|
+
borderColor: "yellow",
|
|
1460
|
+
paddingRight: 1,
|
|
1461
|
+
children: [
|
|
1462
|
+
/* @__PURE__ */ jsx4(Text, { bold: true, color: "yellowBright", children: "Welcome back!" }),
|
|
1463
|
+
/* @__PURE__ */ jsx4(Text, { color: FLAVOR_ACCENT, children: FLAVOR_WORDMARK }),
|
|
1464
|
+
serviceName === void 0 ? null : /* @__PURE__ */ jsx4(Text, { color: "cyan", wrap: "truncate-end", children: serviceName }),
|
|
1465
|
+
/* @__PURE__ */ jsx4(Text, { dimColor: true, wrap: "truncate-end", children: model }),
|
|
1466
|
+
/* @__PURE__ */ jsx4(Text, { dimColor: true, wrap: "truncate-end", children: workspaceName })
|
|
1467
|
+
]
|
|
1468
|
+
}
|
|
1469
|
+
),
|
|
1470
|
+
/* @__PURE__ */ jsxs3(Box_default, { flexGrow: 1, flexDirection: "column", paddingLeft: 1, children: [
|
|
1471
|
+
/* @__PURE__ */ jsx4(Text, { bold: true, color: "yellowBright", children: "Tips for getting started" }),
|
|
1472
|
+
/* @__PURE__ */ jsxs3(Text, { children: [
|
|
1473
|
+
"Run ",
|
|
1474
|
+
/* @__PURE__ */ jsx4(Text, { color: "cyan", children: "/init" }),
|
|
1475
|
+
" to create or refresh FLAVOR.md"
|
|
1476
|
+
] }),
|
|
1477
|
+
/* @__PURE__ */ jsxs3(Text, { children: [
|
|
1478
|
+
"Type ",
|
|
1479
|
+
/* @__PURE__ */ jsx4(Text, { color: "cyan", children: "@" }),
|
|
1480
|
+
" to attach a project file"
|
|
1481
|
+
] }),
|
|
1482
|
+
/* @__PURE__ */ jsx4(Box_default, { height: 1 }),
|
|
1483
|
+
/* @__PURE__ */ jsx4(Text, { bold: true, color: "yellowBright", children: "Quick commands" }),
|
|
1484
|
+
/* @__PURE__ */ jsxs3(Text, { children: [
|
|
1485
|
+
/* @__PURE__ */ jsx4(Text, { color: "cyan", children: "/help" }),
|
|
1486
|
+
" \xB7 ",
|
|
1487
|
+
/* @__PURE__ */ jsx4(Text, { color: "cyan", children: "/config" }),
|
|
1488
|
+
" \xB7 ",
|
|
1489
|
+
/* @__PURE__ */ jsx4(Text, { color: "cyan", children: "/tasks" })
|
|
1490
|
+
] })
|
|
1491
|
+
] })
|
|
1492
|
+
] }) : /* @__PURE__ */ jsxs3(Box_default, { width: "100%", flexDirection: "column", children: [
|
|
1493
|
+
/* @__PURE__ */ jsx4(Text, { bold: true, color: FLAVOR_ACCENT, children: "\u25C6 Flavor Code" }),
|
|
1494
|
+
/* @__PURE__ */ jsx4(Text, { children: "Welcome back!" }),
|
|
1495
|
+
/* @__PURE__ */ jsxs3(Text, { dimColor: true, wrap: "truncate-end", children: [
|
|
1496
|
+
model,
|
|
1497
|
+
" \xB7 ",
|
|
1498
|
+
workspaceName
|
|
1499
|
+
] }),
|
|
1500
|
+
/* @__PURE__ */ jsxs3(Text, { children: [
|
|
1501
|
+
/* @__PURE__ */ jsx4(Text, { color: "cyan", children: "/init" }),
|
|
1502
|
+
" setup \xB7 ",
|
|
1503
|
+
/* @__PURE__ */ jsx4(Text, { color: "cyan", children: "/help" }),
|
|
1504
|
+
" commands"
|
|
1505
|
+
] })
|
|
1506
|
+
] }) });
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// src/session/assets.ts
|
|
1510
|
+
import { createHash, randomUUID } from "crypto";
|
|
1511
|
+
import { constants } from "fs";
|
|
1512
|
+
import { mkdir, open, rename, rm } from "fs/promises";
|
|
1513
|
+
import { basename as basename2, join, resolve } from "path";
|
|
1514
|
+
var DEFAULT_MAX_IMAGES = 5;
|
|
1515
|
+
var DEFAULT_MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
1516
|
+
var DEFAULT_MAX_TOTAL_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
1517
|
+
var SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
1518
|
+
var BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
1519
|
+
var SessionAssetStore = class {
|
|
1520
|
+
#root;
|
|
1521
|
+
#maxImages;
|
|
1522
|
+
#maxImageBytes;
|
|
1523
|
+
#maxTotalBytes;
|
|
1524
|
+
constructor(options) {
|
|
1525
|
+
this.#root = join(resolve(options.workspace), ".flavor", "session-assets");
|
|
1526
|
+
this.#maxImages = positiveInteger(options.maxImages ?? DEFAULT_MAX_IMAGES, "maxImages");
|
|
1527
|
+
this.#maxImageBytes = positiveInteger(options.maxImageBytes ?? DEFAULT_MAX_IMAGE_BYTES, "maxImageBytes");
|
|
1528
|
+
this.#maxTotalBytes = positiveInteger(options.maxTotalBytes ?? DEFAULT_MAX_TOTAL_IMAGE_BYTES, "maxTotalBytes");
|
|
1529
|
+
}
|
|
1530
|
+
async store(sessionId, inputs) {
|
|
1531
|
+
if (!SESSION_ID.test(sessionId)) throw new Error("Invalid session id");
|
|
1532
|
+
if (inputs.length > this.#maxImages) {
|
|
1533
|
+
throw new Error(`A prompt can contain at most ${this.#maxImages} images`);
|
|
1534
|
+
}
|
|
1535
|
+
const decoded = inputs.map((input) => decodeAttachment(input, this.#maxImageBytes));
|
|
1536
|
+
const total = decoded.reduce((sum, item) => sum + item.data.byteLength, 0);
|
|
1537
|
+
if (total > this.#maxTotalBytes) {
|
|
1538
|
+
throw new Error(`Image attachments exceed the maximum total size of ${this.#maxTotalBytes} bytes`);
|
|
1539
|
+
}
|
|
1540
|
+
if (decoded.length === 0) return [];
|
|
1541
|
+
const directory = join(this.#root, sessionId);
|
|
1542
|
+
await mkdir(directory, { recursive: true, mode: 448 });
|
|
1543
|
+
const output = [];
|
|
1544
|
+
for (const item of decoded) {
|
|
1545
|
+
const sha256 = createHash("sha256").update(item.data).digest("hex");
|
|
1546
|
+
const target = join(directory, `${sha256}${extension(item.mediaType)}`);
|
|
1547
|
+
await writeOnce(target, item.data);
|
|
1548
|
+
output.push({
|
|
1549
|
+
type: "image",
|
|
1550
|
+
source: { type: "file", path: target },
|
|
1551
|
+
mediaType: item.mediaType,
|
|
1552
|
+
sha256,
|
|
1553
|
+
bytes: item.data.byteLength,
|
|
1554
|
+
name: safeName(item.name)
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
return output;
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
function decodeAttachment(input, maxBytes) {
|
|
1561
|
+
if (!input.name || input.name.length > 255) throw new Error("Image name must be between 1 and 255 characters");
|
|
1562
|
+
if (!["image/png", "image/jpeg", "image/webp"].includes(input.mediaType)) {
|
|
1563
|
+
throw new Error(`Unsupported image media type: ${String(input.mediaType)}`);
|
|
1564
|
+
}
|
|
1565
|
+
if (!input.dataBase64 || !BASE64.test(input.dataBase64)) throw new Error("Image data is not valid base64");
|
|
1566
|
+
const data = Buffer.from(input.dataBase64, "base64");
|
|
1567
|
+
if (data.toString("base64") !== input.dataBase64) throw new Error("Image data is not canonical base64");
|
|
1568
|
+
if (data.byteLength > maxBytes) throw new Error(`Image exceeds the maximum size of ${maxBytes} bytes`);
|
|
1569
|
+
const detected = detectMediaType(data);
|
|
1570
|
+
if (detected !== input.mediaType) {
|
|
1571
|
+
throw new Error(`Image media type ${input.mediaType} does not match file bytes`);
|
|
1572
|
+
}
|
|
1573
|
+
return { ...input, data };
|
|
1574
|
+
}
|
|
1575
|
+
function detectMediaType(data) {
|
|
1576
|
+
if (data.length >= 8 && data.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
1577
|
+
return "image/png";
|
|
1578
|
+
}
|
|
1579
|
+
if (data.length >= 3 && data[0] === 255 && data[1] === 216 && data[2] === 255) return "image/jpeg";
|
|
1580
|
+
if (data.length >= 12 && data.subarray(0, 4).toString("ascii") === "RIFF" && data.subarray(8, 12).toString("ascii") === "WEBP") return "image/webp";
|
|
1581
|
+
return void 0;
|
|
1582
|
+
}
|
|
1583
|
+
function extension(mediaType) {
|
|
1584
|
+
if (mediaType === "image/png") return ".png";
|
|
1585
|
+
if (mediaType === "image/jpeg") return ".jpg";
|
|
1586
|
+
return ".webp";
|
|
1587
|
+
}
|
|
1588
|
+
function safeName(name) {
|
|
1589
|
+
return basename2(name).replace(/[\u0000-\u001f\u007f]/g, "_").slice(0, 255) || "image";
|
|
1590
|
+
}
|
|
1591
|
+
async function writeOnce(target, data) {
|
|
1592
|
+
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
1593
|
+
let handle;
|
|
1594
|
+
try {
|
|
1595
|
+
handle = await open(temporary, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 384);
|
|
1596
|
+
await handle.writeFile(data);
|
|
1597
|
+
await handle.sync();
|
|
1598
|
+
await handle.close();
|
|
1599
|
+
handle = void 0;
|
|
1600
|
+
try {
|
|
1601
|
+
await rename(temporary, target);
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
if (!isCode(error, "EEXIST") && !isCode(error, "EPERM")) throw error;
|
|
1604
|
+
}
|
|
1605
|
+
} finally {
|
|
1606
|
+
await handle?.close().catch(() => void 0);
|
|
1607
|
+
await rm(temporary, { force: true }).catch(() => void 0);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
function positiveInteger(value, name) {
|
|
1611
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`${name} must be a positive integer`);
|
|
1612
|
+
return value;
|
|
1613
|
+
}
|
|
1614
|
+
function isCode(error, code) {
|
|
1615
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
// src/ui/clipboard-image.ts
|
|
1619
|
+
var CLIPBOARD_TIMEOUT_MS = 1e4;
|
|
1620
|
+
var NO_IMAGE_EXIT_CODE = 3;
|
|
1621
|
+
var READ_WINDOWS_CLIPBOARD_IMAGE = String.raw`
|
|
1622
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
1623
|
+
Add-Type -AssemblyName System.Drawing
|
|
1624
|
+
$image = [Windows.Forms.Clipboard]::GetImage()
|
|
1625
|
+
if ($null -eq $image) { exit 3 }
|
|
1626
|
+
$stream = New-Object System.IO.MemoryStream
|
|
1627
|
+
try {
|
|
1628
|
+
$image.Save($stream, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
1629
|
+
[Console]::Out.Write([Convert]::ToBase64String($stream.ToArray()))
|
|
1630
|
+
} finally {
|
|
1631
|
+
$stream.Dispose()
|
|
1632
|
+
$image.Dispose()
|
|
1633
|
+
}
|
|
1634
|
+
`.trim();
|
|
1635
|
+
var READ_MAC_CLIPBOARD_IMAGE = String.raw`
|
|
1636
|
+
ObjC.import("AppKit");
|
|
1637
|
+
ObjC.import("Foundation");
|
|
1638
|
+
ObjC.import("stdlib");
|
|
1639
|
+
|
|
1640
|
+
const pasteboard = $.NSPasteboard.generalPasteboard;
|
|
1641
|
+
let data = pasteboard.dataForType($.NSPasteboardTypePNG);
|
|
1642
|
+
if (!data) {
|
|
1643
|
+
const tiff = pasteboard.dataForType($.NSPasteboardTypeTIFF);
|
|
1644
|
+
if (tiff) {
|
|
1645
|
+
const representation = $.NSBitmapImageRep.imageRepWithData(tiff);
|
|
1646
|
+
if (representation) {
|
|
1647
|
+
data = representation.representationUsingTypeProperties(
|
|
1648
|
+
$.NSBitmapImageFileTypePNG,
|
|
1649
|
+
$({})
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
if (!data) {
|
|
1655
|
+
$.exit(3);
|
|
1656
|
+
}
|
|
1657
|
+
const encoded = data.base64EncodedStringWithOptions(0);
|
|
1658
|
+
console.log(ObjC.unwrap(encoded));
|
|
1659
|
+
`.trim();
|
|
1660
|
+
function shouldReadClipboardImage(input, key, isPasted) {
|
|
1661
|
+
return (key.ctrl || key.meta === true) && input.toLowerCase() === "v" || isPasted && input.length === 0;
|
|
1662
|
+
}
|
|
1663
|
+
async function readClipboardImage(options = {}) {
|
|
1664
|
+
const platform = options.platform ?? process.platform;
|
|
1665
|
+
if (platform === "win32") return readWindowsClipboardImage({ ...options, platform });
|
|
1666
|
+
if (platform === "darwin") return readMacClipboardImage({ ...options, platform });
|
|
1667
|
+
throw new Error("Pasting clipboard images is currently supported on Windows and macOS only");
|
|
1668
|
+
}
|
|
1669
|
+
async function readWindowsClipboardImage(options = {}) {
|
|
1670
|
+
const platform = options.platform ?? process.platform;
|
|
1671
|
+
if (platform !== "win32") {
|
|
1672
|
+
throw new Error("Pasting clipboard images is currently supported on Windows only");
|
|
1673
|
+
}
|
|
1674
|
+
const result = await (options.run ?? execFileNoThrow)(
|
|
1675
|
+
"powershell.exe",
|
|
1676
|
+
["-NoProfile", "-NonInteractive", "-STA", "-Command", READ_WINDOWS_CLIPBOARD_IMAGE],
|
|
1677
|
+
{ timeout: CLIPBOARD_TIMEOUT_MS, useCwd: false }
|
|
1678
|
+
);
|
|
1679
|
+
return attachmentFromResult(result, options.now?.() ?? /* @__PURE__ */ new Date());
|
|
1680
|
+
}
|
|
1681
|
+
async function readMacClipboardImage(options = {}) {
|
|
1682
|
+
const platform = options.platform ?? process.platform;
|
|
1683
|
+
if (platform !== "darwin") {
|
|
1684
|
+
throw new Error("Pasting clipboard images through AppKit is supported on macOS only");
|
|
1685
|
+
}
|
|
1686
|
+
const result = await (options.run ?? execFileNoThrow)(
|
|
1687
|
+
"osascript",
|
|
1688
|
+
["-l", "JavaScript", "-e", READ_MAC_CLIPBOARD_IMAGE],
|
|
1689
|
+
{ timeout: CLIPBOARD_TIMEOUT_MS, useCwd: false }
|
|
1690
|
+
);
|
|
1691
|
+
return attachmentFromResult(result, options.now?.() ?? /* @__PURE__ */ new Date());
|
|
1692
|
+
}
|
|
1693
|
+
function attachmentFromResult(result, now) {
|
|
1694
|
+
if (result.code === NO_IMAGE_EXIT_CODE) return void 0;
|
|
1695
|
+
if (result.code !== 0) {
|
|
1696
|
+
const detail = result.stderr.trim() || result.error?.trim();
|
|
1697
|
+
throw new Error(detail ? `Could not read the clipboard image: ${detail}` : "Could not read the clipboard image");
|
|
1698
|
+
}
|
|
1699
|
+
const dataBase64 = result.stdout.trim();
|
|
1700
|
+
if (dataBase64.length === 0) return void 0;
|
|
1701
|
+
return {
|
|
1702
|
+
name: `clipboard-${timestamp(now)}.png`,
|
|
1703
|
+
mediaType: "image/png",
|
|
1704
|
+
dataBase64
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
function timestamp(value) {
|
|
1708
|
+
const year = value.getUTCFullYear();
|
|
1709
|
+
const month = String(value.getUTCMonth() + 1).padStart(2, "0");
|
|
1710
|
+
const day = String(value.getUTCDate()).padStart(2, "0");
|
|
1711
|
+
const hour = String(value.getUTCHours()).padStart(2, "0");
|
|
1712
|
+
const minute = String(value.getUTCMinutes()).padStart(2, "0");
|
|
1713
|
+
const second = String(value.getUTCSeconds()).padStart(2, "0");
|
|
1714
|
+
return `${year}${month}${day}-${hour}${minute}${second}`;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
// src/ui/app.tsx
|
|
1718
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1719
|
+
var HISTORY_CAP = 200;
|
|
1720
|
+
var BUILTIN_SLASH_CANDIDATES = MVP_COMMANDS.map((name) => ({ name, description: COMMAND_DESCRIPTIONS[name] }));
|
|
1721
|
+
var PROMPT_HORIZONTAL_PADDING = 1;
|
|
1722
|
+
function removeLastCliImageOnBackspace(input, cursor, images) {
|
|
1723
|
+
if (input.length === 0 && cursor === 0 && images.length > 0) {
|
|
1724
|
+
return { handled: true, images: images.slice(0, -1) };
|
|
1725
|
+
}
|
|
1726
|
+
return { handled: false, images: [...images] };
|
|
1727
|
+
}
|
|
1728
|
+
function prepareCliSubmission(input, images, activeSession) {
|
|
1729
|
+
const entered = input.trim();
|
|
1730
|
+
if (entered.length === 0 && images.length === 0) return { kind: "empty" };
|
|
1731
|
+
if (images.length > 0 && entered.startsWith("/")) {
|
|
1732
|
+
return { kind: "error", message: "Image attachments cannot be used with slash commands." };
|
|
1733
|
+
}
|
|
1734
|
+
if (images.length > 0 && activeSession) {
|
|
1735
|
+
return { kind: "error", message: "Images can only be attached to a new prompt." };
|
|
1736
|
+
}
|
|
1737
|
+
const text = entered || "Analyze the attached image(s).";
|
|
1738
|
+
if (images.length === 0) return { kind: "ready", text, displayText: text };
|
|
1739
|
+
const content = [{ type: "text", text }, ...images];
|
|
1740
|
+
return {
|
|
1741
|
+
kind: "ready",
|
|
1742
|
+
text,
|
|
1743
|
+
displayText: modelContentTranscriptText(content),
|
|
1744
|
+
content
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
function promptDelivery(activeSession, key) {
|
|
1748
|
+
if (!activeSession) return "prompt";
|
|
1749
|
+
return "followUp";
|
|
1750
|
+
}
|
|
1751
|
+
function resolvePromptDelivery(activeSession, key, input) {
|
|
1752
|
+
if (activeSession) {
|
|
1753
|
+
const followUp = input.match(/^\/follow-?up\s+([\s\S]+)$/iu);
|
|
1754
|
+
if (followUp !== null) return { delivery: "followUp", prompt: followUp[1].trim() };
|
|
1755
|
+
const steer = input.match(/^\/steer\s+([\s\S]+)$/iu);
|
|
1756
|
+
if (steer !== null) return { delivery: "steer", prompt: steer[1].trim() };
|
|
1757
|
+
}
|
|
1758
|
+
return { delivery: promptDelivery(activeSession, key), prompt: input };
|
|
1759
|
+
}
|
|
1760
|
+
var SinglePendingPrompt = class {
|
|
1761
|
+
#value;
|
|
1762
|
+
get value() {
|
|
1763
|
+
return this.#value;
|
|
1764
|
+
}
|
|
1765
|
+
queue(prompt) {
|
|
1766
|
+
const value = prompt.trim();
|
|
1767
|
+
if (value.length === 0 || this.#value !== void 0) return false;
|
|
1768
|
+
this.#value = value;
|
|
1769
|
+
return true;
|
|
1770
|
+
}
|
|
1771
|
+
cancel() {
|
|
1772
|
+
const value = this.#value;
|
|
1773
|
+
this.#value = void 0;
|
|
1774
|
+
return value;
|
|
1775
|
+
}
|
|
1776
|
+
take() {
|
|
1777
|
+
return this.cancel();
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
async function runTerminalSubmissionChain(options) {
|
|
1781
|
+
let prompt = options.initialPrompt;
|
|
1782
|
+
let initial = true;
|
|
1783
|
+
while (prompt !== void 0) {
|
|
1784
|
+
options.onStart(initial ? options.initialDisplayPrompt ?? prompt : prompt);
|
|
1785
|
+
if (initial && options.initialSubmit !== void 0) {
|
|
1786
|
+
try {
|
|
1787
|
+
await options.initialSubmit(prompt);
|
|
1788
|
+
} catch (error) {
|
|
1789
|
+
safeReport(options.report, safeUiError(error));
|
|
1790
|
+
}
|
|
1791
|
+
} else {
|
|
1792
|
+
await submitSafely(options.session, prompt, options.report);
|
|
1793
|
+
}
|
|
1794
|
+
options.onFinish();
|
|
1795
|
+
if (options.shouldContinue?.() === false) return;
|
|
1796
|
+
prompt = options.pending.take();
|
|
1797
|
+
if (prompt !== void 0) options.onPendingConsumed();
|
|
1798
|
+
initial = false;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
function classifyTerminalInput(key) {
|
|
1802
|
+
if (key.wheelUp) return { type: "scroll", rows: -3 };
|
|
1803
|
+
if (key.wheelDown) return { type: "scroll", rows: 3 };
|
|
1804
|
+
if (key.pageUp) return { type: "page", fraction: -0.5 };
|
|
1805
|
+
if (key.pageDown) return { type: "page", fraction: 0.5 };
|
|
1806
|
+
if (key.upArrow) return { type: "history", direction: "up" };
|
|
1807
|
+
if (key.downArrow) return { type: "history", direction: "down" };
|
|
1808
|
+
return null;
|
|
1809
|
+
}
|
|
1810
|
+
function completionKeyAction(key, menuOpen) {
|
|
1811
|
+
if (!menuOpen) return null;
|
|
1812
|
+
if (key.upArrow) return { type: "select", delta: -1 };
|
|
1813
|
+
if (key.downArrow) return { type: "select", delta: 1 };
|
|
1814
|
+
if (key.tab) return { type: "complete" };
|
|
1815
|
+
if (key.escape) return { type: "dismiss" };
|
|
1816
|
+
return null;
|
|
1817
|
+
}
|
|
1818
|
+
function slashKeyAction(key, completion) {
|
|
1819
|
+
return completionKeyAction(key, completion !== null);
|
|
1820
|
+
}
|
|
1821
|
+
function App({ workspace, home, resumeSession }) {
|
|
1822
|
+
const { exit } = use_app_default();
|
|
1823
|
+
const { stdout } = useStdout();
|
|
1824
|
+
const [runtime, setRuntime] = useState2();
|
|
1825
|
+
const [input, setInput] = useState2("");
|
|
1826
|
+
const [pastedBlocks, setPastedBlocks] = useState2([]);
|
|
1827
|
+
const [imageAttachments, setImageAttachments] = useState2([]);
|
|
1828
|
+
const [clipboardNotice, setClipboardNotice] = useState2();
|
|
1829
|
+
const [history, setHistory] = useState2([]);
|
|
1830
|
+
const [historyCursor, setHistoryCursor] = useState2(0);
|
|
1831
|
+
const [promptCursor, setPromptCursor] = useState2(0);
|
|
1832
|
+
const [slashCandidates, setSlashCandidates] = useState2(
|
|
1833
|
+
() => buildSlashCandidates(BUILTIN_SLASH_CANDIDATES, [], [])
|
|
1834
|
+
);
|
|
1835
|
+
const [slashSelection, setSlashSelection] = useState2(0);
|
|
1836
|
+
const [dismissedSlashInput, setDismissedSlashInput] = useState2();
|
|
1837
|
+
const [mentionCandidates, setMentionCandidates] = useState2([]);
|
|
1838
|
+
const [mentionSelection, setMentionSelection] = useState2(0);
|
|
1839
|
+
const [dismissedMentionInput, setDismissedMentionInput] = useState2();
|
|
1840
|
+
const [pendingPrompt, setPendingPrompt] = useState2();
|
|
1841
|
+
const [revision, setRevision] = useState2(0);
|
|
1842
|
+
const [ideContext, setIdeContext] = useState2();
|
|
1843
|
+
const [questionIndex, setQuestionIndex] = useState2(0);
|
|
1844
|
+
const [questionAnswers, setQuestionAnswers] = useState2({});
|
|
1845
|
+
const [customQuestionActive, setCustomQuestionActive] = useState2(false);
|
|
1846
|
+
const [columns, setColumns] = useState2(stdout?.columns ?? 80);
|
|
1847
|
+
const [rows, setRows] = useState2(stdout?.rows ?? 24);
|
|
1848
|
+
const [transcript, dispatch] = useReducer(transcriptReducer, void 0, createTranscriptState);
|
|
1849
|
+
const scrollRef = useRef4(null);
|
|
1850
|
+
const taskScrollRef = useRef4(null);
|
|
1851
|
+
const taskPanelHovered = useRef4(false);
|
|
1852
|
+
const runtimeRef = useRef4(void 0);
|
|
1853
|
+
const pendingPromptRef = useRef4(void 0);
|
|
1854
|
+
pendingPromptRef.current ??= new SinglePendingPrompt();
|
|
1855
|
+
const closing = useRef4(false);
|
|
1856
|
+
const clipboardBusy = useRef4(false);
|
|
1857
|
+
const textBuf = useRef4({ pending: "", timer: null });
|
|
1858
|
+
useTerminalTitle("Flavor Code");
|
|
1859
|
+
const flushText = () => {
|
|
1860
|
+
const t = textBuf.current;
|
|
1861
|
+
if (t.timer !== null) {
|
|
1862
|
+
clearTimeout(t.timer);
|
|
1863
|
+
t.timer = null;
|
|
1864
|
+
}
|
|
1865
|
+
if (t.pending.length > 0) {
|
|
1866
|
+
dispatch({ type: "session", event: { type: "text", text: t.pending } });
|
|
1867
|
+
t.pending = "";
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
const shutdown = async (active) => {
|
|
1871
|
+
if (closing.current) return;
|
|
1872
|
+
closing.current = true;
|
|
1873
|
+
await shutdownRuntime(active, exit, (error) => {
|
|
1874
|
+
dispatch({ type: "submit-error", message: error });
|
|
1875
|
+
});
|
|
1876
|
+
};
|
|
1877
|
+
const shutdownRef = useRef4(shutdown);
|
|
1878
|
+
shutdownRef.current = shutdown;
|
|
1879
|
+
const interruptRef = useRef4(void 0);
|
|
1880
|
+
interruptRef.current ??= createSessionInterruptHandler(
|
|
1881
|
+
() => runtimeRef.current?.session,
|
|
1882
|
+
() => shutdownRef.current(runtimeRef.current)
|
|
1883
|
+
);
|
|
1884
|
+
const interrupt = interruptRef.current;
|
|
1885
|
+
useEffect3(() => {
|
|
1886
|
+
let disposed = false;
|
|
1887
|
+
const FLUSH_MS = 100;
|
|
1888
|
+
const receive = (event) => {
|
|
1889
|
+
if (event.type === "exit") {
|
|
1890
|
+
flushText();
|
|
1891
|
+
void shutdownRef.current(runtimeRef.current);
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
if (event.type === "clear") {
|
|
1895
|
+
flushText();
|
|
1896
|
+
dispatch({ type: "clear" });
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
if (event.type === "text") {
|
|
1900
|
+
const t = textBuf.current;
|
|
1901
|
+
t.pending += event.text;
|
|
1902
|
+
if (t.timer === null) t.timer = setTimeout(flushText, FLUSH_MS);
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
flushText();
|
|
1906
|
+
dispatch({ type: "session", event });
|
|
1907
|
+
};
|
|
1908
|
+
void createProductionRuntime({
|
|
1909
|
+
workspace,
|
|
1910
|
+
...home === void 0 ? {} : { home },
|
|
1911
|
+
...resumeSession === void 0 ? {} : { resumeSession },
|
|
1912
|
+
output: receive,
|
|
1913
|
+
onApprovalChange: () => setRevision((value) => value + 1)
|
|
1914
|
+
}).then(async (created) => {
|
|
1915
|
+
if (disposed) {
|
|
1916
|
+
await created.dispose();
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
dispatch({ type: "restore", state: created.restoredTranscript });
|
|
1920
|
+
runtimeRef.current = created;
|
|
1921
|
+
setSlashCandidates(buildSlashCandidates(BUILTIN_SLASH_CANDIDATES, created.services.pluginCommands(), []));
|
|
1922
|
+
setRuntime(created);
|
|
1923
|
+
await created.session.start();
|
|
1924
|
+
try {
|
|
1925
|
+
const skills = await created.services.skills();
|
|
1926
|
+
if (!disposed) {
|
|
1927
|
+
setSlashCandidates(buildSlashCandidates(BUILTIN_SLASH_CANDIDATES, created.services.pluginCommands(), skills));
|
|
1928
|
+
}
|
|
1929
|
+
} catch {
|
|
1930
|
+
}
|
|
1931
|
+
}).catch((error) => {
|
|
1932
|
+
dispatch({ type: "submit", prompt: "startup" });
|
|
1933
|
+
dispatch({ type: "submit-error", message: safeUiError(error) });
|
|
1934
|
+
});
|
|
1935
|
+
return () => {
|
|
1936
|
+
disposed = true;
|
|
1937
|
+
flushText();
|
|
1938
|
+
void closeAndDisposeRuntime(runtimeRef.current, (error) => process.stderr.write(`flavor cleanup: ${error}
|
|
1939
|
+
`));
|
|
1940
|
+
};
|
|
1941
|
+
}, [workspace, home, resumeSession]);
|
|
1942
|
+
useEffect3(() => installSigintHandler(process, interrupt), [interrupt]);
|
|
1943
|
+
useEffect3(() => {
|
|
1944
|
+
if (runtime?.services.ideContext === void 0) {
|
|
1945
|
+
setIdeContext(void 0);
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
let disposed = false;
|
|
1949
|
+
let refreshing = false;
|
|
1950
|
+
let previous = "";
|
|
1951
|
+
const refresh = async () => {
|
|
1952
|
+
if (refreshing) return;
|
|
1953
|
+
refreshing = true;
|
|
1954
|
+
try {
|
|
1955
|
+
const next = await runtime.services.ideContext().catch(() => void 0);
|
|
1956
|
+
if (disposed) return;
|
|
1957
|
+
const serialized = JSON.stringify(next);
|
|
1958
|
+
if (serialized !== previous) {
|
|
1959
|
+
previous = serialized;
|
|
1960
|
+
setIdeContext(next);
|
|
1961
|
+
}
|
|
1962
|
+
} finally {
|
|
1963
|
+
refreshing = false;
|
|
1964
|
+
}
|
|
1965
|
+
};
|
|
1966
|
+
void refresh();
|
|
1967
|
+
const timer = setInterval(() => void refresh(), 400);
|
|
1968
|
+
return () => {
|
|
1969
|
+
disposed = true;
|
|
1970
|
+
clearInterval(timer);
|
|
1971
|
+
};
|
|
1972
|
+
}, [runtime]);
|
|
1973
|
+
useEffect3(() => {
|
|
1974
|
+
const controller = new AbortController();
|
|
1975
|
+
let disposed = false;
|
|
1976
|
+
void createGlobTool(workspace, { defaultLimit: 1e4 }).execute({ pattern: "**", limit: 1e4 }, controller.signal).then((result) => {
|
|
1977
|
+
if (!disposed) {
|
|
1978
|
+
setMentionCandidates(buildMentionCandidates(result.matches));
|
|
1979
|
+
}
|
|
1980
|
+
}).catch(() => {
|
|
1981
|
+
});
|
|
1982
|
+
return () => {
|
|
1983
|
+
disposed = true;
|
|
1984
|
+
controller.abort();
|
|
1985
|
+
};
|
|
1986
|
+
}, [workspace]);
|
|
1987
|
+
useEffect3(() => {
|
|
1988
|
+
if (!stdout || typeof stdout.on !== "function") return;
|
|
1989
|
+
const onResize = () => {
|
|
1990
|
+
setColumns(stdout.columns ?? 80);
|
|
1991
|
+
setRows(stdout.rows ?? 24);
|
|
1992
|
+
};
|
|
1993
|
+
stdout.on("resize", onResize);
|
|
1994
|
+
onResize();
|
|
1995
|
+
return () => {
|
|
1996
|
+
stdout.off("resize", onResize);
|
|
1997
|
+
};
|
|
1998
|
+
}, [stdout]);
|
|
1999
|
+
const approval = runtime?.approvals.pending;
|
|
2000
|
+
const questions = runtime?.services.questions.pending;
|
|
2001
|
+
const memoryReviews = runtime?.memoryReviews.pending ?? [];
|
|
2002
|
+
const memoryAutoDismissSeconds = runtime?.memoryReviews.autoDismissSeconds ?? 0;
|
|
2003
|
+
useEffect3(() => {
|
|
2004
|
+
setQuestionIndex(0);
|
|
2005
|
+
setQuestionAnswers({});
|
|
2006
|
+
setCustomQuestionActive(false);
|
|
2007
|
+
}, [questions]);
|
|
2008
|
+
const derivedSlashCompletion = deriveSlashCompletion(input, promptCursor, slashCandidates, slashSelection);
|
|
2009
|
+
const slashCompletion = dismissedSlashInput === input || transcript.active !== void 0 || approval !== void 0 || questions !== void 0 ? null : derivedSlashCompletion;
|
|
2010
|
+
const derivedMentionCompletion = slashCompletion === null ? deriveMentionCompletion(input, promptCursor, mentionCandidates, mentionSelection) : null;
|
|
2011
|
+
const mentionCompletion = dismissedMentionInput === input || transcript.active !== void 0 || approval !== void 0 || questions !== void 0 ? null : derivedMentionCompletion;
|
|
2012
|
+
const completedTokenLength = completedSlashTokenLength(input, slashCandidates, slashCompletion !== null);
|
|
2013
|
+
const selection = useSelection();
|
|
2014
|
+
const selectMention = (path) => {
|
|
2015
|
+
const next = completeMentionSelection(input, promptCursor, path);
|
|
2016
|
+
setInput(next.text);
|
|
2017
|
+
setPromptCursor(next.cursor);
|
|
2018
|
+
setMentionSelection(0);
|
|
2019
|
+
setDismissedMentionInput(next.text);
|
|
2020
|
+
};
|
|
2021
|
+
const pasteClipboardImage = async () => {
|
|
2022
|
+
const active = runtimeRef.current;
|
|
2023
|
+
if (active === void 0) {
|
|
2024
|
+
setClipboardNotice("Flavor is still starting; try pasting the image again.");
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
if (transcript.active !== void 0) {
|
|
2028
|
+
setClipboardNotice("Images can only be attached to a new prompt.");
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
2031
|
+
if (imageAttachments.length >= DEFAULT_MAX_IMAGES) {
|
|
2032
|
+
setClipboardNotice(`A prompt can contain at most ${DEFAULT_MAX_IMAGES} images.`);
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
if (clipboardBusy.current) return;
|
|
2036
|
+
clipboardBusy.current = true;
|
|
2037
|
+
setClipboardNotice("Reading image from clipboard...");
|
|
2038
|
+
try {
|
|
2039
|
+
const attachment = await readClipboardImage();
|
|
2040
|
+
if (attachment === void 0) {
|
|
2041
|
+
setClipboardNotice("Clipboard does not contain an image.");
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
const incomingBytes = Buffer.byteLength(attachment.dataBase64, "base64");
|
|
2045
|
+
const currentBytes = imageAttachments.reduce((sum, image) => sum + image.bytes, 0);
|
|
2046
|
+
if (currentBytes + incomingBytes > DEFAULT_MAX_TOTAL_IMAGE_BYTES) {
|
|
2047
|
+
throw new Error(`Image attachments exceed the maximum total size of ${DEFAULT_MAX_TOTAL_IMAGE_BYTES} bytes`);
|
|
2048
|
+
}
|
|
2049
|
+
const [stored] = await new SessionAssetStore({ workspace }).store(active.sessionId, [attachment]);
|
|
2050
|
+
if (stored === void 0) throw new Error("Clipboard image could not be stored");
|
|
2051
|
+
setImageAttachments((current) => [...current, stored]);
|
|
2052
|
+
setClipboardNotice(`Added [Image #${imageAttachments.length + 1}] ${stored.name ?? basename3(stored.source.path)}`);
|
|
2053
|
+
} catch (error) {
|
|
2054
|
+
setClipboardNotice(safeUiError(error));
|
|
2055
|
+
} finally {
|
|
2056
|
+
clipboardBusy.current = false;
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
2059
|
+
use_input_default((character, key, event) => {
|
|
2060
|
+
const terminalAction = classifyTerminalInput(key);
|
|
2061
|
+
if (terminalAction?.type === "scroll") {
|
|
2062
|
+
const scroll = selectWheelScrollTarget(scrollRef.current, taskScrollRef.current, taskPanelHovered.current);
|
|
2063
|
+
if (scroll !== null) {
|
|
2064
|
+
if (terminalAction.rows < 0) scrollUp(scroll, -terminalAction.rows);
|
|
2065
|
+
else scrollDown(scroll, terminalAction.rows);
|
|
2066
|
+
}
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2069
|
+
if (terminalAction?.type === "page") {
|
|
2070
|
+
const scroll = scrollRef.current;
|
|
2071
|
+
if (scroll !== null) jumpScroll(scroll, Math.floor(scroll.getViewportHeight() * terminalAction.fraction));
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
const active = runtimeRef.current;
|
|
2075
|
+
if (key.ctrl && character === "c") {
|
|
2076
|
+
if (selection.hasSelection()) {
|
|
2077
|
+
selection.copySelection();
|
|
2078
|
+
} else {
|
|
2079
|
+
interrupt();
|
|
2080
|
+
}
|
|
2081
|
+
return;
|
|
2082
|
+
}
|
|
2083
|
+
if (shouldReadClipboardImage(character, key, event.keypress.isPasted)) {
|
|
2084
|
+
void pasteClipboardImage();
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
if (active?.approvals.pending !== void 0) {
|
|
2088
|
+
if (character.toLowerCase() === "y") active.approvals.resolve("once");
|
|
2089
|
+
if (character.toLowerCase() === "n" || key.escape) active.approvals.resolve("deny");
|
|
2090
|
+
if (character.toLowerCase() === "a") {
|
|
2091
|
+
if (isDestructiveTool(active.approvals.pending.tool)) {
|
|
2092
|
+
active.approvals.resolve("once");
|
|
2093
|
+
} else {
|
|
2094
|
+
active.approvals.resolve("always");
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
const pendingMemory = active?.memoryReviews.pending[0];
|
|
2100
|
+
if (pendingMemory !== void 0 && key.ctrl && (character.toLowerCase() === "y" || character.toLowerCase() === "n")) {
|
|
2101
|
+
if (character.toLowerCase() === "y") {
|
|
2102
|
+
void active.memoryReviews.accept(pendingMemory.id).catch((error) => {
|
|
2103
|
+
dispatch({ type: "submit-error", message: safeUiError(error) });
|
|
2104
|
+
});
|
|
2105
|
+
} else active.memoryReviews.dismiss(pendingMemory.id);
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
const qs = active?.services.questions.pending;
|
|
2109
|
+
if (qs !== void 0 && qs.length > 0) {
|
|
2110
|
+
const questionsService = active.services.questions;
|
|
2111
|
+
const currentIndex = Math.min(questionIndex, qs.length - 1);
|
|
2112
|
+
const question = qs[currentIndex];
|
|
2113
|
+
const commitQuestionAnswer = (answer) => {
|
|
2114
|
+
const next = { ...questionAnswers, [currentIndex]: answer };
|
|
2115
|
+
setInput("");
|
|
2116
|
+
setPromptCursor(0);
|
|
2117
|
+
setCustomQuestionActive(false);
|
|
2118
|
+
if (currentIndex === qs.length - 1) {
|
|
2119
|
+
setQuestionAnswers({});
|
|
2120
|
+
setQuestionIndex(0);
|
|
2121
|
+
questionsService.answer(next);
|
|
2122
|
+
} else {
|
|
2123
|
+
setQuestionAnswers(next);
|
|
2124
|
+
setQuestionIndex(currentIndex + 1);
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
if (key.escape) {
|
|
2128
|
+
if (customQuestionActive) {
|
|
2129
|
+
setCustomQuestionActive(false);
|
|
2130
|
+
setInput("");
|
|
2131
|
+
setPromptCursor(0);
|
|
2132
|
+
} else questionsService.cancel();
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
if (customQuestionActive) {
|
|
2136
|
+
if (key.return) {
|
|
2137
|
+
const answer = input.trim();
|
|
2138
|
+
if (answer) commitQuestionAnswer(answer);
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
} else {
|
|
2142
|
+
const digit = parseInt(character, 10);
|
|
2143
|
+
if (digit >= 1 && digit <= question.options.length) {
|
|
2144
|
+
commitQuestionAnswer(question.options[digit - 1].label);
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
if (digit === question.options.length + 1) {
|
|
2148
|
+
setCustomQuestionActive(true);
|
|
2149
|
+
setInput("");
|
|
2150
|
+
setPromptCursor(0);
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
if (!key.ctrl && !key.meta && character) setCustomQuestionActive(true);
|
|
2154
|
+
else return;
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
if (key.escape && transcript.active !== void 0) {
|
|
2158
|
+
const restored = pendingPromptRef.current.cancel();
|
|
2159
|
+
if (restored !== void 0) {
|
|
2160
|
+
setPendingPrompt(void 0);
|
|
2161
|
+
setInput(restored);
|
|
2162
|
+
setPastedBlocks([]);
|
|
2163
|
+
setPromptCursor([...restored].length);
|
|
2164
|
+
setSlashSelection(0);
|
|
2165
|
+
setDismissedSlashInput(void 0);
|
|
2166
|
+
setMentionSelection(0);
|
|
2167
|
+
setDismissedMentionInput(void 0);
|
|
2168
|
+
return;
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
const menuAction = slashKeyAction(key, slashCompletion);
|
|
2172
|
+
if (menuAction?.type === "select" && slashCompletion !== null) {
|
|
2173
|
+
setSlashSelection((value) => moveSlashSelection(value, menuAction.delta, slashCompletion.items.length));
|
|
2174
|
+
return;
|
|
2175
|
+
}
|
|
2176
|
+
if (menuAction?.type === "complete" && slashCompletion !== null) {
|
|
2177
|
+
const selected = slashCompletion.items[slashCompletion.selectedIndex];
|
|
2178
|
+
if (selected !== void 0) {
|
|
2179
|
+
const next = completeSlashSelection(input, promptCursor, selected.name);
|
|
2180
|
+
setInput(next.text);
|
|
2181
|
+
setPromptCursor(next.cursor);
|
|
2182
|
+
setDismissedSlashInput(next.text);
|
|
2183
|
+
}
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
if (menuAction?.type === "dismiss") {
|
|
2187
|
+
setDismissedSlashInput(input);
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
const mentionAction = completionKeyAction(key, mentionCompletion !== null);
|
|
2191
|
+
if (mentionAction?.type === "select" && mentionCompletion !== null) {
|
|
2192
|
+
setMentionSelection((value) => moveMentionSelection(value, mentionAction.delta, mentionCompletion.items.length));
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
if (mentionAction?.type === "complete" && mentionCompletion !== null) {
|
|
2196
|
+
const selected = mentionCompletion.items[mentionCompletion.selectedIndex];
|
|
2197
|
+
if (selected !== void 0) selectMention(selected);
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
if (mentionAction?.type === "dismiss") {
|
|
2201
|
+
setDismissedMentionInput(input);
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
if (key.return) {
|
|
2205
|
+
const images = [...imageAttachments];
|
|
2206
|
+
const prepared = prepareCliSubmission(input, images, transcript.active !== void 0);
|
|
2207
|
+
if (prepared.kind === "empty" || active === void 0) return;
|
|
2208
|
+
if (prepared.kind === "error") {
|
|
2209
|
+
setClipboardNotice(prepared.message);
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
const submittedText = prepared.text;
|
|
2213
|
+
const { delivery, prompt } = resolvePromptDelivery(transcript.active !== void 0, key, submittedText);
|
|
2214
|
+
if (!prompt) return;
|
|
2215
|
+
if (delivery === "followUp" && transcript.active !== void 0) {
|
|
2216
|
+
if (!pendingPromptRef.current.queue(prompt)) return;
|
|
2217
|
+
setPendingPrompt(prompt);
|
|
2218
|
+
}
|
|
2219
|
+
scrollRef.current?.scrollToBottom();
|
|
2220
|
+
setHistory((current) => [...current, submittedText].slice(-HISTORY_CAP));
|
|
2221
|
+
setHistoryCursor(history.length + 1);
|
|
2222
|
+
setInput("");
|
|
2223
|
+
setPastedBlocks([]);
|
|
2224
|
+
setImageAttachments([]);
|
|
2225
|
+
setClipboardNotice(void 0);
|
|
2226
|
+
setPromptCursor(0);
|
|
2227
|
+
setSlashSelection(0);
|
|
2228
|
+
setDismissedSlashInput(void 0);
|
|
2229
|
+
setMentionSelection(0);
|
|
2230
|
+
setDismissedMentionInput(void 0);
|
|
2231
|
+
if (delivery === "prompt") {
|
|
2232
|
+
void runTerminalSubmissionChain({
|
|
2233
|
+
session: active.session,
|
|
2234
|
+
initialPrompt: prompt,
|
|
2235
|
+
...prepared.content === void 0 ? {} : {
|
|
2236
|
+
initialDisplayPrompt: prepared.displayText,
|
|
2237
|
+
initialSubmit: (text) => active.session.submit({
|
|
2238
|
+
text,
|
|
2239
|
+
content: prepared.content
|
|
2240
|
+
})
|
|
2241
|
+
},
|
|
2242
|
+
pending: pendingPromptRef.current,
|
|
2243
|
+
onStart: (next) => dispatch({ type: "submit", prompt: next }),
|
|
2244
|
+
onFinish: () => dispatch({ type: "finish" }),
|
|
2245
|
+
onPendingConsumed: () => setPendingPrompt(void 0),
|
|
2246
|
+
report: (error) => dispatch({ type: "submit-error", message: error }),
|
|
2247
|
+
shouldContinue: () => !closing.current
|
|
2248
|
+
});
|
|
2249
|
+
} else if (delivery === "steer") {
|
|
2250
|
+
try {
|
|
2251
|
+
active.session.steer(prompt);
|
|
2252
|
+
} catch (error) {
|
|
2253
|
+
dispatch({ type: "submit-error", message: safeUiError(error) });
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
if (key.backspace) {
|
|
2259
|
+
const imageEdit = removeLastCliImageOnBackspace(input, promptCursor, imageAttachments);
|
|
2260
|
+
if (imageEdit.handled) {
|
|
2261
|
+
setImageAttachments(imageEdit.images);
|
|
2262
|
+
setClipboardNotice(imageEdit.images.length === 0 ? "Removed image attachment." : `Removed image attachment; ${imageEdit.images.length} remaining.`);
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
const next = editPromptWithPastedBlocks(
|
|
2266
|
+
{ text: input, cursor: promptCursor },
|
|
2267
|
+
{ type: "backspace" },
|
|
2268
|
+
pastedBlocks
|
|
2269
|
+
);
|
|
2270
|
+
setInput(next.text);
|
|
2271
|
+
setPromptCursor(next.cursor);
|
|
2272
|
+
setPastedBlocks(next.pastedBlocks);
|
|
2273
|
+
setSlashSelection(0);
|
|
2274
|
+
setDismissedSlashInput(void 0);
|
|
2275
|
+
setMentionSelection(0);
|
|
2276
|
+
setDismissedMentionInput(void 0);
|
|
2277
|
+
} else if (key.delete) {
|
|
2278
|
+
updatePrompt({ type: "delete" }, input, promptCursor, setInput, setPromptCursor);
|
|
2279
|
+
setSlashSelection(0);
|
|
2280
|
+
setDismissedSlashInput(void 0);
|
|
2281
|
+
setMentionSelection(0);
|
|
2282
|
+
setDismissedMentionInput(void 0);
|
|
2283
|
+
} else if (key.leftArrow) setPromptCursor((value) => Math.max(0, value - 1));
|
|
2284
|
+
else if (key.rightArrow) setPromptCursor((value) => Math.min([...input].length, value + 1));
|
|
2285
|
+
else if (terminalAction?.type === "history" && terminalAction.direction === "up" && history.length) {
|
|
2286
|
+
const next = navigateHistory({ history, cursor: historyCursor }, "up");
|
|
2287
|
+
setHistoryCursor(next.cursor);
|
|
2288
|
+
setInput(next.input);
|
|
2289
|
+
setPromptCursor(next.promptCursor);
|
|
2290
|
+
setSlashSelection(0);
|
|
2291
|
+
setDismissedSlashInput(void 0);
|
|
2292
|
+
setMentionSelection(0);
|
|
2293
|
+
setDismissedMentionInput(void 0);
|
|
2294
|
+
} else if (terminalAction?.type === "history" && terminalAction.direction === "down" && history.length) {
|
|
2295
|
+
const next = navigateHistory({ history, cursor: historyCursor }, "down");
|
|
2296
|
+
setHistoryCursor(next.cursor);
|
|
2297
|
+
setInput(next.input);
|
|
2298
|
+
setPromptCursor(next.promptCursor);
|
|
2299
|
+
setSlashSelection(0);
|
|
2300
|
+
setDismissedSlashInput(void 0);
|
|
2301
|
+
setMentionSelection(0);
|
|
2302
|
+
setDismissedMentionInput(void 0);
|
|
2303
|
+
} else if (!key.ctrl && !key.meta && character) {
|
|
2304
|
+
updatePrompt({ type: "insert", value: character }, input, promptCursor, setInput, setPromptCursor);
|
|
2305
|
+
if (/[\r\n]/u.test(character)) {
|
|
2306
|
+
setPastedBlocks((current) => [...current, { id: current.length + 1, text: character }]);
|
|
2307
|
+
}
|
|
2308
|
+
setSlashSelection(0);
|
|
2309
|
+
setDismissedSlashInput(void 0);
|
|
2310
|
+
setMentionSelection(0);
|
|
2311
|
+
setDismissedMentionInput(void 0);
|
|
2312
|
+
}
|
|
2313
|
+
});
|
|
2314
|
+
void revision;
|
|
2315
|
+
if (runtime === void 0) return /* @__PURE__ */ jsx5(
|
|
2316
|
+
StartingLayout,
|
|
2317
|
+
{
|
|
2318
|
+
workspaceName: basename3(workspace),
|
|
2319
|
+
completed: transcript.completed,
|
|
2320
|
+
...transcript.active === void 0 ? {} : { active: transcript.active },
|
|
2321
|
+
columns,
|
|
2322
|
+
rows
|
|
2323
|
+
}
|
|
2324
|
+
);
|
|
2325
|
+
const llmServiceName = runtime.services.llmServiceName?.();
|
|
2326
|
+
return /* @__PURE__ */ jsx5(
|
|
2327
|
+
TerminalLayout,
|
|
2328
|
+
{
|
|
2329
|
+
model: runtime.services.mainModel(),
|
|
2330
|
+
...llmServiceName === void 0 ? {} : { serviceName: llmServiceName },
|
|
2331
|
+
workspaceName: basename3(workspace),
|
|
2332
|
+
completed: transcript.completed,
|
|
2333
|
+
...transcript.active === void 0 ? {} : { active: transcript.active },
|
|
2334
|
+
input,
|
|
2335
|
+
pastedBlocks,
|
|
2336
|
+
imageAttachments,
|
|
2337
|
+
...clipboardNotice === void 0 ? {} : { clipboardNotice },
|
|
2338
|
+
promptCursor,
|
|
2339
|
+
onPromptCursorChange: setPromptCursor,
|
|
2340
|
+
columns,
|
|
2341
|
+
rows,
|
|
2342
|
+
activeSession: transcript.active !== void 0,
|
|
2343
|
+
...ideContext === void 0 ? {} : { ideContext },
|
|
2344
|
+
completedSlashTokenLength: completedTokenLength,
|
|
2345
|
+
scrollRef,
|
|
2346
|
+
taskScrollRef,
|
|
2347
|
+
onTaskPanelHoverChange: (hovered) => {
|
|
2348
|
+
taskPanelHovered.current = hovered;
|
|
2349
|
+
},
|
|
2350
|
+
...slashCompletion === null ? {} : { completion: slashCompletion },
|
|
2351
|
+
...mentionCompletion === null ? {} : { mentionCompletion, onMentionSelect: selectMention },
|
|
2352
|
+
...approval === void 0 ? {} : { approval },
|
|
2353
|
+
...questions === void 0 ? {} : { questions },
|
|
2354
|
+
memoryReviews,
|
|
2355
|
+
memoryAutoDismissSeconds,
|
|
2356
|
+
...pendingPrompt === void 0 ? {} : { pendingPrompt },
|
|
2357
|
+
questionIndex,
|
|
2358
|
+
questionAnswers,
|
|
2359
|
+
customQuestionActive
|
|
2360
|
+
}
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
function StartingLayout({
|
|
2364
|
+
workspaceName,
|
|
2365
|
+
completed,
|
|
2366
|
+
active,
|
|
2367
|
+
columns,
|
|
2368
|
+
rows
|
|
2369
|
+
}) {
|
|
2370
|
+
return /* @__PURE__ */ jsx5(
|
|
2371
|
+
TerminalLayout,
|
|
2372
|
+
{
|
|
2373
|
+
model: "starting",
|
|
2374
|
+
workspaceName,
|
|
2375
|
+
completed,
|
|
2376
|
+
...active === void 0 ? {} : { active },
|
|
2377
|
+
input: "",
|
|
2378
|
+
promptCursor: 0,
|
|
2379
|
+
columns,
|
|
2380
|
+
rows,
|
|
2381
|
+
activeSession: false
|
|
2382
|
+
}
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
function TerminalLayout({
|
|
2386
|
+
model,
|
|
2387
|
+
serviceName,
|
|
2388
|
+
workspaceName,
|
|
2389
|
+
completed,
|
|
2390
|
+
active,
|
|
2391
|
+
input,
|
|
2392
|
+
pastedBlocks = [],
|
|
2393
|
+
imageAttachments = [],
|
|
2394
|
+
clipboardNotice,
|
|
2395
|
+
promptCursor,
|
|
2396
|
+
columns,
|
|
2397
|
+
rows = 24,
|
|
2398
|
+
activeSession,
|
|
2399
|
+
pendingPrompt,
|
|
2400
|
+
approval,
|
|
2401
|
+
questions,
|
|
2402
|
+
memoryReviews = [],
|
|
2403
|
+
memoryAutoDismissSeconds = 0,
|
|
2404
|
+
questionIndex = 0,
|
|
2405
|
+
questionAnswers = {},
|
|
2406
|
+
customQuestionActive = false,
|
|
2407
|
+
completion,
|
|
2408
|
+
mentionCompletion,
|
|
2409
|
+
onMentionSelect,
|
|
2410
|
+
completedSlashTokenLength: tokenLength = 0,
|
|
2411
|
+
scrollRef,
|
|
2412
|
+
taskScrollRef,
|
|
2413
|
+
onTaskPanelHoverChange,
|
|
2414
|
+
onPromptCursorChange,
|
|
2415
|
+
ideContext
|
|
2416
|
+
}) {
|
|
2417
|
+
const dividerWidth = Math.max(1, columns - 1);
|
|
2418
|
+
const showWelcome = completed.length === 0 && active === void 0;
|
|
2419
|
+
const activeCompletion = completion ?? mentionCompletion;
|
|
2420
|
+
const menuRows = activeCompletion === void 0 ? 0 : Math.min(6, activeCompletion.items.length - activeCompletion.windowStart);
|
|
2421
|
+
const activeTaskBlocks = active?.blocks.filter(
|
|
2422
|
+
(block) => block.kind === "status" && block.task !== void 0
|
|
2423
|
+
) ?? [];
|
|
2424
|
+
const activeWithoutTasks = active === void 0 ? void 0 : {
|
|
2425
|
+
...active,
|
|
2426
|
+
blocks: active.blocks.filter(
|
|
2427
|
+
(block) => !(block.kind === "status" && block.task !== void 0)
|
|
2428
|
+
)
|
|
2429
|
+
};
|
|
2430
|
+
const questionRows = questions === void 0 ? 0 : 4 + (questions[questionIndex]?.options.length ?? 0) + questions.length * 2;
|
|
2431
|
+
const memoryReviewRows = memoryReviews.length === 0 ? 0 : 5;
|
|
2432
|
+
const fixedBottomRows = (approval === void 0 ? 0 : 3) + questionRows + memoryReviewRows + menuRows + (pendingPrompt === void 0 ? 0 : 1) + imageAttachments.length + (clipboardNotice === void 0 ? 0 : 1) + 2;
|
|
2433
|
+
const taskPanelRows = taskPanelViewportRows(rows, fixedBottomRows, activeTaskBlocks.length > 0);
|
|
2434
|
+
const availableBottomRows = Math.max(1, rows - taskPanelRows - 1);
|
|
2435
|
+
const bottomMaxRows = Math.min(availableBottomRows, Math.max(Math.floor(rows / 2), fixedBottomRows + 1));
|
|
2436
|
+
const promptMaxLines = Math.max(1, bottomMaxRows - fixedBottomRows);
|
|
2437
|
+
return /* @__PURE__ */ jsxs4(Box_default, { height: rows, width: "100%", flexDirection: "column", overflow: "hidden", children: [
|
|
2438
|
+
/* @__PURE__ */ jsxs4(ScrollBox_default, { ...scrollRef === void 0 ? {} : { ref: scrollRef }, flexGrow: 1, flexDirection: "column", stickyScroll: true, children: [
|
|
2439
|
+
showWelcome ? /* @__PURE__ */ jsx5(WelcomeCard, { model, ...serviceName === void 0 ? {} : { serviceName }, workspaceName, columns }) : /* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
|
|
2440
|
+
"flavor \xB7 ",
|
|
2441
|
+
model,
|
|
2442
|
+
" \xB7 ",
|
|
2443
|
+
workspaceName
|
|
2444
|
+
] }),
|
|
2445
|
+
/* @__PURE__ */ jsx5(Box_default, { height: 1 }),
|
|
2446
|
+
completed.map((turn, index) => /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
|
|
2447
|
+
index > 0 ? /* @__PURE__ */ jsx5(TurnSeparator, { width: columns }) : null,
|
|
2448
|
+
/* @__PURE__ */ jsx5(TurnView, { turn, interactive: false })
|
|
2449
|
+
] }, turn.id)),
|
|
2450
|
+
activeWithoutTasks === void 0 ? null : /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
|
|
2451
|
+
completed.length > 0 ? /* @__PURE__ */ jsx5(TurnSeparator, { width: columns }) : null,
|
|
2452
|
+
/* @__PURE__ */ jsx5(TurnView, { turn: activeWithoutTasks, interactive: activeSession })
|
|
2453
|
+
] })
|
|
2454
|
+
] }),
|
|
2455
|
+
/* @__PURE__ */ jsx5(
|
|
2456
|
+
TaskProgressPanel,
|
|
2457
|
+
{
|
|
2458
|
+
blocks: activeTaskBlocks,
|
|
2459
|
+
interactive: activeSession,
|
|
2460
|
+
maxHeight: taskPanelRows,
|
|
2461
|
+
...taskScrollRef === void 0 ? {} : { scrollRef: taskScrollRef },
|
|
2462
|
+
...onTaskPanelHoverChange === void 0 ? {} : { onHoverChange: onTaskPanelHoverChange }
|
|
2463
|
+
}
|
|
2464
|
+
),
|
|
2465
|
+
/* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", flexShrink: 0, maxHeight: bottomMaxRows, width: "100%", overflowY: "hidden", children: [
|
|
2466
|
+
approval === void 0 ? null : /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, children: [
|
|
2467
|
+
/* @__PURE__ */ jsxs4(Text, { color: "magenta", children: [
|
|
2468
|
+
"\u250C\u2500 approval \xB7 ",
|
|
2469
|
+
approval.tool
|
|
2470
|
+
] }),
|
|
2471
|
+
/* @__PURE__ */ jsxs4(Text, { wrap: "truncate-end", color: "magentaBright", children: [
|
|
2472
|
+
"\u2502 ",
|
|
2473
|
+
approval.reason ?? "This action needs permission."
|
|
2474
|
+
] }),
|
|
2475
|
+
isDestructiveTool(approval.tool) ? /* @__PURE__ */ jsxs4(Text, { bold: true, color: "magenta", children: [
|
|
2476
|
+
"\u2514\u2500 Allow? ",
|
|
2477
|
+
/* @__PURE__ */ jsx5(Text, { color: "green", children: "y" }),
|
|
2478
|
+
"=once / ",
|
|
2479
|
+
/* @__PURE__ */ jsx5(Text, { color: "red", children: "n" }),
|
|
2480
|
+
"=deny"
|
|
2481
|
+
] }) : /* @__PURE__ */ jsxs4(Text, { bold: true, color: "magenta", children: [
|
|
2482
|
+
"\u2514\u2500 Allow? ",
|
|
2483
|
+
/* @__PURE__ */ jsx5(Text, { color: "green", children: "y" }),
|
|
2484
|
+
"=once / ",
|
|
2485
|
+
/* @__PURE__ */ jsx5(Text, { color: "yellow", children: "a" }),
|
|
2486
|
+
"=same-type / ",
|
|
2487
|
+
/* @__PURE__ */ jsx5(Text, { color: "red", children: "n" }),
|
|
2488
|
+
"=deny"
|
|
2489
|
+
] })
|
|
2490
|
+
] }),
|
|
2491
|
+
!questions || questions.length === 0 ? null : /* @__PURE__ */ jsx5(QuestionCards, { questions, activeIndex: questionIndex, answers: questionAnswers, customActive: customQuestionActive }),
|
|
2492
|
+
memoryReviews.length === 0 ? null : /* @__PURE__ */ jsx5(MemoryReviewCards, { reviews: memoryReviews, autoDismissSeconds: memoryAutoDismissSeconds }),
|
|
2493
|
+
completion === void 0 ? null : /* @__PURE__ */ jsx5(SlashMenu, { completion }),
|
|
2494
|
+
mentionCompletion === void 0 ? null : /* @__PURE__ */ jsx5(MentionMenu, { completion: mentionCompletion, ...onMentionSelect === void 0 ? {} : { onSelect: onMentionSelect } }),
|
|
2495
|
+
pendingPrompt === void 0 ? null : /* @__PURE__ */ jsxs4(Text, { color: "yellow", wrap: "truncate-end", children: [
|
|
2496
|
+
"Pending \xB7 ",
|
|
2497
|
+
pendingPrompt,
|
|
2498
|
+
" \xB7 Esc edit"
|
|
2499
|
+
] }),
|
|
2500
|
+
/* @__PURE__ */ jsx5(Text, { dimColor: true, children: "\u2500".repeat(dividerWidth) }),
|
|
2501
|
+
imageAttachments.map((image, index) => /* @__PURE__ */ jsxs4(Text, { color: "cyan", wrap: "truncate-end", children: [
|
|
2502
|
+
"[Image #",
|
|
2503
|
+
index + 1,
|
|
2504
|
+
"] ",
|
|
2505
|
+
image.name ?? basename3(image.source.path)
|
|
2506
|
+
] }, `${image.sha256}:${index}`)),
|
|
2507
|
+
clipboardNotice === void 0 ? null : /* @__PURE__ */ jsx5(Text, { color: "yellow", wrap: "truncate-end", children: clipboardNotice }),
|
|
2508
|
+
/* @__PURE__ */ jsx5(
|
|
2509
|
+
PromptLine,
|
|
2510
|
+
{
|
|
2511
|
+
input,
|
|
2512
|
+
pastedBlocks,
|
|
2513
|
+
cursor: promptCursor,
|
|
2514
|
+
columns,
|
|
2515
|
+
maxVisibleLines: promptMaxLines,
|
|
2516
|
+
completedSlashTokenLength: tokenLength,
|
|
2517
|
+
...onPromptCursorChange === void 0 ? {} : { onCursorChange: onPromptCursorChange }
|
|
2518
|
+
}
|
|
2519
|
+
),
|
|
2520
|
+
/* @__PURE__ */ jsx5(
|
|
2521
|
+
FooterStatus,
|
|
2522
|
+
{
|
|
2523
|
+
hint: activeSession ? "Ctrl+C cancel \xB7 Enter queue \xB7 Esc edit pending \xB7 /steer sends now" : completion !== void 0 ? "\u2191/\u2193 select \xB7 Tab complete \xB7 Esc close" : mentionCompletion !== void 0 ? "\u2191/\u2193 select \xB7 Tab complete \xB7 click choose \xB7 Esc close" : "Enter send \xB7 Ctrl/Cmd+V image \xB7 \u2191\u2193 history \xB7 Ctrl+C exit",
|
|
2524
|
+
...ideContext === void 0 ? {} : { ideContext }
|
|
2525
|
+
}
|
|
2526
|
+
)
|
|
2527
|
+
] })
|
|
2528
|
+
] });
|
|
2529
|
+
}
|
|
2530
|
+
function FooterStatus({
|
|
2531
|
+
hint,
|
|
2532
|
+
ideContext
|
|
2533
|
+
}) {
|
|
2534
|
+
const ide = ideFooterPresentation(ideContext);
|
|
2535
|
+
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "row", width: "100%", justifyContent: "space-between", children: [
|
|
2536
|
+
/* @__PURE__ */ jsx5(Box_default, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ jsx5(Text, { dimColor: true, wrap: "truncate-end", children: hint }) }),
|
|
2537
|
+
ide === void 0 ? null : /* @__PURE__ */ jsx5(Box_default, { flexShrink: 0, marginLeft: 1, children: /* @__PURE__ */ jsxs4(Text, { color: "cyan", wrap: "truncate-end", children: [
|
|
2538
|
+
"\u29C9 ",
|
|
2539
|
+
ide
|
|
2540
|
+
] }) })
|
|
2541
|
+
] });
|
|
2542
|
+
}
|
|
2543
|
+
function ideFooterPresentation(context) {
|
|
2544
|
+
if (context?.filePath === void 0) return void 0;
|
|
2545
|
+
if (context.selection.isEmpty || !context.selectedText) return `In ${basename3(context.filePath)}`;
|
|
2546
|
+
const endLine = context.selection.end.character === 0 && context.selection.end.line > context.selection.start.line ? context.selection.end.line - 1 : context.selection.end.line;
|
|
2547
|
+
const count = Math.max(1, endLine - context.selection.start.line + 1);
|
|
2548
|
+
return `${count} ${count === 1 ? "line" : "lines"} selected`;
|
|
2549
|
+
}
|
|
2550
|
+
function QuestionCards({ questions, activeIndex, answers, customActive }) {
|
|
2551
|
+
return /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", marginBottom: 1, children: questions.map((q, qi) => /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: qi < questions.length - 1 ? 1 : 0, children: [
|
|
2552
|
+
/* @__PURE__ */ jsxs4(Text, { bold: true, color: "cyan", children: [
|
|
2553
|
+
"\u250C\u2500 ",
|
|
2554
|
+
q.header
|
|
2555
|
+
] }),
|
|
2556
|
+
/* @__PURE__ */ jsxs4(Text, { wrap: "truncate-end", color: "cyanBright", children: [
|
|
2557
|
+
"\u2502 ",
|
|
2558
|
+
q.question
|
|
2559
|
+
] }),
|
|
2560
|
+
qi < activeIndex ? /* @__PURE__ */ jsxs4(Text, { color: "green", children: [
|
|
2561
|
+
"\u2502 \u2713 ",
|
|
2562
|
+
answers[qi]
|
|
2563
|
+
] }) : qi > activeIndex ? null : q.options.map((opt, oi) => /* @__PURE__ */ jsxs4(Text, { color: "cyan", children: [
|
|
2564
|
+
"\u2502 ",
|
|
2565
|
+
/* @__PURE__ */ jsx5(Text, { bold: true, color: "green", children: oi + 1 }),
|
|
2566
|
+
". ",
|
|
2567
|
+
opt.label,
|
|
2568
|
+
/* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
|
|
2569
|
+
" ",
|
|
2570
|
+
opt.description
|
|
2571
|
+
] })
|
|
2572
|
+
] }, oi)),
|
|
2573
|
+
qi === activeIndex ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2574
|
+
/* @__PURE__ */ jsxs4(Text, { color: "cyan", children: [
|
|
2575
|
+
"\u2502 ",
|
|
2576
|
+
/* @__PURE__ */ jsx5(Text, { bold: true, color: "green", children: q.options.length + 1 }),
|
|
2577
|
+
". Custom input",
|
|
2578
|
+
/* @__PURE__ */ jsx5(Text, { dimColor: true, children: " Type your own answer" })
|
|
2579
|
+
] }),
|
|
2580
|
+
/* @__PURE__ */ jsxs4(Text, { dimColor: true, color: "cyan", children: [
|
|
2581
|
+
"\u2514\u2500 ",
|
|
2582
|
+
customActive ? "Type below and press Enter; Esc returns to choices" : `Press 1-${q.options.length + 1} to choose; Esc dismisses`
|
|
2583
|
+
] })
|
|
2584
|
+
] }) : null
|
|
2585
|
+
] }, qi)) });
|
|
2586
|
+
}
|
|
2587
|
+
function MemoryReviewCards({ reviews, autoDismissSeconds }) {
|
|
2588
|
+
const review = reviews[0];
|
|
2589
|
+
const [remaining, setRemaining] = useState2(autoDismissSeconds);
|
|
2590
|
+
useEffect3(() => {
|
|
2591
|
+
if (autoDismissSeconds <= 0) return;
|
|
2592
|
+
setRemaining(autoDismissSeconds);
|
|
2593
|
+
const timer = setInterval(() => setRemaining((current) => Math.max(0, current - 1)), 1e3);
|
|
2594
|
+
return () => clearInterval(timer);
|
|
2595
|
+
}, [review.id, autoDismissSeconds]);
|
|
2596
|
+
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, children: [
|
|
2597
|
+
/* @__PURE__ */ jsxs4(Text, { bold: true, color: "yellow", children: [
|
|
2598
|
+
"\u250C\u2500 Long-term memory requires confirmation (",
|
|
2599
|
+
reviews.length,
|
|
2600
|
+
")"
|
|
2601
|
+
] }),
|
|
2602
|
+
/* @__PURE__ */ jsxs4(Text, { color: "yellowBright", wrap: "truncate-end", children: [
|
|
2603
|
+
"\u2502 [",
|
|
2604
|
+
review.type,
|
|
2605
|
+
"] ",
|
|
2606
|
+
review.content
|
|
2607
|
+
] }),
|
|
2608
|
+
/* @__PURE__ */ jsx5(Text, { dimColor: true, children: "\u2502 Model-generated content is not stored until you approve it." }),
|
|
2609
|
+
/* @__PURE__ */ jsxs4(Text, { color: "yellow", children: [
|
|
2610
|
+
"\u2514\u2500 ",
|
|
2611
|
+
/* @__PURE__ */ jsx5(Text, { bold: true, children: "Ctrl+Y" }),
|
|
2612
|
+
" save / ",
|
|
2613
|
+
/* @__PURE__ */ jsx5(Text, { bold: true, children: "Ctrl+N" }),
|
|
2614
|
+
" ignore",
|
|
2615
|
+
autoDismissSeconds > 0 ? /* @__PURE__ */ jsx5(Text, { bold: true, color: "cyanBright", children: ` (auto-dismiss in ${remaining}s)` }) : " (conversation remains available)"
|
|
2616
|
+
] })
|
|
2617
|
+
] });
|
|
2618
|
+
}
|
|
2619
|
+
function SlashMenu({ completion }) {
|
|
2620
|
+
const visible = completion.items.slice(completion.windowStart, completion.windowStart + 6);
|
|
2621
|
+
return /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", width: "100%", children: visible.map((candidate, visibleIndex) => {
|
|
2622
|
+
const index = completion.windowStart + visibleIndex;
|
|
2623
|
+
const presentation = slashCandidatePresentation(index === completion.selectedIndex);
|
|
2624
|
+
return /* @__PURE__ */ jsxs4(Text, { ...presentation.rowStyle, wrap: "truncate-end", children: [
|
|
2625
|
+
presentation.marker,
|
|
2626
|
+
/* @__PURE__ */ jsx5(HighlightedName, { name: candidate.name, query: completion.query, matchStyle: presentation.matchStyle }),
|
|
2627
|
+
candidate.description === void 0 ? null : /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` ${candidate.description}` })
|
|
2628
|
+
] }, `${candidate.kind}:${candidate.name}`);
|
|
2629
|
+
}) });
|
|
2630
|
+
}
|
|
2631
|
+
function MentionMenu({
|
|
2632
|
+
completion,
|
|
2633
|
+
onSelect
|
|
2634
|
+
}) {
|
|
2635
|
+
const visible = completion.items.slice(completion.windowStart, completion.windowStart + 6);
|
|
2636
|
+
return /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", width: "100%", children: visible.map((path, visibleIndex) => {
|
|
2637
|
+
const index = completion.windowStart + visibleIndex;
|
|
2638
|
+
const presentation = mentionCandidatePresentation(index === completion.selectedIndex);
|
|
2639
|
+
return /* @__PURE__ */ jsx5(
|
|
2640
|
+
Box_default,
|
|
2641
|
+
{
|
|
2642
|
+
width: "100%",
|
|
2643
|
+
onClick: (event) => {
|
|
2644
|
+
if (!event.cellIsBlank) onSelect?.(path);
|
|
2645
|
+
},
|
|
2646
|
+
children: /* @__PURE__ */ jsxs4(Text, { ...presentation.textStyle, wrap: "truncate-end", children: [
|
|
2647
|
+
presentation.marker,
|
|
2648
|
+
presentation.highlightMatches ? /* @__PURE__ */ jsx5(HighlightedName, { name: path, query: completion.query, matchStyle: presentation.matchStyle }) : path
|
|
2649
|
+
] })
|
|
2650
|
+
},
|
|
2651
|
+
path
|
|
2652
|
+
);
|
|
2653
|
+
}) });
|
|
2654
|
+
}
|
|
2655
|
+
function HighlightedName({
|
|
2656
|
+
name,
|
|
2657
|
+
query,
|
|
2658
|
+
matchStyle
|
|
2659
|
+
}) {
|
|
2660
|
+
const ranges = matchRanges(name, query);
|
|
2661
|
+
if (ranges.length === 0) return /* @__PURE__ */ jsx5(Text, { children: name });
|
|
2662
|
+
const parts = [];
|
|
2663
|
+
let cursor = 0;
|
|
2664
|
+
for (const [start, end] of ranges) {
|
|
2665
|
+
if (start > cursor) parts.push(name.slice(cursor, start));
|
|
2666
|
+
parts.push(/* @__PURE__ */ jsx5(Text, { ...matchStyle, children: name.slice(start, end) }, `${start}:${end}`));
|
|
2667
|
+
cursor = end;
|
|
2668
|
+
}
|
|
2669
|
+
if (cursor < name.length) parts.push(name.slice(cursor));
|
|
2670
|
+
return /* @__PURE__ */ jsx5(Text, { children: parts });
|
|
2671
|
+
}
|
|
2672
|
+
function jumpScroll(scroll, delta) {
|
|
2673
|
+
const maximum = Math.max(0, scroll.getScrollHeight() - scroll.getViewportHeight());
|
|
2674
|
+
const target = scroll.getScrollTop() + scroll.getPendingDelta() + delta;
|
|
2675
|
+
if (target >= maximum) {
|
|
2676
|
+
scroll.scrollTo(maximum);
|
|
2677
|
+
scroll.scrollToBottom();
|
|
2678
|
+
} else {
|
|
2679
|
+
scroll.scrollTo(Math.max(0, target));
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
function selectWheelScrollTarget(transcript, tasks, taskPanelHovered) {
|
|
2683
|
+
return taskPanelHovered && tasks !== null ? tasks : transcript;
|
|
2684
|
+
}
|
|
2685
|
+
function taskPanelViewportRows(rows, reservedBottomRows, hasTasks) {
|
|
2686
|
+
if (!hasTasks) return 0;
|
|
2687
|
+
const terminalRows = Math.max(0, Math.floor(rows));
|
|
2688
|
+
const reserved = Math.max(0, Math.floor(reservedBottomRows));
|
|
2689
|
+
const available = Math.max(0, terminalRows - reserved - 1);
|
|
2690
|
+
return Math.min(8, Math.max(1, Math.floor(terminalRows / 3)), available);
|
|
2691
|
+
}
|
|
2692
|
+
function scrollDown(scroll, amount) {
|
|
2693
|
+
const maximum = Math.max(0, scroll.getScrollHeight() - scroll.getViewportHeight());
|
|
2694
|
+
if (scroll.getScrollTop() + scroll.getPendingDelta() + amount >= maximum) scroll.scrollToBottom();
|
|
2695
|
+
else scroll.scrollBy(amount);
|
|
2696
|
+
}
|
|
2697
|
+
function scrollUp(scroll, amount) {
|
|
2698
|
+
if (scroll.getScrollTop() + scroll.getPendingDelta() - amount <= 0) scroll.scrollTo(0);
|
|
2699
|
+
else scroll.scrollBy(-amount);
|
|
2700
|
+
}
|
|
2701
|
+
function TurnSeparator({ width }) {
|
|
2702
|
+
return /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "\u2500".repeat(Math.max(1, width - 1)) });
|
|
2703
|
+
}
|
|
2704
|
+
function TurnView({ turn, interactive }) {
|
|
2705
|
+
if (turn.kind === "compaction") {
|
|
2706
|
+
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: "ansi:yellowBright", paddingX: 1, children: [
|
|
2707
|
+
/* @__PURE__ */ jsx5(Text, { color: "ansi:yellowBright", bold: true, children: turn.prompt }),
|
|
2708
|
+
turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive }, block.id) : /* @__PURE__ */ jsx5(Box_default, { children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`))
|
|
2709
|
+
] });
|
|
2710
|
+
}
|
|
2711
|
+
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, children: [
|
|
2712
|
+
/* @__PURE__ */ jsxs4(Box_default, { flexDirection: "row", backgroundColor: "#3a3a3a", paddingX: 1, paddingY: 0, children: [
|
|
2713
|
+
/* @__PURE__ */ jsx5(Text, { color: "ansi:whiteBright", bold: true, backgroundColor: "#3a3a3a", children: "\u276F" }),
|
|
2714
|
+
/* @__PURE__ */ jsxs4(Text, { color: "ansi:whiteBright", bold: true, backgroundColor: "#3a3a3a", children: [
|
|
2715
|
+
" ",
|
|
2716
|
+
turn.prompt
|
|
2717
|
+
] })
|
|
2718
|
+
] }),
|
|
2719
|
+
/* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
|
|
2720
|
+
] });
|
|
2721
|
+
}
|
|
2722
|
+
var DIFF_CONTENT = fileDiffLineStyle("context").contentColor;
|
|
2723
|
+
var DIFF_REMOVED_MARKER = fileDiffLineStyle("removed").markerColor;
|
|
2724
|
+
var DIFF_ADDED_MARKER = fileDiffLineStyle("added").markerColor;
|
|
2725
|
+
function FileDiffView({ presentation }) {
|
|
2726
|
+
const operation = presentation.operation === "create" ? "Create" : presentation.operation === "delete" ? "Delete" : "Update";
|
|
2727
|
+
if (presentation.operation === "delete") {
|
|
2728
|
+
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "row", children: [
|
|
2729
|
+
/* @__PURE__ */ jsx5(Text, { color: DIFF_REMOVED_MARKER, children: "\u25CF" }),
|
|
2730
|
+
/* @__PURE__ */ jsxs4(Text, { color: DIFF_CONTENT, children: [
|
|
2731
|
+
" ",
|
|
2732
|
+
/* @__PURE__ */ jsx5(Text, { bold: true, children: operation }),
|
|
2733
|
+
"(",
|
|
2734
|
+
basename3(presentation.path),
|
|
2735
|
+
")"
|
|
2736
|
+
] })
|
|
2737
|
+
] });
|
|
2738
|
+
}
|
|
2739
|
+
const lineWidth = Math.max(1, ...presentation.lines.map((line) => Math.max(line.oldLine ?? 0, line.newLine ?? 0))).toString().length;
|
|
2740
|
+
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", width: "100%", children: [
|
|
2741
|
+
/* @__PURE__ */ jsxs4(Box_default, { flexDirection: "row", children: [
|
|
2742
|
+
/* @__PURE__ */ jsx5(Text, { color: DIFF_ADDED_MARKER, children: "\u25CF" }),
|
|
2743
|
+
/* @__PURE__ */ jsxs4(Text, { color: DIFF_CONTENT, children: [
|
|
2744
|
+
" ",
|
|
2745
|
+
/* @__PURE__ */ jsx5(Text, { bold: true, children: operation }),
|
|
2746
|
+
"(",
|
|
2747
|
+
basename3(presentation.path),
|
|
2748
|
+
")"
|
|
2749
|
+
] })
|
|
2750
|
+
] }),
|
|
2751
|
+
/* @__PURE__ */ jsxs4(Text, { color: DIFF_CONTENT, children: [
|
|
2752
|
+
" \u2514 Added ",
|
|
2753
|
+
lineCount(presentation.added),
|
|
2754
|
+
", removed ",
|
|
2755
|
+
lineCount(presentation.removed)
|
|
2756
|
+
] }),
|
|
2757
|
+
presentation.lines.map((line, index) => /* @__PURE__ */ jsx5(FileDiffRow, { line, lineWidth }, `${line.kind}:${line.oldLine ?? ""}:${line.newLine ?? ""}:${index}`))
|
|
2758
|
+
] });
|
|
2759
|
+
}
|
|
2760
|
+
function FileDiffRow({ line, lineWidth }) {
|
|
2761
|
+
if (line.kind === "omitted") {
|
|
2762
|
+
return /* @__PURE__ */ jsxs4(Text, { color: DIFF_CONTENT, dimColor: true, children: [
|
|
2763
|
+
" ".repeat(lineWidth + 4),
|
|
2764
|
+
line.text
|
|
2765
|
+
] });
|
|
2766
|
+
}
|
|
2767
|
+
const number = line.newLine ?? line.oldLine;
|
|
2768
|
+
const marker = line.kind === "removed" ? "-" : line.kind === "added" ? "+" : " ";
|
|
2769
|
+
const { backgroundColor, markerColor, contentColor } = fileDiffLineStyle(line.kind);
|
|
2770
|
+
const prefix = `${String(number ?? "").padStart(lineWidth)} ${marker}| `;
|
|
2771
|
+
return /* @__PURE__ */ jsxs4(
|
|
2772
|
+
Box_default,
|
|
2773
|
+
{
|
|
2774
|
+
flexDirection: "row",
|
|
2775
|
+
width: "100%",
|
|
2776
|
+
...backgroundColor === void 0 ? {} : { backgroundColor },
|
|
2777
|
+
children: [
|
|
2778
|
+
/* @__PURE__ */ jsx5(
|
|
2779
|
+
Text,
|
|
2780
|
+
{
|
|
2781
|
+
color: markerColor,
|
|
2782
|
+
...line.kind === "context" ? { dimColor: true } : {},
|
|
2783
|
+
...backgroundColor === void 0 ? {} : { backgroundColor },
|
|
2784
|
+
children: prefix
|
|
2785
|
+
}
|
|
2786
|
+
),
|
|
2787
|
+
/* @__PURE__ */ jsx5(
|
|
2788
|
+
Text,
|
|
2789
|
+
{
|
|
2790
|
+
color: contentColor,
|
|
2791
|
+
...backgroundColor === void 0 ? {} : { backgroundColor },
|
|
2792
|
+
children: line.text
|
|
2793
|
+
}
|
|
2794
|
+
)
|
|
2795
|
+
]
|
|
2796
|
+
}
|
|
2797
|
+
);
|
|
2798
|
+
}
|
|
2799
|
+
function lineCount(count) {
|
|
2800
|
+
return `${count} ${count === 1 ? "line" : "lines"}`;
|
|
2801
|
+
}
|
|
2802
|
+
function StatusLine({ block, interactive }) {
|
|
2803
|
+
if (block.progress !== void 0) return /* @__PURE__ */ jsx5(CompactProgressLine, { progress: block.progress });
|
|
2804
|
+
const running = block.state === "running";
|
|
2805
|
+
const [ref, time] = useAnimationFrame(running && interactive ? 120 : null);
|
|
2806
|
+
const color = statusLineColor(block);
|
|
2807
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2808
|
+
const spinner = running && interactive ? frames[Math.floor(Math.max(0, time) / 120) % frames.length] + " " : "";
|
|
2809
|
+
return /* @__PURE__ */ jsxs4(Box_default, { ref, flexDirection: "row", children: [
|
|
2810
|
+
/* @__PURE__ */ jsxs4(Text, { ...color === void 0 ? { dimColor: true } : { color }, children: [
|
|
2811
|
+
spinner,
|
|
2812
|
+
block.text
|
|
2813
|
+
] }),
|
|
2814
|
+
block.hint === void 0 ? null : /* @__PURE__ */ jsxs4(Text, { dimColor: true, wrap: "truncate-end", children: [
|
|
2815
|
+
" (",
|
|
2816
|
+
block.hint,
|
|
2817
|
+
")"
|
|
2818
|
+
] })
|
|
2819
|
+
] });
|
|
2820
|
+
}
|
|
2821
|
+
function statusLineColor(block) {
|
|
2822
|
+
if (block.tone === "retry" || block.tone === "warning") return "ansi:yellowBright";
|
|
2823
|
+
if (block.state === "running") return "#d77757";
|
|
2824
|
+
if (block.state === "completed") return "ansi:green";
|
|
2825
|
+
if (block.state === "failed" || block.state === "cancelled") return "#e06c50";
|
|
2826
|
+
return void 0;
|
|
2827
|
+
}
|
|
2828
|
+
function CompactProgressLine({ progress }) {
|
|
2829
|
+
const presentation = compactProgressPresentation(progress);
|
|
2830
|
+
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "row", children: [
|
|
2831
|
+
/* @__PURE__ */ jsx5(Text, { children: "Compacting context " }),
|
|
2832
|
+
presentation.cells.map((cell, index) => /* @__PURE__ */ jsxs4(Text, { color: cell.color, children: [
|
|
2833
|
+
"\u25A0",
|
|
2834
|
+
index === presentation.cells.length - 1 ? "" : " "
|
|
2835
|
+
] }, index)),
|
|
2836
|
+
/* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
|
|
2837
|
+
" ",
|
|
2838
|
+
presentation.progress,
|
|
2839
|
+
"%"
|
|
2840
|
+
] })
|
|
2841
|
+
] });
|
|
2842
|
+
}
|
|
2843
|
+
function PromptLine({
|
|
2844
|
+
input,
|
|
2845
|
+
pastedBlocks,
|
|
2846
|
+
cursor,
|
|
2847
|
+
columns,
|
|
2848
|
+
maxVisibleLines,
|
|
2849
|
+
completedSlashTokenLength: tokenLength = 0,
|
|
2850
|
+
onCursorChange
|
|
2851
|
+
}) {
|
|
2852
|
+
const innerColumns = Math.max(1, columns - PROMPT_HORIZONTAL_PADDING * 2);
|
|
2853
|
+
const presentation = pastedDraftPresentation(input, cursor, pastedBlocks);
|
|
2854
|
+
const wrap = wrapPromptInput(presentation.text, presentation.cursor, { columns: innerColumns, indent: 2 });
|
|
2855
|
+
const visibleCount = Math.max(1, maxVisibleLines ?? wrap.lines.length);
|
|
2856
|
+
const windowStart = Math.min(
|
|
2857
|
+
Math.max(0, wrap.cursor.line - visibleCount + 1),
|
|
2858
|
+
Math.max(0, wrap.lines.length - visibleCount)
|
|
2859
|
+
);
|
|
2860
|
+
const visibleLines = wrap.lines.slice(windowStart, windowStart + visibleCount);
|
|
2861
|
+
const lineStarts = [];
|
|
2862
|
+
let nextLineStart = 0;
|
|
2863
|
+
for (const line of wrap.lines) {
|
|
2864
|
+
lineStarts.push(nextLineStart);
|
|
2865
|
+
nextLineStart += [...line].length;
|
|
2866
|
+
}
|
|
2867
|
+
return /* @__PURE__ */ jsx5(Box_default, { width: "100%", flexDirection: "column", paddingX: PROMPT_HORIZONTAL_PADDING, children: visibleLines.map((line, visibleIndex) => {
|
|
2868
|
+
const lineIndex = windowStart + visibleIndex;
|
|
2869
|
+
const isCursorLine = lineIndex === wrap.cursor.line;
|
|
2870
|
+
const points = [...line];
|
|
2871
|
+
const cursorCol = wrap.cursor.column;
|
|
2872
|
+
const styledCount = Math.max(0, Math.min(points.length, tokenLength - (lineStarts[lineIndex] ?? 0)));
|
|
2873
|
+
return /* @__PURE__ */ jsxs4(
|
|
2874
|
+
Box_default,
|
|
2875
|
+
{
|
|
2876
|
+
...onCursorChange === void 0 ? {} : {
|
|
2877
|
+
onClick: (event) => {
|
|
2878
|
+
onCursorChange(promptCursorFromClick(input, pastedBlocks, {
|
|
2879
|
+
columns: innerColumns,
|
|
2880
|
+
lineIndex,
|
|
2881
|
+
localColumn: event.localCol
|
|
2882
|
+
}));
|
|
2883
|
+
event.stopImmediatePropagation();
|
|
2884
|
+
}
|
|
2885
|
+
},
|
|
2886
|
+
children: [
|
|
2887
|
+
/* @__PURE__ */ jsx5(Text, { color: "yellow", bold: true, children: lineIndex === 0 ? "\u276F " : " " }),
|
|
2888
|
+
/* @__PURE__ */ jsx5(PromptLineContent, { points, cursor: cursorCol, cursorVisible: isCursorLine, styledCount })
|
|
2889
|
+
]
|
|
2890
|
+
},
|
|
2891
|
+
lineIndex
|
|
2892
|
+
);
|
|
2893
|
+
}) });
|
|
2894
|
+
}
|
|
2895
|
+
function StatusBlockView({
|
|
2896
|
+
block,
|
|
2897
|
+
interactive
|
|
2898
|
+
}) {
|
|
2899
|
+
const visibleBlock = cliToolTitle(block);
|
|
2900
|
+
const outcome = cliToolOutcome(block);
|
|
2901
|
+
const primary = visibleBlock.activity === "model" || visibleBlock.task !== void 0 ? /* @__PURE__ */ jsx5(TaskStatusLine, { block: visibleBlock, interactive }) : visibleBlock.state === "completed" && visibleBlock.presentation !== void 0 ? /* @__PURE__ */ jsx5(FileDiffView, { presentation: visibleBlock.presentation }) : /* @__PURE__ */ jsx5(StatusLine, { block: visibleBlock, interactive });
|
|
2902
|
+
return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
|
|
2903
|
+
primary,
|
|
2904
|
+
outcome === void 0 ? null : /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
|
|
2905
|
+
"\u2514 ",
|
|
2906
|
+
outcome
|
|
2907
|
+
] }) }),
|
|
2908
|
+
block.details === void 0 ? null : /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.details }) })
|
|
2909
|
+
] });
|
|
2910
|
+
}
|
|
2911
|
+
function cliToolTitle(block) {
|
|
2912
|
+
if (block.tool === void 0 || block.presentation !== void 0) return block;
|
|
2913
|
+
const input = record(block.tool.input);
|
|
2914
|
+
const path = typeof input?.path === "string" ? basename3(input.path) : void 0;
|
|
2915
|
+
if (path === void 0 || block.text.includes(path)) return block;
|
|
2916
|
+
return { ...block, text: `${block.text} ${path}` };
|
|
2917
|
+
}
|
|
2918
|
+
function cliToolOutcome(block) {
|
|
2919
|
+
const result = block.tool?.result;
|
|
2920
|
+
if (result === void 0 || block.presentation !== void 0) return void 0;
|
|
2921
|
+
if (!result.ok) return truncateInline(result.error?.message ?? "Tool call failed");
|
|
2922
|
+
const output = record(result.output);
|
|
2923
|
+
if (output === void 0) return void 0;
|
|
2924
|
+
if (typeof output.exitCode === "number" || output.exitCode === null) {
|
|
2925
|
+
const exit = output.exitCode === null ? "no exit code" : `exit ${output.exitCode}`;
|
|
2926
|
+
return output.truncated === true ? `${exit} \xB7 output truncated` : exit;
|
|
2927
|
+
}
|
|
2928
|
+
if (typeof output.replacements === "number") return `${output.replacements} replacement${output.replacements === 1 ? "" : "s"}`;
|
|
2929
|
+
if (typeof output.bytes === "number") return `${formatBytes(output.bytes)} written`;
|
|
2930
|
+
if (Array.isArray(output.matches)) return `${output.matches.length} match${output.matches.length === 1 ? "" : "es"}${output.truncated === true ? " \xB7 truncated" : ""}`;
|
|
2931
|
+
if (Array.isArray(output.paths)) return `${output.paths.length} file${output.paths.length === 1 ? "" : "s"}${output.truncated === true ? " \xB7 truncated" : ""}`;
|
|
2932
|
+
if (Array.isArray(output.files)) return `${output.files.length} file${output.files.length === 1 ? "" : "s"} changed`;
|
|
2933
|
+
return void 0;
|
|
2934
|
+
}
|
|
2935
|
+
function record(value) {
|
|
2936
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
2937
|
+
}
|
|
2938
|
+
function truncateInline(value, maxChars = 180) {
|
|
2939
|
+
const text = value.replace(/\s+/gu, " ").trim();
|
|
2940
|
+
return text.length <= maxChars ? text : `${text.slice(0, maxChars - 1)}\u2026`;
|
|
2941
|
+
}
|
|
2942
|
+
function formatBytes(bytes) {
|
|
2943
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2944
|
+
if (bytes < 1048576) return `${(bytes / 1024).toFixed(bytes < 10240 ? 1 : 0)} KB`;
|
|
2945
|
+
return `${(bytes / 1048576).toFixed(1)} MB`;
|
|
2946
|
+
}
|
|
2947
|
+
function pastedDraftModel(input, cursor, blocks) {
|
|
2948
|
+
const sourceMatches = [];
|
|
2949
|
+
for (const block of blocks) {
|
|
2950
|
+
const extraLines = block.text.split(/\r\n|\r|\n/u).length - 1;
|
|
2951
|
+
if (extraLines === 0 || block.text.length === 0) continue;
|
|
2952
|
+
let start = input.indexOf(block.text);
|
|
2953
|
+
while (start >= 0) {
|
|
2954
|
+
const end = start + block.text.length;
|
|
2955
|
+
const overlaps = sourceMatches.some((match) => start < match.end && end > match.start);
|
|
2956
|
+
if (!overlaps) {
|
|
2957
|
+
sourceMatches.push({
|
|
2958
|
+
start,
|
|
2959
|
+
end,
|
|
2960
|
+
startPoint: [...input.slice(0, start)].length,
|
|
2961
|
+
endPoint: [...input.slice(0, end)].length,
|
|
2962
|
+
label: `[Pasted text #${block.id} +${extraLines} lines]`
|
|
2963
|
+
});
|
|
2964
|
+
break;
|
|
2965
|
+
}
|
|
2966
|
+
start = input.indexOf(block.text, start + 1);
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
if (sourceMatches.length === 0) return { text: input, cursor, matches: [] };
|
|
2970
|
+
sourceMatches.sort((left, right) => left.start - right.start);
|
|
2971
|
+
let text = "";
|
|
2972
|
+
let sourceOffset = 0;
|
|
2973
|
+
const matches = [];
|
|
2974
|
+
for (const match of sourceMatches) {
|
|
2975
|
+
text += input.slice(sourceOffset, match.start);
|
|
2976
|
+
const displayStartPoint = [...text].length;
|
|
2977
|
+
text += match.label;
|
|
2978
|
+
const displayEndPoint = [...text].length;
|
|
2979
|
+
matches.push({
|
|
2980
|
+
sourceStartPoint: match.startPoint,
|
|
2981
|
+
sourceEndPoint: match.endPoint,
|
|
2982
|
+
displayStartPoint,
|
|
2983
|
+
displayEndPoint,
|
|
2984
|
+
label: match.label
|
|
2985
|
+
});
|
|
2986
|
+
sourceOffset = match.end;
|
|
2987
|
+
}
|
|
2988
|
+
text += input.slice(sourceOffset);
|
|
2989
|
+
let pointDelta = 0;
|
|
2990
|
+
let mappedCursor = cursor;
|
|
2991
|
+
for (const match of matches) {
|
|
2992
|
+
const labelLength = match.displayEndPoint - match.displayStartPoint;
|
|
2993
|
+
if (cursor <= match.sourceStartPoint) break;
|
|
2994
|
+
if (cursor < match.sourceEndPoint) {
|
|
2995
|
+
mappedCursor = match.displayEndPoint;
|
|
2996
|
+
break;
|
|
2997
|
+
}
|
|
2998
|
+
pointDelta += labelLength - (match.sourceEndPoint - match.sourceStartPoint);
|
|
2999
|
+
mappedCursor = cursor + pointDelta;
|
|
3000
|
+
}
|
|
3001
|
+
return { text, cursor: mappedCursor, matches };
|
|
3002
|
+
}
|
|
3003
|
+
function pastedDraftPresentation(input, cursor, blocks) {
|
|
3004
|
+
const model = pastedDraftModel(input, cursor, blocks);
|
|
3005
|
+
return { text: model.text, cursor: model.cursor };
|
|
3006
|
+
}
|
|
3007
|
+
function codePointIndexAtVisualColumn(line, targetColumn) {
|
|
3008
|
+
const points = [...line];
|
|
3009
|
+
const target = Math.max(0, targetColumn);
|
|
3010
|
+
let visualColumn = 0;
|
|
3011
|
+
for (let index = 0; index < points.length; index += 1) {
|
|
3012
|
+
const width = charWidth(points[index].codePointAt(0) ?? 0);
|
|
3013
|
+
if (target <= visualColumn) return index;
|
|
3014
|
+
if (target < visualColumn + width) {
|
|
3015
|
+
return target - visualColumn < width / 2 ? index : index + 1;
|
|
3016
|
+
}
|
|
3017
|
+
visualColumn += width;
|
|
3018
|
+
}
|
|
3019
|
+
return points.length;
|
|
3020
|
+
}
|
|
3021
|
+
function sourceCursorFromDisplayed(model, displayedCursor) {
|
|
3022
|
+
let sourceOffset = 0;
|
|
3023
|
+
let displayOffset = 0;
|
|
3024
|
+
for (const match of model.matches) {
|
|
3025
|
+
if (displayedCursor < match.displayStartPoint) {
|
|
3026
|
+
return sourceOffset + displayedCursor - displayOffset;
|
|
3027
|
+
}
|
|
3028
|
+
if (displayedCursor <= match.displayEndPoint) return match.sourceEndPoint;
|
|
3029
|
+
sourceOffset = match.sourceEndPoint;
|
|
3030
|
+
displayOffset = match.displayEndPoint;
|
|
3031
|
+
}
|
|
3032
|
+
return sourceOffset + displayedCursor - displayOffset;
|
|
3033
|
+
}
|
|
3034
|
+
function promptCursorFromClick(input, blocks, position) {
|
|
3035
|
+
const model = pastedDraftModel(input, 0, blocks);
|
|
3036
|
+
const wrap = wrapPromptInput(model.text, 0, { columns: position.columns, indent: 2 });
|
|
3037
|
+
const lineIndex = Math.max(0, Math.min(wrap.lines.length - 1, position.lineIndex));
|
|
3038
|
+
const beforeLine = wrap.lines.slice(0, lineIndex).reduce((total, line) => total + [...line].length, 0);
|
|
3039
|
+
const withinLine = codePointIndexAtVisualColumn(
|
|
3040
|
+
wrap.lines[lineIndex] ?? "",
|
|
3041
|
+
position.localColumn - 2
|
|
3042
|
+
);
|
|
3043
|
+
const sourceCursor = sourceCursorFromDisplayed(model, beforeLine + withinLine);
|
|
3044
|
+
return Math.max(0, Math.min([...input].length, sourceCursor));
|
|
3045
|
+
}
|
|
3046
|
+
function PromptLineContent({
|
|
3047
|
+
points,
|
|
3048
|
+
cursor,
|
|
3049
|
+
cursorVisible,
|
|
3050
|
+
styledCount
|
|
3051
|
+
}) {
|
|
3052
|
+
const tokenStyle = completedSlashTokenPresentation();
|
|
3053
|
+
return /* @__PURE__ */ jsxs4(Text, { children: [
|
|
3054
|
+
points.map((point, index) => {
|
|
3055
|
+
const styled = index < styledCount;
|
|
3056
|
+
const caret = cursorVisible && index === cursor;
|
|
3057
|
+
return /* @__PURE__ */ jsx5(
|
|
3058
|
+
Text,
|
|
3059
|
+
{
|
|
3060
|
+
...styled ? tokenStyle : {},
|
|
3061
|
+
...caret ? { inverse: true } : {},
|
|
3062
|
+
children: point
|
|
3063
|
+
},
|
|
3064
|
+
index
|
|
3065
|
+
);
|
|
3066
|
+
}),
|
|
3067
|
+
cursorVisible && cursor >= points.length ? /* @__PURE__ */ jsx5(Text, { inverse: true, children: " " }) : null
|
|
3068
|
+
] });
|
|
3069
|
+
}
|
|
3070
|
+
function editPrompt(state, edit) {
|
|
3071
|
+
const points = [...state.text];
|
|
3072
|
+
const cursor = Math.max(0, Math.min(points.length, state.cursor));
|
|
3073
|
+
if (edit.type === "insert") {
|
|
3074
|
+
const inserted = [...edit.value];
|
|
3075
|
+
points.splice(cursor, 0, ...inserted);
|
|
3076
|
+
return { text: points.join(""), cursor: cursor + inserted.length };
|
|
3077
|
+
}
|
|
3078
|
+
if (edit.type === "left") return { text: state.text, cursor: Math.max(0, cursor - 1) };
|
|
3079
|
+
if (edit.type === "right") return { text: state.text, cursor: Math.min(points.length, cursor + 1) };
|
|
3080
|
+
if (edit.type === "backspace") {
|
|
3081
|
+
if (cursor === 0) return { text: state.text, cursor };
|
|
3082
|
+
points.splice(cursor - 1, 1);
|
|
3083
|
+
return { text: points.join(""), cursor: cursor - 1 };
|
|
3084
|
+
}
|
|
3085
|
+
if (edit.type === "delete") {
|
|
3086
|
+
points.splice(cursor, 1);
|
|
3087
|
+
return { text: points.join(""), cursor };
|
|
3088
|
+
}
|
|
3089
|
+
return { text: state.text, cursor };
|
|
3090
|
+
}
|
|
3091
|
+
function editPromptWithPastedBlocks(state, edit, pastedBlocks) {
|
|
3092
|
+
const points = [...state.text];
|
|
3093
|
+
const cursor = Math.max(0, Math.min(points.length, state.cursor));
|
|
3094
|
+
const latest = pastedBlocks.at(-1);
|
|
3095
|
+
if (edit.type === "backspace" && latest !== void 0) {
|
|
3096
|
+
const pastedPoints = [...latest.text];
|
|
3097
|
+
const start = cursor - pastedPoints.length;
|
|
3098
|
+
if (start >= 0 && points.slice(start, cursor).join("") === latest.text) {
|
|
3099
|
+
points.splice(start, pastedPoints.length);
|
|
3100
|
+
return {
|
|
3101
|
+
text: points.join(""),
|
|
3102
|
+
cursor: start,
|
|
3103
|
+
pastedBlocks: pastedBlocks.slice(0, -1)
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
return { ...editPrompt({ text: state.text, cursor }, edit), pastedBlocks: [...pastedBlocks] };
|
|
3108
|
+
}
|
|
3109
|
+
function navigateHistory(state, direction) {
|
|
3110
|
+
const cursor = direction === "up" ? Math.max(0, state.cursor - 1) : Math.min(state.history.length, state.cursor + 1);
|
|
3111
|
+
const input = state.history[cursor] ?? "";
|
|
3112
|
+
return { cursor, input, promptCursor: [...input].length };
|
|
3113
|
+
}
|
|
3114
|
+
function updatePrompt(edit, text, cursor, setText, setCursor) {
|
|
3115
|
+
const next = editPrompt({ text, cursor }, edit);
|
|
3116
|
+
setText(next.text);
|
|
3117
|
+
setCursor(next.cursor);
|
|
3118
|
+
}
|
|
3119
|
+
async function submitSafely(session, prompt, report) {
|
|
3120
|
+
try {
|
|
3121
|
+
await session.submit(prompt);
|
|
3122
|
+
} catch (error) {
|
|
3123
|
+
safeReport(report, safeUiError(error));
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
async function shutdownRuntime(runtime, exit, report) {
|
|
3127
|
+
try {
|
|
3128
|
+
await closeAndDisposeRuntime(runtime, report);
|
|
3129
|
+
} finally {
|
|
3130
|
+
exit();
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
async function closeAndDisposeRuntime(runtime, report) {
|
|
3134
|
+
if (runtime === void 0) return;
|
|
3135
|
+
try {
|
|
3136
|
+
await runtime.session.close();
|
|
3137
|
+
} catch (error) {
|
|
3138
|
+
safeReport(report, safeUiError(error));
|
|
3139
|
+
} finally {
|
|
3140
|
+
try {
|
|
3141
|
+
await runtime.dispose();
|
|
3142
|
+
} catch (error) {
|
|
3143
|
+
safeReport(report, safeUiError(error));
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
function safeUiError(error) {
|
|
3148
|
+
return redactErrorText(message(error)).slice(0, 2e3);
|
|
3149
|
+
}
|
|
3150
|
+
function safeReport(report, value) {
|
|
3151
|
+
try {
|
|
3152
|
+
report(value);
|
|
3153
|
+
} catch {
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
export {
|
|
3157
|
+
App,
|
|
3158
|
+
FooterStatus,
|
|
3159
|
+
HISTORY_CAP,
|
|
3160
|
+
MentionMenu,
|
|
3161
|
+
PromptLine,
|
|
3162
|
+
SinglePendingPrompt,
|
|
3163
|
+
TerminalLayout,
|
|
3164
|
+
classifyTerminalInput,
|
|
3165
|
+
closeAndDisposeRuntime,
|
|
3166
|
+
completionKeyAction,
|
|
3167
|
+
editPrompt,
|
|
3168
|
+
editPromptWithPastedBlocks,
|
|
3169
|
+
ideFooterPresentation,
|
|
3170
|
+
navigateHistory,
|
|
3171
|
+
pastedDraftPresentation,
|
|
3172
|
+
prepareCliSubmission,
|
|
3173
|
+
promptCursorFromClick,
|
|
3174
|
+
promptDelivery,
|
|
3175
|
+
removeLastCliImageOnBackspace,
|
|
3176
|
+
resolvePromptDelivery,
|
|
3177
|
+
runTerminalSubmissionChain,
|
|
3178
|
+
selectWheelScrollTarget,
|
|
3179
|
+
shutdownRuntime,
|
|
3180
|
+
slashKeyAction,
|
|
3181
|
+
statusLineColor,
|
|
3182
|
+
submitSafely,
|
|
3183
|
+
taskPanelViewportRows
|
|
3184
|
+
};
|
|
3185
|
+
//# sourceMappingURL=app-WC2XXGLM.js.map
|