zames_pro 2.68.0 → 2.69.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/CHANGELOG.md +24 -0
- package/dist/i18n/config.js +2 -2
- package/dist/input/layout.js +55 -0
- package/dist/input.js +12 -6
- package/dist/markdown.js +7 -9
- package/dist/spinner.js +6 -4
- package/dist/theme.js +6 -7
- package/dist/width.js +25 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.69.0] - 2026-10-08
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **ui:** ONE content width for every block, from a new single source of
|
|
15
|
+
truth `src/width.ts`. The model answers, the echo of the operator text,
|
|
16
|
+
the tool previews, the service warnings and the divider now all stop at
|
|
17
|
+
the same margin (`min(ui.answerWidth, terminal - 1)`, capped at 100 by
|
|
18
|
+
default). Before, answers and the divider were capped at 100 while the
|
|
19
|
+
echoed operator text and warnings ran to the full terminal width — the
|
|
20
|
+
mismatch looked ragged after a resize.
|
|
21
|
+
- **config:** `ui.answerWidth` now sets the SHARED content width (label
|
|
22
|
+
updated). `0` = auto (terminal width, capped at 100).
|
|
23
|
+
|
|
24
|
+
## [2.68.1] - 2026-10-08
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **ui:** long service messages (send failures, rate limits) are now
|
|
29
|
+
word-wrapped to the same margin as the tool previews (`cols - 1`).
|
|
30
|
+
Before, they were printed at the full terminal width and the terminal
|
|
31
|
+
wrapped them mid-word, so some lines ran to the edge and some stopped
|
|
32
|
+
short. Applies to `LineEditor.warning()` and the non-TTY spinner.
|
|
33
|
+
|
|
10
34
|
## [2.68.0] - 2026-10-08
|
|
11
35
|
|
|
12
36
|
### Changed
|
package/dist/i18n/config.js
CHANGED
|
@@ -15,8 +15,8 @@ export const configMessages = {
|
|
|
15
15
|
en: 'Interface and agent reply language',
|
|
16
16
|
},
|
|
17
17
|
'cfg.f.ui_answerWidth': {
|
|
18
|
-
ru: '
|
|
19
|
-
en: '
|
|
18
|
+
ru: 'Ширина контента в колонках: ответы, ввод, инструменты (0 — авто, до 100)',
|
|
19
|
+
en: 'Content width in columns: answers, input, tools (0 = auto, capped at 100)',
|
|
20
20
|
},
|
|
21
21
|
'cfg.f.maxIterations': {
|
|
22
22
|
ru: 'Лимит итераций на задачу',
|
package/dist/input/layout.js
CHANGED
|
@@ -154,6 +154,61 @@ export function charWidth(cp) {
|
|
|
154
154
|
// appending an ellipsis when it was cut. Used for one-line previews (tool call
|
|
155
155
|
// args, tool results) so a long argument cannot print a line wider than the
|
|
156
156
|
// terminal — which made some terminals show a horizontal scrollbar.
|
|
157
|
+
// Word-wrap a PLAIN (no-ANSI) string to at most `maxCols` visible columns per
|
|
158
|
+
// line. Used for service messages printed above the input (warnings): the
|
|
159
|
+
// terminal's OWN wrap breaks at the full width and mid-word, while our previews
|
|
160
|
+
// stop at cols-1 — the mismatch looked ragged. Wrapping here keeps every line
|
|
161
|
+
// inside the same margin. A single word longer than the width is hard-split so
|
|
162
|
+
// it never overflows.
|
|
163
|
+
export function wrapToWidth(s, maxCols) {
|
|
164
|
+
const width = Math.max(1, Math.floor(maxCols));
|
|
165
|
+
const text = String(s ?? '');
|
|
166
|
+
const out = [];
|
|
167
|
+
for (const raw of text.split(NL)) {
|
|
168
|
+
if (raw === '') {
|
|
169
|
+
out.push('');
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
let line = '';
|
|
173
|
+
let lineW = 0;
|
|
174
|
+
for (const word of raw.split(' ')) {
|
|
175
|
+
const w = visLen(word);
|
|
176
|
+
// Hard-split a word longer than a whole line.
|
|
177
|
+
if (w > width) {
|
|
178
|
+
if (line) {
|
|
179
|
+
out.push(line);
|
|
180
|
+
}
|
|
181
|
+
let piece = '';
|
|
182
|
+
let pieceW = 0;
|
|
183
|
+
for (const ch of word) {
|
|
184
|
+
const cw = charWidth(ch.codePointAt(0));
|
|
185
|
+
if (pieceW + cw > width) {
|
|
186
|
+
out.push(piece);
|
|
187
|
+
piece = '';
|
|
188
|
+
pieceW = 0;
|
|
189
|
+
}
|
|
190
|
+
piece += ch;
|
|
191
|
+
pieceW += cw;
|
|
192
|
+
}
|
|
193
|
+
line = piece;
|
|
194
|
+
lineW = pieceW;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const sep = line ? 1 : 0;
|
|
198
|
+
if (lineW + sep + w > width) {
|
|
199
|
+
out.push(line);
|
|
200
|
+
line = word;
|
|
201
|
+
lineW = w;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
line = line ? line + ' ' + word : word;
|
|
205
|
+
lineW += sep + w;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
out.push(line);
|
|
209
|
+
}
|
|
210
|
+
return out.join(NL);
|
|
211
|
+
}
|
|
157
212
|
export function truncateToWidth(s, maxCols) {
|
|
158
213
|
const width = Math.max(1, Math.floor(maxCols));
|
|
159
214
|
const text = String(s ?? '');
|
package/dist/input.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { theme, divider } from './theme.js';
|
|
2
2
|
import { renderMarkdown } from './markdown.js';
|
|
3
|
+
import { contentWidth } from './width.js';
|
|
3
4
|
import { randomThinkingPhrase, stripEllipsis, DOTS, renderDots, } from './spinner.js';
|
|
4
5
|
import { translate } from './i18n.js';
|
|
5
6
|
import { AttachmentStore, parseImagePaste, looksLikeFilePath, } from './attachments.js';
|
|
6
|
-
import { NL, CR, ESC, PASTE_START, PASTE_END, SUGGEST_PAGE, isSubsequence, pasteReplacement, expandPastes, safeJson, visRows, visLen, charWidth, layoutInput, formatTokenStatus, tokenStatusLevel, truncateToWidth, CONTEXT_LIMIT, } from './input/layout.js';
|
|
7
|
+
import { NL, CR, ESC, PASTE_START, PASTE_END, SUGGEST_PAGE, isSubsequence, pasteReplacement, expandPastes, safeJson, visRows, visLen, charWidth, layoutInput, formatTokenStatus, tokenStatusLevel, truncateToWidth, wrapToWidth, CONTEXT_LIMIT, } from './input/layout.js';
|
|
7
8
|
// The pure layout/formatting helpers (visual rows, cursor position, visible
|
|
8
9
|
// width, paste markers, token formatting) live in ./input/layout.js. They are
|
|
9
10
|
// re-exported here so every existing import from './input.js' keeps working,
|
|
10
11
|
// and so tests can import them from either module (C3 step 3).
|
|
11
|
-
export { PASTE_MIN_LINES, SUGGEST_PAGE, isSubsequence, countPasteLines, formatPasteMarker, pasteReplacement, expandPastes, visRows, visLen, charWidth, layoutInput, tokenStatusLevel, formatTokenStatus, formatCompactTokens, truncateToWidth, CONTEXT_LIMIT, CONTEXT_YELLOW_PCT, CONTEXT_RED_PCT, } from './input/layout.js';
|
|
12
|
+
export { PASTE_MIN_LINES, SUGGEST_PAGE, isSubsequence, countPasteLines, formatPasteMarker, pasteReplacement, expandPastes, visRows, visLen, charWidth, layoutInput, tokenStatusLevel, formatTokenStatus, formatCompactTokens, truncateToWidth, wrapToWidth, CONTEXT_LIMIT, CONTEXT_YELLOW_PCT, CONTEXT_RED_PCT, } from './input/layout.js';
|
|
12
13
|
// How long a lone ESC is held before it is treated as the Escape key (and not
|
|
13
14
|
// as the head of a control sequence split across two reads). 40ms is below a
|
|
14
15
|
// human "two Escapes" interval but above typical chunk-split latency.
|
|
@@ -1075,7 +1076,7 @@ export class LineEditor {
|
|
|
1075
1076
|
// Clip by VISIBLE WIDTH, not character count: a long arg line used to print
|
|
1076
1077
|
// wider than the terminal and some terminals then showed a horizontal
|
|
1077
1078
|
// scrollbar. Keep one column free (autowrap safety), same as the status.
|
|
1078
|
-
const maxW =
|
|
1079
|
+
const maxW = contentWidth();
|
|
1079
1080
|
const preview = truncateToWidth(full, maxW);
|
|
1080
1081
|
this.printAbove(theme.tool('🔧 ' + name) + ' ' + theme.dim(preview));
|
|
1081
1082
|
// A tool may run for a long time (Bash, npm test, MCP). Without an active
|
|
@@ -1094,7 +1095,7 @@ export class LineEditor {
|
|
|
1094
1095
|
toolResult(result) {
|
|
1095
1096
|
this.stop();
|
|
1096
1097
|
const text = typeof result === 'string' ? result : safeJson(result);
|
|
1097
|
-
const maxW =
|
|
1098
|
+
const maxW = contentWidth();
|
|
1098
1099
|
const preview = truncateToWidth(text.split(NL).join(' ↵ '), maxW);
|
|
1099
1100
|
this.printAbove(theme.toolResult(' → ' + preview));
|
|
1100
1101
|
}
|
|
@@ -1115,7 +1116,12 @@ export class LineEditor {
|
|
|
1115
1116
|
// Warning to the operator (e.g. the agent stopped suspiciously).
|
|
1116
1117
|
// Printed above the input line without overwriting it.
|
|
1117
1118
|
warning(msg) {
|
|
1118
|
-
|
|
1119
|
+
// Wrap long service messages (send failures, rate limits) to the same
|
|
1120
|
+
// margin the tool previews use, instead of letting the terminal wrap them
|
|
1121
|
+
// at the full width and mid-word — that mismatch looked ragged.
|
|
1122
|
+
const maxW = contentWidth();
|
|
1123
|
+
const wrapped = wrapToWidth('⚠ ' + msg, maxW);
|
|
1124
|
+
this.printAbove(NL + theme.warn(wrapped));
|
|
1119
1125
|
}
|
|
1120
1126
|
// ---------- input ----------
|
|
1121
1127
|
_submit() {
|
|
@@ -1187,7 +1193,7 @@ export class LineEditor {
|
|
|
1187
1193
|
// Attachments collected for this message (images/files). They are handed
|
|
1188
1194
|
// to the browser layer to be attached to the chat.
|
|
1189
1195
|
const attached = this.attachments.items.slice();
|
|
1190
|
-
this.printAbove(theme.user('❯ ') + display);
|
|
1196
|
+
this.printAbove(theme.user('❯ ') + wrapToWidth(display, contentWidth()));
|
|
1191
1197
|
this.pastes = [];
|
|
1192
1198
|
this.attachments.reset();
|
|
1193
1199
|
if (this.onSubmit)
|
package/dist/markdown.js
CHANGED
|
@@ -26,21 +26,19 @@ function highlighter(code, lang) {
|
|
|
26
26
|
return code;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
29
|
+
// The answer width is the SHARED content width (src/width.ts), the same one
|
|
30
|
+
// the operator's echoed text, the tool previews, the warnings and the divider
|
|
31
|
+
// use — so nothing is rendered at a different margin. `setAnswerWidth` is kept
|
|
32
|
+
// as a thin alias for callers that still use the old name.
|
|
33
|
+
import { contentWidth, setContentWidth } from './width.js';
|
|
33
34
|
export function setAnswerWidth(width) {
|
|
34
|
-
|
|
35
|
+
setContentWidth(width);
|
|
35
36
|
}
|
|
36
37
|
export function renderMarkdown(text) {
|
|
37
38
|
if (!text)
|
|
38
39
|
return '';
|
|
39
40
|
try {
|
|
40
|
-
const
|
|
41
|
-
const width = configuredWidth > 0
|
|
42
|
-
? Math.min(configuredWidth, cols)
|
|
43
|
-
: Math.min(cols, 100);
|
|
41
|
+
const width = contentWidth();
|
|
44
42
|
return render(String(text), {
|
|
45
43
|
width,
|
|
46
44
|
theme: theme,
|
package/dist/spinner.js
CHANGED
|
@@ -2,7 +2,8 @@ import ora from 'ora';
|
|
|
2
2
|
import { theme, divider } from './theme.js';
|
|
3
3
|
import { renderMarkdown } from './markdown.js';
|
|
4
4
|
import { translate } from './i18n.js';
|
|
5
|
-
import { truncateToWidth } from './input/layout.js';
|
|
5
|
+
import { truncateToWidth, wrapToWidth } from './input/layout.js';
|
|
6
|
+
import { contentWidth } from './width.js';
|
|
6
7
|
// Phrases for the "agent is working" spinner. Chosen in random order.
|
|
7
8
|
// The text comes from i18n via the key 'spinner.phrases' (strings separated by "|").
|
|
8
9
|
export function randomThinkingPhrase(locale = 'ru') {
|
|
@@ -171,7 +172,7 @@ export function createSpinner(locale = 'ru') {
|
|
|
171
172
|
},
|
|
172
173
|
toolCall: (name, args) => {
|
|
173
174
|
stop();
|
|
174
|
-
const maxW =
|
|
175
|
+
const maxW = contentWidth();
|
|
175
176
|
const preview = truncateToWidth(JSON.stringify(args), maxW);
|
|
176
177
|
console.log(theme.tool('🔧 ' + name), theme.dim(preview));
|
|
177
178
|
// Keep an animated status while the tool runs (Bash/npm/MCP can take a
|
|
@@ -183,7 +184,7 @@ export function createSpinner(locale = 'ru') {
|
|
|
183
184
|
toolResult: (result) => {
|
|
184
185
|
stop();
|
|
185
186
|
const text = typeof result === 'string' ? result : JSON.stringify(result);
|
|
186
|
-
const maxW =
|
|
187
|
+
const maxW = contentWidth();
|
|
187
188
|
const preview = truncateToWidth(text.split(String.fromCharCode(10)).join(' ↵ '), maxW);
|
|
188
189
|
console.log(theme.toolResult(' → ' + preview + String.fromCharCode(10)));
|
|
189
190
|
},
|
|
@@ -199,7 +200,8 @@ export function createSpinner(locale = 'ru') {
|
|
|
199
200
|
},
|
|
200
201
|
warning: (msg) => {
|
|
201
202
|
stop();
|
|
202
|
-
|
|
203
|
+
const maxW = contentWidth();
|
|
204
|
+
console.log(String.fromCharCode(10) + theme.warn(wrapToWidth('⚠ ' + msg, maxW)));
|
|
203
205
|
},
|
|
204
206
|
stop,
|
|
205
207
|
succeed: () => { },
|
package/dist/theme.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { contentWidth } from './width.js';
|
|
2
3
|
// A calm palette for a dark terminal.
|
|
3
4
|
// Muted, slightly "faded" tones without high saturation
|
|
4
5
|
// (in the spirit of Tokyo Night / Nord). The goal is comfortable reading of
|
|
@@ -32,13 +33,11 @@ export const theme = {
|
|
|
32
33
|
bold: chalk.bold,
|
|
33
34
|
};
|
|
34
35
|
// Horizontal rule that separates the model's answer from the next block. It
|
|
35
|
-
// spans the
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const cols = process.stdout.columns || 0;
|
|
41
|
-
const width = Math.max(20, Math.min(max, cols ? cols - 1 : 60));
|
|
36
|
+
// spans the SHARED content width (src/width.ts) so the rule, the answers and
|
|
37
|
+
// the tool previews all line up at the same margin. The `max` argument is kept
|
|
38
|
+
// for a shorter rule in special call sites; by default it is the content width.
|
|
39
|
+
export function divider(max) {
|
|
40
|
+
const width = max && max > 0 ? Math.min(max, contentWidth()) : contentWidth();
|
|
42
41
|
return '─'.repeat(width);
|
|
43
42
|
}
|
|
44
43
|
export default theme;
|
package/dist/width.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Single source of truth for the CONTENT column width used by every part
|
|
2
|
+
// of the terminal UI: the model's answers, the operator's echoed text,
|
|
3
|
+
// tool previews and service warnings. One width for all of them means nothing
|
|
4
|
+
// is printed at the full terminal width while something else stops at 100 --
|
|
5
|
+
// the mismatch looked ragged after a resize.
|
|
6
|
+
//
|
|
7
|
+
// 0 (default) = auto: the terminal width, capped at DEFAULT_CONTENT_WIDTH.
|
|
8
|
+
// A longer line is harder to read and the eye loses the start of the next
|
|
9
|
+
// line on a wide terminal, so there is a cap.
|
|
10
|
+
// > 0 = an explicit cap (still never wider than the terminal).
|
|
11
|
+
export const DEFAULT_CONTENT_WIDTH = 100;
|
|
12
|
+
let configured = 0;
|
|
13
|
+
/** Set the configured content width (0 or a non-positive value = auto). */
|
|
14
|
+
export function setContentWidth(width) {
|
|
15
|
+
configured = typeof width === 'number' && width > 0 ? width : 0;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The effective content width in columns. One column is kept free (autowrap
|
|
19
|
+
* safety), like the status/input layout. Never shorter than 20 columns.
|
|
20
|
+
*/
|
|
21
|
+
export function contentWidth() {
|
|
22
|
+
const cols = process.stdout.columns || 80;
|
|
23
|
+
const cap = configured > 0 ? configured : DEFAULT_CONTENT_WIDTH;
|
|
24
|
+
return Math.max(20, Math.min(cap, cols - 1));
|
|
25
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.69.0",
|
|
4
4
|
"description": "Terminal coding agent that drives chat.deepseek.com through Playwright — no API key required. Reads and edits files, runs commands, commits to git, like Claude Code / Codex CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|