wormclaude 1.0.51 → 1.0.53

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.51';
19
+ export const VERSION = '1.0.53';
package/dist/tui.js CHANGED
@@ -67,8 +67,8 @@ export async function runTui() {
67
67
  let streamPreview = ''; // canlı akış önizlemesi (footer'da)
68
68
  const PREVIEW_LINES = 6; // akışta sabit önizleme satırı (yükseklik stabil → titreme yok)
69
69
  const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
70
- // Giriş kutusunu genişliğe göre ÇOK SATIRA sar (uzun mesaj/kod sağa taşmaz, alta iner).
71
- const MAX_INPUT_LINES = 10;
70
+ // Giriş kutusunu genişliğe göre ÇOK SATIRA sar; çok uzunsa (yapıştırma) KISALTIP özet gösterir.
71
+ const MAX_INPUT_LINES = 6;
72
72
  const inputBoxLines = (W) => {
73
73
  const inner = Math.max(4, W - 2); // "✶ " önek payı
74
74
  const wrapped = [];
@@ -82,28 +82,26 @@ export async function runTui() {
82
82
  cur += ch;
83
83
  }
84
84
  wrapped.push(cur);
85
- const shown = wrapped.length > MAX_INPUT_LINES ? wrapped.slice(-MAX_INPUT_LINES) : wrapped; // çok uzunsa sonu göster
86
- return shown.map((ln, i) => (i === 0 ? paint('✶ ', theme.redBright, true) : ' ') + paint(ln, theme.white) + (i === shown.length - 1 ? paint('▌', theme.greyDim) : ''));
85
+ if (wrapped.length <= MAX_INPUT_LINES) {
86
+ return wrapped.map((ln, i) => (i === 0 ? paint('✶ ', theme.redBright, true) : ' ') + paint(ln, theme.white) + (i === wrapped.length - 1 ? paint('▌', theme.greyDim) : ''));
87
+ }
88
+ // Çok uzun (büyük yapıştırma): son birkaç satır + üstte özet — footer'ı doldurmaz.
89
+ const extra = wrapped.length - (MAX_INPUT_LINES - 1);
90
+ const tail = wrapped.slice(-(MAX_INPUT_LINES - 1));
91
+ const head = paint(`✶ …(+${extra} satır · ${inputBuf.length} karakter, Enter ile gönder)`, theme.greyDim);
92
+ return [head, ...tail.map((ln, i) => ' ' + paint(ln, theme.white) + (i === tail.length - 1 ? paint('▌', theme.greyDim) : ''))];
87
93
  };
88
94
  // Footer yüksekliği DİNAMİK: izin=4; değilse (durum/menü) + çizgi + giriş-satırları + çizgi.
89
- let prevFH = 4;
90
- const footerHeight = () => {
91
- if (perm)
92
- return 4;
93
- if (ask)
94
- return ask.options.length + 3; // çizgi + soru + seçenekler... aslında: line+q+opts+line
95
- const il = inputBoxLines(Math.max(8, cols())).length;
96
- if (busy && streamPreview)
97
- return PREVIEW_LINES + 2 + il; // canlı akış önizlemesi
98
- const m = cmdMatches();
99
- const statusLines = m.length ? Math.min(8, m.length) : 1;
100
- return statusLines + 2 + il;
101
- };
102
- // Yükseklik değiştiyse scroll-region'ı + içeriği yeniden bas; değişmediyse sadece footer'ı çiz.
103
- const refresh = () => { const fh = footerHeight(); if (fh !== prevFH)
104
- redrawAll();
105
- else
106
- drawFooter(); };
95
+ // SABİT rezerve footer alanı (scroll region bir kez ayarlanır → churn/duplicate yok). Footer bu
96
+ // alanın DİBİNE sabitlenir; küçükse üstü boşaltılır. Menü/önizleme/giriş bu sınıra göre kırpılır.
97
+ const FOOTER_MAX = () => Math.min(11, Math.max(5, rows() - 6));
98
+ // refresh = sadece footer'ı çiz (region sabit; içerik yeniden BASILMAZ → duplicate olmaz).
99
+ const refresh = () => drawFooter();
100
+ // DEBOUNCE: hızlı yazma/yapıştırma akınında her tuşta DEĞİL, tick başına BİR KEZ çiz. Yoksa
101
+ // her karakter tam footer redraw'u tetikleyip render'ı kuyruğa biriktiriyor (elini çeksen de gidiyor).
102
+ let _fpending = false;
103
+ const scheduleFooter = () => { if (_fpending)
104
+ return; _fpending = true; setImmediate(() => { _fpending = false; drawFooter(); }); };
107
105
  setToolConfig(config); // araçlar (Write/Bash/Edit…) aynı config'i kullansın
108
106
  // İzin (araç onayı) + soru (AskUserQuestion) durumu
109
107
  let perm = null;
@@ -171,16 +169,15 @@ export async function runTui() {
171
169
  return [...L, ...R].map((r) => fit(cell(r), W)); // dar ekran → alt alta
172
170
  return [0, 1, 2].map((i) => fit(padVis(cell(L[i]), colW) + cell(R[i]), W)); // geniş → 2 sütun
173
171
  }
174
- // ── Scroll region: üst = içerik (kayar), alt = sabit footer (dinamik yükseklik) ──
172
+ // ── Scroll region: üst=içerik, alt=SABİT rezerve footer alanı. İmleci koru (DECSTBM home'a alır). ──
175
173
  function setRegion() {
176
- const bottom = Math.max(1, rows() - footerHeight() - 1); // footer üstünde 1 boş satır
177
- process.stdout.write(`\x1b[1;${bottom}r`);
174
+ const bottom = Math.max(1, rows() - FOOTER_MAX() - 1);
175
+ process.stdout.write(`\x1b7\x1b[1;${bottom}r\x1b8`); // save · region · restore (içerik imleci bozulmaz)
178
176
  }
179
- // ── Sabit footeren alta mutlak konumla çiz (imleci kaydet/geri yükle içerik bozulmaz) ──
177
+ // ── Footerrezerve alanın DİBİNE çiz; rezerve alanın geri kalanını boşalt (eski menü kalmaz). ──
180
178
  function drawFooter() {
181
179
  const W = Math.max(8, cols());
182
- const fh = footerHeight();
183
- const start = rows() - fh + 1;
180
+ const R = rows(), FM = FOOTER_MAX();
184
181
  const line = paint('─'.repeat(W), theme.red);
185
182
  let body;
186
183
  if (perm) {
@@ -227,14 +224,19 @@ export async function runTui() {
227
224
  body = [status, line, ...inputLines, line];
228
225
  }
229
226
  }
230
- let out = '\x1b7'; // imleci kaydet
231
- body.forEach((l, i) => { out += `\x1b[${start + i};1H\x1b[2K` + fit(l, W); });
232
- out += '\x1b8'; // imleci geri yükle (içerik alanı)
227
+ if (body.length > FM)
228
+ body = body.slice(body.length - FM); // rezerve alanı aşma (giriş hep görünür)
229
+ const bodyStart = R - body.length + 1; // footer'ı ekranın DİBİNE sabitle
230
+ const reservedTop = R - FM + 1;
231
+ let out = '\x1b7'; // imleci kaydet (içerik alanı)
232
+ for (let r = reservedTop; r < bodyStart; r++)
233
+ out += `\x1b[${r};1H\x1b[2K`; // üst rezerve satırları boşalt
234
+ body.forEach((l, i) => { out += `\x1b[${bodyStart + i};1H\x1b[2K` + fit(l, W); });
235
+ out += '\x1b8'; // imleci geri yükle
233
236
  process.stdout.write(out);
234
237
  }
235
- // ── Her şeyi yeni boyutta yeniden bas (banner/kod responsive); içerik bölgesine ──
238
+ // ── Her şeyi yeni boyutta yeniden bas (SADECE açılış/resize/clear; footer değişiminde DEĞİL). ──
236
239
  function redrawAll() {
237
- prevFH = footerHeight(); // mevcut footer yüksekliğini kaydet (refresh karşılaştırması için)
238
240
  setRegion();
239
241
  process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
240
242
  const W = cols();
@@ -567,7 +569,7 @@ export async function runTui() {
567
569
  if (key && key.name === 'backspace') {
568
570
  inputBuf = inputBuf.slice(0, -1);
569
571
  cmdSel = 0;
570
- refresh();
572
+ scheduleFooter();
571
573
  return;
572
574
  }
573
575
  if (key && key.name === 'escape') {
@@ -579,7 +581,7 @@ export async function runTui() {
579
581
  if (str && !key?.ctrl && !key?.meta && !str.startsWith('\x1b') && !/[\x00-\x1f]/.test(str)) {
580
582
  inputBuf += str;
581
583
  cmdSel = 0;
582
- refresh();
584
+ scheduleFooter();
583
585
  }
584
586
  });
585
587
  // 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.51",
3
+ "version": "1.0.53",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {