zames_pro 2.50.0 → 2.50.1
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/config-menu.js +13 -2
- package/dist/input.js +71 -5
- package/package.json +1 -1
package/dist/config-menu.js
CHANGED
|
@@ -9,11 +9,20 @@
|
|
|
9
9
|
// types): the caller reads and writes values through callbacks. This makes the
|
|
10
10
|
// menu easy to test and reuse.
|
|
11
11
|
import { theme } from './theme.js';
|
|
12
|
+
import { visLen } from './input.js';
|
|
12
13
|
const ESC = String.fromCharCode(27);
|
|
13
14
|
// Strip ANSI sequences to compute the visible length of a line.
|
|
14
15
|
function stripAnsi(s) {
|
|
15
16
|
return s.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, '');
|
|
16
17
|
}
|
|
18
|
+
// Visible COLUMNS of a line (ANSI removed, wide chars counted as 2). Using the
|
|
19
|
+
// code-point count made the block height disagree with the real screen on lines
|
|
20
|
+
// with emoji/wide chars, so the erase moved to the wrong row and the config list
|
|
21
|
+
// duplicated. A row that fills the terminal exactly also triggers autowrap on
|
|
22
|
+
// some terminals, so we keep one column free here as the editor does.
|
|
23
|
+
function visCols(s) {
|
|
24
|
+
return visLen(stripAnsi(s));
|
|
25
|
+
}
|
|
17
26
|
function displayValue(field, value, t) {
|
|
18
27
|
if (value === undefined)
|
|
19
28
|
return t('cfg.menu.default');
|
|
@@ -91,9 +100,11 @@ export function runConfigMenu(opts) {
|
|
|
91
100
|
// (accounting for wrapping by terminal width), otherwise on narrow
|
|
92
101
|
// terminals you get "garbage" from unerased tails.
|
|
93
102
|
const cols = output.columns || 80;
|
|
103
|
+
// Reserve the last column like the editor does: a row that fills the
|
|
104
|
+
// terminal exactly can autowrap, which desyncs the block height.
|
|
105
|
+
const usable = Math.max(1, cols - 1);
|
|
94
106
|
const rowsOf = (s) => {
|
|
95
|
-
|
|
96
|
-
return Math.max(1, Math.ceil(len / Math.max(1, cols)));
|
|
107
|
+
return Math.max(1, Math.ceil(visCols(s) / usable));
|
|
97
108
|
};
|
|
98
109
|
if (lastRows > 0) {
|
|
99
110
|
output.write(ESC + '[' + lastRows + 'A');
|
package/dist/input.js
CHANGED
|
@@ -157,7 +157,16 @@ export function charWidth(cp) {
|
|
|
157
157
|
return 1;
|
|
158
158
|
}
|
|
159
159
|
export function layoutInput(promptStr, buf, cursor, cols) {
|
|
160
|
-
|
|
160
|
+
// Reserve the LAST column. If a printed row reaches exactly `cols` columns,
|
|
161
|
+
// terminals with autowrap (Tabby, iTerm, Windows Terminal, xterm's default
|
|
162
|
+
// deferred wrap) either wrap the cursor to the next line or leave it in the
|
|
163
|
+
// last cell with a "pending wrap". In both cases the editor's row count
|
|
164
|
+
// disagrees with the real screen by one, so the cursor/erase arithmetic in
|
|
165
|
+
// `_renderInputOnly`/`_writeBlock` walks one row too far and the input line
|
|
166
|
+
// climbs. This was most visible in an EMPTY chat with a single long,
|
|
167
|
+
// space-less line (which fills the row exactly) and when moving the cursor
|
|
168
|
+
// with the arrow keys.
|
|
169
|
+
const width = Math.max(2, (Number(cols) || 80) - 1);
|
|
161
170
|
const promptW = visLen(promptStr);
|
|
162
171
|
const pad = ' '.repeat(promptW);
|
|
163
172
|
const chars = Array.from(String(buf));
|
|
@@ -324,6 +333,13 @@ export class LineEditor {
|
|
|
324
333
|
// keeping the status untouched removes that flicker. Cleared whenever the
|
|
325
334
|
// status genuinely changes so the cache can never go stale.
|
|
326
335
|
_lastStatusBlock;
|
|
336
|
+
// Rows occupied by the WHOLE block (status + input + hints) at the last
|
|
337
|
+
// write. When the block SHRINKS (the pause status disappears, the input
|
|
338
|
+
// unwraps) the erase leaves the cursor at the old block top; printing a
|
|
339
|
+
// shorter block then ends above the screen bottom, so the footer floated with
|
|
340
|
+
// blank rows below it. Padding the redraw with (prev - new) newlines pushes
|
|
341
|
+
// it back down to the bottom (see _padShrink).
|
|
342
|
+
_blockRows;
|
|
327
343
|
_wasRaw;
|
|
328
344
|
_onData;
|
|
329
345
|
// On SIGWINCH we redraw the whole block so the layout follows the new
|
|
@@ -411,6 +427,7 @@ export class LineEditor {
|
|
|
411
427
|
this._thinkBase = '';
|
|
412
428
|
this._sendState = '';
|
|
413
429
|
this._lastStatusBlock = '';
|
|
430
|
+
this._blockRows = 0;
|
|
414
431
|
this._wasRaw = false;
|
|
415
432
|
this._onData = (b) => this._handle(b);
|
|
416
433
|
this._resizeTimer = null;
|
|
@@ -665,8 +682,20 @@ export class LineEditor {
|
|
|
665
682
|
stdin.resume();
|
|
666
683
|
process.stdout.write(ESC + '[?2004h');
|
|
667
684
|
stdin.on('data', this._onData);
|
|
685
|
+
// `pause()` erased our block and handed the terminal to a foreign UI (the
|
|
686
|
+
// /config menu), which may leave the cursor ANYWHERE — after Esc the menu
|
|
687
|
+
// clears its block and parks the cursor at the TOP of the screen. Without
|
|
688
|
+
// re-pinning, the block would be redrawn up there and every later repaint
|
|
689
|
+
// would keep it glued to the top. Re-fill the viewport and forget the
|
|
690
|
+
// stale block height so the editor lands at the bottom again.
|
|
691
|
+
this._lastStatusBlock = '';
|
|
692
|
+
this._blockRows = 0;
|
|
693
|
+
// Start from a KNOWN row: the foreign UI may have parked the cursor
|
|
694
|
+
// anywhere (the /config menu leaves it near the top). From home, padding
|
|
695
|
+
// lands exactly on the last screen row.
|
|
696
|
+
process.stdout.write(ESC + '[H');
|
|
697
|
+
this._padToBottom();
|
|
668
698
|
this._writeBlock();
|
|
669
|
-
this._render();
|
|
670
699
|
}
|
|
671
700
|
// Lock input while a long operation runs (chat resume/open, /new,
|
|
672
701
|
// self-review). The line stays visible but text input and Enter are ignored,
|
|
@@ -865,6 +894,12 @@ export class LineEditor {
|
|
|
865
894
|
NL +
|
|
866
895
|
theme.dim(' ' + translate(this.locale)('editor.more', { n: hidden }));
|
|
867
896
|
}
|
|
897
|
+
// The input may have UNWRAPPED (fewer rows than before) — push the block
|
|
898
|
+
// back to the bottom BEFORE the new content is printed (the padding is
|
|
899
|
+
// plain newlines at the top of the block, which scroll the screen).
|
|
900
|
+
const blockRowsOnly = top + layOnly.rows.length + linesBelowOnly;
|
|
901
|
+
this._padShrink(blockRowsOnly, this._blockRows);
|
|
902
|
+
this._blockRows = blockRowsOnly;
|
|
868
903
|
process.stdout.write(outOnly);
|
|
869
904
|
const upOnly = layOnly.rows.length - 1 - layOnly.cursorRow + linesBelowOnly;
|
|
870
905
|
if (upOnly > 0)
|
|
@@ -899,13 +934,20 @@ export class LineEditor {
|
|
|
899
934
|
NL +
|
|
900
935
|
theme.dim(' ' + translate(this.locale)('editor.more', { n: hidden }));
|
|
901
936
|
}
|
|
937
|
+
const linesBelow = shown.length
|
|
938
|
+
? shown.length + (sugg.length > shown.length ? 1 : 0)
|
|
939
|
+
: 0;
|
|
940
|
+
// If the block SHRANK (the pause status went away, the input unwrapped),
|
|
941
|
+
// the erase left the cursor at the old block top; printing a shorter block
|
|
942
|
+
// would end above the screen bottom and the footer would float with blank
|
|
943
|
+
// rows below it. Padding with the difference pushes it back to the bottom.
|
|
944
|
+
const blockRows = top + lay.rows.length + linesBelow;
|
|
945
|
+
this._padShrink(blockRows, this._blockRows);
|
|
946
|
+
this._blockRows = blockRows;
|
|
902
947
|
process.stdout.write(out);
|
|
903
948
|
const lastRow = lay.rows.length - 1;
|
|
904
949
|
// Move the cursor up: first to the input line within lay, then further by
|
|
905
950
|
// the hint lines (if any) — the cursor must sit on the input.
|
|
906
|
-
const linesBelow = shown.length
|
|
907
|
-
? shown.length + (sugg.length > shown.length ? 1 : 0)
|
|
908
|
-
: 0;
|
|
909
951
|
const up = lastRow - lay.cursorRow + linesBelow;
|
|
910
952
|
if (up > 0)
|
|
911
953
|
process.stdout.write(ESC + '[' + up + 'A');
|
|
@@ -915,6 +957,19 @@ export class LineEditor {
|
|
|
915
957
|
this.rendered = true;
|
|
916
958
|
this.cursorRowFromTop = top + lay.cursorRow;
|
|
917
959
|
}
|
|
960
|
+
// Push the redrawn block back down to the bottom when it got SHORTER than
|
|
961
|
+
// the previous one. `_eraseBlock`/`_eraseInputOnly` put the cursor at the old
|
|
962
|
+
// block top, so emitting (prev - next) newlines scrolls the screen just
|
|
963
|
+
// enough for the new, shorter block to end at the screen bottom again. See
|
|
964
|
+
// _blockRows.
|
|
965
|
+
_padShrink(nextRows, prevRows) {
|
|
966
|
+
if (prevRows <= nextRows)
|
|
967
|
+
return;
|
|
968
|
+
const rows = process.stdout.rows || 24;
|
|
969
|
+
const pad = Math.min(prevRows - nextRows, Math.max(0, rows - 1));
|
|
970
|
+
if (pad > 0)
|
|
971
|
+
process.stdout.write(NL.repeat(pad));
|
|
972
|
+
}
|
|
918
973
|
// Public repaint hook: called when an external state that the status line
|
|
919
974
|
// reads (e.g. the toggle icons) changed, so it can be redrawn immediately.
|
|
920
975
|
refreshStatus() {
|
|
@@ -933,6 +988,10 @@ export class LineEditor {
|
|
|
933
988
|
process.stdout.write(ESC + '[2J' + ESC + '[H');
|
|
934
989
|
this.rendered = false;
|
|
935
990
|
this._lastStatusBlock = '';
|
|
991
|
+
// We are about to re-pin the block to the bottom ourselves, so forget the
|
|
992
|
+
// previous block height — otherwise the shrink padding would add extra
|
|
993
|
+
// rows on top of the padding below.
|
|
994
|
+
this._blockRows = 0;
|
|
936
995
|
// Re-pin the block to the bottom after the screen was cleared.
|
|
937
996
|
this._padToBottom();
|
|
938
997
|
this._writeBlock();
|
|
@@ -973,6 +1032,13 @@ export class LineEditor {
|
|
|
973
1032
|
NL +
|
|
974
1033
|
theme.dim(' ' + translate(this.locale)('editor.more', { n: hidden }));
|
|
975
1034
|
}
|
|
1035
|
+
// A buffer change can UNWRAP the input (backspace on a wrapped line), so
|
|
1036
|
+
// the block can get shorter here too — push it back to the bottom BEFORE
|
|
1037
|
+
// the new content is printed (the padding is plain newlines at the top of
|
|
1038
|
+
// the block, which scroll the screen).
|
|
1039
|
+
const blockRows = this._statusTop + lay.rows.length + linesBelow;
|
|
1040
|
+
this._padShrink(blockRows, this._blockRows);
|
|
1041
|
+
this._blockRows = blockRows;
|
|
976
1042
|
process.stdout.write(out);
|
|
977
1043
|
const up = lay.rows.length - 1 - lay.cursorRow + linesBelow;
|
|
978
1044
|
if (up > 0)
|