vterm.js 0.7.1 → 0.8.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/README.md +3 -1
- package/dist/index.d.mts +10 -1
- package/dist/index.mjs +30 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Part of the [vterm](https://github.com/beorn/vterm) monorepo.
|
|
|
20
20
|
- **Synchronized output** — mode 2026
|
|
21
21
|
- **Application cursor keys & keypad**
|
|
22
22
|
- **Kitty keyboard protocol** — progressive-enhancement flags with a push/pop/set stack (`CSI u`)
|
|
23
|
-
- **OSC sequences** — window
|
|
23
|
+
- **OSC sequences** — independent icon/window labels (OSC 0 sets both, OSC 1 icon, OSC 2 window), hyperlinks (OSC 8), clipboard (OSC 52), colors
|
|
24
24
|
- **DCS sequences** — consumed and ignored, XTVERSION response
|
|
25
25
|
- **Device attributes** — DA1/DA2/DA3 responses
|
|
26
26
|
- **Device status reports** — DSR responses
|
|
@@ -140,6 +140,8 @@ console.log(restored.getText()) // "Hello, Bold Green World!" — text and style
|
|
|
140
140
|
|
|
141
141
|
**Soft-wrap history.** `Snapshot` carries `scrollbackSoftWrapped: boolean[]` alongside `scrollback` — bit `i` true means scrollback row `i` wraps into row `i+1` (one logical line split across rows); an absent array (older stored v1 snapshots) is treated as all-false. Phase-1 history re-links a wrapped row by emitting it at full width with no line break, so the receiver's own autowrap re-records the bit as the row scrolls back.
|
|
142
142
|
|
|
143
|
+
**Icon/window labels.** `Snapshot.title` remains the window title. New v1 snapshots include optional `iconLabel`, including empty strings; the binary codec preserves whether that field is present. Only `restore()` defaults legacy absence to `title` to preserve old CSI20 behavior; historical OSC0 versus OSC2 intent cannot be recovered. An own `undefined`, `null` or other non-string value is rejected. Older readers ignore the new JSON-header key and lose independent icon semantics. `getTitle()` and `title$` remain window-only. `includeTitle:true` emits a nonempty window title with OSC2, preserving the receiver's independent icon instead of the former OSC0 side effect. Default title omission and empty-title omission remain; ANSI replay does not transfer the icon label.
|
|
144
|
+
|
|
143
145
|
### Ops and taps
|
|
144
146
|
|
|
145
147
|
`apply(op)` is the single public write entry — the same seam a session journal records and replays. An op is coarse, serializable data; **bytes stay the canonical encoding** (an `output` op is just bytes, deterministically re-parsed on replay — vterm never reifies a per-VT-action op):
|
package/dist/index.d.mts
CHANGED
|
@@ -85,6 +85,13 @@ interface ScreenOptions {
|
|
|
85
85
|
cols?: number;
|
|
86
86
|
rows?: number;
|
|
87
87
|
scrollbackLimit?: number;
|
|
88
|
+
/**
|
|
89
|
+
* The embedder renders or relays kitty graphics images, so this screen may
|
|
90
|
+
* claim them: graphics queries answer OK and the kittyGraphics mode is true.
|
|
91
|
+
* Default false — this screen stores no image, so it refuses queries and
|
|
92
|
+
* reports no capability (#31020).
|
|
93
|
+
*/
|
|
94
|
+
kittyGraphicsImages?: boolean;
|
|
88
95
|
/** Callback for DA1/DA2/DSR responses — write these back to the PTY */
|
|
89
96
|
onResponse?: (data: string) => void;
|
|
90
97
|
/**
|
|
@@ -246,6 +253,8 @@ interface Snapshot {
|
|
|
246
253
|
};
|
|
247
254
|
tabStops: number[];
|
|
248
255
|
title: string;
|
|
256
|
+
/** Independent icon label. Legacy absence restores from title; a supplied value must be a string. */
|
|
257
|
+
iconLabel?: string;
|
|
249
258
|
clipboard: string;
|
|
250
259
|
cwd: string;
|
|
251
260
|
notifications: string[];
|
|
@@ -572,7 +581,7 @@ declare function createScreen(options?: ScreenOptions): Screen;
|
|
|
572
581
|
interface SerializeOptions {
|
|
573
582
|
/** Emit scrollback history rows before the screen paint. Default: true. */
|
|
574
583
|
includeScrollback?: boolean;
|
|
575
|
-
/** Emit the window title (OSC
|
|
584
|
+
/** Emit only the window title (OSC 2) when non-empty, preserving the receiver's icon label. Default: false. */
|
|
576
585
|
includeTitle?: boolean;
|
|
577
586
|
/** Emit OSC 8 hyperlinks for cell and pending-pen urls. Default: true. */
|
|
578
587
|
hyperlinks?: boolean;
|
package/dist/index.mjs
CHANGED
|
@@ -478,6 +478,7 @@ function createScreen(options = {}) {
|
|
|
478
478
|
let rows = options.rows ?? 24;
|
|
479
479
|
let scrollbackLimit = options.scrollbackLimit ?? 1e3;
|
|
480
480
|
const onResponse = options.onResponse;
|
|
481
|
+
const kittyGraphicsImages = options.kittyGraphicsImages ?? false;
|
|
481
482
|
const maxStringSequenceLength = options.maxStringSequenceLength ?? 16777216;
|
|
482
483
|
if (!Number.isSafeInteger(maxStringSequenceLength) || maxStringSequenceLength < 0) throw new RangeError("maxStringSequenceLength must be a non-negative safe integer");
|
|
483
484
|
let mainGrid = makeGrid(cols, rows);
|
|
@@ -503,6 +504,7 @@ function createScreen(options = {}) {
|
|
|
503
504
|
let savedCurY = 0;
|
|
504
505
|
let attrs = resetAttrs();
|
|
505
506
|
let title = "";
|
|
507
|
+
let iconLabel = "";
|
|
506
508
|
let useAltScreen = false;
|
|
507
509
|
let bracketedPaste = false;
|
|
508
510
|
let applicationCursor = false;
|
|
@@ -519,7 +521,6 @@ function createScreen(options = {}) {
|
|
|
519
521
|
let syncOutput = false;
|
|
520
522
|
let kittyKeyboardFlags = 0;
|
|
521
523
|
let kittyKeyboardStack = [];
|
|
522
|
-
let hasKittyGraphics = false;
|
|
523
524
|
let scrollTop = 0;
|
|
524
525
|
let scrollBottom = rows - 1;
|
|
525
526
|
let leftRightMarginMode = false;
|
|
@@ -716,6 +717,7 @@ function createScreen(options = {}) {
|
|
|
716
717
|
if (!Array.isArray(value.tabStops)) throw new TypeError("Invalid vterm snapshot tabStops");
|
|
717
718
|
for (const stop of value.tabStops) assertInteger("tabStops", stop);
|
|
718
719
|
assertString("title", value.title);
|
|
720
|
+
if (Object.hasOwn(value, "iconLabel")) assertString("iconLabel", value.iconLabel);
|
|
719
721
|
assertString("clipboard", value.clipboard);
|
|
720
722
|
assertString("cwd", value.cwd);
|
|
721
723
|
if (!Array.isArray(value.notifications) || value.notifications.some((entry) => typeof entry !== "string")) throw new TypeError("Invalid vterm snapshot notifications");
|
|
@@ -782,9 +784,6 @@ function createScreen(options = {}) {
|
|
|
782
784
|
return 0;
|
|
783
785
|
}
|
|
784
786
|
let textScale = 1;
|
|
785
|
-
const CELL_W_PX = 8;
|
|
786
|
-
const CELL_H_PX = 17;
|
|
787
|
-
const FONT_ASCENT_PX = 14;
|
|
788
787
|
let fontSize = 12;
|
|
789
788
|
let fontWindowSize = 12;
|
|
790
789
|
let locale = "en_US.UTF-8";
|
|
@@ -1465,20 +1464,18 @@ function createScreen(options = {}) {
|
|
|
1465
1464
|
case "t": {
|
|
1466
1465
|
const op = parts[0] ?? 0;
|
|
1467
1466
|
if (!onResponse) break;
|
|
1468
|
-
const CELL_H = 16;
|
|
1469
|
-
const CELL_W = 8;
|
|
1470
1467
|
switch (op) {
|
|
1471
1468
|
case 14:
|
|
1472
|
-
onResponse(
|
|
1469
|
+
onResponse("\x1B[4;0;0t");
|
|
1473
1470
|
break;
|
|
1474
1471
|
case 16:
|
|
1475
|
-
onResponse(
|
|
1472
|
+
onResponse("\x1B[6;0;0t");
|
|
1476
1473
|
break;
|
|
1477
1474
|
case 18:
|
|
1478
1475
|
onResponse(`\x1b[8;${rows};${cols}t`);
|
|
1479
1476
|
break;
|
|
1480
1477
|
case 20:
|
|
1481
|
-
onResponse(`\x1b]L${
|
|
1478
|
+
onResponse(`\x1b]L${iconLabel}\x1b\\`);
|
|
1482
1479
|
break;
|
|
1483
1480
|
case 21: onResponse(`\x1b]l${title}\x1b\\`);
|
|
1484
1481
|
}
|
|
@@ -2302,6 +2299,9 @@ function createScreen(options = {}) {
|
|
|
2302
2299
|
const value = semicolonIdx === -1 ? "" : oscString.substring(semicolonIdx + 1);
|
|
2303
2300
|
switch (code) {
|
|
2304
2301
|
case 0:
|
|
2302
|
+
iconLabel = value;
|
|
2303
|
+
title = value;
|
|
2304
|
+
break;
|
|
2305
2305
|
case 2:
|
|
2306
2306
|
title = value;
|
|
2307
2307
|
break;
|
|
@@ -2328,7 +2328,9 @@ function createScreen(options = {}) {
|
|
|
2328
2328
|
});
|
|
2329
2329
|
}
|
|
2330
2330
|
break;
|
|
2331
|
-
case 1:
|
|
2331
|
+
case 1:
|
|
2332
|
+
iconLabel = value;
|
|
2333
|
+
break;
|
|
2332
2334
|
case 7:
|
|
2333
2335
|
cwd = value;
|
|
2334
2336
|
break;
|
|
@@ -2660,7 +2662,7 @@ function createScreen(options = {}) {
|
|
|
2660
2662
|
break;
|
|
2661
2663
|
}
|
|
2662
2664
|
case 776:
|
|
2663
|
-
if (onResponse) onResponse(
|
|
2665
|
+
if (onResponse) onResponse("\x1B]776;0;0;0\x1B\\");
|
|
2664
2666
|
break;
|
|
2665
2667
|
case 7770:
|
|
2666
2668
|
if (value === "?") {
|
|
@@ -2698,7 +2700,7 @@ function createScreen(options = {}) {
|
|
|
2698
2700
|
advancedClipboard = "";
|
|
2699
2701
|
}
|
|
2700
2702
|
break;
|
|
2701
|
-
case 1337: if (value === "ReportCellSize" && onResponse) onResponse("\x1B]1337;ReportCellSize=
|
|
2703
|
+
case 1337: if (value === "ReportCellSize" && onResponse) onResponse("\x1B]1337;ReportCellSize=0;0\x1B\\");
|
|
2702
2704
|
else if (value === "RequestCapabilities" && onResponse) onResponse("\x1B]1337;Capabilities=\x1B\\");
|
|
2703
2705
|
}
|
|
2704
2706
|
}
|
|
@@ -2728,7 +2730,6 @@ function createScreen(options = {}) {
|
|
|
2728
2730
|
}
|
|
2729
2731
|
function handleAPC(data) {
|
|
2730
2732
|
if (!data.startsWith("G")) return;
|
|
2731
|
-
hasKittyGraphics = true;
|
|
2732
2733
|
const semicolonIdx = data.indexOf(";");
|
|
2733
2734
|
const kvPart = semicolonIdx >= 0 ? data.substring(1, semicolonIdx) : data.substring(1);
|
|
2734
2735
|
const params = {};
|
|
@@ -2736,7 +2737,11 @@ function createScreen(options = {}) {
|
|
|
2736
2737
|
const eqIdx = pair.indexOf("=");
|
|
2737
2738
|
if (eqIdx >= 0) params[pair.substring(0, eqIdx)] = pair.substring(eqIdx + 1);
|
|
2738
2739
|
}
|
|
2739
|
-
if (params.a === "q" && onResponse)
|
|
2740
|
+
if (params.a === "q" && onResponse) {
|
|
2741
|
+
if (kittyGraphicsImages) {
|
|
2742
|
+
if (params.q !== "1" && params.q !== "2") onResponse(`\x1b_Gi=${params.i ?? "0"};OK\x1b\\`);
|
|
2743
|
+
} else if (params.q !== "2") onResponse(`\x1b_Gi=${params.i ?? "0"};ENOTSUP\x1b\\`);
|
|
2744
|
+
}
|
|
2740
2745
|
}
|
|
2741
2746
|
function softReset() {
|
|
2742
2747
|
insertMode = false;
|
|
@@ -2791,6 +2796,7 @@ function createScreen(options = {}) {
|
|
|
2791
2796
|
};
|
|
2792
2797
|
attrs = resetAttrs();
|
|
2793
2798
|
title = "";
|
|
2799
|
+
iconLabel = "";
|
|
2794
2800
|
useAltScreen = false;
|
|
2795
2801
|
bracketedPaste = false;
|
|
2796
2802
|
applicationCursor = false;
|
|
@@ -2807,7 +2813,6 @@ function createScreen(options = {}) {
|
|
|
2807
2813
|
syncOutput = false;
|
|
2808
2814
|
kittyKeyboardFlags = 0;
|
|
2809
2815
|
kittyKeyboardStack = [];
|
|
2810
|
-
hasKittyGraphics = false;
|
|
2811
2816
|
hasSixel = false;
|
|
2812
2817
|
sixelImages = [];
|
|
2813
2818
|
scrollTop = 0;
|
|
@@ -3531,7 +3536,7 @@ function createScreen(options = {}) {
|
|
|
3531
3536
|
syncOutput,
|
|
3532
3537
|
kittyKeyboardFlags,
|
|
3533
3538
|
kittyKeyboardStack: [...kittyKeyboardStack],
|
|
3534
|
-
kittyGraphics:
|
|
3539
|
+
kittyGraphics: kittyGraphicsImages,
|
|
3535
3540
|
colorSchemeReporting,
|
|
3536
3541
|
decColumn: decColumnMode,
|
|
3537
3542
|
altScroll: altScrollMode,
|
|
@@ -3550,6 +3555,7 @@ function createScreen(options = {}) {
|
|
|
3550
3555
|
},
|
|
3551
3556
|
tabStops: [...tabStops].sort((a, b) => a - b),
|
|
3552
3557
|
title,
|
|
3558
|
+
iconLabel,
|
|
3553
3559
|
clipboard,
|
|
3554
3560
|
cwd,
|
|
3555
3561
|
notifications: [...notifications],
|
|
@@ -3635,7 +3641,6 @@ function createScreen(options = {}) {
|
|
|
3635
3641
|
syncOutput = snapshotValue.modes.syncOutput;
|
|
3636
3642
|
kittyKeyboardFlags = snapshotValue.modes.kittyKeyboardFlags;
|
|
3637
3643
|
kittyKeyboardStack = [...snapshotValue.modes.kittyKeyboardStack];
|
|
3638
|
-
hasKittyGraphics = snapshotValue.modes.kittyGraphics;
|
|
3639
3644
|
colorSchemeReporting = snapshotValue.modes.colorSchemeReporting;
|
|
3640
3645
|
decColumnMode = snapshotValue.modes.decColumn;
|
|
3641
3646
|
altScrollMode = snapshotValue.modes.altScroll;
|
|
@@ -3650,6 +3655,7 @@ function createScreen(options = {}) {
|
|
|
3650
3655
|
colorStack.push(...snapshotValue.colors.stack.map(cloneColorStateSnapshot));
|
|
3651
3656
|
tabStops = new Set(snapshotValue.tabStops);
|
|
3652
3657
|
title = snapshotValue.title;
|
|
3658
|
+
iconLabel = Object.hasOwn(snapshotValue, "iconLabel") ? snapshotValue.iconLabel : snapshotValue.title;
|
|
3653
3659
|
clipboard = snapshotValue.clipboard;
|
|
3654
3660
|
cwd = snapshotValue.cwd;
|
|
3655
3661
|
notifications = [...snapshotValue.notifications];
|
|
@@ -3798,7 +3804,7 @@ function createScreen(options = {}) {
|
|
|
3798
3804
|
case "leftRightMargin": return leftRightMarginMode;
|
|
3799
3805
|
case "colorSchemeReporting": return colorSchemeReporting;
|
|
3800
3806
|
case "kittyKeyboard": return kittyKeyboardFlags > 0;
|
|
3801
|
-
case "kittyGraphics": return
|
|
3807
|
+
case "kittyGraphics": return kittyGraphicsImages;
|
|
3802
3808
|
case "sixel": return hasSixel;
|
|
3803
3809
|
default: return false;
|
|
3804
3810
|
}
|
|
@@ -3876,7 +3882,7 @@ function createScreen(options = {}) {
|
|
|
3876
3882
|
leftRightMargin: leftRightMarginMode,
|
|
3877
3883
|
colorSchemeReporting,
|
|
3878
3884
|
kittyKeyboard: kittyKeyboardFlags > 0,
|
|
3879
|
-
kittyGraphics:
|
|
3885
|
+
kittyGraphics: kittyGraphicsImages,
|
|
3880
3886
|
sixel: hasSixel
|
|
3881
3887
|
};
|
|
3882
3888
|
}
|
|
@@ -4321,7 +4327,7 @@ function serializeSnapshot(snapshot, options = {}) {
|
|
|
4321
4327
|
const def = SERIALIZE_DEFAULT_PALETTE[i];
|
|
4322
4328
|
if (cur !== void 0 && def !== void 0 && !serializeColorEq(cur, def)) out.push(`\x1b]4;${String(i)};${serializeColorSpec(cur)}\x1b\\`);
|
|
4323
4329
|
}
|
|
4324
|
-
if (options.includeTitle === true && snapshot.title.length > 0) out.push(`\x1b]
|
|
4330
|
+
if (options.includeTitle === true && snapshot.title.length > 0) out.push(`\x1b]2;${snapshot.title}\x1b\\`);
|
|
4325
4331
|
out.push("\x1B[?7l", "\x1B[H\x1B[2J");
|
|
4326
4332
|
const grid = snapshot.activeBuffer === "alt" ? snapshot.alt.grid : snapshot.main.grid;
|
|
4327
4333
|
for (let r = 0; r < rows; r++) {
|
|
@@ -4819,6 +4825,7 @@ function readGridSection(r, softWrapNullable, expected, strings, colors, styles)
|
|
|
4819
4825
|
* deterministic for identical input and decodes back to a deep-equal snapshot.
|
|
4820
4826
|
*/
|
|
4821
4827
|
function encodeScreenSnapshotBinary(snapshot) {
|
|
4828
|
+
if (Object.hasOwn(snapshot, "iconLabel") && typeof snapshot.iconLabel !== "string") throw new TypeError("vterm snapshot codec: invalid iconLabel");
|
|
4822
4829
|
const w = createWriter();
|
|
4823
4830
|
w.u8(VERSION);
|
|
4824
4831
|
const header = {
|
|
@@ -4835,6 +4842,7 @@ function encodeScreenSnapshotBinary(snapshot) {
|
|
|
4835
4842
|
colors: snapshot.colors,
|
|
4836
4843
|
tabStops: snapshot.tabStops,
|
|
4837
4844
|
title: snapshot.title,
|
|
4845
|
+
...Object.hasOwn(snapshot, "iconLabel") ? { iconLabel: snapshot.iconLabel } : {},
|
|
4838
4846
|
clipboard: snapshot.clipboard,
|
|
4839
4847
|
cwd: snapshot.cwd,
|
|
4840
4848
|
notifications: snapshot.notifications,
|
|
@@ -4868,6 +4876,7 @@ function decodeScreenSnapshotBinary(bytes) {
|
|
|
4868
4876
|
if (version !== VERSION) throw new Error(`vterm snapshot codec: unsupported version byte ${version} (expected ${VERSION})`);
|
|
4869
4877
|
const headerLen = r.varint();
|
|
4870
4878
|
const header = JSON.parse(decoder.decode(r.take(headerLen)));
|
|
4879
|
+
if (Object.hasOwn(header, "iconLabel") && typeof header.iconLabel !== "string") throw new TypeError("vterm snapshot codec: invalid iconLabel");
|
|
4871
4880
|
const strings = readStringTable(r);
|
|
4872
4881
|
const colors = readColorTable(r);
|
|
4873
4882
|
const styles = readStyleTable(r);
|
|
@@ -4903,6 +4912,7 @@ function decodeScreenSnapshotBinary(bytes) {
|
|
|
4903
4912
|
colors: header.colors,
|
|
4904
4913
|
tabStops: header.tabStops,
|
|
4905
4914
|
title: header.title,
|
|
4915
|
+
...Object.hasOwn(header, "iconLabel") ? { iconLabel: header.iconLabel } : {},
|
|
4906
4916
|
clipboard: header.clipboard,
|
|
4907
4917
|
cwd: header.cwd,
|
|
4908
4918
|
notifications: header.notifications,
|