plasalid 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +213 -0
- package/README.md +176 -0
- package/dist/accounts/taxonomy.d.ts +31 -0
- package/dist/accounts/taxonomy.js +189 -0
- package/dist/ai/agent.d.ts +43 -0
- package/dist/ai/agent.js +155 -0
- package/dist/ai/context.d.ts +4 -0
- package/dist/ai/context.js +33 -0
- package/dist/ai/memory.d.ts +14 -0
- package/dist/ai/memory.js +12 -0
- package/dist/ai/provider.d.ts +67 -0
- package/dist/ai/provider.js +5 -0
- package/dist/ai/providers/anthropic.d.ts +5 -0
- package/dist/ai/providers/anthropic.js +49 -0
- package/dist/ai/providers/index.d.ts +2 -0
- package/dist/ai/providers/index.js +12 -0
- package/dist/ai/providers/openai-compat.d.ts +5 -0
- package/dist/ai/providers/openai-compat.js +147 -0
- package/dist/ai/providers/openai.d.ts +5 -0
- package/dist/ai/providers/openai.js +147 -0
- package/dist/ai/redactor.d.ts +2 -0
- package/dist/ai/redactor.js +91 -0
- package/dist/ai/sanitize.d.ts +14 -0
- package/dist/ai/sanitize.js +25 -0
- package/dist/ai/system-prompt.d.ts +13 -0
- package/dist/ai/system-prompt.js +174 -0
- package/dist/ai/thai-taxonomy-hint.d.ts +8 -0
- package/dist/ai/thai-taxonomy-hint.js +22 -0
- package/dist/ai/thinking-phrases.d.ts +7 -0
- package/dist/ai/thinking-phrases.js +15 -0
- package/dist/ai/thinking.d.ts +7 -0
- package/dist/ai/thinking.js +15 -0
- package/dist/ai/tools/common.d.ts +2 -0
- package/dist/ai/tools/common.js +83 -0
- package/dist/ai/tools/index.d.ts +8 -0
- package/dist/ai/tools/index.js +34 -0
- package/dist/ai/tools/ingest.d.ts +2 -0
- package/dist/ai/tools/ingest.js +202 -0
- package/dist/ai/tools/read.d.ts +2 -0
- package/dist/ai/tools/read.js +123 -0
- package/dist/ai/tools/reconcile.d.ts +2 -0
- package/dist/ai/tools/reconcile.js +227 -0
- package/dist/ai/tools/scan.d.ts +2 -0
- package/dist/ai/tools/scan.js +24 -0
- package/dist/ai/tools/types.d.ts +26 -0
- package/dist/ai/tools/types.js +1 -0
- package/dist/ai/tools.d.ts +18 -0
- package/dist/ai/tools.js +402 -0
- package/dist/cli/chat.d.ts +1 -0
- package/dist/cli/chat.js +28 -0
- package/dist/cli/commands/accounts.d.ts +1 -0
- package/dist/cli/commands/accounts.js +86 -0
- package/dist/cli/commands/data.d.ts +1 -0
- package/dist/cli/commands/data.js +28 -0
- package/dist/cli/commands/reconcile.d.ts +2 -0
- package/dist/cli/commands/reconcile.js +15 -0
- package/dist/cli/commands/revert.d.ts +1 -0
- package/dist/cli/commands/revert.js +68 -0
- package/dist/cli/commands/scan.d.ts +4 -0
- package/dist/cli/commands/scan.js +45 -0
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +22 -0
- package/dist/cli/commands/transactions.d.ts +8 -0
- package/dist/cli/commands/transactions.js +92 -0
- package/dist/cli/commands/undo.d.ts +1 -0
- package/dist/cli/commands/undo.js +38 -0
- package/dist/cli/commands.d.ts +14 -0
- package/dist/cli/commands.js +196 -0
- package/dist/cli/format.d.ts +8 -0
- package/dist/cli/format.js +109 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +126 -0
- package/dist/cli/ink/ChatApp.d.ts +8 -0
- package/dist/cli/ink/ChatApp.js +94 -0
- package/dist/cli/ink/PromptFrame.d.ts +10 -0
- package/dist/cli/ink/PromptFrame.js +11 -0
- package/dist/cli/ink/TextInput.d.ts +13 -0
- package/dist/cli/ink/TextInput.js +24 -0
- package/dist/cli/ink/hooks/useAgent.d.ts +27 -0
- package/dist/cli/ink/hooks/useAgent.js +65 -0
- package/dist/cli/ink/hooks/useCtrlCExit.d.ts +16 -0
- package/dist/cli/ink/hooks/useCtrlCExit.js +43 -0
- package/dist/cli/ink/hooks/useFooterText.d.ts +2 -0
- package/dist/cli/ink/hooks/useFooterText.js +43 -0
- package/dist/cli/ink/hooks/useTextInput.d.ts +32 -0
- package/dist/cli/ink/hooks/useTextInput.js +356 -0
- package/dist/cli/ink/messages/AssistantMessage.d.ts +3 -0
- package/dist/cli/ink/messages/AssistantMessage.js +6 -0
- package/dist/cli/ink/messages/ErrorMessage.d.ts +4 -0
- package/dist/cli/ink/messages/ErrorMessage.js +6 -0
- package/dist/cli/ink/messages/InterruptedMessage.d.ts +1 -0
- package/dist/cli/ink/messages/InterruptedMessage.js +6 -0
- package/dist/cli/ink/messages/ThinkingLine.d.ts +12 -0
- package/dist/cli/ink/messages/ThinkingLine.js +23 -0
- package/dist/cli/ink/messages/UserMessage.d.ts +4 -0
- package/dist/cli/ink/messages/UserMessage.js +15 -0
- package/dist/cli/ink/mount.d.ts +6 -0
- package/dist/cli/ink/mount.js +12 -0
- package/dist/cli/logo.d.ts +1 -0
- package/dist/cli/logo.js +20 -0
- package/dist/cli/setup.d.ts +2 -0
- package/dist/cli/setup.js +210 -0
- package/dist/cli/ux.d.ts +38 -0
- package/dist/cli/ux.js +104 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.js +66 -0
- package/dist/currency.d.ts +6 -0
- package/dist/currency.js +19 -0
- package/dist/db/connection.d.ts +5 -0
- package/dist/db/connection.js +45 -0
- package/dist/db/encryption.d.ts +11 -0
- package/dist/db/encryption.js +45 -0
- package/dist/db/helpers.d.ts +16 -0
- package/dist/db/helpers.js +45 -0
- package/dist/db/queries/account_balance.d.ts +61 -0
- package/dist/db/queries/account_balance.js +146 -0
- package/dist/db/queries/journal.d.ts +95 -0
- package/dist/db/queries/journal.js +204 -0
- package/dist/db/queries/search.d.ts +7 -0
- package/dist/db/queries/search.js +19 -0
- package/dist/db/schema.d.ts +2 -0
- package/dist/db/schema.js +95 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/parser/pdf.d.ts +14 -0
- package/dist/parser/pdf.js +40 -0
- package/dist/parser/pipeline.d.ts +44 -0
- package/dist/parser/pipeline.js +160 -0
- package/dist/parser/prompts.d.ts +8 -0
- package/dist/parser/prompts.js +20 -0
- package/dist/parser/walker.d.ts +8 -0
- package/dist/parser/walker.js +42 -0
- package/dist/reconciler/pipeline.d.ts +17 -0
- package/dist/reconciler/pipeline.js +45 -0
- package/dist/reconciler/prompts.d.ts +12 -0
- package/dist/reconciler/prompts.js +22 -0
- package/dist/scanner/password-store.d.ts +34 -0
- package/dist/scanner/password-store.js +83 -0
- package/dist/scanner/pdf-unlock.d.ts +17 -0
- package/dist/scanner/pdf-unlock.js +48 -0
- package/dist/scanner/pdf.d.ts +17 -0
- package/dist/scanner/pdf.js +36 -0
- package/dist/scanner/pipeline.d.ts +32 -0
- package/dist/scanner/pipeline.js +137 -0
- package/dist/scanner/prompts.d.ts +8 -0
- package/dist/scanner/prompts.js +20 -0
- package/dist/scanner/state-machine.d.ts +60 -0
- package/dist/scanner/state-machine.js +64 -0
- package/dist/scanner/unlock.d.ts +24 -0
- package/dist/scanner/unlock.js +122 -0
- package/dist/scanner/walker.d.ts +8 -0
- package/dist/scanner/walker.js +42 -0
- package/package.json +65 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { useEffect, useRef, useState, useCallback } from "react";
|
|
2
|
+
import { useStdin } from "ink";
|
|
3
|
+
const EMPTY_BUFFER = { lines: [""], row: 0, col: 0 };
|
|
4
|
+
/** Key bytes we care about. */
|
|
5
|
+
const CTRL_A = 1;
|
|
6
|
+
const CTRL_C = 3;
|
|
7
|
+
const CTRL_D = 4;
|
|
8
|
+
const CTRL_E = 5;
|
|
9
|
+
const CTRL_K = 11;
|
|
10
|
+
const CTRL_U = 21;
|
|
11
|
+
const CTRL_W = 23;
|
|
12
|
+
const ENTER = 13;
|
|
13
|
+
const BACKSPACE = 127;
|
|
14
|
+
const BACKSPACE_ALT = 8;
|
|
15
|
+
const ESC = 27;
|
|
16
|
+
function cloneBuf(b) {
|
|
17
|
+
return { lines: [...b.lines], row: b.row, col: b.col };
|
|
18
|
+
}
|
|
19
|
+
function wordLeft(line, col) {
|
|
20
|
+
let p = col;
|
|
21
|
+
while (p > 0 && line[p - 1] === " ")
|
|
22
|
+
p--;
|
|
23
|
+
while (p > 0 && line[p - 1] !== " ")
|
|
24
|
+
p--;
|
|
25
|
+
return p;
|
|
26
|
+
}
|
|
27
|
+
function wordRight(line, col) {
|
|
28
|
+
let p = col;
|
|
29
|
+
while (p < line.length && line[p] !== " ")
|
|
30
|
+
p++;
|
|
31
|
+
while (p < line.length && line[p] === " ")
|
|
32
|
+
p++;
|
|
33
|
+
return p;
|
|
34
|
+
}
|
|
35
|
+
function insertText(buf, text) {
|
|
36
|
+
const pieces = text.split("\n");
|
|
37
|
+
const lines = [...buf.lines];
|
|
38
|
+
const cur = lines[buf.row];
|
|
39
|
+
const before = cur.slice(0, buf.col);
|
|
40
|
+
const after = cur.slice(buf.col);
|
|
41
|
+
if (pieces.length === 1) {
|
|
42
|
+
lines[buf.row] = before + pieces[0] + after;
|
|
43
|
+
return { lines, row: buf.row, col: buf.col + pieces[0].length };
|
|
44
|
+
}
|
|
45
|
+
const first = pieces[0];
|
|
46
|
+
const last = pieces[pieces.length - 1];
|
|
47
|
+
const middle = pieces.slice(1, -1);
|
|
48
|
+
const newLines = [
|
|
49
|
+
...lines.slice(0, buf.row),
|
|
50
|
+
before + first,
|
|
51
|
+
...middle,
|
|
52
|
+
last + after,
|
|
53
|
+
...lines.slice(buf.row + 1),
|
|
54
|
+
];
|
|
55
|
+
return {
|
|
56
|
+
lines: newLines,
|
|
57
|
+
row: buf.row + pieces.length - 1,
|
|
58
|
+
col: last.length,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function backspace(buf) {
|
|
62
|
+
if (buf.col > 0) {
|
|
63
|
+
const lines = [...buf.lines];
|
|
64
|
+
const cur = lines[buf.row];
|
|
65
|
+
lines[buf.row] = cur.slice(0, buf.col - 1) + cur.slice(buf.col);
|
|
66
|
+
return { lines, row: buf.row, col: buf.col - 1 };
|
|
67
|
+
}
|
|
68
|
+
if (buf.row > 0) {
|
|
69
|
+
const lines = [...buf.lines];
|
|
70
|
+
const prev = lines[buf.row - 1];
|
|
71
|
+
const cur = lines[buf.row];
|
|
72
|
+
lines[buf.row - 1] = prev + cur;
|
|
73
|
+
lines.splice(buf.row, 1);
|
|
74
|
+
return { lines, row: buf.row - 1, col: prev.length };
|
|
75
|
+
}
|
|
76
|
+
return buf;
|
|
77
|
+
}
|
|
78
|
+
function deleteWordLeft(buf) {
|
|
79
|
+
const cur = buf.lines[buf.row];
|
|
80
|
+
if (buf.col === 0)
|
|
81
|
+
return backspace(buf);
|
|
82
|
+
const target = wordLeft(cur, buf.col);
|
|
83
|
+
const lines = [...buf.lines];
|
|
84
|
+
lines[buf.row] = cur.slice(0, target) + cur.slice(buf.col);
|
|
85
|
+
return { lines, row: buf.row, col: target };
|
|
86
|
+
}
|
|
87
|
+
function moveLeft(buf) {
|
|
88
|
+
if (buf.col > 0)
|
|
89
|
+
return { ...buf, col: buf.col - 1 };
|
|
90
|
+
if (buf.row > 0) {
|
|
91
|
+
const prev = buf.lines[buf.row - 1];
|
|
92
|
+
return { ...buf, row: buf.row - 1, col: prev.length };
|
|
93
|
+
}
|
|
94
|
+
return buf;
|
|
95
|
+
}
|
|
96
|
+
function moveRight(buf) {
|
|
97
|
+
const cur = buf.lines[buf.row];
|
|
98
|
+
if (buf.col < cur.length)
|
|
99
|
+
return { ...buf, col: buf.col + 1 };
|
|
100
|
+
if (buf.row < buf.lines.length - 1)
|
|
101
|
+
return { ...buf, row: buf.row + 1, col: 0 };
|
|
102
|
+
return buf;
|
|
103
|
+
}
|
|
104
|
+
function moveUp(buf) {
|
|
105
|
+
if (buf.row === 0)
|
|
106
|
+
return buf;
|
|
107
|
+
const target = buf.lines[buf.row - 1];
|
|
108
|
+
return { ...buf, row: buf.row - 1, col: Math.min(buf.col, target.length) };
|
|
109
|
+
}
|
|
110
|
+
function moveDown(buf) {
|
|
111
|
+
if (buf.row === buf.lines.length - 1)
|
|
112
|
+
return buf;
|
|
113
|
+
const target = buf.lines[buf.row + 1];
|
|
114
|
+
return { ...buf, row: buf.row + 1, col: Math.min(buf.col, target.length) };
|
|
115
|
+
}
|
|
116
|
+
function moveWordLeft(buf) {
|
|
117
|
+
const cur = buf.lines[buf.row];
|
|
118
|
+
if (buf.col === 0)
|
|
119
|
+
return moveLeft(buf);
|
|
120
|
+
return { ...buf, col: wordLeft(cur, buf.col) };
|
|
121
|
+
}
|
|
122
|
+
function moveWordRight(buf) {
|
|
123
|
+
const cur = buf.lines[buf.row];
|
|
124
|
+
if (buf.col === cur.length)
|
|
125
|
+
return moveRight(buf);
|
|
126
|
+
return { ...buf, col: wordRight(cur, buf.col) };
|
|
127
|
+
}
|
|
128
|
+
function toString(buf) {
|
|
129
|
+
return buf.lines.join("\n");
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Raw-stdin driven keystroke state machine that owns a multiline buffer and
|
|
133
|
+
* exposes its current state plus reset/insert helpers. Purely stateful — Ink
|
|
134
|
+
* re-renders whenever the buffer changes via setBuffer.
|
|
135
|
+
*
|
|
136
|
+
* Handles: Enter/submit, Backspace, Ctrl+A/E/K/U/W, arrow keys, Option+←/→,
|
|
137
|
+
* Option+B/F, Option+Backspace, Kitty Cmd+Backspace, Shift+Enter (newline),
|
|
138
|
+
* bracketed paste.
|
|
139
|
+
*/
|
|
140
|
+
export function useTextInput(opts) {
|
|
141
|
+
const [buffer, setBuffer] = useState(EMPTY_BUFFER);
|
|
142
|
+
const bufferRef = useRef(buffer);
|
|
143
|
+
bufferRef.current = buffer;
|
|
144
|
+
const optsRef = useRef(opts);
|
|
145
|
+
optsRef.current = opts;
|
|
146
|
+
const { stdin, setRawMode, isRawModeSupported } = useStdin();
|
|
147
|
+
const apply = useCallback((fn) => {
|
|
148
|
+
setBuffer(prev => {
|
|
149
|
+
const next = fn(prev);
|
|
150
|
+
optsRef.current.onChange?.(next);
|
|
151
|
+
return next;
|
|
152
|
+
});
|
|
153
|
+
}, []);
|
|
154
|
+
const reset = useCallback(() => {
|
|
155
|
+
setBuffer(EMPTY_BUFFER);
|
|
156
|
+
optsRef.current.onChange?.(EMPTY_BUFFER);
|
|
157
|
+
}, []);
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
if (!isRawModeSupported || !stdin)
|
|
160
|
+
return;
|
|
161
|
+
setRawMode(true);
|
|
162
|
+
// Enable bracketed paste
|
|
163
|
+
process.stdout.write("\x1b[?2004h");
|
|
164
|
+
let pasteBuffer = null;
|
|
165
|
+
const onData = (data) => {
|
|
166
|
+
const chunk = data.toString("utf8");
|
|
167
|
+
// If we're inside a bracketed paste, accumulate until we see the end marker
|
|
168
|
+
if (pasteBuffer !== null) {
|
|
169
|
+
const endIdx = chunk.indexOf("\x1b[201~");
|
|
170
|
+
if (endIdx === -1) {
|
|
171
|
+
pasteBuffer += chunk;
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
pasteBuffer += chunk.slice(0, endIdx);
|
|
175
|
+
const pasted = pasteBuffer;
|
|
176
|
+
pasteBuffer = null;
|
|
177
|
+
apply(b => insertText(b, pasted));
|
|
178
|
+
// Process any bytes after the end marker
|
|
179
|
+
const after = chunk.slice(endIdx + "\x1b[201~".length);
|
|
180
|
+
if (after.length > 0)
|
|
181
|
+
handleChunk(after);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// Detect start-of-paste marker anywhere in the chunk
|
|
185
|
+
const startIdx = chunk.indexOf("\x1b[200~");
|
|
186
|
+
if (startIdx !== -1) {
|
|
187
|
+
const before = chunk.slice(0, startIdx);
|
|
188
|
+
if (before.length > 0)
|
|
189
|
+
handleChunk(before);
|
|
190
|
+
const rest = chunk.slice(startIdx + "\x1b[200~".length);
|
|
191
|
+
const endIdx = rest.indexOf("\x1b[201~");
|
|
192
|
+
if (endIdx === -1) {
|
|
193
|
+
pasteBuffer = rest;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const pasted = rest.slice(0, endIdx);
|
|
197
|
+
apply(b => insertText(b, pasted));
|
|
198
|
+
const after = rest.slice(endIdx + "\x1b[201~".length);
|
|
199
|
+
if (after.length > 0)
|
|
200
|
+
handleChunk(after);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
handleChunk(chunk);
|
|
204
|
+
};
|
|
205
|
+
const handleChunk = (chunk) => {
|
|
206
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
207
|
+
const code = chunk.charCodeAt(i);
|
|
208
|
+
const raw = chunk[i];
|
|
209
|
+
// Let custom handlers intercept first
|
|
210
|
+
if (optsRef.current.onKey?.({ code, raw }) === true)
|
|
211
|
+
continue;
|
|
212
|
+
if (code === CTRL_C) {
|
|
213
|
+
optsRef.current.onCtrlC(bufferRef.current.lines.length === 1 && bufferRef.current.lines[0] === "");
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (code === CTRL_D) {
|
|
217
|
+
// Treat as exit request when buffer empty; otherwise ignore
|
|
218
|
+
const b = bufferRef.current;
|
|
219
|
+
if (b.lines.length === 1 && b.lines[0] === "") {
|
|
220
|
+
optsRef.current.onCtrlC(true);
|
|
221
|
+
}
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (code === CTRL_A) {
|
|
225
|
+
apply(b => ({ ...b, col: 0 }));
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (code === CTRL_E) {
|
|
229
|
+
apply(b => ({ ...b, col: b.lines[b.row].length }));
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (code === CTRL_K) {
|
|
233
|
+
apply(b => {
|
|
234
|
+
const lines = [...b.lines];
|
|
235
|
+
lines[b.row] = lines[b.row].slice(0, b.col);
|
|
236
|
+
return { lines, row: b.row, col: b.col };
|
|
237
|
+
});
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (code === CTRL_U) {
|
|
241
|
+
apply(b => {
|
|
242
|
+
const lines = [...b.lines];
|
|
243
|
+
lines[b.row] = lines[b.row].slice(b.col);
|
|
244
|
+
return { lines, row: b.row, col: 0 };
|
|
245
|
+
});
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (code === CTRL_W) {
|
|
249
|
+
apply(deleteWordLeft);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (code === ENTER) {
|
|
253
|
+
optsRef.current.onSubmit(toString(bufferRef.current));
|
|
254
|
+
setBuffer(EMPTY_BUFFER);
|
|
255
|
+
optsRef.current.onChange?.(EMPTY_BUFFER);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (code === BACKSPACE || code === BACKSPACE_ALT) {
|
|
259
|
+
apply(backspace);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (code === ESC) {
|
|
263
|
+
// Option+Backspace (ESC + DEL)
|
|
264
|
+
if (i + 1 < chunk.length && chunk.charCodeAt(i + 1) === 127) {
|
|
265
|
+
i++;
|
|
266
|
+
apply(deleteWordLeft);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
// Option+b / Option+f
|
|
270
|
+
if (i + 1 < chunk.length && chunk[i + 1] === "b") {
|
|
271
|
+
i++;
|
|
272
|
+
apply(moveWordLeft);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (i + 1 < chunk.length && chunk[i + 1] === "f") {
|
|
276
|
+
i++;
|
|
277
|
+
apply(moveWordRight);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
// CSI sequence: ESC [ ... final
|
|
281
|
+
if (i + 1 < chunk.length && chunk[i + 1] === "[") {
|
|
282
|
+
i += 2;
|
|
283
|
+
let seq = "";
|
|
284
|
+
while (i < chunk.length && chunk.charCodeAt(i) < 64) {
|
|
285
|
+
seq += chunk[i];
|
|
286
|
+
i++;
|
|
287
|
+
}
|
|
288
|
+
if (i < chunk.length) {
|
|
289
|
+
const final = chunk[i];
|
|
290
|
+
const isWordMod = seq === "1;3" || seq === "1;5" || seq === "1;9";
|
|
291
|
+
if (final === "D") {
|
|
292
|
+
apply(isWordMod ? moveWordLeft : moveLeft);
|
|
293
|
+
}
|
|
294
|
+
else if (final === "C") {
|
|
295
|
+
apply(isWordMod ? moveWordRight : moveRight);
|
|
296
|
+
}
|
|
297
|
+
else if (final === "A") {
|
|
298
|
+
apply(moveUp);
|
|
299
|
+
}
|
|
300
|
+
else if (final === "B") {
|
|
301
|
+
apply(moveDown);
|
|
302
|
+
}
|
|
303
|
+
else if (final === "H") {
|
|
304
|
+
apply(b => ({ ...b, col: 0 }));
|
|
305
|
+
}
|
|
306
|
+
else if (final === "F") {
|
|
307
|
+
apply(b => ({ ...b, col: b.lines[b.row].length }));
|
|
308
|
+
}
|
|
309
|
+
else if (final === "u") {
|
|
310
|
+
// Kitty keyboard protocol: ESC [ codepoint ; modifier u
|
|
311
|
+
const parts = seq.split(";");
|
|
312
|
+
const codepoint = parseInt(parts[0], 10);
|
|
313
|
+
const mod = parts.length > 1 ? parseInt(parts[1], 10) : 1;
|
|
314
|
+
const hasShift = ((mod - 1) & 1) !== 0;
|
|
315
|
+
const hasCtrl = ((mod - 1) & 4) !== 0;
|
|
316
|
+
const hasCmd = ((mod - 1) & 8) !== 0;
|
|
317
|
+
if (codepoint === 13 && hasShift) {
|
|
318
|
+
// Shift+Enter → insert newline
|
|
319
|
+
apply(b => insertText(b, "\n"));
|
|
320
|
+
}
|
|
321
|
+
else if (codepoint === 127 && (hasCmd || hasCtrl)) {
|
|
322
|
+
// Cmd/Ctrl+Backspace → delete to line start
|
|
323
|
+
apply(b => {
|
|
324
|
+
const lines = [...b.lines];
|
|
325
|
+
lines[b.row] = lines[b.row].slice(b.col);
|
|
326
|
+
return { lines, row: b.row, col: 0 };
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
// Lone ESC / unknown — ignore
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
// Printable (and tab)
|
|
337
|
+
if (code >= 32 || code === 9) {
|
|
338
|
+
apply(b => insertText(b, raw));
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
stdin.on("data", onData);
|
|
344
|
+
return () => {
|
|
345
|
+
stdin.removeListener("data", onData);
|
|
346
|
+
process.stdout.write("\x1b[?2004l");
|
|
347
|
+
setRawMode(false);
|
|
348
|
+
};
|
|
349
|
+
}, [stdin, setRawMode, isRawModeSupported, apply]);
|
|
350
|
+
return {
|
|
351
|
+
buffer,
|
|
352
|
+
reset,
|
|
353
|
+
insert: (text) => apply(b => insertText(b, text)),
|
|
354
|
+
isEmpty: buffer.lines.length === 1 && buffer.lines[0] === "",
|
|
355
|
+
};
|
|
356
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { formatResponse } from "../../format.js";
|
|
4
|
+
export function AssistantMessage({ text }) {
|
|
5
|
+
return (_jsx(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: _jsx(Text, { children: formatResponse(text) }) }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { formatError } from "../../format.js";
|
|
4
|
+
export function ErrorMessage({ error, context }) {
|
|
5
|
+
return (_jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { children: formatError(error, context) }) }));
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function InterruptedMessage(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
export function InterruptedMessage() {
|
|
5
|
+
return (_jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { children: chalk.dim("(interrupted)") }) }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ThinkingState {
|
|
2
|
+
phrase: string;
|
|
3
|
+
progress?: {
|
|
4
|
+
toolName?: string;
|
|
5
|
+
toolCount: number;
|
|
6
|
+
elapsedMs: number;
|
|
7
|
+
phase: "tool" | "responding";
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export declare function ThinkingLine({ state }: {
|
|
11
|
+
state: ThinkingState;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import Spinner from "ink-spinner";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { TOOL_LABELS } from "../../../ai/tools/index.js";
|
|
6
|
+
import { formatDuration } from "../../format.js";
|
|
7
|
+
export function ThinkingLine({ state }) {
|
|
8
|
+
const text = buildText(state);
|
|
9
|
+
return (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { children: [" ", text] })] }));
|
|
10
|
+
}
|
|
11
|
+
function buildText(state) {
|
|
12
|
+
const { phrase, progress } = state;
|
|
13
|
+
if (!progress)
|
|
14
|
+
return phrase;
|
|
15
|
+
const { phase, toolName, toolCount, elapsedMs } = progress;
|
|
16
|
+
const label = phase === "tool" && toolName
|
|
17
|
+
? (TOOL_LABELS[toolName] || toolName)
|
|
18
|
+
: "Composing response";
|
|
19
|
+
const suffix = toolCount > 0
|
|
20
|
+
? chalk.dim(` (${toolCount} ${toolCount === 1 ? "tool" : "tools"}, ${formatDuration(elapsedMs)})`)
|
|
21
|
+
: "";
|
|
22
|
+
return `${label}...${suffix}`;
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Text, useStdout } from "ink";
|
|
3
|
+
/** Gray-background, full-width echo of a submitted user message. */
|
|
4
|
+
export function UserMessage({ text }) {
|
|
5
|
+
const { stdout } = useStdout();
|
|
6
|
+
const cols = stdout?.columns || 80;
|
|
7
|
+
const line = `❯ ${text}`;
|
|
8
|
+
const pad = Math.max(0, cols - visibleLength(line));
|
|
9
|
+
return _jsx(Text, { backgroundColor: "gray", color: "white", children: line + " ".repeat(pad) });
|
|
10
|
+
}
|
|
11
|
+
function visibleLength(s) {
|
|
12
|
+
// Rough approximation — ignores combining characters and wide glyphs.
|
|
13
|
+
// Ink pads within its own width calc so this is only for right-edge filler.
|
|
14
|
+
return [...s].length;
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { render } from "ink";
|
|
2
|
+
import { createElement } from "react";
|
|
3
|
+
import { ChatApp } from "./ChatApp.js";
|
|
4
|
+
/** Mounts the chat UI and resolves when the user exits. */
|
|
5
|
+
export async function runChatApp(opts) {
|
|
6
|
+
if (!process.stdin.isTTY) {
|
|
7
|
+
console.error("plasalid chat requires an interactive TTY.");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const instance = render(createElement(ChatApp, opts));
|
|
11
|
+
await instance.waitUntilExit();
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function printLogo(): void;
|
package/dist/cli/logo.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
const FISH = [
|
|
3
|
+
" ",
|
|
4
|
+
" ",
|
|
5
|
+
" ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ↑↑↑↑ ",
|
|
6
|
+
" ↑↑↑↑↑↑↑↑↑ ↑↑↑↑↑↑↑↑↑↑ ↑↑↑↑↑↑↑ ",
|
|
7
|
+
" ↑↑↑↑↑ ↑↑↑ ↑↑↑↑↑↑ ↑↑↑↑↑↑ ",
|
|
8
|
+
" ↑↑↑↑↑↑↑↑ ↑↑↑ ↑↑↑↑↑ ↑↑↑↑↑↑ ",
|
|
9
|
+
" ↑↑↑ ↑↑ ↑↑↑ ↑↑↑↑↑↑↑ ",
|
|
10
|
+
" ↑↑↑ ↑↑↑ ↑↑↑↑↑↑↑↑↑ ",
|
|
11
|
+
" ↑↑↑↑ ↑↑↑ ↑↑↑↑↑↑↑ ↑↑↑↑↑↑↑ ",
|
|
12
|
+
" ↑↑↑↑↑↑↑ ↑↑↑ ↑↑↑↑↑↑↑↑ ↑↑↑↑↑↑ ",
|
|
13
|
+
" ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ↑↑↑↑↑ ",
|
|
14
|
+
" ↑↑↑↑↑↑↑↑↑↑↑↑ ↑↑ ",
|
|
15
|
+
" ",
|
|
16
|
+
" ",
|
|
17
|
+
].join("\n");
|
|
18
|
+
export function printLogo() {
|
|
19
|
+
console.log(chalk.bold(FISH));
|
|
20
|
+
}
|