wormclaude 1.0.46 → 1.0.47

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 CHANGED
@@ -16,4 +16,4 @@ export const theme = {
16
16
  synType: '#a78bfa', // tip/sınıf adları, sabitler
17
17
  synProp: '#e0e0e0', // özellik/anahtar adları
18
18
  };
19
- export const VERSION = '1.0.46';
19
+ export const VERSION = '1.0.47';
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,21 @@ 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
- const FOOTER_H = 4; // kutu (üst çizgi + giriş/dialog + alt çizgi) + ipucu satırı
58
+ // Footer yüksekliği DİNAMİK: izin=4, slash menü=N+3 (menü satırları + çizgi/giriş/çizgi), normal=4.
59
+ let prevFH = 4;
60
+ const footerHeight = () => {
61
+ if (perm)
62
+ return 4;
63
+ const m = cmdMatches();
64
+ if (m.length)
65
+ return Math.min(8, m.length) + 3;
66
+ return 4;
67
+ };
68
+ // Yükseklik değiştiyse scroll-region'ı + içeriği yeniden bas; değişmediyse sadece footer'ı çiz.
69
+ const refresh = () => { const fh = footerHeight(); if (fh !== prevFH)
70
+ redrawAll();
71
+ else
72
+ drawFooter(); };
58
73
  setToolConfig(config); // araçlar (Write/Bash/Edit…) aynı config'i kullansın
59
74
  // İzin (araç onayı) durumu
60
75
  let perm = null;
@@ -81,9 +96,9 @@ export async function runTui() {
81
96
  };
82
97
  async function runCommand(v) {
83
98
  busy = true;
84
- drawFooter();
99
+ refresh();
85
100
  const timer = setInterval(() => { spin++; if (busy && !perm)
86
- drawFooter(); }, 120);
101
+ refresh(); }, 120);
87
102
  try {
88
103
  await runSlashCommand(v, cmdCtx);
89
104
  }
@@ -92,7 +107,7 @@ export async function runTui() {
92
107
  }
93
108
  clearInterval(timer);
94
109
  busy = false;
95
- drawFooter();
110
+ refresh();
96
111
  }
97
112
  // ── İkonlu, 2-sütunlu bilgi başlığı (3 sol / 3 sağ) — responsive ──
98
113
  function headerLines(W) {
@@ -115,15 +130,16 @@ export async function runTui() {
115
130
  return [...L, ...R].map((r) => fit(cell(r), W)); // dar ekran → alt alta
116
131
  return [0, 1, 2].map((i) => fit(padVis(cell(L[i]), colW) + cell(R[i]), W)); // geniş → 2 sütun
117
132
  }
118
- // ── Scroll region: üst = içerik (kayar), alt = sabit footer ──
133
+ // ── Scroll region: üst = içerik (kayar), alt = sabit footer (dinamik yükseklik) ──
119
134
  function setRegion() {
120
- const bottom = Math.max(1, rows() - FOOTER_H - 1); // footer üstünde 1 boş satır
135
+ const bottom = Math.max(1, rows() - footerHeight() - 1); // footer üstünde 1 boş satır
121
136
  process.stdout.write(`\x1b[1;${bottom}r`);
122
137
  }
123
138
  // ── Sabit footer'ı en alta mutlak konumla çiz (imleci kaydet/geri yükle → içerik bozulmaz) ──
124
139
  function drawFooter() {
125
140
  const W = Math.max(8, cols());
126
- const start = rows() - FOOTER_H + 1;
141
+ const fh = footerHeight();
142
+ const start = rows() - fh + 1;
127
143
  const line = paint('─'.repeat(W), theme.red);
128
144
  let body;
129
145
  if (perm) {
@@ -139,21 +155,23 @@ export async function runTui() {
139
155
  if (vis(shown) > avail)
140
156
  shown = '…' + shown.slice(-(avail - 1));
141
157
  const inputLine = paint('✶ ', theme.redBright, true) + paint(shown, theme.white) + paint('▌', theme.greyDim);
142
- // Durum satırı KUTUNUN ÜSTÜNDE: slash menü > spinner > ipucu.
143
158
  const m = cmdMatches();
144
- let status;
145
159
  if (m.length) {
146
- const sel = Math.min(cmdSel, m.length - 1);
147
- const names = m.slice(0, 8).map((c, i) => i === sel ? paint(c.name, theme.redBright, true) : paint(c.name, theme.greyDim)).join(' ');
148
- status = ' ' + names + paint(' ↹ tamamla · ↑↓ seç', theme.greyDim);
149
- }
150
- else if (busy) {
151
- status = paint(` ${SPIN[spin % SPIN.length]} çalışıyor…${streamChars ? ' ' + streamChars + ' karakter' : ''}`, theme.grey);
160
+ // DİKEY slash menü (alt alta), seçili kırmızı — kutunun üstünde.
161
+ const items = m.slice(0, 8);
162
+ const sel = Math.min(cmdSel, items.length - 1);
163
+ const menu = items.map((c, i) => {
164
+ const on = i === sel;
165
+ return ' ' + paint((on ? '✶ ' : ' ') + c.name.padEnd(13), on ? theme.redBright : theme.grey) + paint(cmdDesc(c.name) || c.desc || '', theme.greyDim);
166
+ });
167
+ body = [...menu, line, inputLine, line];
152
168
  }
153
169
  else {
154
- status = paint(' / komutlar · ↑↓ geçmiş · /kopyala · Ctrl+C çıkış', theme.greyDim);
170
+ const status = busy
171
+ ? paint(` ${SPIN[spin % SPIN.length]} çalışıyor…${streamChars ? ' ' + streamChars + ' karakter' : ''}`, theme.grey)
172
+ : paint(' / komutlar · ↑↓ geçmiş · /kopyala · Ctrl+C çıkış', theme.greyDim);
173
+ body = [status, line, inputLine, line];
155
174
  }
156
- body = [status, line, inputLine, line];
157
175
  }
158
176
  let out = '\x1b7'; // imleci kaydet
159
177
  body.forEach((l, i) => { out += `\x1b[${start + i};1H\x1b[2K` + fit(l, W); });
@@ -162,6 +180,7 @@ export async function runTui() {
162
180
  }
163
181
  // ── Her şeyi yeni boyutta yeniden bas (banner/kod responsive); içerik bölgesine ──
164
182
  function redrawAll() {
183
+ prevFH = footerHeight(); // mevcut footer yüksekliğini kaydet (refresh karşılaştırması için)
165
184
  setRegion();
166
185
  process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
167
186
  const W = cols();
@@ -173,7 +192,7 @@ export async function runTui() {
173
192
  drawFooter();
174
193
  }
175
194
  // İç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'); drawFooter(); };
195
+ const printItem = (it) => { displayItems.push(it); process.stdout.write(itemAnsi(it, cols()) + '\n'); refresh(); };
177
196
  // ── Agent döngüsü (M2: araçlar + izin). Model tool çağırırsa izin sorulup çalıştırılır,
178
197
  // sonuç geçmişe eklenip döngü devam eder; tool yoksa biter. ──
179
198
  async function runTurn(userText) {
@@ -181,7 +200,7 @@ export async function runTui() {
181
200
  streamChars = 0;
182
201
  history.push({ role: 'user', content: userText });
183
202
  const timer = setInterval(() => { spin++; if (busy && !perm)
184
- drawFooter(); }, 120);
203
+ refresh(); }, 120);
185
204
  let lastSig = '', sameCount = 0;
186
205
  try {
187
206
  for (let iter = 0; iter < 25; iter++) {
@@ -226,7 +245,7 @@ export async function runTui() {
226
245
  if (allowAll.has(c.name))
227
246
  return resolve('allow');
228
247
  perm = { label: toolLabel(c.name, args), name: c.name, resolve };
229
- drawFooter();
248
+ refresh();
230
249
  }),
231
250
  onResult: (c, _i, res) => printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }),
232
251
  });
@@ -240,7 +259,7 @@ export async function runTui() {
240
259
  clearInterval(timer);
241
260
  busy = false;
242
261
  perm = null;
243
- drawFooter();
262
+ refresh();
244
263
  }
245
264
  // ── Kurulum ──
246
265
  readline.emitKeypressEvents(process.stdin);
@@ -270,31 +289,31 @@ export async function runTui() {
270
289
  if (key && key.ctrl && key.name === 'c') {
271
290
  perm = null;
272
291
  r({ deny: 'kullanıcı iptal etti' });
273
- drawFooter();
292
+ refresh();
274
293
  return;
275
294
  }
276
295
  if (k === 'e' || k === '1' || (key && key.name === 'return')) {
277
296
  perm = null;
278
297
  r('allow');
279
- drawFooter();
298
+ refresh();
280
299
  }
281
300
  else if (k === 't' || k === '2') {
282
301
  allowAll.add(nm);
283
302
  perm = null;
284
303
  r('allow');
285
- drawFooter();
304
+ refresh();
286
305
  }
287
306
  else if (k === 'h' || k === '3' || (key && key.name === 'escape')) {
288
307
  perm = null;
289
308
  r({ deny: '' });
290
- drawFooter();
309
+ refresh();
291
310
  }
292
311
  return;
293
312
  }
294
313
  if (key && key.ctrl && key.name === 'c') {
295
314
  if (inputBuf) {
296
315
  inputBuf = '';
297
- drawFooter();
316
+ refresh();
298
317
  return;
299
318
  }
300
319
  const now = Date.now();
@@ -313,13 +332,13 @@ export async function runTui() {
313
332
  const m = cmdMatches();
314
333
  if (m.length) {
315
334
  cmdSel = (cmdSel - 1 + m.length) % m.length;
316
- drawFooter();
335
+ refresh();
317
336
  return;
318
337
  }
319
338
  if (inputHistory.length) {
320
339
  histIdx = histIdx < 0 ? inputHistory.length - 1 : Math.max(0, histIdx - 1);
321
340
  inputBuf = inputHistory[histIdx];
322
- drawFooter();
341
+ refresh();
323
342
  }
324
343
  return;
325
344
  }
@@ -327,7 +346,7 @@ export async function runTui() {
327
346
  const m = cmdMatches();
328
347
  if (m.length) {
329
348
  cmdSel = (cmdSel + 1) % m.length;
330
- drawFooter();
349
+ refresh();
331
350
  return;
332
351
  }
333
352
  if (histIdx >= 0) {
@@ -338,7 +357,7 @@ export async function runTui() {
338
357
  }
339
358
  else
340
359
  inputBuf = inputHistory[histIdx];
341
- drawFooter();
360
+ refresh();
342
361
  }
343
362
  return;
344
363
  }
@@ -348,7 +367,7 @@ export async function runTui() {
348
367
  if (m.length) {
349
368
  inputBuf = m[Math.min(cmdSel, m.length - 1)].name + ' ';
350
369
  cmdSel = 0;
351
- drawFooter();
370
+ refresh();
352
371
  }
353
372
  return;
354
373
  }
@@ -359,7 +378,7 @@ export async function runTui() {
359
378
  inputBuf = '';
360
379
  histIdx = -1;
361
380
  if (!v) {
362
- drawFooter();
381
+ refresh();
363
382
  return;
364
383
  }
365
384
  // Slash komutu: tam eşleşme yoksa menüde SEÇİLİ olanı kullan
@@ -395,19 +414,19 @@ export async function runTui() {
395
414
  if (key && key.name === 'backspace') {
396
415
  inputBuf = inputBuf.slice(0, -1);
397
416
  cmdSel = 0;
398
- drawFooter();
417
+ refresh();
399
418
  return;
400
419
  }
401
420
  if (key && key.name === 'escape') {
402
421
  inputBuf = '';
403
- drawFooter();
422
+ refresh();
404
423
  return;
405
424
  }
406
425
  // sadece gerçek yazdırılabilir karakter (her zaman → cevap üretilirken bile type-ahead)
407
426
  if (str && !key?.ctrl && !key?.meta && !str.startsWith('\x1b') && !/[\x00-\x1f]/.test(str)) {
408
427
  inputBuf += str;
409
428
  cmdSel = 0;
410
- drawFooter();
429
+ refresh();
411
430
  }
412
431
  });
413
432
  // resize → settle sonrası her şeyi yeni boyutta yeniden bas (region + banner + footer)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.46",
3
+ "version": "1.0.47",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {