vterm.js 0.7.0 → 0.7.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/README.md CHANGED
@@ -35,6 +35,8 @@ Part of the [vterm](https://github.com/beorn/vterm) monorepo.
35
35
 
36
36
  ## Install
37
37
 
38
+ > Install from npm; a git install resolves the TypeScript source and runs only under Bun.
39
+
38
40
  ```bash
39
41
  npm install vterm.js
40
42
  ```
package/dist/index.d.mts CHANGED
@@ -204,6 +204,10 @@ interface Snapshot {
204
204
  originMode: boolean;
205
205
  autoWrap: boolean;
206
206
  charsetG0: boolean;
207
+ /** True when DEC Special Graphics is designated as G1. Absent in older snapshots: ASCII. */
208
+ charsetG1?: boolean;
209
+ /** True when SO has invoked G1 into GL. Absent in older snapshots: SI/G0. */
210
+ activeG1?: boolean;
207
211
  };
208
212
  attrs: ScreenAttrsSnapshot;
209
213
  modes: {
@@ -268,6 +272,10 @@ interface Snapshot {
268
272
  };
269
273
  unicode: {
270
274
  charsetG0: boolean;
275
+ /** True when DEC Special Graphics is designated as G1. Absent in older snapshots: ASCII. */
276
+ charsetG1?: boolean;
277
+ /** True when SO has invoked G1 into GL. Absent in older snapshots: SI/G0. */
278
+ activeG1?: boolean;
271
279
  lastChar: string;
272
280
  pendingRegionalIndicator: string | null;
273
281
  afterZWJ: boolean;
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region src/screen.ts
2
2
  /** Default retained payload bound for one APC/DCS sequence (16 MiB of ASCII/base64). */
3
- const DEFAULT_MAX_STRING_SEQUENCE_LENGTH = 16 * 1024 * 1024;
3
+ const DEFAULT_MAX_STRING_SEQUENCE_LENGTH = 16777216;
4
4
  /** Frozen sentinel for unwritten cells — never mutate, copy-on-write in writeChar(). */
5
5
  const EMPTY_CELL = Object.freeze({
6
6
  char: "",
@@ -58,7 +58,7 @@ const F_HIDDEN$1 = 64;
58
58
  const F_BLINK$1 = 128;
59
59
  const F_WIDE$1 = 256;
60
60
  const UL_SHIFT$1 = 9;
61
- const UL_MASK$1 = 7 << UL_SHIFT$1;
61
+ const UL_MASK$1 = 3584;
62
62
  const F_HAS_FG = 4096;
63
63
  const F_HAS_BG = 8192;
64
64
  const F_HAS_UL = 16384;
@@ -79,6 +79,17 @@ function underlineToBits(u) {
79
79
  function bitsToUnderline(meta) {
80
80
  return UL_STYLES[(meta & UL_MASK$1) >> UL_SHIFT$1] ?? "none";
81
81
  }
82
+ /** Require an invariant-owned value and fail where the impossible state is first observed. */
83
+ function required(value, subject) {
84
+ if (value === null || value === void 0) throw new Error(`Missing ${subject}`);
85
+ return value;
86
+ }
87
+ /** Read a dense indexed value and fail at the violated invariant instead of propagating `undefined`. */
88
+ function itemAt(items, index, subject) {
89
+ const value = items[index];
90
+ if (value === void 0) throw new RangeError(`${subject}[${index}] is outside its ${items.length}-item range`);
91
+ return value;
92
+ }
82
93
  /** Pack a color's 8-bit channels into a 24-bit `0xRRGGBB` word. */
83
94
  function packRgb(c) {
84
95
  return ((c.r & 255) << 16 | (c.g & 255) << 8 | c.b & 255) >>> 0;
@@ -110,7 +121,7 @@ function makePackedRow(width) {
110
121
  let ulIdx = null;
111
122
  let urlMap = null;
112
123
  function setFg(col, c) {
113
- if (fgRgb === null) {
124
+ if (fgRgb === null || fgIdx === null) {
114
125
  fgRgb = new Uint32Array(width);
115
126
  fgIdx = new Int16Array(width);
116
127
  }
@@ -118,7 +129,7 @@ function makePackedRow(width) {
118
129
  fgIdx[col] = c.index ?? -1;
119
130
  }
120
131
  function setBg(col, c) {
121
- if (bgRgb === null) {
132
+ if (bgRgb === null || bgIdx === null) {
122
133
  bgRgb = new Uint32Array(width);
123
134
  bgIdx = new Int16Array(width);
124
135
  }
@@ -126,7 +137,7 @@ function makePackedRow(width) {
126
137
  bgIdx[col] = c.index ?? -1;
127
138
  }
128
139
  function setUl(col, c) {
129
- if (ulRgb === null) {
140
+ if (ulRgb === null || ulIdx === null) {
130
141
  ulRgb = new Uint32Array(width);
131
142
  ulIdx = new Int16Array(width);
132
143
  }
@@ -211,27 +222,27 @@ function makePackedRow(width) {
211
222
  } else meta[col] = 0;
212
223
  },
213
224
  widen(col) {
214
- meta[col] = (meta[col] | F_WIDE$1) >>> 0;
225
+ meta[col] = (itemAt(meta, col, "packed row metadata") | F_WIDE$1) >>> 0;
215
226
  if (col + 1 < width) row.setEmpty(col + 1);
216
227
  },
217
228
  appendChar(col, ch) {
218
- chars[col] = chars[col] + ch;
229
+ chars[col] = itemAt(chars, col, "packed row characters") + ch;
219
230
  },
220
231
  copyCellFrom(col, src, srcCol) {
221
232
  row.setCellRaw(col, src.getCellRaw(srcCol));
222
233
  },
223
234
  getCellRaw(col) {
224
- const m = meta[col];
235
+ const m = itemAt(meta, col, "packed row metadata");
225
236
  if (m === 0 && chars[col] === "") return emptyCell();
226
237
  return {
227
- char: chars[col],
228
- fg: m & F_HAS_FG ? unpackColor(fgRgb[col], fgIdx[col]) : null,
229
- bg: m & F_HAS_BG ? unpackColor(bgRgb[col], bgIdx[col]) : null,
238
+ char: itemAt(chars, col, "packed row characters"),
239
+ fg: m & F_HAS_FG ? unpackColor(itemAt(required(fgRgb, "foreground color plane"), col, "foreground color plane"), itemAt(required(fgIdx, "foreground color index plane"), col, "foreground color index plane")) : null,
240
+ bg: m & F_HAS_BG ? unpackColor(itemAt(required(bgRgb, "background color plane"), col, "background color plane"), itemAt(required(bgIdx, "background color index plane"), col, "background color index plane")) : null,
230
241
  bold: (m & F_BOLD$1) !== 0,
231
242
  faint: (m & F_FAINT$1) !== 0,
232
243
  italic: (m & F_ITALIC$1) !== 0,
233
244
  underline: bitsToUnderline(m),
234
- underlineColor: m & F_HAS_UL ? unpackColor(ulRgb[col], ulIdx[col]) : null,
245
+ underlineColor: m & F_HAS_UL ? unpackColor(itemAt(required(ulRgb, "underline color plane"), col, "underline color plane"), itemAt(required(ulIdx, "underline color index plane"), col, "underline color index plane")) : null,
235
246
  overline: (m & F_OVERLINE$1) !== 0,
236
247
  strikethrough: (m & F_STRIKE$1) !== 0,
237
248
  inverse: (m & F_INVERSE$1) !== 0,
@@ -239,7 +250,7 @@ function makePackedRow(width) {
239
250
  blink: (m & F_BLINK$1) !== 0,
240
251
  protected: (m & F_PROTECTED$1) !== 0,
241
252
  wide: (m & F_WIDE$1) !== 0,
242
- url: m & F_HAS_URL ? urlMap.get(col) ?? null : null
253
+ url: m & F_HAS_URL ? required(urlMap, "packed row URL map").get(col) ?? null : null
243
254
  };
244
255
  },
245
256
  toCells() {
@@ -254,10 +265,10 @@ function makePackedRow(width) {
254
265
  return meta[col] === 0 && chars[col] === "";
255
266
  },
256
267
  getChar(col) {
257
- return chars[col];
268
+ return itemAt(chars, col, "packed row characters");
258
269
  },
259
270
  isWide(col) {
260
- return (meta[col] & F_WIDE$1) !== 0;
271
+ return (itemAt(meta, col, "packed row metadata") & F_WIDE$1) !== 0;
261
272
  }
262
273
  };
263
274
  return row;
@@ -361,9 +372,9 @@ function buildPalette256() {
361
372
  255
362
373
  ];
363
374
  for (let r = 0; r < 6; r++) for (let g = 0; g < 6; g++) for (let b = 0; b < 6; b++) palette.push({
364
- r: levels[r],
365
- g: levels[g],
366
- b: levels[b]
375
+ r: itemAt(levels, r, "ANSI cube red levels"),
376
+ g: itemAt(levels, g, "ANSI cube green levels"),
377
+ b: itemAt(levels, b, "ANSI cube blue levels")
367
378
  });
368
379
  for (let i = 0; i < 24; i++) {
369
380
  const v = 8 + i * 10;
@@ -389,14 +400,14 @@ function parseColorSpec(spec) {
389
400
  return Math.round(v * 255 / max);
390
401
  };
391
402
  return {
392
- r: scale(rgbMatch[1]),
393
- g: scale(rgbMatch[2]),
394
- b: scale(rgbMatch[3])
403
+ r: scale(itemAt(rgbMatch, 1, "X11 red capture")),
404
+ g: scale(itemAt(rgbMatch, 2, "X11 green capture")),
405
+ b: scale(itemAt(rgbMatch, 3, "X11 blue capture"))
395
406
  };
396
407
  }
397
408
  const hex6 = /^#([0-9a-fA-F]{6})$/.exec(s);
398
409
  if (hex6) {
399
- const v = hex6[1];
410
+ const v = itemAt(hex6, 1, "six-digit hex capture");
400
411
  return {
401
412
  r: parseInt(v.substring(0, 2), 16),
402
413
  g: parseInt(v.substring(2, 4), 16),
@@ -405,12 +416,12 @@ function parseColorSpec(spec) {
405
416
  }
406
417
  const hex3 = /^#([0-9a-fA-F]{3})$/.exec(s);
407
418
  if (hex3) {
408
- const v = hex3[1];
419
+ const v = itemAt(hex3, 1, "three-digit hex capture");
409
420
  const expand = (ch) => parseInt(ch + ch, 16);
410
421
  return {
411
- r: expand(v[0]),
412
- g: expand(v[1]),
413
- b: expand(v[2])
422
+ r: expand(itemAt(v, 0, "short hex red channel")),
423
+ g: expand(itemAt(v, 1, "short hex green channel")),
424
+ b: expand(itemAt(v, 2, "short hex blue channel"))
414
425
  };
415
426
  }
416
427
  return null;
@@ -484,7 +495,9 @@ function createScreen(options = {}) {
484
495
  attrs: resetAttrs(),
485
496
  originMode: false,
486
497
  autoWrap: true,
487
- charsetG0: false
498
+ charsetG0: false,
499
+ charsetG1: false,
500
+ activeG1: false
488
501
  };
489
502
  let savedCurX = 0;
490
503
  let savedCurY = 0;
@@ -663,6 +676,8 @@ function createScreen(options = {}) {
663
676
  assertBoolean("savedState.originMode", saved.originMode);
664
677
  assertBoolean("savedState.autoWrap", saved.autoWrap);
665
678
  assertBoolean("savedState.charsetG0", saved.charsetG0);
679
+ if (Object.hasOwn(saved, "charsetG1")) assertBoolean("savedState.charsetG1", saved.charsetG1);
680
+ if (Object.hasOwn(saved, "activeG1")) assertBoolean("savedState.activeG1", saved.activeG1);
666
681
  assertRecord("attrs", value.attrs);
667
682
  const modes = value.modes;
668
683
  assertRecord("modes", modes);
@@ -745,6 +760,8 @@ function createScreen(options = {}) {
745
760
  const unicode = value.unicode;
746
761
  assertRecord("unicode", unicode);
747
762
  assertBoolean("unicode.charsetG0", unicode.charsetG0);
763
+ if (Object.hasOwn(unicode, "charsetG1")) assertBoolean("unicode.charsetG1", unicode.charsetG1);
764
+ if (Object.hasOwn(unicode, "activeG1")) assertBoolean("unicode.activeG1", unicode.activeG1);
748
765
  assertString("unicode.lastChar", unicode.lastChar);
749
766
  if (unicode.pendingRegionalIndicator !== null) assertString("unicode.pendingRegionalIndicator", unicode.pendingRegionalIndicator);
750
767
  assertBoolean("unicode.afterZWJ", unicode.afterZWJ);
@@ -774,6 +791,8 @@ function createScreen(options = {}) {
774
791
  let advancedClipboard = "";
775
792
  let viewportOffset = 0;
776
793
  let charsetG0 = false;
794
+ let charsetG1 = false;
795
+ let activeG1 = false;
777
796
  let clipboard = "";
778
797
  let cwd = "";
779
798
  let notifications = [];
@@ -939,13 +958,12 @@ function createScreen(options = {}) {
939
958
  if (bytes.length === 0) return 0;
940
959
  let continuationCount = 0;
941
960
  let leadIndex = bytes.length - 1;
942
- while (leadIndex >= 0 && (bytes[leadIndex] & 192) === 128 && continuationCount < 3) {
961
+ while (leadIndex >= 0 && (itemAt(bytes, leadIndex, "UTF-8 pending bytes") & 192) === 128 && continuationCount < 3) {
943
962
  continuationCount++;
944
963
  leadIndex--;
945
964
  }
946
965
  if (leadIndex < 0) return bytes.length;
947
- const lead = bytes[leadIndex];
948
- const expectedLength = utf8SequenceLength(lead);
966
+ const expectedLength = utf8SequenceLength(itemAt(bytes, leadIndex, "UTF-8 lead byte"));
949
967
  if (expectedLength === 0) return bytes.length;
950
968
  return expectedLength > bytes.length - leadIndex ? leadIndex : bytes.length;
951
969
  }
@@ -1051,11 +1069,11 @@ function createScreen(options = {}) {
1051
1069
  const lm = leftMargin;
1052
1070
  const rm = rightMargin;
1053
1071
  for (let r = top; r < bottom; r++) {
1054
- const srcRow = grid[r + 1];
1055
- const dstRow = grid[r];
1072
+ const srcRow = itemAt(grid, r + 1, "scroll source rows");
1073
+ const dstRow = itemAt(grid, r, "scroll destination rows");
1056
1074
  for (let c = lm; c <= rm && c < cols; c++) dstRow.copyCellFrom(c, srcRow, c);
1057
1075
  }
1058
- const bottomRow = grid[bottom];
1076
+ const bottomRow = itemAt(grid, bottom, "scroll bottom row");
1059
1077
  for (let c = lm; c <= rm && c < cols; c++) bottomRow.setEmpty(c);
1060
1078
  markScreenRowsDirty(top, bottom);
1061
1079
  } else {
@@ -1098,16 +1116,16 @@ function createScreen(options = {}) {
1098
1116
  const lm = leftMargin;
1099
1117
  const rm = rightMargin;
1100
1118
  for (let r = bottom; r > top; r--) {
1101
- const srcRow = grid[r - 1];
1102
- const dstRow = grid[r];
1119
+ const srcRow = itemAt(grid, r - 1, "reverse-scroll source rows");
1120
+ const dstRow = itemAt(grid, r, "reverse-scroll destination rows");
1103
1121
  for (let c = lm; c <= rm && c < cols; c++) dstRow.copyCellFrom(c, srcRow, c);
1104
1122
  }
1105
- const topRow = grid[top];
1123
+ const topRow = itemAt(grid, top, "reverse-scroll top row");
1106
1124
  for (let c = lm; c <= rm && c < cols; c++) topRow.setEmpty(c);
1107
1125
  } else {
1108
1126
  for (let i = bottom; i > top; i--) {
1109
- grid[i] = grid[i - 1];
1110
- softWrapped[i] = softWrapped[i - 1];
1127
+ grid[i] = itemAt(grid, i - 1, "reverse-scroll source rows");
1128
+ softWrapped[i] = itemAt(softWrapped, i - 1, "reverse-scroll source wrap flags");
1111
1129
  }
1112
1130
  grid[top] = makeRow(cols);
1113
1131
  softWrapped[top] = false;
@@ -1132,7 +1150,7 @@ function createScreen(options = {}) {
1132
1150
  if (prevRow < 0) return null;
1133
1151
  prevCol = cols - 1;
1134
1152
  }
1135
- const row = grid[prevRow];
1153
+ const row = itemAt(grid, prevRow, "previous-cell rows");
1136
1154
  if (prevCol > 0 && row.getChar(prevCol) === "" && row.isWide(prevCol - 1)) prevCol--;
1137
1155
  if (row.isEmpty(prevCol)) return null;
1138
1156
  return {
@@ -1146,7 +1164,7 @@ function createScreen(options = {}) {
1146
1164
  row.widen(col);
1147
1165
  }
1148
1166
  function writeChar(ch) {
1149
- if (charsetG0 && ch.length === 1) {
1167
+ if ((activeG1 ? charsetG1 : charsetG0) && ch.length === 1) {
1150
1168
  const mapped = DEC_SPECIAL_GRAPHICS[ch];
1151
1169
  if (mapped) ch = mapped;
1152
1170
  }
@@ -1196,14 +1214,16 @@ function createScreen(options = {}) {
1196
1214
  return;
1197
1215
  }
1198
1216
  }
1199
- if (isRegionalIndicator(codePoint)) if (pendingRegionalIndicator !== null) {
1200
- const flag = pendingRegionalIndicator + ch;
1201
- pendingRegionalIndicator = null;
1202
- writeCharCore(flag, true);
1203
- return;
1204
- } else {
1205
- pendingRegionalIndicator = ch;
1206
- return;
1217
+ if (isRegionalIndicator(codePoint)) {
1218
+ if (pendingRegionalIndicator !== null) {
1219
+ const flag = pendingRegionalIndicator + ch;
1220
+ pendingRegionalIndicator = null;
1221
+ writeCharCore(flag, true);
1222
+ return;
1223
+ } else {
1224
+ pendingRegionalIndicator = ch;
1225
+ return;
1226
+ }
1207
1227
  }
1208
1228
  if (pendingRegionalIndicator !== null) {
1209
1229
  const ri = pendingRegionalIndicator;
@@ -1218,18 +1238,20 @@ function createScreen(options = {}) {
1218
1238
  const charWidth = wide ? 2 : 1;
1219
1239
  const wrapBoundary = leftRightMarginMode ? rightMargin + 1 : rowCols(curY);
1220
1240
  const wrapReturn = leftRightMarginMode ? leftMargin : 0;
1221
- if (curX + charWidth > wrapBoundary) if (autoWrap) {
1222
- softWrapped[curY] = true;
1223
- curX = wrapReturn;
1224
- curY++;
1225
- if (curY > scrollBottom) {
1226
- curY = scrollBottom;
1227
- scrollUp(scrollTop, scrollBottom);
1228
- }
1229
- } else curX = wrapBoundary - charWidth;
1241
+ if (curX + charWidth > wrapBoundary) {
1242
+ if (autoWrap) {
1243
+ softWrapped[curY] = true;
1244
+ curX = wrapReturn;
1245
+ curY++;
1246
+ if (curY > scrollBottom) {
1247
+ curY = scrollBottom;
1248
+ scrollUp(scrollTop, scrollBottom);
1249
+ }
1250
+ } else curX = wrapBoundary - charWidth;
1251
+ }
1230
1252
  if (insertMode) {
1231
1253
  const insertEnd = leftRightMarginMode ? rightMargin + 1 : cols;
1232
- mutateRowAsCells(grid[curY], (cells) => {
1254
+ mutateRowAsCells(itemAt(grid, curY, "cursor row"), (cells) => {
1233
1255
  for (let i = 0; i < charWidth; i++) {
1234
1256
  cells.splice(insertEnd - 1, 1);
1235
1257
  cells.splice(curX, 0, emptyCell());
@@ -1272,7 +1294,6 @@ function createScreen(options = {}) {
1272
1294
  case 6:
1273
1295
  cursorShape = "bar";
1274
1296
  cursorBlinking = false;
1275
- break;
1276
1297
  }
1277
1298
  else if (finalByte === "@") handleShiftLeft(Math.max(parts[0] ?? 1, 1));
1278
1299
  else if (finalByte === "A") handleShiftRight(Math.max(parts[0] ?? 1, 1));
@@ -1301,7 +1322,7 @@ function createScreen(options = {}) {
1301
1322
  const { t, l, b, r } = normalizeRect(parts[1] ?? 1, parts[2] ?? 1, parts[3] ?? rows, parts[4] ?? cols);
1302
1323
  const fillChar = String.fromCharCode(charCode);
1303
1324
  for (let row = t; row <= b && row < rows; row++) {
1304
- const gr = grid[row];
1325
+ const gr = itemAt(grid, row, "rectangular erase rows");
1305
1326
  for (let col = l; col <= r && col < cols; col++) {
1306
1327
  const cell = emptyCell();
1307
1328
  cell.char = fillChar;
@@ -1312,7 +1333,7 @@ function createScreen(options = {}) {
1312
1333
  } else if (finalByte === "z" || finalByte === "{") {
1313
1334
  const { t, l, b, r } = normalizeRect(parts[0] ?? 1, parts[1] ?? 1, parts[2] ?? rows, parts[3] ?? cols);
1314
1335
  for (let row = t; row <= b && row < rows; row++) {
1315
- const gr = grid[row];
1336
+ const gr = itemAt(grid, row, "rectangular fill rows");
1316
1337
  for (let col = l; col <= r && col < cols; col++) gr.setEmpty(col);
1317
1338
  }
1318
1339
  markScreenRowsDirty(t, Math.min(b, rows - 1));
@@ -1332,11 +1353,11 @@ function createScreen(options = {}) {
1332
1353
  for (let row = 0; row < h; row++) {
1333
1354
  const dr = dstTop + row;
1334
1355
  if (dr < 0 || dr >= rows) continue;
1335
- const dstRow = grid[dr];
1356
+ const dstRow = itemAt(grid, dr, "rectangular copy destination rows");
1336
1357
  for (let col = 0; col < w; col++) {
1337
1358
  const dc = dstLeft + col;
1338
1359
  if (dc < 0 || dc >= cols) continue;
1339
- dstRow.setCellRaw(dc, snapshot[row][col]);
1360
+ dstRow.setCellRaw(dc, itemAt(itemAt(snapshot, row, "rectangular copy rows"), col, "rectangular copy cells"));
1340
1361
  }
1341
1362
  }
1342
1363
  markScreenRowsDirty(Math.max(0, dstTop), Math.min(dstTop + h - 1, rows - 1));
@@ -1345,7 +1366,7 @@ function createScreen(options = {}) {
1345
1366
  const sgrParts = parts.slice(4);
1346
1367
  const reverse = finalByte === "t";
1347
1368
  for (let row = t; row <= b && row < rows; row++) {
1348
- const gr = grid[row];
1369
+ const gr = itemAt(grid, row, "rectangular attribute rows");
1349
1370
  for (let col = l; col <= r && col < cols; col++) {
1350
1371
  const cell = gr.getCellRaw(col);
1351
1372
  applyRectAttrs(cell, sgrParts, reverse);
@@ -1459,9 +1480,7 @@ function createScreen(options = {}) {
1459
1480
  case 20:
1460
1481
  onResponse(`\x1b]L${title}\x1b\\`);
1461
1482
  break;
1462
- case 21:
1463
- onResponse(`\x1b]l${title}\x1b\\`);
1464
- break;
1483
+ case 21: onResponse(`\x1b]l${title}\x1b\\`);
1465
1484
  }
1466
1485
  break;
1467
1486
  }
@@ -1580,8 +1599,6 @@ function createScreen(options = {}) {
1580
1599
  curX = savedCurX;
1581
1600
  curY = savedCurY;
1582
1601
  clampCursor();
1583
- break;
1584
- default: break;
1585
1602
  }
1586
1603
  }
1587
1604
  function handleCSIGt(params, _intermediates, finalByte) {
@@ -1688,9 +1705,7 @@ function createScreen(options = {}) {
1688
1705
  case 1007:
1689
1706
  value = altScrollMode ? 1 : 2;
1690
1707
  break;
1691
- case 1048:
1692
- value = 2;
1693
- break;
1708
+ case 1048: value = 2;
1694
1709
  }
1695
1710
  onResponse(`\x1b[?${mode};${value}$y`);
1696
1711
  }
@@ -1819,16 +1834,14 @@ function createScreen(options = {}) {
1819
1834
  case 1007:
1820
1835
  altScrollMode = set;
1821
1836
  break;
1822
- case 1048:
1823
- if (set) {
1824
- savedCurX = curX;
1825
- savedCurY = curY;
1826
- } else {
1827
- curX = savedCurX;
1828
- curY = savedCurY;
1829
- clampCursor();
1830
- }
1831
- break;
1837
+ case 1048: if (set) {
1838
+ savedCurX = curX;
1839
+ savedCurY = curY;
1840
+ } else {
1841
+ curX = savedCurX;
1842
+ curY = savedCurY;
1843
+ clampCursor();
1844
+ }
1832
1845
  }
1833
1846
  }
1834
1847
  function handleEraseDisplay(mode, selective = false) {
@@ -1846,7 +1859,6 @@ function createScreen(options = {}) {
1846
1859
  for (let row = 0; row < rows; row++) eraseCells(row, 0, row, cols - 1, selective);
1847
1860
  if (mode === 3) clearScrollback();
1848
1861
  markAllDirty();
1849
- break;
1850
1862
  }
1851
1863
  }
1852
1864
  function handleEraseLine(mode, selective = false) {
@@ -1859,9 +1871,7 @@ function createScreen(options = {}) {
1859
1871
  case 1:
1860
1872
  eraseCells(curY, eraseLeft, curY, curX, selective);
1861
1873
  break;
1862
- case 2:
1863
- eraseCells(curY, eraseLeft, curY, eraseRight, selective);
1864
- break;
1874
+ case 2: eraseCells(curY, eraseLeft, curY, eraseRight, selective);
1865
1875
  }
1866
1876
  }
1867
1877
  function eraseCells(row, startCol, _endRow, endCol, selective = false) {
@@ -2014,9 +2024,7 @@ function createScreen(options = {}) {
2014
2024
  case 25:
2015
2025
  cell.blink = false;
2016
2026
  break;
2017
- case 27:
2018
- cell.inverse = false;
2019
- break;
2027
+ case 27: cell.inverse = false;
2020
2028
  }
2021
2029
  }
2022
2030
  function handleSGR(rawParams) {
@@ -2026,7 +2034,7 @@ function createScreen(options = {}) {
2026
2034
  for (const seg of segments) if (seg.includes(":")) {
2027
2035
  const subs = seg.split(":").map((s) => s === "" ? 0 : parseInt(s, 10));
2028
2036
  subParams.set(params.length, subs);
2029
- params.push(subs[0]);
2037
+ params.push(itemAt(subs, 0, "SGR sub-parameters"));
2030
2038
  } else params.push(seg === "" ? 0 : parseInt(seg, 10));
2031
2039
  if (params.length === 0 || params.length === 1 && params[0] === 0) {
2032
2040
  attrs = resetSgrAttrs();
@@ -2034,7 +2042,7 @@ function createScreen(options = {}) {
2034
2042
  }
2035
2043
  let i = 0;
2036
2044
  while (i < params.length) {
2037
- const code = params[i];
2045
+ const code = itemAt(params, i, "SGR parameters");
2038
2046
  switch (code) {
2039
2047
  case 0:
2040
2048
  attrs = resetSgrAttrs();
@@ -2050,7 +2058,7 @@ function createScreen(options = {}) {
2050
2058
  break;
2051
2059
  case 4: {
2052
2060
  const subs = subParams.get(i);
2053
- if (subs && subs.length > 1) switch (subs[1]) {
2061
+ if (subs && subs.length > 1) switch (itemAt(subs, 1, "underline style sub-parameters")) {
2054
2062
  case 0:
2055
2063
  attrs.underline = "none";
2056
2064
  break;
@@ -2069,9 +2077,7 @@ function createScreen(options = {}) {
2069
2077
  case 5:
2070
2078
  attrs.underline = "dashed";
2071
2079
  break;
2072
- default:
2073
- attrs.underline = "single";
2074
- break;
2080
+ default: attrs.underline = "single";
2075
2081
  }
2076
2082
  else attrs.underline = "single";
2077
2083
  break;
@@ -2125,7 +2131,7 @@ function createScreen(options = {}) {
2125
2131
  case 36:
2126
2132
  case 37:
2127
2133
  attrs.fg = {
2128
- ...palette256[code - 30],
2134
+ ...itemAt(palette256, code - 30, "basic foreground palette"),
2129
2135
  index: code - 30
2130
2136
  };
2131
2137
  break;
@@ -2155,7 +2161,7 @@ function createScreen(options = {}) {
2155
2161
  case 46:
2156
2162
  case 47:
2157
2163
  attrs.bg = {
2158
- ...palette256[code - 40],
2164
+ ...itemAt(palette256, code - 40, "basic background palette"),
2159
2165
  index: code - 40
2160
2166
  };
2161
2167
  break;
@@ -2208,7 +2214,7 @@ function createScreen(options = {}) {
2208
2214
  case 96:
2209
2215
  case 97:
2210
2216
  attrs.fg = {
2211
- ...palette256[code - 90 + 8],
2217
+ ...itemAt(palette256, code - 90 + 8, "bright foreground palette"),
2212
2218
  index: code - 90 + 8
2213
2219
  };
2214
2220
  break;
@@ -2219,12 +2225,10 @@ function createScreen(options = {}) {
2219
2225
  case 104:
2220
2226
  case 105:
2221
2227
  case 106:
2222
- case 107:
2223
- attrs.bg = {
2224
- ...palette256[code - 100 + 8],
2225
- index: code - 100 + 8
2226
- };
2227
- break;
2228
+ case 107: attrs.bg = {
2229
+ ...itemAt(palette256, code - 100 + 8, "bright background palette"),
2230
+ index: code - 100 + 8
2231
+ };
2228
2232
  }
2229
2233
  i++;
2230
2234
  }
@@ -2233,7 +2237,7 @@ function createScreen(options = {}) {
2233
2237
  if (startIndex + 1 >= params.length) return null;
2234
2238
  const type = params[startIndex + 1];
2235
2239
  if (type === 5 && startIndex + 2 < params.length) {
2236
- const idx = params[startIndex + 2];
2240
+ const idx = itemAt(params, startIndex + 2, "indexed color parameters");
2237
2241
  const entry = palette256[idx];
2238
2242
  return {
2239
2243
  color: entry ? {
@@ -2248,9 +2252,9 @@ function createScreen(options = {}) {
2248
2252
  };
2249
2253
  } else if (type === 2 && startIndex + 4 < params.length) return {
2250
2254
  color: {
2251
- r: params[startIndex + 2],
2252
- g: params[startIndex + 3],
2253
- b: params[startIndex + 4]
2255
+ r: itemAt(params, startIndex + 2, "truecolor red parameter"),
2256
+ g: itemAt(params, startIndex + 3, "truecolor green parameter"),
2257
+ b: itemAt(params, startIndex + 4, "truecolor blue parameter")
2254
2258
  },
2255
2259
  nextIndex: startIndex + 5
2256
2260
  };
@@ -2261,7 +2265,7 @@ function createScreen(options = {}) {
2261
2265
  if (subs.length < 3) return null;
2262
2266
  const type = subs[1];
2263
2267
  if (type === 5 && subs.length >= 3) {
2264
- const idx = subs[2];
2268
+ const idx = itemAt(subs, 2, "indexed color sub-parameters");
2265
2269
  const entry = palette256[idx];
2266
2270
  return entry ? {
2267
2271
  ...entry,
@@ -2274,18 +2278,18 @@ function createScreen(options = {}) {
2274
2278
  } else if (type === 2) {
2275
2279
  if (subs.length >= 5) {
2276
2280
  if (subs.length >= 6) return {
2277
- r: subs[3],
2278
- g: subs[4],
2279
- b: subs[5]
2281
+ r: itemAt(subs, 3, "colorspace red sub-parameter"),
2282
+ g: itemAt(subs, 4, "colorspace green sub-parameter"),
2283
+ b: itemAt(subs, 5, "colorspace blue sub-parameter")
2280
2284
  };
2281
2285
  return {
2282
- r: subs[2],
2283
- g: subs[3],
2284
- b: subs[4]
2286
+ r: itemAt(subs, 2, "truecolor red sub-parameter"),
2287
+ g: itemAt(subs, 3, "truecolor green sub-parameter"),
2288
+ b: itemAt(subs, 4, "truecolor blue sub-parameter")
2285
2289
  };
2286
2290
  } else if (subs.length >= 4) return {
2287
- r: subs[2],
2288
- g: subs[3],
2291
+ r: itemAt(subs, 2, "partial truecolor red sub-parameter"),
2292
+ g: itemAt(subs, 3, "partial truecolor green sub-parameter"),
2289
2293
  b: 0
2290
2294
  };
2291
2295
  }
@@ -2317,14 +2321,11 @@ function createScreen(options = {}) {
2317
2321
  startCol: curX
2318
2322
  });
2319
2323
  break;
2320
- case "C":
2321
- semanticZones.push({
2322
- type: "output",
2323
- startRow: curY,
2324
- startCol: curX
2325
- });
2326
- break;
2327
- case "D": break;
2324
+ case "C": semanticZones.push({
2325
+ type: "output",
2326
+ startRow: curY,
2327
+ startCol: curX
2328
+ });
2328
2329
  }
2329
2330
  break;
2330
2331
  case 1: break;
@@ -2342,8 +2343,8 @@ function createScreen(options = {}) {
2342
2343
  case 4: {
2343
2344
  const fields = value.split(";");
2344
2345
  for (let fi = 0; fi + 1 < fields.length; fi += 2) {
2345
- const idx = parseInt(fields[fi], 10);
2346
- const spec = fields[fi + 1];
2346
+ const idx = parseInt(itemAt(fields, fi, "OSC 4 palette fields"), 10);
2347
+ const spec = itemAt(fields, fi + 1, "OSC 4 palette fields");
2347
2348
  if (isNaN(idx) || idx < 0 || idx > 255) continue;
2348
2349
  if (spec === "?") {
2349
2350
  const c = palette256[idx];
@@ -2358,8 +2359,8 @@ function createScreen(options = {}) {
2358
2359
  case 5: {
2359
2360
  const fields = value.split(";");
2360
2361
  for (let fi = 0; fi + 1 < fields.length; fi += 2) {
2361
- const idx = parseInt(fields[fi], 10);
2362
- const spec = fields[fi + 1];
2362
+ const idx = parseInt(itemAt(fields, fi, "OSC 5 special-color fields"), 10);
2363
+ const spec = itemAt(fields, fi + 1, "OSC 5 special-color fields");
2363
2364
  if (isNaN(idx) || idx < 0 || idx > 4) continue;
2364
2365
  if (spec === "?") {
2365
2366
  const c = specialColors.get(idx);
@@ -2469,70 +2470,76 @@ function createScreen(options = {}) {
2469
2470
  const isQuery = val === "?";
2470
2471
  const paletteMatch = /^color(\d+)$/.exec(key);
2471
2472
  if (paletteMatch) {
2472
- const idx = parseInt(paletteMatch[1], 10);
2473
+ const idx = parseInt(itemAt(paletteMatch, 1, "OSC 104 palette capture"), 10);
2473
2474
  if (idx < 0 || idx > 255) continue;
2474
2475
  if (isQuery) {
2475
2476
  const c = palette256[idx];
2476
2477
  if (c) response.push(`${key}=${formatColorResponse(c)}`);
2477
2478
  } else if (val === "") {
2478
2479
  const fresh = buildPalette256();
2479
- palette256[idx] = fresh[idx];
2480
+ palette256[idx] = itemAt(fresh, idx, "fresh ANSI palette");
2480
2481
  } else {
2481
2482
  const c = parseColorSpec(val);
2482
2483
  if (c) palette256[idx] = c;
2483
2484
  }
2484
- } else if (key === "foreground") if (isQuery) {
2485
- const c = defaultFgColor ?? {
2486
- r: 255,
2487
- g: 255,
2488
- b: 255
2489
- };
2490
- response.push(`${key}=${formatColorResponse(c)}`);
2491
- } else if (val === "") defaultFgColor = null;
2492
- else {
2493
- const c = parseColorSpec(val);
2494
- if (c) defaultFgColor = c;
2495
- }
2496
- else if (key === "background") if (isQuery) {
2497
- const c = defaultBgColor ?? {
2498
- r: 0,
2499
- g: 0,
2500
- b: 0
2501
- };
2502
- response.push(`${key}=${formatColorResponse(c)}`);
2503
- } else if (val === "") defaultBgColor = null;
2504
- else {
2505
- const c = parseColorSpec(val);
2506
- if (c) defaultBgColor = c;
2507
- }
2508
- else if (key === "cursor") if (isQuery) {
2509
- const c = cursorColor ?? defaultFgColor ?? {
2510
- r: 255,
2511
- g: 255,
2512
- b: 255
2513
- };
2514
- response.push(`${key}=${formatColorResponse(c)}`);
2515
- } else if (val === "") cursorColor = null;
2516
- else {
2517
- const c = parseColorSpec(val);
2518
- if (c) cursorColor = c;
2485
+ } else if (key === "foreground") {
2486
+ if (isQuery) {
2487
+ const c = defaultFgColor ?? {
2488
+ r: 255,
2489
+ g: 255,
2490
+ b: 255
2491
+ };
2492
+ response.push(`${key}=${formatColorResponse(c)}`);
2493
+ } else if (val === "") defaultFgColor = null;
2494
+ else {
2495
+ const c = parseColorSpec(val);
2496
+ if (c) defaultFgColor = c;
2497
+ }
2498
+ } else if (key === "background") {
2499
+ if (isQuery) {
2500
+ const c = defaultBgColor ?? {
2501
+ r: 0,
2502
+ g: 0,
2503
+ b: 0
2504
+ };
2505
+ response.push(`${key}=${formatColorResponse(c)}`);
2506
+ } else if (val === "") defaultBgColor = null;
2507
+ else {
2508
+ const c = parseColorSpec(val);
2509
+ if (c) defaultBgColor = c;
2510
+ }
2511
+ } else if (key === "cursor") {
2512
+ if (isQuery) {
2513
+ const c = cursorColor ?? defaultFgColor ?? {
2514
+ r: 255,
2515
+ g: 255,
2516
+ b: 255
2517
+ };
2518
+ response.push(`${key}=${formatColorResponse(c)}`);
2519
+ } else if (val === "") cursorColor = null;
2520
+ else {
2521
+ const c = parseColorSpec(val);
2522
+ if (c) cursorColor = c;
2523
+ }
2524
+ } else if (key === "selection_background") {
2525
+ if (isQuery) {
2526
+ const c = highlightBgColor ?? defaultFgColor ?? {
2527
+ r: 255,
2528
+ g: 255,
2529
+ b: 255
2530
+ };
2531
+ response.push(`${key}=${formatColorResponse(c)}`);
2532
+ } else highlightBgColor = val === "" ? null : parseColorSpec(val);
2533
+ } else if (key === "selection_foreground") {
2534
+ if (isQuery) {
2535
+ const c = highlightFgColor ?? defaultBgColor ?? {
2536
+ r: 0,
2537
+ g: 0,
2538
+ b: 0
2539
+ };
2540
+ response.push(`${key}=${formatColorResponse(c)}`);
2541
+ } else highlightFgColor = val === "" ? null : parseColorSpec(val);
2519
2542
  }
2520
- else if (key === "selection_background") if (isQuery) {
2521
- const c = highlightBgColor ?? defaultFgColor ?? {
2522
- r: 255,
2523
- g: 255,
2524
- b: 255
2525
- };
2526
- response.push(`${key}=${formatColorResponse(c)}`);
2527
- } else highlightBgColor = val === "" ? null : parseColorSpec(val);
2528
- else if (key === "selection_foreground") if (isQuery) {
2529
- const c = highlightFgColor ?? defaultBgColor ?? {
2530
- r: 0,
2531
- g: 0,
2532
- b: 0
2533
- };
2534
- response.push(`${key}=${formatColorResponse(c)}`);
2535
- } else highlightFgColor = val === "" ? null : parseColorSpec(val);
2536
2543
  }
2537
2544
  if (response.length > 0 && onResponse) onResponse(`\x1b]21;${response.join(";")}\x1b\\`);
2538
2545
  break;
@@ -2543,7 +2550,7 @@ function createScreen(options = {}) {
2543
2550
  const fresh = buildPalette256();
2544
2551
  for (const tok of value.split(";")) {
2545
2552
  const idx = parseInt(tok, 10);
2546
- if (!isNaN(idx) && idx >= 0 && idx < 256) palette256[idx] = fresh[idx];
2553
+ if (!isNaN(idx) && idx >= 0 && idx < 256) palette256[idx] = itemAt(fresh, idx, "fresh ANSI palette");
2547
2554
  }
2548
2555
  }
2549
2556
  break;
@@ -2614,14 +2621,11 @@ function createScreen(options = {}) {
2614
2621
  startCol: curX
2615
2622
  });
2616
2623
  break;
2617
- case "C":
2618
- semanticZones.push({
2619
- type: "output",
2620
- startRow: curY,
2621
- startCol: curX
2622
- });
2623
- break;
2624
- case "D": break;
2624
+ case "C": semanticZones.push({
2625
+ type: "output",
2626
+ startRow: curY,
2627
+ startCol: curX
2628
+ });
2625
2629
  }
2626
2630
  break;
2627
2631
  case 66:
@@ -2694,10 +2698,8 @@ function createScreen(options = {}) {
2694
2698
  advancedClipboard = "";
2695
2699
  }
2696
2700
  break;
2697
- case 1337:
2698
- if (value === "ReportCellSize" && onResponse) onResponse("\x1B]1337;ReportCellSize=16;8\x1B\\");
2699
- else if (value === "RequestCapabilities" && onResponse) onResponse("\x1B]1337;Capabilities=\x1B\\");
2700
- break;
2701
+ case 1337: if (value === "ReportCellSize" && onResponse) onResponse("\x1B]1337;ReportCellSize=16;8\x1B\\");
2702
+ else if (value === "RequestCapabilities" && onResponse) onResponse("\x1B]1337;Capabilities=\x1B\\");
2701
2703
  }
2702
2704
  }
2703
2705
  function handleDCS(data) {
@@ -2705,7 +2707,7 @@ function createScreen(options = {}) {
2705
2707
  if (match) {
2706
2708
  hasSixel = true;
2707
2709
  sixelImages.push({
2708
- data: match[2],
2710
+ data: itemAt(match, 2, "sixel payload capture"),
2709
2711
  row: dcsStartRow,
2710
2712
  col: dcsStartCol
2711
2713
  });
@@ -2759,6 +2761,8 @@ function createScreen(options = {}) {
2759
2761
  rightMargin = cols - 1;
2760
2762
  attrs = resetAttrs();
2761
2763
  charsetG0 = false;
2764
+ charsetG1 = false;
2765
+ activeG1 = false;
2762
2766
  curX = 0;
2763
2767
  curY = 0;
2764
2768
  }
@@ -2781,7 +2785,9 @@ function createScreen(options = {}) {
2781
2785
  attrs: resetAttrs(),
2782
2786
  originMode: false,
2783
2787
  autoWrap: true,
2784
- charsetG0: false
2788
+ charsetG0: false,
2789
+ charsetG1: false,
2790
+ activeG1: false
2785
2791
  };
2786
2792
  attrs = resetAttrs();
2787
2793
  title = "";
@@ -2820,6 +2826,8 @@ function createScreen(options = {}) {
2820
2826
  advancedClipboard = "";
2821
2827
  viewportOffset = 0;
2822
2828
  charsetG0 = false;
2829
+ charsetG1 = false;
2830
+ activeG1 = false;
2823
2831
  clipboard = "";
2824
2832
  cwd = "";
2825
2833
  notifications = [];
@@ -3015,6 +3023,12 @@ function createScreen(options = {}) {
3015
3023
  } else if (code === 13) {
3016
3024
  curX = 0;
3017
3025
  emitExecute(13);
3026
+ } else if (code === 14) {
3027
+ activeG1 = true;
3028
+ emitExecute(14);
3029
+ } else if (code === 15) {
3030
+ activeG1 = false;
3031
+ emitExecute(15);
3018
3032
  } else if (code >= 32) {
3019
3033
  let char = ch;
3020
3034
  if (code >= 55296 && code <= 56319 && i + 1 < text.length) {
@@ -3067,7 +3081,9 @@ function createScreen(options = {}) {
3067
3081
  },
3068
3082
  originMode,
3069
3083
  autoWrap,
3070
- charsetG0
3084
+ charsetG0,
3085
+ charsetG1,
3086
+ activeG1
3071
3087
  };
3072
3088
  parserState = "ground";
3073
3089
  emitEsc("7", "");
@@ -3083,6 +3099,8 @@ function createScreen(options = {}) {
3083
3099
  originMode = savedState.originMode;
3084
3100
  autoWrap = savedState.autoWrap;
3085
3101
  charsetG0 = savedState.charsetG0;
3102
+ charsetG1 = savedState.charsetG1;
3103
+ activeG1 = savedState.activeG1;
3086
3104
  clampCursor();
3087
3105
  parserState = "ground";
3088
3106
  emitEsc("8", "");
@@ -3104,9 +3122,11 @@ function createScreen(options = {}) {
3104
3122
  parserState = "escape_hash";
3105
3123
  } else if (ch === "(") {
3106
3124
  escIntermediate = "(";
3125
+ escBuf = "(";
3107
3126
  parserState = "escape_charset";
3108
3127
  } else if (ch === ")") {
3109
3128
  escIntermediate = ")";
3129
+ escBuf = ")";
3110
3130
  parserState = "escape_charset";
3111
3131
  } else if (ch === "=") {
3112
3132
  applicationKeypad = true;
@@ -3126,17 +3146,19 @@ function createScreen(options = {}) {
3126
3146
  } else parserState = "ground";
3127
3147
  break;
3128
3148
  case "escape_charset":
3129
- if (ch === "0") charsetG0 = true;
3130
- else charsetG0 = false;
3149
+ const designator = escBuf === ")" ? ")" : "(";
3150
+ if (designator === ")") charsetG1 = ch === "0";
3151
+ else charsetG0 = ch === "0";
3152
+ escBuf = "";
3131
3153
  parserState = "ground";
3132
- emitEsc(ch, escIntermediate);
3154
+ emitEsc(ch, designator);
3133
3155
  break;
3134
3156
  case "escape_hash":
3135
3157
  if (ch === "8") {
3136
3158
  const alignCell = emptyCell();
3137
3159
  alignCell.char = "E";
3138
3160
  for (let r = 0; r < rows; r++) {
3139
- const row = grid[r];
3161
+ const row = itemAt(grid, r, "selective erase rows");
3140
3162
  for (let c = 0; c < cols; c++) row.setCellRaw(c, alignCell);
3141
3163
  softWrapped[r] = false;
3142
3164
  }
@@ -3223,7 +3245,6 @@ function createScreen(options = {}) {
3223
3245
  apcOverflow = false;
3224
3246
  }
3225
3247
  parserState = "ground";
3226
- break;
3227
3248
  }
3228
3249
  }
3229
3250
  flushPrintRun();
@@ -3283,10 +3304,10 @@ function createScreen(options = {}) {
3283
3304
  const outLine = [];
3284
3305
  let tracked = null;
3285
3306
  for (let li = 0; li < logicalLines.length; li++) {
3286
- const line = logicalLines[li];
3307
+ const line = itemAt(logicalLines, li, "reflow logical lines");
3287
3308
  let lineLen = line.length;
3288
3309
  while (lineLen > 0) {
3289
- const cell = line[lineLen - 1];
3310
+ const cell = itemAt(line, lineLen - 1, "reflow logical-line cells");
3290
3311
  if (cell.char === "" && !cell.wide) lineLen--;
3291
3312
  else break;
3292
3313
  }
@@ -3305,7 +3326,7 @@ function createScreen(options = {}) {
3305
3326
  const row = makeRow(newCols);
3306
3327
  let col = 0;
3307
3328
  while (col < newCols && pos < lineLen) {
3308
- const cell = line[pos];
3329
+ const cell = itemAt(line, pos, "reflow logical-line cells");
3309
3330
  if (cell.wide && col + 2 > newCols) break;
3310
3331
  if (track?.line === li && pos === track.offset) tracked = {
3311
3332
  row: outRows.length,
@@ -3351,14 +3372,11 @@ function createScreen(options = {}) {
3351
3372
  */
3352
3373
  function trimTrailingEmptyRows(result) {
3353
3374
  const floor = result.tracked == null ? 1 : Math.max(1, result.tracked.row + 1);
3354
- while (result.rows.length > floor) {
3355
- const lastRow = result.rows[result.rows.length - 1];
3356
- if (packedRowIsBlank(lastRow) && !result.wrapped[result.rows.length - 2]) {
3357
- result.rows.pop();
3358
- result.wrapped.pop();
3359
- result.lineIndex?.pop();
3360
- } else break;
3361
- }
3375
+ while (result.rows.length > floor) if (packedRowIsBlank(itemAt(result.rows, result.rows.length - 1, "reflow result rows")) && !result.wrapped[result.rows.length - 2]) {
3376
+ result.rows.pop();
3377
+ result.wrapped.pop();
3378
+ result.lineIndex?.pop();
3379
+ } else break;
3362
3380
  }
3363
3381
  function resize(newCols, newRows) {
3364
3382
  if (newCols === cols && newRows === rows) return;
@@ -3388,7 +3406,7 @@ function createScreen(options = {}) {
3388
3406
  if (restoredRows.length > 0) {
3389
3407
  let completed = 0;
3390
3408
  for (const wrapped of restoredWrapped) if (!wrapped) completed++;
3391
- if (cursorTrack.line === 0 && restoredWrapped[restoredWrapped.length - 1] === true) for (let i = restoredWrapped.length - 1; i >= 0 && restoredWrapped[i]; i--) cursorTrack.offset += restoredRows[i].length;
3409
+ if (cursorTrack.line === 0 && restoredWrapped[restoredWrapped.length - 1] === true) for (let i = restoredWrapped.length - 1; i >= 0 && restoredWrapped[i]; i--) cursorTrack.offset += itemAt(restoredRows, i, "restored rows").length;
3392
3410
  cursorTrack.line += completed;
3393
3411
  }
3394
3412
  const mainSrc = restoredRows.length > 0 ? [...restoredRows, ...mainGrid] : mainGrid;
@@ -3410,8 +3428,8 @@ function createScreen(options = {}) {
3410
3428
  if (overflow !== void 0) bankScrollbackRow(overflow, mainResult.wrapped[r] ?? false, mainLogicalAttrs[mainResult.lineIndex[r] ?? 0] ?? 0);
3411
3429
  }
3412
3430
  for (let r = 0; r < newRows && mainStartRow + r < mainResult.rows.length; r++) {
3413
- newMain[r] = mainResult.rows[mainStartRow + r];
3414
- newMainWrapped[r] = mainResult.wrapped[mainStartRow + r];
3431
+ newMain[r] = itemAt(mainResult.rows, mainStartRow + r, "resized main rows");
3432
+ newMainWrapped[r] = itemAt(mainResult.wrapped, mainStartRow + r, "resized main wrap flags");
3415
3433
  newMainAttrs[r] = mainLogicalAttrs[mainResult.lineIndex[mainStartRow + r] ?? 0] ?? 0;
3416
3434
  }
3417
3435
  const newAlt = makeGrid(newCols, newRows);
@@ -3419,8 +3437,8 @@ function createScreen(options = {}) {
3419
3437
  const newAltAttrs = new Array(newRows).fill(0);
3420
3438
  const altStartRow = Math.max(0, altResult.rows.length - newRows);
3421
3439
  for (let r = 0; r < newRows && altStartRow + r < altResult.rows.length; r++) {
3422
- newAlt[r] = altResult.rows[altStartRow + r];
3423
- newAltWrapped[r] = altResult.wrapped[altStartRow + r];
3440
+ newAlt[r] = itemAt(altResult.rows, altStartRow + r, "resized alternate rows");
3441
+ newAltWrapped[r] = itemAt(altResult.wrapped, altStartRow + r, "resized alternate wrap flags");
3424
3442
  newAltAttrs[r] = altLogicalAttrs[altResult.lineIndex[altStartRow + r] ?? 0] ?? 0;
3425
3443
  }
3426
3444
  const activeResult = useAltScreen ? altResult : mainResult;
@@ -3492,7 +3510,9 @@ function createScreen(options = {}) {
3492
3510
  attrs: cloneAttrsSnapshot(savedState.attrs),
3493
3511
  originMode: savedState.originMode,
3494
3512
  autoWrap: savedState.autoWrap,
3495
- charsetG0: savedState.charsetG0
3513
+ charsetG0: savedState.charsetG0,
3514
+ charsetG1: savedState.charsetG1,
3515
+ activeG1: savedState.activeG1
3496
3516
  },
3497
3517
  attrs: cloneAttrsSnapshot(attrs),
3498
3518
  modes: {
@@ -3556,6 +3576,8 @@ function createScreen(options = {}) {
3556
3576
  },
3557
3577
  unicode: {
3558
3578
  charsetG0,
3579
+ charsetG1,
3580
+ activeG1,
3559
3581
  lastChar,
3560
3582
  pendingRegionalIndicator,
3561
3583
  afterZWJ
@@ -3593,7 +3615,9 @@ function createScreen(options = {}) {
3593
3615
  attrs: cloneAttrsSnapshot(snapshotValue.savedState.attrs),
3594
3616
  originMode: snapshotValue.savedState.originMode,
3595
3617
  autoWrap: snapshotValue.savedState.autoWrap,
3596
- charsetG0: snapshotValue.savedState.charsetG0
3618
+ charsetG0: snapshotValue.savedState.charsetG0,
3619
+ charsetG1: snapshotValue.savedState.charsetG1 ?? false,
3620
+ activeG1: snapshotValue.savedState.activeG1 ?? false
3597
3621
  };
3598
3622
  attrs = cloneAttrsSnapshot(snapshotValue.attrs);
3599
3623
  bracketedPaste = snapshotValue.modes.bracketedPaste;
@@ -3645,6 +3669,8 @@ function createScreen(options = {}) {
3645
3669
  apcOverflow = snapshotValue.parser.apcOverflow ?? false;
3646
3670
  utf8PendingBytes = [...snapshotValue.parser.utf8PendingBytes];
3647
3671
  charsetG0 = snapshotValue.unicode.charsetG0;
3672
+ charsetG1 = snapshotValue.unicode.charsetG1 ?? false;
3673
+ activeG1 = snapshotValue.unicode.activeG1 ?? false;
3648
3674
  lastChar = snapshotValue.unicode.lastChar;
3649
3675
  pendingRegionalIndicator = snapshotValue.unicode.pendingRegionalIndicator;
3650
3676
  afterZWJ = snapshotValue.unicode.afterZWJ;
@@ -3678,7 +3704,7 @@ function createScreen(options = {}) {
3678
3704
  }
3679
3705
  function getText() {
3680
3706
  const lines = [];
3681
- for (let r = 0; r < rows; r++) lines.push(rowToString(grid[r]));
3707
+ for (let r = 0; r < rows; r++) lines.push(rowToString(itemAt(grid, r, "screen text rows")));
3682
3708
  return lines.join("\n");
3683
3709
  }
3684
3710
  function getScrollbackText() {
@@ -3697,7 +3723,7 @@ function createScreen(options = {}) {
3697
3723
  }
3698
3724
  function getRowAbsolute(row) {
3699
3725
  if (row < 0 || row >= scrollback.length + rows) return makeRow(cols).toCells();
3700
- return (row < scrollback.length ? scrollback[row] : grid[row - scrollback.length]).toCells().map(stripCellColorIndex);
3726
+ return (row < scrollback.length ? itemAt(scrollback, row, "scrollback rows") : itemAt(grid, row - scrollback.length, "screen rows")).toCells().map(stripCellColorIndex);
3701
3727
  }
3702
3728
  function firstRetainedRow() {
3703
3729
  return trimmedRowCount;
@@ -4267,7 +4293,7 @@ function serializeSnapshot(snapshot, options = {}) {
4267
4293
  const wraps = snapshot.scrollbackSoftWrapped;
4268
4294
  const last = snapshot.scrollback.length - 1;
4269
4295
  for (let i = 0; i < snapshot.scrollback.length; i++) {
4270
- const row = snapshot.scrollback[i];
4296
+ const row = itemAt(snapshot.scrollback, i, "snapshot scrollback rows");
4271
4297
  if (wraps?.[i] === true && i < last) out.push(serializeEncodeRow(row, hyperlinks, snapshot.cols), "\x1B[0m");
4272
4298
  else out.push(serializeEncodeRow(row, hyperlinks), "\x1B[0m\r\n");
4273
4299
  }
@@ -4306,6 +4332,8 @@ function serializeSnapshot(snapshot, options = {}) {
4306
4332
  out.push(m.autoWrap ? "\x1B[?7h" : "\x1B[?7l");
4307
4333
  if (snapshot.cursor.shape !== "block" || !snapshot.cursor.blinking) out.push(`\x1b[${String(serializeCursorStyleCode(snapshot.cursor.shape, snapshot.cursor.blinking))} q`);
4308
4334
  if (snapshot.unicode.charsetG0) out.push("\x1B(0");
4335
+ if (snapshot.unicode.charsetG1) out.push("\x1B)0");
4336
+ if (snapshot.unicode.activeG1) out.push("");
4309
4337
  if (m.insert && !excluded.has("insert")) out.push("\x1B[4h");
4310
4338
  if (!serializeTabStopsAreDefault(snapshot.tabStops, snapshot.cols)) {
4311
4339
  out.push("\x1B[3g");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vterm.js",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Modern terminal emulator — full VT/ECMA-48/xterm standards coverage. Pure TypeScript, zero dependencies.",
5
5
  "keywords": [
6
6
  "ansi",
@@ -46,5 +46,8 @@
46
46
  },
47
47
  "engines": {
48
48
  "node": ">=23.6.0"
49
+ },
50
+ "scripts": {
51
+ "build": "tsdown"
49
52
  }
50
53
  }