@simon_he/vue-tui 1.0.1 → 1.1.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.
Files changed (128) hide show
  1. package/README.md +139 -46
  2. package/dist/TMermaidText-frVr8Ysb.js +885 -0
  3. package/dist/{TSelect-HzILuti9.js → TSelect-tJ87rKPM.js} +59 -242
  4. package/dist/{TTree-ChOot2Bh.js → TTree-CGdE-CYu.js} +138 -102
  5. package/dist/agent/mermaid.cjs +2 -0
  6. package/dist/agent/mermaid.d.cts +2 -0
  7. package/dist/agent/mermaid.d.ts +2 -0
  8. package/dist/agent/mermaid.js +1 -0
  9. package/dist/agent.cjs +31188 -24907
  10. package/dist/agent.d.cts +11 -1
  11. package/dist/agent.d.ts +11 -1
  12. package/dist/agent.js +516 -11
  13. package/dist/{block-source-CKUOnkgT.js → block-source-CrlmaBM5.js} +1039 -66
  14. package/dist/cli/input.d.ts +8 -0
  15. package/dist/cli/stdin-buffer.d.ts +1 -1
  16. package/dist/cli/video/ffmpeg-frame-source.d.ts +10 -0
  17. package/dist/cli/video/png-frame-parser.d.ts +4 -0
  18. package/dist/cli/video/raw-frame-parser.d.ts +1 -0
  19. package/dist/cli/video/yt-dlp-frame-source.d.ts +11 -0
  20. package/dist/cli.cjs +5470 -6176
  21. package/dist/cli.d.cts +6 -2
  22. package/dist/cli.d.ts +6 -2
  23. package/dist/cli.js +3747 -350
  24. package/dist/core/buffer/buffer.d.ts +2 -2
  25. package/dist/core/buffer/eaw-ranges-unicode-17.d.ts +14 -0
  26. package/dist/core/perf/instrumentation-noop.d.ts +48 -0
  27. package/dist/core/perf/instrumentation.d.ts +136 -0
  28. package/dist/core/terminal/create-terminal.d.ts +1 -0
  29. package/dist/core/types.d.ts +0 -4
  30. package/dist/core.cjs +851 -1462
  31. package/dist/core.js +4 -5
  32. package/dist/create-terminal-app.d.ts +24 -1
  33. package/dist/debug-logger-CmsGI4of.js +74 -0
  34. package/dist/{dom-renderer-BqGijQID.js → dom-renderer-DXx8fWzD.js} +2 -3
  35. package/dist/experimental/3d/bun/contributor-avatar-atlas.d.ts +12 -0
  36. package/dist/experimental/3d/bun/png.d.ts +1 -0
  37. package/dist/experimental/3d/bun/terminal-badge-scene.d.ts +11 -0
  38. package/dist/experimental/3d/bun/terminal-badge.wgsl.d.ts +1 -0
  39. package/dist/experimental/3d/bun/webgpu-renderer.d.ts +26 -0
  40. package/dist/experimental/3d/bun.cjs +7271 -0
  41. package/dist/experimental/3d/bun.d.cts +4 -0
  42. package/dist/experimental/3d/bun.d.ts +4 -0
  43. package/dist/experimental/3d/bun.js +7390 -0
  44. package/dist/experimental/video/node.cjs +497 -0
  45. package/dist/experimental/video/node.d.cts +4 -0
  46. package/dist/experimental/video/node.d.ts +4 -0
  47. package/dist/experimental/video/node.js +610 -0
  48. package/dist/experimental.cjs +6876 -6192
  49. package/dist/experimental.d.cts +7 -0
  50. package/dist/experimental.d.ts +7 -0
  51. package/dist/experimental.js +3005 -9
  52. package/dist/frame-perf-CHpWhbC4.js +111 -0
  53. package/dist/{create-terminal-C2oiWFJU.js → hyperlink-_1FAj7Wq.js} +455 -19
  54. package/dist/index.cjs +5025 -10332
  55. package/dist/index.js +14 -13
  56. package/dist/{linkify-D_gzn9Xq.js → linkify-D68T7rdW.js} +193 -3
  57. package/dist/markdown.cjs +21295 -12691
  58. package/dist/markdown.d.cts +1 -1
  59. package/dist/markdown.d.ts +1 -1
  60. package/dist/markdown.js +211 -51
  61. package/dist/mermaid-BNhM1jAZ.js +123 -0
  62. package/dist/mermaid.cjs +1707 -0
  63. package/dist/mermaid.d.cts +3 -0
  64. package/dist/mermaid.d.ts +3 -0
  65. package/dist/mermaid.js +6 -0
  66. package/dist/observability/frame-perf-store.d.ts +2 -1
  67. package/dist/observability/frame-perf.d.ts +33 -0
  68. package/dist/observability.cjs +106 -107
  69. package/dist/observability.d.cts +2 -2
  70. package/dist/observability.d.ts +2 -2
  71. package/dist/observability.js +3 -3
  72. package/dist/{wheel-scroll-vBRaQ7p-.js → rect-DUpeDfBg.js} +253 -117
  73. package/dist/renderer/cli/index.d.ts +1 -1
  74. package/dist/renderer/cli/stdout-metrics.d.ts +10 -0
  75. package/dist/renderer/cli/stdout-renderer.d.ts +53 -7
  76. package/dist/renderer/terminal-graphic-render-queue.d.ts +50 -0
  77. package/dist/renderer/terminal-graphics-trace.d.ts +53 -0
  78. package/dist/renderer/terminal-graphics.d.ts +147 -0
  79. package/dist/renderer-dom.cjs +364 -903
  80. package/dist/renderer-dom.js +4 -5
  81. package/dist/runtime/index.d.ts +2 -0
  82. package/dist/{runtime-rfcZCyJq.js → runtime-Byf8XXOT.js} +28 -15
  83. package/dist/runtime.cjs +427 -889
  84. package/dist/runtime.js +2 -2
  85. package/dist/terminal-graphics-Bbe83wSr.js +597 -0
  86. package/dist/{terminal-selection-DBinO6p5.js → terminal-selection-4W1SJj4c.js} +4 -1
  87. package/dist/{tlog-theme-CvLFNMwk.js → tlog-theme-Bul2Na3u.js} +1237 -12
  88. package/dist/{trace-CjzYeIxN.js → trace-Bh8WnoMo.js} +5 -0
  89. package/dist/vue/agent/create-png-terminal-graphic-renderer.d.ts +19 -0
  90. package/dist/vue/components/T3DViewport.d.ts +473 -0
  91. package/dist/vue/components/TAgentTerminalGraphic.d.ts +494 -0
  92. package/dist/vue/components/TCharts.d.ts +1121 -0
  93. package/dist/vue/components/TFeedback.d.ts +1 -1
  94. package/dist/vue/components/TFlex.d.ts +389 -0
  95. package/dist/vue/components/TForm.d.ts +4 -4
  96. package/dist/vue/components/TLogSearchBar.d.ts +3 -3
  97. package/dist/vue/components/TLogSearchPager.d.ts +2 -2
  98. package/dist/vue/components/TMarkdownText.d.ts +162 -4
  99. package/dist/vue/components/TMermaidText.d.ts +900 -0
  100. package/dist/vue/components/TVideo.d.ts +291 -0
  101. package/dist/vue/components/TVirtualList.d.ts +9 -0
  102. package/dist/vue/components/TVirtualMarkdown.d.ts +170 -9
  103. package/dist/vue/components/TVirtualRows.d.ts +9 -0
  104. package/dist/vue/components/TerminalProvider.d.ts +2 -0
  105. package/dist/vue/context.d.ts +24 -0
  106. package/dist/vue/index.d.ts +5 -0
  107. package/dist/vue/layout/flex.d.ts +67 -0
  108. package/dist/vue/markdown/ast.d.ts +5 -2
  109. package/dist/vue/markdown/document.d.ts +5 -0
  110. package/dist/vue/markdown/image-actions.d.ts +14 -0
  111. package/dist/vue/markdown/image.d.ts +11 -0
  112. package/dist/vue/markdown/link-actions.d.ts +14 -0
  113. package/dist/vue/markdown/math-actions.d.ts +14 -0
  114. package/dist/vue/markdown/math.d.ts +6 -0
  115. package/dist/vue/markdown/render.d.ts +28 -0
  116. package/dist/vue/markdown/types.d.ts +78 -0
  117. package/dist/vue/mermaid/beautiful-mermaid.d.ts +1436 -0
  118. package/dist/vue/render/render-manager.d.ts +7 -0
  119. package/dist/vue/video/types.d.ts +39 -0
  120. package/dist/vue.cjs +7215 -12430
  121. package/dist/vue.js +704 -38
  122. package/dist/wheel-scroll-DASkEZr-.js +106 -0
  123. package/dist/{hyperlink-Bn02UXJb.js → width-GqllnV8C.js} +166 -111
  124. package/package.json +141 -18
  125. package/dist/frame-perf-C6Xfg85O.js +0 -24
  126. package/dist/sgr-BmzxEjAO.js +0 -304
  127. /package/dist/{ansi-palette-CNXqFciC.js → ansi-palette-CH6jc2Z7.js} +0 -0
  128. /package/dist/{path-suggest-BNqcuE3-.js → path-suggest-Dndu4dTi.js} +0 -0
