zames_pro 2.68.1 → 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 CHANGED
@@ -7,6 +7,20 @@ 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
+
10
24
  ## [2.68.1] - 2026-10-08
11
25
 
12
26
  ### Fixed
@@ -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: 'Максимальная ширина ответа в колонках (0 — авто, до 100)',
19
- en: 'Max answer width in columns (0 = auto, capped at 100)',
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.js CHANGED
@@ -1,5 +1,6 @@
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';
@@ -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 = Math.max(20, (process.stdout.columns || 80) - 1);
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 = Math.max(20, (process.stdout.columns || 80) - 1);
1098
+ const maxW = contentWidth();
1098
1099
  const preview = truncateToWidth(text.split(NL).join(' ↵ '), maxW);
1099
1100
  this.printAbove(theme.toolResult(' → ' + preview));
1100
1101
  }
@@ -1118,7 +1119,7 @@ export class LineEditor {
1118
1119
  // Wrap long service messages (send failures, rate limits) to the same
1119
1120
  // margin the tool previews use, instead of letting the terminal wrap them
1120
1121
  // at the full width and mid-word — that mismatch looked ragged.
1121
- const maxW = Math.max(20, (process.stdout.columns || 80) - 1);
1122
+ const maxW = contentWidth();
1122
1123
  const wrapped = wrapToWidth('⚠ ' + msg, maxW);
1123
1124
  this.printAbove(NL + theme.warn(wrapped));
1124
1125
  }
@@ -1192,7 +1193,7 @@ export class LineEditor {
1192
1193
  // Attachments collected for this message (images/files). They are handed
1193
1194
  // to the browser layer to be attached to the chat.
1194
1195
  const attached = this.attachments.items.slice();
1195
- this.printAbove(theme.user('❯ ') + display);
1196
+ this.printAbove(theme.user('❯ ') + wrapToWidth(display, contentWidth()));
1196
1197
  this.pastes = [];
1197
1198
  this.attachments.reset();
1198
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
- // Configured maximum answer width (columns). 0 = auto: terminal width, capped
30
- // at 100. Set once from config (ui.answerWidth) so every render path honors it
31
- // without threading the option through every call site.
32
- let configuredWidth = 0;
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
- configuredWidth = typeof width === 'number' && width > 0 ? width : 0;
35
+ setContentWidth(width);
35
36
  }
36
37
  export function renderMarkdown(text) {
37
38
  if (!text)
38
39
  return '';
39
40
  try {
40
- const cols = process.stdout.columns || 80;
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
@@ -3,6 +3,7 @@ import { theme, divider } from './theme.js';
3
3
  import { renderMarkdown } from './markdown.js';
4
4
  import { translate } from './i18n.js';
5
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 = Math.max(20, (process.stdout.columns || 80) - 1);
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 = Math.max(20, (process.stdout.columns || 80) - 1);
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,7 @@ export function createSpinner(locale = 'ru') {
199
200
  },
200
201
  warning: (msg) => {
201
202
  stop();
202
- const maxW = Math.max(20, (process.stdout.columns || 80) - 1);
203
+ const maxW = contentWidth();
203
204
  console.log(String.fromCharCode(10) + theme.warn(wrapToWidth('⚠ ' + msg, maxW)));
204
205
  },
205
206
  stop,
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 terminal width (capped at `max`) so it does not look stubby on a
36
- // wide terminal. Falls back to 60 columns when the width is unknown (non-TTY).
37
- // One column is kept free, like everywhere else in the status/input layout, so
38
- // a full-width rule cannot trigger autowrap.
39
- export function divider(max = 100) {
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.68.1",
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": {