zames_pro 2.29.16 → 2.32.0

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/input.js CHANGED
@@ -260,6 +260,11 @@ export class LineEditor {
260
260
  _lastStatusBlock;
261
261
  _wasRaw;
262
262
  _onData;
263
+ // On SIGWINCH we redraw the whole block so the layout follows the new
264
+ // terminal width. Debounced: a drag fires many resize events. `_onResize`
265
+ // is stored so it can be removed in dispose().
266
+ _onResize;
267
+ _resizeTimer;
263
268
  // History of sent messages (for the up/down arrows).
264
269
  history;
265
270
  _histIndex;
@@ -304,6 +309,10 @@ export class LineEditor {
304
309
  // A callback the editor calls to fetch the CURRENT token count before every
305
310
  // status render, so the status line stays fresh without the caller polling.
306
311
  onContextQuery;
312
+ // The context window size used for the fill percentage and its color. Kept
313
+ // as a mutable field (set from config.ui.contextLimit) so the status bar
314
+ // reflects the real limit instead of a hardcoded constant.
315
+ contextLimit;
307
316
  constructor({ prompt = '> ', commands = [], locale = 'ru', } = {}) {
308
317
  this.locale = locale;
309
318
  this.promptStr = prompt;
@@ -327,6 +336,26 @@ export class LineEditor {
327
336
  this._lastStatusBlock = '';
328
337
  this._wasRaw = false;
329
338
  this._onData = (b) => this._handle(b);
339
+ this._resizeTimer = null;
340
+ this._onResize = () => {
341
+ // Debounce: a window drag fires a burst of SIGWINCH events. We wait a
342
+ // short moment, then redraw the block at the NEW width.
343
+ if (this._resizeTimer)
344
+ clearTimeout(this._resizeTimer);
345
+ this._resizeTimer = setTimeout(() => {
346
+ this._resizeTimer = null;
347
+ // Repaint ONLY the status + input block. We deliberately do NOT clear
348
+ // the whole viewport here: the operator's scrollback (the tool-call
349
+ // lines and answers printed above the input) must stay on screen.
350
+ // `_eraseBlock` erases from the top of the block DOWN (CR + ESC[J),
351
+ // so everything above it is preserved; then _writeBlock redraws the
352
+ // block at the new width. A slight leftover is possible at extreme
353
+ // width changes, but losing the history is far worse.
354
+ this._render();
355
+ }, 120);
356
+ if (this._resizeTimer.unref)
357
+ this._resizeTimer.unref();
358
+ };
330
359
  this.history = [];
331
360
  this._histIndex = 0;
332
361
  this._histDraft = '';
@@ -343,6 +372,7 @@ export class LineEditor {
343
372
  this.contextTokens = null;
344
373
  this.thinkingEnabled = false;
345
374
  this.searchEnabled = false;
375
+ this.contextLimit = CONTEXT_LIMIT;
346
376
  this.onToggleQuery = null;
347
377
  this.onContextQuery = null;
348
378
  }
@@ -370,7 +400,9 @@ export class LineEditor {
370
400
  const n = this.onContextQuery();
371
401
  this.contextTokens = typeof n === 'number' ? n : null;
372
402
  this.contextStatus =
373
- n === null || n === undefined ? null : formatTokenStatus(n) || null;
403
+ n === null || n === undefined
404
+ ? null
405
+ : formatTokenStatus(n, this.contextLimit) || null;
374
406
  }
375
407
  catch {
376
408
  // A broken callback must never break the render.
@@ -384,7 +416,7 @@ export class LineEditor {
384
416
  const ctx = this._contextForRender();
385
417
  if (!ctx)
386
418
  return '';
387
- const level = tokenStatusLevel(this.contextTokens);
419
+ const level = tokenStatusLevel(this.contextTokens, this.contextLimit);
388
420
  if (level === 'high')
389
421
  return theme.error(ctx);
390
422
  if (level === 'warn')
@@ -392,11 +424,11 @@ export class LineEditor {
392
424
  return theme.success(ctx);
393
425
  }
394
426
  // Icons for the DeepSeek toggles: 🧠 "Deep thinking" and 🌐 "Smart search",
395
- // shown before the context counter. Only the ENABLED toggle is shown, in
396
- // green. A hidden icon means OFF — this does not rely on the terminal
397
- // honoring the ANSI color (it ignores it for emoji), so there is no
398
- // ambiguity. U+FE0E forces the monochrome TEXT presentation so the green is
399
- // actually visible.
427
+ // shown before the context counter. Only the ENABLED toggles are shown, so
428
+ // the state is unambiguous WITHOUT relying on the terminal coloring the
429
+ // emoji: an icon present = ON, absent = OFF. Emoji are (almost) never
430
+ // colored by ANSI, so a "dim" vs "green" pair of the SAME emoji would look
431
+ // identical — that is why the disabled icon is hidden instead.
400
432
  _toggleIcons() {
401
433
  const TEXT = '\uFE0E';
402
434
  const parts = [];
@@ -466,12 +498,34 @@ export class LineEditor {
466
498
  stdin.resume();
467
499
  process.stdout.write(ESC + '[?2004h');
468
500
  stdin.on('data', this._onData);
501
+ // Redraw on terminal resize so the layout follows the new width instead
502
+ // of leaving artifacts from the old one.
503
+ process.stdout.on('resize', this._onResize);
504
+ // Pin the input block to the BOTTOM of the terminal. A fresh terminal
505
+ // starts the cursor at the top, so without this the input line sits at
506
+ // the top and only "descends" as output accumulates. Filling the viewport
507
+ // with newlines first makes the block start at the bottom, and every
508
+ // later printAbove() pushes content UP while the input stays put.
509
+ this._padToBottom();
469
510
  this._writeBlock();
470
511
  return true;
471
512
  }
513
+ // Fill the current viewport with empty lines so the next _writeBlock()
514
+ // lands at the BOTTOM of the terminal. Used at start and after clearScreen.
515
+ // We deliberately emit real newlines (not a cursor-position escape) because
516
+ // the block height is not known yet and scrolling keeps the block's own
517
+ // cursor math (`cursorRowFromTop`) correct.
518
+ _padToBottom() {
519
+ const rows = process.stdout.rows || 24;
520
+ if (rows > 1)
521
+ process.stdout.write(NL.repeat(rows - 1));
522
+ }
472
523
  dispose() {
473
524
  const stdin = process.stdin;
474
525
  stdin.removeListener('data', this._onData);
526
+ process.stdout.removeListener('resize', this._onResize);
527
+ if (this._resizeTimer)
528
+ clearTimeout(this._resizeTimer);
475
529
  process.stdout.write(ESC + '[?2004l');
476
530
  this._stopDots();
477
531
  if (this.rendered)
@@ -529,9 +583,16 @@ export class LineEditor {
529
583
  setContextStatus(tokens) {
530
584
  this.contextTokens =
531
585
  typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
532
- this.contextStatus = formatTokenStatus(tokens) || null;
586
+ this.contextStatus = formatTokenStatus(tokens, this.contextLimit) || null;
533
587
  this._render();
534
588
  }
589
+ // Update the context window size (from config.ui.contextLimit). The next
590
+ // render recomputes the percentage and its color with the new limit.
591
+ setContextLimit(limit) {
592
+ if (typeof limit === 'number' && Number.isFinite(limit) && limit > 0) {
593
+ this.contextLimit = limit;
594
+ }
595
+ }
535
596
  // Update the interface language (labels: hint, answer marker, pause).
536
597
  setLocale(locale) {
537
598
  this.locale = locale;
@@ -735,6 +796,19 @@ export class LineEditor {
735
796
  this._eraseBlock();
736
797
  this._writeBlock();
737
798
  }
799
+ // Full-screen clear (like /clear in a shell) followed by a fresh repaint of
800
+ // the status + input block. Used by /new and /resume so a new/resumed chat
801
+ // starts on a clean terminal instead of under pages of the previous chat.
802
+ // We do NOT clear the scrollback here: the operator may still want it.
803
+ clearScreen() {
804
+ this._eraseBlock();
805
+ process.stdout.write(ESC + '[2J' + ESC + '[H');
806
+ this.rendered = false;
807
+ this._lastStatusBlock = '';
808
+ // Re-pin the block to the bottom after the screen was cleared.
809
+ this._padToBottom();
810
+ this._writeBlock();
811
+ }
738
812
  // Repaint ONLY the input rows, leaving the status/spinner block untouched.
739
813
  // Used for buffer/cursor changes (typing, arrows, backspace, …): the status
740
814
  // line has its OWN animation timer, and repainting it on every keystroke
@@ -874,7 +948,8 @@ export class LineEditor {
874
948
  this.setStatus('');
875
949
  }
876
950
  toolCall(name, args) {
877
- const preview = safeJson(args).slice(0, 120);
951
+ const full = safeJson(args);
952
+ const preview = full.slice(0, 120) + (full.length > 120 ? ' …' : '');
878
953
  this.printAbove(theme.tool('🔧 ' + name) + ' ' + theme.dim(preview));
879
954
  // A tool may run for a long time (Bash, npm test, MCP). Without an active
880
955
  // animation the operator sees a frozen screen and cannot tell work is in
@@ -887,18 +962,36 @@ export class LineEditor {
887
962
  toolResult(result) {
888
963
  this.stop();
889
964
  const text = typeof result === 'string' ? result : safeJson(result);
890
- const preview = text.slice(0, 200).split(NL).join(' ↵ ');
965
+ const preview = text.slice(0, 200).split(NL).join(' ↵ ') + (text.length > 200 ? ' …' : '');
891
966
  this.printAbove(theme.toolResult(' → ' + preview));
892
967
  }
893
968
  assistant(msg) {
894
969
  this.stop();
895
970
  const rendered = renderMarkdown(msg);
971
+ // Show the used context size at the END of the answer too. During
972
+ // generation it lives in the status line, but ui.stop() clears that right
973
+ // before this call, so the operator lost the number exactly when the
974
+ // answer arrived. Read it fresh via onContextQuery and append it dimmed
975
+ // under the answer divider.
976
+ let ctxLine = '';
977
+ if (this.onContextQuery) {
978
+ try {
979
+ const n = this.onContextQuery();
980
+ const txt = formatTokenStatus(n, this.contextLimit);
981
+ if (txt)
982
+ ctxLine = NL + theme.dim(txt);
983
+ }
984
+ catch {
985
+ // never break the render
986
+ }
987
+ }
896
988
  this.printAbove(NL +
897
989
  theme.assistant(translate(this.locale)('editor.answer')) +
898
990
  NL +
899
991
  rendered +
900
992
  NL +
901
- theme.dim('─'.repeat(60)));
993
+ theme.dim('─'.repeat(60)) +
994
+ ctxLine);
902
995
  }
903
996
  // Warning to the operator (e.g. the agent stopped suspiciously).
904
997
  // Printed above the input line without overwriting it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.29.16",
3
+ "version": "2.32.0",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {