@timurproko/a1 0.1.8-dev.368 → 0.1.8-dev.402
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/dist/composition/owned-ui.d.ts +2 -0
- package/dist/composition/owned-ui.js +28 -11
- package/dist/contracts/owned-ui/image-attachments.d.ts +7 -0
- package/dist/contracts/owned-ui/image-attachments.js +7 -0
- package/dist/foundation/release/dependency-certification.js +65 -7
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +8 -6
- package/dist/integrations/pi/components/owned-editor-ux.js +115 -26
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +4 -0
- package/dist/integrations/pi/components/shell-presenters-transcript.js +15 -5
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +14 -5
- package/dist/integrations/pi/components/tool-image-presentation.d.ts +21 -0
- package/dist/integrations/pi/components/tool-image-presentation.js +86 -0
- package/dist/integrations/pi/components/tool-result-adapter.d.ts +2 -2
- package/dist/integrations/pi/components/transcript-image-resolver.d.ts +7 -0
- package/dist/integrations/pi/components/transcript-image-resolver.js +19 -0
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +3 -1
- package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +61 -0
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +344 -0
- package/dist/integrations/pi/engine/adapter.d.ts +4 -1
- package/dist/integrations/pi/engine/adapter.js +66 -28
- package/dist/integrations/pi/engine/pending-delivery.d.ts +4 -0
- package/dist/integrations/pi/engine/pending-delivery.js +15 -6
- package/dist/integrations/pi/engine/tool-rendering.js +26 -3
- package/dist/integrations/pi/engine/transcript-image-assets.d.ts +11 -0
- package/dist/integrations/pi/engine/transcript-image-assets.js +86 -0
- package/dist/integrations/pi/session-ui/clipboard-diagnostics.d.ts +16 -0
- package/dist/integrations/pi/session-ui/clipboard-diagnostics.js +65 -0
- package/dist/integrations/pi/session-ui/editor-hyperlink-budget.d.ts +9 -0
- package/dist/integrations/pi/session-ui/editor-hyperlink-budget.js +23 -0
- package/dist/integrations/pi/session-ui/index.d.ts +1 -0
- package/dist/integrations/pi/session-ui/index.js +1 -0
- package/dist/integrations/pi/session-ui/paste-executor.d.ts +10 -0
- package/dist/integrations/pi/session-ui/paste-executor.js +232 -0
- package/dist/integrations/pi/session-ui/paste-helper.d.ts +1 -0
- package/dist/integrations/pi/session-ui/paste-helper.js +173 -0
- package/dist/integrations/pi/session-ui/paste-preparation-client.d.ts +16 -0
- package/dist/integrations/pi/session-ui/paste-preparation-client.js +159 -0
- package/dist/integrations/pi/session-ui/paste-protocol.d.ts +78 -0
- package/dist/integrations/pi/session-ui/paste-protocol.js +18 -0
- package/dist/integrations/pi/session-ui/paste-text-preparation.d.ts +20 -0
- package/dist/integrations/pi/session-ui/paste-text-preparation.js +134 -0
- package/dist/integrations/pi/session-ui/paste-text-worker.d.ts +1 -0
- package/dist/integrations/pi/session-ui/paste-text-worker.js +12 -0
- package/dist/integrations/pi/session-ui/path-chip-presentation.d.ts +7 -0
- package/dist/integrations/pi/session-ui/path-chip-presentation.js +24 -0
- package/dist/integrations/pi/session-ui/prompt-chips.d.ts +8 -1
- package/dist/integrations/pi/session-ui/prompt-chips.js +129 -149
- package/dist/integrations/pi/session-ui/response-copy-coordinator.d.ts +27 -0
- package/dist/integrations/pi/session-ui/response-copy-coordinator.js +189 -0
- package/dist/integrations/pi/session-ui/response-copy-helper.d.ts +1 -0
- package/dist/integrations/pi/session-ui/response-copy-helper.js +122 -0
- package/dist/integrations/pi/session-ui/response-copy-protocol.d.ts +56 -0
- package/dist/integrations/pi/session-ui/response-copy-protocol.js +5 -0
- package/dist/integrations/pi/session-ui/response-copy-transport.d.ts +31 -0
- package/dist/integrations/pi/session-ui/response-copy-transport.js +187 -0
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +15 -6
- package/dist/integrations/pi/session-ui/session-shell-root.js +25 -6
- package/dist/integrations/pi/session-ui/session-shell.js +100 -28
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +2 -2
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +4 -3
- package/dist/integrations/pi/session-ui/system-clipboard.d.ts +2 -2
- package/dist/integrations/pi/session-ui/system-clipboard.js +26 -11
- package/dist/integrations/pi/tui-runtime/adapter.js +3 -1
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +1 -0
- package/dist/ui/components/index.d.ts +1 -0
- package/dist/ui/components/index.js +1 -0
- package/dist/ui/components/selection-copy.d.ts +24 -0
- package/dist/ui/components/selection-copy.js +39 -0
- package/dist/ui/components/spans.d.ts +5 -2
- package/dist/ui/components/spans.js +12 -3
- package/dist/ui/components/text-selection.js +2 -0
- package/dist/ui/components/text.js +1 -1
- package/dist/ui/components/transcript-viewport.d.ts +5 -0
- package/dist/ui/components/transcript-viewport.js +100 -23
- package/dist/ui/components/visible-hyperlinks.js +40 -8
- package/docs/architecture/response-copy-delivery.md +83 -0
- package/docs/architecture/tool-image-presentation.md +62 -0
- package/docs/ci-release-runbook.md +19 -7
- package/docs/openspec-archive-automation.md +124 -0
- package/package.json +2 -1
|
@@ -4,7 +4,9 @@ import { PINNED_PI_HIDDEN_COMMAND_NAMES, PINNED_PI_WORKFLOW_COMMAND_NAMES, } fro
|
|
|
4
4
|
import { createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellStatus, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, onPiThemeChange, isPiPromptStyleCompaction, piShellVisibleWidth, piShellTruncateToWidth, piTheme, renderPiShellCommandMessage, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, } from "../components/index.js";
|
|
5
5
|
import { PiTuiRuntimeAdapter, classifyPiTuiInput, } from "../tui-runtime/index.js";
|
|
6
6
|
import { PromptChipStore } from "./prompt-chips.js";
|
|
7
|
+
import { EditorHyperlinkBudget } from "./editor-hyperlink-budget.js";
|
|
7
8
|
import { SessionViewportController } from "./session-viewport-controller.js";
|
|
9
|
+
import { canPreparePasteInline } from "./paste-text-preparation.js";
|
|
8
10
|
/** Composes backend session state into the owned transcript, viewport, editor, and dock presentation. */
|
|
9
11
|
export class OwnedUiSessionShellRoot {
|
|
10
12
|
editor;
|
|
@@ -24,7 +26,9 @@ export class OwnedUiSessionShellRoot {
|
|
|
24
26
|
#queued;
|
|
25
27
|
#extensionRenderers;
|
|
26
28
|
#imageAssets;
|
|
27
|
-
#promptChips
|
|
29
|
+
#promptChips;
|
|
30
|
+
#editorHyperlinks = new EditorHyperlinkBudget();
|
|
31
|
+
#pasteFramingId = 0;
|
|
28
32
|
#customViewport;
|
|
29
33
|
#submittedPromptComposer;
|
|
30
34
|
#viewportController;
|
|
@@ -64,6 +68,8 @@ export class OwnedUiSessionShellRoot {
|
|
|
64
68
|
}, sessionLayout = "pinned", imageAssets) {
|
|
65
69
|
this.#view = view;
|
|
66
70
|
this.#customViewport = sessionLayout === "custom-viewport";
|
|
71
|
+
this.#promptChips = new PromptChipStore({ isolated: this.#customViewport,
|
|
72
|
+
...(handlers.pasteDiagnostics === undefined ? {} : { onEvent: handlers.pasteDiagnostics }) });
|
|
67
73
|
this.#submittedPromptComposer = this.#customViewport
|
|
68
74
|
? {
|
|
69
75
|
layout: submittedPromptLayout,
|
|
@@ -99,13 +105,25 @@ export class OwnedUiSessionShellRoot {
|
|
|
99
105
|
return "";
|
|
100
106
|
}
|
|
101
107
|
},
|
|
102
|
-
...(this.#customViewport ? {
|
|
108
|
+
...(this.#customViewport ? {
|
|
109
|
+
beginClipboardPaste: () => this.#promptChips.beginPaste(this.editor.getText(), handlers.captureClipboardPaste?.() ?? { kind: "provided", read: signal => handlers.readClipboardContent?.(signal) ?? Promise.resolve(null) }, error => handlers.onPasteRejected?.(error), () => handlers.requestRender()),
|
|
110
|
+
onPasteInput: bytes => {
|
|
111
|
+
try {
|
|
112
|
+
handlers.pasteDiagnostics?.({ request: --this.#pasteFramingId, phase: "framing", bytes, atMs: performance.now(), pending: 0, transport: "terminal" });
|
|
113
|
+
}
|
|
114
|
+
catch { /* Invariant: payload-free diagnostics cannot interfere with the input path. */ }
|
|
115
|
+
},
|
|
116
|
+
deferTextPaste: text => !canPreparePasteInline(text),
|
|
117
|
+
beginTextPaste: text => this.#promptChips.beginPaste(this.editor.getText(), { kind: "text", text }, error => handlers.onPasteRejected?.(error)),
|
|
118
|
+
} : {}),
|
|
103
119
|
editorAtomicRanges: line => this.#promptChips.atomicRanges(line),
|
|
104
120
|
editorHiddenRanges: line => this.#promptChips.hiddenRanges(line),
|
|
105
|
-
decorateEditorRow: (row, width) => {
|
|
121
|
+
decorateEditorRow: (row, width, rowIndex) => {
|
|
122
|
+
if (rowIndex === 0)
|
|
123
|
+
this.#editorHyperlinks.reset();
|
|
106
124
|
const plain = stripAnsi(row);
|
|
107
125
|
const ranges = this.#promptChips.hyperlinkRanges(plain);
|
|
108
|
-
if (ranges.length === 0)
|
|
126
|
+
if (ranges.length === 0 || !this.#editorHyperlinks.takeCleanup())
|
|
109
127
|
return row;
|
|
110
128
|
const linkResetAndTail = `\u001b]8;;\u001b\\\u001b[24m${" ".repeat(Math.max(0, width - piShellVisibleWidth(row)))}`;
|
|
111
129
|
// Platform: VS15 is zero-column and default-ignorable. It breaks Windows Terminal's
|
|
@@ -115,7 +133,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
115
133
|
: row;
|
|
116
134
|
const decorated = this.#viewportController.editorPointerSelecting
|
|
117
135
|
? ranges.reduce((result, range) => backgroundSgrSpan(result, piShellVisibleWidth(plain.slice(0, range.start)), piShellVisibleWidth(plain.slice(0, range.end)), "\u001b[4:4m", "\u001b[24m", piShellVisibleWidth), paintRow)
|
|
118
|
-
: ranges.reduce((result, range) => hyperlinkSgrSpan(result, piShellVisibleWidth(plain.slice(0, range.start)), piShellVisibleWidth(plain.slice(0, range.end)), range.target, piShellVisibleWidth), row);
|
|
136
|
+
: ranges.filter(range => this.#editorHyperlinks.take(range.target)).reduce((result, range) => hyperlinkSgrSpan(result, piShellVisibleWidth(plain.slice(0, range.start)), piShellVisibleWidth(plain.slice(0, range.end)), range.target, piShellVisibleWidth), row);
|
|
119
137
|
// Platform: Windows Terminal can retain stale native dotted-link cells when a mutable
|
|
120
138
|
// prompt replaces a longer URL. Explicit non-link spaces overwrite that tail.
|
|
121
139
|
return `${decorated}${linkResetAndTail}`;
|
|
@@ -611,6 +629,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
611
629
|
firstRow,
|
|
612
630
|
lastRow: Math.max(firstRow, rows.length - 1),
|
|
613
631
|
sourceRow: pinnedPromptSourceRow(block, blockRows[0], blockWidth),
|
|
632
|
+
prominentSourceRow: blockRows[0],
|
|
614
633
|
});
|
|
615
634
|
}
|
|
616
635
|
}
|
|
@@ -1103,7 +1122,7 @@ function nativeTranscriptLinkColor(text, target) {
|
|
|
1103
1122
|
function withoutTerminalBackground(text) {
|
|
1104
1123
|
return text.replace(TERMINAL_BACKGROUND, "");
|
|
1105
1124
|
}
|
|
1106
|
-
/**
|
|
1125
|
+
/** Preserve the established prompt foreground for quiet and hovered pinned timestamps. */
|
|
1107
1126
|
function pinnedPromptSourceRow(block, sourceRow, width) {
|
|
1108
1127
|
if (typeof block.payload !== "object" || block.payload === null)
|
|
1109
1128
|
return sourceRow;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { ResponseCopyCoordinator } from "./response-copy-coordinator.js";
|
|
2
|
+
import { createResponseCopyExecutor, hasAsyncClipboardOutput, responseCopyDestination } from "./response-copy-transport.js";
|
|
3
|
+
import { MAX_COPY_CONTROL_BYTES } from "./response-copy-protocol.js";
|
|
1
4
|
import { PromptHistoryController } from "./prompt-history-controller.js";
|
|
2
5
|
import { PRODUCT_TEXT } from "../../../product-identity.js";
|
|
3
6
|
import { boundedCleanup } from "../../../foundation/terminal-cleanup/index.js";
|
|
@@ -37,6 +40,8 @@ export class OwnedUiSessionShell {
|
|
|
37
40
|
#disposed = false;
|
|
38
41
|
#pointerReporting = false;
|
|
39
42
|
#customViewport;
|
|
43
|
+
#responseCopy;
|
|
44
|
+
#unbindClipboardWriter;
|
|
40
45
|
#damageTerminal;
|
|
41
46
|
#streamPresentation;
|
|
42
47
|
#removeViewportPreInput;
|
|
@@ -72,6 +77,52 @@ export class OwnedUiSessionShell {
|
|
|
72
77
|
let streamPresentation;
|
|
73
78
|
let pendingClipboardWrite = Promise.resolve();
|
|
74
79
|
let promptSuggestionController = null;
|
|
80
|
+
const terminalCopy = options.terminal !== undefined || hasAsyncClipboardOutput();
|
|
81
|
+
this.#responseCopy = this.#customViewport ? new ResponseCopyCoordinator({
|
|
82
|
+
execute: options.responseCopy?.execute ?? createResponseCopyExecutor({
|
|
83
|
+
...(options.terminal === undefined && options.clipboard === undefined ? {} : { destination: "terminal" }),
|
|
84
|
+
...(options.clipboard?.writeText === undefined ? {} : { writeText: (text, signal) => options.clipboard.writeText(text, signal) }),
|
|
85
|
+
...(terminalCopy ? { terminal: { submit: async (control, signal) => {
|
|
86
|
+
if (signal.aborted || this.#disposed || !runtime?.active)
|
|
87
|
+
throw new Error("Copy canceled");
|
|
88
|
+
// Performance: never grow the real terminal's pending buffer with another clipboard payload.
|
|
89
|
+
if (options.terminal === undefined && process.stdout.writableLength + control.length > MAX_COPY_CONTROL_BYTES) {
|
|
90
|
+
throw new Error("Clipboard terminal is busy");
|
|
91
|
+
}
|
|
92
|
+
runtime.writeControl(control);
|
|
93
|
+
} } } : {}),
|
|
94
|
+
}),
|
|
95
|
+
...(options.responseCopy?.onEvent === undefined ? {} : { onEvent: options.responseCopy.onEvent }),
|
|
96
|
+
onFailure: result => {
|
|
97
|
+
if (this.#disposed || !runtime?.active)
|
|
98
|
+
return;
|
|
99
|
+
this.root.appendWorkflowStatus(result.outcome === "timed-out" ? "Copy timed out; the clipboard did not respond."
|
|
100
|
+
: result.failure === "size" ? "Selection exceeds this clipboard route's size limit."
|
|
101
|
+
: "Could not copy the selection; the clipboard is unavailable.");
|
|
102
|
+
runtime.requestRender();
|
|
103
|
+
},
|
|
104
|
+
}) : null;
|
|
105
|
+
const readClipboard = async (signal) => {
|
|
106
|
+
if (signal.aborted)
|
|
107
|
+
throw new ImageAttachmentError("image-canceled");
|
|
108
|
+
if (options.clipboard === undefined)
|
|
109
|
+
return runImageWorker({ kind: "clipboard" }, signal);
|
|
110
|
+
try {
|
|
111
|
+
const image = await options.clipboard.readImage?.(signal);
|
|
112
|
+
if (image !== null && image !== undefined) {
|
|
113
|
+
const canonical = await runImageWorker({ kind: "canonicalize", source: image }, signal);
|
|
114
|
+
if (canonical !== null)
|
|
115
|
+
return { kind: "image", ...canonical };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (error instanceof ImageAttachmentError)
|
|
120
|
+
throw error;
|
|
121
|
+
// Compatibility: an unavailable image reader can still provide text.
|
|
122
|
+
}
|
|
123
|
+
const text = await options.clipboard.readText(signal);
|
|
124
|
+
return text === null ? null : { kind: "text", text };
|
|
125
|
+
};
|
|
75
126
|
this.root = new OwnedUiSessionShellRoot(this.backend.view(), options.cwd, {
|
|
76
127
|
getColumns: () => runtime?.viewport().columns ?? options.terminal?.columns ?? 80,
|
|
77
128
|
getRows: () => runtime?.viewport().rows ?? options.terminal?.rows ?? 24,
|
|
@@ -107,34 +158,35 @@ export class OwnedUiSessionShell {
|
|
|
107
158
|
this.#promptHistory?.synchronize();
|
|
108
159
|
},
|
|
109
160
|
onCopyText: text => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
161
|
+
if (this.#responseCopy !== null) {
|
|
162
|
+
void this.#responseCopy.submitText(text);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const write = () => {
|
|
166
|
+
if (this.#disposed)
|
|
167
|
+
return Promise.resolve();
|
|
168
|
+
runtime?.writeControl(`\u001b]52;c;${Buffer.from(text, "utf8").toString("base64")}\u0007`);
|
|
169
|
+
return options.clipboard === undefined ? writeSystemClipboardText(text)
|
|
170
|
+
: options.clipboard.writeText?.(text) ?? Promise.resolve();
|
|
171
|
+
};
|
|
172
|
+
// Compatibility: comparison profiles retain their existing clipboard path.
|
|
173
|
+
pendingClipboardWrite = write().catch(() => { });
|
|
115
174
|
},
|
|
116
175
|
readClipboardContent: async (signal = new AbortController().signal) => {
|
|
117
176
|
await pendingClipboardWrite;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
if (error instanceof ImageAttachmentError)
|
|
132
|
-
throw error;
|
|
133
|
-
// Compatibility: an unavailable native image reader can still provide clipboard text.
|
|
134
|
-
}
|
|
135
|
-
const text = await options.clipboard.readText(signal);
|
|
136
|
-
return text === null ? null : { kind: "text", text };
|
|
177
|
+
return readClipboard(signal);
|
|
178
|
+
},
|
|
179
|
+
captureClipboardPaste: () => {
|
|
180
|
+
const before = this.#responseCopy?.capturePasteBarrier() ?? (async () => true);
|
|
181
|
+
if (options.clipboard !== undefined)
|
|
182
|
+
return { kind: "provided", read: readClipboard, before };
|
|
183
|
+
if (responseCopyDestination(process.env) === "terminal")
|
|
184
|
+
return {
|
|
185
|
+
kind: "provided", before, read: async () => { throw new ImageAttachmentError("paste-unavailable"); },
|
|
186
|
+
};
|
|
187
|
+
return { kind: "native", before };
|
|
137
188
|
},
|
|
189
|
+
...(options.pasteDiagnostics === undefined ? {} : { pasteDiagnostics: options.pasteDiagnostics }),
|
|
138
190
|
}, {
|
|
139
191
|
...options.startup,
|
|
140
192
|
resources: options.startup?.resources ?? shellResourceEntries(this.backend),
|
|
@@ -261,8 +313,8 @@ export class OwnedUiSessionShell {
|
|
|
261
313
|
this.runtime.renderNow();
|
|
262
314
|
}
|
|
263
315
|
const routed = this.root.handleViewportPreInput(data, true, Date.now(), this.root.usesDefaultInputSurface() && !this.runtime.hasFocusedOverlay());
|
|
264
|
-
if (routed.
|
|
265
|
-
this
|
|
316
|
+
if (routed.copySelection !== undefined) {
|
|
317
|
+
void this.#responseCopy?.submit(routed.copySelection, pendingClipboardWrite);
|
|
266
318
|
}
|
|
267
319
|
if (!routed.consumed)
|
|
268
320
|
return routed.data === data ? undefined : { data: routed.data };
|
|
@@ -431,6 +483,14 @@ export class OwnedUiSessionShell {
|
|
|
431
483
|
if (event.type === "session-lifecycle" && event.lifecycle === "stopped")
|
|
432
484
|
this.#resolveStopped?.();
|
|
433
485
|
});
|
|
486
|
+
this.#unbindClipboardWriter = this.#responseCopy === null ? () => { } : this.backend.bindClipboardWriter(async (text) => {
|
|
487
|
+
const result = await this.#responseCopy.submitText(text);
|
|
488
|
+
if (result.outcome === "delivered")
|
|
489
|
+
return true;
|
|
490
|
+
if (result.outcome === "submitted-unverified")
|
|
491
|
+
return false;
|
|
492
|
+
throw new Error(result.outcome === "timed-out" ? "Clipboard delivery timed out" : "Clipboard delivery could not be completed");
|
|
493
|
+
});
|
|
434
494
|
if (this.backend.view().lifecycle === "stopped")
|
|
435
495
|
this.#resolveStopped?.();
|
|
436
496
|
}
|
|
@@ -873,10 +933,15 @@ export class OwnedUiSessionShell {
|
|
|
873
933
|
this.runtime.requestRender();
|
|
874
934
|
return;
|
|
875
935
|
}
|
|
876
|
-
|
|
877
|
-
|
|
936
|
+
const generation = this.backend.sessionBindingGeneration;
|
|
937
|
+
void this.backend.copyWorkflowText(text).then(acknowledged => {
|
|
938
|
+
if (this.#disposed || generation !== this.backend.sessionBindingGeneration)
|
|
939
|
+
return;
|
|
940
|
+
this.root.appendWorkflowStatus(acknowledged ? "Copied selected message to clipboard" : "Submitted selected message to clipboard");
|
|
878
941
|
this.runtime.requestRender();
|
|
879
942
|
}).catch(error => {
|
|
943
|
+
if (this.#disposed || generation !== this.backend.sessionBindingGeneration)
|
|
944
|
+
return;
|
|
880
945
|
this.root.appendWorkflowResult({ command: "tree", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
|
|
881
946
|
this.runtime.requestRender();
|
|
882
947
|
});
|
|
@@ -1007,6 +1072,7 @@ export class OwnedUiSessionShell {
|
|
|
1007
1072
|
return this.runWorkflow({ command: "quit", argument: "" });
|
|
1008
1073
|
}
|
|
1009
1074
|
async runWorkflow(request) {
|
|
1075
|
+
const copyGeneration = request.command === "copy" ? this.backend.sessionBindingGeneration : undefined;
|
|
1010
1076
|
if (request.command === "login" && request.selection !== undefined) {
|
|
1011
1077
|
const setup = this.backend.pinnedAmbientAuthentication(request.selection);
|
|
1012
1078
|
if (setup) {
|
|
@@ -1095,6 +1161,9 @@ export class OwnedUiSessionShell {
|
|
|
1095
1161
|
this.runtime.requestRender();
|
|
1096
1162
|
}
|
|
1097
1163
|
}
|
|
1164
|
+
if (copyGeneration !== undefined && (this.#disposed || copyGeneration !== this.backend.sessionBindingGeneration)) {
|
|
1165
|
+
return workflowAdapterResult({ command: "copy", outcome: "cancelled", message: "" });
|
|
1166
|
+
}
|
|
1098
1167
|
if (result.outcome === "requires-selection" && request.command === "model") {
|
|
1099
1168
|
if (result.messages !== undefined) {
|
|
1100
1169
|
for (const message of result.messages)
|
|
@@ -1246,6 +1315,7 @@ export class OwnedUiSessionShell {
|
|
|
1246
1315
|
if (this.#disposed)
|
|
1247
1316
|
return;
|
|
1248
1317
|
this.#disposed = true;
|
|
1318
|
+
this.#responseCopy?.dispose();
|
|
1249
1319
|
this.#cancelWaitingImages();
|
|
1250
1320
|
const failures = [];
|
|
1251
1321
|
const attempt = (action) => { try {
|
|
@@ -1274,6 +1344,7 @@ export class OwnedUiSessionShell {
|
|
|
1274
1344
|
fullscreenExitText = this.runtime.mode !== "fullscreen" ? ""
|
|
1275
1345
|
: exitMode === "resume-hint" ? resumeHint : [exitTranscript, resumeHint].filter(Boolean).join("\n\n");
|
|
1276
1346
|
});
|
|
1347
|
+
attempt(() => this.#unbindClipboardWriter());
|
|
1277
1348
|
attempt(() => this.#unbindPiSettings());
|
|
1278
1349
|
attempt(() => this.#unbindTerminalSettings());
|
|
1279
1350
|
attempt(() => this.#unbindShutdownSettings());
|
|
@@ -1316,6 +1387,7 @@ export class OwnedUiSessionShell {
|
|
|
1316
1387
|
this.#streamPresentation.noteImmediatePresentation();
|
|
1317
1388
|
const view = this.view();
|
|
1318
1389
|
if (this.backend.sessionGeneration !== this.#sessionGeneration) {
|
|
1390
|
+
this.#responseCopy?.reset();
|
|
1319
1391
|
this.#cancelWaitingImages();
|
|
1320
1392
|
this.root.resetPendingPastes();
|
|
1321
1393
|
this.#promptSuggestions?.invalidate();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type PiTuiPointerSurface } from "../tui-runtime/index.js";
|
|
2
2
|
import type { OwnedUiViewportSettings } from "../../../contracts/owned-ui/index.js";
|
|
3
|
-
import { type TranscriptViewportFrame, type TranscriptViewportFrameInput } from "../../../ui/components/index.js";
|
|
3
|
+
import { type SelectionCopySnapshot, type TranscriptViewportFrame, type TranscriptViewportFrameInput } from "../../../ui/components/index.js";
|
|
4
4
|
import type { PiShellEditorPort } from "../components/index.js";
|
|
5
5
|
export interface SessionViewportControllerOptions {
|
|
6
6
|
readonly enabled: boolean;
|
|
@@ -13,7 +13,7 @@ export interface SessionViewportControllerOptions {
|
|
|
13
13
|
export interface SessionViewportInputResult {
|
|
14
14
|
readonly data: string;
|
|
15
15
|
readonly consumed: boolean;
|
|
16
|
-
readonly
|
|
16
|
+
readonly copySelection?: SelectionCopySnapshot;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Owns the stateful interaction policy of the bare-A1 session viewport. The
|
|
@@ -249,11 +249,12 @@ export class SessionViewportController {
|
|
|
249
249
|
this.#requestRender();
|
|
250
250
|
}
|
|
251
251
|
else {
|
|
252
|
-
const
|
|
253
|
-
if (
|
|
252
|
+
const copySelection = this.#viewport.captureSelectedText();
|
|
253
|
+
if (copySelection !== null) {
|
|
254
254
|
this.#viewport.clearSelection();
|
|
255
|
+
this.#stopSelectionAutoScroll();
|
|
255
256
|
this.#requestRender();
|
|
256
|
-
return { data: "", consumed: true,
|
|
257
|
+
return { data: "", consumed: true, copySelection };
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
}
|
|
@@ -11,10 +11,10 @@ export declare function preloadSystemClipboard(): void;
|
|
|
11
11
|
* private clipboard module. Failures are deliberately non-fatal: a denied or
|
|
12
12
|
* unavailable clipboard simply makes the paste action a no-op.
|
|
13
13
|
*/
|
|
14
|
-
export declare function readSystemClipboardContent(signal?: AbortSignal): Promise<PiShellClipboardContent | null>;
|
|
14
|
+
export declare function readSystemClipboardContent(signal?: AbortSignal, strict?: boolean): Promise<PiShellClipboardContent | null>;
|
|
15
15
|
export declare function readSystemClipboardImage(reader: SystemClipboardImageReader): Promise<{
|
|
16
16
|
readonly data: string;
|
|
17
17
|
readonly mimeType: string;
|
|
18
18
|
} | null>;
|
|
19
|
-
export declare function readSystemClipboardText(signal?: AbortSignal): Promise<string | null>;
|
|
19
|
+
export declare function readSystemClipboardText(signal?: AbortSignal, strict?: boolean): Promise<string | null>;
|
|
20
20
|
export declare function writeSystemClipboardText(text: string): Promise<void>;
|
|
@@ -15,13 +15,13 @@ export function preloadSystemClipboard() {
|
|
|
15
15
|
* private clipboard module. Failures are deliberately non-fatal: a denied or
|
|
16
16
|
* unavailable clipboard simply makes the paste action a no-op.
|
|
17
17
|
*/
|
|
18
|
-
export async function readSystemClipboardContent(signal) {
|
|
18
|
+
export async function readSystemClipboardContent(signal, strict = false) {
|
|
19
19
|
await pendingClipboardWrite;
|
|
20
20
|
const native = await nativeClipboard();
|
|
21
21
|
if (native !== null) {
|
|
22
22
|
try {
|
|
23
23
|
if (process.platform === "win32" && native.availableFormats().some(isFileDropFormat)) {
|
|
24
|
-
const files = await readSystemClipboardText(signal);
|
|
24
|
+
const files = await readSystemClipboardText(signal, strict);
|
|
25
25
|
if (files !== null)
|
|
26
26
|
return { kind: "text", text: files };
|
|
27
27
|
}
|
|
@@ -35,7 +35,7 @@ export async function readSystemClipboardContent(signal) {
|
|
|
35
35
|
// Compatibility: fall through to text and platform readers.
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
const text = await readSystemClipboardText(signal);
|
|
38
|
+
const text = await readSystemClipboardText(signal, strict);
|
|
39
39
|
return text === null ? null : { kind: "text", text };
|
|
40
40
|
}
|
|
41
41
|
export async function readSystemClipboardImage(reader) {
|
|
@@ -74,7 +74,7 @@ export async function readSystemClipboardImage(reader) {
|
|
|
74
74
|
}
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
|
-
export async function readSystemClipboardText(signal) {
|
|
77
|
+
export async function readSystemClipboardText(signal, strict = false) {
|
|
78
78
|
// Concurrency: Ctrl+C/Ctrl+X and Ctrl+V can arrive in adjacent input turns. Serialize the
|
|
79
79
|
// read behind our native write so paste never observes the previous value.
|
|
80
80
|
await pendingClipboardWrite;
|
|
@@ -85,6 +85,8 @@ export async function readSystemClipboardText(signal) {
|
|
|
85
85
|
const text = await native.getText();
|
|
86
86
|
if (text.length > 0)
|
|
87
87
|
return text;
|
|
88
|
+
if (strict)
|
|
89
|
+
return null;
|
|
88
90
|
}
|
|
89
91
|
catch {
|
|
90
92
|
// Compatibility: fall through to the platform command.
|
|
@@ -97,12 +99,17 @@ export async function readSystemClipboardText(signal) {
|
|
|
97
99
|
"$t=Get-Clipboard -Raw -ErrorAction SilentlyContinue",
|
|
98
100
|
"if ($null -ne $t -and $t.Length -gt 0) {[Console]::Out.Write($t)} else {$f=Get-Clipboard -Format FileDropList -ErrorAction SilentlyContinue; if ($f) {[Console]::Out.Write(($f | ForEach-Object {$_.FullName}) -join [Environment]::NewLine)}}",
|
|
99
101
|
].join("; ");
|
|
100
|
-
return execClipboard("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], signal);
|
|
102
|
+
return execClipboard("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], signal, strict);
|
|
101
103
|
}
|
|
102
104
|
if (process.platform === "darwin")
|
|
103
|
-
return execClipboard("pbpaste", [], signal);
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
return execClipboard("pbpaste", [], signal, strict);
|
|
106
|
+
try {
|
|
107
|
+
const value = await execClipboard("wl-paste", ["--no-newline", "--type", "text"], signal, strict);
|
|
108
|
+
if (strict || value !== null)
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
catch { /* Compatibility: X11 may be available when the Wayland command is not. */ }
|
|
112
|
+
return execClipboard("xclip", ["-selection", "clipboard", "-o"], signal, strict);
|
|
106
113
|
}
|
|
107
114
|
export function writeSystemClipboardText(text) {
|
|
108
115
|
const write = async () => {
|
|
@@ -128,14 +135,22 @@ function nativeClipboard() {
|
|
|
128
135
|
function isFileDropFormat(format) {
|
|
129
136
|
return /(?:filedrop|hdrop|shell idlist)/iu.test(format);
|
|
130
137
|
}
|
|
131
|
-
function execClipboard(command, args, signal) {
|
|
132
|
-
|
|
138
|
+
function execClipboard(command, args, signal, strict = false) {
|
|
139
|
+
if (signal?.aborted)
|
|
140
|
+
return strict ? Promise.reject(new ImageAttachmentError("paste-timeout")) : Promise.resolve(null);
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
133
142
|
execFile(command, args, {
|
|
134
143
|
encoding: "utf8",
|
|
135
144
|
maxBuffer: MAX_CLIPBOARD_BYTES,
|
|
136
145
|
timeout: 5_000,
|
|
137
146
|
windowsHide: true,
|
|
147
|
+
...(strict ? { killSignal: "SIGKILL" } : {}),
|
|
138
148
|
...(signal === undefined ? {} : { signal }),
|
|
139
|
-
}, (error, stdout) =>
|
|
149
|
+
}, (error, stdout) => {
|
|
150
|
+
if (error && strict)
|
|
151
|
+
reject(new ImageAttachmentError("paste-unavailable"));
|
|
152
|
+
else
|
|
153
|
+
resolve(error || stdout.length === 0 ? null : stdout);
|
|
154
|
+
});
|
|
140
155
|
});
|
|
141
156
|
}
|
|
@@ -34,7 +34,9 @@ class ComponentBridge {
|
|
|
34
34
|
this.traceComposition?.("composition-start");
|
|
35
35
|
try {
|
|
36
36
|
const lines = [...this.port.render(width)];
|
|
37
|
-
|
|
37
|
+
// Protocol: Kitty APC carries pixels, not columns. Still validate every ordinary cell
|
|
38
|
+
// around the payload instead of exempting the entire image-bearing row from width checks.
|
|
39
|
+
const overflow = lines.findIndex(line => visibleWidth(line.replace(/\u001b_G[^\u001b]*\u001b\\/g, "")) > width);
|
|
38
40
|
if (overflow >= 0) {
|
|
39
41
|
throw new RangeError(`Pi TUI component row ${overflow} exceeds available width ${width}`);
|
|
40
42
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-14T18:48:36.221Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "9db1726bbe3fc2e8292f2ead1e7e9d0fd4d7d0dc9217b372582bf354b0f565dc",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "linux",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-14T18:48:19.618Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "d8cda6b0c7cb36c0cc41e90802aceebf6c02eaebbc08a83d7d963d2e918dbd7a",
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"platform": "win32",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-14T18:49:01.865Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "711c72395a5bd9efb4a302df595a7f38ed4cfafb8968426ebb2ed75a39cf1502",
|
|
12
12
|
"size": 177664
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const ENVIRONMENT_KEYS: readonly ["configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "paneId", "probeTrace", "processGuardianPath", "profileHome", "runtimeDir", "startupTrace", "suggestionDiagnostics", "terminalSessionId"];
|
|
1
|
+
declare const ENVIRONMENT_KEYS: readonly ["clipboardDiagnostics", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "paneId", "probeTrace", "processGuardianPath", "profileHome", "runtimeDir", "startupTrace", "suggestionDiagnostics", "terminalSessionId"];
|
|
2
2
|
declare const FILESYSTEM_KEYS: readonly ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
3
3
|
declare const STATE_KEYS: readonly ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile"];
|
|
4
4
|
declare const ENDPOINT_KEYS: readonly ["windowsPipeStem", "unixSocketFilename", "metadataFilename", "supervisorLogFilename", "databaseFilename"];
|
package/dist/product-identity.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import rawIdentity from "./product-identity.json" with { type: "json" };
|
|
2
2
|
const ENVIRONMENT_KEYS = [
|
|
3
|
-
"configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint",
|
|
3
|
+
"clipboardDiagnostics", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint",
|
|
4
4
|
"paneId", "probeTrace", "processGuardianPath", "profileHome", "runtimeDir", "startupTrace", "suggestionDiagnostics", "terminalSessionId",
|
|
5
5
|
];
|
|
6
6
|
const FILESYSTEM_KEYS = ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
@@ -9,6 +9,7 @@ export * from "./mouse.js";
|
|
|
9
9
|
export * from "./pane.js";
|
|
10
10
|
export * from "./progress-status.js";
|
|
11
11
|
export * from "./revision.js";
|
|
12
|
+
export * from "./selection-copy.js";
|
|
12
13
|
export * from "./scrollbar.js";
|
|
13
14
|
export * from "./shortcuts.js";
|
|
14
15
|
export * from "./spans.js";
|
|
@@ -9,6 +9,7 @@ export * from "./mouse.js";
|
|
|
9
9
|
export * from "./pane.js";
|
|
10
10
|
export * from "./progress-status.js";
|
|
11
11
|
export * from "./revision.js";
|
|
12
|
+
export * from "./selection-copy.js";
|
|
12
13
|
export * from "./scrollbar.js";
|
|
13
14
|
export * from "./shortcuts.js";
|
|
14
15
|
export * from "./spans.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type OrderedTextSelection, type TextSelectionLineContent } from "./text-selection.js";
|
|
2
|
+
/** Bound selected-source retention independently of history length and encoded payload size. */
|
|
3
|
+
export declare const MAX_COPY_SOURCE_UNITS: number;
|
|
4
|
+
export declare const MAX_COPY_ROWS = 65536;
|
|
5
|
+
export type SelectionCopyPrompt = "first" | "continuation" | undefined;
|
|
6
|
+
export interface SelectionCopyRow {
|
|
7
|
+
readonly text: string;
|
|
8
|
+
readonly prompt?: "first" | "continuation";
|
|
9
|
+
}
|
|
10
|
+
/** Immutable selected rows only; safe to transfer to an isolated text preparer. */
|
|
11
|
+
export interface SelectionCopySnapshot {
|
|
12
|
+
readonly selection: OrderedTextSelection;
|
|
13
|
+
readonly revision: number;
|
|
14
|
+
/** Prompt copy/cut already supplies exact expanded text; do not apply transcript chrome stripping. */
|
|
15
|
+
readonly literal?: boolean;
|
|
16
|
+
readonly rows: readonly SelectionCopyRow[];
|
|
17
|
+
readonly sourceUnits: number;
|
|
18
|
+
readonly captureMs?: number;
|
|
19
|
+
readonly rejected?: "size";
|
|
20
|
+
}
|
|
21
|
+
/** Shares the viewport's semantic chrome exclusions with isolated copy preparation. */
|
|
22
|
+
export declare function selectionCopyLineContent(text: string, prompt: SelectionCopyPrompt): TextSelectionLineContent;
|
|
23
|
+
/** Extracts one snapshot row with the existing grapheme/newline policy; run expensive rows off-thread. */
|
|
24
|
+
export declare function selectionCopyRowText(snapshot: Pick<SelectionCopySnapshot, "selection" | "literal">, row: SelectionCopyRow, index: number): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { textSelectionText, usefulTextLineContent } from "./text-selection.js";
|
|
2
|
+
import { stripAnsi } from "./text.js";
|
|
3
|
+
/** Bound selected-source retention independently of history length and encoded payload size. */
|
|
4
|
+
export const MAX_COPY_SOURCE_UNITS = 32 * 1024 * 1024;
|
|
5
|
+
export const MAX_COPY_ROWS = 65_536;
|
|
6
|
+
/** Shares the viewport's semantic chrome exclusions with isolated copy preparation. */
|
|
7
|
+
export function selectionCopyLineContent(text, prompt) {
|
|
8
|
+
const plain = stripAnsi(text);
|
|
9
|
+
if (prompt !== undefined) {
|
|
10
|
+
if (prompt === "first") {
|
|
11
|
+
const timestamp = /\s+\d{2}:\d{2}\s*$/.exec(plain);
|
|
12
|
+
if (timestamp !== null)
|
|
13
|
+
return usefulTextLineContent(plain.slice(0, timestamp.index), 3);
|
|
14
|
+
}
|
|
15
|
+
return usefulTextLineContent(plain, 3);
|
|
16
|
+
}
|
|
17
|
+
const from = plain.trim().length === 0 || (plain.startsWith(" ") && !plain.startsWith(" ")) ? 2 : 1;
|
|
18
|
+
if (plain.trim().length === 0)
|
|
19
|
+
return { from, to: from + 1 };
|
|
20
|
+
return usefulTextLineContent(plain, from);
|
|
21
|
+
}
|
|
22
|
+
/** Extracts one snapshot row with the existing grapheme/newline policy; run expensive rows off-thread. */
|
|
23
|
+
export function selectionCopyRowText(snapshot, row, index) {
|
|
24
|
+
if (snapshot.literal)
|
|
25
|
+
return row.text;
|
|
26
|
+
let plain = stripAnsi(row.text);
|
|
27
|
+
if (row.prompt === "first") {
|
|
28
|
+
const timestamp = /\s+\d{2}:\d{2}\s*$/.exec(plain);
|
|
29
|
+
if (timestamp !== null)
|
|
30
|
+
plain = plain.slice(0, timestamp.index);
|
|
31
|
+
}
|
|
32
|
+
const from = row.prompt !== undefined ? 3 : plain.startsWith(" ") && !plain.startsWith(" ") ? 2 : 1;
|
|
33
|
+
// Performance: trailing chrome is excluded before extraction; full-row copy need not measure every grapheme.
|
|
34
|
+
plain = plain.trimEnd();
|
|
35
|
+
return textSelectionText({
|
|
36
|
+
start: { line: 0, column: index === snapshot.selection.start.line ? snapshot.selection.start.column : 0 },
|
|
37
|
+
end: { line: 0, column: index === snapshot.selection.end.line ? snapshot.selection.end.column : Number.MAX_SAFE_INTEGER },
|
|
38
|
+
}, [plain], () => ({ from }));
|
|
39
|
+
}
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* Replaces the visible columns `[from, to)` of `line` with `span`, preserving
|
|
3
3
|
* the styling before it and re-asserting whatever was still open afterwards, so
|
|
4
4
|
* the tail keeps the colour it had. A wide character straddling either edge is
|
|
5
|
-
* replaced by spaces rather than split.
|
|
5
|
+
* replaced by spaces rather than split. Opt into the replaced cell's boundary
|
|
6
|
+
* style for background-preserving overlays; the span still owns its decoration.
|
|
6
7
|
*/
|
|
7
|
-
export declare function overlaySpan(line: string, from: number, to: number, span: string
|
|
8
|
+
export declare function overlaySpan(line: string, from: number, to: number, span: string, options?: {
|
|
9
|
+
readonly inheritStartStyle?: boolean;
|
|
10
|
+
}): string;
|
|
8
11
|
/** Wraps visible columns `[from,to)` in an explicit OSC 8 hyperlink. */
|
|
9
12
|
export declare function hyperlinkSgrSpan(line: string, from: number, to: number, target: string, widthOf?: (text: string) => number): string;
|
|
10
13
|
/** Returns the OSC 8 target painted at a zero-based visible terminal column. */
|