@simon_he/vue-tui 1.0.2 → 1.1.1

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 (104) hide show
  1. package/README.md +44 -26
  2. package/dist/TMermaidText-CXm_L969.js +885 -0
  3. package/dist/{TSelect-DR0FomSu.js → TSelect-CiyMvwl-.js} +60 -81
  4. package/dist/{TTree-kzhlB5P0.js → TTree-DKGiuIcM.js} +140 -103
  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 +31184 -24907
  10. package/dist/agent.d.cts +11 -1
  11. package/dist/agent.d.ts +11 -1
  12. package/dist/agent.js +1700 -13
  13. package/dist/block-source-5CQp4tF8.js +3230 -0
  14. package/dist/cli/stdin-buffer.d.ts +1 -1
  15. package/dist/cli.cjs +5037 -6767
  16. package/dist/cli.d.cts +4 -0
  17. package/dist/cli.d.ts +4 -0
  18. package/dist/cli.js +2733 -148
  19. package/dist/core/buffer/eaw-ranges-unicode-17.d.ts +14 -0
  20. package/dist/core/perf/instrumentation-noop.d.ts +48 -0
  21. package/dist/core/perf/instrumentation.d.ts +136 -0
  22. package/dist/core/terminal/create-terminal.d.ts +1 -0
  23. package/dist/core.cjs +843 -1469
  24. package/dist/core.js +6 -6
  25. package/dist/{create-terminal-DBFNYr8f.js → create-terminal-DXuZ2fci.js} +39 -13
  26. package/dist/debug-logger-ChY_PXVk.js +74 -0
  27. package/dist/{dom-renderer-BniJSQyX.js → dom-renderer-DNzmg2As.js} +2 -2
  28. package/dist/experimental.cjs +4115 -5928
  29. package/dist/experimental.d.cts +2 -0
  30. package/dist/experimental.d.ts +2 -0
  31. package/dist/experimental.js +1490 -9
  32. package/dist/frame-perf-BBhE-iuS.js +111 -0
  33. package/dist/{sgr-BmzxEjAO.js → hyperlink-B35-O-cW.js} +100 -1
  34. package/dist/index.cjs +5017 -10339
  35. package/dist/index.js +16 -14
  36. package/dist/{linkify-D_gzn9Xq.js → linkify-CuuquMQT.js} +32 -3
  37. package/dist/markdown.cjs +21295 -12691
  38. package/dist/markdown.d.cts +1 -1
  39. package/dist/markdown.d.ts +1 -1
  40. package/dist/markdown.js +211 -51
  41. package/dist/mermaid-CWto52Wk.js +123 -0
  42. package/dist/mermaid.cjs +1707 -0
  43. package/dist/mermaid.d.cts +3 -0
  44. package/dist/mermaid.d.ts +3 -0
  45. package/dist/mermaid.js +6 -0
  46. package/dist/observability/frame-perf-store.d.ts +2 -1
  47. package/dist/observability/frame-perf.d.ts +33 -0
  48. package/dist/observability.cjs +106 -107
  49. package/dist/observability.d.cts +2 -2
  50. package/dist/observability.d.ts +2 -2
  51. package/dist/observability.js +3 -3
  52. package/dist/{path-suggest-BNqcuE3-.js → path-suggest-fPzRsgPv.js} +1 -1
  53. package/dist/{wheel-scroll-vBRaQ7p-.js → rect-Ng-3eKzF.js} +253 -117
  54. package/dist/renderer/cli/stdout-metrics.d.ts +10 -0
  55. package/dist/renderer/cli/stdout-renderer.d.ts +3 -0
  56. package/dist/renderer/terminal-graphic-render-queue.d.ts +50 -0
  57. package/dist/renderer/terminal-graphics-trace.d.ts +53 -0
  58. package/dist/renderer/terminal-graphics.d.ts +146 -0
  59. package/dist/renderer-dom.cjs +364 -903
  60. package/dist/renderer-dom.js +4 -4
  61. package/dist/runtime/index.d.ts +2 -0
  62. package/dist/{runtime-rfcZCyJq.js → runtime-D77C03RM.js} +28 -15
  63. package/dist/runtime.cjs +427 -889
  64. package/dist/runtime.js +2 -2
  65. package/dist/{terminal-selection-DBinO6p5.js → terminal-selection-CQYr--26.js} +4 -1
  66. package/dist/{tlog-theme-CvLFNMwk.js → tlog-theme-CopRt9SC.js} +37 -6
  67. package/dist/{trace-CjzYeIxN.js → trace-CYHDKM1m.js} +5 -0
  68. package/dist/vue/agent/create-png-terminal-graphic-renderer.d.ts +19 -0
  69. package/dist/vue/components/TAgentTerminalGraphic.d.ts +481 -0
  70. package/dist/vue/components/TCharts.d.ts +1121 -0
  71. package/dist/vue/components/TFeedback.d.ts +1 -1
  72. package/dist/vue/components/TFlex.d.ts +389 -0
  73. package/dist/vue/components/TForm.d.ts +4 -4
  74. package/dist/vue/components/TLogSearchBar.d.ts +3 -3
  75. package/dist/vue/components/TLogSearchPager.d.ts +2 -2
  76. package/dist/vue/components/TMarkdownText.d.ts +162 -4
  77. package/dist/vue/components/TMermaidText.d.ts +900 -0
  78. package/dist/vue/components/TVirtualList.d.ts +9 -0
  79. package/dist/vue/components/TVirtualMarkdown.d.ts +170 -9
  80. package/dist/vue/components/TVirtualRows.d.ts +9 -0
  81. package/dist/vue/components/TerminalProvider.d.ts +2 -0
  82. package/dist/vue/context.d.ts +24 -0
  83. package/dist/vue/index.d.ts +5 -0
  84. package/dist/vue/layout/flex.d.ts +67 -0
  85. package/dist/vue/markdown/ast.d.ts +5 -2
  86. package/dist/vue/markdown/document.d.ts +5 -0
  87. package/dist/vue/markdown/image-actions.d.ts +14 -0
  88. package/dist/vue/markdown/image.d.ts +11 -0
  89. package/dist/vue/markdown/link-actions.d.ts +14 -0
  90. package/dist/vue/markdown/math-actions.d.ts +14 -0
  91. package/dist/vue/markdown/math.d.ts +6 -0
  92. package/dist/vue/markdown/render.d.ts +28 -0
  93. package/dist/vue/markdown/types.d.ts +78 -0
  94. package/dist/vue/mermaid/beautiful-mermaid.d.ts +1436 -0
  95. package/dist/vue/render/render-manager.d.ts +7 -0
  96. package/dist/vue.cjs +7207 -12437
  97. package/dist/vue.js +706 -39
  98. package/dist/wheel-scroll-CQ22Hcyj.js +106 -0
  99. package/dist/{hyperlink-Bn02UXJb.js → width-GqllnV8C.js} +166 -111
  100. package/package.json +85 -14
  101. package/dist/block-source-CKUOnkgT.js +0 -1663
  102. package/dist/frame-perf-C6Xfg85O.js +0 -24
  103. /package/dist/{env-Cl8qmNFN.js → env-DNCzvX8b.js} +0 -0
  104. /package/dist/{path-mnSgEpCm.js → path-BI9W15Zt.js} +0 -0
@@ -1,5 +1,5 @@
1
- import { charCellWidth, segmentedGraphemes } from "./hyperlink-Bn02UXJb.js";
2
- import { computed, inject, onBeforeUnmount, provide, ref, vShow, watchEffect } from "vue";
1
+ import { charCellWidth, segmentedGraphemes } from "./width-GqllnV8C.js";
2
+ import { computed, inject, onBeforeUnmount, provide, readonly, ref, vShow, watchEffect } from "vue";
3
3
 
4
4
  //#region src/vue/injection-key.ts
5
5
  const VUE_TUI_INJECTION_PROTOCOL = "v1";
@@ -7,6 +7,120 @@ function injectionKey(name) {
7
7
  return Symbol.for(`@simon_he/vue-tui:${VUE_TUI_INJECTION_PROTOCOL}:${name}`);
8
8
  }
9
9
 
10
+ //#endregion
11
+ //#region src/renderer/terminal-graphics-trace.ts
12
+ const metrics = {
13
+ requests: 0,
14
+ skippedHidden: 0,
15
+ skippedScrolling: 0,
16
+ cacheHits: 0,
17
+ cacheMisses: 0,
18
+ cacheStores: 0,
19
+ rendererRuns: 0,
20
+ rendererAborts: 0,
21
+ rendererErrors: 0,
22
+ queued: 0,
23
+ queueWaits: 0,
24
+ queueDeduped: 0,
25
+ cleared: 0,
26
+ bytesQueued: 0,
27
+ totalRendererMs: 0,
28
+ totalValidateMs: 0,
29
+ totalQueueWaitMs: 0,
30
+ maxQueueWaitMs: 0,
31
+ maxActiveRenders: 0,
32
+ maxWaitingRenders: 0,
33
+ maxCacheEntries: 0,
34
+ maxCacheBytes: 0,
35
+ scrollStarts: 0,
36
+ scrollMarks: 0,
37
+ scrollIdles: 0,
38
+ totalScrollMs: 0,
39
+ maxScrollMs: 0
40
+ };
41
+ const subscribers = new Set();
42
+ function nowTerminalGraphicTraceTime() {
43
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
44
+ }
45
+ function recordTerminalGraphicTrace(event) {
46
+ const normalized = {
47
+ ...event,
48
+ timestamp: event.timestamp ?? nowTerminalGraphicTraceTime()
49
+ };
50
+ switch (normalized.type) {
51
+ case "request":
52
+ metrics.requests++;
53
+ break;
54
+ case "skip-hidden":
55
+ metrics.skippedHidden++;
56
+ break;
57
+ case "skip-scrolling":
58
+ metrics.skippedScrolling++;
59
+ break;
60
+ case "cache-hit":
61
+ metrics.cacheHits++;
62
+ break;
63
+ case "cache-miss":
64
+ metrics.cacheMisses++;
65
+ break;
66
+ case "cache-store":
67
+ metrics.cacheStores++;
68
+ break;
69
+ case "renderer-start":
70
+ metrics.rendererRuns++;
71
+ break;
72
+ case "renderer-end":
73
+ metrics.totalRendererMs += normalized.durationMs ?? 0;
74
+ break;
75
+ case "renderer-abort":
76
+ metrics.rendererAborts++;
77
+ break;
78
+ case "renderer-error":
79
+ metrics.rendererErrors++;
80
+ break;
81
+ case "validate-end":
82
+ metrics.totalValidateMs += normalized.durationMs ?? 0;
83
+ break;
84
+ case "queue":
85
+ metrics.queued++;
86
+ metrics.bytesQueued += normalized.bytes ?? 0;
87
+ break;
88
+ case "queue-wait": {
89
+ const durationMs = normalized.durationMs ?? 0;
90
+ metrics.queueWaits++;
91
+ metrics.totalQueueWaitMs += durationMs;
92
+ metrics.maxQueueWaitMs = Math.max(metrics.maxQueueWaitMs, durationMs);
93
+ break;
94
+ }
95
+ case "queue-dedupe":
96
+ metrics.queueDeduped++;
97
+ break;
98
+ case "clear":
99
+ metrics.cleared++;
100
+ break;
101
+ case "scroll-start":
102
+ metrics.scrollStarts++;
103
+ break;
104
+ case "scroll-mark":
105
+ metrics.scrollMarks++;
106
+ break;
107
+ case "scroll-idle": {
108
+ const durationMs = normalized.durationMs ?? 0;
109
+ metrics.scrollIdles++;
110
+ metrics.totalScrollMs += durationMs;
111
+ metrics.maxScrollMs = Math.max(metrics.maxScrollMs, durationMs);
112
+ break;
113
+ }
114
+ }
115
+ if (normalized.active != null && Number.isFinite(normalized.active)) metrics.maxActiveRenders = Math.max(metrics.maxActiveRenders, Math.max(0, normalized.active));
116
+ if (normalized.waiting != null && Number.isFinite(normalized.waiting)) metrics.maxWaitingRenders = Math.max(metrics.maxWaitingRenders, Math.max(0, normalized.waiting));
117
+ if (normalized.cacheEntries != null && Number.isFinite(normalized.cacheEntries)) metrics.maxCacheEntries = Math.max(metrics.maxCacheEntries, Math.max(0, normalized.cacheEntries));
118
+ if (normalized.cacheBytes != null && Number.isFinite(normalized.cacheBytes)) metrics.maxCacheBytes = Math.max(metrics.maxCacheBytes, Math.max(0, normalized.cacheBytes));
119
+ for (const subscriber of subscribers) try {
120
+ subscriber(normalized);
121
+ } catch {}
122
+ }
123
+
10
124
  //#endregion
11
125
  //#region src/vue/context.ts
12
126
  const TerminalContextKey = injectionKey("TerminalContext");
@@ -17,7 +131,95 @@ const RenderPlaneContextKey = injectionKey("RenderPlane");
17
131
  const ImeAnchorContextKey = injectionKey("ImeAnchor");
18
132
  const TInputPluginsContextKey = injectionKey("TInputPlugins");
19
133
  const TPathPickerProviderContextKey = injectionKey("TPathPickerProvider");
134
+ const TerminalGraphicsActivityKey = injectionKey("TerminalGraphicsActivity");
20
135
  const DialogContextKey = injectionKey("DialogContext");
136
+ function createTerminalGraphicsActivity(options = {}) {
137
+ let scrollIdleMs = normalizeTerminalGraphicsScrollIdleMs(options.scrollIdleMs);
138
+ const traceId = options.traceId ?? "TerminalGraphicsActivity";
139
+ const scrolling = ref(false);
140
+ const version = ref(0);
141
+ let timer = null;
142
+ let scrollStartedAt = 0;
143
+ function bump() {
144
+ version.value++;
145
+ }
146
+ function clearTimer() {
147
+ if (!timer) return;
148
+ clearTimeout(timer);
149
+ timer = null;
150
+ }
151
+ function traceScroll(type, durationMs) {
152
+ if (options.trace !== true) return;
153
+ recordTerminalGraphicTrace({
154
+ type,
155
+ id: traceId,
156
+ key: traceId,
157
+ durationMs
158
+ });
159
+ }
160
+ function finishScroll() {
161
+ if (!scrolling.value) return;
162
+ const endedAt = nowTerminalGraphicTraceTime();
163
+ const durationMs = scrollStartedAt > 0 ? endedAt - scrollStartedAt : void 0;
164
+ scrolling.value = false;
165
+ scrollStartedAt = 0;
166
+ traceScroll("scroll-idle", durationMs);
167
+ bump();
168
+ }
169
+ function markScroll() {
170
+ const started = !scrolling.value;
171
+ if (started) {
172
+ scrolling.value = true;
173
+ scrollStartedAt = nowTerminalGraphicTraceTime();
174
+ traceScroll("scroll-start");
175
+ bump();
176
+ } else traceScroll("scroll-mark");
177
+ clearTimer();
178
+ timer = setTimeout(() => {
179
+ timer = null;
180
+ finishScroll();
181
+ }, scrollIdleMs);
182
+ }
183
+ function setScrollIdleMs(value) {
184
+ scrollIdleMs = normalizeTerminalGraphicsScrollIdleMs(value);
185
+ if (!scrolling.value) return;
186
+ clearTimer();
187
+ timer = setTimeout(() => {
188
+ timer = null;
189
+ finishScroll();
190
+ }, scrollIdleMs);
191
+ }
192
+ function dispose() {
193
+ clearTimer();
194
+ finishScroll();
195
+ }
196
+ return {
197
+ scrolling: readonly(scrolling),
198
+ version: readonly(version),
199
+ markScroll,
200
+ setScrollIdleMs,
201
+ dispose
202
+ };
203
+ }
204
+ function createCombinedTerminalGraphicsActivity(parent, own) {
205
+ if (!parent) return own;
206
+ return {
207
+ scrolling: computed(() => parent.scrolling.value || own.scrolling.value),
208
+ version: computed(() => parent.version.value + own.version.value),
209
+ markScroll: own.markScroll,
210
+ setScrollIdleMs: own.setScrollIdleMs,
211
+ dispose: own.dispose
212
+ };
213
+ }
214
+ function positiveFiniteInt(value, fallback) {
215
+ const n = Number(value);
216
+ if (!Number.isFinite(n)) return fallback;
217
+ const int = Math.floor(n);
218
+ return int > 0 ? int : fallback;
219
+ }
220
+ function normalizeTerminalGraphicsScrollIdleMs(value) {
221
+ return Math.max(16, positiveFiniteInt(value, 96));
222
+ }
21
223
 
