@hex1b/web-terminal 0.175.0-alpha.1905.1.34394d2 → 0.175.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 +31 -0
- package/dist/index.js +123 -50
- package/dist/index.js.map +4 -4
- package/dist/program-status.d.ts +6 -0
- package/dist/program-status.d.ts.map +1 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web-terminal.d.ts +2 -1
- package/dist/web-terminal.d.ts.map +1 -1
- package/dist/wire-types.d.ts +3 -1
- package/dist/wire-types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -604,6 +604,37 @@ The per-title bound is not a limit on all parser buffering or saved-stack depth.
|
|
|
604
604
|
An HMP1 snapshot with too much saved title state fails its 16 MiB replay limit
|
|
605
605
|
rather than silently discarding saved titles.
|
|
606
606
|
|
|
607
|
+
### Program status (OSC 7501)
|
|
608
|
+
|
|
609
|
+
`terminal.programStatus` is an immutable snapshot of the last presented
|
|
610
|
+
inventory. Its `records` contain `id`, `state`, nullable `kind`, `progress`,
|
|
611
|
+
`app`, `effectiveApp`, `title` and `message`. The empty ID addresses the root;
|
|
612
|
+
other IDs are slash-separated paths. `find(id)` looks up a record and
|
|
613
|
+
`getDescendants(id)` returns existing descendants, excluding the addressed
|
|
614
|
+
record. Parents need not exist, and their states do not aggregate.
|
|
615
|
+
|
|
616
|
+
Supply `onProgramStatusChange(snapshot)` to receive the initial authoritative
|
|
617
|
+
inventory before mount resolves, then distinct presented updates. The getter
|
|
618
|
+
updates before callbacks. The snapshot and all its records are frozen; older
|
|
619
|
+
snapshots retain their values. Metadata-only updates repaint without cell
|
|
620
|
+
changes. Reports can coalesce between frames: this is current state, not an
|
|
621
|
+
event log. Disconnect/disposal retains the last inventory; check `connected`
|
|
622
|
+
separately.
|
|
623
|
+
|
|
624
|
+
The states are `idle`, `working`, `blocked`, `done` and `error`. A blocked record
|
|
625
|
+
may specify `permission`, `question` or `auth`. Progress is nullable (indeterminate)
|
|
626
|
+
and applies only to working/blocked records. `effectiveApp` resolves the nearest
|
|
627
|
+
ancestor's reported `app`; `app` retains the record's own reported value.
|
|
628
|
+
Clearing replaces the inventory accordingly, rather than exposing a `clear` state.
|
|
629
|
+
Messages and titles are untrusted plain text, not markup or authenticated identity.
|
|
630
|
+
|
|
631
|
+
The paired server emits full inventory in every frame. Frames from a
|
|
632
|
+
pre-extension HWT1 server without this field are treated as an empty inventory;
|
|
633
|
+
malformed present inventories are rejected, never converted to empty state.
|
|
634
|
+
No status UI, notifications, approval operations or automatic acknowledgement
|
|
635
|
+
are supplied by the component. A host can build those on this API. See the
|
|
636
|
+
[WebTerminalDemo scenario](../../samples/WebTerminalDemo/README.md#try-osc-7501-program-status).
|
|
637
|
+
|
|
607
638
|
### Application progress and shell activity
|
|
608
639
|
|
|
609
640
|
`terminal.progress` exposes OSC 9;4 state as `{ state, percentage }`.
|
package/dist/index.js
CHANGED
|
@@ -396,6 +396,67 @@ var init_terminal_palette = __esm({
|
|
|
396
396
|
}
|
|
397
397
|
});
|
|
398
398
|
|
|
399
|
+
// src/program-status.ts
|
|
400
|
+
function text(value, limit) {
|
|
401
|
+
if (value === null) return;
|
|
402
|
+
if (typeof value !== "string" || value.length > limit || /[\u0000-\u001f\u007f-\u009f\ud800-\udfff]/u.test(value) || encoder.encode(value).byteLength > limit) throw new Error("Invalid program status text");
|
|
403
|
+
}
|
|
404
|
+
function validateProgramStatus(value) {
|
|
405
|
+
if (!isRecord(value) || !Array.isArray(value.records) || value.records.length > 256)
|
|
406
|
+
throw new Error("Invalid program status inventory");
|
|
407
|
+
const records = /* @__PURE__ */ new Map();
|
|
408
|
+
for (const record of value.records) {
|
|
409
|
+
if (!isRecord(record) || typeof record.id !== "string" || record.id !== "" && (!idPattern.test(record.id) || record.id.length > 128) || records.has(record.id)) throw new Error("Invalid or duplicate program status ID");
|
|
410
|
+
if (typeof record.state !== "string" || !["idle", "working", "blocked", "done", "error"].includes(record.state))
|
|
411
|
+
throw new Error("Invalid program status state");
|
|
412
|
+
if (record.kind !== null && (record.state !== "blocked" || typeof record.kind !== "string" || !["permission", "question", "auth"].includes(record.kind)))
|
|
413
|
+
throw new Error("Invalid program status kind");
|
|
414
|
+
if (record.progress !== null && (!(record.state === "working" || record.state === "blocked") || typeof record.progress !== "number" || !Number.isInteger(record.progress) || record.progress < 0 || record.progress > 100))
|
|
415
|
+
throw new Error("Invalid program status progress");
|
|
416
|
+
for (const app of [record.app, record.effectiveApp])
|
|
417
|
+
if (app !== null && (typeof app !== "string" || !appPattern.test(app)))
|
|
418
|
+
throw new Error("Invalid program status application");
|
|
419
|
+
text(record.title, 192);
|
|
420
|
+
text(record.message, 2048);
|
|
421
|
+
records.set(record.id, { id: record.id, app: record.app, effectiveApp: record.effectiveApp });
|
|
422
|
+
}
|
|
423
|
+
for (const record of records.values()) {
|
|
424
|
+
let expected = record.app;
|
|
425
|
+
let id = record.id;
|
|
426
|
+
while (expected === null && id !== "") {
|
|
427
|
+
const slash = id.lastIndexOf("/");
|
|
428
|
+
id = slash < 0 ? "" : id.slice(0, slash);
|
|
429
|
+
expected = records.get(id)?.app ?? null;
|
|
430
|
+
}
|
|
431
|
+
if (record.effectiveApp !== expected) throw new Error("Inconsistent program status application inheritance");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function programStatusSnapshot(inventory) {
|
|
435
|
+
const records = Object.freeze(inventory.records.map((record) => Object.freeze({ ...record })));
|
|
436
|
+
const index = new Map(records.map((record) => [record.id, record]));
|
|
437
|
+
return Object.freeze({
|
|
438
|
+
records,
|
|
439
|
+
find: (id) => index.get(id),
|
|
440
|
+
getDescendants: (id) => Object.freeze(records.filter((record) => record.id !== id && (id === "" || record.id.startsWith(`${id}/`))))
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
function sameProgramStatus(left, right) {
|
|
444
|
+
return left.records.length === right.records.length && left.records.every((record, index) => {
|
|
445
|
+
const other = right.records[index];
|
|
446
|
+
return record.id === other.id && record.state === other.state && record.kind === other.kind && record.progress === other.progress && record.app === other.app && record.effectiveApp === other.effectiveApp && record.title === other.title && record.message === other.message;
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
var encoder, idPattern, appPattern;
|
|
450
|
+
var init_program_status = __esm({
|
|
451
|
+
"src/program-status.ts"() {
|
|
452
|
+
"use strict";
|
|
453
|
+
init_validation();
|
|
454
|
+
encoder = new TextEncoder();
|
|
455
|
+
idPattern = /^[A-Za-z0-9_.+-]{1,32}(?:\/[A-Za-z0-9_.+-]{1,32}){0,7}$/u;
|
|
456
|
+
appPattern = /^[A-Za-z0-9_.+-]{1,32}$/u;
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
399
460
|
// src/protocol.ts
|
|
400
461
|
function assertCommandSize(command) {
|
|
401
462
|
if (utf8Encoder.encode(JSON.stringify(command)).byteLength > LIMITS.commandBytes)
|
|
@@ -530,6 +591,8 @@ function validateMetadata(metadata2) {
|
|
|
530
591
|
throw new Error("Invalid terminal title");
|
|
531
592
|
}
|
|
532
593
|
const progress = metadata2.progress;
|
|
594
|
+
if (metadata2.programStatus === void 0) metadata2.programStatus = { records: [] };
|
|
595
|
+
validateProgramStatus(metadata2.programStatus);
|
|
533
596
|
if (!isRecord(progress) || typeof progress.state !== "string" || !["none", "normal", "error", "indeterminate", "warning"].includes(progress.state)) {
|
|
534
597
|
throw new Error("Invalid terminal progress");
|
|
535
598
|
}
|
|
@@ -700,9 +763,9 @@ function decodeFrame(buffer) {
|
|
|
700
763
|
offset += 6;
|
|
701
764
|
if (underlineStyle > 5) throw new Error("Unsupported underline style");
|
|
702
765
|
requireBytes(textLength);
|
|
703
|
-
const
|
|
766
|
+
const text2 = utf8.decode(new Uint8Array(buffer, offset, textLength));
|
|
704
767
|
offset += textLength;
|
|
705
|
-
cells2.push({ index, foreground, background, underlineColor: underlineColor2, attributes, width, underlineStyle, text });
|
|
768
|
+
cells2.push({ index, foreground, background, underlineColor: underlineColor2, attributes, width, underlineStyle, text: text2 });
|
|
706
769
|
}
|
|
707
770
|
const imageBytes = metadata2.images.reduce((total, image) => total + image.byteLength, 0);
|
|
708
771
|
if (imageBytes !== view.byteLength - offset) throw new Error("Image payload length mismatch");
|
|
@@ -733,6 +796,7 @@ var init_protocol = __esm({
|
|
|
733
796
|
init_validation();
|
|
734
797
|
init_marker_state();
|
|
735
798
|
init_terminal_palette();
|
|
799
|
+
init_program_status();
|
|
736
800
|
LIMITS = Object.freeze({
|
|
737
801
|
commandBytes: 64 * 1024,
|
|
738
802
|
frameBytes: 96 * 1024 * 1024,
|
|
@@ -1343,8 +1407,8 @@ struct VertexOut {
|
|
|
1343
1407
|
});
|
|
1344
1408
|
}
|
|
1345
1409
|
if (usedBytes) this.device.queue.writeBuffer(this.instanceBuffer, 0, instances, 0, quadCount * QUAD_STRIDE);
|
|
1346
|
-
const
|
|
1347
|
-
const pass =
|
|
1410
|
+
const encoder2 = this.device.createCommandEncoder();
|
|
1411
|
+
const pass = encoder2.beginRenderPass({
|
|
1348
1412
|
colorAttachments: [{
|
|
1349
1413
|
view: this.context.getCurrentTexture().createView(),
|
|
1350
1414
|
clearValue: { r: background[0], g: background[1], b: background[2], a: 1 },
|
|
@@ -1360,7 +1424,7 @@ struct VertexOut {
|
|
|
1360
1424
|
pass.draw(6, batch.count, 0, batch.start);
|
|
1361
1425
|
}
|
|
1362
1426
|
pass.end();
|
|
1363
|
-
this.device.queue.submit([
|
|
1427
|
+
this.device.queue.submit([encoder2.finish()]);
|
|
1364
1428
|
}
|
|
1365
1429
|
async idle() {
|
|
1366
1430
|
await this.device.queue.onSubmittedWorkDone();
|
|
@@ -2530,7 +2594,7 @@ function send(message) {
|
|
|
2530
2594
|
});
|
|
2531
2595
|
}
|
|
2532
2596
|
}
|
|
2533
|
-
function emitStats(
|
|
2597
|
+
function emitStats(text2) {
|
|
2534
2598
|
if (renderer && !renderer.disposed) Object.assign(stats, renderer.metrics());
|
|
2535
2599
|
else if (renderer?.disposed) Object.assign(stats, {
|
|
2536
2600
|
imageCount: 0,
|
|
@@ -2539,7 +2603,7 @@ function emitStats(text) {
|
|
|
2539
2603
|
atlasBytes: 0,
|
|
2540
2604
|
instanceBufferBytes: 0
|
|
2541
2605
|
});
|
|
2542
|
-
self.postMessage({ type: "stats", stats: { ...stats }, ...
|
|
2606
|
+
self.postMessage({ type: "stats", stats: { ...stats }, ...text2 === void 0 ? {} : { text: text2 } });
|
|
2543
2607
|
}
|
|
2544
2608
|
function fail(error) {
|
|
2545
2609
|
if (failed || stopped) return;
|
|
@@ -2600,7 +2664,7 @@ async function drawFrame() {
|
|
|
2600
2664
|
stats.mouseTracking = metadata.mouseTracking;
|
|
2601
2665
|
stats.peer = metadata.peer;
|
|
2602
2666
|
stats.history = metadata.history;
|
|
2603
|
-
const
|
|
2667
|
+
const text2 = screenText(cells, metadata.columns, metadata.rows);
|
|
2604
2668
|
self.postMessage({
|
|
2605
2669
|
type: "geometry",
|
|
2606
2670
|
columns: metadata.columns,
|
|
@@ -2614,15 +2678,16 @@ async function drawFrame() {
|
|
|
2614
2678
|
title: metadata.title,
|
|
2615
2679
|
progress: metadata.progress,
|
|
2616
2680
|
shellIntegration: metadata.shellIntegration,
|
|
2681
|
+
programStatus: metadata.programStatus ?? { records: [] },
|
|
2617
2682
|
workingDirectory: metadata.workingDirectory,
|
|
2618
2683
|
commandMark: metadata.commandMark,
|
|
2619
|
-
text,
|
|
2684
|
+
text: text2,
|
|
2620
2685
|
hyperlinks: metadata.hyperlinks,
|
|
2621
2686
|
lineRenditions: metadata.lineRenditions,
|
|
2622
2687
|
...links.present(cells, metadata)
|
|
2623
2688
|
});
|
|
2624
2689
|
send({ type: "ack", revision: frame.revision });
|
|
2625
|
-
emitStats(
|
|
2690
|
+
emitStats(text2);
|
|
2626
2691
|
} else {
|
|
2627
2692
|
const snapshot = links.snapshot();
|
|
2628
2693
|
if (snapshot) self.postMessage({ type: "linkSnapshot", generation: links.generation, snapshot });
|
|
@@ -2930,17 +2995,17 @@ var link_detection_worker_exports = {};
|
|
|
2930
2995
|
__export(link_detection_worker_exports, {
|
|
2931
2996
|
scanLinks: () => scanLinks
|
|
2932
2997
|
});
|
|
2933
|
-
function trimProse(
|
|
2934
|
-
let end =
|
|
2998
|
+
function trimProse(text2) {
|
|
2999
|
+
let end = text2.length;
|
|
2935
3000
|
const pairs = { ")": "(", "]": "[", "}": "{" };
|
|
2936
3001
|
while (end > 0) {
|
|
2937
|
-
const char =
|
|
3002
|
+
const char = text2[end - 1];
|
|
2938
3003
|
if (".,;:!?".includes(char)) {
|
|
2939
3004
|
end--;
|
|
2940
3005
|
continue;
|
|
2941
3006
|
}
|
|
2942
3007
|
if (pairs[char]) {
|
|
2943
|
-
const current =
|
|
3008
|
+
const current = text2.slice(0, end);
|
|
2944
3009
|
if (current.split(char).length > current.split(pairs[char]).length) {
|
|
2945
3010
|
end--;
|
|
2946
3011
|
continue;
|
|
@@ -2948,7 +3013,7 @@ function trimProse(text) {
|
|
|
2948
3013
|
}
|
|
2949
3014
|
break;
|
|
2950
3015
|
}
|
|
2951
|
-
return
|
|
3016
|
+
return text2.slice(0, end);
|
|
2952
3017
|
}
|
|
2953
3018
|
function scanLinks(request) {
|
|
2954
3019
|
try {
|
|
@@ -2985,22 +3050,22 @@ function scanLinks(request) {
|
|
|
2985
3050
|
regex.lastIndex += (regex.unicode || regex.unicodeSets) && point !== void 0 && point > 65535 ? 2 : 1;
|
|
2986
3051
|
continue;
|
|
2987
3052
|
}
|
|
2988
|
-
let
|
|
3053
|
+
let text2 = match[0];
|
|
2989
3054
|
if (builtin) {
|
|
2990
3055
|
const before = chunk.text[match.index - 1];
|
|
2991
3056
|
if (before !== void 0 && !/[\s([{"'<>=]/u.test(before)) continue;
|
|
2992
|
-
if (builtin === "uri" && /^[a-z]:[\\/]/iu.test(
|
|
2993
|
-
|
|
2994
|
-
if (!
|
|
3057
|
+
if (builtin === "uri" && /^[a-z]:[\\/]/iu.test(text2)) continue;
|
|
3058
|
+
text2 = trimProse(text2);
|
|
3059
|
+
if (!text2 || builtin === "absolutePath" && (text2 === "/" || /^[a-z]:[\\/]$/iu.test(text2))) continue;
|
|
2995
3060
|
if (builtin === "url") {
|
|
2996
3061
|
try {
|
|
2997
|
-
if (!new URL(
|
|
3062
|
+
if (!new URL(text2).hostname) continue;
|
|
2998
3063
|
} catch {
|
|
2999
3064
|
continue;
|
|
3000
3065
|
}
|
|
3001
3066
|
}
|
|
3002
3067
|
}
|
|
3003
|
-
const candidate = { index: match.index, text, captures: match.slice(1), groups: { ...match.groups } };
|
|
3068
|
+
const candidate = { index: match.index, text: text2, captures: match.slice(1), groups: { ...match.groups } };
|
|
3004
3069
|
resultText += linkMatchTextSize(candidate);
|
|
3005
3070
|
if (resultText > LINK_LIMITS.resultText) {
|
|
3006
3071
|
return { id: request.id, error: "limit", message: "Link result text limit exceeded" };
|
|
@@ -5002,6 +5067,7 @@ var terminalThemeCss = `
|
|
|
5002
5067
|
|
|
5003
5068
|
// src/web-terminal.ts
|
|
5004
5069
|
init_protocol();
|
|
5070
|
+
init_program_status();
|
|
5005
5071
|
|
|
5006
5072
|
// src/selection-ui.ts
|
|
5007
5073
|
init_validation();
|
|
@@ -5164,26 +5230,26 @@ function extractLinkText(snapshot, mode) {
|
|
|
5164
5230
|
const { columns, rows, cells: cells2 } = snapshot;
|
|
5165
5231
|
if (!Number.isSafeInteger(columns) || !Number.isSafeInteger(rows) || columns < 1 || rows < 1 || columns * rows > LINK_LIMITS.cells) throw new RangeError("Link grid exceeds cell limit");
|
|
5166
5232
|
const result = [];
|
|
5167
|
-
let
|
|
5233
|
+
let text2 = "", spans = [], unsafe = /* @__PURE__ */ new Set(), firstRow = 0, total = 0;
|
|
5168
5234
|
let start = "unknown";
|
|
5169
5235
|
const wrapped = (row) => row >= 0 && !!((cells2[(row + 1) * columns - 1]?.attributes ?? 0) & SOFT_WRAP);
|
|
5170
5236
|
const finish = (end) => {
|
|
5171
|
-
const chunk = Object.freeze({ text, mode, start, end });
|
|
5172
|
-
const boundaries = /* @__PURE__ */ new Set([
|
|
5173
|
-
for (const segment of segmenter.segment(
|
|
5237
|
+
const chunk = Object.freeze({ text: text2, mode, start, end });
|
|
5238
|
+
const boundaries = /* @__PURE__ */ new Set([text2.length]);
|
|
5239
|
+
for (const segment of segmenter.segment(text2)) boundaries.add(segment.index);
|
|
5174
5240
|
result.push({
|
|
5175
5241
|
chunk,
|
|
5176
5242
|
spans,
|
|
5177
5243
|
unsafe,
|
|
5178
5244
|
boundaries,
|
|
5179
|
-
key: JSON.stringify([mode, firstRow, start, end,
|
|
5245
|
+
key: JSON.stringify([mode, firstRow, start, end, text2])
|
|
5180
5246
|
});
|
|
5181
|
-
|
|
5247
|
+
text2 = "";
|
|
5182
5248
|
spans = [];
|
|
5183
5249
|
unsafe = /* @__PURE__ */ new Set();
|
|
5184
5250
|
};
|
|
5185
5251
|
for (let row = 0; row < rows; row++) {
|
|
5186
|
-
if (
|
|
5252
|
+
if (text2.length === 0) {
|
|
5187
5253
|
firstRow = row;
|
|
5188
5254
|
start = row === 0 ? "unknown" : wrapped(row - 1) ? "clipped" : "complete";
|
|
5189
5255
|
}
|
|
@@ -5192,26 +5258,26 @@ function extractLinkText(snapshot, mode) {
|
|
|
5192
5258
|
const width = cell?.width ?? 1;
|
|
5193
5259
|
const valid = width >= 1 && width <= 2 && col + width <= columns && !(cell && (cell.attributes & HIDDEN || cell.text.codePointAt(0) === 1109742)) && (width !== 2 || cells2[row * columns + col + 1]?.width === 0);
|
|
5194
5260
|
const value = valid ? cell?.text || " " : "\0";
|
|
5195
|
-
if (total + value.length > LINK_LIMITS.totalText ||
|
|
5261
|
+
if (total + value.length > LINK_LIMITS.totalText || text2.length + value.length > LINK_LIMITS.chunk) {
|
|
5196
5262
|
throw new RangeError("Link text exceeds chunk or viewport limit");
|
|
5197
5263
|
}
|
|
5198
|
-
const offset =
|
|
5199
|
-
|
|
5264
|
+
const offset = text2.length;
|
|
5265
|
+
text2 += value;
|
|
5200
5266
|
total += value.length;
|
|
5201
|
-
if (valid) spans.push({ start: offset, end:
|
|
5267
|
+
if (valid) spans.push({ start: offset, end: text2.length, row, startColumn: col, endColumn: col + width });
|
|
5202
5268
|
else {
|
|
5203
5269
|
unsafe.add(offset);
|
|
5204
|
-
unsafe.add(
|
|
5270
|
+
unsafe.add(text2.length);
|
|
5205
5271
|
}
|
|
5206
5272
|
if (valid && width === 2) col++;
|
|
5207
5273
|
}
|
|
5208
5274
|
const soft = wrapped(row);
|
|
5209
|
-
if (!soft) unsafe.add(
|
|
5275
|
+
if (!soft) unsafe.add(text2.length);
|
|
5210
5276
|
if (mode === "physicalRow" || mode === "logicalLine" && !soft || row === rows - 1) {
|
|
5211
5277
|
finish(soft ? "clipped" : "unknown");
|
|
5212
5278
|
} else if (!soft) {
|
|
5213
|
-
if (++total > LINK_LIMITS.totalText ||
|
|
5214
|
-
|
|
5279
|
+
if (++total > LINK_LIMITS.totalText || text2.length + 1 > LINK_LIMITS.chunk) throw new RangeError("Link text exceeds limit");
|
|
5280
|
+
text2 += "\n";
|
|
5215
5281
|
}
|
|
5216
5282
|
}
|
|
5217
5283
|
return result;
|
|
@@ -5559,6 +5625,7 @@ var WebTerminal = class _WebTerminal {
|
|
|
5559
5625
|
#title = "";
|
|
5560
5626
|
#hasTitle = false;
|
|
5561
5627
|
#progress = { state: "none", percentage: null };
|
|
5628
|
+
#programStatus = programStatusSnapshot({ records: [] });
|
|
5562
5629
|
#shellIntegration = { phase: "unknown", lastExitCode: null };
|
|
5563
5630
|
#workingDirectory = { uri: null, host: null, path: null };
|
|
5564
5631
|
#commandMark = null;
|
|
@@ -5693,6 +5760,9 @@ var WebTerminal = class _WebTerminal {
|
|
|
5693
5760
|
get progress() {
|
|
5694
5761
|
return { ...this.#progress };
|
|
5695
5762
|
}
|
|
5763
|
+
get programStatus() {
|
|
5764
|
+
return this.#programStatus;
|
|
5765
|
+
}
|
|
5696
5766
|
get shellIntegration() {
|
|
5697
5767
|
return { ...this.#shellIntegration };
|
|
5698
5768
|
}
|
|
@@ -6053,18 +6123,21 @@ var WebTerminal = class _WebTerminal {
|
|
|
6053
6123
|
if (!this.#disposed && this.#connected && (this.#peer.id !== null || this.#peer.isPrimary)) {
|
|
6054
6124
|
const titleChanged = !this.#hasTitle || this.#title !== message.title;
|
|
6055
6125
|
const progressChanged = !this.#hasActivity || this.#progress.state !== message.progress.state || this.#progress.percentage !== message.progress.percentage;
|
|
6126
|
+
const programStatusChanged = !this.#hasActivity || !sameProgramStatus(this.#programStatus, message.programStatus);
|
|
6056
6127
|
const shellChanged = !this.#hasActivity || this.#shellIntegration.phase !== message.shellIntegration.phase || this.#shellIntegration.lastExitCode !== message.shellIntegration.lastExitCode;
|
|
6057
6128
|
const workingDirectoryChanged = !this.#hasActivity || this.#workingDirectory.uri !== message.workingDirectory.uri;
|
|
6058
6129
|
const commandMarkChanged = !this.#hasActivity || this.#commandMark?.phase !== message.commandMark?.phase || this.#commandMark?.exitCode !== message.commandMark?.exitCode || this.#commandMark?.rawParameters !== message.commandMark?.rawParameters;
|
|
6059
6130
|
this.#title = message.title;
|
|
6060
6131
|
this.#hasTitle = true;
|
|
6061
6132
|
this.#progress = { ...message.progress };
|
|
6133
|
+
if (programStatusChanged) this.#programStatus = programStatusSnapshot(message.programStatus);
|
|
6062
6134
|
this.#shellIntegration = { ...message.shellIntegration };
|
|
6063
6135
|
this.#workingDirectory = { ...message.workingDirectory };
|
|
6064
6136
|
this.#commandMark = message.commandMark ? { ...message.commandMark } : null;
|
|
6065
6137
|
this.#hasActivity = true;
|
|
6066
6138
|
if (titleChanged) this.#options.onTitleChange?.(this.#title);
|
|
6067
6139
|
if (!this.#disposed && progressChanged) this.#options.onProgressChange?.(this.progress);
|
|
6140
|
+
if (!this.#disposed && programStatusChanged) this.#options.onProgramStatusChange?.(this.programStatus);
|
|
6068
6141
|
if (!this.#disposed && shellChanged) this.#options.onShellIntegrationChange?.(this.shellIntegration);
|
|
6069
6142
|
if (!this.#disposed && workingDirectoryChanged) this.#options.onWorkingDirectoryChange?.(this.workingDirectory);
|
|
6070
6143
|
if (!this.#disposed && commandMarkChanged) this.#options.onCommandMarkChange?.(this.commandMark);
|
|
@@ -6261,11 +6334,11 @@ var WebTerminal = class _WebTerminal {
|
|
|
6261
6334
|
if (!navigator.clipboard?.write || typeof ClipboardItem !== "function") {
|
|
6262
6335
|
throw new Error("Clipboard writing is unavailable. Use a secure browser context with clipboard permission.");
|
|
6263
6336
|
}
|
|
6264
|
-
const
|
|
6337
|
+
const text2 = this.#history.copy();
|
|
6265
6338
|
this.#copying = true;
|
|
6266
6339
|
this.#inspectionChanged();
|
|
6267
|
-
const item = new ClipboardItem({ "text/plain":
|
|
6268
|
-
const [value] = await Promise.all([
|
|
6340
|
+
const item = new ClipboardItem({ "text/plain": text2.then((value2) => new Blob([value2], { type: "text/plain" })) });
|
|
6341
|
+
const [value] = await Promise.all([text2, navigator.clipboard.write([item])]);
|
|
6269
6342
|
if (clear && serial === this.#copySerial && this.selection.status === "valid" && this.selection.requestId === selectionId && this.viewport.generation === generation)
|
|
6270
6343
|
this.clearSelection();
|
|
6271
6344
|
return value;
|
|
@@ -6350,10 +6423,10 @@ var WebTerminal = class _WebTerminal {
|
|
|
6350
6423
|
}
|
|
6351
6424
|
}
|
|
6352
6425
|
/** Sends an explicit paste through the producer's mode-aware input encoder. */
|
|
6353
|
-
paste(
|
|
6354
|
-
if (typeof
|
|
6426
|
+
paste(text2) {
|
|
6427
|
+
if (typeof text2 !== "string") throw new TypeError("Paste text must be a string");
|
|
6355
6428
|
if (!this.#canInput()) throw new Error("Terminal view does not accept input");
|
|
6356
|
-
if (
|
|
6429
|
+
if (text2) this.#inputCommand({ type: "paste", text: text2 });
|
|
6357
6430
|
}
|
|
6358
6431
|
async pasteClipboard() {
|
|
6359
6432
|
if (!this.#canInput()) throw new Error("Terminal view does not accept input");
|
|
@@ -6363,11 +6436,11 @@ var WebTerminal = class _WebTerminal {
|
|
|
6363
6436
|
const generation = this.viewport.generation;
|
|
6364
6437
|
const selectionId = this.selection.requestId;
|
|
6365
6438
|
const focused = document.activeElement;
|
|
6366
|
-
const
|
|
6439
|
+
const text2 = await navigator.clipboard.readText();
|
|
6367
6440
|
if (!this.#canInput() || serial !== this.#inputSerial || generation !== this.viewport.generation || selectionId !== this.selection.requestId || document.activeElement !== focused)
|
|
6368
6441
|
throw new Error("Terminal input, selection, focus, or buffer changed while reading the clipboard. Paste again.");
|
|
6369
|
-
this.paste(
|
|
6370
|
-
return
|
|
6442
|
+
this.paste(text2);
|
|
6443
|
+
return text2;
|
|
6371
6444
|
}
|
|
6372
6445
|
get colorMode() {
|
|
6373
6446
|
return this.#colorMode;
|
|
@@ -6521,17 +6594,17 @@ var WebTerminal = class _WebTerminal {
|
|
|
6521
6594
|
const range = this.#osc8Rows.get(input.point.y)?.find((range2) => input.point.x >= range2.startColumn && input.point.x < range2.endColumn);
|
|
6522
6595
|
const snapshot = this.#linkSnapshot;
|
|
6523
6596
|
if (!range || !snapshot) return;
|
|
6524
|
-
let
|
|
6597
|
+
let text2 = "";
|
|
6525
6598
|
for (let column = range.startColumn; column < range.endColumn; column++) {
|
|
6526
6599
|
const cell = snapshot.cells[range.row * snapshot.columns + column];
|
|
6527
|
-
if (cell?.width && !(cell.attributes & 64))
|
|
6600
|
+
if (cell?.width && !(cell.attributes & 64)) text2 += cell.text;
|
|
6528
6601
|
}
|
|
6529
6602
|
const activation = Object.freeze({
|
|
6530
6603
|
source: "osc8",
|
|
6531
6604
|
ruleId: null,
|
|
6532
6605
|
kind: "uri",
|
|
6533
6606
|
target: pointer.target,
|
|
6534
|
-
text,
|
|
6607
|
+
text: text2,
|
|
6535
6608
|
revision: this.#linkRevision,
|
|
6536
6609
|
ranges: Object.freeze([Object.freeze({ row: range.row, startColumn: range.startColumn, endColumn: range.endColumn })])
|
|
6537
6610
|
});
|
|
@@ -6611,9 +6684,9 @@ var WebTerminal = class _WebTerminal {
|
|
|
6611
6684
|
const inputEvent = event instanceof InputEvent ? event : void 0;
|
|
6612
6685
|
if (inputEvent?.isComposing || composing) return;
|
|
6613
6686
|
clearTimeout(this.#compositionTimer);
|
|
6614
|
-
const
|
|
6687
|
+
const text2 = compositionCommit ?? inputEvent?.data ?? input.value;
|
|
6615
6688
|
compositionCommit = null;
|
|
6616
|
-
if (
|
|
6689
|
+
if (text2 && inputEvent?.inputType !== "insertFromPaste") this.#dispatchInput({ type: "text", text: text2 }, event);
|
|
6617
6690
|
input.value = "";
|
|
6618
6691
|
}, options);
|
|
6619
6692
|
}
|