@@ -0,0 +1,111 @@
1
+ //#region src/observability/frame-perf.ts
2
+ const FRAME_PERF_REASON_PRIORITY = {
3
+ unknown: 0,
4
+ manual: 1,
5
+ data: 2,
6
+ selection: 3,
7
+ stream: 4,
8
+ resize: 5,
9
+ scroll: 6,
10
+ input: 7
11
+ };
12
+ const EMPTY_METRIC_STATS = Object.freeze({
13
+ avg: 0,
14
+ max: 0,
15
+ min: 0
16
+ });
17
+ function metricStats(values) {
18
+ if (!values.length) return EMPTY_METRIC_STATS;
19
+ let sum = 0;
20
+ let max = Number.NEGATIVE_INFINITY;
21
+ let min = Number.POSITIVE_INFINITY;
22
+ for (const value of values) {
23
+ sum += value;
24
+ if (value > max) max = value;
25
+ if (value < min) min = value;
26
+ }
27
+ return {
28
+ avg: sum / values.length,
29
+ max,
30
+ min
31
+ };
32
+ }
33
+ function summarizeFramePerf(samples) {
34
+ const durationMs = [];
35
+ const renderManagerMs = [];
36
+ const commitMs = [];
37
+ const domFlushMs = [];
38
+ const stdoutFlushMs = [];
39
+ const dirtyRows = [];
40
+ const scannedNodes = [];
41
+ const paintedNodes = [];
42
+ const reasons = {};
43
+ const activePlanes = {};
44
+ let fullDirtyFrames = 0;
45
+ let coalescedInvalidates = 0;
46
+ let coalescedFrameTasks = 0;
47
+ let droppedUpdates = 0;
48
+ let maxQueueDepth = 0;
49
+ let rowBucketFallbacks = 0;
50
+ for (const sample of samples) {
51
+ durationMs.push(sample.durationMs);
52
+ renderManagerMs.push(sample.renderManagerMs);
53
+ commitMs.push(sample.commitMs);
54
+ scannedNodes.push(sample.scannedNodes);
55
+ paintedNodes.push(sample.paintedNodes);
56
+ if (sample.domFlushMs != null) domFlushMs.push(sample.domFlushMs);
57
+ if (sample.stdoutFlushMs != null) stdoutFlushMs.push(sample.stdoutFlushMs);
58
+ if (sample.dirtyRows == null) fullDirtyFrames++;
59
+ else dirtyRows.push(sample.dirtyRows);
60
+ reasons[sample.reason] = (reasons[sample.reason] ?? 0) + 1;
61
+ const planes = sample.activePlanes;
62
+ if (!planes) activePlanes.all = (activePlanes.all ?? 0) + 1;
63
+ else for (const plane of planes) activePlanes[plane] = (activePlanes[plane] ?? 0) + 1;
64
+ coalescedInvalidates += sample.coalescedInvalidates;
65
+ coalescedFrameTasks += sample.coalescedFrameTasks;
66
+ droppedUpdates += sample.droppedUpdates;
67
+ if (sample.queueDepth > maxQueueDepth) maxQueueDepth = sample.queueDepth;
68
+ rowBucketFallbacks += sample.rowBucketFallbacks?.length ?? 0;
69
+ }
70
+ const first = samples[0] ?? null;
71
+ const latest = samples[samples.length - 1] ?? null;
72
+ return {
73
+ frames: samples.length,
74
+ firstFrameId: first?.frameId ?? null,
75
+ latestFrameId: latest?.frameId ?? null,
76
+ windowMs: first && latest ? Math.max(0, latest.startedAt - first.startedAt) : 0,
77
+ durationMs: metricStats(durationMs),
78
+ renderManagerMs: metricStats(renderManagerMs),
79
+ commitMs: metricStats(commitMs),
80
+ domFlushMs: metricStats(domFlushMs),
81
+ stdoutFlushMs: metricStats(stdoutFlushMs),
82
+ dirtyRows: {
83
+ avg: dirtyRows.length ? dirtyRows.reduce((sum, value) => sum + value, 0) / dirtyRows.length : 0,
84
+ max: dirtyRows.length ? Math.max(...dirtyRows) : 0,
85
+ sampledFrames: dirtyRows.length,
86
+ fullFrames: fullDirtyFrames
87
+ },
88
+ scannedNodes: metricStats(scannedNodes),
89
+ paintedNodes: metricStats(paintedNodes),
90
+ coalescedInvalidates,
91
+ coalescedFrameTasks,
92
+ droppedUpdates,
93
+ maxQueueDepth,
94
+ rowBucketFallbacks,
95
+ reasons,
96
+ activePlanes
97
+ };
98
+ }
99
+ function framePerfNow() {
100
+ const p = globalThis.performance;
101
+ if (p && typeof p.now === "function") return p.now();
102
+ return Date.now();
103
+ }
104
+ function mergeFramePerfReason(prev, next) {
105
+ const a = prev ?? "unknown";
106
+ const b = next ?? "unknown";
107
+ return FRAME_PERF_REASON_PRIORITY[b] > FRAME_PERF_REASON_PRIORITY[a] ? b : a;
108
+ }
109
+
110
+ //#endregion
111
+ export { framePerfNow, mergeFramePerfReason, summarizeFramePerf };
@@ -1,5 +1,4 @@
1
- import { charCellWidth, segmentedGraphemes } from "./hyperlink-Bn02UXJb.js";
2
- import { parseAnsiSgr } from "./sgr-BmzxEjAO.js";
1
+ import { charCellWidth, segmentedGraphemes } from "./width-GqllnV8C.js";
3
2
 
