@simon_he/vue-tui 0.0.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -30
- package/dist/TSelect-BwJJbuTt.js +6089 -0
- package/dist/TTree-DK_DQBYm.js +6480 -0
- package/dist/agent.cjs +39416 -0
- package/dist/agent.d.cts +44 -0
- package/dist/agent.d.ts +44 -0
- package/dist/agent.js +828 -0
- package/dist/ansi-palette-CNXqFciC.js +57 -0
- package/dist/ansi-styles.d.ts +200 -0
- package/dist/block-source-CKUOnkgT.js +1663 -0
- package/dist/cli/headless-renderer.d.ts +29 -0
- package/dist/cli/input.d.ts +49 -0
- package/dist/cli/node-file-writers.d.ts +13 -0
- package/dist/cli/node-module.d.ts +2 -0
- package/dist/cli/parse-kitty.d.ts +2 -0
- package/dist/cli/parse-mouse.d.ts +2 -0
- package/dist/cli/parse-utils.d.ts +14 -0
- package/dist/cli/parse-xterm.d.ts +2 -0
- package/dist/cli/path-provider-types.d.ts +1 -0
- package/dist/cli/path-provider.d.ts +4 -0
- package/dist/cli/path-suggest-core.d.ts +1 -0
- package/dist/cli/path-suggest.d.ts +15 -0
- package/dist/cli/recording.d.ts +4 -0
- package/dist/cli/stdin-buffer.d.ts +33 -0
- package/dist/cli.cjs +11061 -0
- package/dist/cli.d.cts +23 -0
- package/dist/cli.d.ts +23 -0
- package/dist/cli.js +9875 -0
- package/dist/core/ansi/capability.d.ts +20 -0
- package/dist/core/ansi/colors.d.ts +41 -0
- package/dist/core/ansi/sgr.d.ts +7 -0
- package/dist/core/ansi/theme.d.ts +6 -0
- package/dist/core/ansi-palette.d.ts +14 -0
- package/dist/core/buffer/buffer.d.ts +54 -0
- package/dist/core/buffer/width.d.ts +4 -0
- package/dist/core/debug-logger.d.ts +18 -0
- package/dist/core/hyperlink.d.ts +9 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/path-provider-types.d.ts +24 -0
- package/dist/core/path-suggest.d.ts +35 -0
- package/dist/core/render-plane.d.ts +3 -0
- package/dist/core/terminal/create-terminal.d.ts +8 -0
- package/dist/core/terminal/emitter.d.ts +7 -0
- package/dist/core/types.d.ts +107 -0
- package/dist/core.cjs +3153 -0
- package/dist/core.d.cts +14 -0
- package/dist/core.d.ts +14 -0
- package/dist/core.js +424 -0
- package/dist/create-terminal-C2oiWFJU.js +1434 -0
- package/dist/create-terminal-app.d.ts +36 -0
- package/dist/dom-renderer-BqGijQID.js +1173 -0
- package/dist/env-Cl8qmNFN.js +19 -0
- package/dist/events/index.d.ts +6 -0
- package/dist/events/manager/cli-event-manager.d.ts +22 -0
- package/dist/events/manager/event-manager.d.ts +30 -0
- package/dist/events/manager/selection-suppression.d.ts +3 -0
- package/dist/events/manager/types.d.ts +108 -0
- package/dist/events/recording.d.ts +44 -0
- package/dist/experimental.cjs +4412 -1641
- package/dist/experimental.d.cts +42 -0
- package/dist/experimental.d.ts +42 -825
- package/dist/experimental.js +31 -7392
- package/dist/frame-perf-C6Xfg85O.js +24 -0
- package/dist/hyperlink-Bn02UXJb.js +419 -0
- package/dist/index.cjs +14765 -17582
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +35 -1528
- package/dist/index.js +17 -20026
- package/dist/linkify-D_gzn9Xq.js +562 -0
- package/dist/markdown.cjs +9321 -7623
- package/dist/markdown.d.cts +11 -0
- package/dist/markdown.d.ts +11 -112
- package/dist/markdown.js +36 -1427
- package/dist/observability/cli-latency-node.d.ts +3 -0
- package/dist/observability/cli-latency-types.d.ts +41 -0
- package/dist/observability/cli-latency.d.ts +3 -0
- package/dist/observability/frame-perf-store.d.ts +18 -0
- package/dist/observability/frame-perf.d.ts +40 -0
- package/dist/observability/perf-sink.d.ts +56 -0
- package/dist/observability/trace.d.ts +40 -0
- package/dist/observability/tui-profiler.d.ts +33 -0
- package/dist/observability.cjs +221 -0
- package/dist/observability.d.cts +8 -0
- package/dist/observability.d.ts +8 -0
- package/dist/observability.js +4 -0
- package/dist/path-mnSgEpCm.js +81 -0
- package/dist/path-suggest-BNqcuE3-.js +274 -0
- package/dist/path-suggest-Dk9TRLh5.js +3 -0
- package/dist/{path-suggest-DmrmeueH.js → path-suggest-bvR9SY6Q.js} +86 -18
- package/dist/renderer/capabilities.d.ts +13 -0
- package/dist/renderer/cli/index.d.ts +2 -0
- package/dist/renderer/cli/stdout-metrics.d.ts +18 -0
- package/dist/renderer/cli/stdout-renderer.d.ts +48 -0
- package/dist/renderer/dom/dom-renderer.d.ts +157 -0
- package/dist/renderer/dom/index.d.ts +2 -0
- package/dist/renderer/index.d.ts +2 -0
- package/dist/renderer-dom.cjs +1505 -0
- package/dist/renderer-dom.d.cts +4 -0
- package/dist/renderer-dom.d.ts +4 -0
- package/dist/renderer-dom.js +7 -0
- package/dist/runtime/index.d.ts +36 -0
- package/dist/runtime/osc52.d.ts +9 -0
- package/dist/runtime-rfcZCyJq.js +1197 -0
- package/dist/runtime.cjs +1698 -0
- package/dist/runtime.d.cts +9 -0
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.js +4 -0
- package/dist/selection/terminal-selection.d.ts +123 -0
- package/dist/sgr-BmzxEjAO.js +304 -0
- package/dist/terminal-selection-DBinO6p5.js +330 -0
- package/dist/tlog-theme-qPBt5rge.js +9254 -0
- package/dist/trace-CjzYeIxN.js +182 -0
- package/dist/utils/env.d.ts +3 -0
- package/dist/utils/grapheme.d.ts +5 -0
- package/dist/utils/newlines.d.ts +1 -0
- package/dist/utils/path.d.ts +7 -0
- package/dist/utils/url-safety.d.ts +2 -0
- package/dist/vue/agent/view-models.d.ts +98 -0
- package/dist/vue/components/TAnchor.d.ts +108 -0
- package/dist/vue/components/TBox.d.ts +144 -0
- package/dist/vue/components/TCommandPalette.d.ts +587 -0
- package/dist/vue/components/TDataTable.d.ts +367 -0
- package/dist/vue/components/TDebugOverlay.d.ts +43 -0
- package/dist/vue/components/TDialog.d.ts +255 -0
- package/dist/vue/components/TFeedback.d.ts +635 -0
- package/dist/vue/components/TFlow.d.ts +85 -0
- package/dist/vue/components/TForm.d.ts +1230 -0
- package/dist/vue/components/TInput.d.ts +545 -0
- package/dist/vue/components/TInputBox.d.ts +158 -0
- package/dist/vue/components/TJsonEditor.d.ts +165 -0
- package/dist/vue/components/TLink.d.ts +239 -0
- package/dist/vue/components/TLinkifyText.d.ts +141 -0
- package/dist/vue/components/TList.d.ts +119 -0
- package/dist/vue/components/TLogLinksPanel.d.ts +238 -0
- package/dist/vue/components/TLogMinimap.d.ts +263 -0
- package/dist/vue/components/TLogScrollbar.d.ts +236 -0
- package/dist/vue/components/TLogSearchBar.d.ts +267 -0
- package/dist/vue/components/TLogSearchPager.d.ts +162 -0
- package/dist/vue/components/TLogSearchResults.d.ts +200 -0
- package/dist/vue/components/TLogView.d.ts +536 -0
- package/dist/vue/components/TLogVirtualLinksPanel.d.ts +141 -0
- package/dist/vue/components/TLogVirtualSearchResults.d.ts +146 -0
- package/dist/vue/components/TMarkdownText.d.ts +134 -0
- package/dist/vue/components/TMultilineModal.d.ts +62 -0
- package/dist/vue/components/TNavigation.d.ts +324 -0
- package/dist/vue/components/TOverlay.d.ts +366 -0
- package/dist/vue/components/TPanels.d.ts +244 -0
- package/dist/vue/components/TPathPicker.d.ts +213 -0
- package/dist/vue/components/TRenderLayer.d.ts +16 -0
- package/dist/vue/components/TRenderPlane.d.ts +17 -0
- package/dist/vue/components/TSelect.d.ts +335 -0
- package/dist/vue/components/TTable.d.ts +317 -0
- package/dist/vue/components/TText.d.ts +116 -0
- package/dist/vue/components/TThinkingView.d.ts +233 -0
- package/dist/vue/components/TToolCallView.d.ts +384 -0
- package/dist/vue/components/TTranscriptView.d.ts +221 -0
- package/dist/vue/components/TTransition.d.ts +83 -0
- package/dist/vue/components/TTree.d.ts +200 -0
- package/dist/vue/components/TUserMessageView.d.ts +344 -0
- package/dist/vue/components/TView.d.ts +127 -0
- package/dist/vue/components/TVirtualList.d.ts +182 -0
- package/dist/vue/components/TVirtualMarkdown.d.ts +168 -0
- package/dist/vue/components/TVirtualRows.d.ts +247 -0
- package/dist/vue/components/TerminalProvider.d.ts +218 -0
- package/dist/vue/components/input/host.d.ts +17 -0
- package/dist/vue/components/input/plugins/hostPlugin.d.ts +7 -0
- package/dist/vue/components/input/plugins/hostPlugin.node.d.ts +15 -0
- package/dist/vue/components/input/plugins/mentionUtils.d.ts +17 -0
- package/dist/vue/components/input/plugins/nodeMentionPathProvider.d.ts +2 -0
- package/dist/vue/components/input/plugins/promptMentionPlugin.d.ts +8 -0
- package/dist/vue/components/input/plugins/promptMentionState.d.ts +98 -0
- package/dist/vue/components/input/plugins/restrictText.d.ts +27 -0
- package/dist/vue/components/input/plugins/types.d.ts +90 -0
- package/dist/vue/components/input/utils/inlineText.d.ts +57 -0
- package/dist/vue/components/input/utils/inlineTextTokens.d.ts +34 -0
- package/dist/vue/components/input/utils/primitives.d.ts +3 -0
- package/dist/vue/components/input/utils/wordNavigation.d.ts +6 -0
- package/dist/vue/components/link/host.d.ts +15 -0
- package/dist/vue/components/list/list-geometry.d.ts +16 -0
- package/dist/vue/components/list/list-paint.d.ts +1 -0
- package/dist/vue/components/list/scroll-state.d.ts +25 -0
- package/dist/vue/components/list/selection-state.d.ts +6 -0
- package/dist/vue/components/simple-utils.d.ts +5 -0
- package/dist/vue/components/terminal-provider/copy-toast.d.ts +6 -0
- package/dist/vue/components/terminal-provider/portals.d.ts +17 -0
- package/dist/vue/components/terminal-provider/scheduler.d.ts +29 -0
- package/dist/vue/components/terminal-provider/selection-config.d.ts +15 -0
- package/dist/vue/components/terminal-provider/utils.d.ts +5 -0
- package/dist/vue/composables/use-layout.d.ts +2 -0
- package/dist/vue/composables/use-render-node.d.ts +19 -0
- package/dist/vue/composables/use-render-stack.d.ts +3 -0
- package/dist/vue/composables/use-runtime.d.ts +5 -0
- package/dist/vue/composables/use-terminal-node.d.ts +14 -0
- package/dist/vue/composables/use-terminal.d.ts +2 -0
- package/dist/vue/composables/use-visibility.d.ts +12 -0
- package/dist/vue/context.d.ts +153 -0
- package/dist/vue/index.d.ts +69 -0
- package/dist/vue/injection-key.d.ts +2 -0
- package/dist/vue/linkify.d.ts +11 -0
- package/dist/vue/log/append-only-log-store.d.ts +2 -0
- package/dist/vue/log/tlog-keymap.d.ts +28 -0
- package/dist/vue/log/tlog-plugins.d.ts +144 -0
- package/dist/vue/log/tlog-session.d.ts +81 -0
- package/dist/vue/log/tlog-theme.d.ts +68 -0
- package/dist/vue/log/types.d.ts +43 -0
- package/dist/vue/log/use-tlog-link-controller.d.ts +31 -0
- package/dist/vue/log/use-tlog-retained-index.d.ts +26 -0
- package/dist/vue/log/use-tlog-search-controller.d.ts +51 -0
- package/dist/vue/log/use-tlog-search-results-page.d.ts +31 -0
- package/dist/vue/log/use-tlog-virtual-search-results.d.ts +22 -0
- package/dist/vue/markdown/ast.d.ts +3 -0
- package/dist/vue/markdown/block-source.d.ts +22 -0
- package/dist/vue/markdown/document.d.ts +20 -0
- package/dist/vue/markdown/layout.d.ts +21 -0
- package/dist/vue/markdown/parser.d.ts +11 -0
- package/dist/vue/markdown/render.d.ts +10 -0
- package/dist/vue/markdown/theme.d.ts +186 -0
- package/dist/vue/markdown/types.d.ts +59 -0
- package/dist/vue/overlay.d.ts +27 -0
- package/dist/vue/render/context.d.ts +3 -0
- package/dist/vue/render/render-manager.d.ts +94 -0
- package/dist/vue/router/RouterView.d.ts +25 -0
- package/dist/vue/router/composables.d.ts +4 -0
- package/dist/vue/router/context.d.ts +4 -0
- package/dist/vue/router/router.d.ts +7 -0
- package/dist/vue/router/types.d.ts +29 -0
- package/dist/vue/scheduler/frame-mailbox.d.ts +55 -0
- package/dist/vue/scheduler/frame-scheduler.d.ts +51 -0
- package/dist/vue/theme.d.ts +48 -0
- package/dist/vue/transcript/hit-regions.d.ts +2 -0
- package/dist/vue/transcript/layout.d.ts +20 -0
- package/dist/vue/transcript/plain-text.d.ts +2 -0
- package/dist/vue/transcript/types.d.ts +122 -0
- package/dist/vue/utils/frame-task.d.ts +6 -0
- package/dist/vue/utils/rect.d.ts +4 -0
- package/dist/vue/utils/row-scroll.d.ts +25 -0
- package/dist/vue/utils/style-cache.d.ts +3 -0
- package/dist/vue/utils/text.d.ts +27 -0
- package/dist/vue/utils/wheel-scroll.d.ts +18 -0
- package/dist/vue.cjs +23668 -0
- package/dist/vue.d.cts +1 -0
- package/dist/vue.d.ts +1 -0
- package/dist/vue.js +3957 -0
- package/dist/{wheel-scroll-CiNsasLf.js → wheel-scroll-vBRaQ7p-.js} +404 -405
- package/package.json +185 -35
- package/dist/path-suggest-BeV6n7m-.js +0 -3
- package/dist/sgr-Tko2EQ0U.js +0 -484
- package/dist/theme-Bsm65g8k.d.ts +0 -32
- package/dist/types-ClIV2h1o.d.ts +0 -103
|
@@ -0,0 +1,1173 @@
|
|
|
1
|
+
import { TERMINAL_RENDER_PLANES, getPlaneTerminal } from "./create-terminal-C2oiWFJU.js";
|
|
2
|
+
import { sanitizeDomHref } from "./hyperlink-Bn02UXJb.js";
|
|
3
|
+
import { ansiColorHex, ansiCssVar, installAnsiPaletteCssVars, isAnsiColorName } from "./ansi-palette-CNXqFciC.js";
|
|
4
|
+
|
|
5
|
+
//#region src/renderer/capabilities.ts
|
|
6
|
+
const DOM_RENDERER_CAPABILITIES = Object.freeze({
|
|
7
|
+
syncFlush: true,
|
|
8
|
+
scrollOperations: true,
|
|
9
|
+
domRows: true
|
|
10
|
+
});
|
|
11
|
+
const HEADLESS_RENDERER_CAPABILITIES = Object.freeze({
|
|
12
|
+
syncFlush: false,
|
|
13
|
+
scrollOperations: true,
|
|
14
|
+
domRows: false
|
|
15
|
+
});
|
|
16
|
+
const STDOUT_RENDERER_CAPABILITIES = Object.freeze({
|
|
17
|
+
syncFlush: true,
|
|
18
|
+
scrollOperations: true,
|
|
19
|
+
domRows: false
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/renderer/dom/dom-renderer.ts
|
|
24
|
+
const DEFAULT_FONT_FAMILY = [
|
|
25
|
+
"ui-monospace",
|
|
26
|
+
"SFMono-Regular",
|
|
27
|
+
"Menlo",
|
|
28
|
+
"Monaco",
|
|
29
|
+
"Consolas",
|
|
30
|
+
"\"Sarasa Mono SC\"",
|
|
31
|
+
"\"Noto Sans Mono CJK SC\"",
|
|
32
|
+
"\"Source Han Mono SC\"",
|
|
33
|
+
"\"Liberation Mono\"",
|
|
34
|
+
"\"Courier New\"",
|
|
35
|
+
"monospace"
|
|
36
|
+
].join(", ");
|
|
37
|
+
const DEFAULT_SYNC_FLUSH_MAX_ROWS = 32;
|
|
38
|
+
const DEFAULT_SYNC_FLUSH_CELL_BUDGET = 4096;
|
|
39
|
+
const PREPASS_SAMPLE_ROWS = 512;
|
|
40
|
+
const PREPASS_ENABLE_HIT_RATIO = .5;
|
|
41
|
+
const PREPASS_DISABLE_HIT_RATIO = .25;
|
|
42
|
+
const ACCESSIBILITY_ATTRS = [
|
|
43
|
+
"role",
|
|
44
|
+
"aria-label",
|
|
45
|
+
"aria-labelledby",
|
|
46
|
+
"aria-describedby",
|
|
47
|
+
"aria-live",
|
|
48
|
+
"aria-multiline",
|
|
49
|
+
"aria-readonly"
|
|
50
|
+
];
|
|
51
|
+
function createEmptyRowStats() {
|
|
52
|
+
return {
|
|
53
|
+
rows: 0,
|
|
54
|
+
cacheHits: 0,
|
|
55
|
+
rowKeyPrepassChecks: 0,
|
|
56
|
+
rowKeyPrepassHits: 0,
|
|
57
|
+
rowKeyPrepassMisses: 0,
|
|
58
|
+
transparentBlankRows: 0,
|
|
59
|
+
plainTextRows: 0,
|
|
60
|
+
singleStyledRows: 0,
|
|
61
|
+
segmentReuseRows: 0,
|
|
62
|
+
fragmentRows: 0,
|
|
63
|
+
spansCreated: 0,
|
|
64
|
+
spansReused: 0,
|
|
65
|
+
textNodeUpdates: 0,
|
|
66
|
+
replaceChildren: 0
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function freezeRowStats(stats) {
|
|
70
|
+
return Object.freeze({ ...stats });
|
|
71
|
+
}
|
|
72
|
+
function addRowStats(target, source) {
|
|
73
|
+
target.rows += source.rows;
|
|
74
|
+
target.cacheHits += source.cacheHits;
|
|
75
|
+
target.rowKeyPrepassChecks += source.rowKeyPrepassChecks;
|
|
76
|
+
target.rowKeyPrepassHits += source.rowKeyPrepassHits;
|
|
77
|
+
target.rowKeyPrepassMisses += source.rowKeyPrepassMisses;
|
|
78
|
+
target.transparentBlankRows += source.transparentBlankRows;
|
|
79
|
+
target.plainTextRows += source.plainTextRows;
|
|
80
|
+
target.singleStyledRows += source.singleStyledRows;
|
|
81
|
+
target.segmentReuseRows += source.segmentReuseRows;
|
|
82
|
+
target.fragmentRows += source.fragmentRows;
|
|
83
|
+
target.spansCreated += source.spansCreated;
|
|
84
|
+
target.spansReused += source.spansReused;
|
|
85
|
+
target.textNodeUpdates += source.textNodeUpdates;
|
|
86
|
+
target.replaceChildren += source.replaceChildren;
|
|
87
|
+
}
|
|
88
|
+
function applyAccessibilityOptions(container, options) {
|
|
89
|
+
if (options === false) return;
|
|
90
|
+
for (const attr of ACCESSIBILITY_ATTRS) container.removeAttribute(attr);
|
|
91
|
+
const role = options?.role ?? "application";
|
|
92
|
+
container.setAttribute("role", role);
|
|
93
|
+
container.setAttribute("aria-live", options?.live ?? "off");
|
|
94
|
+
if (options?.labelledBy) container.setAttribute("aria-labelledby", options.labelledBy);
|
|
95
|
+
else container.setAttribute("aria-label", options?.label ?? "Terminal");
|
|
96
|
+
if (options?.describedBy) container.setAttribute("aria-describedby", options.describedBy);
|
|
97
|
+
if (role === "textbox" || options?.multiline != null) container.setAttribute("aria-multiline", String(options?.multiline ?? true));
|
|
98
|
+
if (role === "textbox" || options?.readonly != null) container.setAttribute("aria-readonly", String(options?.readonly ?? true));
|
|
99
|
+
}
|
|
100
|
+
function visualStyleKey(style, linkOptions) {
|
|
101
|
+
const href = renderableHref(style, linkOptions) ?? "";
|
|
102
|
+
const key = [
|
|
103
|
+
style.fg ?? "",
|
|
104
|
+
style.bg ?? "",
|
|
105
|
+
style.bold ? "1" : "0",
|
|
106
|
+
style.dim ? "1" : "0",
|
|
107
|
+
style.italic ? "1" : "0",
|
|
108
|
+
style.underline ? "1" : "0",
|
|
109
|
+
style.inverse ? "1" : "0",
|
|
110
|
+
href
|
|
111
|
+
].join("|");
|
|
112
|
+
return key;
|
|
113
|
+
}
|
|
114
|
+
function resolveColorHex(color, palette) {
|
|
115
|
+
if (!color) return void 0;
|
|
116
|
+
if (color === "transparent") return void 0;
|
|
117
|
+
if (color.startsWith("#")) return color;
|
|
118
|
+
return ansiColorHex(color, palette);
|
|
119
|
+
}
|
|
120
|
+
function resolveColorCss(color) {
|
|
121
|
+
if (!color) return void 0;
|
|
122
|
+
if (color === "transparent") return void 0;
|
|
123
|
+
if (color.startsWith("#")) return color;
|
|
124
|
+
if (!isAnsiColorName(color)) return void 0;
|
|
125
|
+
return ansiCssVar(color);
|
|
126
|
+
}
|
|
127
|
+
function parseColorToRgb(color) {
|
|
128
|
+
if (color.startsWith("#")) {
|
|
129
|
+
const hex = color.slice(1);
|
|
130
|
+
if (hex.length === 3) {
|
|
131
|
+
const r = Number.parseInt(hex[0] + hex[0], 16);
|
|
132
|
+
const g = Number.parseInt(hex[1] + hex[1], 16);
|
|
133
|
+
const b = Number.parseInt(hex[2] + hex[2], 16);
|
|
134
|
+
return Number.isFinite(r) && Number.isFinite(g) && Number.isFinite(b) ? {
|
|
135
|
+
r,
|
|
136
|
+
g,
|
|
137
|
+
b
|
|
138
|
+
} : null;
|
|
139
|
+
}
|
|
140
|
+
if (hex.length === 6) {
|
|
141
|
+
const r = Number.parseInt(hex.slice(0, 2), 16);
|
|
142
|
+
const g = Number.parseInt(hex.slice(2, 4), 16);
|
|
143
|
+
const b = Number.parseInt(hex.slice(4, 6), 16);
|
|
144
|
+
return Number.isFinite(r) && Number.isFinite(g) && Number.isFinite(b) ? {
|
|
145
|
+
r,
|
|
146
|
+
g,
|
|
147
|
+
b
|
|
148
|
+
} : null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const m = color.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
|
|
152
|
+
if (m) {
|
|
153
|
+
const r = Number.parseInt(m[1], 10);
|
|
154
|
+
const g = Number.parseInt(m[2], 10);
|
|
155
|
+
const b = Number.parseInt(m[3], 10);
|
|
156
|
+
return {
|
|
157
|
+
r,
|
|
158
|
+
g,
|
|
159
|
+
b
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function luminance({ r, g, b }) {
|
|
165
|
+
const srgb = [
|
|
166
|
+
r,
|
|
167
|
+
g,
|
|
168
|
+
b
|
|
169
|
+
].map((v) => {
|
|
170
|
+
const c = v / 255;
|
|
171
|
+
return c <= .03928 ? c / 12.92 : ((c + .055) / 1.055) ** 2.4;
|
|
172
|
+
});
|
|
173
|
+
return .2126 * srgb[0] + .7152 * srgb[1] + .0722 * srgb[2];
|
|
174
|
+
}
|
|
175
|
+
function contrastText(bgColor) {
|
|
176
|
+
const rgb = parseColorToRgb(bgColor);
|
|
177
|
+
if (!rgb) return "#111827";
|
|
178
|
+
return luminance(rgb) > .6 ? "#111827" : "#e5e7eb";
|
|
179
|
+
}
|
|
180
|
+
function applyStyle(span, style, palette) {
|
|
181
|
+
const fgHex = resolveColorHex(style.fg, palette);
|
|
182
|
+
const bgHex = resolveColorHex(style.bg, palette);
|
|
183
|
+
const fgCss = resolveColorCss(style.fg);
|
|
184
|
+
const bgCss = resolveColorCss(style.bg);
|
|
185
|
+
const effectiveFg = style.inverse ? bgCss : fgCss;
|
|
186
|
+
const effectiveBg = style.inverse ? fgCss : bgCss;
|
|
187
|
+
const effectiveFgHex = style.inverse ? bgHex : fgHex;
|
|
188
|
+
const effectiveBgHex = style.inverse ? fgHex : bgHex;
|
|
189
|
+
if (span.tagName === "A") {
|
|
190
|
+
span.style.pointerEvents = "auto";
|
|
191
|
+
span.style.cursor = "pointer";
|
|
192
|
+
span.style.color = "inherit";
|
|
193
|
+
span.style.textDecoration = "inherit";
|
|
194
|
+
}
|
|
195
|
+
if (effectiveFg) {
|
|
196
|
+
span.style.color = effectiveFg;
|
|
197
|
+
if (!span.style.color && effectiveFgHex) span.style.color = effectiveFgHex;
|
|
198
|
+
}
|
|
199
|
+
if (effectiveBg) {
|
|
200
|
+
span.style.backgroundColor = effectiveBg;
|
|
201
|
+
if (!span.style.backgroundColor && effectiveBgHex) span.style.backgroundColor = effectiveBgHex;
|
|
202
|
+
}
|
|
203
|
+
if (!effectiveFg && style.inverse && effectiveBg && effectiveBgHex) span.style.color = contrastText(effectiveBgHex);
|
|
204
|
+
if (style.bold) span.style.fontWeight = "700";
|
|
205
|
+
if (style.italic) span.style.fontStyle = "italic";
|
|
206
|
+
if (style.dim) span.style.opacity = "0.75";
|
|
207
|
+
if (style.underline) span.style.textDecoration = "underline";
|
|
208
|
+
}
|
|
209
|
+
function measureCell(container) {
|
|
210
|
+
const probe = container.ownerDocument.createElement("span");
|
|
211
|
+
probe.textContent = "M";
|
|
212
|
+
probe.style.position = "absolute";
|
|
213
|
+
probe.style.visibility = "hidden";
|
|
214
|
+
probe.style.whiteSpace = "pre";
|
|
215
|
+
container.appendChild(probe);
|
|
216
|
+
const rect = probe.getBoundingClientRect();
|
|
217
|
+
probe.remove();
|
|
218
|
+
return {
|
|
219
|
+
cellWidth: Math.max(1, rect.width),
|
|
220
|
+
cellHeight: Math.max(1, rect.height)
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function measureCharWidth(container, ch) {
|
|
224
|
+
const probe = container.ownerDocument.createElement("span");
|
|
225
|
+
probe.textContent = ch;
|
|
226
|
+
probe.style.position = "absolute";
|
|
227
|
+
probe.style.visibility = "hidden";
|
|
228
|
+
probe.style.whiteSpace = "pre";
|
|
229
|
+
container.appendChild(probe);
|
|
230
|
+
const rect = probe.getBoundingClientRect();
|
|
231
|
+
probe.remove();
|
|
232
|
+
return Math.max(0, rect.width);
|
|
233
|
+
}
|
|
234
|
+
function formatRowSegmentKeyPart(key, text, cols, wide) {
|
|
235
|
+
return `${key}:${text}:${cols}:${wide ? 1 : 0}`;
|
|
236
|
+
}
|
|
237
|
+
function formatSingleRowKey(key, text, cols, wide, style, linkOptions) {
|
|
238
|
+
const plain = isPlainVisualStyle(style, linkOptions);
|
|
239
|
+
const hasRenderableHref = renderableHref(style, linkOptions) != null;
|
|
240
|
+
if (!wide && plain) return `p:${text}:${cols}`;
|
|
241
|
+
if (!wide && !hasRenderableHref && !plain) return `s:${key}:${text}:${cols}`;
|
|
242
|
+
return `1:${formatRowSegmentKeyPart(key, text, cols, wide)}`;
|
|
243
|
+
}
|
|
244
|
+
function computeRowSegmentsWithKey(terminal, y, linkOptions) {
|
|
245
|
+
const cells = terminal.getRow(y);
|
|
246
|
+
let currentKey = null;
|
|
247
|
+
let currentStyle = null;
|
|
248
|
+
let currentText = "";
|
|
249
|
+
let currentCols = 0;
|
|
250
|
+
let currentWide = false;
|
|
251
|
+
const segments = [];
|
|
252
|
+
let keyBody = "";
|
|
253
|
+
let segmentCount = 0;
|
|
254
|
+
function pushSegment(segment) {
|
|
255
|
+
if (segmentCount === 1) {
|
|
256
|
+
const first = segments[0];
|
|
257
|
+
keyBody += `|${formatRowSegmentKeyPart(first.key, first.text, first.cols, first.wide)}`;
|
|
258
|
+
}
|
|
259
|
+
segments.push(segment);
|
|
260
|
+
segmentCount++;
|
|
261
|
+
if (segmentCount > 1) keyBody += `|${formatRowSegmentKeyPart(segment.key, segment.text, segment.cols, segment.wide)}`;
|
|
262
|
+
}
|
|
263
|
+
for (const cell of cells) {
|
|
264
|
+
if (cell.continuation) continue;
|
|
265
|
+
const ch = cell.ch || " ";
|
|
266
|
+
const cols = cell.width || 1;
|
|
267
|
+
const wide = cols === 2;
|
|
268
|
+
const nextStyle = cell.style;
|
|
269
|
+
const key = nextStyle === currentStyle && currentKey != null ? currentKey : visualStyleKey(nextStyle, linkOptions);
|
|
270
|
+
if (currentKey == null) {
|
|
271
|
+
currentKey = key;
|
|
272
|
+
currentStyle = nextStyle;
|
|
273
|
+
currentText = ch;
|
|
274
|
+
currentCols = cols;
|
|
275
|
+
currentWide = wide;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (key === currentKey && wide === currentWide) {
|
|
279
|
+
currentText += ch;
|
|
280
|
+
currentCols += cols;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
pushSegment({
|
|
284
|
+
text: currentText,
|
|
285
|
+
cols: currentCols,
|
|
286
|
+
wide: currentWide,
|
|
287
|
+
style: currentStyle,
|
|
288
|
+
key: currentKey
|
|
289
|
+
});
|
|
290
|
+
currentKey = key;
|
|
291
|
+
currentStyle = nextStyle;
|
|
292
|
+
currentText = ch;
|
|
293
|
+
currentCols = cols;
|
|
294
|
+
currentWide = wide;
|
|
295
|
+
}
|
|
296
|
+
if (currentKey != null) pushSegment({
|
|
297
|
+
text: currentText,
|
|
298
|
+
cols: currentCols,
|
|
299
|
+
wide: currentWide,
|
|
300
|
+
style: currentStyle,
|
|
301
|
+
key: currentKey
|
|
302
|
+
});
|
|
303
|
+
if (segmentCount === 0) return {
|
|
304
|
+
segments,
|
|
305
|
+
key: "0"
|
|
306
|
+
};
|
|
307
|
+
if (segmentCount === 1) {
|
|
308
|
+
const s = segments[0];
|
|
309
|
+
return {
|
|
310
|
+
segments,
|
|
311
|
+
key: formatSingleRowKey(s.key, s.text, s.cols, s.wide, s.style, linkOptions)
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
segments,
|
|
316
|
+
key: `${segmentCount}${keyBody}`
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function computeRowKey(terminal, y, linkOptions) {
|
|
320
|
+
const cells = terminal.getRow(y);
|
|
321
|
+
let currentKey = null;
|
|
322
|
+
let currentStyle = null;
|
|
323
|
+
let currentText = "";
|
|
324
|
+
let currentCols = 0;
|
|
325
|
+
let currentWide = false;
|
|
326
|
+
let firstKey = "";
|
|
327
|
+
let firstStyle = null;
|
|
328
|
+
let firstText = "";
|
|
329
|
+
let firstCols = 0;
|
|
330
|
+
let firstWide = false;
|
|
331
|
+
let keyBody = "";
|
|
332
|
+
let segmentCount = 0;
|
|
333
|
+
function pushSegment(key, text, cols, wide, style) {
|
|
334
|
+
if (segmentCount === 0) {
|
|
335
|
+
firstKey = key;
|
|
336
|
+
firstStyle = style;
|
|
337
|
+
firstText = text;
|
|
338
|
+
firstCols = cols;
|
|
339
|
+
firstWide = wide;
|
|
340
|
+
} else {
|
|
341
|
+
if (segmentCount === 1) keyBody += `|${formatRowSegmentKeyPart(firstKey, firstText, firstCols, firstWide)}`;
|
|
342
|
+
keyBody += `|${formatRowSegmentKeyPart(key, text, cols, wide)}`;
|
|
343
|
+
}
|
|
344
|
+
segmentCount++;
|
|
345
|
+
}
|
|
346
|
+
for (const cell of cells) {
|
|
347
|
+
if (cell.continuation) continue;
|
|
348
|
+
const ch = cell.ch || " ";
|
|
349
|
+
const cols = cell.width || 1;
|
|
350
|
+
const wide = cols === 2;
|
|
351
|
+
const nextStyle = cell.style;
|
|
352
|
+
const key = nextStyle === currentStyle && currentKey != null ? currentKey : visualStyleKey(nextStyle, linkOptions);
|
|
353
|
+
if (currentKey == null) {
|
|
354
|
+
currentKey = key;
|
|
355
|
+
currentStyle = nextStyle;
|
|
356
|
+
currentText = ch;
|
|
357
|
+
currentCols = cols;
|
|
358
|
+
currentWide = wide;
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (key === currentKey && wide === currentWide) {
|
|
362
|
+
currentText += ch;
|
|
363
|
+
currentCols += cols;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
pushSegment(currentKey, currentText, currentCols, currentWide, currentStyle);
|
|
367
|
+
currentKey = key;
|
|
368
|
+
currentStyle = nextStyle;
|
|
369
|
+
currentText = ch;
|
|
370
|
+
currentCols = cols;
|
|
371
|
+
currentWide = wide;
|
|
372
|
+
}
|
|
373
|
+
if (currentKey != null) pushSegment(currentKey, currentText, currentCols, currentWide, currentStyle);
|
|
374
|
+
if (segmentCount === 0) return "0";
|
|
375
|
+
if (segmentCount === 1) return formatSingleRowKey(firstKey, firstText, firstCols, firstWide, firstStyle, linkOptions);
|
|
376
|
+
return `${segmentCount}${keyBody}`;
|
|
377
|
+
}
|
|
378
|
+
const rowCache = new WeakMap();
|
|
379
|
+
function nowMs() {
|
|
380
|
+
const p = globalThis.performance;
|
|
381
|
+
if (p && typeof p.now === "function") return p.now();
|
|
382
|
+
return Date.now();
|
|
383
|
+
}
|
|
384
|
+
function isPlainVisualStyle(style, linkOptions) {
|
|
385
|
+
const hasRenderableHref = renderableHref(style, linkOptions) != null;
|
|
386
|
+
return !style.fg && !style.bg && !style.bold && !style.dim && !style.italic && !style.underline && !style.inverse && !hasRenderableHref;
|
|
387
|
+
}
|
|
388
|
+
function isTransparentBlankRow(segments, linkOptions) {
|
|
389
|
+
return segments.length === 1 && isPlainVisualStyle(segments[0].style, linkOptions) && segments[0].text.trim().length === 0;
|
|
390
|
+
}
|
|
391
|
+
function isPlainTextRow(segments, linkOptions) {
|
|
392
|
+
return segments.length === 1 && !segments[0].wide && isPlainVisualStyle(segments[0].style, linkOptions);
|
|
393
|
+
}
|
|
394
|
+
function normalizeLinkOptions(value) {
|
|
395
|
+
if (value === true) return {};
|
|
396
|
+
if (value === false || value == null) return false;
|
|
397
|
+
const activation = value.activation ?? (typeof value.onActivate === "function" ? "event" : "native");
|
|
398
|
+
if (activation === "event" && typeof value.onActivate !== "function") return {
|
|
399
|
+
...value,
|
|
400
|
+
activation: "native"
|
|
401
|
+
};
|
|
402
|
+
return {
|
|
403
|
+
...value,
|
|
404
|
+
activation
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function linkOptionsKey(options) {
|
|
408
|
+
if (!options) return "disabled";
|
|
409
|
+
return JSON.stringify({
|
|
410
|
+
allowRelative: options.allowRelative !== false,
|
|
411
|
+
externalTarget: options.externalTarget ?? "_blank",
|
|
412
|
+
tabIndex: options.tabIndex ?? -1,
|
|
413
|
+
activation: options.activation ?? "native",
|
|
414
|
+
hasOnActivate: typeof options.onActivate === "function"
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
function renderableHref(style, linkOptions) {
|
|
418
|
+
if (!linkOptions) return null;
|
|
419
|
+
if (linkOptions.activation === "none") return null;
|
|
420
|
+
return sanitizeDomHref(style.href, { allowRelative: linkOptions.allowRelative !== false });
|
|
421
|
+
}
|
|
422
|
+
function isSingleStyledTextRow(segments, linkOptions) {
|
|
423
|
+
return segments.length === 1 && !segments[0].wide && !renderableHref(segments[0].style, linkOptions) && !isPlainVisualStyle(segments[0].style, linkOptions);
|
|
424
|
+
}
|
|
425
|
+
function canReuseSegmentSpans(segments, linkOptions) {
|
|
426
|
+
return segments.length > 1 && segments.every((segment) => !segment.wide && !renderableHref(segment.style, linkOptions));
|
|
427
|
+
}
|
|
428
|
+
function resetSpanStyle(span) {
|
|
429
|
+
span.removeAttribute("style");
|
|
430
|
+
if (span.tagName === "A") {
|
|
431
|
+
span.removeAttribute("href");
|
|
432
|
+
span.removeAttribute("target");
|
|
433
|
+
span.removeAttribute("rel");
|
|
434
|
+
delete span.dataset.vtHref;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function createSegmentElement(doc, style, options) {
|
|
438
|
+
const linkOptions = options.links;
|
|
439
|
+
if (!linkOptions) return doc.createElement("span");
|
|
440
|
+
const href = renderableHref(style, linkOptions);
|
|
441
|
+
if (!href) return doc.createElement("span");
|
|
442
|
+
const anchor = doc.createElement("a");
|
|
443
|
+
anchor.href = href;
|
|
444
|
+
anchor.dataset.vtHref = href;
|
|
445
|
+
const protocol = href.match(/^[a-z][a-z0-9+.-]*:/i)?.[0].toLowerCase() ?? null;
|
|
446
|
+
if (protocol === "http:" || protocol === "https:") {
|
|
447
|
+
anchor.target = linkOptions.externalTarget ?? "_blank";
|
|
448
|
+
anchor.rel = "noopener noreferrer";
|
|
449
|
+
}
|
|
450
|
+
anchor.tabIndex = linkOptions.tabIndex ?? -1;
|
|
451
|
+
anchor.draggable = false;
|
|
452
|
+
anchor.addEventListener("pointerdown", options.onAnchorPointerDown);
|
|
453
|
+
anchor.addEventListener("click", options.onAnchorClick);
|
|
454
|
+
return anchor;
|
|
455
|
+
}
|
|
456
|
+
function isSpanNode(node, lineEl) {
|
|
457
|
+
const SpanCtor = lineEl.ownerDocument.defaultView?.HTMLSpanElement ?? globalThis.HTMLSpanElement;
|
|
458
|
+
return typeof SpanCtor === "function" && node instanceof SpanCtor;
|
|
459
|
+
}
|
|
460
|
+
function textNodeType(lineEl) {
|
|
461
|
+
return lineEl.ownerDocument.defaultView?.Node.TEXT_NODE ?? 3;
|
|
462
|
+
}
|
|
463
|
+
function tryUpdateSegmentSpans(lineEl, segments, metrics, palette) {
|
|
464
|
+
if (lineEl.childNodes.length !== segments.length) return false;
|
|
465
|
+
for (let i = 0; i < segments.length; i++) {
|
|
466
|
+
const node = lineEl.childNodes[i];
|
|
467
|
+
if (!isSpanNode(node, lineEl)) return false;
|
|
468
|
+
if (node.dataset.vtFastRow !== "segment") return false;
|
|
469
|
+
if (node.dataset.vtSegmentIndex !== String(i)) return false;
|
|
470
|
+
}
|
|
471
|
+
for (let i = 0; i < segments.length; i++) {
|
|
472
|
+
const span = lineEl.childNodes[i];
|
|
473
|
+
const seg = segments[i];
|
|
474
|
+
span.textContent = seg.text;
|
|
475
|
+
resetSpanStyle(span);
|
|
476
|
+
span.style.cssText = `display:inline-block;width:${seg.cols * metrics.cellWidth}px;height:${metrics.cellHeight}px;overflow:hidden;white-space:pre;vertical-align:top`;
|
|
477
|
+
applyStyle(span, seg.style, palette);
|
|
478
|
+
}
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
function renderRow(terminal, metrics, wideScaleX, y, lineEl, stats, enableRowKeyPrepass, palette, linkOptions, onAnchorClick, onAnchorPointerDown, linkVersion) {
|
|
482
|
+
stats.rows++;
|
|
483
|
+
const doc = lineEl.ownerDocument;
|
|
484
|
+
const rowModeKey = `links:${linkVersion}`;
|
|
485
|
+
const cachedKey = rowCache.get(lineEl);
|
|
486
|
+
if (enableRowKeyPrepass && cachedKey != null) {
|
|
487
|
+
stats.rowKeyPrepassChecks++;
|
|
488
|
+
const prepassKey = `${rowModeKey}|${computeRowKey(terminal, y, linkOptions)}`;
|
|
489
|
+
if (cachedKey === prepassKey) {
|
|
490
|
+
stats.rowKeyPrepassHits++;
|
|
491
|
+
stats.cacheHits++;
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
stats.rowKeyPrepassMisses++;
|
|
495
|
+
}
|
|
496
|
+
const { segments, key } = computeRowSegmentsWithKey(terminal, y, linkOptions);
|
|
497
|
+
const newKey = `${rowModeKey}|${key}`;
|
|
498
|
+
if (cachedKey === newKey) {
|
|
499
|
+
stats.cacheHits++;
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
rowCache.set(lineEl, newKey);
|
|
503
|
+
if (isTransparentBlankRow(segments, linkOptions)) {
|
|
504
|
+
stats.transparentBlankRows++;
|
|
505
|
+
stats.replaceChildren++;
|
|
506
|
+
lineEl.replaceChildren();
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (isPlainTextRow(segments, linkOptions)) {
|
|
510
|
+
stats.plainTextRows++;
|
|
511
|
+
stats.textNodeUpdates++;
|
|
512
|
+
const text = segments[0].text;
|
|
513
|
+
const firstChild = lineEl.firstChild;
|
|
514
|
+
if (lineEl.childNodes.length === 1 && firstChild?.nodeType === textNodeType(lineEl)) firstChild.nodeValue = text;
|
|
515
|
+
else lineEl.textContent = text;
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (isSingleStyledTextRow(segments, linkOptions)) {
|
|
519
|
+
const seg = segments[0];
|
|
520
|
+
const firstChild = lineEl.firstChild;
|
|
521
|
+
let span;
|
|
522
|
+
if (lineEl.childNodes.length === 1 && isSpanNode(firstChild, lineEl) && firstChild.dataset.vtFastRow === "styled") {
|
|
523
|
+
span = firstChild;
|
|
524
|
+
stats.spansReused++;
|
|
525
|
+
} else {
|
|
526
|
+
span = createSegmentElement(doc, seg.style, {
|
|
527
|
+
links: linkOptions,
|
|
528
|
+
onAnchorClick,
|
|
529
|
+
onAnchorPointerDown
|
|
530
|
+
});
|
|
531
|
+
span.dataset.vtFastRow = "styled";
|
|
532
|
+
stats.spansCreated++;
|
|
533
|
+
stats.replaceChildren++;
|
|
534
|
+
lineEl.replaceChildren(span);
|
|
535
|
+
}
|
|
536
|
+
stats.singleStyledRows++;
|
|
537
|
+
span.textContent = seg.text;
|
|
538
|
+
resetSpanStyle(span);
|
|
539
|
+
span.style.cssText = `display:inline-block;width:${seg.cols * metrics.cellWidth}px;height:${metrics.cellHeight}px;overflow:hidden;white-space:pre;vertical-align:top`;
|
|
540
|
+
applyStyle(span, seg.style, palette);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const canReuseSpans = canReuseSegmentSpans(segments, linkOptions);
|
|
544
|
+
if (canReuseSpans && tryUpdateSegmentSpans(lineEl, segments, metrics, palette)) {
|
|
545
|
+
stats.segmentReuseRows++;
|
|
546
|
+
stats.spansReused += segments.length;
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
stats.fragmentRows++;
|
|
550
|
+
stats.spansCreated += segments.length;
|
|
551
|
+
stats.replaceChildren++;
|
|
552
|
+
const fragment = doc.createDocumentFragment();
|
|
553
|
+
for (let i = 0; i < segments.length; i++) {
|
|
554
|
+
const seg = segments[i];
|
|
555
|
+
const span = createSegmentElement(doc, seg.style, {
|
|
556
|
+
links: linkOptions,
|
|
557
|
+
onAnchorClick,
|
|
558
|
+
onAnchorPointerDown
|
|
559
|
+
});
|
|
560
|
+
if (canReuseSpans) {
|
|
561
|
+
span.dataset.vtFastRow = "segment";
|
|
562
|
+
span.dataset.vtSegmentIndex = String(i);
|
|
563
|
+
}
|
|
564
|
+
if (seg.wide && wideScaleX > 1.01) {
|
|
565
|
+
const inner = doc.createElement("span");
|
|
566
|
+
inner.textContent = seg.text;
|
|
567
|
+
inner.style.cssText = `display:inline-block;white-space:pre;transform:scaleX(${wideScaleX});transform-origin:left`;
|
|
568
|
+
span.appendChild(inner);
|
|
569
|
+
} else span.textContent = seg.text;
|
|
570
|
+
span.style.cssText = `display:inline-block;width:${seg.cols * metrics.cellWidth}px;height:${metrics.cellHeight}px;overflow:hidden;white-space:pre;vertical-align:top`;
|
|
571
|
+
applyStyle(span, seg.style, palette);
|
|
572
|
+
fragment.appendChild(span);
|
|
573
|
+
}
|
|
574
|
+
lineEl.replaceChildren(fragment);
|
|
575
|
+
}
|
|
576
|
+
function createDomRenderer(terminal, container, options = {}) {
|
|
577
|
+
const doc = container.ownerDocument;
|
|
578
|
+
let rendererLinkOptions = normalizeLinkOptions(options.links);
|
|
579
|
+
let rendererOnLinkClick = options.onLinkClick;
|
|
580
|
+
let rendererLinkOptionsKey = linkOptionsKey(rendererLinkOptions);
|
|
581
|
+
let rendererLinkVersion = 0;
|
|
582
|
+
const onAnchorClick = (event) => {
|
|
583
|
+
const anchor = event.currentTarget;
|
|
584
|
+
const href = anchor.dataset.vtHref;
|
|
585
|
+
if (!href || !rendererLinkOptions) return;
|
|
586
|
+
const linkClickResult = rendererOnLinkClick?.(event, href);
|
|
587
|
+
if (linkClickResult === false) {
|
|
588
|
+
event.preventDefault();
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (rendererLinkOptions.activation === "event") {
|
|
592
|
+
event.preventDefault();
|
|
593
|
+
const activateResult = rendererLinkOptions.onActivate?.(href, event);
|
|
594
|
+
if (activateResult === false) event.preventDefault();
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
const onAnchorPointerDown = () => {
|
|
599
|
+
if (!rendererLinkOptions || rendererLinkOptions.activation !== "event") return;
|
|
600
|
+
try {
|
|
601
|
+
container.focus({ preventScroll: true });
|
|
602
|
+
} catch {
|
|
603
|
+
container.focus();
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
container.style.fontFamily = DEFAULT_FONT_FAMILY;
|
|
607
|
+
container.style.whiteSpace = "pre";
|
|
608
|
+
container.style.display = "inline-block";
|
|
609
|
+
container.style.overflow = "hidden";
|
|
610
|
+
container.style.position = "relative";
|
|
611
|
+
container.style.userSelect = "text";
|
|
612
|
+
container.style.outline = "none";
|
|
613
|
+
container.style.contain = "layout style";
|
|
614
|
+
if (options.accessibility !== false && !container.hasAttribute("tabindex")) container.tabIndex = 0;
|
|
615
|
+
applyAccessibilityOptions(container, options.accessibility);
|
|
616
|
+
let palette = options.palette ?? null;
|
|
617
|
+
installAnsiPaletteCssVars(container, palette);
|
|
618
|
+
let metrics = measureCell(container);
|
|
619
|
+
let wideScaleX = 1;
|
|
620
|
+
const planeLayers = new Map();
|
|
621
|
+
let raf = 0;
|
|
622
|
+
const pending = new Map();
|
|
623
|
+
let disposed = false;
|
|
624
|
+
const syncFlushMaxRows = Math.max(0, Math.floor(options.syncFlushMaxRows ?? DEFAULT_SYNC_FLUSH_MAX_ROWS));
|
|
625
|
+
const syncFlushCellBudget = Math.max(0, Math.floor(options.syncFlushCellBudget ?? DEFAULT_SYNC_FLUSH_CELL_BUDGET));
|
|
626
|
+
const rowKeyPrepassMode = options.enableRowKeyPrepass ?? "auto";
|
|
627
|
+
const capabilities = Object.freeze({
|
|
628
|
+
...DOM_RENDERER_CAPABILITIES,
|
|
629
|
+
scrollOperations: options.enableScrollOperations !== false
|
|
630
|
+
});
|
|
631
|
+
let syncFlushRequested = 0;
|
|
632
|
+
let syncFlushPerformed = 0;
|
|
633
|
+
let syncFlushDeferred = 0;
|
|
634
|
+
let lastSyncFlushDecision = null;
|
|
635
|
+
let domFlushCount = 0;
|
|
636
|
+
let lastDomFlush = null;
|
|
637
|
+
const rowRenderTotal = createEmptyRowStats();
|
|
638
|
+
let lastRowRenderStats = null;
|
|
639
|
+
let rowKeyPrepassAutoReady = false;
|
|
640
|
+
let rowKeyPrepassAutoDecision = "sampling";
|
|
641
|
+
let rowKeyPrepassSampleRows = 0;
|
|
642
|
+
let rowKeyPrepassSampleHits = 0;
|
|
643
|
+
let rowKeyPrepassSampleMisses = 0;
|
|
644
|
+
let rowKeyPrepassLastSampleRows = 0;
|
|
645
|
+
let rowKeyPrepassLastSampleHitRatio = null;
|
|
646
|
+
function rowKeyPrepassDecision() {
|
|
647
|
+
if (rowKeyPrepassMode === true) return "forced-enabled";
|
|
648
|
+
if (rowKeyPrepassMode === false) return "forced-disabled";
|
|
649
|
+
return rowKeyPrepassAutoDecision;
|
|
650
|
+
}
|
|
651
|
+
const debugStats = {
|
|
652
|
+
get syncFlush() {
|
|
653
|
+
return {
|
|
654
|
+
requested: syncFlushRequested,
|
|
655
|
+
performed: syncFlushPerformed,
|
|
656
|
+
deferred: syncFlushDeferred,
|
|
657
|
+
last: lastSyncFlushDecision
|
|
658
|
+
};
|
|
659
|
+
},
|
|
660
|
+
get flush() {
|
|
661
|
+
return {
|
|
662
|
+
count: domFlushCount,
|
|
663
|
+
last: lastDomFlush
|
|
664
|
+
};
|
|
665
|
+
},
|
|
666
|
+
get rowRender() {
|
|
667
|
+
return {
|
|
668
|
+
total: freezeRowStats(rowRenderTotal),
|
|
669
|
+
lastFlush: lastRowRenderStats
|
|
670
|
+
};
|
|
671
|
+
},
|
|
672
|
+
get rowKeyPrepass() {
|
|
673
|
+
const sampleHitRatio = rowKeyPrepassSampleRows > 0 ? rowKeyPrepassSampleHits / rowKeyPrepassSampleRows : null;
|
|
674
|
+
return {
|
|
675
|
+
mode: rowKeyPrepassMode,
|
|
676
|
+
decision: rowKeyPrepassDecision(),
|
|
677
|
+
sampleRows: rowKeyPrepassSampleRows,
|
|
678
|
+
sampleHits: rowKeyPrepassSampleHits,
|
|
679
|
+
sampleMisses: rowKeyPrepassSampleMisses,
|
|
680
|
+
sampleHitRatio,
|
|
681
|
+
lastSampleRows: rowKeyPrepassLastSampleRows,
|
|
682
|
+
lastSampleHitRatio: rowKeyPrepassLastSampleHitRatio
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
function shouldUseRowKeyPrepass() {
|
|
687
|
+
if (rowKeyPrepassMode === true) return true;
|
|
688
|
+
if (rowKeyPrepassMode === false) return false;
|
|
689
|
+
return rowKeyPrepassAutoReady && rowKeyPrepassAutoDecision !== "disabled";
|
|
690
|
+
}
|
|
691
|
+
function recordRowKeyPrepassAutoStats(stats) {
|
|
692
|
+
if (rowKeyPrepassMode !== "auto") return;
|
|
693
|
+
if (stats.rowKeyPrepassChecks === 0) return;
|
|
694
|
+
rowKeyPrepassSampleRows += stats.rowKeyPrepassChecks;
|
|
695
|
+
rowKeyPrepassSampleHits += stats.rowKeyPrepassHits;
|
|
696
|
+
rowKeyPrepassSampleMisses += stats.rowKeyPrepassMisses;
|
|
697
|
+
if (rowKeyPrepassSampleRows < PREPASS_SAMPLE_ROWS) return;
|
|
698
|
+
const hitRatio = rowKeyPrepassSampleHits / rowKeyPrepassSampleRows;
|
|
699
|
+
rowKeyPrepassLastSampleRows = rowKeyPrepassSampleRows;
|
|
700
|
+
rowKeyPrepassLastSampleHitRatio = hitRatio;
|
|
701
|
+
if (rowKeyPrepassAutoDecision === "enabled") {
|
|
702
|
+
if (hitRatio < PREPASS_DISABLE_HIT_RATIO) rowKeyPrepassAutoDecision = "disabled";
|
|
703
|
+
} else if (hitRatio >= PREPASS_ENABLE_HIT_RATIO) rowKeyPrepassAutoDecision = "enabled";
|
|
704
|
+
else rowKeyPrepassAutoDecision = "disabled";
|
|
705
|
+
rowKeyPrepassSampleRows = 0;
|
|
706
|
+
rowKeyPrepassSampleHits = 0;
|
|
707
|
+
rowKeyPrepassSampleMisses = 0;
|
|
708
|
+
}
|
|
709
|
+
function recordRowRenderStats(stats) {
|
|
710
|
+
if (stats.rows === 0) return;
|
|
711
|
+
addRowStats(rowRenderTotal, stats);
|
|
712
|
+
lastRowRenderStats = freezeRowStats(stats);
|
|
713
|
+
}
|
|
714
|
+
function applyPlaneOffset(plane, offsetPx) {
|
|
715
|
+
const layer = planeLayers.get(plane);
|
|
716
|
+
if (!layer) return;
|
|
717
|
+
const next = Number.isFinite(offsetPx) ? offsetPx : 0;
|
|
718
|
+
if (layer.offsetPx === next) return;
|
|
719
|
+
layer.offsetPx = next;
|
|
720
|
+
layer.contentEl.style.transform = next ? `translateY(${next}px)` : "translateY(0px)";
|
|
721
|
+
}
|
|
722
|
+
function applyPlaneViewport(plane, viewport) {
|
|
723
|
+
const layer = planeLayers.get(plane);
|
|
724
|
+
if (!layer) return;
|
|
725
|
+
const totalHeight = terminal.size().rows * metrics.cellHeight;
|
|
726
|
+
if (!viewport) {
|
|
727
|
+
layer.viewportTopPx = 0;
|
|
728
|
+
layer.viewportHeightPx = null;
|
|
729
|
+
layer.el.style.clipPath = "none";
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const topPx = Math.max(0, Math.min(totalHeight, Math.floor(viewport.topPx)));
|
|
733
|
+
const heightPx = Math.max(0, Math.min(totalHeight - topPx, Math.floor(viewport.heightPx)));
|
|
734
|
+
layer.viewportTopPx = topPx;
|
|
735
|
+
layer.viewportHeightPx = heightPx;
|
|
736
|
+
if (topPx === 0 && heightPx >= totalHeight) {
|
|
737
|
+
layer.el.style.clipPath = "none";
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const bottomPx = Math.max(0, totalHeight - topPx - heightPx);
|
|
741
|
+
layer.el.style.clipPath = `inset(${topPx}px 0px ${bottomPx}px 0px)`;
|
|
742
|
+
}
|
|
743
|
+
function rebuildLines() {
|
|
744
|
+
const { rows, cols } = terminal.size();
|
|
745
|
+
container.style.width = `${cols * metrics.cellWidth}px`;
|
|
746
|
+
container.style.height = `${rows * metrics.cellHeight}px`;
|
|
747
|
+
const fragment = doc.createDocumentFragment();
|
|
748
|
+
for (let i = 0; i < TERMINAL_RENDER_PLANES.length; i++) {
|
|
749
|
+
const plane = TERMINAL_RENDER_PLANES[i];
|
|
750
|
+
const planeTerminal = getPlaneTerminal(terminal, plane);
|
|
751
|
+
let layer = planeLayers.get(plane);
|
|
752
|
+
if (!layer) {
|
|
753
|
+
const el = doc.createElement("div");
|
|
754
|
+
const contentEl = doc.createElement("div");
|
|
755
|
+
el.dataset.vtPlane = plane;
|
|
756
|
+
el.style.pointerEvents = "none";
|
|
757
|
+
contentEl.style.position = "absolute";
|
|
758
|
+
contentEl.style.left = "0";
|
|
759
|
+
contentEl.style.top = "0";
|
|
760
|
+
layer = {
|
|
761
|
+
el,
|
|
762
|
+
contentEl,
|
|
763
|
+
lines: [],
|
|
764
|
+
offsetPx: 0,
|
|
765
|
+
terminal: planeTerminal,
|
|
766
|
+
viewportTopPx: 0,
|
|
767
|
+
viewportHeightPx: null
|
|
768
|
+
};
|
|
769
|
+
planeLayers.set(plane, layer);
|
|
770
|
+
}
|
|
771
|
+
layer.terminal = planeTerminal;
|
|
772
|
+
layer.el.style.position = "absolute";
|
|
773
|
+
layer.el.style.left = "0";
|
|
774
|
+
layer.el.style.top = "0";
|
|
775
|
+
layer.el.style.width = `${cols * metrics.cellWidth}px`;
|
|
776
|
+
layer.el.style.height = `${rows * metrics.cellHeight}px`;
|
|
777
|
+
layer.el.style.overflow = "hidden";
|
|
778
|
+
layer.el.style.zIndex = String(i);
|
|
779
|
+
layer.contentEl.style.width = `${cols * metrics.cellWidth}px`;
|
|
780
|
+
layer.contentEl.style.height = `${rows * metrics.cellHeight}px`;
|
|
781
|
+
applyPlaneOffset(plane, layer.offsetPx);
|
|
782
|
+
applyPlaneViewport(plane, layer.viewportHeightPx == null ? null : {
|
|
783
|
+
topPx: layer.viewportTopPx,
|
|
784
|
+
heightPx: layer.viewportHeightPx
|
|
785
|
+
});
|
|
786
|
+
const lineFragment = doc.createDocumentFragment();
|
|
787
|
+
layer.lines = Array.from({ length: rows }, () => {
|
|
788
|
+
const line = doc.createElement("div");
|
|
789
|
+
line.style.cssText = `height:${metrics.cellHeight}px;width:${cols * metrics.cellWidth}px;overflow:hidden;contain:layout style`;
|
|
790
|
+
lineFragment.appendChild(line);
|
|
791
|
+
return line;
|
|
792
|
+
});
|
|
793
|
+
layer.contentEl.replaceChildren(lineFragment);
|
|
794
|
+
layer.el.replaceChildren(layer.contentEl);
|
|
795
|
+
fragment.appendChild(layer.el);
|
|
796
|
+
}
|
|
797
|
+
container.replaceChildren(fragment);
|
|
798
|
+
}
|
|
799
|
+
function refresh() {
|
|
800
|
+
cancelPendingRaf();
|
|
801
|
+
clearPendingRows();
|
|
802
|
+
metrics = measureCell(container);
|
|
803
|
+
const wideW = measureCharWidth(container, "中");
|
|
804
|
+
wideScaleX = wideW > 0 ? Math.max(1, Math.min(2, metrics.cellWidth * 2 / wideW)) : 1;
|
|
805
|
+
rebuildLines();
|
|
806
|
+
const size = terminal.size();
|
|
807
|
+
const rowStats = createEmptyRowStats();
|
|
808
|
+
for (const plane of TERMINAL_RENDER_PLANES) {
|
|
809
|
+
const layer = planeLayers.get(plane);
|
|
810
|
+
if (!layer) continue;
|
|
811
|
+
for (let y = 0; y < size.rows; y++) renderRow(layer.terminal, metrics, wideScaleX, y, layer.lines[y], rowStats, shouldUseRowKeyPrepass(), palette, rendererLinkOptions, onAnchorClick, onAnchorPointerDown, rendererLinkVersion);
|
|
812
|
+
}
|
|
813
|
+
recordRowKeyPrepassAutoStats(rowStats);
|
|
814
|
+
recordRowRenderStats(rowStats);
|
|
815
|
+
}
|
|
816
|
+
const pendingRowCountsByPlane = new Map();
|
|
817
|
+
let pendingTotalRowCount = 0;
|
|
818
|
+
let lastSyncFlushWarnAt = 0;
|
|
819
|
+
function cancelPendingRaf() {
|
|
820
|
+
if (raf > 0) cancelAnimationFrame(raf);
|
|
821
|
+
raf = 0;
|
|
822
|
+
}
|
|
823
|
+
function clearPendingRows() {
|
|
824
|
+
pending.clear();
|
|
825
|
+
pendingRowCountsByPlane.clear();
|
|
826
|
+
pendingTotalRowCount = 0;
|
|
827
|
+
}
|
|
828
|
+
function addPendingRow(plane, rows, y) {
|
|
829
|
+
if (rows.has(y)) return;
|
|
830
|
+
rows.add(y);
|
|
831
|
+
pendingTotalRowCount++;
|
|
832
|
+
pendingRowCountsByPlane.set(plane, (pendingRowCountsByPlane.get(plane) ?? 0) + 1);
|
|
833
|
+
}
|
|
834
|
+
function deletePendingRow(plane, rows, y) {
|
|
835
|
+
if (!rows.delete(y)) return;
|
|
836
|
+
pendingTotalRowCount--;
|
|
837
|
+
const nextCount = (pendingRowCountsByPlane.get(plane) ?? 1) - 1;
|
|
838
|
+
if (nextCount > 0) pendingRowCountsByPlane.set(plane, nextCount);
|
|
839
|
+
else pendingRowCountsByPlane.delete(plane);
|
|
840
|
+
}
|
|
841
|
+
function hasPendingRowsInRange(plane, startY, endY) {
|
|
842
|
+
const rows = pending.get(plane);
|
|
843
|
+
if (!rows?.size) return false;
|
|
844
|
+
for (const y of rows) if (y >= startY && y < endY) return true;
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
847
|
+
function addPendingRowRange(plane, startY, endY) {
|
|
848
|
+
let rows = pending.get(plane);
|
|
849
|
+
if (!rows) {
|
|
850
|
+
rows = new Set();
|
|
851
|
+
pending.set(plane, rows);
|
|
852
|
+
}
|
|
853
|
+
for (let y = startY; y < endY; y++) addPendingRow(plane, rows, y);
|
|
854
|
+
}
|
|
855
|
+
function invalidateRowCacheRange(layer, startY, endY) {
|
|
856
|
+
for (let y = startY; y < endY; y++) {
|
|
857
|
+
const line = layer.lines[y];
|
|
858
|
+
if (line) rowCache.delete(line);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
function reorderLayerDomRows(layer, startY, endY) {
|
|
862
|
+
const before = layer.lines[endY] ?? null;
|
|
863
|
+
const fragment = layer.contentEl.ownerDocument.createDocumentFragment();
|
|
864
|
+
for (let y = startY; y < endY; y++) {
|
|
865
|
+
const line = layer.lines[y];
|
|
866
|
+
if (line) fragment.appendChild(line);
|
|
867
|
+
}
|
|
868
|
+
layer.contentEl.insertBefore(fragment, before);
|
|
869
|
+
}
|
|
870
|
+
function scrollOperationRange(op) {
|
|
871
|
+
const { rows } = terminal.size();
|
|
872
|
+
const startY = Math.max(0, Math.min(rows, Math.floor(op.startY)));
|
|
873
|
+
const endY = Math.max(0, Math.min(rows, Math.floor(op.endY)));
|
|
874
|
+
return {
|
|
875
|
+
startY,
|
|
876
|
+
endY
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function addFallbackRows(rows, startY, endY) {
|
|
880
|
+
for (let y = startY; y < endY; y++) rows.add(y);
|
|
881
|
+
}
|
|
882
|
+
function unionRows(a, b) {
|
|
883
|
+
if (a === null) return null;
|
|
884
|
+
if (!b.length) return a;
|
|
885
|
+
const rows = new Set(a);
|
|
886
|
+
for (const y of b) rows.add(y);
|
|
887
|
+
return Array.from(rows).sort((left, right) => left - right);
|
|
888
|
+
}
|
|
889
|
+
function invalidateAndRepaintScrollRange(plane, layer, startY, endY) {
|
|
890
|
+
invalidateRowCacheRange(layer, startY, endY);
|
|
891
|
+
addPendingRowRange(plane, startY, endY);
|
|
892
|
+
}
|
|
893
|
+
function applyScrollOperationToLayer(layer, op) {
|
|
894
|
+
const { startY, endY } = scrollOperationRange(op);
|
|
895
|
+
const delta = Math.trunc(op.delta);
|
|
896
|
+
const height = endY - startY;
|
|
897
|
+
const region = layer.lines.slice(startY, endY);
|
|
898
|
+
const nextRegion = delta > 0 ? [...region.slice(delta), ...region.slice(0, delta)] : [...region.slice(height + delta), ...region.slice(0, height + delta)];
|
|
899
|
+
for (let i = 0; i < height; i++) {
|
|
900
|
+
const line = nextRegion[i];
|
|
901
|
+
if (!line) continue;
|
|
902
|
+
layer.lines[startY + i] = line;
|
|
903
|
+
rowCache.delete(line);
|
|
904
|
+
}
|
|
905
|
+
reorderLayerDomRows(layer, startY, endY);
|
|
906
|
+
}
|
|
907
|
+
function canApplyScrollOperations(planes, operations) {
|
|
908
|
+
if (!capabilities.scrollOperations || planes.length !== 1 || !operations?.length) return false;
|
|
909
|
+
const plane = planes[0];
|
|
910
|
+
const layer = planeLayers.get(plane);
|
|
911
|
+
if (!layer) return false;
|
|
912
|
+
for (const op of operations) {
|
|
913
|
+
const { startY, endY } = scrollOperationRange(op);
|
|
914
|
+
const delta = Math.trunc(op.delta);
|
|
915
|
+
const height = endY - startY;
|
|
916
|
+
if (height <= 0 || delta === 0 || Math.abs(delta) >= height) return false;
|
|
917
|
+
if (hasPendingRowsInRange(plane, startY, endY)) return false;
|
|
918
|
+
}
|
|
919
|
+
return true;
|
|
920
|
+
}
|
|
921
|
+
function applyScrollOperations(planes, operations) {
|
|
922
|
+
if (!operations?.length) return;
|
|
923
|
+
for (const plane of planes) {
|
|
924
|
+
const layer = planeLayers.get(plane);
|
|
925
|
+
if (!layer) continue;
|
|
926
|
+
for (const op of operations) applyScrollOperationToLayer(layer, op);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
function markScrollOperationRangesPending(planes, operations) {
|
|
930
|
+
if (!operations?.length) return [];
|
|
931
|
+
const fallbackRows = new Set();
|
|
932
|
+
for (const plane of planes) {
|
|
933
|
+
const layer = planeLayers.get(plane);
|
|
934
|
+
if (!layer) continue;
|
|
935
|
+
for (const op of operations) {
|
|
936
|
+
const { startY, endY } = scrollOperationRange(op);
|
|
937
|
+
invalidateAndRepaintScrollRange(plane, layer, startY, endY);
|
|
938
|
+
addFallbackRows(fallbackRows, startY, endY);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return Array.from(fallbackRows).sort((a, b) => a - b);
|
|
942
|
+
}
|
|
943
|
+
function estimateSyncFlushWork(scope) {
|
|
944
|
+
const size = terminal.size();
|
|
945
|
+
const rowCount = scope.rows == null ? size.rows : scope.rows.length;
|
|
946
|
+
const planeCount = scope.planes.length || TERMINAL_RENDER_PLANES.length;
|
|
947
|
+
const cellWork = rowCount * size.cols * planeCount;
|
|
948
|
+
return {
|
|
949
|
+
rowCount,
|
|
950
|
+
planeCount,
|
|
951
|
+
cellWork
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
function estimateScrollOperationWork(planes, operations) {
|
|
955
|
+
const size = terminal.size();
|
|
956
|
+
const planeCount = planes.length || TERMINAL_RENDER_PLANES.length;
|
|
957
|
+
let rowCount = 0;
|
|
958
|
+
for (const op of operations ?? []) {
|
|
959
|
+
const { startY, endY } = scrollOperationRange(op);
|
|
960
|
+
rowCount += Math.max(0, endY - startY);
|
|
961
|
+
}
|
|
962
|
+
return {
|
|
963
|
+
rowCount,
|
|
964
|
+
planeCount,
|
|
965
|
+
cellWork: rowCount * size.cols * planeCount
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
function combineSyncFlushWork(repaintWork, scrollWork) {
|
|
969
|
+
return {
|
|
970
|
+
rowCount: Math.max(repaintWork.rowCount, scrollWork.rowCount),
|
|
971
|
+
planeCount: Math.max(repaintWork.planeCount, scrollWork.planeCount),
|
|
972
|
+
cellWork: repaintWork.cellWork + scrollWork.cellWork
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
function recordSyncFlushDecision(work, performed, deferredReason) {
|
|
976
|
+
syncFlushRequested++;
|
|
977
|
+
if (performed) syncFlushPerformed++;
|
|
978
|
+
else syncFlushDeferred++;
|
|
979
|
+
lastSyncFlushDecision = {
|
|
980
|
+
performed,
|
|
981
|
+
deferredReason,
|
|
982
|
+
rows: work.rowCount,
|
|
983
|
+
planes: work.planeCount,
|
|
984
|
+
cells: work.cellWork,
|
|
985
|
+
maxRows: syncFlushMaxRows,
|
|
986
|
+
maxCells: syncFlushCellBudget
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
function recordLargeSyncFlush(work) {
|
|
990
|
+
if (!globalThis.__VT_DEBUG_PERF__) return;
|
|
991
|
+
if (work.rowCount <= syncFlushMaxRows && work.cellWork <= syncFlushCellBudget) return;
|
|
992
|
+
const size = terminal.size();
|
|
993
|
+
const now = Date.now();
|
|
994
|
+
if (now - lastSyncFlushWarnAt < 1e3) return;
|
|
995
|
+
lastSyncFlushWarnAt = now;
|
|
996
|
+
console.warn(`[vue-tui] sync DOM flush request deferred to rAF: rows=${work.rowCount} maxRows=${syncFlushMaxRows} cols=${size.cols} planes=${work.planeCount} cells=${work.cellWork} maxCells=${syncFlushCellBudget}`);
|
|
997
|
+
}
|
|
998
|
+
function shouldSyncFlush(work) {
|
|
999
|
+
return work.rowCount <= syncFlushMaxRows && work.cellWork <= syncFlushCellBudget;
|
|
1000
|
+
}
|
|
1001
|
+
function scopeWillDrainAllPending(scope) {
|
|
1002
|
+
if (pendingTotalRowCount === 0) return true;
|
|
1003
|
+
if (scope.rows !== null && pendingTotalRowCount > scope.rows.length * scope.planes.length) return false;
|
|
1004
|
+
const scopePlanes = new Set(scope.planes);
|
|
1005
|
+
for (const plane of pendingRowCountsByPlane.keys()) if (!scopePlanes.has(plane)) return false;
|
|
1006
|
+
if (scope.rows === null) return true;
|
|
1007
|
+
const scopeRows = new Set(scope.rows);
|
|
1008
|
+
for (const [plane, rows] of pending) {
|
|
1009
|
+
if (!scopePlanes.has(plane)) return false;
|
|
1010
|
+
if (rows.size > scopeRows.size) return false;
|
|
1011
|
+
for (const y of rows) if (!scopeRows.has(y)) return false;
|
|
1012
|
+
}
|
|
1013
|
+
return true;
|
|
1014
|
+
}
|
|
1015
|
+
function flushPending(scope, options$1) {
|
|
1016
|
+
if (options$1?.clearRaf !== false) raf = 0;
|
|
1017
|
+
if (disposed) return;
|
|
1018
|
+
const startedAt = nowMs();
|
|
1019
|
+
const planesToFlush = scope?.planes ?? TERMINAL_RENDER_PLANES;
|
|
1020
|
+
let flushedRows = 0;
|
|
1021
|
+
const rowStats = createEmptyRowStats();
|
|
1022
|
+
const useRowKeyPrepass = shouldUseRowKeyPrepass();
|
|
1023
|
+
for (const plane of planesToFlush) {
|
|
1024
|
+
const layer = planeLayers.get(plane);
|
|
1025
|
+
const rows = pending.get(plane);
|
|
1026
|
+
if (!layer || !rows?.size) continue;
|
|
1027
|
+
const flushRow = (y) => {
|
|
1028
|
+
if (!rows.has(y)) return;
|
|
1029
|
+
const line = layer.lines[y];
|
|
1030
|
+
if (line) renderRow(layer.terminal, metrics, wideScaleX, y, line, rowStats, useRowKeyPrepass, palette, rendererLinkOptions, onAnchorClick, onAnchorPointerDown, rendererLinkVersion);
|
|
1031
|
+
deletePendingRow(plane, rows, y);
|
|
1032
|
+
flushedRows++;
|
|
1033
|
+
};
|
|
1034
|
+
if (scope?.rows == null) for (const y of rows) flushRow(y);
|
|
1035
|
+
else for (const y of scope.rows) flushRow(y);
|
|
1036
|
+
if (rows.size === 0) pending.delete(plane);
|
|
1037
|
+
}
|
|
1038
|
+
if (flushedRows > 0) {
|
|
1039
|
+
recordRowKeyPrepassAutoStats(rowStats);
|
|
1040
|
+
recordRowRenderStats(rowStats);
|
|
1041
|
+
rowKeyPrepassAutoReady = true;
|
|
1042
|
+
domFlushCount++;
|
|
1043
|
+
lastDomFlush = {
|
|
1044
|
+
mode: options$1?.mode ?? "deferred",
|
|
1045
|
+
startedAt,
|
|
1046
|
+
durationMs: nowMs() - startedAt,
|
|
1047
|
+
planeRows: flushedRows,
|
|
1048
|
+
planes: planesToFlush.length
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
if (pending.size > 0) scheduleRafIfNeeded();
|
|
1052
|
+
}
|
|
1053
|
+
function scheduleRafIfNeeded() {
|
|
1054
|
+
if (raf || disposed) return;
|
|
1055
|
+
raf = -1;
|
|
1056
|
+
const id = requestAnimationFrame(() => {
|
|
1057
|
+
flushPending(void 0, { mode: "deferred" });
|
|
1058
|
+
});
|
|
1059
|
+
if (raf === -1) raf = id;
|
|
1060
|
+
}
|
|
1061
|
+
const offCommit = terminal.on("commit", ({ dirtyRows, planes, sync, scrollOperations }) => {
|
|
1062
|
+
const activePlanes = planes?.length ? planes : TERMINAL_RENDER_PLANES;
|
|
1063
|
+
const dirtyCommitRows = dirtyRows === null ? null : [...dirtyRows];
|
|
1064
|
+
const initialSyncWork = estimateSyncFlushWork({
|
|
1065
|
+
planes: activePlanes,
|
|
1066
|
+
rows: dirtyCommitRows
|
|
1067
|
+
});
|
|
1068
|
+
const scrollWork = estimateScrollOperationWork(activePlanes, scrollOperations);
|
|
1069
|
+
const combinedSyncWork = combineSyncFlushWork(initialSyncWork, scrollWork);
|
|
1070
|
+
const shouldApplyScrollOperations = Boolean(sync) && shouldSyncFlush(combinedSyncWork) && canApplyScrollOperations(activePlanes, scrollOperations);
|
|
1071
|
+
const fallbackRows = shouldApplyScrollOperations ? [] : markScrollOperationRangesPending(activePlanes, scrollOperations);
|
|
1072
|
+
if (shouldApplyScrollOperations) applyScrollOperations(activePlanes, scrollOperations);
|
|
1073
|
+
const commitRows = unionRows(dirtyCommitRows, fallbackRows);
|
|
1074
|
+
if (dirtyRows === null) {
|
|
1075
|
+
const size = terminal.size();
|
|
1076
|
+
for (const plane of activePlanes) {
|
|
1077
|
+
let rows = pending.get(plane);
|
|
1078
|
+
if (!rows) {
|
|
1079
|
+
rows = new Set();
|
|
1080
|
+
pending.set(plane, rows);
|
|
1081
|
+
}
|
|
1082
|
+
for (let y = 0; y < size.rows; y++) addPendingRow(plane, rows, y);
|
|
1083
|
+
}
|
|
1084
|
+
} else for (const plane of activePlanes) {
|
|
1085
|
+
let rows = pending.get(plane);
|
|
1086
|
+
if (!rows) {
|
|
1087
|
+
rows = new Set();
|
|
1088
|
+
pending.set(plane, rows);
|
|
1089
|
+
}
|
|
1090
|
+
for (const y of commitRows ?? dirtyRows) addPendingRow(plane, rows, y);
|
|
1091
|
+
}
|
|
1092
|
+
if (sync) {
|
|
1093
|
+
const scope = {
|
|
1094
|
+
planes: activePlanes,
|
|
1095
|
+
rows: commitRows
|
|
1096
|
+
};
|
|
1097
|
+
const syncWork = estimateSyncFlushWork(scope);
|
|
1098
|
+
recordLargeSyncFlush(syncWork);
|
|
1099
|
+
if (!shouldSyncFlush(syncWork)) {
|
|
1100
|
+
recordSyncFlushDecision(syncWork, false, "budget");
|
|
1101
|
+
scheduleRafIfNeeded();
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
recordSyncFlushDecision(syncWork, true);
|
|
1105
|
+
const drainedAll = scopeWillDrainAllPending(scope);
|
|
1106
|
+
if (raf > 0 && drainedAll) {
|
|
1107
|
+
cancelAnimationFrame(raf);
|
|
1108
|
+
raf = 0;
|
|
1109
|
+
}
|
|
1110
|
+
flushPending(scope, {
|
|
1111
|
+
clearRaf: drainedAll,
|
|
1112
|
+
mode: "sync"
|
|
1113
|
+
});
|
|
1114
|
+
if (!drainedAll) scheduleRafIfNeeded();
|
|
1115
|
+
} else if (!raf) scheduleRafIfNeeded();
|
|
1116
|
+
});
|
|
1117
|
+
const offResize = terminal.on("resize", () => {
|
|
1118
|
+
refresh();
|
|
1119
|
+
});
|
|
1120
|
+
function updateTheme(next) {
|
|
1121
|
+
if (disposed) return;
|
|
1122
|
+
let changed = false;
|
|
1123
|
+
if ("palette" in next) {
|
|
1124
|
+
palette = next.palette ?? null;
|
|
1125
|
+
installAnsiPaletteCssVars(container, palette);
|
|
1126
|
+
changed = true;
|
|
1127
|
+
}
|
|
1128
|
+
if (changed) refresh();
|
|
1129
|
+
}
|
|
1130
|
+
function updateOptions(next) {
|
|
1131
|
+
if (disposed) return;
|
|
1132
|
+
if (Object.prototype.hasOwnProperty.call(next, "onLinkClick")) rendererOnLinkClick = next.onLinkClick;
|
|
1133
|
+
if (!Object.prototype.hasOwnProperty.call(next, "links")) return;
|
|
1134
|
+
const normalized = normalizeLinkOptions(next.links);
|
|
1135
|
+
const nextKey = linkOptionsKey(normalized);
|
|
1136
|
+
rendererLinkOptions = normalized;
|
|
1137
|
+
if (nextKey === rendererLinkOptionsKey) return;
|
|
1138
|
+
rendererLinkOptionsKey = nextKey;
|
|
1139
|
+
rendererLinkVersion++;
|
|
1140
|
+
refresh();
|
|
1141
|
+
}
|
|
1142
|
+
refresh();
|
|
1143
|
+
terminal.commit();
|
|
1144
|
+
return {
|
|
1145
|
+
container,
|
|
1146
|
+
capabilities,
|
|
1147
|
+
debugStats,
|
|
1148
|
+
get metrics() {
|
|
1149
|
+
return metrics;
|
|
1150
|
+
},
|
|
1151
|
+
refresh,
|
|
1152
|
+
updateTheme,
|
|
1153
|
+
updateOptions,
|
|
1154
|
+
dispose() {
|
|
1155
|
+
disposed = true;
|
|
1156
|
+
offCommit();
|
|
1157
|
+
offResize();
|
|
1158
|
+
cancelPendingRaf();
|
|
1159
|
+
clearPendingRows();
|
|
1160
|
+
planeLayers.clear();
|
|
1161
|
+
container.replaceChildren();
|
|
1162
|
+
},
|
|
1163
|
+
setPlaneOffset(plane, offsetPx) {
|
|
1164
|
+
applyPlaneOffset(plane, offsetPx);
|
|
1165
|
+
},
|
|
1166
|
+
setPlaneViewport(plane, viewport) {
|
|
1167
|
+
applyPlaneViewport(plane, viewport);
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
//#endregion
|
|
1173
|
+
export { DOM_RENDERER_CAPABILITIES, createDomRenderer };
|