wormclaude 1.0.24 → 1.0.26
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 +2 -2
- package/dist/cli.js +14 -7
- package/dist/theme.js +1 -1
- package/dist/tui.js +58 -4
- package/package.json +1 -1
package/dist/ansi.js
CHANGED
|
@@ -72,9 +72,9 @@ const CLAUDE_ROWS = [
|
|
|
72
72
|
];
|
|
73
73
|
/** Banner'ı ANSI olarak üretir (dar ekranda tek kelime, geniş ekranda yan yana). */
|
|
74
74
|
export function bannerAnsi(cols) {
|
|
75
|
-
if (cols <
|
|
75
|
+
if (cols < 64)
|
|
76
76
|
return paint('WORMCLAUDE', theme.red, true) + '\n' + paint(' ' + t('banner.subtitle'), theme.greyDim);
|
|
77
|
-
const rows = cols >=
|
|
77
|
+
const rows = cols >= 100 ? WORM_ROWS.map((w, i) => w + CLAUDE_ROWS[i]) : [...WORM_ROWS, ...CLAUDE_ROWS];
|
|
78
78
|
return rows.map((r) => paint(r, theme.red, true)).join('\n') + '\n' + paint(' ' + t('banner.subtitle'), theme.greyDim);
|
|
79
79
|
}
|
|
80
80
|
// ── Markdown bloğu → ANSI (kod blokları sözdizimi-vurgulu) ──
|
package/dist/cli.js
CHANGED
|
@@ -254,9 +254,15 @@ function buildLines(items, cols) {
|
|
|
254
254
|
for (const it of items) {
|
|
255
255
|
lines.push([]); // üstte boşluk
|
|
256
256
|
if (it.kind === 'banner') {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
lines.push([{ text:
|
|
257
|
+
// Dar terminalde (<64) ASCII art sarıp bozulur → temiz tek-kelime kullan.
|
|
258
|
+
if (cols < 64) {
|
|
259
|
+
lines.push([{ text: 'WORMCLAUDE', color: red, bold: true }]);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
const rows = cols >= 100 ? WORM_ROWS.map((w, i) => w + CLAUDE_ROWS[i]) : [...WORM_ROWS, ...CLAUDE_ROWS];
|
|
263
|
+
for (const r of rows)
|
|
264
|
+
lines.push([{ text: r, color: red, bold: true }]);
|
|
265
|
+
}
|
|
260
266
|
lines.push([{ text: ' ' + t('banner.subtitle'), dim: true }]);
|
|
261
267
|
}
|
|
262
268
|
else if (it.kind === 'user') {
|
|
@@ -287,14 +293,14 @@ function buildLines(items, cols) {
|
|
|
287
293
|
function Banner() {
|
|
288
294
|
const { cols } = useDimensions();
|
|
289
295
|
const RED = theme.red; // WORM + CLAUDE tek renk (kan kırmızısı)
|
|
290
|
-
//
|
|
291
|
-
if (cols <
|
|
296
|
+
// Dar terminal (<64): temiz tek kelime (ASCII art sarıp bozulmaz)
|
|
297
|
+
if (cols < 64) {
|
|
292
298
|
return (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
293
299
|
React.createElement(Text, { color: RED, bold: true, wrap: "truncate" }, "WORMCLAUDE"),
|
|
294
300
|
React.createElement(Text, { color: theme.greyDim, wrap: "truncate" }, t('banner.subtitle'))));
|
|
295
301
|
}
|
|
296
302
|
// Geniş ekran: tek satır WORMCLAUDE (WORM + CLAUDE yan yana, tek renk → düzgün kırpılır)
|
|
297
|
-
if (cols >=
|
|
303
|
+
if (cols >= 100) {
|
|
298
304
|
return (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
299
305
|
WORM_ROWS.map((w, i) => React.createElement(Text, { key: i, color: RED, bold: true, wrap: "truncate" }, w + CLAUDE_ROWS[i])),
|
|
300
306
|
React.createElement(Text, { color: theme.greyDim, wrap: "truncate" }, ` ${t('banner.subtitle')}`)));
|
|
@@ -368,7 +374,8 @@ function StatusLine({ model, ctxTokens, trust = 0 }) {
|
|
|
368
374
|
const barColor = pct > 85 ? theme.errorRed : pct > 60 ? theme.redBright : theme.grey;
|
|
369
375
|
const cwd = path.basename(process.cwd()) || process.cwd();
|
|
370
376
|
const badge = tier(trust).badge;
|
|
371
|
-
|
|
377
|
+
// Tek satır + truncate → dar terminalde "WormClaud4" gibi taşma/wrap olmaz.
|
|
378
|
+
return (React.createElement(Text, { wrap: "truncate" },
|
|
372
379
|
React.createElement(Text, { color: theme.greyDim },
|
|
373
380
|
' ',
|
|
374
381
|
"WormClaude "),
|
package/dist/theme.js
CHANGED
package/dist/tui.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// Renkli kod + markdown: ansi.ts (itemAnsi). WORMCLAUDE_TUI=1 ile çalışır; ink sürümü dokunulmadan kalır.
|
|
5
5
|
import readline from 'node:readline';
|
|
6
6
|
import logUpdate from 'log-update';
|
|
7
|
+
import stringWidth from 'string-width';
|
|
7
8
|
import { loadConfig, streamChat } from './api.js';
|
|
8
9
|
import { itemAnsi, bannerAnsi } from './ansi.js';
|
|
9
10
|
import { theme, VERSION } from './theme.js';
|
|
@@ -12,6 +13,31 @@ const RESET = '\x1b[0m';
|
|
|
12
13
|
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` : ''; };
|
|
13
14
|
const paint = (s, c, bold = false) => `${bold ? '\x1b[1m' : ''}${c ? hex(c) : ''}${s}${RESET}`;
|
|
14
15
|
const cols = () => process.stdout.columns || 80;
|
|
16
|
+
// Footer satırını terminal genişliğine ANSI-korumalı kırp (otomatik sarmayı önler → log-update
|
|
17
|
+
// satır sayımı şaşmaz, küçük terminalde bozulmaz). ANSI escape'leri genişliğe saymaz, korur.
|
|
18
|
+
function fit(line, max) {
|
|
19
|
+
let out = '', w = 0, i = 0;
|
|
20
|
+
while (i < line.length) {
|
|
21
|
+
if (line[i] === '\x1b') {
|
|
22
|
+
const m = /^\x1b\[[0-9;]*m/.exec(line.slice(i));
|
|
23
|
+
if (m) {
|
|
24
|
+
out += m[0];
|
|
25
|
+
i += m[0].length;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const ch = line[i];
|
|
30
|
+
const cw = stringWidth(ch) || 1;
|
|
31
|
+
if (w + cw > max)
|
|
32
|
+
break;
|
|
33
|
+
out += ch;
|
|
34
|
+
w += cw;
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
return out + RESET;
|
|
38
|
+
}
|
|
39
|
+
// Görünür (ANSI'siz) genişlik
|
|
40
|
+
const vis = (s) => stringWidth(s.replace(/\x1b\[[0-9;]*m/g, ''));
|
|
15
41
|
export async function runTui() {
|
|
16
42
|
const config = loadConfig();
|
|
17
43
|
const history = []; // model bağlamı {role, content}
|
|
@@ -24,19 +50,28 @@ export async function runTui() {
|
|
|
24
50
|
const printItem = (it) => { logUpdate.clear(); process.stdout.write(itemAnsi(it, cols()) + '\n'); renderFooter(); };
|
|
25
51
|
// ── Canlı footer (log-update yönetir) ──
|
|
26
52
|
function renderFooter() {
|
|
53
|
+
const W = Math.max(8, cols());
|
|
27
54
|
const lines = [];
|
|
28
55
|
if (busy && !streamPreview)
|
|
29
56
|
lines.push(paint(`${SPIN[spin % SPIN.length]} `, theme.red) + paint('yanıt bekleniyor…', theme.grey));
|
|
30
57
|
if (streamPreview) {
|
|
31
|
-
const
|
|
58
|
+
const maxTail = Math.max(2, Math.min(12, (process.stdout.rows || 24) - 4)); // ekran boyunu aşma
|
|
59
|
+
const tail = streamPreview.split('\n').slice(-maxTail);
|
|
32
60
|
lines.push(paint('⏺ ', theme.redBright, true) + paint(tail.shift() || '', theme.white));
|
|
33
61
|
for (const l of tail)
|
|
34
62
|
lines.push(' ' + paint(l, theme.white));
|
|
35
63
|
}
|
|
36
64
|
lines.push('');
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
65
|
+
// Giriş satırı: uzunsa SONU göster (yazdıkça imleç görünür kalsın)
|
|
66
|
+
const prompt = paint('❯ ', theme.redBright, true);
|
|
67
|
+
let shown = inputBuf;
|
|
68
|
+
const avail = W - 3; // "❯ " + imleç payı
|
|
69
|
+
if (vis(shown) > avail)
|
|
70
|
+
shown = '…' + shown.slice(-(avail - 1));
|
|
71
|
+
lines.push(prompt + shown + (busy ? '' : paint('▌', theme.greyDim)));
|
|
72
|
+
lines.push(paint('/kopyala panoya · Ctrl+C çıkış', theme.greyDim));
|
|
73
|
+
// HER satırı genişliğe kırp → küçük terminalde sarma/bozulma olmaz
|
|
74
|
+
logUpdate(lines.map((l) => fit(l, W)).join('\n'));
|
|
40
75
|
}
|
|
41
76
|
// ── Bir sohbet turu (Milestone 1: araç yok, saf metin) ──
|
|
42
77
|
async function runTurn(userText) {
|
|
@@ -78,8 +113,27 @@ export async function runTui() {
|
|
|
78
113
|
process.stdout.write(bannerAnsi(cols()) + '\n');
|
|
79
114
|
process.stdout.write(paint(' uncensored security + code', theme.greyDim) + paint(' · WormClaude ', theme.greyDim) + paint('v' + VERSION, theme.red, true) + paint(' · özel renderer (deneysel)', theme.greyDim) + '\n\n');
|
|
80
115
|
renderFooter();
|
|
116
|
+
let ctrlcAt = 0;
|
|
81
117
|
process.stdin.on('keypress', (str, key) => {
|
|
118
|
+
// Ctrl+C tek başına ÇIKMAZ (Windows'ta seçimi Ctrl+C ile kopyalarken uygulama kapanmasın).
|
|
119
|
+
// Giriş varsa temizler; boşsa 2 sn içinde ikinci Ctrl+C ile çıkar.
|
|
82
120
|
if (key && key.ctrl && key.name === 'c') {
|
|
121
|
+
if (inputBuf) {
|
|
122
|
+
inputBuf = '';
|
|
123
|
+
renderFooter();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const now = Date.now();
|
|
127
|
+
if (now - ctrlcAt < 2000) {
|
|
128
|
+
logUpdate.clear();
|
|
129
|
+
process.stdout.write('\n');
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
ctrlcAt = now;
|
|
133
|
+
printItem({ kind: 'note', text: 'Çıkmak için tekrar Ctrl+C (veya /cikis). Kopyalama Ctrl+C\'yi etkilemez.' });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (key && key.ctrl && key.name === 'd') {
|
|
83
137
|
logUpdate.clear();
|
|
84
138
|
process.stdout.write('\n');
|
|
85
139
|
process.exit(0);
|