wormclaude 1.0.52 → 1.0.54
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/ansi.js +1 -1
- package/dist/theme.js +1 -1
- package/dist/tui.js +59 -30
- package/package.json +1 -1
package/dist/ansi.js
CHANGED
|
@@ -81,7 +81,7 @@ export function bannerAnsi(cols) {
|
|
|
81
81
|
return body + '\n' + paint(' ' + t('banner.subtitle'), theme.greyDim);
|
|
82
82
|
}
|
|
83
83
|
// ── Markdown bloğu → ANSI (kod blokları sözdizimi-vurgulu) ──
|
|
84
|
-
function markdownAnsi(text, cols) {
|
|
84
|
+
export function markdownAnsi(text, cols) {
|
|
85
85
|
const W = Math.max(20, cols - 2);
|
|
86
86
|
const lines = text.split('\n');
|
|
87
87
|
const out = [];
|
package/dist/theme.js
CHANGED
package/dist/tui.js
CHANGED
|
@@ -8,7 +8,7 @@ import stringWidth from 'string-width';
|
|
|
8
8
|
import { loadConfig, streamChat, fetchAccount } from './api.js';
|
|
9
9
|
import { allToolSchemas, executeToolCalls, executeTool, toolLabel, setToolConfig } from './tools.js';
|
|
10
10
|
import { sanitizeOutput } from './errorsan.js';
|
|
11
|
-
import { itemAnsi } from './ansi.js';
|
|
11
|
+
import { itemAnsi, markdownAnsi } 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';
|
|
@@ -64,11 +64,10 @@ export async function runTui() {
|
|
|
64
64
|
];
|
|
65
65
|
const displayItems = [{ kind: 'banner' }];
|
|
66
66
|
let inputBuf = '', busy = false, streamChars = 0, spin = 0;
|
|
67
|
-
|
|
68
|
-
const PREVIEW_LINES = 6; // akışta sabit önizleme satırı (yükseklik stabil → titreme yok)
|
|
67
|
+
// Canlı akış artık FOOTER'da DEĞİL — içerik akışına (mesajın altından aşağı) satır-satır basılır.
|
|
69
68
|
const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
|
|
70
|
-
// Giriş kutusunu genişliğe göre ÇOK SATIRA sar
|
|
71
|
-
const MAX_INPUT_LINES =
|
|
69
|
+
// Giriş kutusunu genişliğe göre ÇOK SATIRA sar; çok uzunsa (yapıştırma) KISALTIP özet gösterir.
|
|
70
|
+
const MAX_INPUT_LINES = 6;
|
|
72
71
|
const inputBoxLines = (W) => {
|
|
73
72
|
const inner = Math.max(4, W - 2); // "✶ " önek payı
|
|
74
73
|
const wrapped = [];
|
|
@@ -82,8 +81,14 @@ export async function runTui() {
|
|
|
82
81
|
cur += ch;
|
|
83
82
|
}
|
|
84
83
|
wrapped.push(cur);
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
if (wrapped.length <= MAX_INPUT_LINES) {
|
|
85
|
+
return wrapped.map((ln, i) => (i === 0 ? paint('✶ ', theme.redBright, true) : ' ') + paint(ln, theme.white) + (i === wrapped.length - 1 ? paint('▌', theme.greyDim) : ''));
|
|
86
|
+
}
|
|
87
|
+
// Çok uzun (büyük yapıştırma): son birkaç satır + üstte özet — footer'ı doldurmaz.
|
|
88
|
+
const extra = wrapped.length - (MAX_INPUT_LINES - 1);
|
|
89
|
+
const tail = wrapped.slice(-(MAX_INPUT_LINES - 1));
|
|
90
|
+
const head = paint(`✶ …(+${extra} satır · ${inputBuf.length} karakter, Enter ile gönder)`, theme.greyDim);
|
|
91
|
+
return [head, ...tail.map((ln, i) => ' ' + paint(ln, theme.white) + (i === tail.length - 1 ? paint('▌', theme.greyDim) : ''))];
|
|
87
92
|
};
|
|
88
93
|
// Footer yüksekliği DİNAMİK: izin=4; değilse (durum/menü) + çizgi + giriş-satırları + çizgi.
|
|
89
94
|
// SABİT rezerve footer alanı (scroll region bir kez ayarlanır → churn/duplicate yok). Footer bu
|
|
@@ -91,6 +96,11 @@ export async function runTui() {
|
|
|
91
96
|
const FOOTER_MAX = () => Math.min(11, Math.max(5, rows() - 6));
|
|
92
97
|
// refresh = sadece footer'ı çiz (region sabit; içerik yeniden BASILMAZ → duplicate olmaz).
|
|
93
98
|
const refresh = () => drawFooter();
|
|
99
|
+
// DEBOUNCE: hızlı yazma/yapıştırma akınında her tuşta DEĞİL, tick başına BİR KEZ çiz. Yoksa
|
|
100
|
+
// her karakter tam footer redraw'u tetikleyip render'ı kuyruğa biriktiriyor (elini çeksen de gidiyor).
|
|
101
|
+
let _fpending = false;
|
|
102
|
+
const scheduleFooter = () => { if (_fpending)
|
|
103
|
+
return; _fpending = true; setImmediate(() => { _fpending = false; drawFooter(); }); };
|
|
94
104
|
setToolConfig(config); // araçlar (Write/Bash/Edit…) aynı config'i kullansın
|
|
95
105
|
// İzin (araç onayı) + soru (AskUserQuestion) durumu
|
|
96
106
|
let perm = null;
|
|
@@ -185,16 +195,7 @@ export async function runTui() {
|
|
|
185
195
|
else {
|
|
186
196
|
const inputLines = inputBoxLines(W); // çok-satırlı giriş (sarılmış)
|
|
187
197
|
const m = cmdMatches();
|
|
188
|
-
if (
|
|
189
|
-
// CANLI akış önizlemesi (son PREVIEW_LINES satır, sabit yükseklik)
|
|
190
|
-
const pl = streamPreview.split('\n');
|
|
191
|
-
const tail = pl.slice(-PREVIEW_LINES);
|
|
192
|
-
while (tail.length < PREVIEW_LINES)
|
|
193
|
-
tail.push('');
|
|
194
|
-
const prev = tail.map((l, i) => (i === 0 ? paint('✶ ', theme.redBright, true) : ' ') + paint(l, theme.white));
|
|
195
|
-
body = [...prev, line, ...inputLines, line];
|
|
196
|
-
}
|
|
197
|
-
else if (m.length) {
|
|
198
|
+
if (m.length) {
|
|
198
199
|
// DİKEY slash menü — KAYAN pencere (8 satır, seçimi takip eder), seçili kırmızı.
|
|
199
200
|
const sel = Math.min(cmdSel, m.length - 1);
|
|
200
201
|
const WIN = 8;
|
|
@@ -243,7 +244,6 @@ export async function runTui() {
|
|
|
243
244
|
async function runTurn(userText, displayText) {
|
|
244
245
|
busy = true;
|
|
245
246
|
streamChars = 0;
|
|
246
|
-
streamPreview = '';
|
|
247
247
|
if (displayText !== undefined)
|
|
248
248
|
printItem({ kind: 'user', text: displayText }); // skill/@mention: gösterim farklı
|
|
249
249
|
history.push({ role: 'user', content: userText });
|
|
@@ -253,7 +253,6 @@ export async function runTui() {
|
|
|
253
253
|
try {
|
|
254
254
|
for (let iter = 0; iter < 25; iter++) {
|
|
255
255
|
streamChars = 0;
|
|
256
|
-
streamPreview = '';
|
|
257
256
|
// Oto-compact: bağlam eşiği aşılınca otomatik özetle (uzun sohbet patlamasın)
|
|
258
257
|
if (shouldAutoCompact(history)) {
|
|
259
258
|
try {
|
|
@@ -266,28 +265,59 @@ export async function runTui() {
|
|
|
266
265
|
}
|
|
267
266
|
let answer = '';
|
|
268
267
|
let toolCalls = [];
|
|
268
|
+
// ── CANLI akış İÇERİK AKIŞINA (footer'a değil): tamamlanan satırları, mesajın altından
|
|
269
|
+
// aşağı doğru, formatlı (kod renkli) bas. Açık kod-bloğu kapanana dek bekle (yarım fence
|
|
270
|
+
// bozuk renklenmesin). ✶ öneki yalnız bu cevabın İLK satırına; gerisi " " ile hizalı. ──
|
|
271
|
+
let committed = 0, emittedFirst = false, leadEmitted = false;
|
|
272
|
+
const flushStream = (final) => {
|
|
273
|
+
const lines = answer.split('\n');
|
|
274
|
+
let cut;
|
|
275
|
+
if (final)
|
|
276
|
+
cut = answer.length;
|
|
277
|
+
else {
|
|
278
|
+
let pos = 0, fence = false, lastSafe = 0;
|
|
279
|
+
for (let k = 0; k < lines.length - 1; k++) { // son eleman = yarım satır (newline gelmedi)
|
|
280
|
+
pos += lines[k].length + 1;
|
|
281
|
+
if (/^ *(`{3,}|~{3,})/.test(lines[k]))
|
|
282
|
+
fence = !fence;
|
|
283
|
+
if (!fence)
|
|
284
|
+
lastSafe = pos;
|
|
285
|
+
}
|
|
286
|
+
cut = lastSafe;
|
|
287
|
+
}
|
|
288
|
+
if (cut <= committed)
|
|
289
|
+
return;
|
|
290
|
+
const chunk = cleanModelText(answer.slice(committed, cut)).replace(/\n+$/, '');
|
|
291
|
+
committed = cut;
|
|
292
|
+
if (!chunk && !final)
|
|
293
|
+
return;
|
|
294
|
+
const md = markdownAnsi(chunk, cols()).split('\n');
|
|
295
|
+
let out = leadEmitted ? '' : (leadEmitted = true, '\n'); // cevabın üstüne 1 ayraç satır
|
|
296
|
+
out += md.map((ln) => emittedFirst ? ' ' + ln : (emittedFirst = true, paint('✶ ', theme.redBright, true) + ln)).join('\n') + '\n';
|
|
297
|
+
process.stdout.write(out); // imleç içerik bölgesinde → aşağı akar, taşınca scrollback'e
|
|
298
|
+
refresh(); // footer'ı dibe yeniden sabitle
|
|
299
|
+
};
|
|
269
300
|
for await (const ev of streamChat(history, allToolSchemas(), config)) {
|
|
270
|
-
// CANLI akış: answer biriktir + streamPreview güncelle. drawFooter'ı 120ms timer çizer (titreme yok).
|
|
271
301
|
if (ev.type === 'text') {
|
|
272
302
|
answer += ev.text;
|
|
273
303
|
streamChars = answer.length;
|
|
274
|
-
|
|
304
|
+
flushStream(false);
|
|
275
305
|
}
|
|
276
306
|
else if (ev.type === 'done')
|
|
277
307
|
toolCalls = ev.toolCalls || [];
|
|
278
|
-
else if (ev.type === 'error')
|
|
308
|
+
else if (ev.type === 'error') {
|
|
279
309
|
answer += `\n[hata: ${ev.error}]`;
|
|
310
|
+
flushStream(false);
|
|
311
|
+
}
|
|
280
312
|
}
|
|
313
|
+
flushStream(true); // kalan yarım satırı bas
|
|
281
314
|
answer = cleanModelText(answer).trim();
|
|
282
|
-
streamPreview = ''; // canlı önizlemeyi kapat → footer küçülür, formatlı cevap içeriğe basılır
|
|
283
315
|
const am = { role: 'assistant', content: answer || '' };
|
|
284
316
|
if (toolCalls.length)
|
|
285
317
|
am.tool_calls = toolCalls.map((t) => ({ id: t.id, type: 'function', function: { name: t.name, arguments: t.args || '{}' } }));
|
|
286
318
|
history.push(am);
|
|
287
|
-
if (answer)
|
|
288
|
-
lastAnswer = answer;
|
|
289
|
-
printItem({ kind: 'assistant', text: answer });
|
|
290
|
-
}
|
|
319
|
+
if (answer)
|
|
320
|
+
lastAnswer = answer; // gösterim akışta yapıldı; tekrar printItem YOK (duplikasyon olmaz)
|
|
291
321
|
if (!toolCalls.length)
|
|
292
322
|
break;
|
|
293
323
|
// takılma koruması: aynı araç-çağrısı 3 kez üst üste → dur
|
|
@@ -324,7 +354,6 @@ export async function runTui() {
|
|
|
324
354
|
busy = false;
|
|
325
355
|
perm = null;
|
|
326
356
|
ask = null;
|
|
327
|
-
streamPreview = '';
|
|
328
357
|
refresh();
|
|
329
358
|
// Web-öğrenme: web'de arayıp cevap ürettiyse {soru→cevap}'ı eğitim datasına ekle
|
|
330
359
|
if (usedWeb && lastAnswer) {
|
|
@@ -558,7 +587,7 @@ export async function runTui() {
|
|
|
558
587
|
if (key && key.name === 'backspace') {
|
|
559
588
|
inputBuf = inputBuf.slice(0, -1);
|
|
560
589
|
cmdSel = 0;
|
|
561
|
-
|
|
590
|
+
scheduleFooter();
|
|
562
591
|
return;
|
|
563
592
|
}
|
|
564
593
|
if (key && key.name === 'escape') {
|
|
@@ -570,7 +599,7 @@ export async function runTui() {
|
|
|
570
599
|
if (str && !key?.ctrl && !key?.meta && !str.startsWith('\x1b') && !/[\x00-\x1f]/.test(str)) {
|
|
571
600
|
inputBuf += str;
|
|
572
601
|
cmdSel = 0;
|
|
573
|
-
|
|
602
|
+
scheduleFooter();
|
|
574
603
|
}
|
|
575
604
|
});
|
|
576
605
|
// resize → settle sonrası her şeyi yeni boyutta yeniden bas (region + banner + footer)
|