@timurproko/a1 0.2.7-dev.1255 → 0.2.7-dev.1264
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/app/session-shell/prompt-submission.d.ts +1 -1
- package/dist/app/session-shell/prompt-submission.js +9 -9
- package/dist/app/session-shell/resource-labels.d.ts +2 -0
- package/dist/app/session-shell/resource-labels.js +158 -0
- package/dist/app/session-shell/session-prompt-history.d.ts +29 -0
- package/dist/app/session-shell/session-prompt-history.js +80 -0
- package/dist/app/session-shell/session-selectors.d.ts +46 -0
- package/dist/app/session-shell/session-selectors.js +269 -0
- package/dist/app/session-shell/session-shell-root.d.ts +6 -28
- package/dist/app/session-shell/session-shell-root.js +26 -220
- package/dist/app/session-shell/session-shell.d.ts +3 -23
- package/dist/app/session-shell/session-shell.js +54 -365
- package/dist/app/session-shell/session-viewport-controller.d.ts +2 -10
- package/dist/app/session-shell/session-viewport-controller.js +40 -318
- package/dist/app/session-shell/settings-bindings.d.ts +1 -1
- package/dist/app/session-shell/settings-bindings.js +2 -2
- package/dist/app/session-shell/viewport-pointer.d.ts +83 -0
- package/dist/app/session-shell/viewport-pointer.js +361 -0
- package/dist/composition/owned-ui.js +63 -59
- package/dist/contracts/owned-ui/index.d.ts +1 -1
- package/dist/contracts/owned-ui/index.js +1 -1
- package/dist/contracts/owned-ui/validation.d.ts +1 -11
- package/dist/contracts/owned-ui/validation.js +8 -104
- package/dist/contracts/presentation/index.d.ts +1 -1
- package/dist/contracts/presentation/terminal.d.ts +0 -6
- package/dist/features/owned-ui/settings-app.js +13 -208
- package/dist/features/owned-ui/settings-model-dialog.d.ts +41 -0
- package/dist/features/owned-ui/settings-model-dialog.js +194 -0
- package/dist/foundation/release/automatic-update.js +1 -8
- package/dist/foundation/release/dependency-layer.js +3 -3
- package/dist/foundation/release/latest-release.js +1 -1
- package/dist/foundation/release/managed-path.d.ts +10 -0
- package/dist/foundation/release/managed-path.js +26 -0
- package/dist/foundation/release/release-gc.js +6 -8
- package/dist/foundation/release/restart-certification.js +3 -4
- package/dist/foundation/release/update-activation.js +1 -1
- package/dist/foundation/release/update-execution-lease.js +1 -7
- package/dist/foundation/release/update-installer.d.ts +45 -0
- package/dist/foundation/release/update-installer.js +57 -0
- package/dist/foundation/release/update-recovery.js +2 -9
- package/dist/foundation/release/update.d.ts +4 -23
- package/dist/foundation/release/update.js +160 -179
- package/dist/integrations/pi/components/bracketed-paste.d.ts +33 -0
- package/dist/integrations/pi/components/bracketed-paste.js +123 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +46 -140
- package/dist/integrations/pi/engine/adapter.d.ts +1 -8
- package/dist/integrations/pi/engine/adapter.js +6 -49
- package/dist/integrations/pi/engine/host.js +1 -1
- package/dist/integrations/pi/engine/repository-metadata.d.ts +49 -0
- package/dist/integrations/pi/engine/repository-metadata.js +189 -0
- package/dist/integrations/pi/engine/session-runtime.d.ts +6 -12
- package/dist/integrations/pi/engine/session-runtime.js +21 -178
- package/dist/integrations/pi/engine/view-state.d.ts +21 -2
- package/dist/integrations/pi/engine/view-state.js +129 -1
- package/dist/integrations/pi/engine/workflow-runner.d.ts +0 -1
- package/dist/integrations/pi/engine/workflow-runner.js +254 -365
- package/dist/integrations/pi/engine/workflow-share.d.ts +9 -0
- package/dist/integrations/pi/engine/workflow-share.js +168 -0
- package/dist/integrations/pi/engine/workflow-support.d.ts +0 -14
- package/dist/integrations/pi/engine/workflow-support.js +0 -47
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +9 -19
- package/dist/integrations/pi/tui-runtime/adapter.js +35 -144
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +4 -4
- package/dist/integrations/pi/tui-runtime/index.d.ts +0 -1
- package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +2 -2
- package/dist/integrations/pi/tui-runtime/presentation-adapter.d.ts +2 -2
- package/dist/ui/components/transcript-viewport-frame.d.ts +120 -0
- package/dist/ui/components/transcript-viewport-frame.js +314 -0
- package/dist/ui/components/transcript-viewport-types.d.ts +147 -0
- package/dist/ui/components/transcript-viewport.d.ts +2 -147
- package/dist/ui/components/transcript-viewport.js +87 -315
- package/package.json +3 -3
- package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -9
- package/dist/integrations/pi/engine/diagnostics-state.js +0 -34
- package/dist/integrations/pi/engine/usage-view.d.ts +0 -4
- package/dist/integrations/pi/engine/usage-view.js +0 -56
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +0 -22
- /package/dist/{integrations/pi/tui-runtime/contracts.js → ui/components/transcript-viewport-types.js} +0 -0
|
@@ -1,151 +1,6 @@
|
|
|
1
1
|
import { type SelectionCopySnapshot } from "./selection-copy.js";
|
|
2
|
-
import {
|
|
3
|
-
export
|
|
4
|
-
readonly id: string;
|
|
5
|
-
readonly firstRow: number;
|
|
6
|
-
readonly lastRow: number;
|
|
7
|
-
/** The source first row, including its submitted-prompt styling. */
|
|
8
|
-
readonly sourceRow: string;
|
|
9
|
-
/** Optional source-styled row used only while prominent and not hovered. */
|
|
10
|
-
readonly prominentSourceRow?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface TranscriptViewportConfig {
|
|
13
|
-
readonly scrollbarAppearance: ScrollbarAppearance;
|
|
14
|
-
readonly scrollbarStyle: ScrollbarStyle;
|
|
15
|
-
}
|
|
16
|
-
export interface TranscriptViewportTheme {
|
|
17
|
-
readonly track: (text: string) => string;
|
|
18
|
-
readonly thumb: (text: string, active: boolean) => string;
|
|
19
|
-
readonly sticky: (text: string, hovered: boolean) => string;
|
|
20
|
-
readonly quietSticky: (text: string) => string;
|
|
21
|
-
readonly bottomControl: (text: string, hovered: boolean) => string;
|
|
22
|
-
readonly selection: (line: string, from: number, to: number) => string;
|
|
23
|
-
}
|
|
24
|
-
export interface TranscriptViewportFrameInput {
|
|
25
|
-
readonly documentRows: readonly string[];
|
|
26
|
-
/** Optional paint-only transform; semantic selection and copying keep documentRows exact. */
|
|
27
|
-
readonly paintDocumentRow?: (row: string) => string;
|
|
28
|
-
/** Leading persistent document rows; later transient rows remain selectable visible-frame content. */
|
|
29
|
-
readonly selectableDocumentRowCount?: number;
|
|
30
|
-
/**
|
|
31
|
-
* First document row owned by the block that is currently streaming, if any. Rows from
|
|
32
|
-
* here to the end of the selectable document reflow together whenever that block
|
|
33
|
-
* re-renders, so the frame can declare how much damage is legitimately its own.
|
|
34
|
-
*/
|
|
35
|
-
readonly liveTailStartRow?: number;
|
|
36
|
-
/** Final transient rows to bottom-align with unused viewport space while content fits. */
|
|
37
|
-
readonly bottomAlignedTailRowCount?: number;
|
|
38
|
-
readonly dockRows: readonly string[];
|
|
39
|
-
/** Zero-based dock rows admitted to frame copying, classified before dock composition. */
|
|
40
|
-
readonly copyableDockRows?: readonly {
|
|
41
|
-
readonly row: number;
|
|
42
|
-
readonly prompt?: "first" | "continuation";
|
|
43
|
-
}[];
|
|
44
|
-
readonly promptAnchors: readonly TranscriptPromptAnchor[];
|
|
45
|
-
readonly width: number;
|
|
46
|
-
readonly height: number;
|
|
47
|
-
/**
|
|
48
|
-
* Optional zero-based terminal row for the floating bottom control. The shell
|
|
49
|
-
* supplies a stable anchor above its editor group so transient status and
|
|
50
|
-
* notification rows cannot make the control jump vertically.
|
|
51
|
-
*/
|
|
52
|
-
readonly bottomControlRow?: number;
|
|
53
|
-
/** Latest reported one-based terminal coordinates; bottom hover uses this frame's hit region. */
|
|
54
|
-
readonly pointerPosition?: {
|
|
55
|
-
readonly column: number;
|
|
56
|
-
readonly row: number;
|
|
57
|
-
};
|
|
58
|
-
/** Base-frame cells hidden by modal/replacement surfaces and therefore excluded from visual copy. */
|
|
59
|
-
readonly coveredCells?: readonly {
|
|
60
|
-
readonly row: number;
|
|
61
|
-
readonly columnStart: number;
|
|
62
|
-
readonly columnEnd: number;
|
|
63
|
-
}[];
|
|
64
|
-
/** Transient paint-only text that must not participate in frame allocation or copying. */
|
|
65
|
-
readonly frameOverlay?: {
|
|
66
|
-
/** One-based terminal row. */
|
|
67
|
-
readonly row: number;
|
|
68
|
-
readonly text: string;
|
|
69
|
-
};
|
|
70
|
-
readonly now?: number;
|
|
71
|
-
readonly theme?: TranscriptViewportTheme;
|
|
72
|
-
}
|
|
73
|
-
export interface TranscriptViewportHitRegions {
|
|
74
|
-
readonly viewportHeight: number;
|
|
75
|
-
readonly rail: {
|
|
76
|
-
readonly column: number;
|
|
77
|
-
/** One-based terminal row. The one-row top inset is intentionally excluded. */
|
|
78
|
-
readonly rowStart: number;
|
|
79
|
-
readonly trackHeight: number;
|
|
80
|
-
readonly geometry: ScrollbarGeometry;
|
|
81
|
-
} | null;
|
|
82
|
-
readonly sticky: {
|
|
83
|
-
readonly row: number;
|
|
84
|
-
readonly target: number;
|
|
85
|
-
readonly width: number;
|
|
86
|
-
} | null;
|
|
87
|
-
readonly bottom: {
|
|
88
|
-
readonly row: number;
|
|
89
|
-
readonly columnStart: number;
|
|
90
|
-
readonly columnEnd: number;
|
|
91
|
-
} | null;
|
|
92
|
-
/** Transient non-persistent document-tail rows currently visible in the viewport. */
|
|
93
|
-
readonly transientTail: readonly number[];
|
|
94
|
-
}
|
|
95
|
-
export interface TranscriptViewportFrameDescriptor {
|
|
96
|
-
readonly frameId: number;
|
|
97
|
-
readonly width: number;
|
|
98
|
-
readonly height: number;
|
|
99
|
-
readonly transcript: {
|
|
100
|
-
readonly rowStart: number;
|
|
101
|
-
readonly rowEnd: number;
|
|
102
|
-
} | null;
|
|
103
|
-
readonly dock: {
|
|
104
|
-
readonly rowStart: number;
|
|
105
|
-
readonly rowEnd: number;
|
|
106
|
-
} | null;
|
|
107
|
-
readonly previousDocumentRange: {
|
|
108
|
-
readonly start: number;
|
|
109
|
-
readonly end: number;
|
|
110
|
-
} | null;
|
|
111
|
-
readonly nextDocumentRange: {
|
|
112
|
-
readonly start: number;
|
|
113
|
-
readonly end: number;
|
|
114
|
-
};
|
|
115
|
-
readonly previousFollowingEnd: boolean | null;
|
|
116
|
-
readonly followingEnd: boolean;
|
|
117
|
-
/** Complete non-persistent suffix, including pending steering, alignment, and status rows. */
|
|
118
|
-
readonly transientRowCount: number;
|
|
119
|
-
/** Flexible rows inserted before the bottom-aligned live status while content fits. */
|
|
120
|
-
readonly transientAlignmentGapRows: number;
|
|
121
|
-
/** Bottom-aligned transient rows. */
|
|
122
|
-
readonly bottomAlignedTailRowCount: number;
|
|
123
|
-
/** Visible rows owned by the currently streaming transcript block. */
|
|
124
|
-
readonly liveTailRows: number;
|
|
125
|
-
readonly verticalShiftRows: number;
|
|
126
|
-
readonly safeVerticalShift: boolean;
|
|
127
|
-
/** Monotonic interaction revision used to reject stale selection evidence. */
|
|
128
|
-
readonly selectionRevision: number;
|
|
129
|
-
/** One-based terminal rows whose source or normalized selection range changed. */
|
|
130
|
-
readonly selectionDamagedRows: readonly number[];
|
|
131
|
-
readonly cause: "initial" | "steady" | "dock-input" | "follow-shift" | "detached" | "geometry-change";
|
|
132
|
-
}
|
|
133
|
-
export interface TranscriptViewportFrame {
|
|
134
|
-
readonly rows: readonly string[];
|
|
135
|
-
readonly contentWidth: number;
|
|
136
|
-
readonly scrollTop: number;
|
|
137
|
-
readonly maxScroll: number;
|
|
138
|
-
readonly followingEnd: boolean;
|
|
139
|
-
readonly hits: TranscriptViewportHitRegions;
|
|
140
|
-
readonly descriptor: TranscriptViewportFrameDescriptor;
|
|
141
|
-
/** Deterministic evidence for bounded selection-only row composition. */
|
|
142
|
-
readonly selectionDamage: {
|
|
143
|
-
readonly revision: number;
|
|
144
|
-
readonly recomputedRows: readonly number[];
|
|
145
|
-
readonly reusedRows: readonly number[];
|
|
146
|
-
readonly cacheEntries: number;
|
|
147
|
-
};
|
|
148
|
-
}
|
|
2
|
+
import type { TranscriptViewportConfig, TranscriptViewportFrameInput, TranscriptViewportFrameDescriptor, TranscriptViewportFrame } from "./transcript-viewport-types.js";
|
|
3
|
+
export type { TranscriptPromptAnchor, TranscriptViewportConfig, TranscriptViewportTheme, TranscriptViewportFrameInput, TranscriptViewportHitRegions, TranscriptViewportFrameDescriptor, TranscriptViewportFrame, } from "./transcript-viewport-types.js";
|
|
149
4
|
/**
|
|
150
5
|
* A1-owned viewport state. It knows only rows and semantic prompt anchors; Pi
|
|
151
6
|
* components and transcript payloads remain outside this neutral boundary.
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { MAX_COPY_SOURCE_UNITS, selectionCopyLineContent } from "./selection-copy.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { faint, truncateToWidth, displayWidth, stripAnsi } from "./text.js";
|
|
2
|
+
import { faint, truncateToWidth, stripAnsi } from "./text.js";
|
|
3
|
+
import { alignTailRows, frameDescriptor, hitRegions, layoutScrollbar, maskSelectionRows, placeBottomControl, sameRectangle, VIEWPORT_PROMPT_ZONES, ViewportRowCaches, } from "./transcript-viewport-frame.js";
|
|
5
4
|
import { extendTextSelection, orderedTextSelection, paintTextSelection, pressTextSelection, releaseTextSelection, textSelectionLineExtendColumn, textSelectionPointAt, textSelectionText, usefulTextLineContent, } from "./text-selection.js";
|
|
6
|
-
const CONTROL_STYLE_RESET = "\u001b]8;;\u001b\\\u001b[0m";
|
|
7
|
-
// Protocol: upstream OSC 133 zones are layout annotations, not fullscreen cursor commands.
|
|
8
|
-
// Styling/padding can move them away from the row prefix Pi strips; 133;A then advances a real
|
|
9
|
-
// terminal parser to a fresh line. The custom viewport owns prompt anchors separately.
|
|
10
|
-
const VIEWPORT_PROMPT_ZONES = /\u001b\]133;[ABC](?:\u0007|\u001b\\)/gu;
|
|
11
|
-
// Invariant: scrollbar chrome closes source links and decoration without clearing
|
|
12
|
-
// the row surface painted beneath its dedicated, non-semantic gutter.
|
|
13
|
-
const GUTTER_DECORATION_RESET = "\u001b]8;;\u001b\\\u001b[22;23;24;25;27;28;29;39;54;55m";
|
|
14
5
|
const IDENTITY_ROW = (row) => row;
|
|
15
6
|
function documentSelectionAnchor(row, source, liveFromBottom) {
|
|
16
7
|
return { kind: "document", row, source, identity: stripAnsi(source).trimEnd(), liveFromBottom };
|
|
@@ -66,12 +57,7 @@ export class TranscriptViewport {
|
|
|
66
57
|
#copyableSelection = false;
|
|
67
58
|
// Invariant: exact unselected rows cover the latest visible base frame and dock.
|
|
68
59
|
#selectionRows = [];
|
|
69
|
-
#
|
|
70
|
-
#baseRowCache = new Map();
|
|
71
|
-
#selectedRowCache = new Map();
|
|
72
|
-
#finalRowCache = new Map();
|
|
73
|
-
#functionIds = new WeakMap();
|
|
74
|
-
#nextFunctionId = 1;
|
|
60
|
+
#rowCaches = new ViewportRowCaches();
|
|
75
61
|
#previousVisibleStates = [];
|
|
76
62
|
#documentRows = [];
|
|
77
63
|
#selectableDocumentRowCount = 0;
|
|
@@ -369,10 +355,7 @@ export class TranscriptViewport {
|
|
|
369
355
|
this.#selectionRows = [];
|
|
370
356
|
this.#selectionRowAnchors = [];
|
|
371
357
|
this.#selectionClick = undefined;
|
|
372
|
-
this.#
|
|
373
|
-
this.#baseRowCache.clear();
|
|
374
|
-
this.#selectedRowCache.clear();
|
|
375
|
-
this.#finalRowCache.clear();
|
|
358
|
+
this.#rowCaches.clear();
|
|
376
359
|
this.#previousVisibleStates = [];
|
|
377
360
|
this.#documentRows = [];
|
|
378
361
|
this.#selectableDocumentRowCount = 0;
|
|
@@ -399,18 +382,7 @@ export class TranscriptViewport {
|
|
|
399
382
|
const dock = input.dockRows.length > height ? input.dockRows.slice(-height) : [...input.dockRows];
|
|
400
383
|
this.#copyableDockRows = copyableDockRowsBySuffix(input.dockRows, input.copyableDockRows);
|
|
401
384
|
const viewportHeight = Math.max(0, height - dock.length);
|
|
402
|
-
const bottomAlignedTailRowCount =
|
|
403
|
-
const transientAlignmentGapRows = bottomAlignedTailRowCount > 0
|
|
404
|
-
? Math.max(0, viewportHeight - input.documentRows.length)
|
|
405
|
-
: 0;
|
|
406
|
-
const tailStart = input.documentRows.length - bottomAlignedTailRowCount;
|
|
407
|
-
const documentRows = transientAlignmentGapRows === 0
|
|
408
|
-
? input.documentRows
|
|
409
|
-
: [
|
|
410
|
-
...input.documentRows.slice(0, tailStart),
|
|
411
|
-
...Array.from({ length: transientAlignmentGapRows }, () => ""),
|
|
412
|
-
...input.documentRows.slice(tailStart),
|
|
413
|
-
];
|
|
385
|
+
const { documentRows, bottomAlignedTailRowCount, transientAlignmentGapRows } = alignTailRows(input.documentRows, input.bottomAlignedTailRowCount, viewportHeight);
|
|
414
386
|
this.#maxScroll = Math.max(0, documentRows.length - viewportHeight);
|
|
415
387
|
if (this.#followingEnd)
|
|
416
388
|
this.#scrollTop = this.#maxScroll;
|
|
@@ -420,21 +392,9 @@ export class TranscriptViewport {
|
|
|
420
392
|
this.#followingEnd = true;
|
|
421
393
|
if (this.#followingEnd)
|
|
422
394
|
this.#newMessages = 0;
|
|
423
|
-
const geometry =
|
|
424
|
-
contentLength: documentRows.length,
|
|
425
|
-
|
|
426
|
-
scroll: this.#scrollTop,
|
|
427
|
-
// Invariant: the rail keeps its top gap; the shell owns the spacing above the dock.
|
|
428
|
-
trackHeight: Math.max(0, viewportHeight - 1),
|
|
429
|
-
});
|
|
430
|
-
const presentation = scrollbarPresentation({
|
|
431
|
-
geometry,
|
|
432
|
-
appearance: this.#config.scrollbarAppearance,
|
|
433
|
-
style: this.#config.scrollbarStyle,
|
|
434
|
-
hovered: this.#railHovered,
|
|
435
|
-
dragging: this.#railDragging,
|
|
436
|
-
activeUntil: this.#activeUntil,
|
|
437
|
-
now: input.now ?? Date.now(),
|
|
395
|
+
const { geometry, presentation } = layoutScrollbar({
|
|
396
|
+
contentLength: documentRows.length, viewportHeight, scroll: this.#scrollTop, config: this.#config,
|
|
397
|
+
hovered: this.#railHovered, dragging: this.#railDragging, activeUntil: this.#activeUntil, now: input.now ?? Date.now(),
|
|
438
398
|
});
|
|
439
399
|
const contentWidth = presentation.reservesSpace ? Math.max(1, width - 1) : width;
|
|
440
400
|
this.#documentRows = documentRows;
|
|
@@ -448,212 +408,58 @@ export class TranscriptViewport {
|
|
|
448
408
|
// use contentWidth and leave the gutter outside semantic selection.
|
|
449
409
|
this.#selectionWidth = width;
|
|
450
410
|
this.#viewportHeight = viewportHeight;
|
|
451
|
-
const paintDocumentRow = input.paintDocumentRow ?? IDENTITY_ROW;
|
|
452
411
|
const cacheLimit = Math.max(32, viewportHeight * 6);
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const paintId = this.#functionId(paintDocumentRow);
|
|
458
|
-
const paintRecomputedRows = new Set();
|
|
459
|
-
const visibleSource = documentRows.slice(this.#scrollTop, this.#scrollTop + viewportHeight);
|
|
460
|
-
while (visibleSource.length < viewportHeight)
|
|
461
|
-
visibleSource.push("");
|
|
462
|
-
const visible = visibleSource.map((row, index) => {
|
|
463
|
-
const painted = cachedString(this.#paintedRowCache, `${paintId}\u0000${row}`, cacheLimit, () => paintDocumentRow(row));
|
|
464
|
-
if (!painted.reused)
|
|
465
|
-
paintRecomputedRows.add(index);
|
|
466
|
-
return painted.value;
|
|
467
|
-
});
|
|
468
|
-
const governing = governingPrompt(input.promptAnchors, this.#scrollTop);
|
|
469
|
-
const stickyActive = governing !== null && governing.firstRow < this.#scrollTop;
|
|
470
|
-
if (stickyActive && visible.length > 0) {
|
|
471
|
-
const quiet = this.#scrollTop > governing.lastRow;
|
|
472
|
-
// Compatibility: only non-hovered prominent rows may retain source metadata roles.
|
|
473
|
-
// Quiet and hover keep the established full-row styling; anchors without a
|
|
474
|
-
// variant retain their existing presentation in every state.
|
|
475
|
-
const sourceRow = !quiet && !this.#stickyHovered
|
|
476
|
-
? governing.prominentSourceRow ?? governing.sourceRow
|
|
477
|
-
: governing.sourceRow;
|
|
478
|
-
const source = cachedString(this.#paintedRowCache, `${paintId}\u0000${sourceRow}`, cacheLimit, () => paintDocumentRow(sourceRow));
|
|
479
|
-
if (!source.reused)
|
|
480
|
-
paintRecomputedRows.add(0);
|
|
481
|
-
const sticky = theme.sticky(source.value, this.#stickyHovered);
|
|
482
|
-
visible[0] = quiet && !this.#stickyHovered ? theme.quietSticky(sticky) : sticky;
|
|
483
|
-
visibleSource[0] = sourceRow;
|
|
484
|
-
}
|
|
485
|
-
this.#selectionRows = [
|
|
486
|
-
...visibleSource.map(row => truncateToWidth(row, contentWidth)),
|
|
487
|
-
...dock,
|
|
488
|
-
].slice(0, height);
|
|
489
|
-
const visibleAnchors = Array.from({ length: viewportHeight }, (_value, row) => {
|
|
490
|
-
const documentRow = this.#scrollTop + row;
|
|
491
|
-
return documentRow < documentRows.length
|
|
492
|
-
? viewportSelectionAnchor(documentRow, documentRows[documentRow] ?? "", this.#selectableDocumentRowCount, documentRows.length)
|
|
493
|
-
: { kind: "screen", row, source: this.#selectionRows[row] ?? "" };
|
|
494
|
-
});
|
|
495
|
-
this.#firstSelectableRow = stickyActive && viewportHeight > 0 ? 1 : 0;
|
|
412
|
+
this.#rowCaches.trim(cacheLimit);
|
|
413
|
+
const { visible, visibleSource, paintRecomputedRows, governing } = this.#paintVisibleRows(input, theme, cacheLimit);
|
|
414
|
+
this.#selectionRows = [...visibleSource.map(row => truncateToWidth(row, contentWidth)), ...dock].slice(0, height);
|
|
415
|
+
this.#firstSelectableRow = governing !== null && viewportHeight > 0 ? 1 : 0;
|
|
496
416
|
this.#selectionRowAnchors = [
|
|
497
|
-
...
|
|
417
|
+
...visibleSelectionAnchors(documentRows, this.#scrollTop, viewportHeight, this.#selectableDocumentRowCount, this.#selectionRows),
|
|
498
418
|
...dock.map((_row, index) => ({ kind: "dock", fromBottom: dock.length - index - 1 })),
|
|
499
419
|
].slice(0, height);
|
|
500
420
|
this.#projectSelection(documentRows, dock, height);
|
|
501
|
-
const selectionPainterId = this.#functionId(theme.selection);
|
|
502
421
|
// Concurrency: a painter may synchronously route input in tests or host integrations.
|
|
503
422
|
// Label this frame with the exact selection snapshot it began composing.
|
|
504
423
|
const composingSelectionRevision = this.#selectionRevision;
|
|
505
424
|
const orderedSelection = this.#visibleSelection();
|
|
425
|
+
const frameRows = [...visible, ...dock].slice(0, height);
|
|
426
|
+
const control = placeBottomControl({
|
|
427
|
+
detached: this.#maxScroll > 0 && !this.#followingEnd, newMessages: this.#newMessages, contentWidth, viewportHeight,
|
|
428
|
+
frameRowCount: frameRows.length, anchorRow: input.bottomControlRow, pointer: input.pointerPosition, theme,
|
|
429
|
+
});
|
|
430
|
+
this.#selectionRows = maskSelectionRows(this.#selectionRows, width, control, input.coveredCells);
|
|
431
|
+
this.#updateCopyableSelection();
|
|
432
|
+
const rowPaint = {
|
|
433
|
+
width, contentWidth, viewportHeight, cacheLimit, theme, selectionPainterId: this.#rowCaches.functionId(theme.selection),
|
|
434
|
+
geometry, presentation, railActive: this.#railHovered || this.#railDragging, overlay: input.frameOverlay, control,
|
|
435
|
+
};
|
|
506
436
|
const recomputedRows = [];
|
|
507
437
|
const reusedRows = [];
|
|
508
438
|
const selectionDamagedRows = [];
|
|
509
439
|
const visibleStates = [];
|
|
510
|
-
const frameRows = [...visible, ...dock].slice(0, height);
|
|
511
|
-
const selectionRows = [...this.#selectionRows];
|
|
512
|
-
let bottomHit = null;
|
|
513
|
-
let control;
|
|
514
|
-
if (this.#maxScroll > 0 && !this.#followingEnd && frameRows.length > 0) {
|
|
515
|
-
const genericLabel = " Jump to bottom (Ctrl+End) ↓ ";
|
|
516
|
-
const countedLabel = this.#newMessages > 0
|
|
517
|
-
? ` ${this.#newMessages} new message${this.#newMessages === 1 ? "" : "s"} (Ctrl+End) ↓ `
|
|
518
|
-
: genericLabel;
|
|
519
|
-
const label = displayWidth(countedLabel) <= contentWidth ? countedLabel : genericLabel;
|
|
520
|
-
const labelWidth = displayWidth(label);
|
|
521
|
-
if (labelWidth <= contentWidth) {
|
|
522
|
-
const fallbackRow = Math.max(0, viewportHeight - 1);
|
|
523
|
-
const row = clamp(input.bottomControlRow ?? fallbackRow, 0, frameRows.length - 1);
|
|
524
|
-
const left = Math.floor((contentWidth - labelWidth) / 2);
|
|
525
|
-
bottomHit = { row: row + 1, columnStart: left + 1, columnEnd: left + labelWidth };
|
|
526
|
-
// Invariant: appearance and hit testing share current geometry, even when no mouse moves.
|
|
527
|
-
const pointer = input.pointerPosition;
|
|
528
|
-
const bottomHovered = pointer !== undefined && pointer.row === bottomHit.row
|
|
529
|
-
&& pointer.column >= bottomHit.columnStart && pointer.column <= bottomHit.columnEnd;
|
|
530
|
-
control = { row, left, text: `${CONTROL_STYLE_RESET}${theme.bottomControl(label, bottomHovered)}` };
|
|
531
|
-
// Invariant: the control floats above selection; copy restores the text beneath it.
|
|
532
|
-
selectionRows[row] = overlaySpan(padRowPreservingBackground(selectionRows[row] ?? "", width), left, left + labelWidth, " ".repeat(labelWidth));
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
for (const covered of input.coveredCells ?? []) {
|
|
536
|
-
const row = covered.row - 1;
|
|
537
|
-
if (row < 0 || row >= selectionRows.length)
|
|
538
|
-
continue;
|
|
539
|
-
const from = clamp(covered.columnStart - 1, 0, width);
|
|
540
|
-
const to = clamp(covered.columnEnd, from, width);
|
|
541
|
-
selectionRows[row] = overlaySpan(padRowPreservingBackground(selectionRows[row] ?? "", width), from, to, " ".repeat(to - from));
|
|
542
|
-
}
|
|
543
|
-
this.#selectionRows = selectionRows;
|
|
544
|
-
this.#updateCopyableSelection();
|
|
545
440
|
for (let row = 0; row < frameRows.length; row += 1) {
|
|
546
|
-
const
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const paintRange = range !== null && row < viewportHeight
|
|
553
|
-
&& contentWidth < width && range.to === contentWidth
|
|
554
|
-
? { from: range.from, to: width }
|
|
555
|
-
: range;
|
|
556
|
-
const padded = row < viewportHeight || paintRange !== null;
|
|
557
|
-
const base = cachedString(this.#baseRowCache, `${width}\u0000${contentWidth}\u0000${row < viewportHeight}\u0000${padded}\u0000${painted}`, cacheLimit, () => row < viewportHeight
|
|
558
|
-
? padViewportRowWithGutter(painted, contentWidth, width)
|
|
559
|
-
: padded ? padRowPreservingBackground(painted, width) : painted);
|
|
560
|
-
let rowRecomputed = row < viewportHeight && paintRecomputedRows.has(row) || !base.reused;
|
|
561
|
-
const rangeKey = paintRange === null ? "-" : `${paintRange.from}:${paintRange.to}`;
|
|
562
|
-
const selected = paintRange === null
|
|
563
|
-
? { value: base.value, reused: true }
|
|
564
|
-
: cachedString(this.#selectedRowCache, `${selectionPainterId}\u0000${rangeKey}\u0000${base.value}`, cacheLimit, () => theme.selection(base.value, paintRange.from, paintRange.to));
|
|
565
|
-
rowRecomputed ||= !selected.reused;
|
|
566
|
-
// Invariant: paint the rail after selection over the gutter's continued row
|
|
567
|
-
// background. Source content and selection still end at the preceding cell.
|
|
568
|
-
let railCell = "";
|
|
569
|
-
if (row > 0 && row < viewportHeight && presentation.visible && geometry !== null) {
|
|
570
|
-
const trackRow = row - 1;
|
|
571
|
-
const thumb = isThumbRow(geometry, trackRow);
|
|
572
|
-
const glyph = thumb ? presentation.thumbGlyph : presentation.trackGlyph;
|
|
573
|
-
railCell = thumb ? theme.thumb(glyph, this.#railHovered || this.#railDragging) : theme.track(glyph);
|
|
574
|
-
}
|
|
575
|
-
const overlayLimit = row < viewportHeight ? contentWidth : width;
|
|
576
|
-
const overlayText = input.frameOverlay?.row === row + 1
|
|
577
|
-
? truncateToWidth(input.frameOverlay.text, overlayLimit)
|
|
578
|
-
: "";
|
|
579
|
-
const overlayWidth = displayWidth(overlayText);
|
|
580
|
-
const rowControl = control?.row === row && bottomHit !== null
|
|
581
|
-
? { left: control.left, right: bottomHit.columnEnd, text: control.text }
|
|
582
|
-
: null;
|
|
583
|
-
const final = cachedString(this.#finalRowCache, `${width}\u0000${railCell}\u0000${overlayText}\u0000${rowControl?.text ?? ""}\u0000${selected.value}`, cacheLimit, () => {
|
|
584
|
-
const withRail = railCell.length === 0 ? selected.value : overlaySpan(selected.value, width - 1, width, `${GUTTER_DECORATION_RESET}${railCell}`, { inheritStartStyle: true });
|
|
585
|
-
// Invariant: floating control styling is applied only after the row and
|
|
586
|
-
// gutter surfaces are resolved, so its inline background cannot leak.
|
|
587
|
-
const withControl = rowControl === null ? withRail : overlaySpan(withRail, rowControl.left, rowControl.right, rowControl.text);
|
|
588
|
-
return overlayWidth === 0 ? withControl : overlaySpan(padRowPreservingBackground(withControl, width), overlayLimit - overlayWidth, overlayLimit, `${GUTTER_DECORATION_RESET}${overlayText}`, { inheritStartStyle: true });
|
|
589
|
-
});
|
|
590
|
-
rowRecomputed ||= !final.reused;
|
|
591
|
-
frameRows[row] = final.value.replace(VIEWPORT_PROMPT_ZONES, "");
|
|
592
|
-
(rowRecomputed ? recomputedRows : reusedRows).push(row + 1);
|
|
593
|
-
const state = `${width}\u0000${row}\u0000${painted}\u0000${rangeKey}\u0000${range === null ? "" : selectionPainterId}\u0000${railCell}\u0000${overlayText}\u0000${rowControl?.text ?? ""}`;
|
|
594
|
-
visibleStates.push(state);
|
|
595
|
-
if (this.#previousVisibleStates[row] !== state)
|
|
441
|
+
const range = selectionRangeForLine(orderedSelection, row, this.#selectionRows.length, row < viewportHeight ? contentWidth : width);
|
|
442
|
+
const painted = this.#rowCaches.paintRow(rowPaint, row, frameRows[row] ?? "", range, paintRecomputedRows.has(row));
|
|
443
|
+
frameRows[row] = painted.text;
|
|
444
|
+
(painted.recomputed ? recomputedRows : reusedRows).push(row + 1);
|
|
445
|
+
visibleStates.push(painted.state);
|
|
446
|
+
if (this.#previousVisibleStates[row] !== painted.state)
|
|
596
447
|
selectionDamagedRows.push(row + 1);
|
|
597
448
|
}
|
|
598
449
|
this.#previousVisibleStates = visibleStates;
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
&& previousDescriptor.height === height
|
|
607
|
-
&& sameRectangle(previousDescriptor.transcript, viewportHeight === 0 ? null : { rowStart: 1, rowEnd: viewportHeight })
|
|
608
|
-
&& sameRectangle(previousDescriptor.dock, dock.length === 0 ? null : { rowStart: viewportHeight + 1, rowEnd: height });
|
|
609
|
-
const verticalShiftRows = previousDocumentRange === null ? 0 : nextDocumentRange.start - previousDocumentRange.start;
|
|
610
|
-
const safeVerticalShift = sameGeometry
|
|
611
|
-
&& previousDescriptor?.followingEnd === true
|
|
612
|
-
&& this.#followingEnd
|
|
613
|
-
&& this.#selection === undefined
|
|
614
|
-
&& verticalShiftRows > 0
|
|
615
|
-
&& verticalShiftRows < viewportHeight;
|
|
616
|
-
const descriptor = {
|
|
617
|
-
frameId: ++this.#frameId,
|
|
618
|
-
width,
|
|
619
|
-
height,
|
|
620
|
-
transcript: viewportHeight === 0 ? null : { rowStart: 1, rowEnd: viewportHeight },
|
|
621
|
-
dock: dock.length === 0 ? null : { rowStart: viewportHeight + 1, rowEnd: height },
|
|
622
|
-
previousDocumentRange,
|
|
623
|
-
nextDocumentRange,
|
|
624
|
-
previousFollowingEnd: previousDescriptor?.followingEnd ?? null,
|
|
625
|
-
followingEnd: this.#followingEnd,
|
|
626
|
-
transientRowCount: Math.max(0, documentRows.length - this.#selectableDocumentRowCount),
|
|
627
|
-
transientAlignmentGapRows,
|
|
628
|
-
bottomAlignedTailRowCount,
|
|
629
|
-
liveTailRows: visibleLiveTailRows(input.liveTailStartRow, this.#selectableDocumentRowCount, this.#scrollTop, viewportHeight),
|
|
630
|
-
verticalShiftRows,
|
|
631
|
-
safeVerticalShift,
|
|
632
|
-
selectionRevision: composingSelectionRevision,
|
|
633
|
-
selectionDamagedRows,
|
|
634
|
-
cause: previousDescriptor === null
|
|
635
|
-
? "initial"
|
|
636
|
-
: !sameGeometry
|
|
637
|
-
? "geometry-change"
|
|
638
|
-
: !this.#followingEnd
|
|
639
|
-
? "detached"
|
|
640
|
-
: safeVerticalShift
|
|
641
|
-
? "follow-shift"
|
|
642
|
-
: "steady",
|
|
643
|
-
};
|
|
450
|
+
const descriptor = frameDescriptor({
|
|
451
|
+
frameId: ++this.#frameId, previous: previousDescriptor, width, height, viewportHeight, dockRowCount: dock.length,
|
|
452
|
+
documentRowCount: documentRows.length, selectableDocumentRowCount: this.#selectableDocumentRowCount,
|
|
453
|
+
scrollTop: this.#scrollTop, followingEnd: this.#followingEnd, selecting: this.#selection !== undefined,
|
|
454
|
+
transientAlignmentGapRows, bottomAlignedTailRowCount, liveTailStartRow: input.liveTailStartRow,
|
|
455
|
+
selectionRevision: composingSelectionRevision, selectionDamagedRows,
|
|
456
|
+
});
|
|
644
457
|
assertTranscriptViewportFrameDescriptor(descriptor);
|
|
645
|
-
const hits = {
|
|
646
|
-
viewportHeight,
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
trackHeight: Math.max(0, viewportHeight - 1),
|
|
651
|
-
geometry,
|
|
652
|
-
},
|
|
653
|
-
sticky: stickyActive ? { row: 1, target: governing.firstRow, width: contentWidth } : null,
|
|
654
|
-
bottom: bottomHit,
|
|
655
|
-
transientTail: Array.from({ length: Math.max(0, Math.min(documentRows.length, this.#scrollTop + viewportHeight) - Math.max(this.#scrollTop, this.#selectableDocumentRowCount)) }, (_row, index) => Math.max(this.#scrollTop, this.#selectableDocumentRowCount) - this.#scrollTop + index + 1),
|
|
656
|
-
};
|
|
458
|
+
const hits = hitRegions({
|
|
459
|
+
width, contentWidth, viewportHeight, documentRowCount: documentRows.length,
|
|
460
|
+
selectableDocumentRowCount: this.#selectableDocumentRowCount, scrollTop: this.#scrollTop, geometry,
|
|
461
|
+
railHidden: this.#config.scrollbarAppearance === "hidden", stickyTarget: governing?.firstRow ?? null, bottom: control?.hit ?? null,
|
|
462
|
+
});
|
|
657
463
|
const frame = {
|
|
658
464
|
rows: frameRows,
|
|
659
465
|
contentWidth,
|
|
@@ -662,17 +468,48 @@ export class TranscriptViewport {
|
|
|
662
468
|
followingEnd: this.#followingEnd,
|
|
663
469
|
hits,
|
|
664
470
|
descriptor,
|
|
665
|
-
selectionDamage: {
|
|
666
|
-
revision: composingSelectionRevision,
|
|
667
|
-
recomputedRows,
|
|
668
|
-
reusedRows,
|
|
669
|
-
cacheEntries: this.#paintedRowCache.size + this.#baseRowCache.size
|
|
670
|
-
+ this.#selectedRowCache.size + this.#finalRowCache.size,
|
|
671
|
-
},
|
|
471
|
+
selectionDamage: { revision: composingSelectionRevision, recomputedRows, reusedRows, cacheEntries: this.#rowCaches.entries },
|
|
672
472
|
};
|
|
673
473
|
this.#frame = frame;
|
|
674
474
|
return frame;
|
|
675
475
|
}
|
|
476
|
+
// Rationale: the visible document rows go through the paint transform, with the governing prompt
|
|
477
|
+
// pinned as a sticky header once its first row has scrolled away. `governing` is that header's
|
|
478
|
+
// anchor, or null while the prompt's first row is still on screen.
|
|
479
|
+
#paintVisibleRows(input, theme, cacheLimit) {
|
|
480
|
+
const paintDocumentRow = input.paintDocumentRow ?? IDENTITY_ROW;
|
|
481
|
+
const paintId = this.#rowCaches.functionId(paintDocumentRow);
|
|
482
|
+
const paintRecomputedRows = new Set();
|
|
483
|
+
const visibleSource = this.#documentRows.slice(this.#scrollTop, this.#scrollTop + this.#viewportHeight);
|
|
484
|
+
while (visibleSource.length < this.#viewportHeight)
|
|
485
|
+
visibleSource.push("");
|
|
486
|
+
const visible = visibleSource.map((row, index) => {
|
|
487
|
+
const painted = this.#rowCaches.painted(paintId, row, cacheLimit, paintDocumentRow);
|
|
488
|
+
if (!painted.reused)
|
|
489
|
+
paintRecomputedRows.add(index);
|
|
490
|
+
return painted.value;
|
|
491
|
+
});
|
|
492
|
+
const governing = governingPrompt(input.promptAnchors, this.#scrollTop);
|
|
493
|
+
const stickyActive = governing !== null && governing.firstRow < this.#scrollTop;
|
|
494
|
+
if (!stickyActive)
|
|
495
|
+
return { visible, visibleSource, paintRecomputedRows, governing: null };
|
|
496
|
+
if (visible.length === 0)
|
|
497
|
+
return { visible, visibleSource, paintRecomputedRows, governing };
|
|
498
|
+
const quiet = this.#scrollTop > governing.lastRow;
|
|
499
|
+
// Compatibility: only non-hovered prominent rows may retain source metadata roles.
|
|
500
|
+
// Quiet and hover keep the established full-row styling; anchors without a
|
|
501
|
+
// variant retain their existing presentation in every state.
|
|
502
|
+
const sourceRow = !quiet && !this.#stickyHovered
|
|
503
|
+
? governing.prominentSourceRow ?? governing.sourceRow
|
|
504
|
+
: governing.sourceRow;
|
|
505
|
+
const source = this.#rowCaches.painted(paintId, sourceRow, cacheLimit, paintDocumentRow);
|
|
506
|
+
if (!source.reused)
|
|
507
|
+
paintRecomputedRows.add(0);
|
|
508
|
+
const sticky = theme.sticky(source.value, this.#stickyHovered);
|
|
509
|
+
visible[0] = quiet && !this.#stickyHovered ? theme.quietSticky(sticky) : sticky;
|
|
510
|
+
visibleSource[0] = sourceRow;
|
|
511
|
+
return { visible, visibleSource, paintRecomputedRows, governing };
|
|
512
|
+
}
|
|
676
513
|
/** Reuses an established transcript frame when only same-height dock rows changed. */
|
|
677
514
|
composeDockOnly(dockRows, width, height, copyableDockRows = []) {
|
|
678
515
|
const previous = this.#frame;
|
|
@@ -716,15 +553,6 @@ export class TranscriptViewport {
|
|
|
716
553
|
this.#frame = frame;
|
|
717
554
|
return frame;
|
|
718
555
|
}
|
|
719
|
-
#functionId(value) {
|
|
720
|
-
const existing = this.#functionIds.get(value);
|
|
721
|
-
if (existing !== undefined)
|
|
722
|
-
return existing;
|
|
723
|
-
const id = this.#nextFunctionId;
|
|
724
|
-
this.#nextFunctionId += 1;
|
|
725
|
-
this.#functionIds.set(value, id);
|
|
726
|
-
return id;
|
|
727
|
-
}
|
|
728
556
|
#selectionAnchorAt(line) {
|
|
729
557
|
if (line < 0 || line >= this.#selectionRows.length)
|
|
730
558
|
return undefined;
|
|
@@ -935,34 +763,13 @@ function selectionRangeForLine(selection, documentLine, selectableRowCount, widt
|
|
|
935
763
|
const boundedTo = clamp(to, boundedFrom, width);
|
|
936
764
|
return boundedTo > boundedFrom ? { from: boundedFrom, to: boundedTo } : null;
|
|
937
765
|
}
|
|
938
|
-
function
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
}
|
|
945
|
-
const value = create();
|
|
946
|
-
cache.set(key, value);
|
|
947
|
-
while (cache.size > limit)
|
|
948
|
-
cache.delete(cache.keys().next().value);
|
|
949
|
-
return { value, reused: false };
|
|
950
|
-
}
|
|
951
|
-
function trimCache(cache, limit) {
|
|
952
|
-
while (cache.size > limit)
|
|
953
|
-
cache.delete(cache.keys().next().value);
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* Rows of the streaming block that the visible window actually shows. Gap insertion happens
|
|
957
|
-
* after the selectable document, so live-block indices need no adjustment here.
|
|
958
|
-
*/
|
|
959
|
-
function visibleLiveTailRows(liveTailStartRow, selectableDocumentRowCount, scrollTop, viewportHeight) {
|
|
960
|
-
if (liveTailStartRow === undefined || !Number.isSafeInteger(liveTailStartRow))
|
|
961
|
-
return 0;
|
|
962
|
-
const start = clamp(liveTailStartRow, 0, selectableDocumentRowCount);
|
|
963
|
-
const visibleStart = Math.max(start, scrollTop);
|
|
964
|
-
const visibleEnd = Math.min(selectableDocumentRowCount, scrollTop + viewportHeight);
|
|
965
|
-
return Math.max(0, visibleEnd - visibleStart);
|
|
766
|
+
function visibleSelectionAnchors(documentRows, scrollTop, viewportHeight, selectableDocumentRowCount, selectionRows) {
|
|
767
|
+
return Array.from({ length: viewportHeight }, (_value, row) => {
|
|
768
|
+
const documentRow = scrollTop + row;
|
|
769
|
+
return documentRow < documentRows.length
|
|
770
|
+
? viewportSelectionAnchor(documentRow, documentRows[documentRow] ?? "", selectableDocumentRowCount, documentRows.length)
|
|
771
|
+
: { kind: "screen", row, source: selectionRows[row] ?? "" };
|
|
772
|
+
});
|
|
966
773
|
}
|
|
967
774
|
function governingPrompt(anchors, scrollTop) {
|
|
968
775
|
let result = null;
|
|
@@ -973,38 +780,6 @@ function governingPrompt(anchors, scrollTop) {
|
|
|
973
780
|
}
|
|
974
781
|
return result;
|
|
975
782
|
}
|
|
976
|
-
function padViewportRowWithGutter(line, contentWidth, frameWidth) {
|
|
977
|
-
const boundedContentWidth = Math.min(Math.max(1, contentWidth), frameWidth);
|
|
978
|
-
const content = padRowPreservingBackground(line, boundedContentWidth);
|
|
979
|
-
const gutterWidth = frameWidth - boundedContentWidth;
|
|
980
|
-
if (gutterWidth === 0)
|
|
981
|
-
return content;
|
|
982
|
-
// Invariant: render no source glyph beyond contentWidth, but reuse the established
|
|
983
|
-
// row-padding background before stripping source foreground, links, and emphasis.
|
|
984
|
-
const gutterStyle = explicitBackgroundPaddingStyle(line);
|
|
985
|
-
return `${content}${GUTTER_DECORATION_RESET}${gutterStyle?.on ?? ""}${" ".repeat(gutterWidth)}${gutterStyle?.off ?? ""}`;
|
|
986
|
-
}
|
|
987
|
-
function explicitBackgroundPaddingStyle(line) {
|
|
988
|
-
const background = /\u001b\[(?:4[0-8]|10[0-7]|48;(?:2;\d+;\d+;\d+|5;\d+))m/.exec(line)?.[0];
|
|
989
|
-
return background === undefined ? null : { on: background, off: "\u001b[49m" };
|
|
990
|
-
}
|
|
991
|
-
function rowPaddingStyle(line) {
|
|
992
|
-
const background = explicitBackgroundPaddingStyle(line);
|
|
993
|
-
if (background !== null)
|
|
994
|
-
return background;
|
|
995
|
-
const reverse = /\u001b\[(?:7(?:;[0-9]+)*|[0-9;]*;7(?:;[0-9]+)*)m/.exec(line)?.[0];
|
|
996
|
-
return reverse === undefined ? null : { on: reverse, off: "\u001b[27m" };
|
|
997
|
-
}
|
|
998
|
-
function padRowPreservingBackground(line, width) {
|
|
999
|
-
const shown = displayWidth(line) > width ? truncateToWidth(line, width) : line;
|
|
1000
|
-
const padding = Math.max(0, width - displayWidth(shown));
|
|
1001
|
-
if (padding === 0)
|
|
1002
|
-
return shown;
|
|
1003
|
-
const style = rowPaddingStyle(line);
|
|
1004
|
-
return style === null
|
|
1005
|
-
? shown + " ".repeat(padding)
|
|
1006
|
-
: `${shown}${style.on}${" ".repeat(padding)}${style.off}`;
|
|
1007
|
-
}
|
|
1008
783
|
export function assertTranscriptViewportFrameDescriptor(descriptor) {
|
|
1009
784
|
if (!Number.isSafeInteger(descriptor.frameId) || descriptor.frameId < 1)
|
|
1010
785
|
throw new TypeError("viewport frame descriptor identity is invalid");
|
|
@@ -1069,9 +844,6 @@ function assertDocumentRange(range, label) {
|
|
|
1069
844
|
throw new TypeError(`viewport frame descriptor ${label} document range is invalid`);
|
|
1070
845
|
}
|
|
1071
846
|
}
|
|
1072
|
-
function sameRectangle(left, right) {
|
|
1073
|
-
return left === null ? right === null : right !== null && left.rowStart === right.rowStart && left.rowEnd === right.rowEnd;
|
|
1074
|
-
}
|
|
1075
847
|
function copyableDockRowsBySuffix(dockRows, declarations) {
|
|
1076
848
|
const result = new Map();
|
|
1077
849
|
for (const declaration of declarations ?? []) {
|