wormclaude 1.0.46 → 1.0.48
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/theme.js +1 -1
- package/dist/tui.js +72 -40
- package/package.json +1 -1
package/dist/theme.js
CHANGED
package/dist/tui.js
CHANGED
|
@@ -12,6 +12,7 @@ import { itemAnsi } from './ansi.js';
|
|
|
12
12
|
import { theme, VERSION } from './theme.js';
|
|
13
13
|
import { cleanModelText } from './textclean.js';
|
|
14
14
|
import { COMMANDS, runSlashCommand } from './commands.js';
|
|
15
|
+
import { cmdDesc } from './i18n.js';
|
|
15
16
|
const RESET = '\x1b[0m';
|
|
16
17
|
const hex = (h) => { const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(h); return m ? `\x1b[38;2;${parseInt(m[1], 16)};${parseInt(m[2], 16)};${parseInt(m[3], 16)}m` : ''; };
|
|
17
18
|
const paint = (s, c, bold = false) => `${bold ? '\x1b[1m' : ''}${c ? hex(c) : ''}${s}${RESET}`;
|
|
@@ -54,7 +55,38 @@ export async function runTui() {
|
|
|
54
55
|
const displayItems = [{ kind: 'banner' }];
|
|
55
56
|
let inputBuf = '', busy = false, streamChars = 0, spin = 0;
|
|
56
57
|
const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
|
|
57
|
-
|
|
58
|
+
// Giriş kutusunu genişliğe göre ÇOK SATIRA sar (uzun mesaj/kod sağa taşmaz, alta iner).
|
|
59
|
+
const MAX_INPUT_LINES = 10;
|
|
60
|
+
const inputBoxLines = (W) => {
|
|
61
|
+
const inner = Math.max(4, W - 2); // "✶ " önek payı
|
|
62
|
+
const wrapped = [];
|
|
63
|
+
let cur = '';
|
|
64
|
+
for (const ch of inputBuf) {
|
|
65
|
+
if (vis(cur + ch) > inner) {
|
|
66
|
+
wrapped.push(cur);
|
|
67
|
+
cur = ch;
|
|
68
|
+
}
|
|
69
|
+
else
|
|
70
|
+
cur += ch;
|
|
71
|
+
}
|
|
72
|
+
wrapped.push(cur);
|
|
73
|
+
const shown = wrapped.length > MAX_INPUT_LINES ? wrapped.slice(-MAX_INPUT_LINES) : wrapped; // çok uzunsa sonu göster
|
|
74
|
+
return shown.map((ln, i) => (i === 0 ? paint('✶ ', theme.redBright, true) : ' ') + paint(ln, theme.white) + (i === shown.length - 1 ? paint('▌', theme.greyDim) : ''));
|
|
75
|
+
};
|
|
76
|
+
// Footer yüksekliği DİNAMİK: izin=4; değilse (durum/menü) + çizgi + giriş-satırları + çizgi.
|
|
77
|
+
let prevFH = 4;
|
|
78
|
+
const footerHeight = () => {
|
|
79
|
+
if (perm)
|
|
80
|
+
return 4;
|
|
81
|
+
const m = cmdMatches();
|
|
82
|
+
const statusLines = m.length ? Math.min(8, m.length) : 1;
|
|
83
|
+
return statusLines + 2 + inputBoxLines(Math.max(8, cols())).length;
|
|
84
|
+
};
|
|
85
|
+
// Yükseklik değiştiyse scroll-region'ı + içeriği yeniden bas; değişmediyse sadece footer'ı çiz.
|
|
86
|
+
const refresh = () => { const fh = footerHeight(); if (fh !== prevFH)
|
|
87
|
+
redrawAll();
|
|
88
|
+
else
|
|
89
|
+
drawFooter(); };
|
|
58
90
|
setToolConfig(config); // araçlar (Write/Bash/Edit…) aynı config'i kullansın
|
|
59
91
|
// İzin (araç onayı) durumu
|
|
60
92
|
let perm = null;
|
|
@@ -81,9 +113,9 @@ export async function runTui() {
|
|
|
81
113
|
};
|
|
82
114
|
async function runCommand(v) {
|
|
83
115
|
busy = true;
|
|
84
|
-
|
|
116
|
+
refresh();
|
|
85
117
|
const timer = setInterval(() => { spin++; if (busy && !perm)
|
|
86
|
-
|
|
118
|
+
refresh(); }, 120);
|
|
87
119
|
try {
|
|
88
120
|
await runSlashCommand(v, cmdCtx);
|
|
89
121
|
}
|
|
@@ -92,7 +124,7 @@ export async function runTui() {
|
|
|
92
124
|
}
|
|
93
125
|
clearInterval(timer);
|
|
94
126
|
busy = false;
|
|
95
|
-
|
|
127
|
+
refresh();
|
|
96
128
|
}
|
|
97
129
|
// ── İkonlu, 2-sütunlu bilgi başlığı (3 sol / 3 sağ) — responsive ──
|
|
98
130
|
function headerLines(W) {
|
|
@@ -115,15 +147,16 @@ export async function runTui() {
|
|
|
115
147
|
return [...L, ...R].map((r) => fit(cell(r), W)); // dar ekran → alt alta
|
|
116
148
|
return [0, 1, 2].map((i) => fit(padVis(cell(L[i]), colW) + cell(R[i]), W)); // geniş → 2 sütun
|
|
117
149
|
}
|
|
118
|
-
// ── Scroll region: üst = içerik (kayar), alt = sabit footer ──
|
|
150
|
+
// ── Scroll region: üst = içerik (kayar), alt = sabit footer (dinamik yükseklik) ──
|
|
119
151
|
function setRegion() {
|
|
120
|
-
const bottom = Math.max(1, rows() -
|
|
152
|
+
const bottom = Math.max(1, rows() - footerHeight() - 1); // footer üstünde 1 boş satır
|
|
121
153
|
process.stdout.write(`\x1b[1;${bottom}r`);
|
|
122
154
|
}
|
|
123
155
|
// ── Sabit footer'ı en alta mutlak konumla çiz (imleci kaydet/geri yükle → içerik bozulmaz) ──
|
|
124
156
|
function drawFooter() {
|
|
125
157
|
const W = Math.max(8, cols());
|
|
126
|
-
const
|
|
158
|
+
const fh = footerHeight();
|
|
159
|
+
const start = rows() - fh + 1;
|
|
127
160
|
const line = paint('─'.repeat(W), theme.red);
|
|
128
161
|
let body;
|
|
129
162
|
if (perm) {
|
|
@@ -134,26 +167,24 @@ export async function runTui() {
|
|
|
134
167
|
body = [line, q, opts, line];
|
|
135
168
|
}
|
|
136
169
|
else {
|
|
137
|
-
|
|
138
|
-
const avail = W - 3;
|
|
139
|
-
if (vis(shown) > avail)
|
|
140
|
-
shown = '…' + shown.slice(-(avail - 1));
|
|
141
|
-
const inputLine = paint('✶ ', theme.redBright, true) + paint(shown, theme.white) + paint('▌', theme.greyDim);
|
|
142
|
-
// Durum satırı KUTUNUN ÜSTÜNDE: slash menü > spinner > ipucu.
|
|
170
|
+
const inputLines = inputBoxLines(W); // çok-satırlı giriş (sarılmış)
|
|
143
171
|
const m = cmdMatches();
|
|
144
|
-
let status;
|
|
145
172
|
if (m.length) {
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
173
|
+
// DİKEY slash menü (alt alta), seçili kırmızı — kutunun üstünde.
|
|
174
|
+
const items = m.slice(0, 8);
|
|
175
|
+
const sel = Math.min(cmdSel, items.length - 1);
|
|
176
|
+
const menu = items.map((c, i) => {
|
|
177
|
+
const on = i === sel;
|
|
178
|
+
return ' ' + paint((on ? '✶ ' : ' ') + c.name.padEnd(13), on ? theme.redBright : theme.grey) + paint(cmdDesc(c.name) || c.desc || '', theme.greyDim);
|
|
179
|
+
});
|
|
180
|
+
body = [...menu, line, ...inputLines, line];
|
|
152
181
|
}
|
|
153
182
|
else {
|
|
154
|
-
status =
|
|
183
|
+
const status = busy
|
|
184
|
+
? paint(` ${SPIN[spin % SPIN.length]} çalışıyor…${streamChars ? ' ' + streamChars + ' karakter' : ''}`, theme.grey)
|
|
185
|
+
: paint(' / komutlar · ↑↓ geçmiş · /kopyala · Ctrl+C çıkış', theme.greyDim);
|
|
186
|
+
body = [status, line, ...inputLines, line];
|
|
155
187
|
}
|
|
156
|
-
body = [status, line, inputLine, line];
|
|
157
188
|
}
|
|
158
189
|
let out = '\x1b7'; // imleci kaydet
|
|
159
190
|
body.forEach((l, i) => { out += `\x1b[${start + i};1H\x1b[2K` + fit(l, W); });
|
|
@@ -162,6 +193,7 @@ export async function runTui() {
|
|
|
162
193
|
}
|
|
163
194
|
// ── Her şeyi yeni boyutta yeniden bas (banner/kod responsive); içerik bölgesine ──
|
|
164
195
|
function redrawAll() {
|
|
196
|
+
prevFH = footerHeight(); // mevcut footer yüksekliğini kaydet (refresh karşılaştırması için)
|
|
165
197
|
setRegion();
|
|
166
198
|
process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
|
|
167
199
|
const W = cols();
|
|
@@ -173,7 +205,7 @@ export async function runTui() {
|
|
|
173
205
|
drawFooter();
|
|
174
206
|
}
|
|
175
207
|
// İçerik öğesini ekle + içerik alanına bas (taşarsa scrollback'e → kopyalanır). Footer sabit kalır.
|
|
176
|
-
const printItem = (it) => { displayItems.push(it); process.stdout.write(itemAnsi(it, cols()) + '\n');
|
|
208
|
+
const printItem = (it) => { displayItems.push(it); process.stdout.write(itemAnsi(it, cols()) + '\n'); refresh(); };
|
|
177
209
|
// ── Agent döngüsü (M2: araçlar + izin). Model tool çağırırsa izin sorulup çalıştırılır,
|
|
178
210
|
// sonuç geçmişe eklenip döngü devam eder; tool yoksa biter. ──
|
|
179
211
|
async function runTurn(userText) {
|
|
@@ -181,7 +213,7 @@ export async function runTui() {
|
|
|
181
213
|
streamChars = 0;
|
|
182
214
|
history.push({ role: 'user', content: userText });
|
|
183
215
|
const timer = setInterval(() => { spin++; if (busy && !perm)
|
|
184
|
-
|
|
216
|
+
refresh(); }, 120);
|
|
185
217
|
let lastSig = '', sameCount = 0;
|
|
186
218
|
try {
|
|
187
219
|
for (let iter = 0; iter < 25; iter++) {
|
|
@@ -226,7 +258,7 @@ export async function runTui() {
|
|
|
226
258
|
if (allowAll.has(c.name))
|
|
227
259
|
return resolve('allow');
|
|
228
260
|
perm = { label: toolLabel(c.name, args), name: c.name, resolve };
|
|
229
|
-
|
|
261
|
+
refresh();
|
|
230
262
|
}),
|
|
231
263
|
onResult: (c, _i, res) => printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }),
|
|
232
264
|
});
|
|
@@ -240,7 +272,7 @@ export async function runTui() {
|
|
|
240
272
|
clearInterval(timer);
|
|
241
273
|
busy = false;
|
|
242
274
|
perm = null;
|
|
243
|
-
|
|
275
|
+
refresh();
|
|
244
276
|
}
|
|
245
277
|
// ── Kurulum ──
|
|
246
278
|
readline.emitKeypressEvents(process.stdin);
|
|
@@ -270,31 +302,31 @@ export async function runTui() {
|
|
|
270
302
|
if (key && key.ctrl && key.name === 'c') {
|
|
271
303
|
perm = null;
|
|
272
304
|
r({ deny: 'kullanıcı iptal etti' });
|
|
273
|
-
|
|
305
|
+
refresh();
|
|
274
306
|
return;
|
|
275
307
|
}
|
|
276
308
|
if (k === 'e' || k === '1' || (key && key.name === 'return')) {
|
|
277
309
|
perm = null;
|
|
278
310
|
r('allow');
|
|
279
|
-
|
|
311
|
+
refresh();
|
|
280
312
|
}
|
|
281
313
|
else if (k === 't' || k === '2') {
|
|
282
314
|
allowAll.add(nm);
|
|
283
315
|
perm = null;
|
|
284
316
|
r('allow');
|
|
285
|
-
|
|
317
|
+
refresh();
|
|
286
318
|
}
|
|
287
319
|
else if (k === 'h' || k === '3' || (key && key.name === 'escape')) {
|
|
288
320
|
perm = null;
|
|
289
321
|
r({ deny: '' });
|
|
290
|
-
|
|
322
|
+
refresh();
|
|
291
323
|
}
|
|
292
324
|
return;
|
|
293
325
|
}
|
|
294
326
|
if (key && key.ctrl && key.name === 'c') {
|
|
295
327
|
if (inputBuf) {
|
|
296
328
|
inputBuf = '';
|
|
297
|
-
|
|
329
|
+
refresh();
|
|
298
330
|
return;
|
|
299
331
|
}
|
|
300
332
|
const now = Date.now();
|
|
@@ -313,13 +345,13 @@ export async function runTui() {
|
|
|
313
345
|
const m = cmdMatches();
|
|
314
346
|
if (m.length) {
|
|
315
347
|
cmdSel = (cmdSel - 1 + m.length) % m.length;
|
|
316
|
-
|
|
348
|
+
refresh();
|
|
317
349
|
return;
|
|
318
350
|
}
|
|
319
351
|
if (inputHistory.length) {
|
|
320
352
|
histIdx = histIdx < 0 ? inputHistory.length - 1 : Math.max(0, histIdx - 1);
|
|
321
353
|
inputBuf = inputHistory[histIdx];
|
|
322
|
-
|
|
354
|
+
refresh();
|
|
323
355
|
}
|
|
324
356
|
return;
|
|
325
357
|
}
|
|
@@ -327,7 +359,7 @@ export async function runTui() {
|
|
|
327
359
|
const m = cmdMatches();
|
|
328
360
|
if (m.length) {
|
|
329
361
|
cmdSel = (cmdSel + 1) % m.length;
|
|
330
|
-
|
|
362
|
+
refresh();
|
|
331
363
|
return;
|
|
332
364
|
}
|
|
333
365
|
if (histIdx >= 0) {
|
|
@@ -338,7 +370,7 @@ export async function runTui() {
|
|
|
338
370
|
}
|
|
339
371
|
else
|
|
340
372
|
inputBuf = inputHistory[histIdx];
|
|
341
|
-
|
|
373
|
+
refresh();
|
|
342
374
|
}
|
|
343
375
|
return;
|
|
344
376
|
}
|
|
@@ -348,7 +380,7 @@ export async function runTui() {
|
|
|
348
380
|
if (m.length) {
|
|
349
381
|
inputBuf = m[Math.min(cmdSel, m.length - 1)].name + ' ';
|
|
350
382
|
cmdSel = 0;
|
|
351
|
-
|
|
383
|
+
refresh();
|
|
352
384
|
}
|
|
353
385
|
return;
|
|
354
386
|
}
|
|
@@ -359,7 +391,7 @@ export async function runTui() {
|
|
|
359
391
|
inputBuf = '';
|
|
360
392
|
histIdx = -1;
|
|
361
393
|
if (!v) {
|
|
362
|
-
|
|
394
|
+
refresh();
|
|
363
395
|
return;
|
|
364
396
|
}
|
|
365
397
|
// Slash komutu: tam eşleşme yoksa menüde SEÇİLİ olanı kullan
|
|
@@ -395,19 +427,19 @@ export async function runTui() {
|
|
|
395
427
|
if (key && key.name === 'backspace') {
|
|
396
428
|
inputBuf = inputBuf.slice(0, -1);
|
|
397
429
|
cmdSel = 0;
|
|
398
|
-
|
|
430
|
+
refresh();
|
|
399
431
|
return;
|
|
400
432
|
}
|
|
401
433
|
if (key && key.name === 'escape') {
|
|
402
434
|
inputBuf = '';
|
|
403
|
-
|
|
435
|
+
refresh();
|
|
404
436
|
return;
|
|
405
437
|
}
|
|
406
438
|
// sadece gerçek yazdırılabilir karakter (her zaman → cevap üretilirken bile type-ahead)
|
|
407
439
|
if (str && !key?.ctrl && !key?.meta && !str.startsWith('\x1b') && !/[\x00-\x1f]/.test(str)) {
|
|
408
440
|
inputBuf += str;
|
|
409
441
|
cmdSel = 0;
|
|
410
|
-
|
|
442
|
+
refresh();
|
|
411
443
|
}
|
|
412
444
|
});
|
|
413
445
|
// resize → settle sonrası her şeyi yeni boyutta yeniden bas (region + banner + footer)
|