@jrichman/ink 6.4.11 → 6.4.12
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/build/components/App.d.ts +6 -0
- package/build/components/App.js +5 -0
- package/build/components/App.js.map +1 -1
- package/build/components/AppContext.d.ts +36 -1
- package/build/components/AppContext.js +7 -2
- package/build/components/AppContext.js.map +1 -1
- package/build/components/Box.d.ts +26 -2
- package/build/components/Box.js +3 -2
- package/build/components/Box.js.map +1 -1
- package/build/components/StaticRender.d.ts +8 -0
- package/build/components/StaticRender.js +11 -0
- package/build/components/StaticRender.js.map +1 -0
- package/build/debug-log.d.ts +2 -0
- package/build/debug-log.js +44 -0
- package/build/debug-log.js.map +1 -0
- package/build/dom.d.ts +37 -3
- package/build/dom.js +19 -3
- package/build/dom.js.map +1 -1
- package/build/index.d.ts +4 -1
- package/build/index.js +3 -1
- package/build/index.js.map +1 -1
- package/build/ink.d.ts +29 -2
- package/build/ink.js +215 -102
- package/build/ink.js.map +1 -1
- package/build/measure-element.d.ts +20 -1
- package/build/measure-element.js +201 -51
- package/build/measure-element.js.map +1 -1
- package/build/output.d.ts +195 -10
- package/build/output.js +494 -160
- package/build/output.js.map +1 -1
- package/build/reconciler.js +19 -3
- package/build/reconciler.js.map +1 -1
- package/build/render-background.js +1 -1
- package/build/render-background.js.map +1 -1
- package/build/render-cached.d.ts +17 -0
- package/build/render-cached.js +62 -0
- package/build/render-cached.js.map +1 -0
- package/build/render-container.d.ts +24 -0
- package/build/render-container.js +169 -0
- package/build/render-container.js.map +1 -0
- package/build/render-node-to-output.d.ts +15 -7
- package/build/render-node-to-output.js +123 -485
- package/build/render-node-to-output.js.map +1 -1
- package/build/render-screen-reader.d.ts +5 -0
- package/build/render-screen-reader.js +54 -0
- package/build/render-screen-reader.js.map +1 -0
- package/build/render-scrollbar.d.ts +22 -0
- package/build/render-scrollbar.js +77 -0
- package/build/render-scrollbar.js.map +1 -0
- package/build/render-sticky.d.ts +56 -0
- package/build/render-sticky.js +314 -0
- package/build/render-sticky.js.map +1 -0
- package/build/render-text-node.d.ts +24 -0
- package/build/render-text-node.js +133 -0
- package/build/render-text-node.js.map +1 -0
- package/build/render.d.ts +39 -0
- package/build/render.js +5 -0
- package/build/render.js.map +1 -1
- package/build/renderer.d.ts +10 -2
- package/build/renderer.js +103 -7
- package/build/renderer.js.map +1 -1
- package/build/replay.d.ts +60 -0
- package/build/replay.js +138 -0
- package/build/replay.js.map +1 -0
- package/build/scroll.js +20 -1
- package/build/scroll.js.map +1 -1
- package/build/selection.d.ts +9 -0
- package/build/selection.js +47 -0
- package/build/selection.js.map +1 -1
- package/build/serialization.d.ts +28 -0
- package/build/serialization.js +267 -0
- package/build/serialization.js.map +1 -0
- package/build/styles.d.ts +18 -0
- package/build/styles.js.map +1 -1
- package/build/terminal-buffer.d.ts +53 -0
- package/build/terminal-buffer.js +441 -0
- package/build/terminal-buffer.js.map +1 -0
- package/build/worker/animation-controller.d.ts +72 -0
- package/build/worker/animation-controller.js +128 -0
- package/build/worker/animation-controller.js.map +1 -0
- package/build/worker/ansi-utils.d.ts +16 -0
- package/build/worker/ansi-utils.js +40 -0
- package/build/worker/ansi-utils.js.map +1 -0
- package/build/worker/canvas.d.ts +47 -0
- package/build/worker/canvas.js +94 -0
- package/build/worker/canvas.js.map +1 -0
- package/build/worker/compositor.d.ts +33 -0
- package/build/worker/compositor.js +314 -0
- package/build/worker/compositor.js.map +1 -0
- package/build/worker/platform.d.ts +15 -0
- package/build/worker/platform.js +19 -0
- package/build/worker/platform.js.map +1 -0
- package/build/worker/render-worker.d.ts +112 -0
- package/build/worker/render-worker.js +936 -0
- package/build/worker/render-worker.js.map +1 -0
- package/build/worker/scene-manager.d.ts +26 -0
- package/build/worker/scene-manager.js +99 -0
- package/build/worker/scene-manager.js.map +1 -0
- package/build/worker/scroll-optimizer.d.ts +32 -0
- package/build/worker/scroll-optimizer.js +110 -0
- package/build/worker/scroll-optimizer.js.map +1 -0
- package/build/worker/terminal-writer.d.ts +116 -0
- package/build/worker/terminal-writer.js +722 -0
- package/build/worker/terminal-writer.js.map +1 -0
- package/build/worker/worker-entry.d.ts +6 -0
- package/build/worker/worker-entry.js +130 -0
- package/build/worker/worker-entry.js.map +1 -0
- package/build/wrap-text.d.ts +6 -0
- package/build/wrap-text.js +120 -0
- package/build/wrap-text.js.map +1 -0
- package/package.json +3 -1
|
@@ -1,91 +1,88 @@
|
|
|
1
1
|
import Yoga from 'yoga-layout';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
2
|
+
import { setCachedRender, } from './dom.js';
|
|
3
|
+
import Output, { isRectIntersectingClip, extractSelectableText, } from './output.js';
|
|
4
|
+
import { handleTextNode } from './render-text-node.js';
|
|
5
|
+
import { renderStickyNode, getStickyDescendants } from './render-sticky.js';
|
|
6
|
+
import { handleContainerNode } from './render-container.js';
|
|
7
|
+
import { handleCachedRenderNode } from './render-cached.js';
|
|
8
|
+
import { getRelativeLeft, getRelativeTop } from './measure-element.js';
|
|
9
|
+
export const renderToStatic = (node, options = {}) => {
|
|
10
|
+
if (options.calculateLayout && node.yogaNode) {
|
|
11
|
+
node.yogaNode.calculateLayout(undefined, undefined, Yoga.DIRECTION_LTR);
|
|
12
|
+
}
|
|
13
|
+
const width = node.yogaNode?.getComputedWidth() ?? 0;
|
|
14
|
+
const height = node.yogaNode?.getComputedHeight() ?? 0;
|
|
15
|
+
const stickyNodes = getStickyDescendants(node);
|
|
16
|
+
const cachedStickyHeaders = [];
|
|
17
|
+
for (const { node: stickyNode } of stickyNodes) {
|
|
18
|
+
const { naturalLines, stuckLines, naturalHeight, maxHeaderHeight } = renderStickyNode(stickyNode, {
|
|
19
|
+
skipStaticElements: options.skipStaticElements ?? false,
|
|
20
|
+
selectionMap: options.selectionMap,
|
|
21
|
+
selectionStyle: options.selectionStyle,
|
|
22
|
+
});
|
|
23
|
+
const parent = stickyNode.parentNode;
|
|
24
|
+
const parentYogaNode = parent?.yogaNode;
|
|
25
|
+
const currentBorderTop = node.yogaNode?.getComputedBorder(Yoga.EDGE_TOP) ?? 0;
|
|
26
|
+
const naturalRow = getRelativeTop(stickyNode, node) ?? 0 - currentBorderTop;
|
|
27
|
+
const stickyType = stickyNode.internalSticky === 'bottom' ? 'bottom' : 'top';
|
|
28
|
+
const headerObj = {
|
|
29
|
+
nodeId: stickyNode.internalId,
|
|
30
|
+
lines: naturalLines,
|
|
31
|
+
stuckLines,
|
|
32
|
+
styledOutput: stuckLines ?? naturalLines,
|
|
33
|
+
x: getRelativeLeft(stickyNode, node) ??
|
|
34
|
+
0 - (node.yogaNode?.getComputedBorder(Yoga.EDGE_LEFT) ?? 0),
|
|
35
|
+
y: getRelativeTop(stickyNode, node) ?? 0 - currentBorderTop,
|
|
36
|
+
naturalRow,
|
|
37
|
+
startRow: naturalRow,
|
|
38
|
+
endRow: naturalRow + naturalHeight,
|
|
39
|
+
scrollContainerId: -1,
|
|
40
|
+
isStuckOnly: true,
|
|
41
|
+
relativeX: getRelativeLeft(stickyNode, node) ??
|
|
42
|
+
0 - (node.yogaNode?.getComputedBorder(Yoga.EDGE_LEFT) ?? 0),
|
|
43
|
+
relativeY: getRelativeTop(stickyNode, node) ?? 0 - currentBorderTop,
|
|
44
|
+
height: maxHeaderHeight,
|
|
45
|
+
type: stickyType,
|
|
46
|
+
parentRelativeTop: parent
|
|
47
|
+
? (getRelativeTop(parent, node) ?? 0 - currentBorderTop)
|
|
48
|
+
: 0,
|
|
49
|
+
parentHeight: parentYogaNode
|
|
50
|
+
? parentYogaNode.getComputedHeight()
|
|
51
|
+
: Number.MAX_SAFE_INTEGER,
|
|
52
|
+
parentBorderTop: parentYogaNode
|
|
53
|
+
? parentYogaNode.getComputedBorder(Yoga.EDGE_TOP)
|
|
54
|
+
: 0,
|
|
55
|
+
parentBorderBottom: parentYogaNode
|
|
56
|
+
? parentYogaNode.getComputedBorder(Yoga.EDGE_BOTTOM)
|
|
57
|
+
: 0,
|
|
58
|
+
node: stickyNode,
|
|
26
59
|
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const separator = node.style.flexDirection === 'row' ||
|
|
50
|
-
node.style.flexDirection === 'row-reverse'
|
|
51
|
-
? ' '
|
|
52
|
-
: '\n';
|
|
53
|
-
const childNodes = node.style.flexDirection === 'row-reverse' ||
|
|
54
|
-
node.style.flexDirection === 'column-reverse'
|
|
55
|
-
? [...node.childNodes].reverse()
|
|
56
|
-
: [...node.childNodes];
|
|
57
|
-
output = childNodes
|
|
58
|
-
.map(childNode => {
|
|
59
|
-
const screenReaderOutput = renderNodeToScreenReaderOutput(childNode, {
|
|
60
|
-
parentRole: node.internal_accessibility?.role,
|
|
61
|
-
skipStaticElements: options.skipStaticElements,
|
|
62
|
-
});
|
|
63
|
-
return screenReaderOutput;
|
|
64
|
-
})
|
|
65
|
-
.filter(Boolean)
|
|
66
|
-
.join(separator);
|
|
67
|
-
}
|
|
68
|
-
if (node.internal_accessibility) {
|
|
69
|
-
const { role, state } = node.internal_accessibility;
|
|
70
|
-
if (state) {
|
|
71
|
-
const stateKeys = Object.keys(state);
|
|
72
|
-
const stateDescription = stateKeys.filter(key => state[key]).join(', ');
|
|
73
|
-
if (stateDescription) {
|
|
74
|
-
output = `(${stateDescription}) ${output}`;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (role && role !== options.parentRole) {
|
|
78
|
-
output = `${role}: ${output}`;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return output;
|
|
60
|
+
cachedStickyHeaders.push(headerObj);
|
|
61
|
+
}
|
|
62
|
+
const staticOutput = new Output({
|
|
63
|
+
width,
|
|
64
|
+
height,
|
|
65
|
+
id: node.internalId,
|
|
66
|
+
});
|
|
67
|
+
for (const childNode of node.childNodes) {
|
|
68
|
+
renderNodeToOutput(childNode, staticOutput, {
|
|
69
|
+
offsetX: 0,
|
|
70
|
+
offsetY: 0,
|
|
71
|
+
transformers: undefined,
|
|
72
|
+
skipStaticElements: options.skipStaticElements ?? false,
|
|
73
|
+
isStickyRender: options.isStickyRender,
|
|
74
|
+
selectionMap: options.selectionMap,
|
|
75
|
+
selectionStyle: options.selectionStyle,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const rootRegion = staticOutput.get();
|
|
79
|
+
rootRegion.cachedStickyHeaders = cachedStickyHeaders;
|
|
80
|
+
rootRegion.selectableText = extractSelectableText(rootRegion.selectableSpans);
|
|
81
|
+
setCachedRender(node, rootRegion);
|
|
82
82
|
};
|
|
83
83
|
// After nodes are laid out, render each to output object, which later gets rendered to terminal
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const { offsetX = 0, offsetY = 0, transformers = [], skipStaticElements, isStickyRender = false, selectionMap, selectionStyle, } = options;
|
|
84
|
+
function renderNodeToOutput(node, output, options) {
|
|
85
|
+
const { offsetX = 0, offsetY = 0, absoluteOffsetX = 0, absoluteOffsetY = 0, transformers = [], skipStaticElements, isStickyRender = false, skipStickyHeaders = false, selectionMap, selectionStyle, } = options;
|
|
89
86
|
if (skipStaticElements && node.internal_static) {
|
|
90
87
|
return;
|
|
91
88
|
}
|
|
@@ -100,22 +97,23 @@ const renderNodeToOutput = (node, output, options) => {
|
|
|
100
97
|
// Left and top positions in Yoga are relative to their parent node
|
|
101
98
|
const x = offsetX + yogaNode.getComputedLeft();
|
|
102
99
|
const y = offsetY + yogaNode.getComputedTop();
|
|
100
|
+
// Absolute screen coordinates (for clipping/visibility check)
|
|
101
|
+
const absX = absoluteOffsetX + yogaNode.getComputedLeft();
|
|
102
|
+
const absY = absoluteOffsetY + yogaNode.getComputedTop();
|
|
103
103
|
const width = yogaNode.getComputedWidth();
|
|
104
104
|
const height = yogaNode.getComputedHeight();
|
|
105
105
|
const clip = output.getCurrentClip();
|
|
106
106
|
if (clip) {
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
nodeBottom > clipTop &&
|
|
118
|
-
nodeTop < clipBottom;
|
|
107
|
+
const absoluteNodeLeft = absX;
|
|
108
|
+
const absoluteNodeRight = absoluteNodeLeft + width;
|
|
109
|
+
const absoluteNodeTop = absY;
|
|
110
|
+
const absoluteNodeBottom = absoluteNodeTop + height;
|
|
111
|
+
const isVisible = isRectIntersectingClip({
|
|
112
|
+
x1: absoluteNodeLeft,
|
|
113
|
+
y1: absoluteNodeTop,
|
|
114
|
+
x2: absoluteNodeRight,
|
|
115
|
+
y2: absoluteNodeBottom,
|
|
116
|
+
}, clip);
|
|
119
117
|
if (!isVisible) {
|
|
120
118
|
return;
|
|
121
119
|
}
|
|
@@ -126,401 +124,41 @@ const renderNodeToOutput = (node, output, options) => {
|
|
|
126
124
|
if (typeof node.internal_transform === 'function') {
|
|
127
125
|
newTransformers = [node.internal_transform, ...transformers];
|
|
128
126
|
}
|
|
129
|
-
if (node.nodeName === 'ink-
|
|
130
|
-
const text = squashTextNodes(node);
|
|
131
|
-
let styledChars = toStyledCharacters(text);
|
|
132
|
-
let selectionState;
|
|
133
|
-
const selectionRange = selectionMap?.get(node);
|
|
134
|
-
if (selectionRange) {
|
|
135
|
-
selectionState = {
|
|
136
|
-
range: selectionRange,
|
|
137
|
-
currentOffset: 0,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
if (selectionState) {
|
|
141
|
-
styledChars = applySelectionToStyledChars(styledChars, selectionState, selectionStyle);
|
|
142
|
-
}
|
|
143
|
-
if (styledChars.length > 0 || node.internal_terminalCursorFocus) {
|
|
144
|
-
let lines = [];
|
|
145
|
-
let cursorLineIndex = 0;
|
|
146
|
-
let relativeCursorPosition = node.internal_terminalCursorPosition ?? 0;
|
|
147
|
-
if (styledChars.length > 0) {
|
|
148
|
-
const { width: currentWidth } = measureStyledChars(styledChars);
|
|
149
|
-
const maxWidth = getMaxWidth(yogaNode);
|
|
150
|
-
lines =
|
|
151
|
-
currentWidth > maxWidth
|
|
152
|
-
? wrapOrTruncateStyledChars(styledChars, maxWidth, node.style.textWrap ?? 'wrap')
|
|
153
|
-
: splitStyledCharsByNewline(styledChars);
|
|
154
|
-
lines = applyPaddingToStyledChars(node, lines);
|
|
155
|
-
// Calculate cursor line index for terminal cursor positioning
|
|
156
|
-
cursorLineIndex = lines.length - 1;
|
|
157
|
-
if (node.internal_terminalCursorFocus &&
|
|
158
|
-
node.internal_terminalCursorPosition !== undefined) {
|
|
159
|
-
({ cursorLineIndex, relativeCursorPosition } =
|
|
160
|
-
calculateWrappedCursorPosition(lines, styledChars, node.internal_terminalCursorPosition));
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
// Empty text with cursor focus - use single empty line for IME support
|
|
165
|
-
lines = [[]];
|
|
166
|
-
}
|
|
167
|
-
for (const [index, line] of lines.entries()) {
|
|
168
|
-
output.write(x, y + index, line, {
|
|
169
|
-
transformers: newTransformers,
|
|
170
|
-
lineIndex: index,
|
|
171
|
-
isTerminalCursorFocused: node.internal_terminalCursorFocus && index === cursorLineIndex,
|
|
172
|
-
terminalCursorPosition: relativeCursorPosition,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
127
|
+
if (node.nodeName === 'ink-static-render' && !node.cachedRender) {
|
|
176
128
|
return;
|
|
177
129
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
renderBackground(x, y, node, output);
|
|
187
|
-
renderBorder(x, y, node, output);
|
|
188
|
-
const overflow = node.style.overflow ?? 'visible';
|
|
189
|
-
const overflowX = node.style.overflowX ?? overflow;
|
|
190
|
-
const overflowY = node.style.overflowY ?? overflow;
|
|
191
|
-
verticallyScrollable = overflowY === 'scroll';
|
|
192
|
-
horizontallyScrollable = overflowX === 'scroll';
|
|
193
|
-
if (verticallyScrollable) {
|
|
194
|
-
childrenOffsetY -= node.internal_scrollState?.scrollTop ?? 0;
|
|
195
|
-
const stickyNodes = getStickyDescendants(node);
|
|
196
|
-
if (stickyNodes.length > 0) {
|
|
197
|
-
const scrollTop = (node.internal_scrollState?.scrollTop ?? 0) +
|
|
198
|
-
yogaNode.getComputedBorder(Yoga.EDGE_TOP);
|
|
199
|
-
let activeStickyNodeIndex = -1;
|
|
200
|
-
for (const [index, stickyNode] of stickyNodes.entries()) {
|
|
201
|
-
if (stickyNode.yogaNode) {
|
|
202
|
-
const stickyNodeTop = getRelativeTop(stickyNode, node);
|
|
203
|
-
if (stickyNodeTop < scrollTop) {
|
|
204
|
-
const parent = stickyNode.parentNode;
|
|
205
|
-
if (parent?.yogaNode) {
|
|
206
|
-
const parentTop = getRelativeTop(parent, node);
|
|
207
|
-
const parentHeight = parent.yogaNode.getComputedHeight();
|
|
208
|
-
if (parentTop + parentHeight > scrollTop) {
|
|
209
|
-
activeStickyNode = stickyNode;
|
|
210
|
-
activeStickyNodeIndex = index;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (activeStickyNodeIndex !== -1 &&
|
|
217
|
-
activeStickyNodeIndex + 1 < stickyNodes.length) {
|
|
218
|
-
nextStickyNode = stickyNodes[activeStickyNodeIndex + 1];
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (horizontallyScrollable) {
|
|
223
|
-
childrenOffsetX -= node.internal_scrollState?.scrollLeft ?? 0;
|
|
224
|
-
}
|
|
225
|
-
const clipHorizontally = overflowX === 'hidden' || overflowX === 'scroll';
|
|
226
|
-
const clipVertically = overflowY === 'hidden' || overflowY === 'scroll';
|
|
227
|
-
if (clipHorizontally || clipVertically) {
|
|
228
|
-
const x1 = clipHorizontally
|
|
229
|
-
? x + yogaNode.getComputedBorder(Yoga.EDGE_LEFT)
|
|
230
|
-
: undefined;
|
|
231
|
-
const x2 = clipHorizontally
|
|
232
|
-
? x +
|
|
233
|
-
yogaNode.getComputedWidth() -
|
|
234
|
-
yogaNode.getComputedBorder(Yoga.EDGE_RIGHT)
|
|
235
|
-
: undefined;
|
|
236
|
-
const y1 = clipVertically
|
|
237
|
-
? y + yogaNode.getComputedBorder(Yoga.EDGE_TOP)
|
|
238
|
-
: undefined;
|
|
239
|
-
const y2 = clipVertically
|
|
240
|
-
? y +
|
|
241
|
-
yogaNode.getComputedHeight() -
|
|
242
|
-
yogaNode.getComputedBorder(Yoga.EDGE_BOTTOM)
|
|
243
|
-
: undefined;
|
|
244
|
-
output.clip({ x1, x2, y1, y2 });
|
|
245
|
-
clipped = true;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (node.nodeName === 'ink-root' || node.nodeName === 'ink-box') {
|
|
249
|
-
for (const childNode of node.childNodes) {
|
|
250
|
-
renderNodeToOutput(childNode, output, {
|
|
251
|
-
offsetX: childrenOffsetX,
|
|
252
|
-
offsetY: childrenOffsetY,
|
|
253
|
-
transformers: newTransformers,
|
|
254
|
-
skipStaticElements,
|
|
255
|
-
nodeToSkip: activeStickyNode,
|
|
256
|
-
isStickyRender,
|
|
257
|
-
selectionMap,
|
|
258
|
-
selectionStyle,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
if (activeStickyNode?.yogaNode) {
|
|
262
|
-
const alternateStickyNode = activeStickyNode.childNodes.find(childNode => childNode.internalStickyAlternate);
|
|
263
|
-
const nodeToRender = alternateStickyNode ?? activeStickyNode;
|
|
264
|
-
const nodeToRenderYogaNode = nodeToRender.yogaNode;
|
|
265
|
-
if (!nodeToRenderYogaNode) {
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
const stickyYogaNode = activeStickyNode.yogaNode;
|
|
269
|
-
const borderTop = yogaNode.getComputedBorder(Yoga.EDGE_TOP);
|
|
270
|
-
const scrollTop = node.internal_scrollState?.scrollTop ?? 0;
|
|
271
|
-
const parent = activeStickyNode.parentNode;
|
|
272
|
-
const parentYogaNode = parent.yogaNode;
|
|
273
|
-
const parentTop = getRelativeTop(parent, node);
|
|
274
|
-
const parentHeight = parentYogaNode.getComputedHeight();
|
|
275
|
-
const parentBottom = parentTop + parentHeight;
|
|
276
|
-
const stickyNodeHeight = nodeToRenderYogaNode.getComputedHeight();
|
|
277
|
-
const maxStickyTop = y - scrollTop + parentBottom - stickyNodeHeight;
|
|
278
|
-
const naturalStickyY = y - scrollTop + getRelativeTop(activeStickyNode, node);
|
|
279
|
-
const stuckStickyY = y + borderTop;
|
|
280
|
-
let finalStickyY = Math.min(Math.max(stuckStickyY, naturalStickyY), maxStickyTop);
|
|
281
|
-
if (nextStickyNode?.yogaNode) {
|
|
282
|
-
const nextStickyNodeTop = getRelativeTop(nextStickyNode, node);
|
|
283
|
-
const nextStickyNodeTopInViewport = y - scrollTop + nextStickyNodeTop;
|
|
284
|
-
if (nextStickyNodeTopInViewport < finalStickyY + stickyNodeHeight) {
|
|
285
|
-
finalStickyY = nextStickyNodeTopInViewport - stickyNodeHeight;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
let offsetX;
|
|
289
|
-
let offsetY;
|
|
290
|
-
if (nodeToRender === alternateStickyNode) {
|
|
291
|
-
const parentAbsoluteX = x + getRelativeLeft(parent, node);
|
|
292
|
-
const stickyNodeAbsoluteX = parentAbsoluteX + stickyYogaNode.getComputedLeft();
|
|
293
|
-
offsetX = stickyNodeAbsoluteX;
|
|
294
|
-
offsetY = finalStickyY;
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
const parentAbsoluteX = x + getRelativeLeft(parent, node);
|
|
298
|
-
offsetX = parentAbsoluteX;
|
|
299
|
-
offsetY = finalStickyY - stickyYogaNode.getComputedTop();
|
|
300
|
-
}
|
|
301
|
-
renderNodeToOutput(nodeToRender, output, {
|
|
302
|
-
offsetX,
|
|
303
|
-
offsetY,
|
|
304
|
-
transformers: newTransformers,
|
|
305
|
-
skipStaticElements,
|
|
306
|
-
isStickyRender: true,
|
|
307
|
-
selectionMap,
|
|
308
|
-
selectionStyle,
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
if (clipped) {
|
|
312
|
-
output.unclip();
|
|
313
|
-
}
|
|
314
|
-
if (node.nodeName === 'ink-box') {
|
|
315
|
-
if (verticallyScrollable) {
|
|
316
|
-
renderVerticalScrollbar(node, x, y, output);
|
|
317
|
-
}
|
|
318
|
-
if (horizontallyScrollable) {
|
|
319
|
-
renderHorizontalScrollbar(node, x, y, output);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
const calculateWrappedCursorPosition = (lines, styledChars, targetOffset) => {
|
|
326
|
-
const styledCharToOffset = new Map();
|
|
327
|
-
let offset = 0;
|
|
328
|
-
for (const char of styledChars) {
|
|
329
|
-
styledCharToOffset.set(char, offset);
|
|
330
|
-
offset += char.value.length;
|
|
331
|
-
}
|
|
332
|
-
let cursorLineIndex = lines.length - 1;
|
|
333
|
-
let relativeCursorPosition = targetOffset;
|
|
334
|
-
// -1 represents "before document start" so first character (offset 0) is handled correctly
|
|
335
|
-
let previousLineEndOffset = -1;
|
|
336
|
-
for (const [i, line] of lines.entries()) {
|
|
337
|
-
if (line.length > 0) {
|
|
338
|
-
const firstChar = line.find(char => styledCharToOffset.has(char));
|
|
339
|
-
const lastChar = line.findLast(char => styledCharToOffset.has(char));
|
|
340
|
-
if (!firstChar || !lastChar) {
|
|
341
|
-
// Padding-only line (originally empty), treat as empty line
|
|
342
|
-
if (targetOffset > previousLineEndOffset) {
|
|
343
|
-
cursorLineIndex = i;
|
|
344
|
-
relativeCursorPosition = targetOffset - previousLineEndOffset - 1;
|
|
345
|
-
previousLineEndOffset++;
|
|
346
|
-
}
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
const lineStartOffset = styledCharToOffset.get(firstChar);
|
|
350
|
-
const lineEndOffset = styledCharToOffset.get(lastChar) + lastChar.value.length;
|
|
351
|
-
// Set as candidate if targetOffset is at or after line start
|
|
352
|
-
if (targetOffset >= lineStartOffset) {
|
|
353
|
-
cursorLineIndex = i;
|
|
354
|
-
relativeCursorPosition = Math.max(0, targetOffset - lineStartOffset);
|
|
355
|
-
}
|
|
356
|
-
// Finalize and exit if targetOffset is within or before this line's range.
|
|
357
|
-
// If targetOffset is in a gap (between previousLineEndOffset and lineStartOffset),
|
|
358
|
-
// the cursor stays at the previous line's end (already set in previous iteration).
|
|
359
|
-
if (targetOffset <= lineEndOffset) {
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
previousLineEndOffset = lineEndOffset;
|
|
363
|
-
}
|
|
364
|
-
else if (i === 0 && targetOffset === 0) {
|
|
365
|
-
// Edge case: First line is empty and cursor is at position 0
|
|
366
|
-
cursorLineIndex = 0;
|
|
367
|
-
relativeCursorPosition = 0;
|
|
368
|
-
break;
|
|
369
|
-
}
|
|
370
|
-
else if (i > 0 && targetOffset > previousLineEndOffset) {
|
|
371
|
-
// Handle empty lines (usually caused by \n)
|
|
372
|
-
cursorLineIndex = i;
|
|
373
|
-
relativeCursorPosition = targetOffset - previousLineEndOffset - 1;
|
|
374
|
-
// Advance past the \n character
|
|
375
|
-
previousLineEndOffset++;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
return { cursorLineIndex, relativeCursorPosition };
|
|
379
|
-
};
|
|
380
|
-
function getStickyDescendants(node) {
|
|
381
|
-
const stickyDescendants = [];
|
|
382
|
-
for (const child of node.childNodes) {
|
|
383
|
-
if (child.nodeName === '#text') {
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
const domChild = child;
|
|
387
|
-
if (domChild.internalStickyAlternate) {
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
if (domChild.internalSticky) {
|
|
391
|
-
stickyDescendants.push(domChild);
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
const overflow = domChild.style.overflow ?? 'visible';
|
|
395
|
-
const overflowX = domChild.style.overflowX ?? overflow;
|
|
396
|
-
const overflowY = domChild.style.overflowY ?? overflow;
|
|
397
|
-
const isScrollable = overflowX === 'scroll' || overflowY === 'scroll';
|
|
398
|
-
if (!isScrollable && domChild.childNodes) {
|
|
399
|
-
stickyDescendants.push(...getStickyDescendants(domChild));
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
return stickyDescendants;
|
|
404
|
-
}
|
|
405
|
-
function getRelativeTop(node, ancestor) {
|
|
406
|
-
if (!node.yogaNode) {
|
|
407
|
-
return 0;
|
|
408
|
-
}
|
|
409
|
-
let top = node.yogaNode.getComputedTop();
|
|
410
|
-
let parent = node.parentNode;
|
|
411
|
-
while (parent && parent !== ancestor) {
|
|
412
|
-
if (parent.yogaNode) {
|
|
413
|
-
top += parent.yogaNode.getComputedTop();
|
|
414
|
-
if (parent.nodeName === 'ink-box') {
|
|
415
|
-
const overflow = parent.style.overflow ?? 'visible';
|
|
416
|
-
const overflowY = parent.style.overflowY ?? overflow;
|
|
417
|
-
if (overflowY === 'scroll') {
|
|
418
|
-
top -= parent.internal_scrollState?.scrollTop ?? 0;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
parent = parent.parentNode;
|
|
423
|
-
}
|
|
424
|
-
return top;
|
|
425
|
-
}
|
|
426
|
-
function getRelativeLeft(node, ancestor) {
|
|
427
|
-
if (!node.yogaNode) {
|
|
428
|
-
return 0;
|
|
429
|
-
}
|
|
430
|
-
let left = node.yogaNode.getComputedLeft();
|
|
431
|
-
let parent = node.parentNode;
|
|
432
|
-
while (parent && parent !== ancestor) {
|
|
433
|
-
if (parent.yogaNode) {
|
|
434
|
-
left += parent.yogaNode.getComputedLeft();
|
|
435
|
-
if (parent.nodeName === 'ink-box') {
|
|
436
|
-
const overflow = parent.style.overflow ?? 'visible';
|
|
437
|
-
const overflowX = parent.style.overflowX ?? overflow;
|
|
438
|
-
if (overflowX === 'scroll') {
|
|
439
|
-
left -= parent.internal_scrollState?.scrollLeft ?? 0;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
130
|
+
if (node.cachedRender) {
|
|
131
|
+
handleCachedRenderNode(node, output, {
|
|
132
|
+
x,
|
|
133
|
+
y,
|
|
134
|
+
selectionMap,
|
|
135
|
+
selectionStyle,
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
442
138
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
for (let index = thumb.start; index < thumb.end; index++) {
|
|
451
|
-
const cellStartHalf = index * 2;
|
|
452
|
-
const cellEndHalf = (index + 1) * 2;
|
|
453
|
-
const start = Math.max(cellStartHalf, thumb.startHalf);
|
|
454
|
-
const end = Math.min(cellEndHalf, thumb.endHalf);
|
|
455
|
-
const fill = end - start;
|
|
456
|
-
if (fill > 0) {
|
|
457
|
-
const char = axis === 'vertical'
|
|
458
|
-
? fill === 2
|
|
459
|
-
? '█'
|
|
460
|
-
: // Fill === 1
|
|
461
|
-
start % 2 === 0
|
|
462
|
-
? '▀' // Top half of the cell is filled
|
|
463
|
-
: '▄' // Bottom half of the cell is filled
|
|
464
|
-
: fill === 2
|
|
465
|
-
? '█'
|
|
466
|
-
: // Fill === 1
|
|
467
|
-
start % 2 === 0
|
|
468
|
-
? '▌' // Left half of the cell is filled
|
|
469
|
-
: '▐'; // Right half of the cell is filled
|
|
470
|
-
const outputX = axis === 'vertical' ? layout.x : layout.x + index;
|
|
471
|
-
const outputY = axis === 'vertical' ? layout.y + index : layout.y;
|
|
472
|
-
output.write(outputX, outputY, colorize(char, thumbColor, 'foreground'), {
|
|
473
|
-
transformers: [],
|
|
474
|
-
preserveBackgroundColor: true,
|
|
139
|
+
if (node.nodeName === 'ink-text') {
|
|
140
|
+
handleTextNode(node, output, {
|
|
141
|
+
x,
|
|
142
|
+
y,
|
|
143
|
+
newTransformers,
|
|
144
|
+
selectionMap,
|
|
145
|
+
selectionStyle,
|
|
475
146
|
});
|
|
147
|
+
return;
|
|
476
148
|
}
|
|
149
|
+
handleContainerNode(node, output, {
|
|
150
|
+
x,
|
|
151
|
+
y,
|
|
152
|
+
width,
|
|
153
|
+
height,
|
|
154
|
+
newTransformers,
|
|
155
|
+
skipStaticElements,
|
|
156
|
+
isStickyRender,
|
|
157
|
+
skipStickyHeaders,
|
|
158
|
+
selectionMap,
|
|
159
|
+
selectionStyle,
|
|
160
|
+
});
|
|
477
161
|
}
|
|
478
162
|
}
|
|
479
|
-
function renderVerticalScrollbar(node, x, y, output) {
|
|
480
|
-
const layout = getVerticalScrollbarBoundingBox(node, { x, y });
|
|
481
|
-
if (layout) {
|
|
482
|
-
renderScrollbar(node, output, layout, 'vertical');
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
function renderHorizontalScrollbar(node, x, y, output) {
|
|
486
|
-
const layout = getHorizontalScrollbarBoundingBox(node, { x, y });
|
|
487
|
-
if (layout) {
|
|
488
|
-
renderScrollbar(node, output, layout, 'horizontal');
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
const applySelectionToStyledChars = (styledChars, selectionState, selectionStyle) => {
|
|
492
|
-
const { range, currentOffset } = selectionState;
|
|
493
|
-
const { start, end } = range;
|
|
494
|
-
let charCodeUnitOffset = 0;
|
|
495
|
-
const newStyledChars = [];
|
|
496
|
-
for (const char of styledChars) {
|
|
497
|
-
const charLength = char.value.length;
|
|
498
|
-
const globalOffset = currentOffset + charCodeUnitOffset;
|
|
499
|
-
if (globalOffset >= start && globalOffset < end) {
|
|
500
|
-
if (selectionStyle) {
|
|
501
|
-
newStyledChars.push(selectionStyle(char));
|
|
502
|
-
}
|
|
503
|
-
else {
|
|
504
|
-
// 7 is the ANSI code for inverse (reverse video)
|
|
505
|
-
const newChar = {
|
|
506
|
-
...char,
|
|
507
|
-
styles: [...char.styles],
|
|
508
|
-
};
|
|
509
|
-
newChar.styles.push({
|
|
510
|
-
type: 'ansi',
|
|
511
|
-
code: '\u001B[7m',
|
|
512
|
-
endCode: '\u001B[27m',
|
|
513
|
-
});
|
|
514
|
-
newStyledChars.push(newChar);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
newStyledChars.push(char);
|
|
519
|
-
}
|
|
520
|
-
charCodeUnitOffset += charLength;
|
|
521
|
-
}
|
|
522
|
-
selectionState.currentOffset += charCodeUnitOffset;
|
|
523
|
-
return newStyledChars;
|
|
524
|
-
};
|
|
525
163
|
export default renderNodeToOutput;
|
|
526
164
|
//# sourceMappingURL=render-node-to-output.js.map
|