zames_pro 2.49.1 → 2.49.2

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.
Files changed (2) hide show
  1. package/dist/input.js +68 -12
  2. package/package.json +1 -1
package/dist/input.js CHANGED
@@ -93,7 +93,12 @@ export function visRows(s, cols) {
93
93
  const len = visLen(s);
94
94
  return Math.max(1, Math.ceil(len / width));
95
95
  }
96
- // Visible length of a line without ANSI sequences.
96
+ // Visible length of a line without ANSI sequences. WIDE characters (emoji,
97
+ // CJK) count as 2 columns — a plain code-point count made the status-line
98
+ // arithmetic (right-align, wrapping, block erase) disagree with the real
99
+ // screen, so a status containing an emoji/CJK could wrap past the edge and
100
+ // make the block erase miss (status "stacking"). Combining marks and the
101
+ // zero-width joiner/space/variation selectors count as 0.
97
102
  export function visLen(s) {
98
103
  s = String(s);
99
104
  let n = 0;
@@ -109,11 +114,48 @@ export function visLen(s) {
109
114
  }
110
115
  continue;
111
116
  }
112
- n++;
113
- i++;
117
+ const cp = s.codePointAt(i);
118
+ n += charWidth(cp);
119
+ i += cp > 0xffff ? 2 : 1;
114
120
  }
115
121
  return n;
116
122
  }
123
+ // Display width of one code point in columns (0, 1 or 2). Based on the
124
+ // conventional wcwidth ranges — good enough for terminal layout, and it makes
125
+ // the emoji used in the status line (🧠🌐) count as 2, while math symbols like
126
+ // ⧗ (U+29D7) stay 1, matching a real terminal.
127
+ export function charWidth(cp) {
128
+ // Zero width: combining marks, ZWJ/ZWNJ, variation selectors.
129
+ if (cp === 0x200b ||
130
+ cp === 0x200c ||
131
+ cp === 0x200d ||
132
+ (cp >= 0x0300 && cp <= 0x036f) ||
133
+ (cp >= 0x1ab0 && cp <= 0x1aff) ||
134
+ (cp >= 0x1dc0 && cp <= 0x1dff) ||
135
+ (cp >= 0x20d0 && cp <= 0x20ff) ||
136
+ (cp >= 0xfe00 && cp <= 0xfe0f) ||
137
+ (cp >= 0xfe20 && cp <= 0xfe2f)) {
138
+ return 0;
139
+ }
140
+ // Wide: CJK, Hangul, fullwidth forms, emoji.
141
+ if ((cp >= 0x1100 && cp <= 0x115f) ||
142
+ (cp >= 0x2e80 && cp <= 0x303e) ||
143
+ (cp >= 0x3041 && cp <= 0x33ff) ||
144
+ (cp >= 0x3400 && cp <= 0x4dbf) ||
145
+ (cp >= 0x4e00 && cp <= 0x9fff) ||
146
+ (cp >= 0xa000 && cp <= 0xa4cf) ||
147
+ (cp >= 0xac00 && cp <= 0xd7a3) ||
148
+ (cp >= 0xf900 && cp <= 0xfaff) ||
149
+ (cp >= 0xfe30 && cp <= 0xfe4f) ||
150
+ (cp >= 0xff00 && cp <= 0xff60) ||
151
+ (cp >= 0xffe0 && cp <= 0xffe6) ||
152
+ (cp >= 0x1f000 && cp <= 0x1f02f) ||
153
+ (cp >= 0x1f300 && cp <= 0x1faff) ||
154
+ (cp >= 0x20000 && cp <= 0x3fffd)) {
155
+ return 2;
156
+ }
157
+ return 1;
158
+ }
117
159
  export function layoutInput(promptStr, buf, cursor, cols) {
118
160
  const width = Math.max(20, Number(cols) || 80);
119
161
  const promptW = visLen(promptStr);
@@ -128,21 +170,29 @@ export function layoutInput(promptStr, buf, cursor, cols) {
128
170
  const avail = Math.max(1, width - promptW);
129
171
  const start = i;
130
172
  // Build the next visual line. First we take as many
131
- // characters as fit (count < avail). If we hit the width
173
+ // characters as fit (columns < avail). If we hit the width
132
174
  // and the next character is not the end of the line, we roll back to the
133
175
  // last space so we don't split a word in the middle (word-wrap).
176
+ // Wrapping and the cursor column are measured in COLUMNS (charWidth),
177
+ // not code points — otherwise a CJK/emoji input would overflow the row
178
+ // and put the cursor on the wrong cell.
134
179
  let text = '';
135
- let count = 0;
136
- let lastSpace = -1; // index of the space in text (by Array.from)
137
- while (i < chars.length && chars[i] !== NL && count < avail) {
180
+ let widthUsed = 0;
181
+ let charIdx = 0;
182
+ let lastSpace = -1; // char index of the space within text
183
+ while (i < chars.length && chars[i] !== NL) {
184
+ const w = charWidth(chars[i].codePointAt(0));
185
+ if (widthUsed + w > avail)
186
+ break;
138
187
  if (chars[i] === ' ')
139
- lastSpace = count;
188
+ lastSpace = charIdx;
140
189
  text += chars[i];
141
190
  i++;
142
- count++;
191
+ charIdx++;
192
+ widthUsed += w;
143
193
  }
144
194
  const atLineEnd = i >= chars.length || chars[i] === NL;
145
- if (!atLineEnd && count >= avail && lastSpace > 0) {
195
+ if (!atLineEnd && widthUsed >= avail && lastSpace > 0) {
146
196
  // Move the "tail" of the line to the next visual line.
147
197
  const textChars = Array.from(text);
148
198
  const tailLen = textChars.length - lastSpace;
@@ -162,7 +212,8 @@ export function layoutInput(promptStr, buf, cursor, cols) {
162
212
  let cursorCol = promptW;
163
213
  for (let r = 0; r < rows.length; r++) {
164
214
  const row = rows[r];
165
- const len = Array.from(row.text).length;
215
+ const rowChars = Array.from(row.text);
216
+ const len = rowChars.length;
166
217
  const from = row.start;
167
218
  const to = row.start + len;
168
219
  if (cur >= from && cur <= to) {
@@ -170,7 +221,12 @@ export function layoutInput(promptStr, buf, cursor, cols) {
170
221
  if (cur === to && next && next.start === to)
171
222
  continue;
172
223
  cursorRow = r;
173
- cursorCol = promptW + (cur - from);
224
+ // Cursor column in COLUMNS (wide chars count as 2).
225
+ let w = 0;
226
+ for (let k = 0; k < cur - from; k++) {
227
+ w += charWidth(rowChars[k].codePointAt(0));
228
+ }
229
+ cursorCol = promptW + w;
174
230
  break;
175
231
  }
176
232
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.49.1",
3
+ "version": "2.49.2",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {