@simon_he/vue-tui 1.0.2 → 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 (125) hide show
  1. package/README.md +84 -26
  2. package/dist/TMermaidText-frVr8Ysb.js +885 -0
  3. package/dist/{TSelect-DR0FomSu.js → TSelect-tJ87rKPM.js} +59 -242
  4. package/dist/{TTree-kzhlB5P0.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 +5146 -6767
  21. package/dist/cli.d.cts +5 -1
  22. package/dist/cli.d.ts +5 -1
  23. package/dist/cli.js +2869 -154
  24. package/dist/core/buffer/eaw-ranges-unicode-17.d.ts +14 -0
  25. package/dist/core/perf/instrumentation-noop.d.ts +48 -0
  26. package/dist/core/perf/instrumentation.d.ts +136 -0
  27. package/dist/core/terminal/create-terminal.d.ts +1 -0
  28. package/dist/core.cjs +843 -1469
  29. package/dist/core.js +4 -5
  30. package/dist/create-terminal-app.d.ts +24 -1
  31. package/dist/debug-logger-CmsGI4of.js +74 -0
  32. package/dist/{dom-renderer-BniJSQyX.js → dom-renderer-DXx8fWzD.js} +2 -3
  33. package/dist/experimental/3d/bun/contributor-avatar-atlas.d.ts +12 -0
  34. package/dist/experimental/3d/bun/png.d.ts +1 -0
  35. package/dist/experimental/3d/bun/terminal-badge-scene.d.ts +11 -0
  36. package/dist/experimental/3d/bun/terminal-badge.wgsl.d.ts +1 -0
  37. package/dist/experimental/3d/bun/webgpu-renderer.d.ts +26 -0
  38. package/dist/experimental/3d/bun.cjs +7271 -0
  39. package/dist/experimental/3d/bun.d.cts +4 -0
  40. package/dist/experimental/3d/bun.d.ts +4 -0
  41. package/dist/experimental/3d/bun.js +7390 -0
  42. package/dist/experimental/video/node.cjs +497 -0
  43. package/dist/experimental/video/node.d.cts +4 -0
  44. package/dist/experimental/video/node.d.ts +4 -0
  45. package/dist/experimental/video/node.js +610 -0
  46. package/dist/experimental.cjs +6876 -6192
  47. package/dist/experimental.d.cts +7 -0
  48. package/dist/experimental.d.ts +7 -0
  49. package/dist/experimental.js +3005 -9
  50. package/dist/frame-perf-CHpWhbC4.js +111 -0
  51. package/dist/{create-terminal-DBFNYr8f.js → hyperlink-_1FAj7Wq.js} +440 -13
  52. package/dist/index.cjs +5017 -10339
  53. package/dist/index.js +14 -13
  54. package/dist/{linkify-D_gzn9Xq.js → linkify-D68T7rdW.js} +193 -3
  55. package/dist/markdown.cjs +21295 -12691
  56. package/dist/markdown.d.cts +1 -1
  57. package/dist/markdown.d.ts +1 -1
  58. package/dist/markdown.js +211 -51
  59. package/dist/mermaid-BNhM1jAZ.js +123 -0
  60. package/dist/mermaid.cjs +1707 -0
  61. package/dist/mermaid.d.cts +3 -0
  62. package/dist/mermaid.d.ts +3 -0
  63. package/dist/mermaid.js +6 -0
  64. package/dist/observability/frame-perf-store.d.ts +2 -1
  65. package/dist/observability/frame-perf.d.ts +33 -0
  66. package/dist/observability.cjs +106 -107
  67. package/dist/observability.d.cts +2 -2
  68. package/dist/observability.d.ts +2 -2
  69. package/dist/observability.js +3 -3
  70. package/dist/{wheel-scroll-vBRaQ7p-.js → rect-DUpeDfBg.js} +253 -117
  71. package/dist/renderer/cli/stdout-metrics.d.ts +10 -0
  72. package/dist/renderer/cli/stdout-renderer.d.ts +3 -0
  73. package/dist/renderer/terminal-graphic-render-queue.d.ts +50 -0
  74. package/dist/renderer/terminal-graphics-trace.d.ts +53 -0
  75. package/dist/renderer/terminal-graphics.d.ts +147 -0
  76. package/dist/renderer-dom.cjs +364 -903
  77. package/dist/renderer-dom.js +4 -5
  78. package/dist/runtime/index.d.ts +2 -0
  79. package/dist/{runtime-rfcZCyJq.js → runtime-Byf8XXOT.js} +28 -15
  80. package/dist/runtime.cjs +427 -889
  81. package/dist/runtime.js +2 -2
  82. package/dist/terminal-graphics-Bbe83wSr.js +597 -0
  83. package/dist/{terminal-selection-DBinO6p5.js → terminal-selection-4W1SJj4c.js} +4 -1
  84. package/dist/{tlog-theme-CvLFNMwk.js → tlog-theme-Bul2Na3u.js} +1237 -12
  85. package/dist/{trace-CjzYeIxN.js → trace-Bh8WnoMo.js} +5 -0
  86. package/dist/vue/agent/create-png-terminal-graphic-renderer.d.ts +19 -0
  87. package/dist/vue/components/T3DViewport.d.ts +473 -0
  88. package/dist/vue/components/TAgentTerminalGraphic.d.ts +494 -0
  89. package/dist/vue/components/TCharts.d.ts +1121 -0
  90. package/dist/vue/components/TFeedback.d.ts +1 -1
  91. package/dist/vue/components/TFlex.d.ts +389 -0
  92. package/dist/vue/components/TForm.d.ts +4 -4
  93. package/dist/vue/components/TLogSearchBar.d.ts +3 -3
  94. package/dist/vue/components/TLogSearchPager.d.ts +2 -2
  95. package/dist/vue/components/TMarkdownText.d.ts +162 -4
  96. package/dist/vue/components/TMermaidText.d.ts +900 -0
  97. package/dist/vue/components/TVideo.d.ts +291 -0
  98. package/dist/vue/components/TVirtualList.d.ts +9 -0
  99. package/dist/vue/components/TVirtualMarkdown.d.ts +170 -9
  100. package/dist/vue/components/TVirtualRows.d.ts +9 -0
  101. package/dist/vue/components/TerminalProvider.d.ts +2 -0
  102. package/dist/vue/context.d.ts +24 -0
  103. package/dist/vue/index.d.ts +5 -0
  104. package/dist/vue/layout/flex.d.ts +67 -0
  105. package/dist/vue/markdown/ast.d.ts +5 -2
  106. package/dist/vue/markdown/document.d.ts +5 -0
  107. package/dist/vue/markdown/image-actions.d.ts +14 -0
  108. package/dist/vue/markdown/image.d.ts +11 -0
  109. package/dist/vue/markdown/link-actions.d.ts +14 -0
  110. package/dist/vue/markdown/math-actions.d.ts +14 -0
  111. package/dist/vue/markdown/math.d.ts +6 -0
  112. package/dist/vue/markdown/render.d.ts +28 -0
  113. package/dist/vue/markdown/types.d.ts +78 -0
  114. package/dist/vue/mermaid/beautiful-mermaid.d.ts +1436 -0
  115. package/dist/vue/render/render-manager.d.ts +7 -0
  116. package/dist/vue/video/types.d.ts +39 -0
  117. package/dist/vue.cjs +7207 -12437
  118. package/dist/vue.js +704 -38
  119. package/dist/wheel-scroll-DASkEZr-.js +106 -0
  120. package/dist/{hyperlink-Bn02UXJb.js → width-GqllnV8C.js} +166 -111
  121. package/package.json +119 -14
  122. package/dist/frame-perf-C6Xfg85O.js +0 -24
  123. package/dist/sgr-BmzxEjAO.js +0 -304
  124. /package/dist/{ansi-palette-CNXqFciC.js → ansi-palette-CH6jc2Z7.js} +0 -0
  125. /package/dist/{path-suggest-BNqcuE3-.js → path-suggest-Dndu4dTi.js} +0 -0
package/dist/cli.js CHANGED
@@ -20,7 +20,7 @@ function envFlag(env, primary, legacy) {
20
20
  const raw = String(firstNonEmptyEnv(env, primary, legacy) ?? "").trim().toLowerCase();
21
21
  return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
22
22
  }
23
- function envString(env, primary, legacy, fallback = "") {
23
+ function envString$1(env, primary, legacy, fallback = "") {
24
24
  return String(firstNonEmptyEnv(env, primary, legacy) ?? fallback).trim() || fallback;
25
25
  }
26
26
 
@@ -33,7 +33,7 @@ const MAX_OP_AGE_MS = 5e3;
33
33
  const GLOBAL_PROFILER_KEY = "__vueTuiCliLatencyProfiler";
34
34
  let nextOpId = 1;
35
35
  let singleton;
36
- function now$1() {
36
+ function now$2() {
37
37
  return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
38
38
  }
39
39
  function diffMs(end, start) {
@@ -68,7 +68,7 @@ function createProfiler() {
68
68
  const processLike = process;
69
69
  const env = (processLike === null || processLike === void 0 ? void 0 : processLike.env) ?? {};
70
70
  if (!envFlag(env, "VUE_TUI_PROFILE_INPUT_LATENCY", "DIMCODE_PROFILE_INPUT_LATENCY")) return createDisabledProfiler();
71
- const logPath = envString(env, "VUE_TUI_PROFILE_INPUT_LATENCY_LOG_PATH", "DIMCODE_PROFILE_INPUT_LATENCY_LOG_PATH", defaultCliLatencyLogPath());
71
+ const logPath = envString$1(env, "VUE_TUI_PROFILE_INPUT_LATENCY_LOG_PATH", "DIMCODE_PROFILE_INPUT_LATENCY_LOG_PATH", defaultCliLatencyLogPath());
72
72
  const ops = new Map();
73
73
  const pendingCommitIds = new Set();
74
74
  const pendingWriteIds = new Set();
@@ -146,7 +146,7 @@ function createProfiler() {
146
146
  cleanupOp(op.id);
147
147
  };
148
148
  const expireOldOps = () => {
149
- const currentTime = now$1();
149
+ const currentTime = now$2();
150
150
  for (const op of ops.values()) {
151
151
  if (currentTime - op.createdAt < MAX_OP_AGE_MS) continue;
152
152
  flushOp(op, op.writeEndAt != null ? "completed" : "timeout");
@@ -163,7 +163,7 @@ function createProfiler() {
163
163
  const id = nextOpId++;
164
164
  const op = {
165
165
  id,
166
- createdAt: now$1(),
166
+ createdAt: now$2(),
167
167
  createdAtIso: new Date().toISOString(),
168
168
  eventType: type,
169
169
  key: String((event === null || event === void 0 ? void 0 : event.key) ?? ""),
@@ -206,7 +206,7 @@ function createProfiler() {
206
206
  };
207
207
  const resolveActiveOp = () => {
208
208
  if (currentDispatchOpId != null) return ops.get(currentDispatchOpId) ?? null;
209
- if (lastDispatchOpId != null && lastDispatchEndedAt != null && now$1() - lastDispatchEndedAt <= INVALIDATE_ASSOCIATION_WINDOW_MS) return ops.get(lastDispatchOpId) ?? null;
209
+ if (lastDispatchOpId != null && lastDispatchEndedAt != null && now$2() - lastDispatchEndedAt <= INVALIDATE_ASSOCIATION_WINDOW_MS) return ops.get(lastDispatchOpId) ?? null;
210
210
  return null;
211
211
  };
212
212
  processLike === null || processLike === void 0 || (_processLike$once = processLike.once) === null || _processLike$once === void 0 || _processLike$once.call(processLike, "exit", () => {
@@ -215,27 +215,27 @@ function createProfiler() {
215
215
  return {
216
216
  enabled: true,
217
217
  recordRawInput(_info) {
218
- lastRawInputAt = now$1();
218
+ lastRawInputAt = now$2();
219
219
  expireOldOps();
220
220
  },
221
221
  recordStdinDispatch(event, info) {
222
222
  const op = ensureOp(event);
223
223
  if (!op) return;
224
224
  if (op.rawInputAt == null && lastRawInputAt != null) op.rawInputAt = lastRawInputAt;
225
- if (op.stdinDispatchAt == null) op.stdinDispatchAt = now$1();
225
+ if (op.stdinDispatchAt == null) op.stdinDispatchAt = now$2();
226
226
  if (!op.parser && (info === null || info === void 0 ? void 0 : info.parser)) op.parser = String(info.parser);
227
227
  },
228
228
  recordEventDispatchStart(event) {
229
229
  const op = ensureOp(event);
230
230
  if (!op) return;
231
231
  currentDispatchOpId = op.id;
232
- if (op.dispatchStartAt == null) op.dispatchStartAt = now$1();
232
+ if (op.dispatchStartAt == null) op.dispatchStartAt = now$2();
233
233
  },
234
234
  recordEventDispatchEnd(event, info) {
235
235
  const op = ensureOp(event);
236
236
  if (!op) return;
237
237
  op.defaultPrevented = Boolean(info.defaultPrevented);
238
- if (op.dispatchEndAt == null) op.dispatchEndAt = now$1();
238
+ if (op.dispatchEndAt == null) op.dispatchEndAt = now$2();
239
239
  currentDispatchOpId = currentDispatchOpId === op.id ? null : currentDispatchOpId;
240
240
  lastDispatchOpId = op.id;
241
241
  lastDispatchEndedAt = op.dispatchEndAt;
@@ -243,7 +243,7 @@ function createProfiler() {
243
243
  recordSchedulerInvalidate(info) {
244
244
  const op = resolveActiveOp();
245
245
  if (!op) return;
246
- if (op.invalidateAt == null) op.invalidateAt = now$1();
246
+ if (op.invalidateAt == null) op.invalidateAt = now$2();
247
247
  if (!op.invalidatePriority && (info === null || info === void 0 ? void 0 : info.priority)) op.invalidatePriority = String(info.priority);
248
248
  if (!op.invalidatePlane && (info === null || info === void 0 ? void 0 : info.plane)) op.invalidatePlane = String(info.plane);
249
249
  pendingCommitIds.add(op.id);
@@ -251,7 +251,7 @@ function createProfiler() {
251
251
  recordFlushStart(info) {
252
252
  currentFlushIds = Array.from(pendingCommitIds);
253
253
  if (!currentFlushIds.length) return;
254
- const flushTime = now$1();
254
+ const flushTime = now$2();
255
255
  forIds(currentFlushIds, (op) => {
256
256
  if (op.flushStartAt == null) op.flushStartAt = flushTime;
257
257
  if (op.commitSync == null && (info === null || info === void 0 ? void 0 : info.sync) != null) op.commitSync = Boolean(info.sync);
@@ -260,7 +260,7 @@ function createProfiler() {
260
260
  },
261
261
  recordFlushEnd() {
262
262
  if (!currentFlushIds.length) return;
263
- const flushTime = now$1();
263
+ const flushTime = now$2();
264
264
  forIds(currentFlushIds, (op) => {
265
265
  if (op.flushEndAt == null) op.flushEndAt = flushTime;
266
266
  });
@@ -268,7 +268,7 @@ function createProfiler() {
268
268
  },
269
269
  recordCommit(info) {
270
270
  if (!pendingCommitIds.size) return;
271
- const commitTime = now$1();
271
+ const commitTime = now$2();
272
272
  const ids = Array.from(pendingCommitIds);
273
273
  pendingCommitIds.clear();
274
274
  forIds(ids, (op) => {
@@ -288,7 +288,7 @@ function createProfiler() {
288
288
  },
289
289
  recordStdoutRenderStart() {
290
290
  if (!pendingWriteIds.size) return;
291
- const renderTime = now$1();
291
+ const renderTime = now$2();
292
292
  forIds(pendingWriteIds, (op) => {
293
293
  if (op.stdoutRenderStartAt == null) op.stdoutRenderStartAt = renderTime;
294
294
  });
@@ -301,7 +301,7 @@ function createProfiler() {
301
301
  },
302
302
  recordStdoutWrite(info) {
303
303
  if (!pendingWriteIds.size) return;
304
- const writeEndTime = now$1();
304
+ const writeEndTime = now$2();
305
305
  const ids = Array.from(pendingWriteIds);
306
306
  pendingWriteIds.clear();
307
307
  forIds(ids, (op) => {
@@ -966,7 +966,7 @@ function setDebugFileWriter(writer) {
966
966
  if (!writer) initializedDebugLogPaths.clear();
967
967
  }
968
968
  function resolveDebugLogPath(env, fallback = DEFAULT_LOG_FILE) {
969
- return envString(env, "VUE_TUI_DEBUG_LOG_PATH", "DIMCODE_DEBUG_LOG_PATH", fallback);
969
+ return envString$1(env, "VUE_TUI_DEBUG_LOG_PATH", "DIMCODE_DEBUG_LOG_PATH", fallback);
970
970
  }
971
971
  function setDebugLogDefaultPath(path) {
972
972
  debugLogDefaultPath = path || DEFAULT_LOG_FILE;
@@ -1076,7 +1076,7 @@ function createTuiProfiler(name, options = {}) {
1076
1076
  if (!envFlag(env, "VUE_TUI_PROFILE", "DIMCODE_PROFILE_TUI")) return null;
1077
1077
  const format = parseLogFormat(firstNonEmptyEnv(env, "VUE_TUI_PROFILE_FORMAT", "DIMCODE_PROFILE_TUI_FORMAT")) ?? (String(firstNonEmptyEnv(env, "VUE_TUI_PROFILE_LOG_PATH", "DIMCODE_PROFILE_TUI_LOG_PATH") ?? "").trim().endsWith(".jsonl") ? "json" : "text");
1078
1078
  const logDest = parseLogDest(firstNonEmptyEnv(env, "VUE_TUI_PROFILE_LOG_DEST", "DIMCODE_PROFILE_TUI_LOG_DEST")) ?? options.defaultLogDest ?? "stdout";
1079
- const logPath = envString(env, "VUE_TUI_PROFILE_LOG_PATH", "DIMCODE_PROFILE_TUI_LOG_PATH") || options.defaultLogPath || "";
1079
+ const logPath = envString$1(env, "VUE_TUI_PROFILE_LOG_PATH", "DIMCODE_PROFILE_TUI_LOG_PATH") || options.defaultLogPath || "";
1080
1080
  let invalidates = 0;
1081
1081
  let renders = 0;
1082
1082
  let fullRenders = 0;
@@ -1095,8 +1095,8 @@ function createTuiProfiler(name, options = {}) {
1095
1095
  const invalidatePlaneCounts = new Map();
1096
1096
  const renderPlaneCounts = new Map();
1097
1097
  const logEveryMs = parseLogEveryMs(firstNonEmptyEnv(env, "VUE_TUI_PROFILE_LOG_EVERY_MS", "DIMCODE_PROFILE_TUI_LOG_EVERY_MS")) ?? 1e3;
1098
- const now$2 = defaultNow;
1099
- let lastLogAt = now$2();
1098
+ const now$3 = defaultNow;
1099
+ let lastLogAt = now$3();
1100
1100
  let disposed = false;
1101
1101
  function incrementPlaneCount(target, key) {
1102
1102
  const normalized = String(key ?? "all").trim() || "all";
@@ -1117,7 +1117,7 @@ function createTuiProfiler(name, options = {}) {
1117
1117
  function flushLog(force = false) {
1118
1118
  if (disposed) return;
1119
1119
  if (!hasPendingSamples()) return;
1120
- const at = now$2();
1120
+ const at = now$3();
1121
1121
  const elapsedRaw = at - lastLogAt;
1122
1122
  if (!force && elapsedRaw <= 0) return;
1123
1123
  const elapsed = Math.max(1, elapsedRaw);
@@ -1189,7 +1189,7 @@ function createTuiProfiler(name, options = {}) {
1189
1189
  const timer = setInterval(flushLog, Number.isFinite(logEveryMs) ? Math.max(100, logEveryMs) : 1e3);
1190
1190
  (_unref = timer.unref) === null || _unref === void 0 || _unref.call(timer);
1191
1191
  return {
1192
- now: now$2,
1192
+ now: now$3,
1193
1193
  recordInvalidate(info) {
1194
1194
  if (disposed) return;
1195
1195
  invalidates++;
@@ -1247,7 +1247,7 @@ function legacyDebugFlag(env) {
1247
1247
  return String((env === null || env === void 0 ? void 0 : env.DEBUG) ?? "").trim() === "1";
1248
1248
  }
1249
1249
  function shouldInstallFileWriters(env) {
1250
- const profileLogDest = envString(env, "VUE_TUI_PROFILE_LOG_DEST", "DIMCODE_PROFILE_TUI_LOG_DEST").toLowerCase();
1250
+ const profileLogDest = envString$1(env, "VUE_TUI_PROFILE_LOG_DEST", "DIMCODE_PROFILE_TUI_LOG_DEST").toLowerCase();
1251
1251
  return envFlag(env, "VUE_TUI_DEBUG", "DIMCODE_DEBUG") || legacyDebugFlag(env) || profileLogDest === "file" || profileLogDest === "both";
1252
1252
  }
1253
1253
  function installNodeFileWriters(options = {}) {
@@ -1277,7 +1277,7 @@ function parseMouseSgr(sequence) {
1277
1277
  const env = (process === null || process === void 0 ? void 0 : process.env) ?? {};
1278
1278
  const invertWheel = envFlag(env, "VUE_TUI_WHEEL_INVERT", "DIMCODE_WHEEL_INVERT");
1279
1279
  const mouseDebugEnabled = envFlag(env, "VUE_TUI_MOUSE_DEBUG", "DIMCODE_MOUSE_DEBUG");
1280
- const mouseDebugPath = envString(env, "VUE_TUI_MOUSE_DEBUG_PATH", "DIMCODE_MOUSE_DEBUG_PATH", defaultVueTuiMouseDebugLogPath());
1280
+ const mouseDebugPath = envString$1(env, "VUE_TUI_MOUSE_DEBUG_PATH", "DIMCODE_MOUSE_DEBUG_PATH", defaultVueTuiMouseDebugLogPath());
1281
1281
  const debug = (msg) => {
1282
1282
  if (!mouseDebugEnabled) return;
1283
1283
  try {
@@ -1696,13 +1696,13 @@ function parseXtermSequence(sequence) {
1696
1696
 
1697
1697
  //#endregion
1698
1698
  //#region src/cli/stdin-buffer.ts
1699
- const ESC$2 = "\x1B";
1699
+ const ESC$3 = "\x1B";
1700
1700
  const BRACKETED_PASTE_START = "\x1B[200~";
1701
1701
  const BRACKETED_PASTE_END = "\x1B[201~";
1702
1702
  const ESC_CONTINUATION_RESCUE_WINDOW_MS = 64;
1703
1703
  function looksLikeQuotedPathPaste(value) {
1704
1704
  if (!value) return false;
1705
- if (value.includes(ESC$2)) return false;
1705
+ if (value.includes(ESC$3)) return false;
1706
1706
  if (value.length < 12) return false;
1707
1707
  if (!/['"]/.test(value)) return false;
1708
1708
  if (!/[\\/]/.test(value)) return false;
@@ -1712,7 +1712,7 @@ function looksLikeQuotedPathPaste(value) {
1712
1712
  return false;
1713
1713
  }
1714
1714
  function isCompleteSequence(data) {
1715
- if (!data.startsWith(ESC$2)) return "not-escape";
1715
+ if (!data.startsWith(ESC$3)) return "not-escape";
1716
1716
  if (data.length === 1) return "incomplete";
1717
1717
  const afterEsc = data.slice(1);
1718
1718
  if (afterEsc.startsWith("[")) {
@@ -1727,7 +1727,7 @@ function isCompleteSequence(data) {
1727
1727
  return "complete";
1728
1728
  }
1729
1729
  function isCompleteCsiSequence(data) {
1730
- if (!data.startsWith(`${ESC$2}[`)) return "complete";
1730
+ if (!data.startsWith(`${ESC$3}[`)) return "complete";
1731
1731
  if (data.length < 3) return "incomplete";
1732
1732
  const payload = data.slice(2);
1733
1733
  const lastChar = payload[payload.length - 1];
@@ -1745,19 +1745,35 @@ function isCompleteCsiSequence(data) {
1745
1745
  }
1746
1746
  return "incomplete";
1747
1747
  }
1748
+ function isPotentialSgrMousePayloadPrefix(payload) {
1749
+ if (!payload.startsWith("<")) return false;
1750
+ let semicolons = 0;
1751
+ for (let i = 1; i < payload.length; i++) {
1752
+ const c = payload.charCodeAt(i);
1753
+ if (c >= 48 && c <= 57) continue;
1754
+ if (payload[i] === ";") {
1755
+ semicolons++;
1756
+ if (semicolons > 2) return false;
1757
+ continue;
1758
+ }
1759
+ if (payload[i] === "M" || payload[i] === "m") return semicolons === 2;
1760
+ return false;
1761
+ }
1762
+ return true;
1763
+ }
1748
1764
  function isCompleteOscSequence(data) {
1749
- if (!data.startsWith(`${ESC$2}]`)) return "complete";
1750
- if (data.endsWith(`${ESC$2}\\`) || data.endsWith("\x07")) return "complete";
1765
+ if (!data.startsWith(`${ESC$3}]`)) return "complete";
1766
+ if (data.endsWith(`${ESC$3}\\`) || data.endsWith("\x07")) return "complete";
1751
1767
  return "incomplete";
1752
1768
  }
1753
1769
  function isCompleteDcsSequence(data) {
1754
- if (!data.startsWith(`${ESC$2}P`)) return "complete";
1755
- if (data.endsWith(`${ESC$2}\\`)) return "complete";
1770
+ if (!data.startsWith(`${ESC$3}P`)) return "complete";
1771
+ if (data.endsWith(`${ESC$3}\\`)) return "complete";
1756
1772
  return "incomplete";
1757
1773
  }
1758
1774
  function isCompleteApcSequence(data) {
1759
- if (!data.startsWith(`${ESC$2}_`)) return "complete";
1760
- if (data.endsWith(`${ESC$2}\\`)) return "complete";
1775
+ if (!data.startsWith(`${ESC$3}_`)) return "complete";
1776
+ if (data.endsWith(`${ESC$3}\\`)) return "complete";
1761
1777
  return "incomplete";
1762
1778
  }
1763
1779
  function extractCompleteSequences(buffer) {
@@ -1765,9 +1781,9 @@ function extractCompleteSequences(buffer) {
1765
1781
  let pos = 0;
1766
1782
  while (pos < buffer.length) {
1767
1783
  const remaining = buffer.slice(pos);
1768
- if (remaining.startsWith(ESC$2)) {
1769
- if (remaining.length >= 2 && remaining[1] === ESC$2) {
1770
- sequences.push(ESC$2);
1784
+ if (remaining.startsWith(ESC$3)) {
1785
+ if (remaining.length >= 2 && remaining[1] === ESC$3) {
1786
+ sequences.push(ESC$3);
1771
1787
  pos += 1;
1772
1788
  continue;
1773
1789
  }
@@ -1802,17 +1818,24 @@ function extractCompleteSequences(buffer) {
1802
1818
  remainder: ""
1803
1819
  };
1804
1820
  }
1805
- function looksLikeSplitEscapeContinuation(value) {
1821
+ function looksLikeSplitEscapeContinuation(value, prefix = ESC$3) {
1822
+ if (prefix.startsWith(`${ESC$3}[`) && isPotentialSgrMousePayloadPrefix(`${prefix.slice(2)}${value}`)) return true;
1806
1823
  const first = value[0];
1807
1824
  if (first !== "[" && first !== "]" && first !== "P" && first !== "_" && first !== "O") return false;
1808
- return isCompleteSequence(`${ESC$2}${value}`) !== "not-escape";
1825
+ return prefix === ESC$3 && isCompleteSequence(`${ESC$3}${value}`) !== "not-escape";
1826
+ }
1827
+ function rescuableEscapePrefix(value) {
1828
+ if (value === ESC$3) return ESC$3;
1829
+ if (value === `${ESC$3}[`) return value;
1830
+ if (value.startsWith(`${ESC$3}[`) && isPotentialSgrMousePayloadPrefix(value.slice(2))) return value;
1831
+ return null;
1809
1832
  }
1810
1833
  var StdinBuffer = class extends EventEmitter {
1811
1834
  buffer = "";
1812
1835
  timeout = null;
1813
1836
  timeoutMs;
1814
1837
  escTimeoutMs;
1815
- pendingEscContinuationAt = null;
1838
+ pendingEscContinuation = null;
1816
1839
  pasteMode = false;
1817
1840
  pasteBuffer = "";
1818
1841
  constructor(options = {}) {
@@ -1831,10 +1854,11 @@ var StdinBuffer = class extends EventEmitter {
1831
1854
  str = `\x1B${String.fromCharCode(byte)}`;
1832
1855
  } else str = data.toString();
1833
1856
  else str = data;
1834
- if (this.pendingEscContinuationAt != null) {
1835
- const canRescue = Date.now() - this.pendingEscContinuationAt <= ESC_CONTINUATION_RESCUE_WINDOW_MS;
1836
- this.pendingEscContinuationAt = null;
1837
- if (canRescue && looksLikeSplitEscapeContinuation(str)) str = `${ESC$2}${str}`;
1857
+ if (this.pendingEscContinuation != null) {
1858
+ const pending = this.pendingEscContinuation;
1859
+ const canRescue = Date.now() - pending.at <= ESC_CONTINUATION_RESCUE_WINDOW_MS;
1860
+ this.pendingEscContinuation = null;
1861
+ if (canRescue && looksLikeSplitEscapeContinuation(str, pending.prefix)) str = `${pending.prefix}${str}`;
1838
1862
  }
1839
1863
  if (str.length === 0 && this.buffer.length === 0) {
1840
1864
  this.emit("data", "");
@@ -1887,11 +1911,14 @@ var StdinBuffer = class extends EventEmitter {
1887
1911
  this.buffer = result.remainder;
1888
1912
  for (const sequence of result.sequences) this.emit("data", sequence);
1889
1913
  if (this.buffer.length > 0) {
1890
- const timeoutMs = this.buffer === ESC$2 ? this.escTimeoutMs : this.timeoutMs;
1914
+ const timeoutMs = this.buffer === ESC$3 ? this.escTimeoutMs : this.timeoutMs;
1891
1915
  this.timeout = setTimeout(() => {
1892
- const rescuableLoneEsc = this.buffer === ESC$2;
1916
+ const prefix = rescuableEscapePrefix(this.buffer);
1893
1917
  const flushed = this.flush();
1894
- if (rescuableLoneEsc && flushed.length === 1 && flushed[0] === ESC$2) this.pendingEscContinuationAt = Date.now();
1918
+ if (prefix && flushed.length === 1 && flushed[0] === prefix) this.pendingEscContinuation = {
1919
+ prefix,
1920
+ at: Date.now()
1921
+ };
1895
1922
  for (const sequence of flushed) this.emit("data", sequence);
1896
1923
  }, timeoutMs);
1897
1924
  }
@@ -1912,7 +1939,7 @@ var StdinBuffer = class extends EventEmitter {
1912
1939
  this.timeout = null;
1913
1940
  }
1914
1941
  this.buffer = "";
1915
- this.pendingEscContinuationAt = null;
1942
+ this.pendingEscContinuation = null;
1916
1943
  this.pasteMode = false;
1917
1944
  this.pasteBuffer = "";
1918
1945
  }
@@ -2355,10 +2382,8 @@ function createStdinDriver(options) {
2355
2382
  if (stdout.isTTY) {
2356
2383
  let restore = "\x1B[?2004l\x1B[?1004l";
2357
2384
  if (keyboardProtocolSequences) restore += keyboardProtocolSequences.disable;
2358
- if (enableMouse) {
2359
- restore += "\x1B[?1007l";
2360
- restore += enableMouseMotion ? "\x1B[?1000l\x1B[?1003l\x1B[?1006l" : "\x1B[?1000l\x1B[?1002l\x1B[?1006l";
2361
- }
2385
+ restore += "\x1B[?1007l";
2386
+ restore += enableMouseMotion ? "\x1B[?1000l\x1B[?1003l\x1B[?1006l" : "\x1B[?1000l\x1B[?1002l\x1B[?1006l";
2362
2387
  writeTTYSyncOrStream(stdout, restore);
2363
2388
  }
2364
2389
  };
@@ -2370,7 +2395,21 @@ function createStdinDriver(options) {
2370
2395
  signalPolicy: typeof autoCleanup === "object" ? cleanupOptions.signalPolicy ?? "reraise" : "reraise"
2371
2396
  });
2372
2397
  }
2373
- return { dispose };
2398
+ const setMouseCapture = (enabled) => {
2399
+ if (disposed || !stdout.isTTY) return;
2400
+ if (enabled) {
2401
+ stdout.write("\x1B[?1007h");
2402
+ if (enableMouseMotion) stdout.write("\x1B[?1000h\x1B[?1003h\x1B[?1006h");
2403
+ else stdout.write("\x1B[?1000h\x1B[?1002h\x1B[?1006h");
2404
+ } else {
2405
+ stdout.write("\x1B[?1007l");
2406
+ stdout.write(enableMouseMotion ? "\x1B[?1000l\x1B[?1003l\x1B[?1006l" : "\x1B[?1000l\x1B[?1002l\x1B[?1006l");
2407
+ }
2408
+ };
2409
+ return {
2410
+ dispose,
2411
+ setMouseCapture
2412
+ };
2374
2413
  }
2375
2414
 
2376
2415
  //#endregion
@@ -2649,7 +2688,7 @@ function segmentedGraphemes(text) {
2649
2688
 
2650
2689
  //#endregion
2651
2690
  //#region src/core/ansi/sgr.ts
2652
- const ESC$1 = "\x1B";
2691
+ const ESC$2 = "\x1B";
2653
2692
  function ansi16ToColorName(code) {
2654
2693
  switch (code) {
2655
2694
  case 30: return "black";
@@ -2922,7 +2961,7 @@ function parseAnsiSgr(input, baseStyle = {}) {
2922
2961
  let style = { ...baseStyle };
2923
2962
  let lastIndex = 0;
2924
2963
  for (let i = 0; i < input.length; i++) {
2925
- if (input[i] !== ESC$1 || input[i + 1] !== "[") continue;
2964
+ if (input[i] !== ESC$2 || input[i + 1] !== "[") continue;
2926
2965
  let j = i + 2;
2927
2966
  while (j < input.length) {
2928
2967
  const c = input.charCodeAt(j);
@@ -2951,17 +2990,131 @@ function parseAnsiSgr(input, baseStyle = {}) {
2951
2990
  }
2952
2991
 
2953
2992
  //#endregion
2954
- //#region src/core/buffer/width.ts
2993
+ //#region src/core/buffer/eaw-ranges-unicode-17.ts
2955
2994
  const fullWidthRanges = [
2956
2995
  [4352, 4447],
2996
+ [8986, 8987],
2957
2997
  [9001, 9002],
2958
- [11904, 42191],
2998
+ [9193, 9196],
2999
+ [9200, 9200],
3000
+ [9203, 9203],
3001
+ [9725, 9726],
3002
+ [9748, 9749],
3003
+ [9776, 9783],
3004
+ [9800, 9811],
3005
+ [9855, 9855],
3006
+ [9866, 9871],
3007
+ [9875, 9875],
3008
+ [9889, 9889],
3009
+ [9898, 9899],
3010
+ [9917, 9918],
3011
+ [9924, 9925],
3012
+ [9934, 9934],
3013
+ [9940, 9940],
3014
+ [9962, 9962],
3015
+ [9970, 9971],
3016
+ [9973, 9973],
3017
+ [9978, 9978],
3018
+ [9981, 9981],
3019
+ [9989, 9989],
3020
+ [9994, 9995],
3021
+ [10024, 10024],
3022
+ [10060, 10060],
3023
+ [10062, 10062],
3024
+ [10067, 10069],
3025
+ [10071, 10071],
3026
+ [10133, 10135],
3027
+ [10160, 10160],
3028
+ [10175, 10175],
3029
+ [11035, 11036],
3030
+ [11088, 11088],
3031
+ [11093, 11093],
3032
+ [11904, 11929],
3033
+ [11931, 12019],
3034
+ [12032, 12245],
3035
+ [12272, 12350],
3036
+ [12353, 12438],
3037
+ [12441, 12543],
3038
+ [12549, 12591],
3039
+ [12593, 12686],
3040
+ [12688, 12773],
3041
+ [12783, 12830],
3042
+ [12832, 12871],
3043
+ [12880, 42124],
3044
+ [42128, 42182],
3045
+ [43360, 43388],
2959
3046
  [44032, 55203],
2960
3047
  [63744, 64255],
2961
3048
  [65040, 65049],
2962
- [65072, 65135],
2963
- [65280, 65376],
2964
- [65504, 65510]
3049
+ [65072, 65106],
3050
+ [65108, 65126],
3051
+ [65128, 65131],
3052
+ [65281, 65376],
3053
+ [65504, 65510],
3054
+ [94176, 94180],
3055
+ [94192, 94198],
3056
+ [94208, 101589],
3057
+ [101631, 101662],
3058
+ [101760, 101874],
3059
+ [110576, 110579],
3060
+ [110581, 110587],
3061
+ [110589, 110590],
3062
+ [110592, 110882],
3063
+ [110898, 110898],
3064
+ [110928, 110930],
3065
+ [110933, 110933],
3066
+ [110948, 110951],
3067
+ [110960, 111355],
3068
+ [119552, 119638],
3069
+ [119648, 119670],
3070
+ [126980, 126980],
3071
+ [127183, 127183],
3072
+ [127374, 127374],
3073
+ [127377, 127386],
3074
+ [127488, 127490],
3075
+ [127504, 127547],
3076
+ [127552, 127560],
3077
+ [127568, 127569],
3078
+ [127584, 127589],
3079
+ [127744, 127776],
3080
+ [127789, 127797],
3081
+ [127799, 127868],
3082
+ [127870, 127891],
3083
+ [127904, 127946],
3084
+ [127951, 127955],
3085
+ [127968, 127984],
3086
+ [127988, 127988],
3087
+ [127992, 128062],
3088
+ [128064, 128064],
3089
+ [128066, 128252],
3090
+ [128255, 128317],
3091
+ [128331, 128334],
3092
+ [128336, 128359],
3093
+ [128378, 128378],
3094
+ [128405, 128406],
3095
+ [128420, 128420],
3096
+ [128507, 128591],
3097
+ [128640, 128709],
3098
+ [128716, 128716],
3099
+ [128720, 128722],
3100
+ [128725, 128728],
3101
+ [128732, 128735],
3102
+ [128747, 128748],
3103
+ [128756, 128764],
3104
+ [128992, 129003],
3105
+ [129008, 129008],
3106
+ [129292, 129338],
3107
+ [129340, 129349],
3108
+ [129351, 129535],
3109
+ [129648, 129660],
3110
+ [129664, 129674],
3111
+ [129678, 129734],
3112
+ [129736, 129736],
3113
+ [129741, 129756],
3114
+ [129759, 129770],
3115
+ [129775, 129784],
3116
+ [131072, 196605],
3117
+ [196608, 262141]
2965
3118
  ];
2966
3119
  const ambiguousWidthRanges = [
2967
3120
  [161, 161],
@@ -3017,6 +3170,7 @@ const ambiguousWidthRanges = [
3017
3170
  [728, 731],
3018
3171
  [733, 733],
3019
3172
  [735, 735],
3173
+ [768, 879],
3020
3174
  [913, 929],
3021
3175
  [931, 937],
3022
3176
  [945, 961],
@@ -3024,7 +3178,8 @@ const ambiguousWidthRanges = [
3024
3178
  [1025, 1025],
3025
3179
  [1040, 1103],
3026
3180
  [1105, 1105],
3027
- [8208, 8213],
3181
+ [8208, 8208],
3182
+ [8211, 8214],
3028
3183
  [8216, 8217],
3029
3184
  [8220, 8221],
3030
3185
  [8224, 8226],
@@ -3086,7 +3241,8 @@ const ambiguousWidthRanges = [
3086
3241
  [8895, 8895],
3087
3242
  [8978, 8978],
3088
3243
  [9312, 9449],
3089
- [9451, 9471],
3244
+ [9451, 9547],
3245
+ [9552, 9587],
3090
3246
  [9600, 9615],
3091
3247
  [9618, 9621],
3092
3248
  [9632, 9633],
@@ -3112,14 +3268,40 @@ const ambiguousWidthRanges = [
3112
3268
  [9831, 9834],
3113
3269
  [9836, 9837],
3114
3270
  [9839, 9839],
3271
+ [9886, 9887],
3272
+ [9919, 9919],
3273
+ [9926, 9933],
3274
+ [9935, 9939],
3275
+ [9941, 9953],
3276
+ [9955, 9955],
3277
+ [9960, 9961],
3278
+ [9963, 9969],
3279
+ [9972, 9972],
3280
+ [9974, 9977],
3281
+ [9979, 9980],
3282
+ [9982, 9983],
3115
3283
  [10045, 10045],
3116
3284
  [10102, 10111],
3117
- [11094, 11094],
3285
+ [11094, 11097],
3286
+ [12872, 12879],
3118
3287
  [57344, 63743],
3119
- [65533, 65533]
3288
+ [65024, 65039],
3289
+ [65533, 65533],
3290
+ [127232, 127242],
3291
+ [127248, 127277],
3292
+ [127280, 127337],
3293
+ [127344, 127373],
3294
+ [127375, 127376],
3295
+ [127387, 127404],
3296
+ [917760, 917999],
3297
+ [983040, 1048573],
3298
+ [1048576, 1114109]
3120
3299
  ];
3300
+
3301
+ //#endregion
3302
+ //#region src/core/buffer/width.ts
3121
3303
  function isFullWidthCodePoint(codePoint) {
3122
- if (codePoint < 4352 || codePoint > 65510) return false;
3304
+ if (codePoint < 4352) return false;
3123
3305
  for (const [start, end] of fullWidthRanges) {
3124
3306
  if (codePoint < start) return false;
3125
3307
  if (codePoint <= end) return true;
@@ -3127,13 +3309,22 @@ function isFullWidthCodePoint(codePoint) {
3127
3309
  return false;
3128
3310
  }
3129
3311
  function isAmbiguousWidthCodePoint(codePoint) {
3130
- if (codePoint < 161 || codePoint > 65533) return false;
3312
+ const first = ambiguousWidthRanges[0];
3313
+ const last = ambiguousWidthRanges[ambiguousWidthRanges.length - 1];
3314
+ if (!first || !last) return false;
3315
+ if (codePoint < first[0] || codePoint > last[1]) return false;
3131
3316
  for (const [start, end] of ambiguousWidthRanges) {
3132
3317
  if (codePoint < start) return false;
3133
3318
  if (codePoint <= end) return true;
3134
3319
  }
3135
3320
  return false;
3136
3321
  }
3322
+ function isVariationSelectorCodePoint(codePoint) {
3323
+ return codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999;
3324
+ }
3325
+ function isCombiningMarkCodePoint(codePoint) {
3326
+ return codePoint >= 768 && codePoint <= 879 || codePoint >= 6832 && codePoint <= 6911 || codePoint >= 7616 && codePoint <= 7679 || codePoint >= 8400 && codePoint <= 8447 || codePoint >= 65056 && codePoint <= 65071 || codePoint >= 12330 && codePoint <= 12335 || codePoint >= 12441 && codePoint <= 12442 || codePoint === 94180;
3327
+ }
3137
3328
  function isEmojiLike(codePoint) {
3138
3329
  return codePoint >= 127744 && codePoint <= 129791 || codePoint >= 127462 && codePoint <= 127487;
3139
3330
  }
@@ -3168,6 +3359,8 @@ function charCellWidth(text, provider = "default") {
3168
3359
  const codePoint = text.codePointAt(0);
3169
3360
  if (codePoint == null) return 1;
3170
3361
  const hasVs16 = text.includes("️");
3362
+ if (isVariationSelectorCodePoint(codePoint) || isCombiningMarkCodePoint(codePoint)) return 1;
3363
+ if (codePoint >= 9472 && codePoint <= 9599) return 1;
3171
3364
  if (isFullWidthCodePoint(codePoint)) return 2;
3172
3365
  if (provider === "cjk" && isAmbiguousWidthCodePoint(codePoint)) return 2;
3173
3366
  if (isEmojiLike(codePoint)) return 2;
@@ -3601,9 +3794,12 @@ function resizeBuffer(buffer, cols, rows) {
3601
3794
  const nextCols = Math.max(0, Math.floor(cols));
3602
3795
  const nextRows = Math.max(0, Math.floor(rows));
3603
3796
  if (nextCols === buffer.cols && nextRows === buffer.rows) return;
3797
+ const prevRows = buffer.rows;
3798
+ const prevDirtyAll = buffer.dirtyAll;
3799
+ const prevDirtyBits = buffer.dirtyBits;
3604
3800
  const blank = createBlankCell();
3605
3801
  const nextGrid = Array.from({ length: nextRows }, () => Array.from({ length: nextCols }, () => blank));
3606
- const copyRows = Math.min(buffer.rows, nextRows);
3802
+ const copyRows = Math.min(prevRows, nextRows);
3607
3803
  const copyCols = Math.min(buffer.cols, nextCols);
3608
3804
  for (let y = 0; y < copyRows; y++) {
3609
3805
  const src = getBufferRow(buffer, y);
@@ -3629,10 +3825,21 @@ function resizeBuffer(buffer, cols, rows) {
3629
3825
  buffer.cols = nextCols;
3630
3826
  buffer.rows = nextRows;
3631
3827
  buffer.dirtyBits = new Uint8Array(nextRows);
3632
- buffer.dirtyCount = nextRows || 0;
3633
- buffer.dirtyMin = nextRows ? 0 : Number.POSITIVE_INFINITY;
3634
- buffer.dirtyMax = nextRows ? nextRows - 1 : -1;
3635
- buffer.dirtyAll = nextRows > 0;
3828
+ buffer.dirtyCount = 0;
3829
+ buffer.dirtyMin = Number.POSITIVE_INFINITY;
3830
+ buffer.dirtyMax = -1;
3831
+ buffer.dirtyAll = prevDirtyAll && nextRows > 0;
3832
+ if (buffer.dirtyAll) {
3833
+ buffer.dirtyCount = nextRows;
3834
+ buffer.dirtyMin = 0;
3835
+ buffer.dirtyMax = nextRows - 1;
3836
+ } else for (let y = 0; y < copyRows; y++) {
3837
+ if (prevDirtyBits[y] !== 1) continue;
3838
+ buffer.dirtyBits[y] = 1;
3839
+ buffer.dirtyCount++;
3840
+ if (y < buffer.dirtyMin) buffer.dirtyMin = y;
3841
+ if (y > buffer.dirtyMax) buffer.dirtyMax = y;
3842
+ }
3636
3843
  buffer.cursorX = clamp(buffer.cursorX, 0, Math.max(0, nextCols));
3637
3844
  buffer.cursorY = clamp(buffer.cursorY, 0, Math.max(0, nextRows - 1));
3638
3845
  if (buffer.fingerprintFn) {
@@ -3705,6 +3912,10 @@ function mergePlanes(prev, next) {
3705
3912
  return Array.from(merged);
3706
3913
  }
3707
3914
  const TERMINAL_PLANE_INTERNALS = Symbol.for("@simon_he/vue-tui:v1:terminal-plane-internals");
3915
+ const TERMINAL_ROOT = Symbol.for("@simon_he/vue-tui:v1:terminal-root");
3916
+ function getRootTerminal(terminal) {
3917
+ return terminal[TERMINAL_ROOT] ?? terminal;
3918
+ }
3708
3919
  function createCoverage(cols, rows) {
3709
3920
  return Array.from({ length: rows }, () => new Uint8Array(cols));
3710
3921
  }
@@ -3789,6 +4000,8 @@ function markPlaneCoverageRange(state, x0, y0, x1, y1, value) {
3789
4000
  }
3790
4001
  }
3791
4002
  function markPlaneCoverageForPut(state, x, y, ch, prevCell) {
4003
+ if (!Number.isSafeInteger(x) || !Number.isSafeInteger(y)) return;
4004
+ if (x < 0 || x >= state.buffer.cols || y < 0 || y >= state.buffer.rows) return;
3792
4005
  const row = state.coverage[y];
3793
4006
  if (!row) return;
3794
4007
  if ((prevCell === null || prevCell === void 0 ? void 0 : prevCell.continuation) && x > 0) row[x - 1] = 1;
@@ -3912,10 +4125,14 @@ function createTerminal(opts) {
3912
4125
  }
3913
4126
  function putForPlane(plane, x, y, ch, style) {
3914
4127
  const state = getPlaneState(plane);
3915
- if (y < 0 || y >= state.buffer.rows || x < 0 || x >= state.buffer.cols) return;
3916
- const prevCell = getBufferCell(state.buffer, x, y);
3917
- putCell(state.buffer, x, y, ch, style);
3918
- markPlaneCoverageForPut(state, x, y, ch, prevCell);
4128
+ if (typeof x !== "number" || typeof y !== "number") return;
4129
+ const cellX = Math.floor(x);
4130
+ const cellY = Math.floor(y);
4131
+ if (!Number.isFinite(cellX) || !Number.isFinite(cellY)) return;
4132
+ if (cellY < 0 || cellY >= state.buffer.rows || cellX < 0 || cellX >= state.buffer.cols) return;
4133
+ const prevCell = getBufferCell(state.buffer, cellX, cellY);
4134
+ putCell(state.buffer, cellX, cellY, ch, style);
4135
+ markPlaneCoverageForPut(state, cellX, cellY, ch, prevCell);
3919
4136
  }
3920
4137
  function writeAtForPlane(plane, text, x, y, style) {
3921
4138
  const state = getPlaneState(plane);
@@ -4419,6 +4636,7 @@ function createTerminal(opts) {
4419
4636
  return getRowFingerprints(state.buffer, yy);
4420
4637
  }
4421
4638
  };
4639
+ api[TERMINAL_ROOT] = base;
4422
4640
  planeTerminals.set(plane, api);
4423
4641
  return api;
4424
4642
  }
@@ -4502,7 +4720,7 @@ function createTerminal(opts) {
4502
4720
  const rows = collectAndClearDirtyRows(state.buffer);
4503
4721
  if (rows === null) {
4504
4722
  composeAllRows = true;
4505
- break;
4723
+ continue;
4506
4724
  }
4507
4725
  for (const y of rows) dirtyRows.add(y);
4508
4726
  }
@@ -4624,6 +4842,7 @@ function createTerminal(opts) {
4624
4842
  scrollRowsForPlane(plane, startY, endY, lines);
4625
4843
  }
4626
4844
  };
4845
+ base[TERMINAL_ROOT] = base;
4627
4846
  return base;
4628
4847
  }
4629
4848
 
@@ -4650,7 +4869,7 @@ function sameRect$1(a, b) {
4650
4869
  function isVisible(node) {
4651
4870
  return node.visible !== false;
4652
4871
  }
4653
- function now() {
4872
+ function now$1() {
4654
4873
  return typeof performance !== "undefined" ? performance.now() : Date.now();
4655
4874
  }
4656
4875
  function rectRowRange(rect) {
@@ -4830,7 +5049,7 @@ function createCliEventManager(options) {
4830
5049
  bubbles: true,
4831
5050
  cancelable: true,
4832
5051
  defaultPrevented: false,
4833
- timeStamp: typeof time === "number" ? time : now(),
5052
+ timeStamp: typeof time === "number" ? time : now$1(),
4834
5053
  __stopped: false,
4835
5054
  stopPropagation() {
4836
5055
  this.__stopped = true;
@@ -5196,6 +5415,94 @@ const FRAME_PERF_REASON_PRIORITY = {
5196
5415
  scroll: 6,
5197
5416
  input: 7
5198
5417
  };
5418
+ const EMPTY_METRIC_STATS = Object.freeze({
5419
+ avg: 0,
5420
+ max: 0,
5421
+ min: 0
5422
+ });
5423
+ function metricStats(values) {
5424
+ if (!values.length) return EMPTY_METRIC_STATS;
5425
+ let sum = 0;
5426
+ let max = Number.NEGATIVE_INFINITY;
5427
+ let min = Number.POSITIVE_INFINITY;
5428
+ for (const value of values) {
5429
+ sum += value;
5430
+ if (value > max) max = value;
5431
+ if (value < min) min = value;
5432
+ }
5433
+ return {
5434
+ avg: sum / values.length,
5435
+ max,
5436
+ min
5437
+ };
5438
+ }
5439
+ function summarizeFramePerf(samples) {
5440
+ const durationMs = [];
5441
+ const renderManagerMs = [];
5442
+ const commitMs = [];
5443
+ const domFlushMs = [];
5444
+ const stdoutFlushMs = [];
5445
+ const dirtyRows = [];
5446
+ const scannedNodes = [];
5447
+ const paintedNodes = [];
5448
+ const reasons = {};
5449
+ const activePlanes = {};
5450
+ let fullDirtyFrames = 0;
5451
+ let coalescedInvalidates = 0;
5452
+ let coalescedFrameTasks = 0;
5453
+ let droppedUpdates = 0;
5454
+ let maxQueueDepth = 0;
5455
+ let rowBucketFallbacks = 0;
5456
+ for (const sample of samples) {
5457
+ var _sample$rowBucketFall;
5458
+ durationMs.push(sample.durationMs);
5459
+ renderManagerMs.push(sample.renderManagerMs);
5460
+ commitMs.push(sample.commitMs);
5461
+ scannedNodes.push(sample.scannedNodes);
5462
+ paintedNodes.push(sample.paintedNodes);
5463
+ if (sample.domFlushMs != null) domFlushMs.push(sample.domFlushMs);
5464
+ if (sample.stdoutFlushMs != null) stdoutFlushMs.push(sample.stdoutFlushMs);
5465
+ if (sample.dirtyRows == null) fullDirtyFrames++;
5466
+ else dirtyRows.push(sample.dirtyRows);
5467
+ reasons[sample.reason] = (reasons[sample.reason] ?? 0) + 1;
5468
+ const planes = sample.activePlanes;
5469
+ if (!planes) activePlanes.all = (activePlanes.all ?? 0) + 1;
5470
+ else for (const plane of planes) activePlanes[plane] = (activePlanes[plane] ?? 0) + 1;
5471
+ coalescedInvalidates += sample.coalescedInvalidates;
5472
+ coalescedFrameTasks += sample.coalescedFrameTasks;
5473
+ droppedUpdates += sample.droppedUpdates;
5474
+ if (sample.queueDepth > maxQueueDepth) maxQueueDepth = sample.queueDepth;
5475
+ rowBucketFallbacks += ((_sample$rowBucketFall = sample.rowBucketFallbacks) === null || _sample$rowBucketFall === void 0 ? void 0 : _sample$rowBucketFall.length) ?? 0;
5476
+ }
5477
+ const first = samples[0] ?? null;
5478
+ const latest = samples[samples.length - 1] ?? null;
5479
+ return {
5480
+ frames: samples.length,
5481
+ firstFrameId: (first === null || first === void 0 ? void 0 : first.frameId) ?? null,
5482
+ latestFrameId: (latest === null || latest === void 0 ? void 0 : latest.frameId) ?? null,
5483
+ windowMs: first && latest ? Math.max(0, latest.startedAt - first.startedAt) : 0,
5484
+ durationMs: metricStats(durationMs),
5485
+ renderManagerMs: metricStats(renderManagerMs),
5486
+ commitMs: metricStats(commitMs),
5487
+ domFlushMs: metricStats(domFlushMs),
5488
+ stdoutFlushMs: metricStats(stdoutFlushMs),
5489
+ dirtyRows: {
5490
+ avg: dirtyRows.length ? dirtyRows.reduce((sum, value) => sum + value, 0) / dirtyRows.length : 0,
5491
+ max: dirtyRows.length ? Math.max(...dirtyRows) : 0,
5492
+ sampledFrames: dirtyRows.length,
5493
+ fullFrames: fullDirtyFrames
5494
+ },
5495
+ scannedNodes: metricStats(scannedNodes),
5496
+ paintedNodes: metricStats(paintedNodes),
5497
+ coalescedInvalidates,
5498
+ coalescedFrameTasks,
5499
+ droppedUpdates,
5500
+ maxQueueDepth,
5501
+ rowBucketFallbacks,
5502
+ reasons,
5503
+ activePlanes
5504
+ };
5505
+ }
5199
5506
  function framePerfNow() {
5200
5507
  const p = globalThis.performance;
5201
5508
  if (p && typeof p.now === "function") return p.now();
@@ -5336,6 +5643,9 @@ function createFramePerfStore(limit = 120, opts) {
5336
5643
  function list() {
5337
5644
  return samples.slice();
5338
5645
  }
5646
+ function summary() {
5647
+ return summarizeFramePerf(samples);
5648
+ }
5339
5649
  function clear() {
5340
5650
  samples.length = 0;
5341
5651
  }
@@ -5348,6 +5658,7 @@ function createFramePerfStore(limit = 120, opts) {
5348
5658
  addSink,
5349
5659
  latest,
5350
5660
  list,
5661
+ summary,
5351
5662
  clear
5352
5663
  };
5353
5664
  }
@@ -5463,6 +5774,9 @@ function clampPointToRect(point, rect) {
5463
5774
  y: Math.max(rect.y, Math.min(rect.y + Math.max(0, rect.h - 1), Math.floor(point.y)))
5464
5775
  };
5465
5776
  }
5777
+ function clipboardCanWrite$1(api) {
5778
+ return api.canWrite ?? api.supported;
5779
+ }
5466
5780
  function createTerminalSelectionController(options) {
5467
5781
  const state = shallowRef(EMPTY_STATE);
5468
5782
  let range = null;
@@ -5664,7 +5978,7 @@ function createTerminalSelectionController(options) {
5664
5978
  const text = state.value.text || selectedText();
5665
5979
  if (!text) return false;
5666
5980
  setResolvedText(text);
5667
- if (!options.clipboard.supported) {
5981
+ if (!clipboardCanWrite$1(options.clipboard)) {
5668
5982
  var _options$onCopy;
5669
5983
  (_options$onCopy = options.onCopy) === null || _options$onCopy === void 0 || _options$onCopy.call(options, copyPayload(text, false, new Error("Clipboard unavailable")));
5670
5984
  return false;
@@ -5769,16 +6083,19 @@ function createOsc52ClipboardProvider(options = {}) {
5769
6083
  var _stdout$write;
5770
6084
  stdout === null || stdout === void 0 || (_stdout$write = stdout.write) === null || _stdout$write === void 0 || _stdout$write.call(stdout, sequence);
5771
6085
  });
5772
- const supported = options.supported ?? Boolean(options.write || (stdout === null || stdout === void 0 ? void 0 : stdout.write) && stdout.isTTY);
6086
+ const canWrite = options.supported ?? Boolean(options.write || (stdout === null || stdout === void 0 ? void 0 : stdout.write) && stdout.isTTY);
6087
+ const canRead = typeof options.readText === "function";
5773
6088
  const target = normalizeOsc52Target(options.target);
5774
6089
  return {
5775
- supported,
6090
+ supported: canWrite,
6091
+ canRead,
6092
+ canWrite,
5776
6093
  async readText() {
5777
- if (!options.readText) throw new Error("Clipboard read not available in this runtime");
6094
+ if (!canRead) throw new Error("Clipboard read not available in this runtime");
5778
6095
  return options.readText();
5779
6096
  },
5780
6097
  async writeText(text) {
5781
- if (!supported) throw new Error("Clipboard not available in this runtime");
6098
+ if (!canWrite) throw new Error("Clipboard not available in this runtime");
5782
6099
  const bytes = encodeTextBytes(text);
5783
6100
  const maxBytes = resolveMaxBytes(options.maxBytes, 100 * 1024);
5784
6101
  if (bytes.byteLength > maxBytes) throw new Error(`OSC52 clipboard payload too large: ${bytes.byteLength} bytes`);
@@ -6133,6 +6450,120 @@ function normalizeTerminalLinkOpener(opener) {
6133
6450
  return opener;
6134
6451
  }
6135
6452
 
6453
+ //#endregion
6454
+ //#region src/renderer/terminal-graphics-trace.ts
6455
+ const metrics$1 = {
6456
+ requests: 0,
6457
+ skippedHidden: 0,
6458
+ skippedScrolling: 0,
6459
+ cacheHits: 0,
6460
+ cacheMisses: 0,
6461
+ cacheStores: 0,
6462
+ rendererRuns: 0,
6463
+ rendererAborts: 0,
6464
+ rendererErrors: 0,
6465
+ queued: 0,
6466
+ queueWaits: 0,
6467
+ queueDeduped: 0,
6468
+ cleared: 0,
6469
+ bytesQueued: 0,
6470
+ totalRendererMs: 0,
6471
+ totalValidateMs: 0,
6472
+ totalQueueWaitMs: 0,
6473
+ maxQueueWaitMs: 0,
6474
+ maxActiveRenders: 0,
6475
+ maxWaitingRenders: 0,
6476
+ maxCacheEntries: 0,
6477
+ maxCacheBytes: 0,
6478
+ scrollStarts: 0,
6479
+ scrollMarks: 0,
6480
+ scrollIdles: 0,
6481
+ totalScrollMs: 0,
6482
+ maxScrollMs: 0
6483
+ };
6484
+ const subscribers = new Set();
6485
+ function nowTerminalGraphicTraceTime() {
6486
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
6487
+ }
6488
+ function recordTerminalGraphicTrace(event) {
6489
+ const normalized = {
6490
+ ...event,
6491
+ timestamp: event.timestamp ?? nowTerminalGraphicTraceTime()
6492
+ };
6493
+ switch (normalized.type) {
6494
+ case "request":
6495
+ metrics$1.requests++;
6496
+ break;
6497
+ case "skip-hidden":
6498
+ metrics$1.skippedHidden++;
6499
+ break;
6500
+ case "skip-scrolling":
6501
+ metrics$1.skippedScrolling++;
6502
+ break;
6503
+ case "cache-hit":
6504
+ metrics$1.cacheHits++;
6505
+ break;
6506
+ case "cache-miss":
6507
+ metrics$1.cacheMisses++;
6508
+ break;
6509
+ case "cache-store":
6510
+ metrics$1.cacheStores++;
6511
+ break;
6512
+ case "renderer-start":
6513
+ metrics$1.rendererRuns++;
6514
+ break;
6515
+ case "renderer-end":
6516
+ metrics$1.totalRendererMs += normalized.durationMs ?? 0;
6517
+ break;
6518
+ case "renderer-abort":
6519
+ metrics$1.rendererAborts++;
6520
+ break;
6521
+ case "renderer-error":
6522
+ metrics$1.rendererErrors++;
6523
+ break;
6524
+ case "validate-end":
6525
+ metrics$1.totalValidateMs += normalized.durationMs ?? 0;
6526
+ break;
6527
+ case "queue":
6528
+ metrics$1.queued++;
6529
+ metrics$1.bytesQueued += normalized.bytes ?? 0;
6530
+ break;
6531
+ case "queue-wait": {
6532
+ const durationMs = normalized.durationMs ?? 0;
6533
+ metrics$1.queueWaits++;
6534
+ metrics$1.totalQueueWaitMs += durationMs;
6535
+ metrics$1.maxQueueWaitMs = Math.max(metrics$1.maxQueueWaitMs, durationMs);
6536
+ break;
6537
+ }
6538
+ case "queue-dedupe":
6539
+ metrics$1.queueDeduped++;
6540
+ break;
6541
+ case "clear":
6542
+ metrics$1.cleared++;
6543
+ break;
6544
+ case "scroll-start":
6545
+ metrics$1.scrollStarts++;
6546
+ break;
6547
+ case "scroll-mark":
6548
+ metrics$1.scrollMarks++;
6549
+ break;
6550
+ case "scroll-idle": {
6551
+ const durationMs = normalized.durationMs ?? 0;
6552
+ metrics$1.scrollIdles++;
6553
+ metrics$1.totalScrollMs += durationMs;
6554
+ metrics$1.maxScrollMs = Math.max(metrics$1.maxScrollMs, durationMs);
6555
+ break;
6556
+ }
6557
+ }
6558
+ if (normalized.active != null && Number.isFinite(normalized.active)) metrics$1.maxActiveRenders = Math.max(metrics$1.maxActiveRenders, Math.max(0, normalized.active));
6559
+ if (normalized.waiting != null && Number.isFinite(normalized.waiting)) metrics$1.maxWaitingRenders = Math.max(metrics$1.maxWaitingRenders, Math.max(0, normalized.waiting));
6560
+ if (normalized.cacheEntries != null && Number.isFinite(normalized.cacheEntries)) metrics$1.maxCacheEntries = Math.max(metrics$1.maxCacheEntries, Math.max(0, normalized.cacheEntries));
6561
+ if (normalized.cacheBytes != null && Number.isFinite(normalized.cacheBytes)) metrics$1.maxCacheBytes = Math.max(metrics$1.maxCacheBytes, Math.max(0, normalized.cacheBytes));
6562
+ for (const subscriber of subscribers) try {
6563
+ subscriber(normalized);
6564
+ } catch {}
6565
+ }
6566
+
6136
6567
  //#endregion
6137
6568
  //#region src/vue/context.ts
6138
6569
  const TerminalContextKey = injectionKey("TerminalContext");
@@ -6143,6 +6574,7 @@ const RenderPlaneContextKey = injectionKey("RenderPlane");
6143
6574
  const ImeAnchorContextKey = injectionKey("ImeAnchor");
6144
6575
  const TInputPluginsContextKey = injectionKey("TInputPlugins");
6145
6576
  const TPathPickerProviderContextKey = injectionKey("TPathPickerProvider");
6577
+ const TerminalGraphicsActivityKey = injectionKey("TerminalGraphicsActivity");
6146
6578
  const DialogContextKey = injectionKey("DialogContext");
6147
6579
 
6148
6580
  //#endregion
@@ -6293,6 +6725,10 @@ function sameRect(a, b) {
6293
6725
  if (!a || !b) return false;
6294
6726
  return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
6295
6727
  }
6728
+ function rectsIntersect(a, b) {
6729
+ if (isEmptyRect(a) || isEmptyRect(b)) return false;
6730
+ return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
6731
+ }
6296
6732
  function createRenderManager(terminal, options = {}) {
6297
6733
  const widthProvider = options.widthProvider ?? "default";
6298
6734
  const withRenderTextPass = (fn) => withTextRenderPass(fn, widthProvider);
@@ -6300,6 +6736,7 @@ function createRenderManager(terminal, options = {}) {
6300
6736
  const nodes = new Map();
6301
6737
  const planeDirtyStates = new Map();
6302
6738
  const initialSize = terminal.size();
6739
+ let terminalCols = initialSize.cols;
6303
6740
  let terminalRows = initialSize.rows;
6304
6741
  let allRows = Array.from({ length: terminalRows }, (_, index) => index);
6305
6742
  let sortedNodes = [];
@@ -6320,19 +6757,37 @@ function createRenderManager(terminal, options = {}) {
6320
6757
  let disposed = false;
6321
6758
  const stackPathCache = new WeakMap();
6322
6759
  const profiler = createTuiProfiler("render-manager", options.profiler);
6323
- const offResize = terminal.on("resize", ({ rows }) => {
6760
+ const offResize = terminal.on("resize", ({ cols, rows }) => {
6324
6761
  if (disposed) return;
6762
+ const colsChanged = cols !== terminalCols;
6763
+ const prevRows = terminalRows;
6764
+ terminalCols = cols;
6325
6765
  terminalRows = rows;
6326
6766
  allRows = Array.from({ length: terminalRows }, (_, index) => index);
6327
6767
  for (const state of planeDirtyStates.values()) {
6328
- state.allRowsDirty = true;
6768
+ const prevBits = state.dirtyRowBits;
6329
6769
  state.dirtyRowBits = new Uint8Array(terminalRows);
6330
6770
  state.dirtyRowCount = 0;
6331
6771
  state.dirtyMinY = Number.POSITIVE_INFINITY;
6332
6772
  state.dirtyMaxY = -1;
6773
+ const limit = Math.min(terminalRows, prevBits.length);
6774
+ for (let y = 0; y < limit; y++) {
6775
+ if (prevBits[y] !== 1) continue;
6776
+ state.dirtyRowBits[y] = 1;
6777
+ state.dirtyRowCount++;
6778
+ if (y < state.dirtyMinY) state.dirtyMinY = y;
6779
+ if (y > state.dirtyMaxY) state.dirtyMaxY = y;
6780
+ }
6781
+ for (let y = prevRows; y < terminalRows; y++) {
6782
+ if (state.dirtyRowBits[y] === 1) continue;
6783
+ state.dirtyRowBits[y] = 1;
6784
+ state.dirtyRowCount++;
6785
+ if (y < state.dirtyMinY) state.dirtyMinY = y;
6786
+ if (y > state.dirtyMaxY) state.dirtyMaxY = y;
6787
+ }
6333
6788
  }
6334
6789
  rebuildRowBuckets();
6335
- clearTextCaches();
6790
+ if (colsChanged) clearTextCaches();
6336
6791
  });
6337
6792
  const rootStack = Object.freeze({
6338
6793
  id: `s${nextStackId++}`,
@@ -6531,6 +6986,33 @@ function createRenderManager(terminal, options = {}) {
6531
6986
  if (y > state.dirtyMaxY) state.dirtyMaxY = y;
6532
6987
  }
6533
6988
  }
6989
+ function markLowerPlanesForRect(plane, rect) {
6990
+ const planeIndex = TERMINAL_RENDER_PLANES.indexOf(plane);
6991
+ if (planeIndex <= 0) return;
6992
+ for (let i = 0; i < planeIndex; i++) markRect(TERMINAL_RENDER_PLANES[i], rect);
6993
+ }
6994
+ function markRowRange(plane, startY, endY) {
6995
+ const state = getDirtyState(plane);
6996
+ const start = Math.max(0, Math.min(terminalRows, Math.floor(startY)));
6997
+ const end = Math.max(start, Math.min(terminalRows, Math.floor(endY)));
6998
+ for (let y = start; y < end; y++) {
6999
+ if (state.dirtyRowBits[y] === 1) continue;
7000
+ state.dirtyRowBits[y] = 1;
7001
+ state.dirtyRowCount++;
7002
+ if (y < state.dirtyMinY) state.dirtyMinY = y;
7003
+ if (y > state.dirtyMaxY) state.dirtyMaxY = y;
7004
+ }
7005
+ }
7006
+ function markRectHeightDelta(plane, prev, next) {
7007
+ if (!prev || !next) return false;
7008
+ if (prev.x !== next.x || prev.y !== next.y || prev.w !== next.w) return false;
7009
+ const prevBounds = rectToYBounds(prev);
7010
+ const nextBounds = rectToYBounds(next);
7011
+ if (prevBounds.y0 !== nextBounds.y0) return false;
7012
+ if (nextBounds.y1 > prevBounds.y1) markRowRange(plane, prevBounds.y1, nextBounds.y1);
7013
+ else if (prevBounds.y1 > nextBounds.y1) markRowRange(plane, nextBounds.y1, prevBounds.y1);
7014
+ return true;
7015
+ }
6534
7016
  function markRowsForNode(node, rows) {
6535
7017
  if (!rows.length) return false;
6536
7018
  warnIfRowsLookLocal(node, rows);
@@ -6578,6 +7060,7 @@ function createRenderManager(terminal, options = {}) {
6578
7060
  nodes.set(id, full);
6579
7061
  addToRowBuckets(full);
6580
7062
  markRect(full.plane, full.rect);
7063
+ markLowerPlanesForRect(full.plane, full.rect);
6581
7064
  sortedDirty = true;
6582
7065
  return full;
6583
7066
  }
@@ -6589,6 +7072,7 @@ function createRenderManager(terminal, options = {}) {
6589
7072
  const nextPlane = next.plane ?? prev.plane;
6590
7073
  const planeChanged = nextPlane !== prev.plane;
6591
7074
  const hasRect = Object.prototype.hasOwnProperty.call(next, "rect");
7075
+ const paintChanged = Object.prototype.hasOwnProperty.call(next, "paint");
6592
7076
  const nextRect = hasRect ? next.rect ?? null : prev.rect;
6593
7077
  const rectChanged = !sameRect(prev.rect, nextRect);
6594
7078
  const bucketChanged = planeChanged || rectChanged;
@@ -6596,10 +7080,12 @@ function createRenderManager(terminal, options = {}) {
6596
7080
  const dirtyRowsHint = next.dirtyRowsHint;
6597
7081
  const canUseDirtyRowsHint = !sortChanged && !rectChanged && nextPlane === prev.plane && dirtyRowsHint != null && dirtyRowsHint.length > 0;
6598
7082
  if (canUseDirtyRowsHint) markRowsForNode(prev, dirtyRowsHint);
6599
- else {
7083
+ else if (rectChanged && !paintChanged && !sortChanged && !planeChanged && markRectHeightDelta(prev.plane, prev.rect, nextRect)) {} else {
6600
7084
  markRect(prev.plane, prev.rect);
6601
7085
  markRect(nextPlane, nextRect);
6602
7086
  }
7087
+ markLowerPlanesForRect(prev.plane, prev.rect);
7088
+ markLowerPlanesForRect(nextPlane, nextRect);
6603
7089
  if (bucketChanged) removeFromRowBuckets(prev);
6604
7090
  const full = Object.freeze({
6605
7091
  ...prev,
@@ -6629,11 +7115,41 @@ function createRenderManager(terminal, options = {}) {
6629
7115
  if (!node) return false;
6630
7116
  return markRowsForNode(node, rows);
6631
7117
  }
7118
+ function isRectCoveredByHigherNode(id, rect, options$1 = {}) {
7119
+ return higherNodeCoverageRects(id, rect, options$1).length > 0;
7120
+ }
7121
+ function higherNodeCoverageRects(id, rect, options$1 = {}) {
7122
+ if (disposed || isEmptyRect(rect)) return [];
7123
+ const owner = nodes.get(id);
7124
+ if (!owner) return [];
7125
+ const ownerPlaneIndex = TERMINAL_RENDER_PLANES.indexOf(owner.plane);
7126
+ if (ownerPlaneIndex < 0) return [];
7127
+ const out = [];
7128
+ for (const candidate of nodes.values()) {
7129
+ if (candidate.id === id || !candidate.rect || isEmptyRect(candidate.rect)) continue;
7130
+ if (!rectsIntersect(rect, candidate.rect)) continue;
7131
+ const candidatePlaneIndex = TERMINAL_RENDER_PLANES.indexOf(candidate.plane);
7132
+ const covered = candidatePlaneIndex > ownerPlaneIndex || !options$1.ignoreSamePlane && candidatePlaneIndex === ownerPlaneIndex && compareNodes(owner, candidate) < 0;
7133
+ if (!covered) continue;
7134
+ const x0 = Math.max(rect.x, candidate.rect.x);
7135
+ const y0 = Math.max(rect.y, candidate.rect.y);
7136
+ const x1 = Math.min(rect.x + rect.w, candidate.rect.x + candidate.rect.w);
7137
+ const y1 = Math.min(rect.y + rect.h, candidate.rect.y + candidate.rect.h);
7138
+ if (x1 > x0 && y1 > y0) out.push({
7139
+ x: x0,
7140
+ y: y0,
7141
+ w: x1 - x0,
7142
+ h: y1 - y0
7143
+ });
7144
+ }
7145
+ return out;
7146
+ }
6632
7147
  function unregister(id) {
6633
7148
  if (disposed) return;
6634
7149
  const prev = nodes.get(id);
6635
7150
  if (prev) {
6636
7151
  markRect(prev.plane, prev.rect);
7152
+ markLowerPlanesForRect(prev.plane, prev.rect);
6637
7153
  removeFromRowBuckets(prev);
6638
7154
  warnedLocalDirtyRows.delete(id);
6639
7155
  }
@@ -6849,6 +7365,8 @@ function createRenderManager(terminal, options = {}) {
6849
7365
  register,
6850
7366
  update,
6851
7367
  markDirtyRows,
7368
+ isRectCoveredByHigherNode,
7369
+ higherNodeCoverageRects,
6852
7370
  unregister,
6853
7371
  render,
6854
7372
  dispose() {
@@ -7247,6 +7765,8 @@ function resolveSelectionConfig(config) {
7247
7765
  }
7248
7766
  const unsupportedClipboard = {
7249
7767
  supported: false,
7768
+ canRead: false,
7769
+ canWrite: false,
7250
7770
  async readText() {
7251
7771
  return "";
7252
7772
  },
@@ -7254,6 +7774,12 @@ const unsupportedClipboard = {
7254
7774
  throw new Error("Clipboard unavailable");
7255
7775
  }
7256
7776
  };
7777
+ function clipboardCanRead(api) {
7778
+ return api.canRead ?? api.supported;
7779
+ }
7780
+ function clipboardCanWrite(api) {
7781
+ return api.canWrite ?? api.supported;
7782
+ }
7257
7783
  function isPlainObject(v) {
7258
7784
  if (!v || typeof v !== "object") return false;
7259
7785
  const proto = Object.getPrototypeOf(v);
@@ -7285,10 +7811,12 @@ function shallowEqualRecord(a, b) {
7285
7811
  function createTerminalApp(options) {
7286
7812
  const env = (process === null || process === void 0 ? void 0 : process.env) ?? {};
7287
7813
  if (shouldInstallFileWriters(env)) installNodeFileWriters();
7814
+ const debugEnabled = isDebugEnabled();
7815
+ const debugLog$1 = createDebugLogger(debugEnabled);
7288
7816
  const widthProvider = options.widthProvider ?? "default";
7289
7817
  const profileEnabled = envFlag(env, "VUE_TUI_PROFILE", "DIMCODE_PROFILE_TUI");
7290
- const profileLogPath = envString(env, "VUE_TUI_PROFILE_LOG_PATH", "DIMCODE_PROFILE_TUI_LOG_PATH");
7291
- const framePerfLogPath = envString(env, "VUE_TUI_FRAME_PERF_LOG_PATH", "DIMCODE_TUI_PERF_LOG") || (profileLogPath.endsWith(".jsonl") ? profileLogPath : defaultVueTuiFramePerfLogPath());
7818
+ const profileLogPath = envString$1(env, "VUE_TUI_PROFILE_LOG_PATH", "DIMCODE_PROFILE_TUI_LOG_PATH");
7819
+ const framePerfLogPath = envString$1(env, "VUE_TUI_FRAME_PERF_LOG_PATH", "DIMCODE_TUI_PERF_LOG") || (profileLogPath.endsWith(".jsonl") ? profileLogPath : defaultVueTuiFramePerfLogPath());
7292
7820
  const framePerfSink = profileEnabled ? createJsonlPerfSink({
7293
7821
  file: framePerfLogPath,
7294
7822
  fileWriter: nodeProfilerFileWriter,
@@ -7339,6 +7867,10 @@ function createTerminalApp(options) {
7339
7867
  }
7340
7868
  });
7341
7869
  const offCommit = terminal.on("commit", ({ dirtyRows, planes, sync }) => {
7870
+ if (debugEnabled) {
7871
+ const size = terminal.size();
7872
+ debugLog$1.render(`app commit: size=${size.cols}x${size.rows}, dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}, planes=${(planes === null || planes === void 0 ? void 0 : planes.join(",")) ?? "all"}${sync ? ", sync" : ""}`);
7873
+ }
7342
7874
  latency === null || latency === void 0 || latency.recordCommit({
7343
7875
  dirtyRows,
7344
7876
  planes,
@@ -7431,6 +7963,10 @@ function createTerminalApp(options) {
7431
7963
  scheduled = false;
7432
7964
  flushing = true;
7433
7965
  const activePlanes = takeActivePlanes();
7966
+ if (debugEnabled) {
7967
+ const size = terminal.size();
7968
+ debugLog$1.render(`app flush start: size=${size.cols}x${size.rows}, activePlanes=${(activePlanes === null || activePlanes === void 0 ? void 0 : activePlanes.join(",")) ?? "all"}, sync=${sync ? "true" : "false"}, frameTasks=${frameTasks.frameTaskCount}`);
7969
+ }
7434
7970
  latency === null || latency === void 0 || latency.recordFlushStart({
7435
7971
  sync,
7436
7972
  activePlanes
@@ -7556,6 +8092,10 @@ function createTerminalApp(options) {
7556
8092
  function invalidate(options$1) {
7557
8093
  if (disposed) return;
7558
8094
  const priority = (options$1 === null || options$1 === void 0 ? void 0 : options$1.priority) ?? "normal";
8095
+ if (debugEnabled) {
8096
+ const size = terminal.size();
8097
+ debugLog$1.render(`app invalidate: size=${size.cols}x${size.rows}, reason=${(options$1 === null || options$1 === void 0 ? void 0 : options$1.reason) ?? "unknown"}, priority=${priority}, plane=${(options$1 === null || options$1 === void 0 ? void 0 : options$1.plane) ?? "all"}, scheduled=${scheduled}, flushing=${flushing}, queueDepth=${queueDepth()}`);
8098
+ }
7559
8099
  noteFrameReason(options$1 === null || options$1 === void 0 ? void 0 : options$1.reason);
7560
8100
  latency === null || latency === void 0 || latency.recordSchedulerInvalidate({
7561
8101
  priority,
@@ -7640,6 +8180,7 @@ function createTerminalApp(options) {
7640
8180
  invalidate({ plane: portal.plane });
7641
8181
  return handle;
7642
8182
  } };
8183
+ const clipboard = options.clipboard ?? unsupportedClipboard;
7643
8184
  const rootLayout = shallowReactive({
7644
8185
  originX: 0,
7645
8186
  originY: 0,
@@ -7652,6 +8193,7 @@ function createTerminalApp(options) {
7652
8193
  });
7653
8194
  const selectionTextProviders = new Map();
7654
8195
  const selectionCopyHandlers = new Set();
8196
+ const selectionConfig = shallowRef(options.selection ?? false);
7655
8197
  const selectionContext = {
7656
8198
  registerTextProvider(provider) {
7657
8199
  selectionTextProviders.set(provider.id, provider);
@@ -7670,6 +8212,10 @@ function createTerminalApp(options) {
7670
8212
  },
7671
8213
  clear() {
7672
8214
  selection.clear();
8215
+ },
8216
+ setConfig(config) {
8217
+ selectionConfig.value = config;
8218
+ selection.refresh();
7673
8219
  }
7674
8220
  };
7675
8221
  const selectionOverlay = getPlaneTerminal(terminal, "overlay");
@@ -7679,14 +8225,15 @@ function createTerminalApp(options) {
7679
8225
  "chrome"
7680
8226
  ];
7681
8227
  let selectionRenderNodeId = null;
8228
+ const selectionRenderRect = () => null;
7682
8229
  const selection = createTerminalSelectionController({
7683
8230
  terminal,
7684
8231
  overlayTerminal: selectionOverlay,
7685
- clipboard: options.clipboard ?? unsupportedClipboard,
8232
+ clipboard,
7686
8233
  getRow: (y) => readTerminalRowForPlanes(terminal, y, selectionReadPlanes),
7687
8234
  getTextProviders: () => Array.from(selectionTextProviders.values()),
7688
8235
  getOptions: () => {
7689
- const config = resolveSelectionConfig(options.selection);
8236
+ const config = resolveSelectionConfig(selectionConfig.value);
7690
8237
  return {
7691
8238
  autoCopy: config.autoCopy,
7692
8239
  copyOnMouseUp: config.copyOnMouseUp,
@@ -7728,12 +8275,7 @@ function createTerminalApp(options) {
7728
8275
  stack: render.rootStack,
7729
8276
  plane: "overlay",
7730
8277
  zIndex: -1e4,
7731
- rect: {
7732
- x: 0,
7733
- y: 0,
7734
- w: options.cols,
7735
- h: options.rows
7736
- },
8278
+ rect: selectionRenderRect(),
7737
8279
  paint: selection.paint
7738
8280
  });
7739
8281
  selectionRenderNodeId = selectionRenderNode.id;
@@ -7848,19 +8390,17 @@ function createTerminalApp(options) {
7848
8390
  ...createDefaultTInputHostAdapter(),
7849
8391
  isTerminalLike: true,
7850
8392
  async readClipboardText() {
7851
- var _options$clipboard;
7852
- if (!((_options$clipboard = options.clipboard) === null || _options$clipboard === void 0 ? void 0 : _options$clipboard.supported)) return "";
8393
+ if (!clipboardCanRead(clipboard)) return "";
7853
8394
  try {
7854
- return await options.clipboard.readText();
8395
+ return await clipboard.readText();
7855
8396
  } catch {
7856
8397
  return "";
7857
8398
  }
7858
8399
  },
7859
8400
  async writeClipboardText(text) {
7860
- var _options$clipboard2;
7861
- if (!text || !((_options$clipboard2 = options.clipboard) === null || _options$clipboard2 === void 0 ? void 0 : _options$clipboard2.supported)) return false;
8401
+ if (!text || !clipboardCanWrite(clipboard)) return false;
7862
8402
  try {
7863
- await options.clipboard.writeText(text);
8403
+ await clipboard.writeText(text);
7864
8404
  return true;
7865
8405
  } catch {
7866
8406
  return false;
@@ -7869,6 +8409,7 @@ function createTerminalApp(options) {
7869
8409
  }))] : [defaultTInputHostPlugin]);
7870
8410
  const linkOpener = ref(normalizeTerminalLinkOpener(options.linkOpener));
7871
8411
  const offResize = terminal.on("resize", ({ cols, rows }) => {
8412
+ if (debugEnabled) debugLog$1.render(`app resize event: size=${cols}x${rows}, scheduled=${scheduled}, flushing=${flushing}, queueDepth=${queueDepth()}`);
7872
8413
  rootLayout.clipRect = {
7873
8414
  x: 0,
7874
8415
  y: 0,
@@ -7876,12 +8417,7 @@ function createTerminalApp(options) {
7876
8417
  h: rows
7877
8418
  };
7878
8419
  selection.clear();
7879
- if (selectionRenderNodeId) render.update(selectionRenderNodeId, { rect: {
7880
- x: 0,
7881
- y: 0,
7882
- w: cols,
7883
- h: rows
7884
- } });
8420
+ if (selectionRenderNodeId) render.update(selectionRenderNodeId, { rect: selectionRenderRect() });
7885
8421
  invalidate({ reason: "resize" });
7886
8422
  });
7887
8423
  schedulerApi = {
@@ -7902,6 +8438,7 @@ function createTerminalApp(options) {
7902
8438
  events: shallowRef(events),
7903
8439
  scheduler: schedulerApi,
7904
8440
  runtime,
8441
+ clipboard,
7905
8442
  observability: {
7906
8443
  trace,
7907
8444
  framePerf
@@ -7941,6 +8478,8 @@ function createTerminalApp(options) {
7941
8478
  events,
7942
8479
  scheduler: ctx.scheduler,
7943
8480
  defaultStyle: ctx.defaultStyle,
8481
+ clipboard,
8482
+ selection: selectionContext,
7944
8483
  getImeAnchor() {
7945
8484
  return imeAnchor.value;
7946
8485
  },
@@ -7975,7 +8514,11 @@ const metrics = {
7975
8514
  writeEmaMs: 0,
7976
8515
  writeMode: "stream",
7977
8516
  fps: 0,
7978
- bytesPerSec: 0
8517
+ bytesPerSec: 0,
8518
+ terminalGraphicsDraws: 0,
8519
+ terminalGraphicsClears: 0,
8520
+ terminalGraphicsBytes: 0,
8521
+ terminalGraphicsActive: 0
7979
8522
  };
7980
8523
  let bucketStart = 0;
7981
8524
  let bucketFrames = 0;
@@ -8009,6 +8552,291 @@ function recordStdoutFrame(info) {
8009
8552
  bucketBytes = 0;
8010
8553
  }
8011
8554
  }
8555
+ function recordStdoutTerminalGraphics(info) {
8556
+ metrics.terminalGraphicsDraws += Math.max(0, Math.floor(Number(info.draws) || 0));
8557
+ metrics.terminalGraphicsClears += Math.max(0, Math.floor(Number(info.clears) || 0));
8558
+ metrics.terminalGraphicsBytes += Math.max(0, Math.floor(Number(info.bytes) || 0));
8559
+ metrics.terminalGraphicsActive = Math.max(0, Math.floor(Number(info.active) || 0));
8560
+ }
8561
+
8562
+ //#endregion
8563
+ //#region src/renderer/terminal-graphic-render-queue.ts
8564
+ const CACHE_MISS = Symbol("terminal-graphic-cache-miss");
8565
+ function now() {
8566
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
8567
+ }
8568
+ function createAbortError() {
8569
+ const error = new Error("Terminal graphic render aborted");
8570
+ error.name = "AbortError";
8571
+ return error;
8572
+ }
8573
+ function positiveFiniteInt(value, fallback) {
8574
+ const n = Number(value);
8575
+ if (!Number.isFinite(n)) return fallback;
8576
+ const int = Math.floor(n);
8577
+ return int > 0 ? int : fallback;
8578
+ }
8579
+ function throwIfAborted(signal) {
8580
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) throw createAbortError();
8581
+ }
8582
+ function raceAbort(promise, signal) {
8583
+ if (!signal) return promise;
8584
+ if (signal.aborted) return Promise.reject(createAbortError());
8585
+ return new Promise((resolve, reject) => {
8586
+ const onAbort = () => reject(createAbortError());
8587
+ signal.addEventListener("abort", onAbort, { once: true });
8588
+ promise.then((value) => {
8589
+ signal.removeEventListener("abort", onAbort);
8590
+ resolve(value);
8591
+ }, (error) => {
8592
+ signal.removeEventListener("abort", onAbort);
8593
+ reject(error);
8594
+ });
8595
+ });
8596
+ }
8597
+ function createTerminalGraphicRenderQueue(options = {}) {
8598
+ const maxConcurrency = positiveFiniteInt(options.maxConcurrency, 2);
8599
+ const maxEntries = positiveFiniteInt(options.maxEntries, 128);
8600
+ const maxBytes = positiveFiniteInt(options.maxBytes, 32 * 1024 * 1024);
8601
+ const ttlMs = positiveFiniteInt(options.ttlMs, 5 * 6e4);
8602
+ const dedupeInflight = options.dedupeInflight ?? false;
8603
+ let active = 0;
8604
+ let cacheBytes = 0;
8605
+ let generation = 0;
8606
+ const waiters = [];
8607
+ const cache = new Map();
8608
+ const inflight = new Map();
8609
+ function traceQueueState(event) {
8610
+ recordTerminalGraphicTrace({
8611
+ ...event,
8612
+ active,
8613
+ waiting: waiters.length,
8614
+ cacheEntries: cache.size,
8615
+ cacheBytes
8616
+ });
8617
+ }
8618
+ function evictUntilBudget() {
8619
+ while (cache.size > maxEntries || cacheBytes > maxBytes) {
8620
+ const oldestKey = cache.keys().next().value;
8621
+ if (!oldestKey) break;
8622
+ const entry = cache.get(oldestKey);
8623
+ cache.delete(oldestKey);
8624
+ if (entry) {
8625
+ var _options$onMetric;
8626
+ cacheBytes -= entry.bytes;
8627
+ (_options$onMetric = options.onMetric) === null || _options$onMetric === void 0 || _options$onMetric.call(options, {
8628
+ type: "evict",
8629
+ key: oldestKey,
8630
+ bytes: entry.bytes
8631
+ });
8632
+ }
8633
+ }
8634
+ }
8635
+ function setCache(key, value, bytes) {
8636
+ var _options$onMetric2;
8637
+ const prev = cache.get(key);
8638
+ if (prev) {
8639
+ cache.delete(key);
8640
+ cacheBytes -= prev.bytes;
8641
+ }
8642
+ if (bytes > maxBytes) return;
8643
+ cache.set(key, {
8644
+ value,
8645
+ bytes,
8646
+ expiresAt: Date.now() + ttlMs
8647
+ });
8648
+ cacheBytes += bytes;
8649
+ (_options$onMetric2 = options.onMetric) === null || _options$onMetric2 === void 0 || _options$onMetric2.call(options, {
8650
+ type: "cache-store",
8651
+ key,
8652
+ bytes
8653
+ });
8654
+ traceQueueState({
8655
+ type: "cache-store",
8656
+ id: key,
8657
+ key,
8658
+ bytes
8659
+ });
8660
+ evictUntilBudget();
8661
+ }
8662
+ function getCache(key) {
8663
+ var _options$onMetric3;
8664
+ const entry = cache.get(key);
8665
+ if (!entry) return CACHE_MISS;
8666
+ if (entry.expiresAt < Date.now()) {
8667
+ cache.delete(key);
8668
+ cacheBytes -= entry.bytes;
8669
+ return CACHE_MISS;
8670
+ }
8671
+ cache.delete(key);
8672
+ cache.set(key, entry);
8673
+ (_options$onMetric3 = options.onMetric) === null || _options$onMetric3 === void 0 || _options$onMetric3.call(options, {
8674
+ type: "cache-hit",
8675
+ key
8676
+ });
8677
+ traceQueueState({
8678
+ type: "cache-hit",
8679
+ id: key,
8680
+ key,
8681
+ bytes: entry.bytes
8682
+ });
8683
+ return entry.value;
8684
+ }
8685
+ function release() {
8686
+ active = Math.max(0, active - 1);
8687
+ pump();
8688
+ }
8689
+ function once(fn) {
8690
+ let called = false;
8691
+ return () => {
8692
+ if (called) return;
8693
+ called = true;
8694
+ fn();
8695
+ };
8696
+ }
8697
+ function observeActiveRenderAbort(key, signal) {
8698
+ if (!signal) return () => void 0;
8699
+ const onAbort = () => {
8700
+ var _options$onMetric4;
8701
+ (_options$onMetric4 = options.onMetric) === null || _options$onMetric4 === void 0 || _options$onMetric4.call(options, {
8702
+ type: "render-abort",
8703
+ key
8704
+ });
8705
+ traceQueueState({
8706
+ type: "renderer-abort",
8707
+ id: key,
8708
+ key
8709
+ });
8710
+ };
8711
+ signal.addEventListener("abort", onAbort, { once: true });
8712
+ return () => signal.removeEventListener("abort", onAbort);
8713
+ }
8714
+ function pump() {
8715
+ while (active < maxConcurrency && waiters.length > 0) {
8716
+ const waiter = waiters.shift();
8717
+ active++;
8718
+ traceQueueState({
8719
+ type: "queue-depth",
8720
+ id: waiter.key,
8721
+ key: waiter.key
8722
+ });
8723
+ waiter.start();
8724
+ }
8725
+ }
8726
+ function acquire(key, signal) {
8727
+ throwIfAborted(signal);
8728
+ if (active < maxConcurrency) {
8729
+ active++;
8730
+ traceQueueState({
8731
+ type: "queue-depth",
8732
+ id: key,
8733
+ key
8734
+ });
8735
+ return Promise.resolve(release);
8736
+ }
8737
+ const waitStartedAt = now();
8738
+ return new Promise((resolve, reject) => {
8739
+ let onAbort = () => void 0;
8740
+ const cleanup = () => {
8741
+ signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
8742
+ };
8743
+ const waiter = {
8744
+ key,
8745
+ start: () => {
8746
+ var _options$onMetric5;
8747
+ cleanup();
8748
+ const waitMs = now() - waitStartedAt;
8749
+ (_options$onMetric5 = options.onMetric) === null || _options$onMetric5 === void 0 || _options$onMetric5.call(options, {
8750
+ type: "queue-wait",
8751
+ key,
8752
+ waitMs
8753
+ });
8754
+ traceQueueState({
8755
+ type: "queue-wait",
8756
+ id: key,
8757
+ key,
8758
+ durationMs: waitMs
8759
+ });
8760
+ resolve(release);
8761
+ },
8762
+ reject: (error) => {
8763
+ cleanup();
8764
+ reject(error);
8765
+ }
8766
+ };
8767
+ onAbort = () => {
8768
+ const index = waiters.indexOf(waiter);
8769
+ if (index >= 0) waiters.splice(index, 1);
8770
+ cleanup();
8771
+ reject(createAbortError());
8772
+ };
8773
+ signal === null || signal === void 0 || signal.addEventListener("abort", onAbort, { once: true });
8774
+ waiters.push(waiter);
8775
+ traceQueueState({
8776
+ type: "queue-depth",
8777
+ id: key,
8778
+ key
8779
+ });
8780
+ });
8781
+ }
8782
+ async function cached(key, signal, render, estimateBytes = () => 0, cachedOptions = {}) {
8783
+ throwIfAborted(signal);
8784
+ const cachedValue = getCache(key);
8785
+ if (cachedValue !== CACHE_MISS) return cachedValue;
8786
+ traceQueueState({
8787
+ type: "cache-miss",
8788
+ id: key,
8789
+ key
8790
+ });
8791
+ const shouldDedupeInflight = cachedOptions.dedupeInflight ?? dedupeInflight;
8792
+ const existing = shouldDedupeInflight ? inflight.get(key) : void 0;
8793
+ if (existing) return raceAbort(existing, signal);
8794
+ let promise;
8795
+ promise = (async () => {
8796
+ let releaseSlot = null;
8797
+ let removeAbortObserver = null;
8798
+ const renderGeneration = generation;
8799
+ try {
8800
+ releaseSlot = once(await acquire(key, signal));
8801
+ removeAbortObserver = observeActiveRenderAbort(key, signal);
8802
+ throwIfAborted(signal);
8803
+ const cachedAfterWait = getCache(key);
8804
+ if (cachedAfterWait !== CACHE_MISS) return cachedAfterWait;
8805
+ const value = await render();
8806
+ throwIfAborted(signal);
8807
+ const rawBytes = Math.floor(Number(estimateBytes(value)));
8808
+ const bytes = Number.isFinite(rawBytes) && rawBytes > 0 ? rawBytes : 0;
8809
+ if (renderGeneration === generation) setCache(key, value, bytes);
8810
+ return value;
8811
+ } finally {
8812
+ removeAbortObserver === null || removeAbortObserver === void 0 || removeAbortObserver();
8813
+ releaseSlot === null || releaseSlot === void 0 || releaseSlot();
8814
+ if (shouldDedupeInflight && inflight.get(key) === promise) inflight.delete(key);
8815
+ }
8816
+ })();
8817
+ if (shouldDedupeInflight) inflight.set(key, promise);
8818
+ return raceAbort(promise, signal);
8819
+ }
8820
+ return {
8821
+ cached,
8822
+ clear() {
8823
+ generation++;
8824
+ cache.clear();
8825
+ inflight.clear();
8826
+ const pending = waiters.splice(0);
8827
+ for (const waiter of pending) waiter.reject(createAbortError());
8828
+ cacheBytes = 0;
8829
+ },
8830
+ stats() {
8831
+ return {
8832
+ active,
8833
+ waiting: waiters.length,
8834
+ cacheEntries: cache.size,
8835
+ cacheBytes
8836
+ };
8837
+ }
8838
+ };
8839
+ }
8012
8840
 
8013
8841
  //#endregion
8014
8842
  //#region src/core/ansi-palette.ts
@@ -8172,13 +9000,13 @@ const ANSI16_COLOR_NAMES = Object.freeze([
8172
9000
  "cyanBright",
8173
9001
  "whiteBright"
8174
9002
  ]);
8175
- const ESC = "\x1B[";
8176
- const SGR_RESET = `${ESC}0m`;
8177
- const SGR_BOLD = `${ESC}1m`;
8178
- const SGR_DIM = `${ESC}2m`;
8179
- const SGR_ITALIC = `${ESC}3m`;
8180
- const SGR_UNDERLINE = `${ESC}4m`;
8181
- const SGR_INVERSE = `${ESC}7m`;
9003
+ const ESC$1 = "\x1B[";
9004
+ const SGR_RESET = `${ESC$1}0m`;
9005
+ const SGR_BOLD = `${ESC$1}1m`;
9006
+ const SGR_DIM = `${ESC$1}2m`;
9007
+ const SGR_ITALIC = `${ESC$1}3m`;
9008
+ const SGR_UNDERLINE = `${ESC$1}4m`;
9009
+ const SGR_INVERSE = `${ESC$1}7m`;
8182
9010
  function toAnsi8ColorName(name) {
8183
9011
  switch (name) {
8184
9012
  case "black":
@@ -8238,32 +9066,32 @@ const ANSI16_BG_CODE = Object.freeze({
8238
9066
  function ansi8FgOpen(name) {
8239
9067
  const base = toAnsi8ColorName(name);
8240
9068
  const idx = ANSI8_COLOR_NAMES.indexOf(base);
8241
- return `${ESC}${30 + (idx < 0 ? 7 : idx)}m`;
9069
+ return `${ESC$1}${30 + (idx < 0 ? 7 : idx)}m`;
8242
9070
  }
8243
9071
  function ansi8BgOpen(name) {
8244
9072
  const base = toAnsi8ColorName(name);
8245
9073
  const idx = ANSI8_COLOR_NAMES.indexOf(base);
8246
- return `${ESC}${40 + (idx < 0 ? 0 : idx)}m`;
9074
+ return `${ESC$1}${40 + (idx < 0 ? 0 : idx)}m`;
8247
9075
  }
8248
9076
  function ansi16FgOpen(name) {
8249
- return `${ESC}${ANSI16_FG_CODE[name]}m`;
9077
+ return `${ESC$1}${ANSI16_FG_CODE[name]}m`;
8250
9078
  }
8251
9079
  function ansi16BgOpen(name) {
8252
- return `${ESC}${ANSI16_BG_CODE[name]}m`;
9080
+ return `${ESC$1}${ANSI16_BG_CODE[name]}m`;
8253
9081
  }
8254
9082
  function ansi256FgOpen(index) {
8255
9083
  const n = clampAnsiIndex(index);
8256
- return `${ESC}38;5;${n}m`;
9084
+ return `${ESC$1}38;5;${n}m`;
8257
9085
  }
8258
9086
  function ansi256BgOpen(index) {
8259
9087
  const n = clampAnsiIndex(index);
8260
- return `${ESC}48;5;${n}m`;
9088
+ return `${ESC$1}48;5;${n}m`;
8261
9089
  }
8262
9090
  function truecolorFgOpen(rgb) {
8263
- return `${ESC}38;2;${clampByte(rgb.r)};${clampByte(rgb.g)};${clampByte(rgb.b)}m`;
9091
+ return `${ESC$1}38;2;${clampByte(rgb.r)};${clampByte(rgb.g)};${clampByte(rgb.b)}m`;
8264
9092
  }
8265
9093
  function truecolorBgOpen(rgb) {
8266
- return `${ESC}48;2;${clampByte(rgb.r)};${clampByte(rgb.g)};${clampByte(rgb.b)}m`;
9094
+ return `${ESC$1}48;2;${clampByte(rgb.r)};${clampByte(rgb.g)};${clampByte(rgb.b)}m`;
8267
9095
  }
8268
9096
  function ansi256ToRgb(index) {
8269
9097
  const n = clampAnsiIndex(index);
@@ -8417,6 +9245,623 @@ function sanitizeTerminalHref(value, options = {}) {
8417
9245
  return null;
8418
9246
  }
8419
9247
 
9248
+ //#endregion
9249
+ //#region src/renderer/terminal-graphics.ts
9250
+ const TERMINAL_GRAPHICS_PROTOCOL_PRIORITY = [
9251
+ "kitty",
9252
+ "iterm2",
9253
+ "sixel"
9254
+ ];
9255
+ const ESC = "\x1B";
9256
+ const BEL = "\x07";
9257
+ const ST = `${ESC}\\`;
9258
+ const ESC_RE = "\\u001B";
9259
+ const BEL_RE = "\\u0007";
9260
+ const CSI_RE = new RegExp(`${ESC_RE}\\[[0-?]*[ -/]*[@-~]`, "g");
9261
+ const OSC_RE = new RegExp(`${ESC_RE}\\][\\s\\S]*?(?:${BEL_RE}|${ESC_RE}\\\\)`, "g");
9262
+ const DCS_RE = new RegExp(`${ESC_RE}P[\\s\\S]*?${ESC_RE}\\\\`, "g");
9263
+ const APC_RE = new RegExp(`${ESC_RE}_[\\s\\S]*?${ESC_RE}\\\\`, "g");
9264
+ const C1_CSI_RE = new RegExp("\\u009B[0-?]*[ -/]*[@-~]", "g");
9265
+ const C1_OSC_RE = new RegExp(`\\u009D[\\s\\S]*?(?:${BEL_RE}|\\u009C)`, "g");
9266
+ const C1_DCS_RE = new RegExp("\\u0090[\\s\\S]*?\\u009C", "g");
9267
+ const C1_APC_RE = new RegExp("\\u009F[\\s\\S]*?\\u009C", "g");
9268
+ const MAX_TERMINAL_GRAPHICS_SEQUENCE_CHARS = 16 * 1024 * 1024;
9269
+ const MAX_TERMINAL_GRAPHICS_FALLBACK_CHARS = 16384;
9270
+ const MAX_TERMINAL_GRAPHIC_CELLS = 1e4;
9271
+ const TERMINAL_GRAPHICS_REGISTRY = Symbol.for("@simon_he/vue-tui:v1:terminal-graphics");
9272
+ const terminalGraphicsGlobal = globalThis;
9273
+ const terminalGraphicsRegistry = terminalGraphicsGlobal[TERMINAL_GRAPHICS_REGISTRY] ?? {
9274
+ outputs: new WeakMap(),
9275
+ outputVersions: new WeakMap(),
9276
+ outputListeners: new WeakMap()
9277
+ };
9278
+ terminalGraphicsGlobal[TERMINAL_GRAPHICS_REGISTRY] = terminalGraphicsRegistry;
9279
+ const terminalGraphicsOutputs = terminalGraphicsRegistry.outputs;
9280
+ const terminalGraphicsOutputVersions = terminalGraphicsRegistry.outputVersions;
9281
+ const terminalGraphicsOutputListeners = terminalGraphicsRegistry.outputListeners;
9282
+ function notifyTerminalGraphicsOutputChange(terminal) {
9283
+ const version = (terminalGraphicsOutputVersions.get(terminal) ?? 0) + 1;
9284
+ terminalGraphicsOutputVersions.set(terminal, version);
9285
+ const listeners = terminalGraphicsOutputListeners.get(terminal);
9286
+ if (!listeners) return;
9287
+ for (const listener of listeners) listener();
9288
+ }
9289
+ function registerTerminalGraphicsOutput(terminal, output) {
9290
+ const rootTerminal = getRootTerminal(terminal);
9291
+ terminalGraphicsOutputs.set(rootTerminal, output);
9292
+ notifyTerminalGraphicsOutputChange(rootTerminal);
9293
+ return () => {
9294
+ if (terminalGraphicsOutputs.get(rootTerminal) === output) {
9295
+ terminalGraphicsOutputs.delete(rootTerminal);
9296
+ notifyTerminalGraphicsOutputChange(rootTerminal);
9297
+ }
9298
+ };
9299
+ }
9300
+ function envString(env, key) {
9301
+ const value = env[key];
9302
+ return value == null ? void 0 : String(value);
9303
+ }
9304
+ function truthy(value) {
9305
+ if (value == null) return false;
9306
+ return /^(1|true|yes|on)$/i.test(String(value));
9307
+ }
9308
+ function readStdoutIsTTY() {
9309
+ var _maybeProcess$stdout;
9310
+ const maybeProcess = globalThis.process;
9311
+ return Boolean(maybeProcess === null || maybeProcess === void 0 || (_maybeProcess$stdout = maybeProcess.stdout) === null || _maybeProcess$stdout === void 0 ? void 0 : _maybeProcess$stdout.isTTY);
9312
+ }
9313
+ function readProcessEnv() {
9314
+ const maybeProcess = globalThis.process;
9315
+ return (maybeProcess === null || maybeProcess === void 0 ? void 0 : maybeProcess.env) ?? {};
9316
+ }
9317
+ function normalizeMode(value) {
9318
+ const raw = String(value ?? "auto").trim().toLowerCase();
9319
+ if (raw === "auto" || raw === "off" || raw === "none" || raw === "kitty" || raw === "iterm2" || raw === "sixel" || raw === "unicode") return raw === "off" ? "none" : raw;
9320
+ return "auto";
9321
+ }
9322
+ function isTerminalGraphicsProtocol(value) {
9323
+ return value === "kitty" || value === "iterm2" || value === "sixel";
9324
+ }
9325
+ function uniqueProtocols(protocols) {
9326
+ return [...new Set(protocols)];
9327
+ }
9328
+ function pickBestTerminalGraphicsProtocol(protocols) {
9329
+ const available = new Set(protocols);
9330
+ for (const protocol of TERMINAL_GRAPHICS_PROTOCOL_PRIORITY) if (available.has(protocol)) return protocol;
9331
+ return null;
9332
+ }
9333
+ function detectCandidates(env) {
9334
+ const candidates = [];
9335
+ const term = String(envString(env, "TERM") ?? "").toLowerCase();
9336
+ const termProgram = String(envString(env, "TERM_PROGRAM") ?? "").toLowerCase();
9337
+ const termProgramVersion = String(envString(env, "TERM_PROGRAM_VERSION") ?? "").toLowerCase();
9338
+ if (envString(env, "KITTY_WINDOW_ID") || envString(env, "GHOSTTY_RESOURCES_DIR") || /(?:^|-)kitty(?:-|$)/i.test(term) || term.includes("ghostty") || termProgram.includes("ghostty")) candidates.push("kitty");
9339
+ if (termProgram.includes("iterm") || termProgram.includes("wezterm") || envString(env, "WEZTERM_PANE") || envString(env, "WEZTERM_EXECUTABLE")) candidates.push("iterm2");
9340
+ if (truthy(envString(env, "VUE_TUI_SIXEL")) || truthy(envString(env, "VUE_TUI_GRAPHICS_SIXEL")) || String(envString(env, "TERMINAL_GRAPHICS") ?? "").toLowerCase().includes("sixel") || /\bsixel\b/i.test(`${term} ${termProgram} ${termProgramVersion}`)) candidates.push("sixel");
9341
+ return uniqueProtocols(candidates);
9342
+ }
9343
+ function capabilitiesFor(protocol, input) {
9344
+ const supported = protocol === "kitty" || protocol === "iterm2" || protocol === "sixel";
9345
+ const candidates = uniqueProtocols([...input.candidates ?? (supported ? [protocol] : [])]);
9346
+ return {
9347
+ supported,
9348
+ kitty: protocol === "kitty" || candidates.includes("kitty"),
9349
+ iterm2: protocol === "iterm2" || candidates.includes("iterm2"),
9350
+ sixel: protocol === "sixel" || candidates.includes("sixel"),
9351
+ preferredProtocol: supported ? protocol : null,
9352
+ protocol,
9353
+ candidates,
9354
+ stdoutIsTTY: input.stdoutIsTTY,
9355
+ insideTmux: input.insideTmux,
9356
+ insideScreen: input.insideScreen,
9357
+ insideZellij: input.insideZellij,
9358
+ multiplexer: input.multiplexer,
9359
+ passthrough: input.passthrough,
9360
+ forced: input.forced,
9361
+ reason: input.reason
9362
+ };
9363
+ }
9364
+ function detectTerminalGraphicsCapabilities(options = {}) {
9365
+ const env = options.env ?? readProcessEnv();
9366
+ const stdoutIsTTY = options.stdoutIsTTY ?? options.isTTY ?? readStdoutIsTTY();
9367
+ const term = String(envString(env, "TERM") ?? "").toLowerCase();
9368
+ const insideTmux = Boolean(envString(env, "TMUX")) || term.includes("tmux");
9369
+ const insideScreen = Boolean(envString(env, "STY")) || term.startsWith("screen");
9370
+ const insideZellij = Boolean(envString(env, "ZELLIJ") || envString(env, "ZELLIJ_SESSION_NAME")) || term.includes("zellij");
9371
+ const multiplexer = insideTmux ? "tmux" : insideScreen ? "screen" : insideZellij ? "zellij" : null;
9372
+ const passthroughRequested = options.passthrough ?? (truthy(envString(env, "VUE_TUI_TERMINAL_GRAPHICS_PASSTHROUGH")) || truthy(envString(env, "VUE_TUI_GRAPHICS_TMUX_PASSTHROUGH")));
9373
+ const passthroughSupported = !multiplexer || multiplexer === "tmux";
9374
+ const passthrough = passthroughRequested && passthroughSupported;
9375
+ const protocolOptionProvided = options.protocol != null;
9376
+ const mode = normalizeMode(options.protocol ?? envString(env, "VUE_TUI_TERMINAL_GRAPHICS") ?? envString(env, "VUE_TUI_GRAPHICS_PROTOCOL"));
9377
+ const forceFromOption = options.force === true;
9378
+ const force = options.force ?? truthy(envString(env, "VUE_TUI_GRAPHICS_FORCE"));
9379
+ const base = {
9380
+ stdoutIsTTY,
9381
+ insideTmux,
9382
+ insideScreen,
9383
+ insideZellij,
9384
+ multiplexer,
9385
+ passthrough,
9386
+ forced: force
9387
+ };
9388
+ if (mode === "none") return capabilitiesFor("none", {
9389
+ ...base,
9390
+ reason: protocolOptionProvided ? "disabled-by-option" : "disabled-by-env"
9391
+ });
9392
+ if (!stdoutIsTTY && !force) return capabilitiesFor("unicode", {
9393
+ ...base,
9394
+ reason: "stdout-is-not-tty"
9395
+ });
9396
+ if (truthy(envString(env, "CI")) && !force) return capabilitiesFor("unicode", {
9397
+ ...base,
9398
+ reason: "ci"
9399
+ });
9400
+ if (multiplexer && passthroughRequested && !passthroughSupported && !force) return capabilitiesFor("unicode", {
9401
+ ...base,
9402
+ reason: `${multiplexer}-passthrough-not-implemented`
9403
+ });
9404
+ if (multiplexer && !passthrough && !force) return capabilitiesFor("unicode", {
9405
+ ...base,
9406
+ reason: `${multiplexer}-without-passthrough`
9407
+ });
9408
+ if (mode === "kitty" || mode === "iterm2" || mode === "sixel") return capabilitiesFor(mode, {
9409
+ ...base,
9410
+ candidates: [mode],
9411
+ reason: force ? forceFromOption ? "forced-by-option" : "forced-by-env" : protocolOptionProvided ? "selected-by-option" : "selected-by-env"
9412
+ });
9413
+ if (mode === "unicode") return capabilitiesFor("unicode", {
9414
+ ...base,
9415
+ reason: protocolOptionProvided ? "unicode-forced-by-option" : "unicode-forced-by-env"
9416
+ });
9417
+ const candidates = detectCandidates(env);
9418
+ const preferredProtocol = pickBestTerminalGraphicsProtocol(candidates);
9419
+ const protocol = preferredProtocol ?? "unicode";
9420
+ return capabilitiesFor(protocol, {
9421
+ ...base,
9422
+ candidates,
9423
+ reason: protocol === "unicode" ? "no-supported-graphics-protocol-detected" : "auto-detected"
9424
+ });
9425
+ }
9426
+ function hashTerminalGraphicsString(input) {
9427
+ let hash = 2166136261;
9428
+ for (let index = 0; index < input.length; index += 1) {
9429
+ hash ^= input.charCodeAt(index);
9430
+ hash = Math.imul(hash, 16777619);
9431
+ }
9432
+ return (hash >>> 0).toString(36);
9433
+ }
9434
+ function stableId(input) {
9435
+ return `tg_${hashTerminalGraphicsString(input)}`;
9436
+ }
9437
+ function positiveGraphicInt(value, max) {
9438
+ const n = Math.trunc(Number(value));
9439
+ if (!Number.isFinite(n) || n <= 0) return null;
9440
+ return Math.min(max, n);
9441
+ }
9442
+ function normalizeTerminalGraphicSize(width, height) {
9443
+ const w = positiveGraphicInt(width, MAX_TERMINAL_GRAPHIC_CELLS);
9444
+ const h$1 = positiveGraphicInt(height, MAX_TERMINAL_GRAPHIC_CELLS);
9445
+ if (w == null || h$1 == null) return null;
9446
+ if (w * h$1 > MAX_TERMINAL_GRAPHIC_CELLS) return null;
9447
+ return {
9448
+ width: w,
9449
+ height: h$1
9450
+ };
9451
+ }
9452
+ function withoutNestedEscapes(body) {
9453
+ return !body.includes(ESC) && !body.includes(BEL);
9454
+ }
9455
+ function isBase64ish(value) {
9456
+ return /^[A-Za-z0-9+/=]*$/.test(value);
9457
+ }
9458
+ function parseKittyControls(raw) {
9459
+ const out = new Map();
9460
+ if (!/^[A-Za-z0-9_,=+.\-:]*$/.test(raw)) return null;
9461
+ for (const part of raw.split(",")) {
9462
+ if (!part) continue;
9463
+ const eq = part.indexOf("=");
9464
+ if (eq <= 0) return null;
9465
+ const key = part.slice(0, eq);
9466
+ const value = part.slice(eq + 1);
9467
+ if (!/^[A-Za-z]$/.test(key)) return null;
9468
+ if (!/^[A-Za-z0-9+.\-:]*$/.test(value)) return null;
9469
+ out.set(key, value);
9470
+ }
9471
+ return out;
9472
+ }
9473
+ function parseKittyPackets(sequence) {
9474
+ const packets = [];
9475
+ let index = 0;
9476
+ while (index < sequence.length) {
9477
+ if (!sequence.startsWith(`${ESC}_G`, index)) return null;
9478
+ const end = sequence.indexOf(ST, index + 3);
9479
+ if (end < 0) return null;
9480
+ const body = sequence.slice(index + 3, end);
9481
+ if (!withoutNestedEscapes(body)) return null;
9482
+ const semicolon = body.indexOf(";");
9483
+ const rawControls = semicolon >= 0 ? body.slice(0, semicolon) : body;
9484
+ const payload = semicolon >= 0 ? body.slice(semicolon + 1) : "";
9485
+ const controls = parseKittyControls(rawControls);
9486
+ if (!controls) return null;
9487
+ if (payload && !isBase64ish(payload)) return null;
9488
+ packets.push({
9489
+ controls,
9490
+ payload
9491
+ });
9492
+ index = end + ST.length;
9493
+ }
9494
+ return packets.length ? packets : null;
9495
+ }
9496
+ const KITTY_DRAW_FIRST_CONTROL_KEYS = new Set([
9497
+ "a",
9498
+ "f",
9499
+ "q",
9500
+ "C",
9501
+ "i",
9502
+ "I",
9503
+ "p",
9504
+ "c",
9505
+ "r",
9506
+ "x",
9507
+ "y",
9508
+ "w",
9509
+ "h",
9510
+ "z",
9511
+ "m",
9512
+ "t"
9513
+ ]);
9514
+ const KITTY_DRAW_CONTINUATION_CONTROL_KEYS = new Set([
9515
+ "a",
9516
+ "q",
9517
+ "m"
9518
+ ]);
9519
+ const KITTY_CLEAR_CONTROL_KEYS = new Set([
9520
+ "a",
9521
+ "d",
9522
+ "i",
9523
+ "I",
9524
+ "p",
9525
+ "q"
9526
+ ]);
9527
+ function kittyControlsUseOnly(controls, allowed) {
9528
+ for (const key of controls.keys()) if (!allowed.has(key)) return false;
9529
+ return true;
9530
+ }
9531
+ const KITTY_UINT32_MAX = 4294967295;
9532
+ const KITTY_INT32_MIN = -2147483648;
9533
+ const KITTY_INT32_MAX = 2147483647;
9534
+ function kittyControlIsUint32(controls, key, min = 0) {
9535
+ const value = controls.get(key);
9536
+ if (value == null) return true;
9537
+ if (!/^\d+$/.test(value)) return false;
9538
+ const n = Number(value);
9539
+ return Number.isSafeInteger(n) && n >= min && n <= KITTY_UINT32_MAX;
9540
+ }
9541
+ function kittyControlIsInt32(controls, key) {
9542
+ const value = controls.get(key);
9543
+ if (value == null) return true;
9544
+ if (!/^-?\d+$/.test(value)) return false;
9545
+ const n = Number(value);
9546
+ return Number.isSafeInteger(n) && n >= KITTY_INT32_MIN && n <= KITTY_INT32_MAX;
9547
+ }
9548
+ function kittyControlPositiveInt(controls, key, fallback) {
9549
+ const value = controls.get(key);
9550
+ if (value == null) return fallback;
9551
+ if (!/^\d+$/.test(value)) return null;
9552
+ const n = Number(value);
9553
+ return Number.isSafeInteger(n) && n >= 1 ? n : null;
9554
+ }
9555
+ function kittyControlIsTerminalGraphicCellsBounded(controls) {
9556
+ const columns = kittyControlPositiveInt(controls, "c", 1);
9557
+ const rows = kittyControlPositiveInt(controls, "r", 1);
9558
+ return columns != null && rows != null && columns * rows <= MAX_TERMINAL_GRAPHIC_CELLS;
9559
+ }
9560
+ function validateKittySharedSafeControls(controls) {
9561
+ const q = controls.get("q");
9562
+ if (q != null && !/^[0-2]$/.test(q)) return false;
9563
+ const cursorMove = controls.get("C");
9564
+ if (cursorMove != null && !/^[01]$/.test(cursorMove)) return false;
9565
+ for (const key of [
9566
+ "i",
9567
+ "I",
9568
+ "p"
9569
+ ]) if (!kittyControlIsUint32(controls, key)) return false;
9570
+ for (const key of ["c", "r"]) if (!kittyControlIsUint32(controls, key, 1)) return false;
9571
+ if (!kittyControlIsTerminalGraphicCellsBounded(controls)) return false;
9572
+ for (const key of ["x", "y"]) if (!kittyControlIsUint32(controls, key)) return false;
9573
+ for (const key of ["w", "h"]) if (!kittyControlIsUint32(controls, key, 1)) return false;
9574
+ if (!kittyControlIsInt32(controls, "z")) return false;
9575
+ const transmissionMedium = controls.get("t");
9576
+ if (transmissionMedium != null && transmissionMedium !== "d") return false;
9577
+ return true;
9578
+ }
9579
+ function validateKittySequence(sequence, op = "draw") {
9580
+ const packets = parseKittyPackets(sequence);
9581
+ if (!packets) return false;
9582
+ if (op === "clear") {
9583
+ if (packets.length !== 1) return false;
9584
+ const packet = packets[0];
9585
+ if (!kittyControlsUseOnly(packet.controls, KITTY_CLEAR_CONTROL_KEYS)) return false;
9586
+ if (!validateKittySharedSafeControls(packet.controls)) return false;
9587
+ if (packet.payload) return false;
9588
+ if (packet.controls.get("a") !== "d") return false;
9589
+ const d = packet.controls.get("d");
9590
+ return d == null || /^[aAcCiI]$/.test(d);
9591
+ }
9592
+ const first = packets[0];
9593
+ if (!kittyControlsUseOnly(first.controls, KITTY_DRAW_FIRST_CONTROL_KEYS)) return false;
9594
+ if (!validateKittySharedSafeControls(first.controls)) return false;
9595
+ if (first.controls.get("a") === "p") {
9596
+ if (packets.length !== 1) return false;
9597
+ if (first.payload) return false;
9598
+ if (!first.controls.has("i") && !first.controls.has("I")) return false;
9599
+ return true;
9600
+ }
9601
+ if (first.controls.get("a") !== "T") return false;
9602
+ if (!first.payload) return false;
9603
+ const f = first.controls.get("f");
9604
+ if (f != null && f !== "24" && f !== "32" && f !== "100") return false;
9605
+ const firstMore = first.controls.get("m");
9606
+ if (firstMore != null && firstMore !== "0" && firstMore !== "1") return false;
9607
+ if (packets.length > 1 && firstMore !== "1") return false;
9608
+ for (let i = 1; i < packets.length; i++) {
9609
+ const packet = packets[i];
9610
+ if (!kittyControlsUseOnly(packet.controls, KITTY_DRAW_CONTINUATION_CONTROL_KEYS)) return false;
9611
+ if (!validateKittySharedSafeControls(packet.controls)) return false;
9612
+ const action = packet.controls.get("a");
9613
+ if (action != null && action !== "T") return false;
9614
+ const m = packet.controls.get("m");
9615
+ if (m !== "0" && m !== "1") return false;
9616
+ if (!packet.payload && !(i === packets.length - 1 && m === "0")) return false;
9617
+ }
9618
+ if (packets.length > 1 && packets[packets.length - 1].controls.get("m") === "1") return false;
9619
+ return true;
9620
+ }
9621
+ function validateIterm2Dimension(value) {
9622
+ return /^auto$/i.test(value) || /^(?:[1-9]\d{0,4})(?:px|%)?$/.test(value);
9623
+ }
9624
+ function validateIterm2Params(params) {
9625
+ const seen = new Set();
9626
+ let inline = false;
9627
+ for (const part of params.split(";").filter(Boolean)) {
9628
+ const eq = part.indexOf("=");
9629
+ if (eq <= 0) return false;
9630
+ const key = part.slice(0, eq);
9631
+ const value = part.slice(eq + 1);
9632
+ if (seen.has(key)) return false;
9633
+ seen.add(key);
9634
+ switch (key) {
9635
+ case "inline":
9636
+ if (value !== "1") return false;
9637
+ inline = true;
9638
+ break;
9639
+ case "width":
9640
+ case "height":
9641
+ if (!validateIterm2Dimension(value)) return false;
9642
+ break;
9643
+ case "preserveAspectRatio":
9644
+ case "doNotMoveCursor":
9645
+ if (!/^[01]$/.test(value)) return false;
9646
+ break;
9647
+ default: return false;
9648
+ }
9649
+ }
9650
+ return inline;
9651
+ }
9652
+ function validateIterm2Sequence(sequence, op = "draw") {
9653
+ if (op === "clear") return false;
9654
+ let index = 0;
9655
+ while (index < sequence.length) {
9656
+ const prefix = `${ESC}]1337;File=`;
9657
+ if (!sequence.startsWith(prefix, index)) return false;
9658
+ const belEnd = sequence.indexOf(BEL, index + prefix.length);
9659
+ const stEnd = sequence.indexOf(ST, index + prefix.length);
9660
+ const end = belEnd >= 0 && stEnd >= 0 ? Math.min(belEnd, stEnd) : belEnd >= 0 ? belEnd : stEnd;
9661
+ if (end < 0) return false;
9662
+ const body = sequence.slice(index + `${ESC}]1337;`.length, end);
9663
+ if (!body.startsWith("File=")) return false;
9664
+ if (!withoutNestedEscapes(body)) return false;
9665
+ const colon = body.indexOf(":");
9666
+ if (colon < 0) return false;
9667
+ const params = body.slice(5, colon);
9668
+ const payload = body.slice(colon + 1);
9669
+ if (!/^[A-Za-z0-9_=;,.%+\-:]*$/.test(params)) return false;
9670
+ if (!payload || !isBase64ish(payload)) return false;
9671
+ if (!validateIterm2Params(params)) return false;
9672
+ index = end + (end === stEnd ? ST.length : BEL.length);
9673
+ }
9674
+ return sequence.length > 0;
9675
+ }
9676
+ function validateSixelSequence(sequence, op = "draw") {
9677
+ if (op === "clear") return false;
9678
+ let index = 0;
9679
+ while (index < sequence.length) {
9680
+ if (!sequence.startsWith(`${ESC}P`, index)) return false;
9681
+ const end = sequence.indexOf(ST, index + 2);
9682
+ if (end < 0) return false;
9683
+ const body = sequence.slice(index + 2, end);
9684
+ if (!withoutNestedEscapes(body)) return false;
9685
+ for (let i = 0; i < body.length; i++) {
9686
+ const code = body.charCodeAt(i);
9687
+ if (code < 32 || code > 126) return false;
9688
+ }
9689
+ const q = body.indexOf("q");
9690
+ if (q < 0 || q > 32) return false;
9691
+ const introducer = body.slice(0, q);
9692
+ if (!/^[0-9;?]*$/.test(introducer)) return false;
9693
+ const data = body.slice(q + 1);
9694
+ if (!data.length) return false;
9695
+ index = end + ST.length;
9696
+ }
9697
+ return sequence.length > 0;
9698
+ }
9699
+ function stripTerminalControlChars(value) {
9700
+ let out = "";
9701
+ for (let index = 0; index < value.length; index++) {
9702
+ const code = value.charCodeAt(index);
9703
+ if (code <= 8 || code === 11 || code === 12 || code >= 14 && code <= 31 || code === 127 || code >= 128 && code <= 159) continue;
9704
+ out += value[index];
9705
+ }
9706
+ return out;
9707
+ }
9708
+ function sanitizeTerminalFallbackText(text) {
9709
+ return stripTerminalControlChars(String(text ?? "").replace(OSC_RE, "").replace(CSI_RE, "").replace(DCS_RE, "").replace(APC_RE, "").replace(C1_OSC_RE, "").replace(C1_CSI_RE, "").replace(C1_DCS_RE, "").replace(C1_APC_RE, "")).slice(0, MAX_TERMINAL_GRAPHICS_FALLBACK_CHARS);
9710
+ }
9711
+ function validateTerminalGraphicFrame(frame) {
9712
+ const startedAt = nowTerminalGraphicTraceTime();
9713
+ const protocol = isTerminalGraphicsProtocol(frame.protocol) ? frame.protocol : void 0;
9714
+ const recordValidation = () => {
9715
+ recordTerminalGraphicTrace({
9716
+ type: "validate-end",
9717
+ id: frame.id ?? "",
9718
+ protocol,
9719
+ durationMs: nowTerminalGraphicTraceTime() - startedAt,
9720
+ bytes: typeof frame.sequence === "string" ? frame.sequence.length : void 0
9721
+ });
9722
+ };
9723
+ if (!protocol) {
9724
+ recordValidation();
9725
+ return null;
9726
+ }
9727
+ const sequence = String(frame.sequence ?? "");
9728
+ if (sequence.length <= 0 || sequence.length > MAX_TERMINAL_GRAPHICS_SEQUENCE_CHARS) {
9729
+ recordValidation();
9730
+ return null;
9731
+ }
9732
+ const size = normalizeTerminalGraphicSize(frame.width, frame.height);
9733
+ if (!size) {
9734
+ recordValidation();
9735
+ return null;
9736
+ }
9737
+ const ok = protocol === "kitty" ? validateKittySequence(sequence, "draw") : protocol === "iterm2" ? validateIterm2Sequence(sequence, "draw") : validateSixelSequence(sequence, "draw");
9738
+ if (!ok) {
9739
+ recordValidation();
9740
+ return null;
9741
+ }
9742
+ recordValidation();
9743
+ return {
9744
+ ...frame,
9745
+ protocol,
9746
+ id: frame.id || stableId(`${protocol}:${sequence.slice(0, 4096)}`),
9747
+ sequence,
9748
+ fallbackText: sanitizeTerminalFallbackText(frame.fallbackText),
9749
+ width: size.width,
9750
+ height: size.height
9751
+ };
9752
+ }
9753
+ function isSafeTerminalGraphicsSequence(sequence, protocol, op = "draw") {
9754
+ if (!isTerminalGraphicsProtocol(protocol)) return false;
9755
+ if (!sequence || sequence.length > MAX_TERMINAL_GRAPHICS_SEQUENCE_CHARS) return false;
9756
+ return protocol === "kitty" ? validateKittySequence(sequence, op) : protocol === "iterm2" ? validateIterm2Sequence(sequence, op) : validateSixelSequence(sequence, op);
9757
+ }
9758
+ function validateTerminalGraphicsPayload(payload, capabilities) {
9759
+ if (!payload.id.trim()) return false;
9760
+ if (!Number.isFinite(payload.x) || !Number.isFinite(payload.y)) return false;
9761
+ if (!normalizeTerminalGraphicSize(payload.w ?? 1, payload.h ?? 1)) return false;
9762
+ if (!capabilities.supported) return false;
9763
+ if (!isTerminalGraphicsProtocol(payload.protocol)) return false;
9764
+ if (payload.protocol !== capabilities.preferredProtocol) return false;
9765
+ return isSafeTerminalGraphicsSequence(payload.sequence, payload.protocol, payload.op ?? "draw");
9766
+ }
9767
+ function uint32Control(key, value, min = 0) {
9768
+ if (value == null) return "";
9769
+ const n = Math.floor(value);
9770
+ return Number.isSafeInteger(n) && n >= min && n <= KITTY_UINT32_MAX ? `${key}=${n}` : "";
9771
+ }
9772
+ function int32Control(key, value) {
9773
+ if (value == null) return "";
9774
+ const n = Math.floor(value);
9775
+ return Number.isSafeInteger(n) && n >= KITTY_INT32_MIN && n <= KITTY_INT32_MAX ? `${key}=${n}` : "";
9776
+ }
9777
+ function sanitizeBase64(value) {
9778
+ const data = String(value ?? "").replace(/\s+/g, "");
9779
+ return isBase64ish(data) ? data : "";
9780
+ }
9781
+ function kittyGraphicCellControls(columns, rows) {
9782
+ if (columns == null && rows == null) return [];
9783
+ const size = normalizeTerminalGraphicSize(columns ?? 1, rows ?? 1);
9784
+ if (!size) return [];
9785
+ return [columns == null ? "" : `c=${size.width}`, rows == null ? "" : `r=${size.height}`];
9786
+ }
9787
+ function kittyGraphicSourceControls(sourceX, sourceY, sourceWidth, sourceHeight) {
9788
+ return [
9789
+ uint32Control("x", sourceX),
9790
+ uint32Control("y", sourceY),
9791
+ uint32Control("w", sourceWidth, 1),
9792
+ uint32Control("h", sourceHeight, 1)
9793
+ ];
9794
+ }
9795
+ function chunkBase64(value, chunkSize) {
9796
+ const normalizedSize = Math.max(4, Math.floor(chunkSize / 4) * 4);
9797
+ const chunks = [];
9798
+ for (let index = 0; index < value.length; index += normalizedSize) chunks.push(value.slice(index, index + normalizedSize));
9799
+ return chunks.length ? chunks : [""];
9800
+ }
9801
+ function createKittyGraphicsSequence(base64Png, options = {}) {
9802
+ const data = sanitizeBase64(base64Png);
9803
+ if (!data) return "";
9804
+ const chunks = chunkBase64(data, options.chunkSize ?? 4096);
9805
+ const baseControls = [
9806
+ "a=T",
9807
+ "f=100",
9808
+ options.quiet === false ? "" : "q=2",
9809
+ options.noCursorMove === false ? "" : "C=1",
9810
+ uint32Control("i", options.imageId),
9811
+ uint32Control("I", options.imageNumber),
9812
+ uint32Control("p", options.placementId),
9813
+ ...kittyGraphicCellControls(options.columns, options.rows),
9814
+ ...kittyGraphicSourceControls(options.sourceX, options.sourceY, options.sourceWidth, options.sourceHeight),
9815
+ int32Control("z", options.zIndex)
9816
+ ].filter(Boolean);
9817
+ return chunks.map((chunk, index) => {
9818
+ const last = index === chunks.length - 1;
9819
+ const controls = index === 0 ? [...baseControls, chunks.length > 1 ? `m=${last ? 0 : 1}` : ""] : [options.quiet === false ? "" : "q=2", `m=${last ? 0 : 1}`].filter(Boolean);
9820
+ return `${ESC}_G${controls.filter(Boolean).join(",")};${chunk}${ST}`;
9821
+ }).join("");
9822
+ }
9823
+ function createKittyDeleteGraphicsSequence(options = {}) {
9824
+ const controls = ["a=d"];
9825
+ if (options.allVisible) controls.push(`d=${options.freeImageData ? "A" : "a"}`);
9826
+ else if (options.imageId != null) {
9827
+ const imageId = uint32Control("i", options.imageId);
9828
+ const placementId = uint32Control("p", options.placementId);
9829
+ if (!imageId || options.placementId != null && !placementId) return "";
9830
+ controls.push(`d=${options.freeImageData ? "I" : "i"}`);
9831
+ controls.push(imageId);
9832
+ controls.push(placementId);
9833
+ } else if (options.currentCell) controls.push(`d=${options.freeImageData ? "C" : "c"}`);
9834
+ else return "";
9835
+ if (options.quiet !== false) controls.push("q=2");
9836
+ return `${ESC}_G${controls.filter(Boolean).join(",")}${ST}`;
9837
+ }
9838
+ function createIterm2InlineImageSequence(base64Data, options = {}) {
9839
+ const data = sanitizeBase64(base64Data);
9840
+ if (!data) return "";
9841
+ const params = [
9842
+ "inline=1",
9843
+ options.width != null ? `width=${formatItermDimension(options.width)}` : "",
9844
+ options.height != null ? `height=${formatItermDimension(options.height)}` : "",
9845
+ `preserveAspectRatio=${options.preserveAspectRatio === false ? 0 : 1}`,
9846
+ options.doNotMoveCursor === false ? "" : "doNotMoveCursor=1"
9847
+ ].filter(Boolean);
9848
+ return `${ESC}]1337;File=${params.join(";")}:${data}${BEL}`;
9849
+ }
9850
+ function formatItermDimension(value) {
9851
+ if (typeof value === "number") {
9852
+ const n = Math.floor(value);
9853
+ if (!Number.isFinite(n)) return "auto";
9854
+ return String(Math.min(99999, Math.max(1, n)));
9855
+ }
9856
+ const raw = String(value ?? "").trim();
9857
+ return validateIterm2Dimension(raw) ? raw : "auto";
9858
+ }
9859
+ function wrapTerminalGraphicsForMultiplexer(sequence, capabilities) {
9860
+ if (!capabilities.passthrough) return sequence;
9861
+ if (capabilities.multiplexer === "tmux") return `${ESC}Ptmux;${sequence.split(ESC).join(`${ESC}${ESC}`)}${ST}`;
9862
+ return sequence;
9863
+ }
9864
+
8420
9865
  //#endregion
8421
9866
  //#region src/renderer/cli/stdout-renderer.ts
8422
9867
  let debugLog = null;
@@ -8431,7 +9876,27 @@ const SYNC_START = "\x1B[?2026h";
8431
9876
  const SYNC_END = "\x1B[?2026l";
8432
9877
  const OSC8_OPEN = (href) => `\u001B]8;;${href}\u0007`;
8433
9878
  const OSC8_CLOSE = "\x1B]8;;\x07";
9879
+ const CURSOR_SAVE = "\x1B7";
9880
+ const CURSOR_RESTORE = "\x1B8";
9881
+ const CURSOR_HOME = "\x1B[H";
8434
9882
  const stdoutFingerprintOwners = new WeakMap();
9883
+ function isTerminalGraphicsCapabilities(value) {
9884
+ return typeof value === "object" && value !== null && "supported" in value && "protocol" in value && "preferredProtocol" in value && "candidates" in value;
9885
+ }
9886
+ function resolveTerminalGraphicsCapabilities(options, env, outputIsTTY) {
9887
+ const terminalGraphics = options === null || options === void 0 ? void 0 : options.terminalGraphics;
9888
+ if (terminalGraphics === false) return detectTerminalGraphicsCapabilities({
9889
+ env,
9890
+ isTTY: outputIsTTY,
9891
+ protocol: "none"
9892
+ });
9893
+ if (isTerminalGraphicsCapabilities(terminalGraphics)) return terminalGraphics;
9894
+ return detectTerminalGraphicsCapabilities({
9895
+ env,
9896
+ isTTY: outputIsTTY,
9897
+ ...terminalGraphics ?? {}
9898
+ });
9899
+ }
8435
9900
  function createStdoutRenderer(terminal, options) {
8436
9901
  const env = (process === null || process === void 0 ? void 0 : process.env) ?? {};
8437
9902
  if (shouldInstallFileWriters(env)) installNodeFileWriters();
@@ -8449,6 +9914,7 @@ function createStdoutRenderer(terminal, options) {
8449
9914
  const clear = (options === null || options === void 0 ? void 0 : options.clear) ?? true;
8450
9915
  const hideCursor = (options === null || options === void 0 ? void 0 : options.hideCursor) ?? true;
8451
9916
  const altScreen = (options === null || options === void 0 ? void 0 : options.altScreen) ?? outputIsTTY;
9917
+ const keepLineWrapDisabled = altScreen && outputIsTTY;
8452
9918
  let defaultBg = (options === null || options === void 0 ? void 0 : options.defaultBg) == null || (options === null || options === void 0 ? void 0 : options.defaultBg) === "transparent" ? void 0 : (options === null || options === void 0 ? void 0 : options.defaultBg) ?? "black";
8453
9919
  let palette = (options === null || options === void 0 ? void 0 : options.palette) ?? null;
8454
9920
  const trackResize = (options === null || options === void 0 ? void 0 : options.trackResize) ?? true;
@@ -8581,6 +10047,7 @@ function createStdoutRenderer(terminal, options) {
8581
10047
  let lastFrameTime = 0;
8582
10048
  let pendingRender = false;
8583
10049
  let renderTimer = null;
10050
+ let rowsOnlyResizeRepaintTimer = null;
8584
10051
  let writeEmaMs = 0;
8585
10052
  let lastCursorX = null;
8586
10053
  let lastCursorY = null;
@@ -8591,7 +10058,505 @@ function createStdoutRenderer(terminal, options) {
8591
10058
  let accumulatedDirtyMax = -1;
8592
10059
  let accumulatedDirtyRowsRequireRepaint = false;
8593
10060
  let accumulatedScrollOperations = null;
10061
+ let allowNextGraphicsOnlyRenderWithoutBaseline = false;
10062
+ let preserveNextRowsOnlyResizeBaseline = false;
10063
+ let deferGraphicsFlushUntilResizeCommit = false;
10064
+ const graphicsCapabilities = resolveTerminalGraphicsCapabilities(options, env, outputIsTTY);
10065
+ const pendingGraphics = new Map();
10066
+ const pendingGraphicClears = new Set();
10067
+ const activeGraphics = new Map();
10068
+ const pendingGraphicSignatures = new Map();
10069
+ const activeGraphicSignatures = new Map();
10070
+ const retainedGraphics = new Map();
10071
+ const knownKittyImageIds = new Set();
10072
+ const terminalResizeDirtyGraphics = new Set();
10073
+ let nextGraphicsOrder = 0;
10074
+ let terminalGraphicsFlushTimer = null;
10075
+ const hasPendingTerminalGraphics = () => pendingGraphics.size > 0 || pendingGraphicClears.size > 0;
10076
+ function canUseTerminalGraphicsProtocol(protocol, capabilities) {
10077
+ return capabilities.supported && isTerminalGraphicsProtocol(protocol) && protocol === capabilities.preferredProtocol;
10078
+ }
10079
+ function requestTerminalGraphicsFlush(defer = false) {
10080
+ if (disposed) return;
10081
+ if (deferGraphicsFlushUntilResizeCommit) {
10082
+ if (isDebugEnabled()) getDebugLog().render("terminal graphics flush deferred until resize commit");
10083
+ return;
10084
+ }
10085
+ if (!defer) {
10086
+ render([], false);
10087
+ return;
10088
+ }
10089
+ if (terminalGraphicsFlushTimer) return;
10090
+ terminalGraphicsFlushTimer = setTimeout(() => {
10091
+ terminalGraphicsFlushTimer = null;
10092
+ if (!disposed) render([], false);
10093
+ }, 0);
10094
+ }
10095
+ function terminalGraphicsPayloadSignature(payload) {
10096
+ return [
10097
+ payload.id,
10098
+ payload.protocol,
10099
+ payload.x,
10100
+ payload.y,
10101
+ payload.w ?? "",
10102
+ payload.h ?? "",
10103
+ hashTerminalGraphicsString(payload.sequence),
10104
+ hashTerminalGraphicsString(payload.resizeSequence ?? ""),
10105
+ hashTerminalGraphicsString(payload.clearSequence ?? ""),
10106
+ payload.allowTextOverlay ? "1" : "0"
10107
+ ].join(":");
10108
+ }
10109
+ function kittyClearSequenceKeepsImageData(sequence) {
10110
+ let index = 0;
10111
+ while (index < sequence.length) {
10112
+ const start = sequence.indexOf("\x1B_G", index);
10113
+ if (start < 0) return false;
10114
+ const end = sequence.indexOf("\x1B\\", start + 3);
10115
+ if (end < 0) return false;
10116
+ const body = sequence.slice(start + 3, end);
10117
+ const controls = body.slice(0, body.indexOf(";") < 0 ? body.length : body.indexOf(";"));
10118
+ const parts = controls.split(",");
10119
+ if (parts.includes("a=d") && parts.includes("d=i")) return true;
10120
+ index = end + 2;
10121
+ }
10122
+ return false;
10123
+ }
10124
+ function retainTerminalGraphic(id, active, signature, options$1 = {}) {
10125
+ if (!signature || active.protocol !== "kitty" || !active.resizeSequence || !active.clearSequence || !kittyClearSequenceKeepsImageData(active.clearSequence)) {
10126
+ retainedGraphics.delete(id);
10127
+ return;
10128
+ }
10129
+ retainedGraphics.set(id, {
10130
+ active,
10131
+ signature,
10132
+ autoRedrawOnResize: Boolean(options$1.autoRedrawOnResize)
10133
+ });
10134
+ }
10135
+ function kittyPlacementKey(sequence) {
10136
+ if (!sequence) return null;
10137
+ const start = sequence.indexOf("\x1B_G");
10138
+ if (start < 0) return null;
10139
+ const end = sequence.indexOf("\x1B\\", start + 3);
10140
+ if (end < 0) return null;
10141
+ const body = sequence.slice(start + 3, end);
10142
+ const controlsRaw = body.slice(0, body.indexOf(";") < 0 ? body.length : body.indexOf(";"));
10143
+ const controls = new Map();
10144
+ for (const part of controlsRaw.split(",")) {
10145
+ const eq = part.indexOf("=");
10146
+ if (eq <= 0) continue;
10147
+ controls.set(part.slice(0, eq), part.slice(eq + 1));
10148
+ }
10149
+ const imageId = controls.get("i");
10150
+ const placementId = controls.get("p");
10151
+ if (controls.get("a") !== "p" || !imageId || !placementId) return null;
10152
+ if (imageId === "0" || placementId === "0") return null;
10153
+ return `${imageId}:${placementId}`;
10154
+ }
10155
+ function kittyImageId(sequence) {
10156
+ if (!sequence) return null;
10157
+ const start = sequence.indexOf("\x1B_G");
10158
+ if (start < 0) return null;
10159
+ const end = sequence.indexOf("\x1B\\", start + 3);
10160
+ if (end < 0) return null;
10161
+ const body = sequence.slice(start + 3, end);
10162
+ const controlsRaw = body.slice(0, body.indexOf(";") < 0 ? body.length : body.indexOf(";"));
10163
+ for (const part of controlsRaw.split(",")) {
10164
+ if (!part.startsWith("i=")) continue;
10165
+ const imageId = Number(part.slice(2));
10166
+ return Number.isInteger(imageId) && imageId > 0 ? imageId : null;
10167
+ }
10168
+ return null;
10169
+ }
10170
+ function canReplaceKittyPlacementWithoutClear(previous, payload) {
10171
+ if (payload.terminalResizeRedraw) return false;
10172
+ if (!payload.resizeRedraw || previous.protocol !== "kitty" || payload.protocol !== "kitty") return false;
10173
+ const nextKey = kittyPlacementKey(payload.resizeSequence ?? payload.sequence);
10174
+ if (!nextKey) return false;
10175
+ return kittyPlacementKey(previous.resizeSequence ?? previous.sequence) === nextKey && (payload.placementMoveWithoutClear || sameGraphicRect(previous, {
10176
+ x: payload.x,
10177
+ y: payload.y,
10178
+ w: payload.w ?? previous.w,
10179
+ h: payload.h ?? previous.h
10180
+ }));
10181
+ }
10182
+ function queueTerminalGraphicClear(id, options$1 = {}) {
10183
+ if (disposed) return false;
10184
+ const removedPendingDraw = pendingGraphics.delete(`${id}:draw`);
10185
+ pendingGraphicSignatures.delete(id);
10186
+ terminalResizeDirtyGraphics.delete(id);
10187
+ const active = activeGraphics.get(id);
10188
+ if (!active) {
10189
+ if (!options$1.retainOnClear) retainedGraphics.delete(id);
10190
+ return removedPendingDraw;
10191
+ }
10192
+ if (active.clearSequence) {
10193
+ const payload = {
10194
+ id,
10195
+ x: active.x,
10196
+ y: active.y,
10197
+ w: active.w,
10198
+ h: active.h,
10199
+ protocol: active.protocol,
10200
+ sequence: active.clearSequence,
10201
+ retainOnClear: options$1.retainOnClear,
10202
+ op: "clear",
10203
+ order: nextGraphicsOrder++
10204
+ };
10205
+ if (validateTerminalGraphicsPayload(payload, graphicsCapabilities)) {
10206
+ pendingGraphics.set(`${id}:clear`, payload);
10207
+ recordTerminalGraphicTrace({
10208
+ type: "clear",
10209
+ id,
10210
+ protocol: payload.protocol
10211
+ });
10212
+ requestTerminalGraphicsFlush();
10213
+ return true;
10214
+ }
10215
+ }
10216
+ pendingGraphicClears.add(id);
10217
+ recordTerminalGraphicTrace({
10218
+ type: "clear",
10219
+ id
10220
+ });
10221
+ requestTerminalGraphicsFlush();
10222
+ return true;
10223
+ }
10224
+ function clearAllTerminalGraphics() {
10225
+ if (disposed || graphicsCapabilities.preferredProtocol !== "kitty") return false;
10226
+ const imageIds = new Set(knownKittyImageIds);
10227
+ let needsVisibleFallback = false;
10228
+ const rememberImageId = (...sequences) => {
10229
+ for (const sequence of sequences) {
10230
+ const imageId = kittyImageId(sequence);
10231
+ if (imageId != null) {
10232
+ imageIds.add(imageId);
10233
+ return;
10234
+ }
10235
+ }
10236
+ needsVisibleFallback = true;
10237
+ };
10238
+ for (const active of activeGraphics.values()) rememberImageId(active.sequence, active.resizeSequence, active.clearSequence);
10239
+ for (const retained of retainedGraphics.values()) rememberImageId(retained.active.sequence, retained.active.resizeSequence, retained.active.clearSequence);
10240
+ for (const payload of pendingGraphics.values()) rememberImageId(payload.sequence, payload.resizeSequence, payload.clearSequence);
10241
+ pendingGraphics.clear();
10242
+ pendingGraphicClears.clear();
10243
+ activeGraphics.clear();
10244
+ pendingGraphicSignatures.clear();
10245
+ activeGraphicSignatures.clear();
10246
+ retainedGraphics.clear();
10247
+ knownKittyImageIds.clear();
10248
+ terminalResizeDirtyGraphics.clear();
10249
+ for (const imageId of imageIds) {
10250
+ const payload = {
10251
+ id: `__terminal_graphics_clear_image_${imageId}__`,
10252
+ x: 0,
10253
+ y: 0,
10254
+ w: 1,
10255
+ h: 1,
10256
+ protocol: "kitty",
10257
+ sequence: createKittyDeleteGraphicsSequence({
10258
+ imageId,
10259
+ freeImageData: true
10260
+ }),
10261
+ op: "clear",
10262
+ order: nextGraphicsOrder++
10263
+ };
10264
+ if (validateTerminalGraphicsPayload(payload, graphicsCapabilities)) pendingGraphics.set(`${payload.id}:clear`, payload);
10265
+ }
10266
+ if (needsVisibleFallback) {
10267
+ const payload = {
10268
+ id: "__terminal_graphics_clear_visible__",
10269
+ x: 0,
10270
+ y: 0,
10271
+ w: 1,
10272
+ h: 1,
10273
+ protocol: "kitty",
10274
+ sequence: createKittyDeleteGraphicsSequence({
10275
+ allVisible: true,
10276
+ freeImageData: true
10277
+ }),
10278
+ op: "clear",
10279
+ order: nextGraphicsOrder++
10280
+ };
10281
+ if (validateTerminalGraphicsPayload(payload, graphicsCapabilities)) pendingGraphics.set(`${payload.id}:clear`, payload);
10282
+ }
10283
+ if (pendingGraphics.size === 0) return false;
10284
+ requestTerminalGraphicsFlush();
10285
+ return true;
10286
+ }
10287
+ function queueTerminalGraphicsForResizeRedraw(size) {
10288
+ if (isDebugEnabled()) getDebugLog().render(`graphics resize redraw queue: size=${size.cols}x${size.rows}, activeGraphics=${activeGraphics.size}, pendingGraphics=${pendingGraphics.size}, pendingClears=${pendingGraphicClears.size}`);
10289
+ for (const [id, active] of activeGraphics) {
10290
+ terminalResizeDirtyGraphics.add(id);
10291
+ pendingGraphics.delete(`${id}:draw`);
10292
+ const anchored = anchoredGraphicRectInViewport(active, size);
10293
+ if (anchored && !active.resizeSequence) {
10294
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic resize active: id=${id}, x=${active.x}, y=${active.y}, w=${active.w}, h=${active.h}, viewport=${size.cols}x${size.rows}, anchored=true, skipped=no-resize-sequence`);
10295
+ continue;
10296
+ }
10297
+ const signature = activeGraphicSignatures.get(id);
10298
+ const sequence = active.sequence;
10299
+ pendingGraphics.set(`${id}:draw`, {
10300
+ id,
10301
+ x: active.x,
10302
+ y: active.y,
10303
+ w: active.w,
10304
+ h: active.h,
10305
+ protocol: active.protocol,
10306
+ sequence,
10307
+ resizeSequence: active.resizeSequence,
10308
+ clearSequence: active.clearSequence,
10309
+ allowTextOverlay: active.allowTextOverlay,
10310
+ op: "draw",
10311
+ order: nextGraphicsOrder++,
10312
+ resizeRedraw: true,
10313
+ terminalResizeRedraw: true
10314
+ });
10315
+ if (signature) pendingGraphicSignatures.set(id, signature);
10316
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic resize active: id=${id}, x=${active.x}, y=${active.y}, w=${active.w}, h=${active.h}, viewport=${size.cols}x${size.rows}, anchored=${anchored ? "true" : "false"}, bytes=${sequence.length}`);
10317
+ }
10318
+ for (const [id, retained] of retainedGraphics) {
10319
+ if (activeGraphics.has(id) || !retained.autoRedrawOnResize) continue;
10320
+ const { active, signature } = retained;
10321
+ if (!anchoredGraphicRectInViewport(active, size)) continue;
10322
+ retainedGraphics.delete(id);
10323
+ activeGraphics.set(id, active);
10324
+ activeGraphicSignatures.set(id, signature);
10325
+ pendingGraphicClears.delete(id);
10326
+ pendingGraphics.delete(`${id}:clear`);
10327
+ pendingGraphics.set(`${id}:draw`, {
10328
+ id,
10329
+ x: active.x,
10330
+ y: active.y,
10331
+ w: active.w,
10332
+ h: active.h,
10333
+ protocol: active.protocol,
10334
+ sequence: active.sequence,
10335
+ resizeSequence: active.resizeSequence,
10336
+ clearSequence: active.clearSequence,
10337
+ allowTextOverlay: active.allowTextOverlay,
10338
+ op: "draw",
10339
+ order: nextGraphicsOrder++,
10340
+ resizeRedraw: true,
10341
+ terminalResizeRedraw: true
10342
+ });
10343
+ pendingGraphicSignatures.set(id, signature);
10344
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic retained resize redraw: id=${id}, x=${active.x}, y=${active.y}, w=${active.w}, h=${active.h}, bytes=${(active.resizeSequence ?? active.sequence).length}`);
10345
+ }
10346
+ }
10347
+ const graphicsOutput = {
10348
+ capabilities: graphicsCapabilities,
10349
+ isActive(id) {
10350
+ if (pendingGraphicClears.has(id) || pendingGraphics.has(`${id}:clear`)) return false;
10351
+ return activeGraphics.has(id) || retainedGraphics.has(id) || pendingGraphics.has(`${id}:draw`);
10352
+ },
10353
+ queue(payload) {
10354
+ if (disposed) return false;
10355
+ const x = Math.floor(payload.x);
10356
+ const y = Math.floor(payload.y);
10357
+ const op = payload.op ?? "draw";
10358
+ const normalized = {
10359
+ ...payload,
10360
+ x,
10361
+ y,
10362
+ w: payload.w == null ? void 0 : Math.floor(payload.w),
10363
+ h: payload.h == null ? void 0 : Math.floor(payload.h),
10364
+ op,
10365
+ order: nextGraphicsOrder++
10366
+ };
10367
+ if (!normalized.id.trim()) return false;
10368
+ if (!Number.isFinite(normalized.x) || !Number.isFinite(normalized.y)) return false;
10369
+ if (!canUseTerminalGraphicsProtocol(normalized.protocol, graphicsCapabilities)) return false;
10370
+ if (op === "draw") {
10371
+ const frame = validateTerminalGraphicFrame({
10372
+ id: normalized.id,
10373
+ protocol: normalized.protocol,
10374
+ sequence: normalized.sequence,
10375
+ fallbackText: normalized.fallbackText,
10376
+ width: normalized.w ?? 1,
10377
+ height: normalized.h ?? 1
10378
+ });
10379
+ if (!frame) return false;
10380
+ const imageId = kittyImageId(frame.sequence) ?? kittyImageId(normalized.resizeSequence);
10381
+ if (imageId != null) knownKittyImageIds.add(imageId);
10382
+ if (isDebugEnabled()) {
10383
+ const size = terminal.size();
10384
+ getDebugLog().render(`terminal graphic queue draw request: id=${frame.id}, x=${normalized.x}, y=${normalized.y}, w=${frame.width}, h=${frame.height}, viewport=${size.cols}x${size.rows}`);
10385
+ }
10386
+ if (!anchoredGraphicRectInViewport({
10387
+ x: normalized.x,
10388
+ y: normalized.y,
10389
+ w: frame.width,
10390
+ h: frame.height
10391
+ }, terminal.size())) {
10392
+ if (isDebugEnabled()) {
10393
+ const size = terminal.size();
10394
+ getDebugLog().render(`terminal graphic queue draw rejected offscreen: id=${frame.id}, x=${normalized.x}, y=${normalized.y}, w=${frame.width}, h=${frame.height}, viewport=${size.cols}x${size.rows}`);
10395
+ }
10396
+ queueTerminalGraphicClear(frame.id, { retainOnClear: normalized.retainOnClear });
10397
+ return false;
10398
+ }
10399
+ const clearSequence = normalized.clearSequence && isSafeTerminalGraphicsSequence(normalized.clearSequence, normalized.protocol, "clear") ? normalized.clearSequence : void 0;
10400
+ const resizeSequence = normalized.resizeSequence && isSafeTerminalGraphicsSequence(normalized.resizeSequence, normalized.protocol, "draw") ? normalized.resizeSequence : void 0;
10401
+ const signature = terminalGraphicsPayloadSignature({
10402
+ ...normalized,
10403
+ id: frame.id,
10404
+ w: frame.width,
10405
+ h: frame.height,
10406
+ sequence: frame.sequence,
10407
+ resizeSequence,
10408
+ clearSequence
10409
+ });
10410
+ const forceTerminalResizeRedraw = terminalResizeDirtyGraphics.has(frame.id);
10411
+ const removedPendingGraphicClear = pendingGraphics.delete(`${frame.id}:clear`);
10412
+ const removedPendingClear = pendingGraphicClears.delete(frame.id);
10413
+ const canceledPendingClear = removedPendingGraphicClear || removedPendingClear;
10414
+ if (!forceTerminalResizeRedraw && !normalized.forceDraw && !canceledPendingClear && (pendingGraphicSignatures.get(frame.id) === signature || activeGraphicSignatures.get(frame.id) === signature)) {
10415
+ recordTerminalGraphicTrace({
10416
+ type: "queue-dedupe",
10417
+ id: frame.id,
10418
+ protocol: normalized.protocol
10419
+ });
10420
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic queue draw dedupe: id=${frame.id}, x=${normalized.x}, y=${normalized.y}, w=${frame.width}, h=${frame.height}`);
10421
+ return true;
10422
+ }
10423
+ if (!forceTerminalResizeRedraw && !normalized.forceDraw && canceledPendingClear && activeGraphicSignatures.get(frame.id) === signature) {
10424
+ recordTerminalGraphicTrace({
10425
+ type: "queue-dedupe",
10426
+ id: frame.id,
10427
+ protocol: normalized.protocol
10428
+ });
10429
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic queue draw canceled clear: id=${frame.id}, x=${normalized.x}, y=${normalized.y}, w=${frame.width}, h=${frame.height}`);
10430
+ return true;
10431
+ }
10432
+ const active = activeGraphics.get(frame.id);
10433
+ const activeCanReplacePlacement = active && active.sequence === frame.sequence && resizeSequence && canReplaceKittyPlacementWithoutClear(active, {
10434
+ ...normalized,
10435
+ id: frame.id,
10436
+ w: frame.width,
10437
+ h: frame.height,
10438
+ sequence: resizeSequence,
10439
+ resizeSequence,
10440
+ clearSequence,
10441
+ fallbackText: frame.fallbackText,
10442
+ resizeRedraw: true,
10443
+ placementMoveWithoutClear: true
10444
+ });
10445
+ if (!forceTerminalResizeRedraw && !normalized.forceDraw && !normalized.resizeRedraw && activeCanReplacePlacement && resizeSequence) {
10446
+ pendingGraphics.set(`${frame.id}:draw`, {
10447
+ ...normalized,
10448
+ id: frame.id,
10449
+ w: frame.width,
10450
+ h: frame.height,
10451
+ sequence: resizeSequence,
10452
+ resizeSequence,
10453
+ clearSequence,
10454
+ fallbackText: frame.fallbackText,
10455
+ resizeRedraw: true,
10456
+ placementMoveWithoutClear: false
10457
+ });
10458
+ pendingGraphicSignatures.set(frame.id, signature);
10459
+ recordTerminalGraphicTrace({
10460
+ type: "queue",
10461
+ id: frame.id,
10462
+ protocol: normalized.protocol,
10463
+ bytes: resizeSequence.length
10464
+ });
10465
+ requestTerminalGraphicsFlush(Boolean(normalized.deferFlush));
10466
+ return true;
10467
+ }
10468
+ const retained = retainedGraphics.get(frame.id);
10469
+ const retainedCanReplacePlacement = retained && retained.active.sequence === frame.sequence && resizeSequence && canReplaceKittyPlacementWithoutClear(retained.active, {
10470
+ ...normalized,
10471
+ id: frame.id,
10472
+ w: frame.width,
10473
+ h: frame.height,
10474
+ sequence: resizeSequence,
10475
+ resizeSequence,
10476
+ clearSequence,
10477
+ fallbackText: frame.fallbackText,
10478
+ resizeRedraw: true,
10479
+ placementMoveWithoutClear: true
10480
+ });
10481
+ if (!canceledPendingClear && retained && resizeSequence && (retained.signature === signature || retainedCanReplacePlacement)) {
10482
+ activeGraphics.set(frame.id, retained.active);
10483
+ activeGraphicSignatures.set(frame.id, retained.signature);
10484
+ retainedGraphics.delete(frame.id);
10485
+ pendingGraphics.set(`${frame.id}:draw`, {
10486
+ ...normalized,
10487
+ id: frame.id,
10488
+ w: frame.width,
10489
+ h: frame.height,
10490
+ sequence: forceTerminalResizeRedraw ? frame.sequence : resizeSequence,
10491
+ resizeSequence,
10492
+ clearSequence,
10493
+ fallbackText: frame.fallbackText,
10494
+ resizeRedraw: true,
10495
+ placementMoveWithoutClear: false,
10496
+ terminalResizeRedraw: forceTerminalResizeRedraw
10497
+ });
10498
+ pendingGraphicSignatures.set(frame.id, signature);
10499
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic retained resize redraw: id=${frame.id}, x=${retained.active.x}, y=${retained.active.y}, w=${retained.active.w}, h=${retained.active.h}, bytes=${resizeSequence.length}`);
10500
+ recordTerminalGraphicTrace({
10501
+ type: "queue",
10502
+ id: frame.id,
10503
+ protocol: normalized.protocol,
10504
+ bytes: resizeSequence.length
10505
+ });
10506
+ requestTerminalGraphicsFlush(Boolean(normalized.deferFlush));
10507
+ return true;
10508
+ }
10509
+ retainedGraphics.delete(frame.id);
10510
+ pendingGraphics.set(`${frame.id}:draw`, {
10511
+ ...normalized,
10512
+ id: frame.id,
10513
+ w: frame.width,
10514
+ h: frame.height,
10515
+ sequence: frame.sequence,
10516
+ resizeSequence,
10517
+ clearSequence,
10518
+ fallbackText: frame.fallbackText,
10519
+ terminalResizeRedraw: forceTerminalResizeRedraw
10520
+ });
10521
+ pendingGraphicSignatures.set(frame.id, signature);
10522
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic queue draw accepted: id=${frame.id}, x=${normalized.x}, y=${normalized.y}, w=${frame.width}, h=${frame.height}, bytes=${frame.sequence.length}`);
10523
+ recordTerminalGraphicTrace({
10524
+ type: "queue",
10525
+ id: frame.id,
10526
+ protocol: normalized.protocol,
10527
+ bytes: frame.sequence.length
10528
+ });
10529
+ requestTerminalGraphicsFlush(Boolean(normalized.deferFlush));
10530
+ return true;
10531
+ }
10532
+ if (!validateTerminalGraphicsPayload(normalized, graphicsCapabilities)) return false;
10533
+ pendingGraphics.delete(`${normalized.id}:draw`);
10534
+ pendingGraphicSignatures.delete(normalized.id);
10535
+ retainedGraphics.delete(normalized.id);
10536
+ pendingGraphics.set(`${normalized.id}:clear`, {
10537
+ ...normalized,
10538
+ w: normalized.w,
10539
+ h: normalized.h
10540
+ });
10541
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic queue clear accepted: id=${normalized.id}, x=${normalized.x}, y=${normalized.y}, w=${normalized.w ?? 1}, h=${normalized.h ?? 1}`);
10542
+ recordTerminalGraphicTrace({
10543
+ type: "clear",
10544
+ id: normalized.id,
10545
+ protocol: normalized.protocol
10546
+ });
10547
+ requestTerminalGraphicsFlush(Boolean(normalized.deferFlush));
10548
+ return true;
10549
+ },
10550
+ clear(id) {
10551
+ return queueTerminalGraphicClear(id);
10552
+ },
10553
+ clearAll() {
10554
+ return clearAllTerminalGraphics();
10555
+ }
10556
+ };
10557
+ const unregisterGraphicsOutput = registerTerminalGraphicsOutput(terminal, graphicsOutput);
8594
10558
  const MIN_FRAME_MS = outputIsTTY ? 16 : 0;
10559
+ const ROWS_ONLY_RESIZE_REPAINT_DELAY_MS = 500;
8595
10560
  function clampCellToViewport(cell, size) {
8596
10561
  const maxX = Math.max(0, Math.floor(size.cols) - 1);
8597
10562
  const maxY = Math.max(0, Math.floor(size.rows) - 1);
@@ -8604,6 +10569,92 @@ function createStdoutRenderer(terminal, options) {
8604
10569
  y
8605
10570
  };
8606
10571
  }
10572
+ function normalizeGraphicRect(rect) {
10573
+ const x0 = Math.floor(rect.x);
10574
+ const y0 = Math.floor(rect.y);
10575
+ const w0 = Math.floor(rect.w);
10576
+ const h0 = Math.floor(rect.h);
10577
+ if (!Number.isFinite(x0) || !Number.isFinite(y0) || !Number.isFinite(w0) || !Number.isFinite(h0)) return null;
10578
+ const size = normalizeTerminalGraphicSize(w0, h0);
10579
+ if (!size) return null;
10580
+ return {
10581
+ x: x0,
10582
+ y: y0,
10583
+ w: size.width,
10584
+ h: size.height
10585
+ };
10586
+ }
10587
+ function clipGraphicRectToViewport(rect, size) {
10588
+ const cols = Math.max(0, Math.floor(size.cols));
10589
+ const rows = Math.max(0, Math.floor(size.rows));
10590
+ if (cols <= 0 || rows <= 0) return null;
10591
+ const normalized = normalizeGraphicRect(rect);
10592
+ if (!normalized) return null;
10593
+ const x0 = normalized.x;
10594
+ const y0 = normalized.y;
10595
+ const w0 = normalized.w;
10596
+ const h0 = normalized.h;
10597
+ const left = Math.max(0, x0);
10598
+ const top = Math.max(0, y0);
10599
+ const right = Math.min(cols, x0 + w0);
10600
+ const bottom = Math.min(rows, y0 + h0);
10601
+ if (right <= left || bottom <= top) return null;
10602
+ return {
10603
+ x: left,
10604
+ y: top,
10605
+ w: right - left,
10606
+ h: bottom - top
10607
+ };
10608
+ }
10609
+ function anchoredGraphicRectInViewport(rect, size) {
10610
+ const cols = Math.max(0, Math.floor(size.cols));
10611
+ const rows = Math.max(0, Math.floor(size.rows));
10612
+ if (cols <= 0 || rows <= 0) return null;
10613
+ const normalized = normalizeGraphicRect(rect);
10614
+ if (!normalized) return null;
10615
+ return normalized.x >= 0 && normalized.y >= 0 && normalized.x < cols && normalized.y < rows ? normalized : null;
10616
+ }
10617
+ function clearGraphicRect(rect) {
10618
+ const blank = " ".repeat(Math.max(0, rect.w));
10619
+ let out$1 = "";
10620
+ for (let row = 0; row < rect.h; row++) out$1 += `\u001B[${rect.y + row + 1};${rect.x + 1}H${blank}`;
10621
+ return out$1;
10622
+ }
10623
+ function sameGraphicRect(a, b) {
10624
+ return Boolean(a && a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h);
10625
+ }
10626
+ function maybeWrapTerminalGraphic(sequence) {
10627
+ return wrapTerminalGraphicsForMultiplexer(sequence, graphicsCapabilities);
10628
+ }
10629
+ function kittyClearTargetsCurrentCell(sequence) {
10630
+ const prefix = "\x1B_G";
10631
+ const terminator = "\x1B\\";
10632
+ let index = 0;
10633
+ while (index < sequence.length) {
10634
+ if (!sequence.startsWith(prefix, index)) return false;
10635
+ const end = sequence.indexOf(terminator, index + prefix.length);
10636
+ if (end < 0) return false;
10637
+ const body = sequence.slice(index + prefix.length, end);
10638
+ const semicolon = body.indexOf(";");
10639
+ const controlsText = semicolon < 0 ? body : body.slice(0, semicolon);
10640
+ let action = "";
10641
+ let deleteMode = "";
10642
+ for (const part of controlsText.split(",")) {
10643
+ const eq = part.indexOf("=");
10644
+ if (eq <= 0) continue;
10645
+ const key = part.slice(0, eq);
10646
+ const value = part.slice(eq + 1);
10647
+ if (key === "a") action = value;
10648
+ else if (key === "d") deleteMode = value;
10649
+ }
10650
+ if (action === "d" && (deleteMode === "c" || deleteMode === "C")) return true;
10651
+ index = end + terminator.length;
10652
+ }
10653
+ return false;
10654
+ }
10655
+ function clearSequenceNeedsVisibleCell(sequence, protocol) {
10656
+ return protocol === "kitty" && kittyClearTargetsCurrentCell(sequence);
10657
+ }
8607
10658
  function resolveColorMode() {
8608
10659
  const opt = (options === null || options === void 0 ? void 0 : options.colorMode) ?? "auto";
8609
10660
  if (opt !== "auto") return opt;
@@ -8755,6 +10806,7 @@ function createStdoutRenderer(terminal, options) {
8755
10806
  const MAX_CELL_TEXT_IDS = 131072;
8756
10807
  let prevOverlayBlockedRows = [];
8757
10808
  let prevOverlayPartialRows = [];
10809
+ let prevOverlayVisibleSpans = new Map();
8758
10810
  const hrefIndex = new Map();
8759
10811
  let nextHrefId = 1;
8760
10812
  const MAX_HREF_IDS = 8192;
@@ -8779,6 +10831,7 @@ function createStdoutRenderer(terminal, options) {
8779
10831
  fpPrevValid = false;
8780
10832
  prevOverlayBlockedRows = [];
8781
10833
  prevOverlayPartialRows = [];
10834
+ prevOverlayVisibleSpans = new Map();
8782
10835
  }
8783
10836
  function cellFingerprintFromStyleKey(_ch, key) {
8784
10837
  return key >>> 0;
@@ -9476,12 +11529,46 @@ function createStdoutRenderer(terminal, options) {
9476
11529
  lastFrameTime = Date.now();
9477
11530
  const renderStart = performance.now();
9478
11531
  const size = terminal.size();
11532
+ const rowsOnlyResizeBaseline = preserveNextRowsOnlyResizeBaseline && fpPrevValid && fpCols === size.cols ? {
11533
+ cols: fpCols,
11534
+ rows: fpRows,
11535
+ fp: prevFP.slice(),
11536
+ hrefIds: prevHrefIds.slice(),
11537
+ textIds: prevTextIds.slice()
11538
+ } : null;
11539
+ preserveNextRowsOnlyResizeBaseline = false;
11540
+ const pendingGraphicClearsToRender = new Set(pendingGraphicClears);
11541
+ let graphicsClearsToRender = Array.from(pendingGraphicClearsToRender);
11542
+ let graphicsEntriesToRender = Array.from(pendingGraphics.entries()).sort(([, a], [, b]) => {
11543
+ const opA = a.op === "clear" ? 0 : 1;
11544
+ const opB = b.op === "clear" ? 0 : 1;
11545
+ return opA - opB || a.order - b.order;
11546
+ });
11547
+ let graphicsToRender = graphicsEntriesToRender.map(([, payload]) => payload);
11548
+ const allowGraphicsOnlyWithoutBaseline = allowNextGraphicsOnlyRenderWithoutBaseline && (dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) === 0 && !(scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length) && (graphicsClearsToRender.length > 0 || graphicsToRender.length > 0);
11549
+ allowNextGraphicsOnlyRenderWithoutBaseline = false;
11550
+ if (isDebugEnabled()) getDebugLog().render(`doRender state: size=${size.cols}x${size.rows}, dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}, scrollOps=${(scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length) ?? 0}, rowsOnlyBaseline=${rowsOnlyResizeBaseline ? "true" : "false"}, allowGraphicsOnlyWithoutBaseline=${allowGraphicsOnlyWithoutBaseline ? "true" : "false"}, activeGraphics=${activeGraphics.size}, pendingGraphics=${graphicsToRender.length}, pendingGraphicClears=${graphicsClearsToRender.length}`);
11551
+ let terminalGraphicsBlockScrollRegions = false;
9479
11552
  ensureRowEscapes(Math.max(size.rows, lastRenderedRows));
9480
11553
  ensureFingerprints(size.cols, size.rows);
9481
11554
  const bgSeq = openBg(defaultBg);
9482
11555
  const bgOnlyStyle = { bg: defaultBg };
9483
11556
  const bgKey = styleKeyFromParts({ bg: defaultBg });
9484
11557
  const blankFP = cellFingerprintFromStyleKey(" ", bgKey);
11558
+ if (rowsOnlyResizeBaseline && rowsOnlyResizeBaseline.cols === size.cols) {
11559
+ prevFP.fill(blankFP);
11560
+ prevHrefIds.fill(0);
11561
+ prevTextIds.fill(blankTextId);
11562
+ const rowsToCopy = Math.min(size.rows, rowsOnlyResizeBaseline.rows);
11563
+ for (let y = 0; y < rowsToCopy; y++) {
11564
+ const start = y * size.cols;
11565
+ const end = start + size.cols;
11566
+ prevFP.set(rowsOnlyResizeBaseline.fp.subarray(start, end), start);
11567
+ prevHrefIds.set(rowsOnlyResizeBaseline.hrefIds.subarray(start, end), start);
11568
+ prevTextIds.set(rowsOnlyResizeBaseline.textIds.subarray(start, end), start);
11569
+ }
11570
+ fpPrevValid = true;
11571
+ }
9485
11572
  let dirtySorted = true;
9486
11573
  const forceFullRender = hrefIndexResetRequiresBaseline || fingerprintInternResetRequiresBaseline;
9487
11574
  if (forceFullRender) {
@@ -9507,7 +11594,172 @@ function createStdoutRenderer(terminal, options) {
9507
11594
  if (!outOps.length) return null;
9508
11595
  return outOps;
9509
11596
  })();
11597
+ const terminalGraphicsAffectedRows = new Set();
11598
+ const terminalGraphicTextPreserveSpans = new Map();
11599
+ const addTerminalGraphicRows = (rect) => {
11600
+ if (!rect) return;
11601
+ const visible = clipGraphicRectToViewport(rect, size);
11602
+ if (!visible) return;
11603
+ for (let y = visible.y; y < visible.y + visible.h; y++) terminalGraphicsAffectedRows.add(y);
11604
+ };
11605
+ const addTerminalGraphicTextPreserveSpans = (rect) => {
11606
+ if (!rect) return;
11607
+ const visible = clipGraphicRectToViewport(rect, size);
11608
+ if (!visible) return;
11609
+ const start = Math.max(0, Math.min(size.cols, visible.x));
11610
+ const end = Math.max(start, Math.min(size.cols, visible.x + visible.w));
11611
+ if (end <= start) return;
11612
+ for (let y = visible.y; y < visible.y + visible.h; y++) {
11613
+ const spans = terminalGraphicTextPreserveSpans.get(y);
11614
+ if (spans) spans.push({
11615
+ start,
11616
+ end
11617
+ });
11618
+ else terminalGraphicTextPreserveSpans.set(y, [{
11619
+ start,
11620
+ end
11621
+ }]);
11622
+ }
11623
+ };
11624
+ const payloadRect = (payload) => ({
11625
+ x: payload.x,
11626
+ y: payload.y,
11627
+ w: payload.w ?? 1,
11628
+ h: payload.h ?? 1
11629
+ });
11630
+ const intersectsScrollOperations = (rect) => {
11631
+ if (!rect || !(normalizedScrollOperations === null || normalizedScrollOperations === void 0 ? void 0 : normalizedScrollOperations.length)) return false;
11632
+ const top = rect.y;
11633
+ const bottom = rect.y + rect.h;
11634
+ for (const op of normalizedScrollOperations) if (top < op.endY && bottom > op.startY) return true;
11635
+ return false;
11636
+ };
11637
+ const graphicRectVisible = (rect) => Boolean(rect && clipGraphicRectToViewport(rect, size));
11638
+ const pendingGraphicIds = new Set(graphicsToRender.map((payload) => payload.id));
11639
+ const explicitGraphicClears = new Set(graphicsClearsToRender);
11640
+ if ((normalizedScrollOperations === null || normalizedScrollOperations === void 0 ? void 0 : normalizedScrollOperations.length) && activeGraphics.size > 0) for (const [id, active] of activeGraphics) {
11641
+ if (explicitGraphicClears.has(id) || pendingGraphicIds.has(id)) continue;
11642
+ if (!intersectsScrollOperations(active)) continue;
11643
+ const signature = activeGraphicSignatures.get(id);
11644
+ graphicsToRender.push({
11645
+ id,
11646
+ x: active.x,
11647
+ y: active.y,
11648
+ w: active.w,
11649
+ h: active.h,
11650
+ protocol: active.protocol,
11651
+ sequence: active.sequence,
11652
+ resizeSequence: active.resizeSequence,
11653
+ clearSequence: active.clearSequence,
11654
+ allowTextOverlay: active.allowTextOverlay,
11655
+ op: "draw",
11656
+ order: nextGraphicsOrder++
11657
+ });
11658
+ if (signature) pendingGraphicSignatures.set(id, signature);
11659
+ pendingGraphicIds.add(id);
11660
+ }
11661
+ for (const [id, active] of activeGraphics) {
11662
+ if (explicitGraphicClears.has(id)) continue;
11663
+ if (pendingGraphicIds.has(id)) continue;
11664
+ if (intersectsScrollOperations(active)) continue;
11665
+ if (!active.allowTextOverlay) addTerminalGraphicTextPreserveSpans(active);
11666
+ }
11667
+ if ((normalizedScrollOperations === null || normalizedScrollOperations === void 0 ? void 0 : normalizedScrollOperations.length) && activeGraphics.size > 0) for (const active of activeGraphics.values()) {
11668
+ if (!intersectsScrollOperations(active)) continue;
11669
+ terminalGraphicsBlockScrollRegions = true;
11670
+ addTerminalGraphicRows(active);
11671
+ }
11672
+ for (const id of graphicsClearsToRender) {
11673
+ const active = activeGraphics.get(id);
11674
+ addTerminalGraphicRows(active);
11675
+ if (intersectsScrollOperations(active)) terminalGraphicsBlockScrollRegions = true;
11676
+ }
11677
+ for (const payload of graphicsToRender) {
11678
+ const active = activeGraphics.get(payload.id);
11679
+ if (payload.op === "clear") {
11680
+ const rect = active ?? payloadRect(payload);
11681
+ addTerminalGraphicRows(rect);
11682
+ if (intersectsScrollOperations(rect)) terminalGraphicsBlockScrollRegions = true;
11683
+ continue;
11684
+ }
11685
+ const next = payloadRect(payload);
11686
+ if (!payload.allowTextOverlay) addTerminalGraphicTextPreserveSpans(next);
11687
+ addTerminalGraphicRows(active);
11688
+ addTerminalGraphicRows(next);
11689
+ if (intersectsScrollOperations(active) || intersectsScrollOperations(next)) terminalGraphicsBlockScrollRegions = true;
11690
+ }
11691
+ if (!terminalGraphicsBlockScrollRegions && (Array.from(activeGraphics.values()).some(graphicRectVisible) || graphicsClearsToRender.some((id) => graphicRectVisible(activeGraphics.get(id))) || graphicsToRender.some((payload) => payload.op === "clear" ? graphicRectVisible(activeGraphics.get(payload.id) ?? payloadRect(payload)) : graphicRectVisible(anchoredGraphicRectInViewport({
11692
+ x: payload.x,
11693
+ y: payload.y,
11694
+ w: payload.w ?? 1,
11695
+ h: payload.h ?? 1
11696
+ }, size))))) terminalGraphicsBlockScrollRegions = true;
11697
+ let forceDirtyRowsRepaintForFrame = forceDirtyRowsRepaint;
11698
+ const forceTerminalGraphicsRowRepaint = (y) => forceDirtyRowsRepaintForFrame || terminalGraphicsAffectedRows.has(y) || terminalGraphicTextPreserveSpans.has(y);
11699
+ for (const [y, spans] of terminalGraphicTextPreserveSpans) {
11700
+ spans.sort((a, b) => a.start - b.start || a.end - b.end);
11701
+ const merged = [];
11702
+ for (const span of spans) {
11703
+ const previous = merged[merged.length - 1];
11704
+ if (previous && span.start <= previous.end) previous.end = Math.max(previous.end, span.end);
11705
+ else merged.push({ ...span });
11706
+ }
11707
+ terminalGraphicTextPreserveSpans.set(y, merged);
11708
+ }
11709
+ const mergeSpans = (spans) => {
11710
+ if (!spans.length) return [];
11711
+ const sorted = [...spans].sort((a, b) => a.start - b.start || a.end - b.end);
11712
+ const merged = [];
11713
+ for (const span of sorted) {
11714
+ const start = Math.max(0, Math.min(size.cols, span.start));
11715
+ const end = Math.max(start, Math.min(size.cols, span.end));
11716
+ if (end <= start) continue;
11717
+ const previous = merged[merged.length - 1];
11718
+ if (previous && start <= previous.end) previous.end = Math.max(previous.end, end);
11719
+ else merged.push({
11720
+ start,
11721
+ end
11722
+ });
11723
+ }
11724
+ return merged;
11725
+ };
11726
+ const overlayVisibleSpansForRows = (rows) => {
11727
+ const overlay = getPlaneTerminal(terminal, "overlay");
11728
+ const result = new Map();
11729
+ for (const y of rows) {
11730
+ if (y < 0 || y >= size.rows) continue;
11731
+ const row = overlay.getRow(y);
11732
+ const spans = [];
11733
+ let start = null;
11734
+ let end = 0;
11735
+ for (let x = 0; x < size.cols; x++) {
11736
+ const cell = row[x];
11737
+ const visible = cell != null && !cell.continuation && (cell.ch !== " " || styleKey(cell.style) !== bgKey || renderedHref(cell.style.href) != null);
11738
+ if (visible) {
11739
+ if (start == null) start = x;
11740
+ end = Math.max(end, Math.min(size.cols, x + Math.max(1, cell.width ?? 1)));
11741
+ continue;
11742
+ }
11743
+ if (start != null && x >= end) {
11744
+ spans.push({
11745
+ start,
11746
+ end
11747
+ });
11748
+ start = null;
11749
+ }
11750
+ }
11751
+ if (start != null) spans.push({
11752
+ start,
11753
+ end: Math.max(end, start + 1)
11754
+ });
11755
+ if (spans.length) result.set(y, mergeSpans(spans));
11756
+ }
11757
+ return result;
11758
+ };
11759
+ const overlayCoverage = overlayPlaneCoverageRows(size.rows);
11760
+ const overlayVisibleSpans = overlayVisibleSpansForRows(overlayCoverage.blockedRows);
9510
11761
  let rowsToRender = (() => {
11762
+ if (allowGraphicsOnlyWithoutBaseline) return [];
9511
11763
  if (forceFullRender || !fpPrevValid) return null;
9512
11764
  if (!dirtyRows) return null;
9513
11765
  if (dirtyRows.length === 0) return [];
@@ -9529,7 +11781,7 @@ function createStdoutRenderer(terminal, options) {
9529
11781
  if (!sorted) outRows.sort((a, b) => a - b);
9530
11782
  return outRows;
9531
11783
  })();
9532
- if ((rowsToRender === null || rowsToRender === void 0 ? void 0 : rowsToRender.length) === 0 && !normalizedScrollOperations && !getImeAnchor) {
11784
+ if ((rowsToRender === null || rowsToRender === void 0 ? void 0 : rowsToRender.length) === 0 && !normalizedScrollOperations && !getImeAnchor && graphicsClearsToRender.length === 0 && graphicsToRender.length === 0) {
9533
11785
  if (isDebugEnabled()) getDebugLog().render(" No valid dirty rows; frame skipped");
9534
11786
  cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutNoOutput();
9535
11787
  return;
@@ -9548,10 +11800,13 @@ function createStdoutRenderer(terminal, options) {
9548
11800
  currentHrefIds.set(prevHrefIds);
9549
11801
  currentTextIds.set(prevTextIds);
9550
11802
  }
11803
+ const frameHasTerminalGraphics = activeGraphics.size > 0 || graphicsClearsToRender.length > 0 || graphicsToRender.length > 0;
11804
+ const useFrameSyncOutput = useSyncOutput || isGhostty && frameHasTerminalGraphics;
9551
11805
  const frameParts = [];
9552
- frameParts.push(!isGhostty && useSyncOutput ? SYNC_START : "");
9553
- frameParts.push("\x1B[?7l");
11806
+ frameParts.push(useFrameSyncOutput ? SYNC_START : "");
11807
+ frameParts.push(allowGraphicsOnlyWithoutBaseline || keepLineWrapDisabled ? "" : "\x1B[?7l");
9554
11808
  frameParts.push(SGR_RESET, bgSeq);
11809
+ if (hideCursor && !getImeAnchor) frameParts.push(CURSOR_HOME);
9555
11810
  let lastRenderedY = -1;
9556
11811
  let lastRenderWasFullRow = false;
9557
11812
  let hasFrameOutput = false;
@@ -9566,6 +11821,7 @@ function createStdoutRenderer(terminal, options) {
9566
11821
  inverse: false,
9567
11822
  href: null
9568
11823
  };
11824
+ let overlayTouchedRowSet = null;
9569
11825
  const closeActiveHrefBeforeCursorMove = () => {
9570
11826
  if (!enableOsc8Links || !activeStyle.href) return;
9571
11827
  frameParts.push(OSC8_CLOSE);
@@ -9668,6 +11924,65 @@ function createStdoutRenderer(terminal, options) {
9668
11924
  lastRenderedY = y;
9669
11925
  lastRenderWasFullRow = spanStart === 0 && (spanEnd >= size.cols || shouldClearToEol);
9670
11926
  };
11927
+ const overlayBypassSpansForRow = (y) => {
11928
+ const current = overlayVisibleSpans.get(y) ?? [];
11929
+ const previous = prevOverlayVisibleSpans.get(y) ?? [];
11930
+ return current.length || previous.length ? mergeSpans([...current, ...previous]) : [];
11931
+ };
11932
+ const subtractSpans = (spans, bypass) => {
11933
+ if (!bypass.length) return [...spans];
11934
+ const result = [];
11935
+ for (const span of spans) {
11936
+ let cursor = span.start;
11937
+ for (const cut of bypass) {
11938
+ if (cut.end <= cursor) continue;
11939
+ if (cut.start >= span.end) break;
11940
+ if (cursor < cut.start) result.push({
11941
+ start: cursor,
11942
+ end: Math.min(cut.start, span.end)
11943
+ });
11944
+ cursor = Math.max(cursor, cut.end);
11945
+ if (cursor >= span.end) break;
11946
+ }
11947
+ if (cursor < span.end) result.push({
11948
+ start: cursor,
11949
+ end: span.end
11950
+ });
11951
+ }
11952
+ return result;
11953
+ };
11954
+ const preserveFingerprintSpans = (y, spans) => {
11955
+ if (!fpPrevValid || y < 0 || y >= fpRows || currentFP.length !== prevFP.length) return;
11956
+ const base = y * fpCols;
11957
+ for (const span of spans) {
11958
+ const start = Math.max(0, Math.min(fpCols, span.start));
11959
+ const end = Math.max(start, Math.min(fpCols, span.end));
11960
+ for (let x = start; x < end; x++) {
11961
+ currentFP[base + x] = prevFP[base + x];
11962
+ currentHrefIds[base + x] = prevHrefIds[base + x];
11963
+ currentTextIds[base + x] = prevTextIds[base + x];
11964
+ }
11965
+ }
11966
+ };
11967
+ const renderRowPreservingTerminalGraphics = (y, row) => {
11968
+ const spans = terminalGraphicTextPreserveSpans.get(y);
11969
+ if (!(spans === null || spans === void 0 ? void 0 : spans.length)) {
11970
+ renderRow(y, row);
11971
+ return;
11972
+ }
11973
+ const effectiveSpans = subtractSpans(spans, overlayBypassSpansForRow(y));
11974
+ if (!effectiveSpans.length) {
11975
+ renderRow(y, row);
11976
+ return;
11977
+ }
11978
+ let cursor = 0;
11979
+ for (const span of effectiveSpans) {
11980
+ if (cursor < span.start) renderRow(y, row, cursor, span.start, false);
11981
+ cursor = Math.max(cursor, span.end);
11982
+ }
11983
+ if (cursor < size.cols) renderRow(y, row, cursor, size.cols, false);
11984
+ preserveFingerprintSpans(y, effectiveSpans);
11985
+ };
9671
11986
  const lastRenderableColumnInCurrentRow = (rowY, cols, blankFingerprint) => {
9672
11987
  if (!fpCols || rowY < 0 || rowY >= fpRows) return -1;
9673
11988
  const base = rowY * fpCols;
@@ -9732,16 +12047,15 @@ function createStdoutRenderer(terminal, options) {
9732
12047
  } else for (const span of spansToPaint) renderRow(rowY, row, span.startX, span.endXExclusive, false);
9733
12048
  if (clearStartX != null && !paintTailInsteadOfEscK) renderRow(rowY, row, clearStartX, clearStartX, true);
9734
12049
  };
9735
- const overlayCoverage = overlayPlaneCoverageRows(size.rows);
9736
12050
  const overlayRows = Array.from(new Set([...overlayCoverage.blockedRows, ...prevOverlayBlockedRows])).sort((a, b) => a - b);
9737
12051
  const overlayRowSet = overlayRows.length ? new Set(overlayRows) : null;
9738
12052
  const overlayPartialRows = Array.from(new Set([...overlayCoverage.partialRows, ...prevOverlayPartialRows])).sort((a, b) => a - b);
9739
12053
  const overlayPartialRowSet = overlayPartialRows.length ? new Set(overlayPartialRows) : null;
9740
- const overlayTouchedRowSet = (() => {
12054
+ overlayTouchedRowSet = (() => {
9741
12055
  if (!overlayRowSet && !overlayPartialRowSet) return null;
9742
12056
  return new Set([...overlayRowSet ? Array.from(overlayRowSet) : [], ...overlayPartialRowSet ? Array.from(overlayPartialRowSet) : []]);
9743
12057
  })();
9744
- let explicitScrollOperations = normalizedScrollOperations;
12058
+ let explicitScrollOperations = terminalGraphicsBlockScrollRegions ? null : normalizedScrollOperations;
9745
12059
  let hiddenExplicitDirtyRows = null;
9746
12060
  let allowInferredScrollRegions = true;
9747
12061
  if (explicitScrollOperations && overlayRows.length) {
@@ -9756,6 +12070,7 @@ function createStdoutRenderer(terminal, options) {
9756
12070
  for (const op of explicitScrollOperations) for (let y = op.startY; y < op.endY; y++) expandedRows$1.add(y);
9757
12071
  rowsToRender = Array.from(expandedRows$1).sort((a, b) => a - b);
9758
12072
  dirtySorted = true;
12073
+ forceDirtyRowsRepaintForFrame = true;
9759
12074
  explicitScrollOperations = null;
9760
12075
  hiddenExplicitDirtyRows = null;
9761
12076
  if (isDebugEnabled()) getDebugLog().render(preparedExplicit.blockedInterior ? " Explicit scroll ops split by interior overlay rows; falling back to region repaint" : " Explicit scroll ops could not be clipped safely; falling back to region repaint");
@@ -9770,10 +12085,18 @@ function createStdoutRenderer(terminal, options) {
9770
12085
  expandedRows.add(y);
9771
12086
  }
9772
12087
  };
12088
+ if (terminalGraphicsBlockScrollRegions && normalizedScrollOperations && rowsToRender) {
12089
+ includeRowsForPendingScrollOperations(normalizedScrollOperations);
12090
+ rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
12091
+ dirtySorted = true;
12092
+ forceDirtyRowsRepaintForFrame = true;
12093
+ if (isDebugEnabled()) getDebugLog().render(" Scroll regions disabled for frame with active/pending terminal graphics");
12094
+ }
9773
12095
  if (explicitScrollOperations && rowsToRender && (!enableScrollRegions || !fpPrevValid)) {
9774
12096
  includeRowsForPendingScrollOperations(explicitScrollOperations);
9775
12097
  rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
9776
12098
  dirtySorted = true;
12099
+ forceDirtyRowsRepaintForFrame = true;
9777
12100
  explicitScrollOperations = null;
9778
12101
  hiddenExplicitDirtyRows = null;
9779
12102
  if (isDebugEnabled()) getDebugLog().render(enableScrollRegions ? " Explicit scroll ops fell back to region repaint because previous fingerprints are unavailable" : " Explicit scroll ops fell back to region repaint because scroll regions are disabled");
@@ -9781,7 +12104,7 @@ function createStdoutRenderer(terminal, options) {
9781
12104
  const denseDirtyRows = Boolean(rowsToRender && size.rows > 1 && rowsToRender.length >= size.rows * dirtyFullThreshold);
9782
12105
  if (denseDirtyRows) rowsToRender = null;
9783
12106
  let profiledRowCount = rowsToRender ? rowsToRender.length : size.rows;
9784
- const useScrollRegions = enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && !forceDirtyRowsRepaint && allowInferredScrollRegions;
12107
+ const useScrollRegions = !terminalGraphicsBlockScrollRegions && enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && !forceDirtyRowsRepaint && allowInferredScrollRegions;
9785
12108
  let scrollHandled = false;
9786
12109
  if (enableScrollRegions && explicitScrollOperations && scrollRowsCandidate && fpPrevValid) {
9787
12110
  if (currentFP.length === prevFP.length) {
@@ -9856,9 +12179,9 @@ function createStdoutRenderer(terminal, options) {
9856
12179
  for (const y of explicitDirtyRows) {
9857
12180
  const row = terminal.getRow(y);
9858
12181
  const overlayRow = overlayTouchedRowSet === null || overlayTouchedRowSet === void 0 ? void 0 : overlayTouchedRowSet.has(y);
9859
- if (forceDirtyRowsRepaint || !shouldUseDirtySpans() || overlayRow) {
12182
+ if (forceTerminalGraphicsRowRepaint(y) || !shouldUseDirtySpans() || overlayRow) {
9860
12183
  fingerprintRow(row, y, size.cols);
9861
- renderRow(y, row);
12184
+ renderRowPreservingTerminalGraphics(y, row);
9862
12185
  paintedExplicitRows.push(y);
9863
12186
  continue;
9864
12187
  }
@@ -9935,12 +12258,12 @@ function createStdoutRenderer(terminal, options) {
9935
12258
  for (let y = shift.newRowStart; y < shift.newRowEnd; y++) {
9936
12259
  const row = terminal.getRow(y);
9937
12260
  fingerprintRow(row, y, size.cols);
9938
- renderRow(y, row);
12261
+ renderRowPreservingTerminalGraphics(y, row);
9939
12262
  }
9940
12263
  const extraDirtyRows = overlayRowSet ? shift.extraDirtyRows.filter((y) => !overlayRowSet.has(y) || Boolean(overlayPartialRowSet === null || overlayPartialRowSet === void 0 ? void 0 : overlayPartialRowSet.has(y))) : shift.extraDirtyRows;
9941
12264
  for (const y of extraDirtyRows) {
9942
12265
  const row = terminal.getRow(y);
9943
- if (shouldUseDirtySpans()) {
12266
+ if (!forceTerminalGraphicsRowRepaint(y) && shouldUseDirtySpans()) {
9944
12267
  const spans = resolveChangedSpansAgainstReference(row, y, size.cols, currentFP, currentHrefIds, currentTextIds);
9945
12268
  if (!spans.length) {
9946
12269
  fingerprintRow(row, y, size.cols);
@@ -9952,7 +12275,7 @@ function createStdoutRenderer(terminal, options) {
9952
12275
  continue;
9953
12276
  }
9954
12277
  fingerprintRow(row, y, size.cols);
9955
- renderRow(y, row);
12278
+ renderRowPreservingTerminalGraphics(y, row);
9956
12279
  }
9957
12280
  for (let y = shift.newRowStart; y < shift.newRowEnd; y++) fingerprintRow(terminal.getRow(y), y, size.cols);
9958
12281
  const renderedRows = new Set();
@@ -9972,7 +12295,7 @@ function createStdoutRenderer(terminal, options) {
9972
12295
  for (let y = 0; y < size.rows; y++) {
9973
12296
  const row = terminal.getRow(y);
9974
12297
  fingerprintRow(row, y, size.cols);
9975
- renderRow(y, row);
12298
+ renderRowPreservingTerminalGraphics(y, row);
9976
12299
  }
9977
12300
  } else {
9978
12301
  const overlayDirtyRows = overlayTouchedRowSet ? rowsToRender.filter((y) => overlayTouchedRowSet.has(y)) : [];
@@ -9980,8 +12303,8 @@ function createStdoutRenderer(terminal, options) {
9980
12303
  for (const y of rowsToRender) {
9981
12304
  const row = terminal.getRow(y);
9982
12305
  fingerprintRow(row, y, size.cols);
9983
- if (forceDirtyRowsRepaint || !shouldUseDirtySpans()) {
9984
- renderRow(y, row);
12306
+ if (forceTerminalGraphicsRowRepaint(y) || !shouldUseDirtySpans()) {
12307
+ renderRowPreservingTerminalGraphics(y, row);
9985
12308
  continue;
9986
12309
  }
9987
12310
  const spans = resolveChangedSpans(row, y, size.cols, (dirtyRowSnapshots === null || dirtyRowSnapshots === void 0 ? void 0 : dirtyRowSnapshots.get(y)) ?? null);
@@ -9990,7 +12313,7 @@ function createStdoutRenderer(terminal, options) {
9990
12313
  renderDirtySpans(y, row, spans, rewriteTail);
9991
12314
  }
9992
12315
  }
9993
- if (!rowsToRender && lastRenderedRows > size.rows) {
12316
+ if (!rowsToRender && lastRenderedRows > size.rows && !rowsOnlyResizeBaseline) {
9994
12317
  const extraRows = lastRenderedRows - size.rows;
9995
12318
  if (activeStyleKey !== bgKey) {
9996
12319
  frameParts.push(SGR_RESET, bgSeq);
@@ -10001,7 +12324,8 @@ function createStdoutRenderer(terminal, options) {
10001
12324
  frameParts.push(rowClearToEol[size.rows + i]);
10002
12325
  }
10003
12326
  }
10004
- const nextLastRenderedRows = !rowsToRender ? size.rows : lastRenderedRows;
12327
+ const nextLastRenderedRows = !rowsToRender || rowsToRender.length === 0 ? size.rows : lastRenderedRows;
12328
+ const graphicsOnlyFrame = Boolean(rowsToRender && rowsToRender.length === 0) && !scrollHandled && (graphicsClearsToRender.length > 0 || graphicsToRender.length > 0);
10005
12329
  const commitRenderBaseline = () => {
10006
12330
  lastRenderedRows = nextLastRenderedRows;
10007
12331
  const tmpFP = prevFP;
@@ -10013,10 +12337,243 @@ function createStdoutRenderer(terminal, options) {
10013
12337
  const tmpTextIds = prevTextIds;
10014
12338
  prevTextIds = currentTextIds;
10015
12339
  currentTextIds = tmpTextIds;
10016
- fpPrevValid = !hrefIndexResetRequiresBaseline && !fingerprintInternResetRequiresBaseline;
12340
+ fpPrevValid = graphicsOnlyFrame ? fpPrevValid : !hrefIndexResetRequiresBaseline && !fingerprintInternResetRequiresBaseline;
10017
12341
  prevOverlayBlockedRows = overlayCoverage.blockedRows;
10018
12342
  prevOverlayPartialRows = overlayCoverage.partialRows;
12343
+ prevOverlayVisibleSpans = overlayVisibleSpans;
10019
12344
  };
12345
+ if (graphicsOnlyFrame && fpPrevValid && currentFP.length === prevFP.length) {
12346
+ currentFP.set(prevFP);
12347
+ currentHrefIds.set(prevHrefIds);
12348
+ currentTextIds.set(prevTextIds);
12349
+ }
12350
+ let renderedRowsLookup;
12351
+ const rowWasPaintedByTextRenderer = (y) => {
12352
+ if (scrollHandled) return true;
12353
+ if (!rowsToRender) return true;
12354
+ renderedRowsLookup ??= new Set(rowsToRender);
12355
+ return renderedRowsLookup.has(y);
12356
+ };
12357
+ const clearGraphicRectForRowsNotPainted = (rect) => {
12358
+ if (scrollHandled) return "";
12359
+ const erase = `\u001B[${Math.max(0, rect.w)}X`;
12360
+ const clearStyle = bgSeq;
12361
+ let out$1 = "";
12362
+ for (let row = 0; row < rect.h; row++) {
12363
+ const y = rect.y + row;
12364
+ const spans = terminalGraphicTextPreserveSpans.get(y);
12365
+ const preservedGraphicSpan = !(overlayTouchedRowSet === null || overlayTouchedRowSet === void 0 ? void 0 : overlayTouchedRowSet.has(y)) && Boolean(spans === null || spans === void 0 ? void 0 : spans.some((span) => span.start < rect.x + rect.w && span.end > rect.x));
12366
+ if ((!rowsToRender || rowWasPaintedByTextRenderer(y)) && !preservedGraphicSpan) continue;
12367
+ out$1 += `\u001B[${y + 1};${rect.x + 1}H${clearStyle}${erase}${SGR_RESET}`;
12368
+ }
12369
+ return out$1;
12370
+ };
12371
+ let commitTerminalGraphicsState = null;
12372
+ let restoreTerminalGraphicsAfterWriteFailure = null;
12373
+ let terminalGraphicsMetrics = null;
12374
+ const mergeGraphicsQueuedDuringPaint = () => {
12375
+ const seenEntries = new Set(graphicsEntriesToRender.map(([key]) => key));
12376
+ let changed = false;
12377
+ for (const entry of pendingGraphics.entries()) {
12378
+ if (seenEntries.has(entry[0])) continue;
12379
+ graphicsEntriesToRender.push(entry);
12380
+ changed = true;
12381
+ }
12382
+ for (const id of pendingGraphicClears) {
12383
+ if (pendingGraphicClearsToRender.has(id)) continue;
12384
+ pendingGraphicClearsToRender.add(id);
12385
+ graphicsClearsToRender.push(id);
12386
+ }
12387
+ if (!changed) return;
12388
+ graphicsEntriesToRender = graphicsEntriesToRender.sort(([, a], [, b]) => {
12389
+ const opA = a.op === "clear" ? 0 : 1;
12390
+ const opB = b.op === "clear" ? 0 : 1;
12391
+ return opA - opB || a.order - b.order;
12392
+ });
12393
+ graphicsToRender = graphicsEntriesToRender.map(([, payload]) => payload);
12394
+ };
12395
+ mergeGraphicsQueuedDuringPaint();
12396
+ if (graphicsClearsToRender.length > 0 || graphicsToRender.length > 0) {
12397
+ const nextActiveGraphics = new Map(activeGraphics);
12398
+ const nextActiveGraphicSignatures = new Map(activeGraphicSignatures);
12399
+ const nextPendingGraphicSignatures = new Map(pendingGraphicSignatures);
12400
+ let terminalGraphicsDraws = 0;
12401
+ let terminalGraphicsClears = 0;
12402
+ let terminalGraphicsBytes = 0;
12403
+ const countGraphicsBytes = (value) => {
12404
+ terminalGraphicsBytes += Buffer.byteLength(value, "utf8");
12405
+ return value;
12406
+ };
12407
+ const appendTerminalGraphicSequenceAt = (sequence, rect, fallbackCell, skipWithoutRect = false) => {
12408
+ if (!rect && skipWithoutRect) return false;
12409
+ const cell = rect ? {
12410
+ x: rect.x,
12411
+ y: rect.y
12412
+ } : clampCellToViewport(fallbackCell, size);
12413
+ frameParts.push(countGraphicsBytes(`\u001B[${cell.y + 1};${cell.x + 1}H`), countGraphicsBytes(maybeWrapTerminalGraphic(sequence)), countGraphicsBytes(SGR_RESET));
12414
+ return true;
12415
+ };
12416
+ const appendActiveGraphicProtocolClear = (active, rect) => {
12417
+ if (!active.clearSequence) return false;
12418
+ return appendTerminalGraphicSequenceAt(active.clearSequence, rect, {
12419
+ cellX: active.x,
12420
+ cellY: active.y
12421
+ }, clearSequenceNeedsVisibleCell(active.clearSequence, active.protocol));
12422
+ };
12423
+ hasFrameOutput = true;
12424
+ if (enableOsc8Links && activeStyle.href) frameParts.push(OSC8_CLOSE);
12425
+ if (allowGraphicsOnlyWithoutBaseline) frameParts.push(CURSOR_SAVE);
12426
+ activeStyle = {
12427
+ fg: null,
12428
+ bg: defaultBg,
12429
+ bold: false,
12430
+ dim: false,
12431
+ italic: false,
12432
+ underline: false,
12433
+ inverse: false,
12434
+ href: null
12435
+ };
12436
+ activeStyleKey = null;
12437
+ frameParts.push(countGraphicsBytes(SGR_RESET));
12438
+ for (const id of graphicsClearsToRender) {
12439
+ const active = nextActiveGraphics.get(id);
12440
+ if (!active) continue;
12441
+ const visibleRect = clipGraphicRectToViewport(active, size);
12442
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic render active clear: id=${id}, x=${active.x}, y=${active.y}, w=${active.w}, h=${active.h}, visible=${visibleRect ? `${visibleRect.x},${visibleRect.y},${visibleRect.w},${visibleRect.h}` : "none"}`);
12443
+ let wroteClear = false;
12444
+ if (visibleRect) {
12445
+ const clearRect$1 = clearGraphicRectForRowsNotPainted(visibleRect);
12446
+ if (clearRect$1) {
12447
+ frameParts.push(countGraphicsBytes(clearRect$1));
12448
+ wroteClear = true;
12449
+ }
12450
+ }
12451
+ wroteClear = appendActiveGraphicProtocolClear(active, visibleRect) || wroteClear;
12452
+ if (wroteClear) terminalGraphicsClears++;
12453
+ retainedGraphics.delete(id);
12454
+ terminalResizeDirtyGraphics.delete(id);
12455
+ nextActiveGraphics.delete(id);
12456
+ nextActiveGraphicSignatures.delete(id);
12457
+ nextPendingGraphicSignatures.delete(id);
12458
+ }
12459
+ for (const payload of graphicsToRender) {
12460
+ if (payload.op === "clear") {
12461
+ const active = nextActiveGraphics.get(payload.id);
12462
+ const rect$1 = clipGraphicRectToViewport({
12463
+ x: (active === null || active === void 0 ? void 0 : active.x) ?? payload.x,
12464
+ y: (active === null || active === void 0 ? void 0 : active.y) ?? payload.y,
12465
+ w: (active === null || active === void 0 ? void 0 : active.w) ?? payload.w ?? 1,
12466
+ h: (active === null || active === void 0 ? void 0 : active.h) ?? payload.h ?? 1
12467
+ }, size);
12468
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic render payload clear: id=${payload.id}, x=${(active === null || active === void 0 ? void 0 : active.x) ?? payload.x}, y=${(active === null || active === void 0 ? void 0 : active.y) ?? payload.y}, w=${(active === null || active === void 0 ? void 0 : active.w) ?? payload.w ?? 1}, h=${(active === null || active === void 0 ? void 0 : active.h) ?? payload.h ?? 1}, visible=${rect$1 ? `${rect$1.x},${rect$1.y},${rect$1.w},${rect$1.h}` : "none"}`);
12469
+ const clearRect$2 = rect$1 ? clearGraphicRectForRowsNotPainted(rect$1) : "";
12470
+ let wroteClear = false;
12471
+ if (clearRect$2) {
12472
+ frameParts.push(countGraphicsBytes(clearRect$2));
12473
+ wroteClear = true;
12474
+ }
12475
+ wroteClear = appendTerminalGraphicSequenceAt(payload.sequence, rect$1, {
12476
+ cellX: (active === null || active === void 0 ? void 0 : active.x) ?? payload.x,
12477
+ cellY: (active === null || active === void 0 ? void 0 : active.y) ?? payload.y
12478
+ }, clearSequenceNeedsVisibleCell(payload.sequence, payload.protocol)) || wroteClear;
12479
+ if (wroteClear) terminalGraphicsClears++;
12480
+ if (active && payload.retainOnClear) retainTerminalGraphic(payload.id, active, nextActiveGraphicSignatures.get(payload.id));
12481
+ else retainedGraphics.delete(payload.id);
12482
+ terminalResizeDirtyGraphics.delete(payload.id);
12483
+ nextActiveGraphics.delete(payload.id);
12484
+ nextActiveGraphicSignatures.delete(payload.id);
12485
+ nextPendingGraphicSignatures.delete(payload.id);
12486
+ continue;
12487
+ }
12488
+ const previous = nextActiveGraphics.get(payload.id);
12489
+ const rect = anchoredGraphicRectInViewport({
12490
+ x: payload.x,
12491
+ y: payload.y,
12492
+ w: payload.w ?? 1,
12493
+ h: payload.h ?? 1
12494
+ }, size);
12495
+ if (!rect) {
12496
+ if (previous) {
12497
+ const visiblePrevious = clipGraphicRectToViewport(previous, size);
12498
+ let wroteClear = false;
12499
+ if (visiblePrevious) {
12500
+ const clearPrevious = clearGraphicRectForRowsNotPainted(visiblePrevious);
12501
+ if (clearPrevious) {
12502
+ frameParts.push(countGraphicsBytes(clearPrevious));
12503
+ wroteClear = true;
12504
+ }
12505
+ }
12506
+ wroteClear = appendActiveGraphicProtocolClear(previous, visiblePrevious) || wroteClear;
12507
+ if (wroteClear) terminalGraphicsClears++;
12508
+ retainTerminalGraphic(payload.id, previous, nextActiveGraphicSignatures.get(payload.id), { autoRedrawOnResize: true });
12509
+ nextActiveGraphics.delete(payload.id);
12510
+ nextActiveGraphicSignatures.delete(payload.id);
12511
+ }
12512
+ terminalResizeDirtyGraphics.delete(payload.id);
12513
+ nextPendingGraphicSignatures.delete(payload.id);
12514
+ continue;
12515
+ }
12516
+ const previousSignature = previous ? nextActiveGraphicSignatures.get(payload.id) : void 0;
12517
+ const nextSignature = nextPendingGraphicSignatures.get(payload.id) ?? terminalGraphicsPayloadSignature(payload);
12518
+ const canReplacePlacement = previous != null && payload.resizeRedraw && canReplaceKittyPlacementWithoutClear(previous, payload);
12519
+ const terminalResizePlacementOnly = previous != null && payload.terminalResizeRedraw && payload.resizeSequence != null && previous.sequence === payload.sequence;
12520
+ const resizeRedrawNeedsClear = previous && (payload.resizeRedraw || payload.terminalResizeRedraw) && Boolean(previous.clearSequence) && !canReplacePlacement;
12521
+ if (previous && (!canReplacePlacement && (previousSignature !== nextSignature || !sameGraphicRect(previous, rect)) || resizeRedrawNeedsClear)) {
12522
+ const visiblePrevious = clipGraphicRectToViewport(previous, size);
12523
+ let wroteClear = false;
12524
+ if (visiblePrevious) {
12525
+ const clearPrevious = clearGraphicRectForRowsNotPainted(visiblePrevious);
12526
+ if (clearPrevious) {
12527
+ frameParts.push(countGraphicsBytes(clearPrevious));
12528
+ wroteClear = true;
12529
+ }
12530
+ }
12531
+ wroteClear = appendActiveGraphicProtocolClear(previous, visiblePrevious) || wroteClear;
12532
+ if (wroteClear) terminalGraphicsClears++;
12533
+ }
12534
+ const visibleRect = clipGraphicRectToViewport(rect, size);
12535
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic render draw: id=${payload.id}, x=${rect.x}, y=${rect.y}, w=${rect.w}, h=${rect.h}, visible=${visibleRect ? `${visibleRect.x},${visibleRect.y},${visibleRect.w},${visibleRect.h}` : "none"}, resizeRedraw=${payload.resizeRedraw || payload.terminalResizeRedraw ? "true" : "false"}, previous=${previous ? `${previous.x},${previous.y},${previous.w},${previous.h}` : "none"}`);
12536
+ const clearRect$1 = visibleRect ? clearGraphicRectForRowsNotPainted(visibleRect) : "";
12537
+ const cursor = `\u001B[${rect.y + 1};${rect.x + 1}H`;
12538
+ const sequencePayload = (canReplacePlacement || terminalResizePlacementOnly) && payload.resizeSequence ? payload.resizeSequence : payload.sequence;
12539
+ const sequence = maybeWrapTerminalGraphic(sequencePayload);
12540
+ frameParts.push(countGraphicsBytes(cursor), countGraphicsBytes(sequence), countGraphicsBytes(SGR_RESET), clearRect$1 ? countGraphicsBytes(clearRect$1) : "");
12541
+ terminalGraphicsDraws++;
12542
+ nextActiveGraphics.set(payload.id, {
12543
+ ...rect,
12544
+ protocol: payload.protocol,
12545
+ sequence: previous && (canReplacePlacement || terminalResizePlacementOnly) ? previous.sequence : payload.sequence,
12546
+ resizeSequence: payload.resizeSequence ?? (previous === null || previous === void 0 ? void 0 : previous.resizeSequence),
12547
+ clearSequence: payload.clearSequence,
12548
+ allowTextOverlay: payload.allowTextOverlay ?? (previous === null || previous === void 0 ? void 0 : previous.allowTextOverlay)
12549
+ });
12550
+ nextActiveGraphicSignatures.set(payload.id, nextSignature);
12551
+ nextPendingGraphicSignatures.delete(payload.id);
12552
+ terminalResizeDirtyGraphics.delete(payload.id);
12553
+ }
12554
+ if (allowGraphicsOnlyWithoutBaseline) frameParts.push(CURSOR_RESTORE);
12555
+ restoreTerminalGraphicsAfterWriteFailure = () => {
12556
+ for (const id of graphicsClearsToRender) pendingGraphicClears.add(id);
12557
+ for (const [key, payload] of graphicsEntriesToRender) pendingGraphics.set(key, payload);
12558
+ };
12559
+ commitTerminalGraphicsState = () => {
12560
+ for (const id of pendingGraphicClearsToRender) pendingGraphicClears.delete(id);
12561
+ for (const [key, payload] of graphicsEntriesToRender) if (pendingGraphics.get(key) === payload) pendingGraphics.delete(key);
12562
+ activeGraphics.clear();
12563
+ for (const [id, active] of nextActiveGraphics) activeGraphics.set(id, active);
12564
+ activeGraphicSignatures.clear();
12565
+ for (const [id, signature] of nextActiveGraphicSignatures) activeGraphicSignatures.set(id, signature);
12566
+ pendingGraphicSignatures.clear();
12567
+ for (const [id, signature] of nextPendingGraphicSignatures) pendingGraphicSignatures.set(id, signature);
12568
+ };
12569
+ terminalGraphicsMetrics = {
12570
+ draws: terminalGraphicsDraws,
12571
+ clears: terminalGraphicsClears,
12572
+ bytes: terminalGraphicsBytes,
12573
+ active: nextActiveGraphics.size
12574
+ };
12575
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphics frame: draws=${terminalGraphicsDraws}, clears=${terminalGraphicsClears}, bytes=${terminalGraphicsBytes}, activeNext=${nextActiveGraphics.size}`);
12576
+ }
10020
12577
  let emittedCursorPos = null;
10021
12578
  if (getImeAnchor) {
10022
12579
  const anchor = getImeAnchor();
@@ -10041,8 +12598,9 @@ function createStdoutRenderer(terminal, options) {
10041
12598
  }
10042
12599
  if (enableOsc8Links && activeStyle.href) frameParts.push(OSC8_CLOSE);
10043
12600
  frameParts.push(SGR_RESET);
10044
- frameParts.push("\x1B[?7h");
10045
- frameParts.push(!isGhostty && useSyncOutput ? SYNC_END : "");
12601
+ if (hideCursor && !getImeAnchor) frameParts.push(CURSOR_HOME);
12602
+ frameParts.push(allowGraphicsOnlyWithoutBaseline || keepLineWrapDisabled ? "" : "\x1B[?7h");
12603
+ frameParts.push(useFrameSyncOutput ? SYNC_END : "");
10046
12604
  const frame = frameParts.join("");
10047
12605
  const frameBytes = Buffer.byteLength(frame, "utf8");
10048
12606
  if (profiler) profiler.recordRender({
@@ -10103,10 +12661,11 @@ function createStdoutRenderer(terminal, options) {
10103
12661
  const resetCount = countResets(frame);
10104
12662
  const buildTime = (performance.now() - renderStart).toFixed(2);
10105
12663
  getDebugLog().render(` Frame built: ${frameSize} bytes, ${cursorSeqCount} cursor sequences, ${resetCount} resets, ${buildTime}ms`);
10106
- getDebugLog().render(` Terminal: ${isGhostty ? "GHOSTTY" : "other"}, useSyncOutput: ${useSyncOutput}`);
12664
+ getDebugLog().render(` Terminal: ${isGhostty ? "GHOSTTY" : "other"}, useSyncOutput: ${useSyncOutput}, useFrameSyncOutput: ${useFrameSyncOutput}`);
10107
12665
  }
10108
12666
  const writeStart = performance.now();
10109
12667
  const resolveWriteMode = (frameSizeBytes) => {
12668
+ if (frameHasTerminalGraphics && frameSizeBytes <= syncMaxBytes) return canUseSyncStdout ? "sync" : "stream";
10110
12669
  if (isGhostty) return "chunked";
10111
12670
  const preferChunked = frameSizeBytes >= chunkThresholdBytes || writeEmaMs >= 24;
10112
12671
  if (preferChunked) return "chunked";
@@ -10136,12 +12695,15 @@ function createStdoutRenderer(terminal, options) {
10136
12695
  getDebugLog().render(` Write completed in ${writeTime}ms`);
10137
12696
  }
10138
12697
  } catch (writeError) {
12698
+ restoreTerminalGraphicsAfterWriteFailure === null || restoreTerminalGraphicsAfterWriteFailure === void 0 || restoreTerminalGraphicsAfterWriteFailure();
10139
12699
  if (isDebugEnabled()) getDebugLog().error(`Write ERROR:`, writeError);
10140
12700
  try {
10141
- out.write(`\u001B[?7h${!isGhostty && useSyncOutput ? SYNC_END : ""}`);
12701
+ out.write(`\u001B[?7h${useFrameSyncOutput ? SYNC_END : ""}`);
10142
12702
  } catch {}
10143
12703
  throw writeError;
10144
12704
  }
12705
+ commitTerminalGraphicsState === null || commitTerminalGraphicsState === void 0 || commitTerminalGraphicsState();
12706
+ if (terminalGraphicsMetrics) recordStdoutTerminalGraphics(terminalGraphicsMetrics);
10145
12707
  commitRenderBaseline();
10146
12708
  {
10147
12709
  const writeMs = performance.now() - writeStart;
@@ -10149,6 +12711,9 @@ function createStdoutRenderer(terminal, options) {
10149
12711
  if (emittedCursorPos) {
10150
12712
  lastCursorX = emittedCursorPos.x;
10151
12713
  lastCursorY = emittedCursorPos.y;
12714
+ } else if (hideCursor && !getImeAnchor) {
12715
+ lastCursorX = 0;
12716
+ lastCursorY = 0;
10152
12717
  } else {
10153
12718
  lastCursorX = null;
10154
12719
  lastCursorY = null;
@@ -10315,6 +12880,7 @@ function createStdoutRenderer(terminal, options) {
10315
12880
  */
10316
12881
  function render(dirtyRows, sync, scrollOperations) {
10317
12882
  if (disposed) return;
12883
+ if (dirtyRows == null || sync) deferGraphicsFlushUntilResizeCommit = false;
10318
12884
  if (isDebugEnabled()) getDebugLog().render(`render() called: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}, pending=${pendingRender}, elapsed=${Date.now() - lastFrameTime}ms`);
10319
12885
  if (dirtyRows == null) {
10320
12886
  accumulatedAllRows = true;
@@ -10326,7 +12892,7 @@ function createStdoutRenderer(terminal, options) {
10326
12892
  accumulatedScrollOperations = null;
10327
12893
  } else if (dirtyRows.length === 0) {
10328
12894
  if (scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length) mergeScrollOperations(scrollOperations);
10329
- else if (!accumulatedAllRows && accumulatedDirtyCount === 0 && !getImeAnchor) {
12895
+ else if (!accumulatedAllRows && accumulatedDirtyCount === 0 && !getImeAnchor && !hasPendingTerminalGraphics()) {
10330
12896
  cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutQueued(0);
10331
12897
  return;
10332
12898
  }
@@ -10345,8 +12911,8 @@ function createStdoutRenderer(terminal, options) {
10345
12911
  }
10346
12912
  }
10347
12913
  }
10348
- const now$2 = Date.now();
10349
- const elapsed = now$2 - lastFrameTime;
12914
+ const now$3 = Date.now();
12915
+ const elapsed = now$3 - lastFrameTime;
10350
12916
  const queuedDelayMs = Math.max(0, MIN_FRAME_MS - elapsed);
10351
12917
  if (pendingRender && !sync) {
10352
12918
  cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutQueued(queuedDelayMs);
@@ -10436,13 +13002,94 @@ function createStdoutRenderer(terminal, options) {
10436
13002
  } catch {}
10437
13003
  }
10438
13004
  installFingerprintFn();
13005
+ let suppressNextResizeCommitRender = false;
13006
+ function consumeResizeDirtyState() {
13007
+ suppressNextResizeCommitRender = true;
13008
+ try {
13009
+ terminal.commit({ sync: true });
13010
+ } finally {
13011
+ suppressNextResizeCommitRender = false;
13012
+ }
13013
+ }
13014
+ function clearRowsOnlyResizeRepaintTimer() {
13015
+ if (!rowsOnlyResizeRepaintTimer) return;
13016
+ clearTimeout(rowsOnlyResizeRepaintTimer);
13017
+ rowsOnlyResizeRepaintTimer = null;
13018
+ }
13019
+ function repaintRowsAfterRowsOnlyResize() {
13020
+ if (disposed) return;
13021
+ const { rows } = terminal.size();
13022
+ if (rows <= 0) return;
13023
+ if (isDebugEnabled()) getDebugLog().render(`rows-only resize repaint: rows=${rows}`);
13024
+ preserveNextRowsOnlyResizeBaseline = true;
13025
+ const dirtyRows = Array.from({ length: rows }, (_, y) => y);
13026
+ accumulatedDirtyRowsRequireRepaint = true;
13027
+ deferGraphicsFlushUntilResizeCommit = false;
13028
+ allowNextGraphicsOnlyRenderWithoutBaseline = false;
13029
+ render(dirtyRows, true);
13030
+ }
13031
+ function scheduleRowsOnlyResizeRepaint() {
13032
+ clearRowsOnlyResizeRepaintTimer();
13033
+ if (isDebugEnabled()) getDebugLog().render(`rows-only resize repaint scheduled: delay=${ROWS_ONLY_RESIZE_REPAINT_DELAY_MS}ms`);
13034
+ rowsOnlyResizeRepaintTimer = setTimeout(() => {
13035
+ rowsOnlyResizeRepaintTimer = null;
13036
+ repaintRowsAfterRowsOnlyResize();
13037
+ }, ROWS_ONLY_RESIZE_REPAINT_DELAY_MS);
13038
+ }
10439
13039
  if (altScreen && outputIsTTY) out.write("\x1B[?1049h");
13040
+ if (keepLineWrapDisabled) out.write("\x1B[?7l");
10440
13041
  if (hideCursor) out.write("\x1B[?25l");
10441
13042
  if (clear) out.write(`${SGR_RESET}${openBg(defaultBg)}\u001B[2J\u001B[H${SGR_RESET}`);
10442
13043
  const off = terminal.on("commit", ({ dirtyRows, scrollOperations, sync }) => {
13044
+ if (suppressNextResizeCommitRender) return;
13045
+ const deferRowsOnlyResizeCommit = rowsOnlyResizeRepaintTimer && (dirtyRows == null || dirtyRows.length >= terminal.size().rows);
13046
+ if (deferRowsOnlyResizeCommit) {
13047
+ if (isDebugEnabled()) getDebugLog().render(`rows-only resize commit deferred: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}${sync ? " (sync)" : ""}`);
13048
+ scheduleRowsOnlyResizeRepaint();
13049
+ return;
13050
+ }
10443
13051
  if (isDebugEnabled()) getDebugLog().render(`Commit event: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}, rows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.join(",")) ?? "all"}${(scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length) ? `, scrollOps=${scrollOperations.map((op) => `${op.startY}-${op.endY - 1}:${op.delta}`).join("|")}` : ""}${sync ? " (sync)" : ""}`);
10444
13052
  render(dirtyRows, sync, scrollOperations);
10445
13053
  });
13054
+ let terminalResizeCols = terminal.size().cols;
13055
+ let terminalResizeRows = terminal.size().rows;
13056
+ const offTerminalResize = terminal.on("resize", ({ cols, rows }) => {
13057
+ const colsChanged = cols !== terminalResizeCols;
13058
+ const rowsChanged = rows !== terminalResizeRows;
13059
+ const rowsIncreased = rows > terminalResizeRows;
13060
+ terminalResizeCols = cols;
13061
+ terminalResizeRows = rows;
13062
+ if (!colsChanged && !rowsChanged) return;
13063
+ if (pendingRender) {
13064
+ pendingRender = false;
13065
+ if (renderTimer) {
13066
+ clearTimeout(renderTimer);
13067
+ renderTimer = null;
13068
+ }
13069
+ if (isDebugEnabled()) getDebugLog().render("pending stdout render canceled until resize commit");
13070
+ }
13071
+ queueTerminalGraphicsForResizeRedraw({
13072
+ cols,
13073
+ rows
13074
+ });
13075
+ if (!colsChanged) {
13076
+ consumeResizeDirtyState();
13077
+ preserveNextRowsOnlyResizeBaseline = true;
13078
+ if (rowsIncreased) {
13079
+ clearRowsOnlyResizeRepaintTimer();
13080
+ repaintRowsAfterRowsOnlyResize();
13081
+ return;
13082
+ }
13083
+ scheduleRowsOnlyResizeRepaint();
13084
+ if (hasPendingTerminalGraphics()) {
13085
+ deferGraphicsFlushUntilResizeCommit = false;
13086
+ allowNextGraphicsOnlyRenderWithoutBaseline = true;
13087
+ render([], true);
13088
+ }
13089
+ return;
13090
+ }
13091
+ clearRowsOnlyResizeRepaintTimer();
13092
+ });
10446
13093
  const resizeSource = (options === null || options === void 0 ? void 0 : options.output) ?? process.stdout;
10447
13094
  const canTrackResize = Boolean(trackResize && outputIsTTY && typeof (resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.on) === "function");
10448
13095
  const onResize = () => {
@@ -10450,9 +13097,13 @@ function createStdoutRenderer(terminal, options) {
10450
13097
  const rows = Number(resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.rows);
10451
13098
  if (!Number.isFinite(cols) || !Number.isFinite(rows)) return;
10452
13099
  const size = terminal.size();
13100
+ if (isDebugEnabled()) getDebugLog().render(`stdout resize event: output=${cols}x${rows}, terminal=${size.cols}x${size.rows}, trackResize=${trackResize ? "true" : "false"}`);
10453
13101
  if (cols === size.cols && rows === size.rows) return;
13102
+ const colsChanged = cols !== size.cols;
13103
+ const rowsChanged = rows !== size.rows;
10454
13104
  terminal.resize(cols, rows);
10455
- render();
13105
+ consumeResizeDirtyState();
13106
+ if (isDebugEnabled()) getDebugLog().render(`stdout resize consumed: target=${cols}x${rows}, colsChanged=${colsChanged ? "true" : "false"}, rowsChanged=${rowsChanged ? "true" : "false"}`);
10456
13107
  };
10457
13108
  if (canTrackResize) try {
10458
13109
  resizeSource.on("resize", onResize);
@@ -10482,17 +13133,73 @@ function createStdoutRenderer(terminal, options) {
10482
13133
  function dispose() {
10483
13134
  if (disposed) return;
10484
13135
  disposed = true;
13136
+ let disposeError = null;
10485
13137
  if (renderTimer) {
10486
13138
  clearTimeout(renderTimer);
10487
13139
  renderTimer = null;
10488
13140
  }
13141
+ if (terminalGraphicsFlushTimer) {
13142
+ clearTimeout(terminalGraphicsFlushTimer);
13143
+ terminalGraphicsFlushTimer = null;
13144
+ }
13145
+ clearRowsOnlyResizeRepaintTimer();
10489
13146
  accumulatedAllRows = false;
10490
13147
  accumulatedDirtyBits = null;
10491
13148
  accumulatedDirtyCount = 0;
10492
13149
  accumulatedDirtyMin = Number.POSITIVE_INFINITY;
10493
13150
  accumulatedDirtyMax = -1;
10494
13151
  accumulatedDirtyRowsRequireRepaint = false;
13152
+ pendingGraphics.clear();
13153
+ pendingGraphicClears.clear();
13154
+ pendingGraphicSignatures.clear();
13155
+ retainedGraphics.clear();
13156
+ knownKittyImageIds.clear();
13157
+ terminalResizeDirtyGraphics.clear();
13158
+ try {
13159
+ if (activeGraphics.size > 0) {
13160
+ const size = terminal.size();
13161
+ const clearParts = [];
13162
+ let terminalGraphicsClears = 0;
13163
+ for (const active of activeGraphics.values()) {
13164
+ const visibleRect = clipGraphicRectToViewport(active, size);
13165
+ let wroteClear = false;
13166
+ if (visibleRect) {
13167
+ clearParts.push(clearGraphicRect(visibleRect));
13168
+ wroteClear = true;
13169
+ }
13170
+ if (active.clearSequence && (visibleRect || !clearSequenceNeedsVisibleCell(active.clearSequence, active.protocol))) {
13171
+ const cell = visibleRect ? {
13172
+ x: visibleRect.x,
13173
+ y: visibleRect.y
13174
+ } : clampCellToViewport({
13175
+ cellX: active.x,
13176
+ cellY: active.y
13177
+ }, size);
13178
+ clearParts.push(`\u001B[${cell.y + 1};${cell.x + 1}H`, maybeWrapTerminalGraphic(active.clearSequence));
13179
+ wroteClear = true;
13180
+ }
13181
+ if (wroteClear) terminalGraphicsClears++;
13182
+ }
13183
+ activeGraphics.clear();
13184
+ activeGraphicSignatures.clear();
13185
+ if (clearParts.length > 0) {
13186
+ const frame = `${SGR_RESET}${clearParts.join("")}${SGR_RESET}`;
13187
+ out.write(frame);
13188
+ recordStdoutTerminalGraphics({
13189
+ clears: terminalGraphicsClears,
13190
+ bytes: Buffer.byteLength(frame, "utf8"),
13191
+ active: 0
13192
+ });
13193
+ } else recordStdoutTerminalGraphics({ active: 0 });
13194
+ }
13195
+ } catch {
13196
+ activeGraphics.clear();
13197
+ activeGraphicSignatures.clear();
13198
+ recordStdoutTerminalGraphics({ active: 0 });
13199
+ }
13200
+ unregisterGraphicsOutput();
10495
13201
  off();
13202
+ offTerminalResize();
10496
13203
  releaseFingerprintFn();
10497
13204
  if (canTrackResize && typeof (resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.off) === "function") try {
10498
13205
  resizeSource.off("resize", onResize);
@@ -10500,9 +13207,15 @@ function createStdoutRenderer(terminal, options) {
10500
13207
  else if (canTrackResize && typeof (resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.removeListener) === "function") try {
10501
13208
  resizeSource.removeListener("resize", onResize);
10502
13209
  } catch {}
10503
- if (hideCursor) out.write("\x1B[?25h");
10504
- if (altScreen && outputIsTTY) out.write("\x1B[?1049l");
13210
+ try {
13211
+ if (keepLineWrapDisabled) out.write("\x1B[?7h");
13212
+ if (hideCursor) out.write("\x1B[?25h");
13213
+ if (altScreen && outputIsTTY) out.write("\x1B[?1049l");
13214
+ } catch (err) {
13215
+ if (disposeError == null) disposeError = err;
13216
+ }
10505
13217
  profiler === null || profiler === void 0 || profiler.dispose();
13218
+ if (disposeError) throw disposeError;
10506
13219
  }
10507
13220
  const updateTheme = (next) => {
10508
13221
  if (disposed) return;
@@ -10517,10 +13230,12 @@ function createStdoutRenderer(terminal, options) {
10517
13230
  fpPrevValid = false;
10518
13231
  prevOverlayBlockedRows = [];
10519
13232
  prevOverlayPartialRows = [];
13233
+ prevOverlayVisibleSpans = new Map();
10520
13234
  render();
10521
13235
  };
10522
13236
  return {
10523
13237
  capabilities: STDOUT_RENDERER_CAPABILITIES,
13238
+ graphicsCapabilities,
10524
13239
  render,
10525
13240
  dispose,
10526
13241
  setCursor,
@@ -10554,4 +13269,4 @@ function createNodeMentionPathProvider() {
10554
13269
  }
10555
13270
 
10556
13271
  //#endregion
10557
- export { HEADLESS_RENDERER_CAPABILITIES, STDOUT_RENDERER_CAPABILITIES, createCliEventManager, createDefaultTInputHostAdapter, createNodeMentionPathProvider, createNodePathPickerProvider, createOsc52ClipboardProvider, createStdinDriver, createStdoutRenderer, createTInputHostPlugin, createTerminalApp, defaultTInputHostPlugin, getCliLatencyProfiler, getStdoutRendererMetrics, installNodeFileWriters, installTerminalCleanup, readEventLog, resetNodeFileWriters, resolveUserPath, sanitizeTerminalHref, suggestPaths, writeEventLog, writeSnapshot };
13272
+ export { HEADLESS_RENDERER_CAPABILITIES, STDOUT_RENDERER_CAPABILITIES, createCliEventManager, createDefaultTInputHostAdapter, createIterm2InlineImageSequence, createKittyDeleteGraphicsSequence, createKittyGraphicsSequence, createNodeMentionPathProvider, createNodePathPickerProvider, createOsc52ClipboardProvider, createStdinDriver, createStdoutRenderer, createTInputHostPlugin, createTerminalApp, createTerminalGraphicRenderQueue, defaultTInputHostPlugin, detectTerminalGraphicsCapabilities, getCliLatencyProfiler, getStdoutRendererMetrics, installNodeFileWriters, installTerminalCleanup, readEventLog, resetNodeFileWriters, resolveUserPath, sanitizeTerminalHref, suggestPaths, writeEventLog, writeSnapshot };