4
3
  //#region src/core/render-plane.ts
5
4
  const TERMINAL_RENDER_PLANES = [
@@ -9,6 +8,309 @@ const TERMINAL_RENDER_PLANES = [
9
8
  "overlay"
10
9
  ];
11
10
 
11
+ //#endregion
12
+ //#region src/core/ansi/sgr.ts
13
+ const ESC = "\x1B";
14
+ function ansi16ToColorName(code) {
15
+ switch (code) {
16
+ case 30: return "black";
17
+ case 31: return "red";
18
+ case 32: return "green";
19
+ case 33: return "yellow";
20
+ case 34: return "blue";
21
+ case 35: return "magenta";
22
+ case 36: return "cyan";
23
+ case 37: return "white";
24
+ case 90: return "blackBright";
25
+ case 91: return "redBright";
26
+ case 92: return "greenBright";
27
+ case 93: return "yellowBright";
28
+ case 94: return "blueBright";
29
+ case 95: return "magentaBright";
30
+ case 96: return "cyanBright";
31
+ case 97: return "whiteBright";
32
+ default: return void 0;
33
+ }
34
+ }
35
+ function ansi16BgToColorName(code) {
36
+ const fgCode = code >= 40 && code <= 47 ? code - 10 : code >= 100 && code <= 107 ? code - 10 : null;
37
+ if (fgCode == null) return void 0;
38
+ return ansi16ToColorName(fgCode);
39
+ }
40
+ const ANSI16_RGB = {
41
+ black: {
42
+ r: 0,
43
+ g: 0,
44
+ b: 0
45
+ },
46
+ red: {
47
+ r: 201,
48
+ g: 27,
49
+ b: 0
50
+ },
51
+ green: {
52
+ r: 0,
53
+ g: 194,
54
+ b: 0
55
+ },
56
+ yellow: {
57
+ r: 199,
58
+ g: 196,
59
+ b: 0
60
+ },
61
+ blue: {
62
+ r: 2,
63
+ g: 37,
64
+ b: 199
65
+ },
66
+ magenta: {
67
+ r: 201,
68
+ g: 48,
69
+ b: 199
70
+ },
71
+ cyan: {
72
+ r: 0,
73
+ g: 197,
74
+ b: 199
75
+ },
76
+ white: {
77
+ r: 199,
78
+ g: 199,
79
+ b: 199
80
+ },
81
+ blackBright: {
82
+ r: 104,
83
+ g: 104,
84
+ b: 104
85
+ },
86
+ redBright: {
87
+ r: 255,
88
+ g: 110,
89
+ b: 103
90
+ },
91
+ greenBright: {
92
+ r: 95,
93
+ g: 250,
94
+ b: 104
95
+ },
96
+ yellowBright: {
97
+ r: 255,
98
+ g: 252,
99
+ b: 103
100
+ },
101
+ blueBright: {
102
+ r: 104,
103
+ g: 113,
104
+ b: 255
105
+ },
106
+ magentaBright: {
107
+ r: 255,
108
+ g: 118,
109
+ b: 255
110
+ },
111
+ cyanBright: {
112
+ r: 95,
113
+ g: 253,
114
+ b: 255
115
+ },
116
+ whiteBright: {
117
+ r: 255,
118
+ g: 255,
119
+ b: 255
120
+ }
121
+ };
122
+ function nearestAnsi16(r, g, b) {
123
+ let best = "white";
124
+ let bestDist = Number.POSITIVE_INFINITY;
125
+ for (const [name, rgb] of Object.entries(ANSI16_RGB)) {
126
+ const dr = r - rgb.r;
127
+ const dg = g - rgb.g;
128
+ const db = b - rgb.b;
129
+ const d = dr * dr + dg * dg + db * db;
130
+ if (d < bestDist) {
131
+ bestDist = d;
132
+ best = name;
133
+ }
134
+ }
135
+ return best;
136
+ }
137
+ function ansi256ToRgb(index) {
138
+ const n = Math.max(0, Math.min(255, Math.trunc(index)));
139
+ if (n < 16) {
140
+ const map = [
141
+ "black",
142
+ "red",
143
+ "green",
144
+ "yellow",
145
+ "blue",
146
+ "magenta",
147
+ "cyan",
148
+ "white",
149
+ "blackBright",
150
+ "redBright",
151
+ "greenBright",
152
+ "yellowBright",
153
+ "blueBright",
154
+ "magentaBright",
155
+ "cyanBright",
156
+ "whiteBright"
157
+ ];
158
+ const name = map[n] ?? "white";
159
+ return ANSI16_RGB[name];
160
+ }
161
+ if (n >= 232) {
162
+ const c = 8 + (n - 232) * 10;
163
+ return {
164
+ r: c,
165
+ g: c,
166
+ b: c
167
+ };
168
+ }
169
+ const i = n - 16;
170
+ const rr = Math.floor(i / 36);
171
+ const gg = Math.floor(i % 36 / 6);
172
+ const bb = i % 6;
173
+ const toComponent = (v) => v === 0 ? 0 : 55 + v * 40;
174
+ return {
175
+ r: toComponent(rr),
176
+ g: toComponent(gg),
177
+ b: toComponent(bb)
178
+ };
179
+ }
180
+ function applyAnsiSgrStyle(current, codes) {
181
+ let next = { ...current };
182
+ for (let i = 0; i < codes.length; i++) {
183
+ const code = codes[i];
184
+ if (code === 0) {
185
+ next = {};
186
+ continue;
187
+ }
188
+ if (code === 1) next = {
189
+ ...next,
190
+ bold: true
191
+ };
192
+ else if (code === 2) next = {
193
+ ...next,
194
+ dim: true
195
+ };
196
+ else if (code === 3) next = {
197
+ ...next,
198
+ italic: true
199
+ };
200
+ else if (code === 4) next = {
201
+ ...next,
202
+ underline: true
203
+ };
204
+ else if (code === 7) next = {
205
+ ...next,
206
+ inverse: true
207
+ };
208
+ else if (code === 22) next = {
209
+ ...next,
210
+ bold: false,
211
+ dim: false
212
+ };
213
+ else if (code === 23) next = {
214
+ ...next,
215
+ italic: false
216
+ };
217
+ else if (code === 24) next = {
218
+ ...next,
219
+ underline: false
220
+ };
221
+ else if (code === 27) next = {
222
+ ...next,
223
+ inverse: false
224
+ };
225
+ else if (code === 39) next = {
226
+ ...next,
227
+ fg: void 0
228
+ };
229
+ else if (code === 49) next = {
230
+ ...next,
231
+ bg: void 0
232
+ };
233
+ else if (code >= 30 && code <= 37 || code >= 90 && code <= 97) {
234
+ const fg = ansi16ToColorName(code);
235
+ next = {
236
+ ...next,
237
+ fg: fg ?? next.fg
238
+ };
239
+ } else if (code >= 40 && code <= 47 || code >= 100 && code <= 107) {
240
+ const bg = ansi16BgToColorName(code);
241
+ next = {
242
+ ...next,
243
+ bg: bg ?? next.bg
244
+ };
245
+ } else if (code === 38 || code === 48) {
246
+ const mode = codes[i + 1];
247
+ if (mode === 5) {
248
+ const idx = codes[i + 2];
249
+ if (idx != null) {
250
+ const { r, g, b } = ansi256ToRgb(idx);
251
+ const name = nearestAnsi16(r, g, b);
252
+ next = code === 38 ? {
253
+ ...next,
254
+ fg: name
255
+ } : {
256
+ ...next,
257
+ bg: name
258
+ };
259
+ }
260
+ i += 2;
261
+ } else if (mode === 2) {
262
+ const r = codes[i + 2];
263
+ const g = codes[i + 3];
264
+ const b = codes[i + 4];
265
+ if (r != null && g != null && b != null) {
266
+ const name = nearestAnsi16(r, g, b);
267
+ next = code === 38 ? {
268
+ ...next,
269
+ fg: name
270
+ } : {
271
+ ...next,
272
+ bg: name
273
+ };
274
+ }
275
+ i += 4;
276
+ }
277
+ }
278
+ }
279
+ return next;
280
+ }
281
+ function parseAnsiSgr(input, baseStyle = {}) {
282
+ const segments = [];
283
+ let style = { ...baseStyle };
284
+ let lastIndex = 0;
285
+ for (let i = 0; i < input.length; i++) {
286
+ if (input[i] !== ESC || input[i + 1] !== "[") continue;
287
+ let j = i + 2;
288
+ while (j < input.length) {
289
+ const c = input.charCodeAt(j);
290
+ if (c >= 48 && c <= 57 || c === 59) {
291
+ j++;
292
+ continue;
293
+ }
294
+ break;
295
+ }
296
+ if (j >= input.length || input[j] !== "m") continue;
297
+ if (i > lastIndex) segments.push({
298
+ text: input.slice(lastIndex, i),
299
+ style
300
+ });
301
+ const body = input.slice(i + 2, j);
302
+ const codes = body.split(";").filter(Boolean).map((n) => Number.parseInt(n, 10)).filter((n) => Number.isFinite(n));
303
+ style = applyAnsiSgrStyle(style, codes.length ? codes : [0]);
304
+ lastIndex = j + 1;
305
+ i = j;
306
+ }
307
+ if (lastIndex < input.length) segments.push({
308
+ text: input.slice(lastIndex),
309
+ style
310
+ });
311
+ return segments;
312
+ }
313
+
12
314
  //#endregion
13
315
  //#region src/core/buffer/buffer.ts
14
316
  const DEFAULT_STYLE = Object.freeze({});
@@ -106,14 +408,14 @@ function clamp(n, min, max) {
106
408
  return Math.max(min, Math.min(max, n));
107
409
  }
108
410
  /**
109
- * Register a fingerprint function on the buffer. This enables SoA fingerprint
411
+ * Register or clear a fingerprint function on the buffer. This enables SoA fingerprint
110
412
  * pre-computation: cell writes will update fingerprints inline, and the renderer
111
413
  * can read them via getRowFingerprints() instead of per-cell hash computation.
112
414
  */
113
415
  function setFingerprintFn(buffer, fn) {
114
416
  buffer.fingerprintFn = fn;
115
417
  const len = buffer.rows * buffer.cols;
116
- if (!len) {
418
+ if (!fn || !len) {
117
419
  buffer.soaFingerprints = null;
118
420
  return;
119
421
  }
@@ -134,7 +436,10 @@ function setFingerprintFn(buffer, fn) {
134
436
  */
135
437
  function getRowFingerprints(buffer, y) {
136
438
  if (!buffer.soaFingerprints) return null;
137
- const physY = physicalRowIndex(buffer, y);
439
+ if (buffer.rows <= 0 || buffer.cols <= 0) return null;
440
+ const yy = Math.floor(y);
441
+ if (!Number.isFinite(yy) || yy < 0 || yy >= buffer.rows) return null;
442
+ const physY = physicalRowIndex(buffer, yy);
138
443
  const base = physY * buffer.cols;
139
444
  return buffer.soaFingerprints.subarray(base, base + buffer.cols);
140
445
  }
@@ -424,9 +729,12 @@ function resizeBuffer(buffer, cols, rows) {
424
729
  const nextCols = Math.max(0, Math.floor(cols));
425
730
  const nextRows = Math.max(0, Math.floor(rows));
426
731
  if (nextCols === buffer.cols && nextRows === buffer.rows) return;
732
+ const prevRows = buffer.rows;
733
+ const prevDirtyAll = buffer.dirtyAll;
734
+ const prevDirtyBits = buffer.dirtyBits;
427
735
  const blank = createBlankCell();
428
736
  const nextGrid = Array.from({ length: nextRows }, () => Array.from({ length: nextCols }, () => blank));
429
- const copyRows = Math.min(buffer.rows, nextRows);
737
+ const copyRows = Math.min(prevRows, nextRows);
430
738
  const copyCols = Math.min(buffer.cols, nextCols);
431
739
  for (let y = 0; y < copyRows; y++) {
432
740
  const src = getBufferRow(buffer, y);
@@ -450,10 +758,21 @@ function resizeBuffer(buffer, cols, rows) {
450
758
  buffer.cols = nextCols;
451
759
  buffer.rows = nextRows;
452
760
  buffer.dirtyBits = new Uint8Array(nextRows);
453
- buffer.dirtyCount = nextRows || 0;
454
- buffer.dirtyMin = nextRows ? 0 : Number.POSITIVE_INFINITY;
455
- buffer.dirtyMax = nextRows ? nextRows - 1 : -1;
456
- buffer.dirtyAll = nextRows > 0;
761
+ buffer.dirtyCount = 0;
762
+ buffer.dirtyMin = Number.POSITIVE_INFINITY;
763
+ buffer.dirtyMax = -1;
764
+ buffer.dirtyAll = prevDirtyAll && nextRows > 0;
765
+ if (buffer.dirtyAll) {
766
+ buffer.dirtyCount = nextRows;
767
+ buffer.dirtyMin = 0;
768
+ buffer.dirtyMax = nextRows - 1;
769
+ } else for (let y = 0; y < copyRows; y++) {
770
+ if (prevDirtyBits[y] !== 1) continue;
771
+ buffer.dirtyBits[y] = 1;
772
+ buffer.dirtyCount++;
773
+ if (y < buffer.dirtyMin) buffer.dirtyMin = y;
774
+ if (y > buffer.dirtyMax) buffer.dirtyMax = y;
775
+ }
457
776
  buffer.cursorX = clamp(buffer.cursorX, 0, Math.max(0, nextCols));
458
777
  buffer.cursorY = clamp(buffer.cursorY, 0, Math.max(0, nextRows - 1));
459
778
  if (buffer.fingerprintFn) {
@@ -526,6 +845,10 @@ function mergePlanes(prev, next) {
526
845
  return Array.from(merged);
527
846
  }
528
847
  const TERMINAL_PLANE_INTERNALS = Symbol.for("@simon_he/vue-tui:v1:terminal-plane-internals");
848
+ const TERMINAL_ROOT = Symbol.for("@simon_he/vue-tui:v1:terminal-root");
849
+ function getRootTerminal(terminal) {
850
+ return terminal[TERMINAL_ROOT] ?? terminal;
851
+ }
529
852
  function createCoverage(cols, rows) {
530
853
  return Array.from({ length: rows }, () => new Uint8Array(cols));
531
854
  }
@@ -609,6 +932,8 @@ function markPlaneCoverageRange(state, x0, y0, x1, y1, value) {
609
932
  }
610
933
  }
611
934
  function markPlaneCoverageForPut(state, x, y, ch, prevCell) {
935
+ if (!Number.isSafeInteger(x) || !Number.isSafeInteger(y)) return;
936
+ if (x < 0 || x >= state.buffer.cols || y < 0 || y >= state.buffer.rows) return;
612
937
  const row = state.coverage[y];
613
938
  if (!row) return;
614
939
  if (prevCell?.continuation && x > 0) row[x - 1] = 1;
@@ -724,10 +1049,14 @@ function createTerminal(opts) {
724
1049
  }
725
1050
  function putForPlane(plane, x, y, ch, style) {
726
1051
  const state = getPlaneState(plane);
727
- if (y < 0 || y >= state.buffer.rows || x < 0 || x >= state.buffer.cols) return;
728
- const prevCell = getBufferCell(state.buffer, x, y);
729
- putCell(state.buffer, x, y, ch, style);
730
- markPlaneCoverageForPut(state, x, y, ch, prevCell);
1052
+ if (typeof x !== "number" || typeof y !== "number") return;
1053
+ const cellX = Math.floor(x);
1054
+ const cellY = Math.floor(y);
1055
+ if (!Number.isFinite(cellX) || !Number.isFinite(cellY)) return;
1056
+ if (cellY < 0 || cellY >= state.buffer.rows || cellX < 0 || cellX >= state.buffer.cols) return;
1057
+ const prevCell = getBufferCell(state.buffer, cellX, cellY);
1058
+ putCell(state.buffer, cellX, cellY, ch, style);
1059
+ markPlaneCoverageForPut(state, cellX, cellY, ch, prevCell);
731
1060
  }
732
1061
  function writeAtForPlane(plane, text, x, y, style) {
733
1062
  const state = getPlaneState(plane);
@@ -1219,13 +1548,18 @@ function createTerminal(opts) {
1219
1548
  },
1220
1549
  setFingerprintFn(fn) {
1221
1550
  assertNotDisposed();
1222
- base.setFingerprintFn(fn);
1551
+ const state = getPlaneState(plane);
1552
+ setFingerprintFn(state.buffer, fn);
1223
1553
  },
1224
1554
  getRowFingerprints(y) {
1225
1555
  assertNotDisposed();
1226
- return base.getRowFingerprints(y);
1556
+ const yy = Math.floor(y);
1557
+ const state = getPlaneState(plane);
1558
+ if (!Number.isFinite(yy) || yy < 0 || yy >= state.buffer.rows) throw new RangeError("Row out of bounds");
1559
+ return getRowFingerprints(state.buffer, yy);
1227
1560
  }
1228
1561
  };
1562
+ api[TERMINAL_ROOT] = base;
1229
1563
  planeTerminals.set(plane, api);
1230
1564
  return api;
1231
1565
  }
@@ -1307,7 +1641,7 @@ function createTerminal(opts) {
1307
1641
  const rows = collectAndClearDirtyRows(state.buffer);
1308
1642
  if (rows === null) {
1309
1643
  composeAllRows = true;
1310
- break;
1644
+ continue;
1311
1645
  }
1312
1646
  for (const y of rows) dirtyRows.add(y);
1313
1647
  }
@@ -1397,7 +1731,9 @@ function createTerminal(opts) {
1397
1731
  },
1398
1732
  getRowFingerprints(y) {
1399
1733
  assertNotDisposed();
1400
- return getRowFingerprints(compositeBuffer, y);
1734
+ const yy = Math.floor(y);
1735
+ if (!Number.isFinite(yy) || yy < 0 || yy >= compositeBuffer.rows) throw new RangeError("Row out of bounds");
1736
+ return getRowFingerprints(compositeBuffer, yy);
1401
1737
  }
1402
1738
  };
1403
1739
  base[TERMINAL_PLANE_INTERNALS] = {
@@ -1427,8 +1763,108 @@ function createTerminal(opts) {
1427
1763
  scrollRowsForPlane(plane, startY, endY, lines);
1428
1764
  }
1429
1765
  };
1766
+ base[TERMINAL_ROOT] = base;
1430
1767
  return base;
1431
1768
  }
1432
1769
 
1433
1770
  //#endregion
1434
- export { TERMINAL_RENDER_PLANES, createTerminal, getPlaneTerminal, resetPlaneRowsForRender, scrollPlaneRows };
1771
+ //#region src/utils/url-safety.ts
1772
+ const ENCODED_ASCII_CONTROL_RE = /%(?:0[0-9a-f]|1[0-9a-f]|7f)/i;
1773
+ function hasControlCodePoint(value) {
1774
+ for (let i = 0; i < value.length; i++) {
1775
+ const code = value.charCodeAt(i);
1776
+ if (code <= 31 || code >= 127 && code <= 159) return true;
1777
+ }
1778
+ return false;
1779
+ }
1780
+ function hasEncodedControl(value) {
1781
+ let current = String(value ?? "");
1782
+ for (let depth = 0; depth < 3; depth++) {
1783
+ if (ENCODED_ASCII_CONTROL_RE.test(current)) return true;
1784
+ let decoded;
1785
+ try {
1786
+ decoded = decodeURIComponent(current);
1787
+ } catch {
1788
+ return true;
1789
+ }
1790
+ if (hasControlCodePoint(decoded)) return true;
1791
+ if (decoded === current) return false;
1792
+ current = decoded;
1793
+ }
1794
+ return ENCODED_ASCII_CONTROL_RE.test(current) || hasControlCodePoint(current);
1795
+ }
1796
+
1797
+ //#endregion
1798
+ //#region src/core/hyperlink.ts
1799
+ const SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
1800
+ const BLOCKED_SCHEME_RE = /^(?:javascript|data|vbscript):/i;
1801
+ const SAFE_RELATIVE_HREF_RE = /^(?:[A-Za-z0-9._~!$&()*+,;=:@/?#-]|%[0-9A-Fa-f]{2})+$/u;
1802
+ function normalizeRawHref(value) {
1803
+ if (typeof value !== "string") return null;
1804
+ const raw = value.trim();
1805
+ if (!raw) return null;
1806
+ if (raw !== value) return null;
1807
+ for (let i = 0; i < value.length; i++) {
1808
+ const code = value.charCodeAt(i);
1809
+ if (code <= 31 || code >= 127 && code <= 159) return null;
1810
+ }
1811
+ if (/\s/u.test(raw)) return null;
1812
+ if (raw.startsWith("//")) return null;
1813
+ if (BLOCKED_SCHEME_RE.test(raw)) return null;
1814
+ if (hasEncodedControl(raw)) return null;
1815
+ return raw;
1816
+ }
1817
+ function hrefScheme(raw) {
1818
+ return raw.match(SCHEME_RE)?.[0].toLowerCase() ?? null;
1819
+ }
1820
+ function parseSafeAbsoluteUrl(raw) {
1821
+ if (raw.includes("\\")) return null;
1822
+ try {
1823
+ return new URL(raw);
1824
+ } catch {
1825
+ return null;
1826
+ }
1827
+ }
1828
+ function sanitizeAbsoluteHref(raw, options = {}) {
1829
+ const url = parseSafeAbsoluteUrl(raw);
1830
+ if (!url) return null;
1831
+ if (url.protocol === "http:" || url.protocol === "https:") {
1832
+ if (!/^https?:\/\//i.test(raw)) return null;
1833
+ return options.preserveHttpHref ? raw : url.toString();
1834
+ }
1835
+ if (url.protocol === "mailto:") return raw;
1836
+ return null;
1837
+ }
1838
+ function isSafeNormalizedRelativeHref(raw) {
1839
+ if (raw.includes("\\")) return false;
1840
+ if (raw.startsWith("//")) return false;
1841
+ if (SCHEME_RE.test(raw)) return false;
1842
+ if (hasEncodedControl(raw)) return false;
1843
+ if (!SAFE_RELATIVE_HREF_RE.test(raw)) return false;
1844
+ return raw.startsWith("#") || raw.startsWith("?") || raw.startsWith("/") || raw.startsWith("./") || raw.startsWith("../") || /^[A-Za-z0-9._~-]/u.test(raw);
1845
+ }
1846
+ function isSafeRelativeHref(value) {
1847
+ const raw = normalizeRawHref(value);
1848
+ return raw != null && isSafeNormalizedRelativeHref(raw);
1849
+ }
1850
+ function sanitizeTerminalHref(value, options = {}) {
1851
+ const raw = normalizeRawHref(value);
1852
+ if (!raw) return null;
1853
+ const sanitized = sanitizeAbsoluteHref(raw, { preserveHttpHref: true });
1854
+ if (sanitized) return sanitized;
1855
+ if (options.allowFileUrls) {
1856
+ const url = parseSafeAbsoluteUrl(raw);
1857
+ if (url?.protocol === "file:") return url.toString();
1858
+ }
1859
+ return null;
1860
+ }
1861
+ function sanitizeDomHref(value, options = {}) {
1862
+ const raw = normalizeRawHref(value);
1863
+ if (!raw) return null;
1864
+ const scheme = hrefScheme(raw);
1865
+ if (!scheme) return options.allowRelative !== false && isSafeNormalizedRelativeHref(raw) ? raw : null;
1866
+ return sanitizeAbsoluteHref(raw);
1867
+ }
1868
+
1869
+ //#endregion
1870
+ export { TERMINAL_RENDER_PLANES, applyAnsiSgrStyle, createTerminal, getPlaneTerminal, getRootTerminal, hasEncodedControl, isSafeRelativeHref, parseAnsiSgr, resetPlaneRowsForRender, sanitizeDomHref, sanitizeTerminalHref, scrollPlaneRows };