arcie 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.ts +1 -1
- package/dist/build-TWNCPXBU.js +82 -0
- package/dist/build-TWNCPXBU.js.map +1 -0
- package/dist/channels/index.d.ts +1 -1
- package/dist/{chunk-2ZSKZYWK.js → chunk-4WSILP75.js} +37 -164
- package/dist/chunk-4WSILP75.js.map +1 -0
- package/dist/chunk-HVBDOHTL.js +148 -0
- package/dist/chunk-HVBDOHTL.js.map +1 -0
- package/dist/chunk-HWD2Z2CH.js +742 -0
- package/dist/chunk-HWD2Z2CH.js.map +1 -0
- package/dist/chunk-KVSX4MLK.js +139 -0
- package/dist/chunk-KVSX4MLK.js.map +1 -0
- package/dist/chunk-N5R3TEEZ.js +15 -0
- package/dist/chunk-N5R3TEEZ.js.map +1 -0
- package/dist/chunk-SGWQZZ2A.js +54 -0
- package/dist/chunk-SGWQZZ2A.js.map +1 -0
- package/dist/chunk-XOFWQUOO.js +30 -0
- package/dist/chunk-XOFWQUOO.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +156 -231
- package/dist/cli/index.js.map +1 -1
- package/dist/context/index.d.ts +1 -1
- package/dist/dev-ZZDVOWUZ.js +1967 -0
- package/dist/dev-ZZDVOWUZ.js.map +1 -0
- package/dist/hooks/index.d.ts +1 -1
- package/dist/{index-b4NcDl3m.d.ts → index-CElLRVTP.d.ts} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/init-5F7CIVLN.js +822 -0
- package/dist/init-5F7CIVLN.js.map +1 -0
- package/dist/instructions/index.d.ts +1 -1
- package/dist/runner/index.d.ts +2 -2
- package/dist/runner/index.js +2 -1
- package/dist/scaffold-web-chat-OQL5C2GK.js +7 -0
- package/dist/scaffold-web-chat-OQL5C2GK.js.map +1 -0
- package/dist/schedules/index.d.ts +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/{types-BxOQTzx1.d.ts → types-DwxwdSa2.d.ts} +1 -1
- package/package.json +1 -1
- package/templates/default/.env.local +12 -0
- package/templates/web-chat/.env.local.example +4 -0
- package/templates/web-chat/README.md +51 -0
- package/templates/web-chat/app/api/chat/route.ts +43 -0
- package/templates/web-chat/app/globals.css +65 -0
- package/templates/web-chat/app/layout.tsx +15 -0
- package/templates/web-chat/app/page.tsx +9 -0
- package/templates/web-chat/components/chat.tsx +219 -0
- package/templates/web-chat/components/input-bar.tsx +181 -0
- package/templates/web-chat/components/message.tsx +123 -0
- package/templates/web-chat/components/tool-call.tsx +79 -0
- package/templates/web-chat/components/ui/button.tsx +46 -0
- package/templates/web-chat/components/ui/input.tsx +19 -0
- package/templates/web-chat/components.json +20 -0
- package/templates/web-chat/lib/stream.ts +41 -0
- package/templates/web-chat/lib/types.ts +62 -0
- package/templates/web-chat/lib/utils.ts +6 -0
- package/templates/web-chat/next.config.mjs +6 -0
- package/templates/web-chat/package.json +33 -0
- package/templates/web-chat/postcss.config.mjs +6 -0
- package/templates/web-chat/tailwind.config.ts +61 -0
- package/templates/web-chat/tsconfig.json +23 -0
- package/dist/chunk-2ZSKZYWK.js.map +0 -1
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
// src/cli/tui/renderer/live-region.ts
|
|
2
|
+
var ESC = "\x1B[";
|
|
3
|
+
var ERASE_DOWN = `${ESC}J`;
|
|
4
|
+
var CURSOR_TO_COL_1 = `${ESC}G`;
|
|
5
|
+
var HIDE_CURSOR = `${ESC}?25l`;
|
|
6
|
+
var SHOW_CURSOR = `${ESC}?25h`;
|
|
7
|
+
function cursorUp(rows) {
|
|
8
|
+
return rows > 0 ? `${ESC}${rows}A` : "";
|
|
9
|
+
}
|
|
10
|
+
function cursorToColumn(col) {
|
|
11
|
+
return `${ESC}${Math.max(1, col)}G`;
|
|
12
|
+
}
|
|
13
|
+
var LiveRegion = class {
|
|
14
|
+
#output;
|
|
15
|
+
#resizeListener;
|
|
16
|
+
#lastFrame = [];
|
|
17
|
+
#caret = { row: 0, col: 1 };
|
|
18
|
+
#stopped = false;
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.#output = options.output ?? process.stdout;
|
|
21
|
+
this.#resizeListener = () => {
|
|
22
|
+
if (this.#stopped) return;
|
|
23
|
+
options.onResize?.({
|
|
24
|
+
columns: this.#output.columns ?? 80,
|
|
25
|
+
rows: this.#output.rows ?? 24
|
|
26
|
+
});
|
|
27
|
+
const previous = this.#lastFrame;
|
|
28
|
+
this.#lastFrame = [];
|
|
29
|
+
this.paint(previous, this.#caret);
|
|
30
|
+
};
|
|
31
|
+
this.#output.on("resize", this.#resizeListener);
|
|
32
|
+
}
|
|
33
|
+
get columns() {
|
|
34
|
+
return this.#output.columns ?? 80;
|
|
35
|
+
}
|
|
36
|
+
get rows() {
|
|
37
|
+
return this.#output.rows ?? 24;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Redraws the live region to match `rows`. The cursor is positioned at
|
|
41
|
+
* `caret` (1-indexed row within the region, 1-indexed column). Passing an
|
|
42
|
+
* empty array clears the region without leaving trailing blank lines.
|
|
43
|
+
*/
|
|
44
|
+
paint(rows, caret = { row: 1, col: 1 }) {
|
|
45
|
+
if (this.#stopped) return;
|
|
46
|
+
const output = this.#output;
|
|
47
|
+
const previousHeight = this.#lastFrame.length;
|
|
48
|
+
let chunk = HIDE_CURSOR;
|
|
49
|
+
if (previousHeight > 0) {
|
|
50
|
+
chunk += cursorUp(previousHeight - 1) + CURSOR_TO_COL_1 + ERASE_DOWN;
|
|
51
|
+
} else {
|
|
52
|
+
chunk += CURSOR_TO_COL_1 + ERASE_DOWN;
|
|
53
|
+
}
|
|
54
|
+
chunk += rows.join("\n");
|
|
55
|
+
const rowsBelowCaret = Math.max(0, rows.length - Math.max(1, caret.row));
|
|
56
|
+
if (rowsBelowCaret > 0) chunk += cursorUp(rowsBelowCaret);
|
|
57
|
+
chunk += cursorToColumn(caret.col);
|
|
58
|
+
chunk += SHOW_CURSOR;
|
|
59
|
+
output.write(chunk);
|
|
60
|
+
this.#lastFrame = [...rows];
|
|
61
|
+
this.#caret = caret;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Commits `rows` to scrollback above the live region, then repaints the
|
|
65
|
+
* region beneath them. Rows are terminated by newlines so the terminal
|
|
66
|
+
* scrolls each into history.
|
|
67
|
+
*/
|
|
68
|
+
commit(rows) {
|
|
69
|
+
if (this.#stopped) return;
|
|
70
|
+
if (rows.length === 0) return;
|
|
71
|
+
const output = this.#output;
|
|
72
|
+
const previousHeight = this.#lastFrame.length;
|
|
73
|
+
let chunk = HIDE_CURSOR;
|
|
74
|
+
if (previousHeight > 0) {
|
|
75
|
+
chunk += cursorUp(previousHeight - 1) + CURSOR_TO_COL_1 + ERASE_DOWN;
|
|
76
|
+
} else {
|
|
77
|
+
chunk += CURSOR_TO_COL_1;
|
|
78
|
+
}
|
|
79
|
+
for (const row of rows) chunk += `${row}
|
|
80
|
+
`;
|
|
81
|
+
output.write(chunk);
|
|
82
|
+
const preserved = this.#lastFrame;
|
|
83
|
+
const caret = this.#caret;
|
|
84
|
+
this.#lastFrame = [];
|
|
85
|
+
if (preserved.length > 0) {
|
|
86
|
+
this.paint(preserved, caret);
|
|
87
|
+
} else {
|
|
88
|
+
output.write(SHOW_CURSOR);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Clears the live region and shows the cursor at the start of what used to
|
|
93
|
+
* be the region's first row. Idempotent; safe to call from cleanup paths.
|
|
94
|
+
*/
|
|
95
|
+
stop() {
|
|
96
|
+
if (this.#stopped) return;
|
|
97
|
+
this.#stopped = true;
|
|
98
|
+
this.#output.off("resize", this.#resizeListener);
|
|
99
|
+
if (this.#lastFrame.length > 0) {
|
|
100
|
+
const chunk = cursorUp(this.#lastFrame.length - 1) + CURSOR_TO_COL_1 + ERASE_DOWN + SHOW_CURSOR;
|
|
101
|
+
this.#output.write(chunk);
|
|
102
|
+
} else {
|
|
103
|
+
this.#output.write(SHOW_CURSOR);
|
|
104
|
+
}
|
|
105
|
+
this.#lastFrame = [];
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/cli/tui/attach-keys.ts
|
|
110
|
+
import { stdin as input } from "process";
|
|
111
|
+
|
|
112
|
+
// src/cli/tui/key-parser.ts
|
|
113
|
+
var CSI_FINAL = /[@-~]/u;
|
|
114
|
+
var PASTE_START = "\x1B[200~";
|
|
115
|
+
var PASTE_END = "\x1B[201~";
|
|
116
|
+
function sanitizePastedText(text) {
|
|
117
|
+
let printable = "";
|
|
118
|
+
for (const character of text.replace(/\r\n?/gu, "\n")) {
|
|
119
|
+
if (character === "\n" || character === " ") {
|
|
120
|
+
printable += character;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const code = character.codePointAt(0);
|
|
124
|
+
if (code === void 0) continue;
|
|
125
|
+
if (code < 32 || code === 127 || code >= 128 && code <= 159) continue;
|
|
126
|
+
printable += character;
|
|
127
|
+
}
|
|
128
|
+
return printable;
|
|
129
|
+
}
|
|
130
|
+
function isIncompletePaste(buffer) {
|
|
131
|
+
return buffer.startsWith(PASTE_START) && !buffer.includes(PASTE_END, PASTE_START.length);
|
|
132
|
+
}
|
|
133
|
+
function stripPromptControlCharacters(text) {
|
|
134
|
+
let printable = "";
|
|
135
|
+
for (const character of text) {
|
|
136
|
+
if (character >= " " && character !== "\x7F") printable += character;
|
|
137
|
+
}
|
|
138
|
+
return printable;
|
|
139
|
+
}
|
|
140
|
+
function nextKey(buffer) {
|
|
141
|
+
const first = buffer[0];
|
|
142
|
+
if (first === void 0) return { consumed: 0, incomplete: true };
|
|
143
|
+
if (first === "\x1B") {
|
|
144
|
+
if (buffer.length === 1) return { consumed: 0, incomplete: true };
|
|
145
|
+
const second = buffer[1];
|
|
146
|
+
if (second === "O") {
|
|
147
|
+
if (buffer.length < 3) return { consumed: 0, incomplete: true };
|
|
148
|
+
return { key: parseKey(Buffer.from(buffer.slice(0, 3))), consumed: 3 };
|
|
149
|
+
}
|
|
150
|
+
if (second === "[") {
|
|
151
|
+
if (buffer.startsWith(PASTE_START)) {
|
|
152
|
+
const end2 = buffer.indexOf(PASTE_END, PASTE_START.length);
|
|
153
|
+
if (end2 === -1) return { consumed: 0, incomplete: true };
|
|
154
|
+
return {
|
|
155
|
+
key: {
|
|
156
|
+
type: "text",
|
|
157
|
+
value: sanitizePastedText(buffer.slice(PASTE_START.length, end2)),
|
|
158
|
+
framing: "bracketed-paste"
|
|
159
|
+
},
|
|
160
|
+
consumed: end2 + PASTE_END.length
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
for (let i = 2; i < buffer.length; i += 1) {
|
|
164
|
+
if (CSI_FINAL.test(buffer[i])) {
|
|
165
|
+
return { key: parseKey(Buffer.from(buffer.slice(0, i + 1))), consumed: i + 1 };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { consumed: 0, incomplete: true };
|
|
169
|
+
}
|
|
170
|
+
return { key: { type: "escape" }, consumed: 1 };
|
|
171
|
+
}
|
|
172
|
+
if (first < " " || first === "\x7F") {
|
|
173
|
+
return { key: parseKey(Buffer.from(first)), consumed: 1 };
|
|
174
|
+
}
|
|
175
|
+
let end = 1;
|
|
176
|
+
while (end < buffer.length) {
|
|
177
|
+
const char = buffer[end];
|
|
178
|
+
if (char === "\x1B" || char < " " || char === "\x7F") break;
|
|
179
|
+
end += 1;
|
|
180
|
+
}
|
|
181
|
+
return { key: parseKey(Buffer.from(buffer.slice(0, end))), consumed: end };
|
|
182
|
+
}
|
|
183
|
+
function parseKey(chunk) {
|
|
184
|
+
const value = chunk.toString("utf8");
|
|
185
|
+
switch (value) {
|
|
186
|
+
case "":
|
|
187
|
+
return { type: "ctrl-a" };
|
|
188
|
+
case "":
|
|
189
|
+
return { type: "ctrl-e" };
|
|
190
|
+
case "":
|
|
191
|
+
return { type: "ctrl-d" };
|
|
192
|
+
case "\v":
|
|
193
|
+
return { type: "ctrl-k" };
|
|
194
|
+
case "":
|
|
195
|
+
return { type: "ctrl-n" };
|
|
196
|
+
case "":
|
|
197
|
+
return { type: "ctrl-p" };
|
|
198
|
+
case "\f":
|
|
199
|
+
return { type: "ctrl-l" };
|
|
200
|
+
case "":
|
|
201
|
+
return { type: "ctrl-r" };
|
|
202
|
+
case "":
|
|
203
|
+
return { type: "ctrl-u" };
|
|
204
|
+
case "":
|
|
205
|
+
return { type: "ctrl-w" };
|
|
206
|
+
case "":
|
|
207
|
+
return { type: "ctrl-c" };
|
|
208
|
+
case "\r":
|
|
209
|
+
case "\n":
|
|
210
|
+
return { type: "enter" };
|
|
211
|
+
case "\x1B[27;2;13~":
|
|
212
|
+
case "\x1B[13;2u":
|
|
213
|
+
return { type: "newline" };
|
|
214
|
+
case "\x7F":
|
|
215
|
+
case "\b":
|
|
216
|
+
return { type: "backspace" };
|
|
217
|
+
case "\x1B[A":
|
|
218
|
+
case "\x1BOA":
|
|
219
|
+
return { type: "up" };
|
|
220
|
+
case "\x1B[B":
|
|
221
|
+
case "\x1BOB":
|
|
222
|
+
return { type: "down" };
|
|
223
|
+
case "\x1B[C":
|
|
224
|
+
case "\x1BOC":
|
|
225
|
+
return { type: "right" };
|
|
226
|
+
case "\x1B[D":
|
|
227
|
+
case "\x1BOD":
|
|
228
|
+
return { type: "left" };
|
|
229
|
+
case "\x1B[H":
|
|
230
|
+
case "\x1BOH":
|
|
231
|
+
case "\x1B[1~":
|
|
232
|
+
return { type: "home" };
|
|
233
|
+
case "\x1B[F":
|
|
234
|
+
case "\x1BOF":
|
|
235
|
+
case "\x1B[4~":
|
|
236
|
+
return { type: "end" };
|
|
237
|
+
case "\x1B[3~":
|
|
238
|
+
return { type: "delete" };
|
|
239
|
+
case " ":
|
|
240
|
+
return { type: "tab" };
|
|
241
|
+
case "\x1B":
|
|
242
|
+
return { type: "escape" };
|
|
243
|
+
default: {
|
|
244
|
+
const printable = stripPromptControlCharacters(value);
|
|
245
|
+
return printable.length > 0 ? { type: "text", value: printable, framing: "unframed" } : { type: "ignore" };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/cli/tui/attach-keys.ts
|
|
251
|
+
function attachKeyStream(onKey) {
|
|
252
|
+
let buffer = "";
|
|
253
|
+
const originalRawMode = input.isRaw;
|
|
254
|
+
const onData = (chunk) => {
|
|
255
|
+
buffer += chunk.toString("utf8");
|
|
256
|
+
if (isIncompletePaste(buffer)) return;
|
|
257
|
+
while (buffer.length > 0) {
|
|
258
|
+
const token = nextKey(buffer);
|
|
259
|
+
if (token.incomplete === true) break;
|
|
260
|
+
buffer = buffer.slice(token.consumed);
|
|
261
|
+
if (token.key !== void 0 && token.key.type !== "ignore") onKey(token.key);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
try {
|
|
265
|
+
input.setRawMode?.(true);
|
|
266
|
+
} catch {
|
|
267
|
+
}
|
|
268
|
+
input.resume();
|
|
269
|
+
input.on("data", onData);
|
|
270
|
+
return () => {
|
|
271
|
+
input.off("data", onData);
|
|
272
|
+
try {
|
|
273
|
+
input.setRawMode?.(originalRawMode ?? false);
|
|
274
|
+
} catch {
|
|
275
|
+
}
|
|
276
|
+
input.pause();
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/cli/tui/theme.ts
|
|
281
|
+
var ESC2 = "\x1B[";
|
|
282
|
+
function ansi(open, close, enabled) {
|
|
283
|
+
if (!enabled) return (text) => text;
|
|
284
|
+
const prefix = `${ESC2}${open}m`;
|
|
285
|
+
const suffix = `${ESC2}${close}m`;
|
|
286
|
+
return (text) => `${prefix}${text}${suffix}`;
|
|
287
|
+
}
|
|
288
|
+
function ansi256(code, enabled) {
|
|
289
|
+
if (!enabled) return (text) => text;
|
|
290
|
+
const prefix = `${ESC2}38;5;${code}m`;
|
|
291
|
+
const suffix = `${ESC2}39m`;
|
|
292
|
+
return (text) => `${prefix}${text}${suffix}`;
|
|
293
|
+
}
|
|
294
|
+
var UNICODE_GLYPHS = {
|
|
295
|
+
brand: "\u26A1",
|
|
296
|
+
user: "\u258C",
|
|
297
|
+
reasoning: "\u25CB",
|
|
298
|
+
success: "\u2713",
|
|
299
|
+
error: "\u2A2F",
|
|
300
|
+
warning: "\u26A0",
|
|
301
|
+
subagent: "\u25C6",
|
|
302
|
+
rule: "\u2502",
|
|
303
|
+
question: "?",
|
|
304
|
+
connection: "\u25CF",
|
|
305
|
+
arrow: "\u2192",
|
|
306
|
+
pointer: "\u25B7",
|
|
307
|
+
selectedPointer: "\u25B6",
|
|
308
|
+
option: "\u25E6",
|
|
309
|
+
prompt: "\u276F",
|
|
310
|
+
elbow: "\u23BF",
|
|
311
|
+
hrule: "\u2594",
|
|
312
|
+
caret: "\u258F",
|
|
313
|
+
dot: "\xB7",
|
|
314
|
+
ellipsis: "\u2026",
|
|
315
|
+
arrowUp: "\u2191",
|
|
316
|
+
arrowDown: "\u2193"
|
|
317
|
+
};
|
|
318
|
+
var ASCII_GLYPHS = {
|
|
319
|
+
brand: "*",
|
|
320
|
+
user: "|",
|
|
321
|
+
reasoning: "o",
|
|
322
|
+
success: "+",
|
|
323
|
+
error: "x",
|
|
324
|
+
warning: "!",
|
|
325
|
+
subagent: "*",
|
|
326
|
+
rule: "|",
|
|
327
|
+
question: "?",
|
|
328
|
+
connection: "*",
|
|
329
|
+
arrow: "->",
|
|
330
|
+
pointer: ">",
|
|
331
|
+
selectedPointer: ">",
|
|
332
|
+
option: ".",
|
|
333
|
+
prompt: ">",
|
|
334
|
+
elbow: "`-",
|
|
335
|
+
hrule: "=",
|
|
336
|
+
caret: "_",
|
|
337
|
+
dot: "-",
|
|
338
|
+
ellipsis: "...",
|
|
339
|
+
arrowUp: "^",
|
|
340
|
+
arrowDown: "v"
|
|
341
|
+
};
|
|
342
|
+
var UNICODE_SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
343
|
+
var ASCII_SPINNER = ["-", "\\", "|", "/"];
|
|
344
|
+
function createTheme(options = {}) {
|
|
345
|
+
const color = options.color ?? true;
|
|
346
|
+
const unicode = options.unicode ?? true;
|
|
347
|
+
return {
|
|
348
|
+
color,
|
|
349
|
+
unicode,
|
|
350
|
+
colors: {
|
|
351
|
+
reset: ansi(0, 0, color),
|
|
352
|
+
bold: ansi(1, 22, color),
|
|
353
|
+
dim: ansi(2, 22, color),
|
|
354
|
+
inverse: ansi(7, 27, color),
|
|
355
|
+
italic: ansi(3, 23, color),
|
|
356
|
+
white: ansi(97, 39, color),
|
|
357
|
+
gray: ansi(90, 39, color),
|
|
358
|
+
cyan: ansi(36, 39, color),
|
|
359
|
+
green: ansi(32, 39, color),
|
|
360
|
+
red: ansi(31, 39, color),
|
|
361
|
+
yellow: ansi(33, 39, color),
|
|
362
|
+
magenta: ansi(35, 39, color),
|
|
363
|
+
blue: ansi(34, 39, color),
|
|
364
|
+
orange: ansi256(208, color)
|
|
365
|
+
},
|
|
366
|
+
glyph: unicode ? UNICODE_GLYPHS : ASCII_GLYPHS,
|
|
367
|
+
spinner: unicode ? UNICODE_SPINNER : ASCII_SPINNER
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function detectUnicode(env = process.env) {
|
|
371
|
+
const override = env.ARCIE_TUI_UNICODE;
|
|
372
|
+
if (override === "0" || override === "false") return false;
|
|
373
|
+
if (override === "1" || override === "true") return true;
|
|
374
|
+
if (env.TERM === "dumb") return false;
|
|
375
|
+
if (process.platform === "win32") {
|
|
376
|
+
return Boolean(env.WT_SESSION || env.TERM_PROGRAM === "vscode");
|
|
377
|
+
}
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/cli/shared/text-boundaries.ts
|
|
382
|
+
var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
383
|
+
function graphemes(text) {
|
|
384
|
+
return Array.from(graphemeSegmenter.segment(text), ({ index, segment }) => ({
|
|
385
|
+
text: segment,
|
|
386
|
+
start: index,
|
|
387
|
+
end: index + segment.length
|
|
388
|
+
}));
|
|
389
|
+
}
|
|
390
|
+
function previousGraphemeBoundary(text, offset) {
|
|
391
|
+
const clamped = Math.max(0, Math.min(offset, text.length));
|
|
392
|
+
if (clamped === 0) return 0;
|
|
393
|
+
return graphemeSegmenter.segment(text).containing(clamped - 1)?.index ?? 0;
|
|
394
|
+
}
|
|
395
|
+
function nextGraphemeBoundary(text, offset) {
|
|
396
|
+
const clamped = Math.max(0, Math.min(offset, text.length));
|
|
397
|
+
if (clamped === text.length) return clamped;
|
|
398
|
+
const grapheme = graphemeSegmenter.segment(text).containing(clamped);
|
|
399
|
+
return grapheme === void 0 ? text.length : grapheme.index + grapheme.segment.length;
|
|
400
|
+
}
|
|
401
|
+
function graphemeBoundaryAtOrAfter(text, offset) {
|
|
402
|
+
const clamped = Math.max(0, Math.min(offset, text.length));
|
|
403
|
+
if (clamped === 0 || clamped === text.length) return clamped;
|
|
404
|
+
const grapheme = graphemeSegmenter.segment(text).containing(clamped);
|
|
405
|
+
if (grapheme === void 0 || grapheme.index === clamped) return clamped;
|
|
406
|
+
return grapheme.index + grapheme.segment.length;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// src/cli/ui/terminal-text.ts
|
|
410
|
+
var ansiEscape = String.fromCharCode(27);
|
|
411
|
+
var ansiPattern = new RegExp(`${ansiEscape}\\[[0-?]*[ -/]*[@-~]`, "g");
|
|
412
|
+
var ansiPrefixPattern = new RegExp(`^${ansiEscape}\\[[0-?]*[ -/]*[@-~]`);
|
|
413
|
+
var emojiPresentationPattern = /\p{Emoji_Presentation}/u;
|
|
414
|
+
var extendedPictographicPattern = /\p{Extended_Pictographic}/u;
|
|
415
|
+
var keycapPattern = /^[#*0-9]\u{fe0f}?\u{20e3}$/u;
|
|
416
|
+
function stripTerminalControls(input2) {
|
|
417
|
+
let output = "";
|
|
418
|
+
let index = 0;
|
|
419
|
+
while (index < input2.length) {
|
|
420
|
+
const codePoint = input2.codePointAt(index);
|
|
421
|
+
if (codePoint == null) break;
|
|
422
|
+
const character = String.fromCodePoint(codePoint);
|
|
423
|
+
index += character.length;
|
|
424
|
+
if (isUnsafeTerminalControlCodePoint(codePoint)) continue;
|
|
425
|
+
output += character;
|
|
426
|
+
}
|
|
427
|
+
return output;
|
|
428
|
+
}
|
|
429
|
+
function visibleLength(input2) {
|
|
430
|
+
let width = 0;
|
|
431
|
+
for (const unit of terminalTextUnits(input2)) width += unit.width;
|
|
432
|
+
return width;
|
|
433
|
+
}
|
|
434
|
+
function sliceVisible(input2, width) {
|
|
435
|
+
if (width <= 0) return "";
|
|
436
|
+
let output = "";
|
|
437
|
+
let visible = 0;
|
|
438
|
+
const units = terminalTextUnits(input2);
|
|
439
|
+
let index = 0;
|
|
440
|
+
while (index < units.length && visible < width) {
|
|
441
|
+
const unit = units[index];
|
|
442
|
+
if (unit.width > 0 && visible + unit.width > width) break;
|
|
443
|
+
output += unit.text;
|
|
444
|
+
visible += unit.width;
|
|
445
|
+
index += 1;
|
|
446
|
+
}
|
|
447
|
+
while (units[index]?.ansi === true) {
|
|
448
|
+
output += units[index].text;
|
|
449
|
+
index += 1;
|
|
450
|
+
}
|
|
451
|
+
return output;
|
|
452
|
+
}
|
|
453
|
+
function inputTextWidth(input2) {
|
|
454
|
+
let width = 0;
|
|
455
|
+
for (const grapheme of graphemes(input2)) {
|
|
456
|
+
width += terminalGraphemeWidth(grapheme.text);
|
|
457
|
+
}
|
|
458
|
+
return width;
|
|
459
|
+
}
|
|
460
|
+
function terminalGraphemeWidth(grapheme) {
|
|
461
|
+
let width = 0;
|
|
462
|
+
for (const character of grapheme) {
|
|
463
|
+
const codePoint = character.codePointAt(0);
|
|
464
|
+
if (codePoint !== void 0) width = Math.max(width, codePointWidth(codePoint));
|
|
465
|
+
}
|
|
466
|
+
const emojiPresentation = emojiPresentationPattern.test(grapheme) || keycapPattern.test(grapheme) || grapheme.includes("\uFE0F") && extendedPictographicPattern.test(grapheme);
|
|
467
|
+
return emojiPresentation ? Math.max(2, width) : width;
|
|
468
|
+
}
|
|
469
|
+
function terminalTextUnits(input2) {
|
|
470
|
+
const units = [];
|
|
471
|
+
let index = 0;
|
|
472
|
+
while (index < input2.length) {
|
|
473
|
+
const remaining = input2.slice(index);
|
|
474
|
+
const ansiMatch = remaining.match(ansiPrefixPattern);
|
|
475
|
+
if (ansiMatch !== null) {
|
|
476
|
+
units.push({ text: ansiMatch[0], width: 0, ansi: true });
|
|
477
|
+
index += ansiMatch[0].length;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const nextAnsi = remaining.search(ansiPattern);
|
|
481
|
+
const plain = remaining.slice(0, nextAnsi === -1 ? remaining.length : nextAnsi);
|
|
482
|
+
for (const grapheme of graphemes(plain)) {
|
|
483
|
+
units.push({ text: grapheme.text, width: terminalGraphemeWidth(grapheme.text), ansi: false });
|
|
484
|
+
}
|
|
485
|
+
index += plain.length;
|
|
486
|
+
}
|
|
487
|
+
return units;
|
|
488
|
+
}
|
|
489
|
+
function wrapVisibleLine(line, width) {
|
|
490
|
+
if (width <= 0) return [line];
|
|
491
|
+
if (line.length === 0) return [""];
|
|
492
|
+
const lines = [];
|
|
493
|
+
let remaining = line;
|
|
494
|
+
while (visibleLength(remaining) > width) {
|
|
495
|
+
const breakAt = findVisibleBreakPoint(remaining, width);
|
|
496
|
+
lines.push(remaining.slice(0, breakAt).trimEnd());
|
|
497
|
+
remaining = remaining.slice(breakAt).trimStart();
|
|
498
|
+
}
|
|
499
|
+
if (remaining.length > 0 || lines.length === 0) lines.push(remaining);
|
|
500
|
+
return lines;
|
|
501
|
+
}
|
|
502
|
+
function findVisibleBreakPoint(input2, width) {
|
|
503
|
+
const slice = sliceVisible(input2, width + 1);
|
|
504
|
+
const lastSpace = slice.lastIndexOf(" ");
|
|
505
|
+
if (lastSpace > 0) return lastSpace;
|
|
506
|
+
const hardSlice = sliceVisible(input2, width);
|
|
507
|
+
if (visibleLength(hardSlice) > 0) return hardSlice.length;
|
|
508
|
+
let offset = 0;
|
|
509
|
+
let foundVisibleUnit = false;
|
|
510
|
+
for (const unit of terminalTextUnits(input2)) {
|
|
511
|
+
if (foundVisibleUnit && unit.width > 0) return offset;
|
|
512
|
+
offset += unit.text.length;
|
|
513
|
+
if (unit.width > 0) foundVisibleUnit = true;
|
|
514
|
+
}
|
|
515
|
+
return offset;
|
|
516
|
+
}
|
|
517
|
+
function codePointWidth(codePoint) {
|
|
518
|
+
if (codePoint === 9) return 4;
|
|
519
|
+
if (codePoint < 32 || codePoint >= 127 && codePoint < 160) return 0;
|
|
520
|
+
if (isZeroWidthCodePoint(codePoint)) return 0;
|
|
521
|
+
return isWideCodePoint(codePoint) ? 2 : 1;
|
|
522
|
+
}
|
|
523
|
+
function isUnsafeTerminalControlCodePoint(codePoint) {
|
|
524
|
+
return codePoint >= 0 && codePoint <= 8 || codePoint >= 11 && codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
525
|
+
}
|
|
526
|
+
function isZeroWidthCodePoint(codePoint) {
|
|
527
|
+
return codePoint >= 768 && codePoint <= 879 || codePoint >= 1155 && codePoint <= 1161 || codePoint >= 1425 && codePoint <= 1469 || codePoint === 1471 || codePoint >= 1473 && codePoint <= 1474 || codePoint >= 1476 && codePoint <= 1477 || codePoint === 1479 || codePoint >= 1552 && codePoint <= 1562 || codePoint >= 1611 && codePoint <= 1631 || codePoint === 1648 || codePoint >= 1750 && codePoint <= 1756 || codePoint >= 1759 && codePoint <= 1764 || codePoint >= 1767 && codePoint <= 1768 || codePoint >= 1770 && codePoint <= 1773 || codePoint === 1809 || codePoint >= 1840 && codePoint <= 1866 || codePoint >= 1958 && codePoint <= 1968 || codePoint >= 2027 && codePoint <= 2035 || codePoint >= 2070 && codePoint <= 2073 || codePoint >= 2075 && codePoint <= 2083 || codePoint >= 2085 && codePoint <= 2087 || codePoint >= 2089 && codePoint <= 2093 || codePoint >= 2137 && codePoint <= 2139 || codePoint >= 2259 && codePoint <= 2306 || codePoint === 2362 || codePoint === 2364 || codePoint >= 2369 && codePoint <= 2376 || codePoint === 2381 || codePoint >= 2385 && codePoint <= 2391 || codePoint === 8205 || codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999;
|
|
528
|
+
}
|
|
529
|
+
function isWideCodePoint(codePoint) {
|
|
530
|
+
return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 42191 && codePoint !== 12351 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65135 || codePoint >= 65280 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127744 && codePoint <= 128591 || codePoint >= 129280 && codePoint <= 129535 || codePoint >= 131072 && codePoint <= 262141);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/cli/tui/line-editor.ts
|
|
534
|
+
var EMPTY_LINE = { text: "", cursor: 0 };
|
|
535
|
+
function lineOf(text) {
|
|
536
|
+
return { text, cursor: text.length };
|
|
537
|
+
}
|
|
538
|
+
function maskLine(state) {
|
|
539
|
+
return {
|
|
540
|
+
text: "\u2022".repeat(graphemes(state.text).length),
|
|
541
|
+
cursor: graphemes(state.text.slice(0, state.cursor)).length
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
function insert(state, value) {
|
|
545
|
+
if (value.length === 0) return state;
|
|
546
|
+
const text = state.text.slice(0, state.cursor) + value + state.text.slice(state.cursor);
|
|
547
|
+
const cursor = graphemeBoundaryAtOrAfter(text, state.cursor + value.length);
|
|
548
|
+
return { text, cursor };
|
|
549
|
+
}
|
|
550
|
+
function backspace(state) {
|
|
551
|
+
if (state.cursor === 0) return state;
|
|
552
|
+
const cursor = previousGraphemeBoundary(state.text, state.cursor);
|
|
553
|
+
const text = state.text.slice(0, cursor) + state.text.slice(state.cursor);
|
|
554
|
+
return { text, cursor: graphemeBoundaryAtOrAfter(text, cursor) };
|
|
555
|
+
}
|
|
556
|
+
function deleteForward(state) {
|
|
557
|
+
if (state.cursor >= state.text.length) return state;
|
|
558
|
+
const end = nextGraphemeBoundary(state.text, state.cursor);
|
|
559
|
+
const text = state.text.slice(0, state.cursor) + state.text.slice(end);
|
|
560
|
+
return { text, cursor: graphemeBoundaryAtOrAfter(text, state.cursor) };
|
|
561
|
+
}
|
|
562
|
+
function moveLeft(state) {
|
|
563
|
+
return state.cursor === 0 ? state : { text: state.text, cursor: previousGraphemeBoundary(state.text, state.cursor) };
|
|
564
|
+
}
|
|
565
|
+
function moveRight(state) {
|
|
566
|
+
return state.cursor >= state.text.length ? state : { text: state.text, cursor: nextGraphemeBoundary(state.text, state.cursor) };
|
|
567
|
+
}
|
|
568
|
+
function moveHome(state) {
|
|
569
|
+
const cursor = logicalLineStart(state.text, state.cursor);
|
|
570
|
+
return state.cursor === cursor ? state : { text: state.text, cursor };
|
|
571
|
+
}
|
|
572
|
+
function moveEnd(state) {
|
|
573
|
+
const cursor = logicalLineEnd(state.text, state.cursor);
|
|
574
|
+
return state.cursor === cursor ? state : { text: state.text, cursor };
|
|
575
|
+
}
|
|
576
|
+
function killToEnd(state) {
|
|
577
|
+
const lineEnd = logicalLineEnd(state.text, state.cursor);
|
|
578
|
+
if (state.cursor >= lineEnd) return state;
|
|
579
|
+
return {
|
|
580
|
+
text: state.text.slice(0, state.cursor) + state.text.slice(lineEnd),
|
|
581
|
+
cursor: state.cursor
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
function killToStart(state) {
|
|
585
|
+
const lineStart = logicalLineStart(state.text, state.cursor);
|
|
586
|
+
if (state.cursor <= lineStart) return state;
|
|
587
|
+
return {
|
|
588
|
+
text: state.text.slice(0, lineStart) + state.text.slice(state.cursor),
|
|
589
|
+
cursor: lineStart
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
function deleteWord(state) {
|
|
593
|
+
if (state.cursor === 0) return state;
|
|
594
|
+
const lineStart = logicalLineStart(state.text, state.cursor);
|
|
595
|
+
let start = state.cursor;
|
|
596
|
+
while (start > lineStart) {
|
|
597
|
+
const previous = previousGraphemeBoundary(state.text, start);
|
|
598
|
+
if (!isWhitespace(state.text.slice(previous, start))) break;
|
|
599
|
+
start = previous;
|
|
600
|
+
}
|
|
601
|
+
while (start > lineStart) {
|
|
602
|
+
const previous = previousGraphemeBoundary(state.text, start);
|
|
603
|
+
if (isWhitespace(state.text.slice(previous, start))) break;
|
|
604
|
+
start = previous;
|
|
605
|
+
}
|
|
606
|
+
const text = state.text.slice(0, start) + state.text.slice(state.cursor);
|
|
607
|
+
return { text, cursor: graphemeBoundaryAtOrAfter(text, start) };
|
|
608
|
+
}
|
|
609
|
+
function logicalLineStart(text, cursor) {
|
|
610
|
+
if (cursor === 0) return 0;
|
|
611
|
+
return text.lastIndexOf("\n", cursor - 1) + 1;
|
|
612
|
+
}
|
|
613
|
+
function logicalLineEnd(text, cursor) {
|
|
614
|
+
const newline = text.indexOf("\n", cursor);
|
|
615
|
+
return newline === -1 ? text.length : newline;
|
|
616
|
+
}
|
|
617
|
+
function applyLineEditorKey(state, key, options) {
|
|
618
|
+
const multiline = options?.multiline ?? false;
|
|
619
|
+
switch (key.type) {
|
|
620
|
+
case "text":
|
|
621
|
+
return insert(state, multiline ? key.value : key.value.replaceAll("\n", " "));
|
|
622
|
+
case "newline":
|
|
623
|
+
return multiline ? insert(state, "\n") : void 0;
|
|
624
|
+
case "backspace":
|
|
625
|
+
return backspace(state);
|
|
626
|
+
case "delete":
|
|
627
|
+
return deleteForward(state);
|
|
628
|
+
case "left":
|
|
629
|
+
return moveLeft(state);
|
|
630
|
+
case "right":
|
|
631
|
+
return moveRight(state);
|
|
632
|
+
case "home":
|
|
633
|
+
case "ctrl-a":
|
|
634
|
+
return moveHome(state);
|
|
635
|
+
case "end":
|
|
636
|
+
case "ctrl-e":
|
|
637
|
+
return moveEnd(state);
|
|
638
|
+
case "ctrl-k":
|
|
639
|
+
return killToEnd(state);
|
|
640
|
+
case "ctrl-u":
|
|
641
|
+
return killToStart(state);
|
|
642
|
+
case "ctrl-w":
|
|
643
|
+
return deleteWord(state);
|
|
644
|
+
default:
|
|
645
|
+
return void 0;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function isWhitespace(text) {
|
|
649
|
+
return /^\s+$/u.test(text);
|
|
650
|
+
}
|
|
651
|
+
function visibleLine(state, budget, ellipsis = "\u2026") {
|
|
652
|
+
const width = Math.max(1, budget);
|
|
653
|
+
const { text, cursor } = state;
|
|
654
|
+
const segments = graphemes(text);
|
|
655
|
+
const caretIndex = segments.findIndex((segment) => segment.start === cursor);
|
|
656
|
+
const underIndex = caretIndex === -1 ? segments.length : caretIndex;
|
|
657
|
+
const under = segments[underIndex]?.text ?? "";
|
|
658
|
+
if (inputTextWidth(text) <= width) {
|
|
659
|
+
return {
|
|
660
|
+
before: text.slice(0, cursor),
|
|
661
|
+
under,
|
|
662
|
+
after: text.slice(cursor + under.length)
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
let start = underIndex;
|
|
666
|
+
let end = underIndex + (under.length > 0 ? 1 : 0);
|
|
667
|
+
const windowWidth = (candidateStart, candidateEnd) => {
|
|
668
|
+
const startOffset2 = segments[candidateStart]?.start ?? text.length;
|
|
669
|
+
const endOffset2 = segments[candidateEnd]?.start ?? text.length;
|
|
670
|
+
return inputTextWidth(text.slice(startOffset2, endOffset2)) + (candidateStart > 0 ? inputTextWidth(ellipsis) : 0) + (candidateEnd < segments.length ? inputTextWidth(ellipsis) : 0);
|
|
671
|
+
};
|
|
672
|
+
let preferBefore = true;
|
|
673
|
+
while (true) {
|
|
674
|
+
const beforeFits = start > 0 && windowWidth(start - 1, end) <= width;
|
|
675
|
+
const afterFits = end < segments.length && windowWidth(start, end + 1) <= width;
|
|
676
|
+
if (!beforeFits && !afterFits) break;
|
|
677
|
+
if (preferBefore && beforeFits || !afterFits) start -= 1;
|
|
678
|
+
else end += 1;
|
|
679
|
+
preferBefore = !preferBefore;
|
|
680
|
+
}
|
|
681
|
+
const startOffset = segments[start]?.start ?? text.length;
|
|
682
|
+
const endOffset = segments[end]?.start ?? text.length;
|
|
683
|
+
return {
|
|
684
|
+
before: `${start > 0 ? ellipsis : ""}${text.slice(startOffset, cursor)}`,
|
|
685
|
+
under,
|
|
686
|
+
after: `${text.slice(cursor + under.length, endOffset)}${end < segments.length ? ellipsis : ""}`
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
var PromptHistory = class {
|
|
690
|
+
#entries = [];
|
|
691
|
+
#index = 0;
|
|
692
|
+
#draft = "";
|
|
693
|
+
add(entry) {
|
|
694
|
+
const value = entry.trim();
|
|
695
|
+
if (value.length === 0) return;
|
|
696
|
+
if (this.#entries.at(-1) === entry) {
|
|
697
|
+
this.#resetCursor();
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
this.#entries.push(entry);
|
|
701
|
+
this.#resetCursor();
|
|
702
|
+
}
|
|
703
|
+
begin(draft) {
|
|
704
|
+
this.#index = this.#entries.length;
|
|
705
|
+
this.#draft = draft;
|
|
706
|
+
}
|
|
707
|
+
previous(currentDraft) {
|
|
708
|
+
if (this.#entries.length === 0) return void 0;
|
|
709
|
+
if (this.#index === this.#entries.length) this.#draft = currentDraft;
|
|
710
|
+
if (this.#index === 0) return void 0;
|
|
711
|
+
this.#index -= 1;
|
|
712
|
+
return this.#entries[this.#index];
|
|
713
|
+
}
|
|
714
|
+
next() {
|
|
715
|
+
if (this.#index >= this.#entries.length) return void 0;
|
|
716
|
+
this.#index += 1;
|
|
717
|
+
return this.#index === this.#entries.length ? this.#draft : this.#entries[this.#index];
|
|
718
|
+
}
|
|
719
|
+
#resetCursor() {
|
|
720
|
+
this.#index = this.#entries.length;
|
|
721
|
+
this.#draft = "";
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
export {
|
|
726
|
+
LiveRegion,
|
|
727
|
+
attachKeyStream,
|
|
728
|
+
createTheme,
|
|
729
|
+
detectUnicode,
|
|
730
|
+
stripTerminalControls,
|
|
731
|
+
visibleLength,
|
|
732
|
+
sliceVisible,
|
|
733
|
+
inputTextWidth,
|
|
734
|
+
wrapVisibleLine,
|
|
735
|
+
EMPTY_LINE,
|
|
736
|
+
lineOf,
|
|
737
|
+
maskLine,
|
|
738
|
+
applyLineEditorKey,
|
|
739
|
+
visibleLine,
|
|
740
|
+
PromptHistory
|
|
741
|
+
};
|
|
742
|
+
//# sourceMappingURL=chunk-HWD2Z2CH.js.map
|