22
224
  //#endregion
23
225
  //#region src/vue/render/context.ts
@@ -374,8 +576,10 @@ function wrapByCells(text, width, provider = currentTextWidthProvider()) {
374
576
  const useCache = canUseDefaultTextCache(provider);
375
577
  const bucket = useCache ? getWrapBucket(width) : null;
376
578
  if (text && hasAsciiFastPath(provider) && isAscii(text)) {
377
- const cached$1 = bucket?.get(text);
378
- if (cached$1) return cached$1;
579
+ if (bucket) {
580
+ const cached = bucket.get(text);
581
+ if (cached) return cached;
582
+ }
379
583
  const out$1 = [];
380
584
  for (const rawLine of text.replace(/\r/g, "").split("\n")) {
381
585
  if (rawLine.length === 0) {
@@ -390,8 +594,10 @@ function wrapByCells(text, width, provider = currentTextWidthProvider()) {
390
594
  }
391
595
  return out$1;
392
596
  }
393
- const cached = bucket?.get(text);
394
- if (cached) return cached;
597
+ if (bucket) {
598
+ const cached = bucket.get(text);
599
+ if (cached) return cached;
600
+ }
395
601
  const out = [];
396
602
  for (const rawLine of text.replace(/\r/g, "").split("\n")) {
397
603
  if (rawLine.length === 0) {
@@ -512,47 +718,82 @@ function requestBatchedInvalidate(scheduler, plane, priority) {
512
718
  });
513
719
  });
514
720
  }
721
+ function sameRenderRect(a, b) {
722
+ if (a === b) return true;
723
+ if (!a || !b) return false;
724
+ return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
725
+ }
726
+ function sameDeps(a, b) {
727
+ if (Object.is(a, b)) return true;
728
+ if (!Array.isArray(a) || !Array.isArray(b)) return false;
729
+ if (a.length !== b.length) return false;
730
+ for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return false;
731
+ return true;
732
+ }
515
733
  function useRenderNode(getOptions) {
516
734
  const { scheduler, render, widthProvider } = useTerminal();
517
735
  const parentStack = useRenderStack();
518
736
  const plane = inject(RenderPlaneContextKey, ref("default"));
519
737
  const id = ref(null);
520
738
  const lastPlane = ref(plane.value);
739
+ let lastStack = null;
740
+ let lastZIndex = 0;
741
+ let lastHasRect = false;
742
+ let lastRect = null;
743
+ let lastDeps;
744
+ let lastHasDeps = false;
521
745
  const options = computed(() => withTextWidthProvider(widthProvider, getOptions));
522
746
  const stop = watchEffect(() => {
523
747
  const opt = options.value;
524
748
  opt.deps;
525
749
  const stack = opt.stack ?? parentStack.value;
526
750
  const nextPlane = plane.value;
751
+ const hasRect = Object.prototype.hasOwnProperty.call(opt, "rect");
752
+ const nextRect = hasRect ? opt.rect ?? null : null;
753
+ const nextZIndex = opt.zIndex ?? 0;
754
+ const hasDeps = Object.prototype.hasOwnProperty.call(opt, "deps");
527
755
  if (!stack) return;
528
756
  if (!id.value) {
529
757
  const node = render.register({
530
758
  stack,
531
- zIndex: opt.zIndex,
532
- rect: opt.rect,
759
+ zIndex: nextZIndex,
760
+ rect: hasRect ? nextRect : void 0,
533
761
  plane: nextPlane,
534
762
  paint: opt.paint
535
763
  });
536
764
  id.value = node.id;
537
765
  lastPlane.value = nextPlane;
766
+ lastStack = stack;
767
+ lastZIndex = nextZIndex;
768
+ lastHasRect = hasRect;
769
+ lastRect = nextRect;
770
+ lastHasDeps = hasDeps;
771
+ lastDeps = opt.deps;
538
772
  requestBatchedInvalidate(scheduler, nextPlane, opt.priority ?? "normal");
539
773
  return;
540
774
  }
541
775
  const prevPlane = lastPlane.value;
776
+ if (hasDeps && lastHasDeps && !opt.dirtyRowsHint?.length && lastStack === stack && lastZIndex === nextZIndex && prevPlane === nextPlane && lastHasRect === hasRect && (!hasRect || sameRenderRect(lastRect, nextRect)) && sameDeps(lastDeps, opt.deps)) return;
542
777
  const updatePayload = {
543
778
  stack,
544
- zIndex: opt.zIndex ?? 0,
779
+ zIndex: nextZIndex,
545
780
  dirtyRowsHint: opt.dirtyRowsHint,
546
781
  plane: nextPlane,
547
782
  paint: opt.paint
548
783
  };
549
- if (Object.prototype.hasOwnProperty.call(opt, "rect")) updatePayload.rect = opt.rect ?? null;
784
+ if (hasRect) updatePayload.rect = nextRect;
550
785
  render.update(id.value, updatePayload);
551
786
  lastPlane.value = nextPlane;
787
+ lastStack = stack;
788
+ lastZIndex = nextZIndex;
789
+ lastHasRect = hasRect;
790
+ lastRect = nextRect;
791
+ lastHasDeps = hasDeps;
792
+ lastDeps = opt.deps;
552
793
  const priority = opt.priority ?? "normal";
553
794
  requestBatchedInvalidate(scheduler, prevPlane, priority);
554
795
  if (prevPlane !== nextPlane) requestBatchedInvalidate(scheduler, nextPlane, priority);
555
- });
796
+ }, { flush: "sync" });
556
797
  onBeforeUnmount(() => {
557
798
  stop();
558
799
  if (id.value) {
@@ -716,109 +957,4 @@ function normalizeCellRect(rect) {
716
957
  }
717
958
 
718
959
  //#endregion
719
- //#region src/vue/utils/wheel-scroll.ts
720
- const LINE_UNIT_THRESHOLD = 3;
721
- const PIXELS_PER_LINE = 16;
722
- const ACCEL_WINDOW_MS = 120;
723
- const MAX_ACCEL = 26;
724
- const EDGE_BOUNCE_MS = 120;
725
- function clamp(n, min, max) {
726
- return Math.max(min, Math.min(max, n));
727
- }
728
- function isLineUnitDelta(deltaY, mode = "auto") {
729
- if (mode === "line") return true;
730
- if (mode === "pixel") return false;
731
- const abs = Math.abs(deltaY);
732
- return abs > 0 && abs <= LINE_UNIT_THRESHOLD && Number.isInteger(deltaY);
733
- }
734
- function normalizeDelta(deltaY, mode = "auto") {
735
- if (mode === "line") return deltaY;
736
- if (mode === "pixel") return deltaY / PIXELS_PER_LINE;
737
- const abs = Math.abs(deltaY);
738
- if (abs <= LINE_UNIT_THRESHOLD && Number.isInteger(deltaY)) return deltaY;
739
- return deltaY / PIXELS_PER_LINE;
740
- }
741
- function accelFactor(now, lastAt, maxAccel = MAX_ACCEL) {
742
- const dt = now - lastAt;
743
- if (!Number.isFinite(dt) || dt <= 0 || dt > ACCEL_WINDOW_MS) return 1;
744
- const t = 1 - dt / ACCEL_WINDOW_MS;
745
- return 1 + t * (maxAccel - 1);
746
- }
747
- function createWheelScrollState() {
748
- return {
749
- accumulator: 0,
750
- lastAt: 0,
751
- lastEdgeDir: 0,
752
- lastEdgeAt: 0
753
- };
754
- }
755
- function resetWheelScrollState(state) {
756
- state.accumulator = 0;
757
- state.lastAt = 0;
758
- state.lastEdgeDir = 0;
759
- state.lastEdgeAt = 0;
760
- }
761
- function applyWheelScroll(state, deltaY, scrollTop, maxTop, now = Date.now(), deltaMode = "auto", options = {}) {
762
- if (!Number.isFinite(deltaY) || deltaY === 0 || maxTop <= 0) {
763
- if (maxTop <= 0) {
764
- state.accumulator = 0;
765
- state.lastEdgeDir = 0;
766
- state.lastEdgeAt = 0;
767
- }
768
- return {
769
- nextTop: scrollTop,
770
- dir: 0,
771
- lines: 0
772
- };
773
- }
774
- const lineUnits = isLineUnitDelta(deltaY, deltaMode);
775
- const normalizedDelta = normalizeDelta(deltaY, deltaMode);
776
- const accel = options.disableAcceleration ? 1 : lineUnits ? 1 : state.lastAt ? accelFactor(now, state.lastAt) : 1;
777
- state.lastAt = now;
778
- state.accumulator += normalizedDelta * accel;
779
- const lines = Math.trunc(state.accumulator);
780
- if (lines === 0) return {
781
- nextTop: scrollTop,
782
- dir: 0,
783
- lines: 0
784
- };
785
- state.accumulator -= lines;
786
- const dir = lines > 0 ? 1 : -1;
787
- const atTop = scrollTop <= 0;
788
- const atBottom = scrollTop >= maxTop;
789
- if (dir < 0 && atTop || dir > 0 && atBottom) {
790
- state.lastEdgeDir = dir;
791
- state.lastEdgeAt = now;
792
- state.accumulator = 0;
793
- return {
794
- nextTop: scrollTop,
795
- dir: 0,
796
- lines: 0
797
- };
798
- }
799
- if (state.lastEdgeDir !== 0 && now - state.lastEdgeAt < EDGE_BOUNCE_MS && dir === -state.lastEdgeDir && (atTop || atBottom)) return {
800
- nextTop: scrollTop,
801
- dir: 0,
802
- lines: 0
803
- };
804
- const unclamped = scrollTop + lines;
805
- const nextTop = clamp(unclamped, 0, maxTop);
806
- if (nextTop === scrollTop) {
807
- state.accumulator = 0;
808
- return {
809
- nextTop: scrollTop,
810
- dir: 0,
811
- lines: 0
812
- };
813
- }
814
- if (nextTop !== unclamped) state.accumulator = 0;
815
- state.lastEdgeDir = 0;
816
- return {
817
- nextTop,
818
- dir: nextTop > scrollTop ? 1 : -1,
819
- lines: nextTop - scrollTop
820
- };
821
- }
822
-
823
- //#endregion
824
- export { DialogContextKey, EventZIndexContextKey, ImeAnchorContextKey, LayoutContextKey, RenderPlaneContextKey, RenderStackKey, TInputPluginsContextKey, TPathPickerProviderContextKey, TerminalContextKey, VisibilityContextKey, applyWheelScroll, clearTextCaches, createWheelScrollState, currentTextWidthProvider, forEachTextCellSegment, formatInlineCellLine, graphemeRangeAt, hasTextWidthAsciiFastPath, injectionKey, intersectRect, normalizeCellRect, padEndByCells as padEndByCells$1, repeatChar, resetWheelScrollState, sanitizeInlineText, sanitizeTextBlock, sliceByCells, sliceByCellsRange, spaces, textCellWidth as textCellWidth$1, translateRect, useLayout, useRenderNode, useRenderStack, useTerminal, useTerminalNode, useVisibility, withTextRenderPass, withTextWidthProvider, wrapByCells };
960
+ export { DialogContextKey, EventZIndexContextKey, ImeAnchorContextKey, LayoutContextKey, RenderPlaneContextKey, RenderStackKey, TInputPluginsContextKey, TPathPickerProviderContextKey, TerminalContextKey, TerminalGraphicsActivityKey, VisibilityContextKey, clearTextCaches, createCombinedTerminalGraphicsActivity, createTerminalGraphicsActivity, currentTextWidthProvider, forEachTextCellSegment, formatInlineCellLine, graphemeRangeAt, hasTextWidthAsciiFastPath, injectionKey, intersectRect, normalizeCellRect, nowTerminalGraphicTraceTime, padEndByCells as padEndByCells$1, recordTerminalGraphicTrace, repeatChar, sanitizeInlineText, sanitizeTextBlock, sliceByCells, sliceByCellsRange, spaces, textCellWidth as textCellWidth$1, translateRect, useLayout, useRenderNode, useRenderStack, useTerminal, useTerminalNode, useVisibility, withTextRenderPass, withTextWidthProvider, wrapByCells };
@@ -7,6 +7,10 @@ export interface StdoutRendererMetrics {
7
7
  writeMode: StdoutRendererWriteMode;
8
8
  fps: number;
9
9
  bytesPerSec: number;
10
+ terminalGraphicsDraws: number;
11
+ terminalGraphicsClears: number;
12
+ terminalGraphicsBytes: number;
13
+ terminalGraphicsActive: number;
10
14
  }
11
15
  export declare function getStdoutRendererMetrics(): StdoutRendererMetrics;
12
16
  export declare function recordStdoutFrame(info: Readonly<{
@@ -16,3 +20,9 @@ export declare function recordStdoutFrame(info: Readonly<{
16
20
  writeEmaMs: number;
17
21
  writeMode: StdoutRendererWriteMode;
18
22
  }>): void;
23
+ export declare function recordStdoutTerminalGraphics(info: Readonly<{
24
+ draws?: number;
25
+ clears?: number;
26
+ bytes?: number;
27
+ active: number;
28
+ }>): void;
@@ -1,5 +1,6 @@
1
1
  import type { Terminal } from "../../core/types.js";
2
2
  import type { RendererCapabilities } from "../capabilities.js";
3
+ import type { TerminalGraphicsCapabilities, TerminalGraphicsDetectionInput } from "../terminal-graphics.js";
3
4
  import type { ThemePalette } from "../../core/ansi-palette.js";
4
5
  export type CliOutput = Readonly<{
5
6
  write: (chunk: string) => unknown;
@@ -12,6 +13,7 @@ export type CliOutput = Readonly<{
12
13
  }>;
13
14
  export type StdoutRenderer = Readonly<{
14
15
  capabilities: RendererCapabilities;
16
+ graphicsCapabilities?: TerminalGraphicsCapabilities;
15
17
  render: () => void;
16
18
  dispose: () => void;
17
19
  /** Move terminal cursor to specified cell position for IME input */
@@ -83,6 +85,7 @@ export type StdoutRendererOptions = Readonly<{
83
85
  } | null;
84
86
  /** Use DEC 2026 synchronized output mode. */
85
87
  useSyncOutput?: boolean;
88
+ terminalGraphics?: false | TerminalGraphicsCapabilities | TerminalGraphicsDetectionInput;
86
89
  allowFileUrls?: boolean;
87
90
  profileFileWriter?: {
88
91
  appendFileSync?: (path: string, data: string) => void;
@@ -0,0 +1,50 @@
1
+ export type TerminalGraphicRenderQueueMetric = Readonly<{
2
+ type: "cache-hit";
3
+ key: string;
4
+ }> | Readonly<{
5
+ type: "cache-store";
6
+ key: string;
7
+ bytes: number;
8
+ }> | Readonly<{
9
+ type: "evict";
10
+ key: string;
11
+ bytes: number;
12
+ }> | Readonly<{
13
+ type: "queue-wait";
14
+ key: string;
15
+ waitMs: number;
16
+ }> | Readonly<{
17
+ type: "render-abort";
18
+ key: string;
19
+ }>;
20
+ export type TerminalGraphicRenderQueueOptions = Readonly<{
21
+ maxConcurrency?: number;
22
+ maxEntries?: number;
23
+ maxBytes?: number;
24
+ ttlMs?: number;
25
+ /**
26
+ * Share in-flight renders for equal cache keys.
27
+ *
28
+ * Keep this disabled when render work observes a caller AbortSignal. This is
29
+ * the safe default for viewport-bound graphics in virtual scrollers: an
30
+ * offscreen row may abort while another still-visible row with the same cache
31
+ * key is still valid. Duplicate queued renders are still cheap because each
32
+ * task re-checks the cache after acquiring a concurrency slot, so a render
33
+ * completed by an earlier row is reused instead of rendering again.
34
+ */
35
+ dedupeInflight?: boolean;
36
+ onMetric?: (metric: TerminalGraphicRenderQueueMetric) => void;
37
+ }>;
38
+ export type TerminalGraphicRenderQueue = Readonly<{
39
+ cached: <T>(key: string, signal: AbortSignal | undefined, render: () => Promise<T>, estimateBytes?: (value: T) => number, options?: Readonly<{
40
+ dedupeInflight?: boolean;
41
+ }>) => Promise<T>;
42
+ clear: () => void;
43
+ stats: () => Readonly<{
44
+ active: number;
45
+ waiting: number;
46
+ cacheEntries: number;
47
+ cacheBytes: number;
48
+ }>;
49
+ }>;
50
+ export declare function createTerminalGraphicRenderQueue(options?: TerminalGraphicRenderQueueOptions): TerminalGraphicRenderQueue;
@@ -0,0 +1,53 @@
1
+ import type { TerminalGraphicsProtocol } from "./terminal-graphics.js";
2
+ export type TerminalGraphicTraceEventType = "request" | "skip-hidden" | "skip-scrolling" | "cache-hit" | "cache-miss" | "cache-store" | "renderer-start" | "renderer-end" | "renderer-abort" | "renderer-error" | "validate-end" | "queue" | "queue-depth" | "queue-wait" | "queue-dedupe" | "clear" | "scroll-start" | "scroll-mark" | "scroll-idle";
3
+ export type TerminalGraphicTraceEvent = Readonly<{
4
+ type: TerminalGraphicTraceEventType;
5
+ id: string;
6
+ key?: string;
7
+ protocol?: TerminalGraphicsProtocol;
8
+ timestamp: number;
9
+ durationMs?: number;
10
+ bytes?: number;
11
+ reason?: string;
12
+ error?: string;
13
+ active?: number;
14
+ waiting?: number;
15
+ cacheEntries?: number;
16
+ cacheBytes?: number;
17
+ }>;
18
+ export type TerminalGraphicTraceMetrics = Readonly<{
19
+ requests: number;
20
+ skippedHidden: number;
21
+ skippedScrolling: number;
22
+ cacheHits: number;
23
+ cacheMisses: number;
24
+ cacheStores: number;
25
+ rendererRuns: number;
26
+ rendererAborts: number;
27
+ rendererErrors: number;
28
+ queued: number;
29
+ queueWaits: number;
30
+ queueDeduped: number;
31
+ cleared: number;
32
+ bytesQueued: number;
33
+ totalRendererMs: number;
34
+ totalValidateMs: number;
35
+ totalQueueWaitMs: number;
36
+ maxQueueWaitMs: number;
37
+ maxActiveRenders: number;
38
+ maxWaitingRenders: number;
39
+ maxCacheEntries: number;
40
+ maxCacheBytes: number;
41
+ scrollStarts: number;
42
+ scrollMarks: number;
43
+ scrollIdles: number;
44
+ totalScrollMs: number;
45
+ maxScrollMs: number;
46
+ }>;
47
+ export declare function nowTerminalGraphicTraceTime(): number;
48
+ export declare function recordTerminalGraphicTrace(event: Omit<TerminalGraphicTraceEvent, "timestamp"> & {
49
+ timestamp?: number;
50
+ }): void;
51
+ export declare function getTerminalGraphicTraceMetrics(): TerminalGraphicTraceMetrics;
52
+ export declare function resetTerminalGraphicTraceMetrics(): void;
53
+ export declare function subscribeTerminalGraphicTrace(listener: (event: TerminalGraphicTraceEvent) => void): () => void;