@simon_he/vue-tui 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +44 -26
  2. package/dist/TMermaidText-CXm_L969.js +885 -0
  3. package/dist/{TSelect-DR0FomSu.js → TSelect-CiyMvwl-.js} +60 -81
  4. package/dist/{TTree-kzhlB5P0.js → TTree-DKGiuIcM.js} +140 -103
  5. package/dist/agent/mermaid.cjs +2 -0
  6. package/dist/agent/mermaid.d.cts +2 -0
  7. package/dist/agent/mermaid.d.ts +2 -0
  8. package/dist/agent/mermaid.js +1 -0
  9. package/dist/agent.cjs +31184 -24907
  10. package/dist/agent.d.cts +11 -1
  11. package/dist/agent.d.ts +11 -1
  12. package/dist/agent.js +1700 -13
  13. package/dist/block-source-5CQp4tF8.js +3230 -0
  14. package/dist/cli/stdin-buffer.d.ts +1 -1
  15. package/dist/cli.cjs +5037 -6767
  16. package/dist/cli.d.cts +4 -0
  17. package/dist/cli.d.ts +4 -0
  18. package/dist/cli.js +2733 -148
  19. package/dist/core/buffer/eaw-ranges-unicode-17.d.ts +14 -0
  20. package/dist/core/perf/instrumentation-noop.d.ts +48 -0
  21. package/dist/core/perf/instrumentation.d.ts +136 -0
  22. package/dist/core/terminal/create-terminal.d.ts +1 -0
  23. package/dist/core.cjs +843 -1469
  24. package/dist/core.js +6 -6
  25. package/dist/{create-terminal-DBFNYr8f.js → create-terminal-DXuZ2fci.js} +39 -13
  26. package/dist/debug-logger-ChY_PXVk.js +74 -0
  27. package/dist/{dom-renderer-BniJSQyX.js → dom-renderer-DNzmg2As.js} +2 -2
  28. package/dist/experimental.cjs +4115 -5928
  29. package/dist/experimental.d.cts +2 -0
  30. package/dist/experimental.d.ts +2 -0
  31. package/dist/experimental.js +1490 -9
  32. package/dist/frame-perf-BBhE-iuS.js +111 -0
  33. package/dist/{sgr-BmzxEjAO.js → hyperlink-B35-O-cW.js} +100 -1
  34. package/dist/index.cjs +5017 -10339
  35. package/dist/index.js +16 -14
  36. package/dist/{linkify-D_gzn9Xq.js → linkify-CuuquMQT.js} +32 -3
  37. package/dist/markdown.cjs +21295 -12691
  38. package/dist/markdown.d.cts +1 -1
  39. package/dist/markdown.d.ts +1 -1
  40. package/dist/markdown.js +211 -51
  41. package/dist/mermaid-CWto52Wk.js +123 -0
  42. package/dist/mermaid.cjs +1707 -0
  43. package/dist/mermaid.d.cts +3 -0
  44. package/dist/mermaid.d.ts +3 -0
  45. package/dist/mermaid.js +6 -0
  46. package/dist/observability/frame-perf-store.d.ts +2 -1
  47. package/dist/observability/frame-perf.d.ts +33 -0
  48. package/dist/observability.cjs +106 -107
  49. package/dist/observability.d.cts +2 -2
  50. package/dist/observability.d.ts +2 -2
  51. package/dist/observability.js +3 -3
  52. package/dist/{path-suggest-BNqcuE3-.js → path-suggest-fPzRsgPv.js} +1 -1
  53. package/dist/{wheel-scroll-vBRaQ7p-.js → rect-Ng-3eKzF.js} +253 -117
  54. package/dist/renderer/cli/stdout-metrics.d.ts +10 -0
  55. package/dist/renderer/cli/stdout-renderer.d.ts +3 -0
  56. package/dist/renderer/terminal-graphic-render-queue.d.ts +50 -0
  57. package/dist/renderer/terminal-graphics-trace.d.ts +53 -0
  58. package/dist/renderer/terminal-graphics.d.ts +146 -0
  59. package/dist/renderer-dom.cjs +364 -903
  60. package/dist/renderer-dom.js +4 -4
  61. package/dist/runtime/index.d.ts +2 -0
  62. package/dist/{runtime-rfcZCyJq.js → runtime-D77C03RM.js} +28 -15
  63. package/dist/runtime.cjs +427 -889
  64. package/dist/runtime.js +2 -2
  65. package/dist/{terminal-selection-DBinO6p5.js → terminal-selection-CQYr--26.js} +4 -1
  66. package/dist/{tlog-theme-CvLFNMwk.js → tlog-theme-CopRt9SC.js} +37 -6
  67. package/dist/{trace-CjzYeIxN.js → trace-CYHDKM1m.js} +5 -0
  68. package/dist/vue/agent/create-png-terminal-graphic-renderer.d.ts +19 -0
  69. package/dist/vue/components/TAgentTerminalGraphic.d.ts +481 -0
  70. package/dist/vue/components/TCharts.d.ts +1121 -0
  71. package/dist/vue/components/TFeedback.d.ts +1 -1
  72. package/dist/vue/components/TFlex.d.ts +389 -0
  73. package/dist/vue/components/TForm.d.ts +4 -4
  74. package/dist/vue/components/TLogSearchBar.d.ts +3 -3
  75. package/dist/vue/components/TLogSearchPager.d.ts +2 -2
  76. package/dist/vue/components/TMarkdownText.d.ts +162 -4
  77. package/dist/vue/components/TMermaidText.d.ts +900 -0
  78. package/dist/vue/components/TVirtualList.d.ts +9 -0
  79. package/dist/vue/components/TVirtualMarkdown.d.ts +170 -9
  80. package/dist/vue/components/TVirtualRows.d.ts +9 -0
  81. package/dist/vue/components/TerminalProvider.d.ts +2 -0
  82. package/dist/vue/context.d.ts +24 -0
  83. package/dist/vue/index.d.ts +5 -0
  84. package/dist/vue/layout/flex.d.ts +67 -0
  85. package/dist/vue/markdown/ast.d.ts +5 -2
  86. package/dist/vue/markdown/document.d.ts +5 -0
  87. package/dist/vue/markdown/image-actions.d.ts +14 -0
  88. package/dist/vue/markdown/image.d.ts +11 -0
  89. package/dist/vue/markdown/link-actions.d.ts +14 -0
  90. package/dist/vue/markdown/math-actions.d.ts +14 -0
  91. package/dist/vue/markdown/math.d.ts +6 -0
  92. package/dist/vue/markdown/render.d.ts +28 -0
  93. package/dist/vue/markdown/types.d.ts +78 -0
  94. package/dist/vue/mermaid/beautiful-mermaid.d.ts +1436 -0
  95. package/dist/vue/render/render-manager.d.ts +7 -0
  96. package/dist/vue.cjs +7207 -12437
  97. package/dist/vue.js +706 -39
  98. package/dist/wheel-scroll-CQ22Hcyj.js +106 -0
  99. package/dist/{hyperlink-Bn02UXJb.js → width-GqllnV8C.js} +166 -111
  100. package/package.json +85 -14
  101. package/dist/block-source-CKUOnkgT.js +0 -1663
  102. package/dist/frame-perf-C6Xfg85O.js +0 -24
  103. /package/dist/{env-Cl8qmNFN.js → env-DNCzvX8b.js} +0 -0
  104. /package/dist/{path-mnSgEpCm.js → path-BI9W15Zt.js} +0 -0
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
  }
@@ -2649,7 +2676,7 @@ function segmentedGraphemes(text) {
2649
2676
 
2650
2677
  //#endregion
2651
2678
  //#region src/core/ansi/sgr.ts
2652
- const ESC$1 = "\x1B";
2679
+ const ESC$2 = "\x1B";
2653
2680
  function ansi16ToColorName(code) {
2654
2681
  switch (code) {
2655
2682
  case 30: return "black";
@@ -2922,7 +2949,7 @@ function parseAnsiSgr(input, baseStyle = {}) {
2922
2949
  let style = { ...baseStyle };
2923
2950
  let lastIndex = 0;
2924
2951
  for (let i = 0; i < input.length; i++) {
2925
- if (input[i] !== ESC$1 || input[i + 1] !== "[") continue;
2952
+ if (input[i] !== ESC$2 || input[i + 1] !== "[") continue;
2926
2953
  let j = i + 2;
2927
2954
  while (j < input.length) {
2928
2955
  const c = input.charCodeAt(j);
@@ -2951,17 +2978,131 @@ function parseAnsiSgr(input, baseStyle = {}) {
2951
2978
  }
2952
2979
 
2953
2980
  //#endregion
2954
- //#region src/core/buffer/width.ts
2981
+ //#region src/core/buffer/eaw-ranges-unicode-17.ts
2955
2982
  const fullWidthRanges = [
2956
2983
  [4352, 4447],
2984
+ [8986, 8987],
2957
2985
  [9001, 9002],
2958
- [11904, 42191],
2986
+ [9193, 9196],
2987
+ [9200, 9200],
2988
+ [9203, 9203],
2989
+ [9725, 9726],
2990
+ [9748, 9749],
2991
+ [9776, 9783],
2992
+ [9800, 9811],
2993
+ [9855, 9855],
2994
+ [9866, 9871],
2995
+ [9875, 9875],
2996
+ [9889, 9889],
2997
+ [9898, 9899],
2998
+ [9917, 9918],
2999
+ [9924, 9925],
3000
+ [9934, 9934],
3001
+ [9940, 9940],
3002
+ [9962, 9962],
3003
+ [9970, 9971],
3004
+ [9973, 9973],
3005
+ [9978, 9978],
3006
+ [9981, 9981],
3007
+ [9989, 9989],
3008
+ [9994, 9995],
3009
+ [10024, 10024],
3010
+ [10060, 10060],
3011
+ [10062, 10062],
3012
+ [10067, 10069],
3013
+ [10071, 10071],
3014
+ [10133, 10135],
3015
+ [10160, 10160],
3016
+ [10175, 10175],
3017
+ [11035, 11036],
3018
+ [11088, 11088],
3019
+ [11093, 11093],
3020
+ [11904, 11929],
3021
+ [11931, 12019],
3022
+ [12032, 12245],
3023
+ [12272, 12350],
3024
+ [12353, 12438],
3025
+ [12441, 12543],
3026
+ [12549, 12591],
3027
+ [12593, 12686],
3028
+ [12688, 12773],
3029
+ [12783, 12830],
3030
+ [12832, 12871],
3031
+ [12880, 42124],
3032
+ [42128, 42182],
3033
+ [43360, 43388],
2959
3034
  [44032, 55203],
2960
3035
  [63744, 64255],
2961
3036
  [65040, 65049],
2962
- [65072, 65135],
2963
- [65280, 65376],
2964
- [65504, 65510]
3037
+ [65072, 65106],
3038
+ [65108, 65126],
3039
+ [65128, 65131],
3040
+ [65281, 65376],
3041
+ [65504, 65510],
3042
+ [94176, 94180],
3043
+ [94192, 94198],
3044
+ [94208, 101589],
3045
+ [101631, 101662],
3046
+ [101760, 101874],
3047
+ [110576, 110579],
3048
+ [110581, 110587],
3049
+ [110589, 110590],
3050
+ [110592, 110882],
3051
+ [110898, 110898],
3052
+ [110928, 110930],
3053
+ [110933, 110933],
3054
+ [110948, 110951],
3055
+ [110960, 111355],
3056
+ [119552, 119638],
3057
+ [119648, 119670],
3058
+ [126980, 126980],
3059
+ [127183, 127183],
3060
+ [127374, 127374],
3061
+ [127377, 127386],
3062
+ [127488, 127490],
3063
+ [127504, 127547],
3064
+ [127552, 127560],
3065
+ [127568, 127569],
3066
+ [127584, 127589],
3067
+ [127744, 127776],
3068
+ [127789, 127797],
3069
+ [127799, 127868],
3070
+ [127870, 127891],
3071
+ [127904, 127946],
3072
+ [127951, 127955],
3073
+ [127968, 127984],
3074
+ [127988, 127988],
3075
+ [127992, 128062],
3076
+ [128064, 128064],
3077
+ [128066, 128252],
3078
+ [128255, 128317],
3079
+ [128331, 128334],
3080
+ [128336, 128359],
3081
+ [128378, 128378],
3082
+ [128405, 128406],
3083
+ [128420, 128420],
3084
+ [128507, 128591],
3085
+ [128640, 128709],
3086
+ [128716, 128716],
3087
+ [128720, 128722],
3088
+ [128725, 128728],
3089
+ [128732, 128735],
3090
+ [128747, 128748],
3091
+ [128756, 128764],
3092
+ [128992, 129003],
3093
+ [129008, 129008],
3094
+ [129292, 129338],
3095
+ [129340, 129349],
3096
+ [129351, 129535],
3097
+ [129648, 129660],
3098
+ [129664, 129674],
3099
+ [129678, 129734],
3100
+ [129736, 129736],
3101
+ [129741, 129756],
3102
+ [129759, 129770],
3103
+ [129775, 129784],
3104
+ [131072, 196605],
3105
+ [196608, 262141]
2965
3106
  ];
2966
3107
  const ambiguousWidthRanges = [
2967
3108
  [161, 161],
@@ -3017,6 +3158,7 @@ const ambiguousWidthRanges = [
3017
3158
  [728, 731],
3018
3159
  [733, 733],
3019
3160
  [735, 735],
3161
+ [768, 879],
3020
3162
  [913, 929],
3021
3163
  [931, 937],
3022
3164
  [945, 961],
@@ -3024,7 +3166,8 @@ const ambiguousWidthRanges = [
3024
3166
  [1025, 1025],
3025
3167
  [1040, 1103],
3026
3168
  [1105, 1105],
3027
- [8208, 8213],
3169
+ [8208, 8208],
3170
+ [8211, 8214],
3028
3171
  [8216, 8217],
3029
3172
  [8220, 8221],
3030
3173
  [8224, 8226],
@@ -3086,7 +3229,8 @@ const ambiguousWidthRanges = [
3086
3229
  [8895, 8895],
3087
3230
  [8978, 8978],
3088
3231
  [9312, 9449],
3089
- [9451, 9471],
3232
+ [9451, 9547],
3233
+ [9552, 9587],
3090
3234
  [9600, 9615],
3091
3235
  [9618, 9621],
3092
3236
  [9632, 9633],
@@ -3112,14 +3256,40 @@ const ambiguousWidthRanges = [
3112
3256
  [9831, 9834],
3113
3257
  [9836, 9837],
3114
3258
  [9839, 9839],
3259
+ [9886, 9887],
3260
+ [9919, 9919],
3261
+ [9926, 9933],
3262
+ [9935, 9939],
3263
+ [9941, 9953],
3264
+ [9955, 9955],
3265
+ [9960, 9961],
3266
+ [9963, 9969],
3267
+ [9972, 9972],
3268
+ [9974, 9977],
3269
+ [9979, 9980],
3270
+ [9982, 9983],
3115
3271
  [10045, 10045],
3116
3272
  [10102, 10111],
3117
- [11094, 11094],
3273
+ [11094, 11097],
3274
+ [12872, 12879],
3118
3275
  [57344, 63743],
3119
- [65533, 65533]
3276
+ [65024, 65039],
3277
+ [65533, 65533],
3278
+ [127232, 127242],
3279
+ [127248, 127277],
3280
+ [127280, 127337],
3281
+ [127344, 127373],
3282
+ [127375, 127376],
3283
+ [127387, 127404],
3284
+ [917760, 917999],
3285
+ [983040, 1048573],
3286
+ [1048576, 1114109]
3120
3287
  ];
3288
+
3289
+ //#endregion
3290
+ //#region src/core/buffer/width.ts
3121
3291
  function isFullWidthCodePoint(codePoint) {
3122
- if (codePoint < 4352 || codePoint > 65510) return false;
3292
+ if (codePoint < 4352) return false;
3123
3293
  for (const [start, end] of fullWidthRanges) {
3124
3294
  if (codePoint < start) return false;
3125
3295
  if (codePoint <= end) return true;
@@ -3127,13 +3297,22 @@ function isFullWidthCodePoint(codePoint) {
3127
3297
  return false;
3128
3298
  }
3129
3299
  function isAmbiguousWidthCodePoint(codePoint) {
3130
- if (codePoint < 161 || codePoint > 65533) return false;
3300
+ const first = ambiguousWidthRanges[0];
3301
+ const last = ambiguousWidthRanges[ambiguousWidthRanges.length - 1];
3302
+ if (!first || !last) return false;
3303
+ if (codePoint < first[0] || codePoint > last[1]) return false;
3131
3304
  for (const [start, end] of ambiguousWidthRanges) {
3132
3305
  if (codePoint < start) return false;
3133
3306
  if (codePoint <= end) return true;
3134
3307
  }
3135
3308
  return false;
3136
3309
  }
3310
+ function isVariationSelectorCodePoint(codePoint) {
3311
+ return codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999;
3312
+ }
3313
+ function isCombiningMarkCodePoint(codePoint) {
3314
+ 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;
3315
+ }
3137
3316
  function isEmojiLike(codePoint) {
3138
3317
  return codePoint >= 127744 && codePoint <= 129791 || codePoint >= 127462 && codePoint <= 127487;
3139
3318
  }
@@ -3168,6 +3347,8 @@ function charCellWidth(text, provider = "default") {
3168
3347
  const codePoint = text.codePointAt(0);
3169
3348
  if (codePoint == null) return 1;
3170
3349
  const hasVs16 = text.includes("️");
3350
+ if (isVariationSelectorCodePoint(codePoint) || isCombiningMarkCodePoint(codePoint)) return 1;
3351
+ if (codePoint >= 9472 && codePoint <= 9599) return 1;
3171
3352
  if (isFullWidthCodePoint(codePoint)) return 2;
3172
3353
  if (provider === "cjk" && isAmbiguousWidthCodePoint(codePoint)) return 2;
3173
3354
  if (isEmojiLike(codePoint)) return 2;
@@ -3601,9 +3782,12 @@ function resizeBuffer(buffer, cols, rows) {
3601
3782
  const nextCols = Math.max(0, Math.floor(cols));
3602
3783
  const nextRows = Math.max(0, Math.floor(rows));
3603
3784
  if (nextCols === buffer.cols && nextRows === buffer.rows) return;
3785
+ const prevRows = buffer.rows;
3786
+ const prevDirtyAll = buffer.dirtyAll;
3787
+ const prevDirtyBits = buffer.dirtyBits;
3604
3788
  const blank = createBlankCell();
3605
3789
  const nextGrid = Array.from({ length: nextRows }, () => Array.from({ length: nextCols }, () => blank));
3606
- const copyRows = Math.min(buffer.rows, nextRows);
3790
+ const copyRows = Math.min(prevRows, nextRows);
3607
3791
  const copyCols = Math.min(buffer.cols, nextCols);
3608
3792
  for (let y = 0; y < copyRows; y++) {
3609
3793
  const src = getBufferRow(buffer, y);
@@ -3629,10 +3813,21 @@ function resizeBuffer(buffer, cols, rows) {
3629
3813
  buffer.cols = nextCols;
3630
3814
  buffer.rows = nextRows;
3631
3815
  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;
3816
+ buffer.dirtyCount = 0;
3817
+ buffer.dirtyMin = Number.POSITIVE_INFINITY;
3818
+ buffer.dirtyMax = -1;
3819
+ buffer.dirtyAll = prevDirtyAll && nextRows > 0;
3820
+ if (buffer.dirtyAll) {
3821
+ buffer.dirtyCount = nextRows;
3822
+ buffer.dirtyMin = 0;
3823
+ buffer.dirtyMax = nextRows - 1;
3824
+ } else for (let y = 0; y < copyRows; y++) {
3825
+ if (prevDirtyBits[y] !== 1) continue;
3826
+ buffer.dirtyBits[y] = 1;
3827
+ buffer.dirtyCount++;
3828
+ if (y < buffer.dirtyMin) buffer.dirtyMin = y;
3829
+ if (y > buffer.dirtyMax) buffer.dirtyMax = y;
3830
+ }
3636
3831
  buffer.cursorX = clamp(buffer.cursorX, 0, Math.max(0, nextCols));
3637
3832
  buffer.cursorY = clamp(buffer.cursorY, 0, Math.max(0, nextRows - 1));
3638
3833
  if (buffer.fingerprintFn) {
@@ -3705,6 +3900,10 @@ function mergePlanes(prev, next) {
3705
3900
  return Array.from(merged);
3706
3901
  }
3707
3902
  const TERMINAL_PLANE_INTERNALS = Symbol.for("@simon_he/vue-tui:v1:terminal-plane-internals");
3903
+ const TERMINAL_ROOT = Symbol.for("@simon_he/vue-tui:v1:terminal-root");
3904
+ function getRootTerminal(terminal) {
3905
+ return terminal[TERMINAL_ROOT] ?? terminal;
3906
+ }
3708
3907
  function createCoverage(cols, rows) {
3709
3908
  return Array.from({ length: rows }, () => new Uint8Array(cols));
3710
3909
  }
@@ -3789,6 +3988,8 @@ function markPlaneCoverageRange(state, x0, y0, x1, y1, value) {
3789
3988
  }
3790
3989
  }
3791
3990
  function markPlaneCoverageForPut(state, x, y, ch, prevCell) {
3991
+ if (!Number.isSafeInteger(x) || !Number.isSafeInteger(y)) return;
3992
+ if (x < 0 || x >= state.buffer.cols || y < 0 || y >= state.buffer.rows) return;
3792
3993
  const row = state.coverage[y];
3793
3994
  if (!row) return;
3794
3995
  if ((prevCell === null || prevCell === void 0 ? void 0 : prevCell.continuation) && x > 0) row[x - 1] = 1;
@@ -3912,10 +4113,14 @@ function createTerminal(opts) {
3912
4113
  }
3913
4114
  function putForPlane(plane, x, y, ch, style) {
3914
4115
  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);
4116
+ if (typeof x !== "number" || typeof y !== "number") return;
4117
+ const cellX = Math.floor(x);
4118
+ const cellY = Math.floor(y);
4119
+ if (!Number.isFinite(cellX) || !Number.isFinite(cellY)) return;
4120
+ if (cellY < 0 || cellY >= state.buffer.rows || cellX < 0 || cellX >= state.buffer.cols) return;
4121
+ const prevCell = getBufferCell(state.buffer, cellX, cellY);
4122
+ putCell(state.buffer, cellX, cellY, ch, style);
4123
+ markPlaneCoverageForPut(state, cellX, cellY, ch, prevCell);
3919
4124
  }
3920
4125
  function writeAtForPlane(plane, text, x, y, style) {
3921
4126
  const state = getPlaneState(plane);
@@ -4419,6 +4624,7 @@ function createTerminal(opts) {
4419
4624
  return getRowFingerprints(state.buffer, yy);
4420
4625
  }
4421
4626
  };
4627
+ api[TERMINAL_ROOT] = base;
4422
4628
  planeTerminals.set(plane, api);
4423
4629
  return api;
4424
4630
  }
@@ -4502,7 +4708,7 @@ function createTerminal(opts) {
4502
4708
  const rows = collectAndClearDirtyRows(state.buffer);
4503
4709
  if (rows === null) {
4504
4710
  composeAllRows = true;
4505
- break;
4711
+ continue;
4506
4712
  }
4507
4713
  for (const y of rows) dirtyRows.add(y);
4508
4714
  }
@@ -4624,6 +4830,7 @@ function createTerminal(opts) {
4624
4830
  scrollRowsForPlane(plane, startY, endY, lines);
4625
4831
  }
4626
4832
  };
4833
+ base[TERMINAL_ROOT] = base;
4627
4834
  return base;
4628
4835
  }
4629
4836
 
@@ -4650,7 +4857,7 @@ function sameRect$1(a, b) {
4650
4857
  function isVisible(node) {
4651
4858
  return node.visible !== false;
4652
4859
  }
4653
- function now() {
4860
+ function now$1() {
4654
4861
  return typeof performance !== "undefined" ? performance.now() : Date.now();
4655
4862
  }
4656
4863
  function rectRowRange(rect) {
@@ -4830,7 +5037,7 @@ function createCliEventManager(options) {
4830
5037
  bubbles: true,
4831
5038
  cancelable: true,
4832
5039
  defaultPrevented: false,
4833
- timeStamp: typeof time === "number" ? time : now(),
5040
+ timeStamp: typeof time === "number" ? time : now$1(),
4834
5041
  __stopped: false,
4835
5042
  stopPropagation() {
4836
5043
  this.__stopped = true;
@@ -5196,6 +5403,94 @@ const FRAME_PERF_REASON_PRIORITY = {
5196
5403
  scroll: 6,
5197
5404
  input: 7
5198
5405
  };
5406
+ const EMPTY_METRIC_STATS = Object.freeze({
5407
+ avg: 0,
5408
+ max: 0,
5409
+ min: 0
5410
+ });
5411
+ function metricStats(values) {
5412
+ if (!values.length) return EMPTY_METRIC_STATS;
5413
+ let sum = 0;
5414
+ let max = Number.NEGATIVE_INFINITY;
5415
+ let min = Number.POSITIVE_INFINITY;
5416
+ for (const value of values) {
5417
+ sum += value;
5418
+ if (value > max) max = value;
5419
+ if (value < min) min = value;
5420
+ }
5421
+ return {
5422
+ avg: sum / values.length,
5423
+ max,
5424
+ min
5425
+ };
5426
+ }
5427
+ function summarizeFramePerf(samples) {
5428
+ const durationMs = [];
5429
+ const renderManagerMs = [];
5430
+ const commitMs = [];
5431
+ const domFlushMs = [];
5432
+ const stdoutFlushMs = [];
5433
+ const dirtyRows = [];
5434
+ const scannedNodes = [];
5435
+ const paintedNodes = [];
5436
+ const reasons = {};
5437
+ const activePlanes = {};
5438
+ let fullDirtyFrames = 0;
5439
+ let coalescedInvalidates = 0;
5440
+ let coalescedFrameTasks = 0;
5441
+ let droppedUpdates = 0;
5442
+ let maxQueueDepth = 0;
5443
+ let rowBucketFallbacks = 0;
5444
+ for (const sample of samples) {
5445
+ var _sample$rowBucketFall;
5446
+ durationMs.push(sample.durationMs);
5447
+ renderManagerMs.push(sample.renderManagerMs);
5448
+ commitMs.push(sample.commitMs);
5449
+ scannedNodes.push(sample.scannedNodes);
5450
+ paintedNodes.push(sample.paintedNodes);
5451
+ if (sample.domFlushMs != null) domFlushMs.push(sample.domFlushMs);
5452
+ if (sample.stdoutFlushMs != null) stdoutFlushMs.push(sample.stdoutFlushMs);
5453
+ if (sample.dirtyRows == null) fullDirtyFrames++;
5454
+ else dirtyRows.push(sample.dirtyRows);
5455
+ reasons[sample.reason] = (reasons[sample.reason] ?? 0) + 1;
5456
+ const planes = sample.activePlanes;
5457
+ if (!planes) activePlanes.all = (activePlanes.all ?? 0) + 1;
5458
+ else for (const plane of planes) activePlanes[plane] = (activePlanes[plane] ?? 0) + 1;
5459
+ coalescedInvalidates += sample.coalescedInvalidates;
5460
+ coalescedFrameTasks += sample.coalescedFrameTasks;
5461
+ droppedUpdates += sample.droppedUpdates;
5462
+ if (sample.queueDepth > maxQueueDepth) maxQueueDepth = sample.queueDepth;
5463
+ rowBucketFallbacks += ((_sample$rowBucketFall = sample.rowBucketFallbacks) === null || _sample$rowBucketFall === void 0 ? void 0 : _sample$rowBucketFall.length) ?? 0;
5464
+ }
5465
+ const first = samples[0] ?? null;
5466
+ const latest = samples[samples.length - 1] ?? null;
5467
+ return {
5468
+ frames: samples.length,
5469
+ firstFrameId: (first === null || first === void 0 ? void 0 : first.frameId) ?? null,
5470
+ latestFrameId: (latest === null || latest === void 0 ? void 0 : latest.frameId) ?? null,
5471
+ windowMs: first && latest ? Math.max(0, latest.startedAt - first.startedAt) : 0,
5472
+ durationMs: metricStats(durationMs),
5473
+ renderManagerMs: metricStats(renderManagerMs),
5474
+ commitMs: metricStats(commitMs),
5475
+ domFlushMs: metricStats(domFlushMs),
5476
+ stdoutFlushMs: metricStats(stdoutFlushMs),
5477
+ dirtyRows: {
5478
+ avg: dirtyRows.length ? dirtyRows.reduce((sum, value) => sum + value, 0) / dirtyRows.length : 0,
5479
+ max: dirtyRows.length ? Math.max(...dirtyRows) : 0,
5480
+ sampledFrames: dirtyRows.length,
5481
+ fullFrames: fullDirtyFrames
5482
+ },
5483
+ scannedNodes: metricStats(scannedNodes),
5484
+ paintedNodes: metricStats(paintedNodes),
5485
+ coalescedInvalidates,
5486
+ coalescedFrameTasks,
5487
+ droppedUpdates,
5488
+ maxQueueDepth,
5489
+ rowBucketFallbacks,
5490
+ reasons,
5491
+ activePlanes
5492
+ };
5493
+ }
5199
5494
  function framePerfNow() {
5200
5495
  const p = globalThis.performance;
5201
5496
  if (p && typeof p.now === "function") return p.now();
@@ -5336,6 +5631,9 @@ function createFramePerfStore(limit = 120, opts) {
5336
5631
  function list() {
5337
5632
  return samples.slice();
5338
5633
  }
5634
+ function summary() {
5635
+ return summarizeFramePerf(samples);
5636
+ }
5339
5637
  function clear() {
5340
5638
  samples.length = 0;
5341
5639
  }
@@ -5348,6 +5646,7 @@ function createFramePerfStore(limit = 120, opts) {
5348
5646
  addSink,
5349
5647
  latest,
5350
5648
  list,
5649
+ summary,
5351
5650
  clear
5352
5651
  };
5353
5652
  }
@@ -5463,6 +5762,9 @@ function clampPointToRect(point, rect) {
5463
5762
  y: Math.max(rect.y, Math.min(rect.y + Math.max(0, rect.h - 1), Math.floor(point.y)))
5464
5763
  };
5465
5764
  }
5765
+ function clipboardCanWrite$1(api) {
5766
+ return api.canWrite ?? api.supported;
5767
+ }
5466
5768
  function createTerminalSelectionController(options) {
5467
5769
  const state = shallowRef(EMPTY_STATE);
5468
5770
  let range = null;
@@ -5664,7 +5966,7 @@ function createTerminalSelectionController(options) {
5664
5966
  const text = state.value.text || selectedText();
5665
5967
  if (!text) return false;
5666
5968
  setResolvedText(text);
5667
- if (!options.clipboard.supported) {
5969
+ if (!clipboardCanWrite$1(options.clipboard)) {
5668
5970
  var _options$onCopy;
5669
5971
  (_options$onCopy = options.onCopy) === null || _options$onCopy === void 0 || _options$onCopy.call(options, copyPayload(text, false, new Error("Clipboard unavailable")));
5670
5972
  return false;
@@ -5769,16 +6071,19 @@ function createOsc52ClipboardProvider(options = {}) {
5769
6071
  var _stdout$write;
5770
6072
  stdout === null || stdout === void 0 || (_stdout$write = stdout.write) === null || _stdout$write === void 0 || _stdout$write.call(stdout, sequence);
5771
6073
  });
5772
- const supported = options.supported ?? Boolean(options.write || (stdout === null || stdout === void 0 ? void 0 : stdout.write) && stdout.isTTY);
6074
+ const canWrite = options.supported ?? Boolean(options.write || (stdout === null || stdout === void 0 ? void 0 : stdout.write) && stdout.isTTY);
6075
+ const canRead = typeof options.readText === "function";
5773
6076
  const target = normalizeOsc52Target(options.target);
5774
6077
  return {
5775
- supported,
6078
+ supported: canWrite,
6079
+ canRead,
6080
+ canWrite,
5776
6081
  async readText() {
5777
- if (!options.readText) throw new Error("Clipboard read not available in this runtime");
6082
+ if (!canRead) throw new Error("Clipboard read not available in this runtime");
5778
6083
  return options.readText();
5779
6084
  },
5780
6085
  async writeText(text) {
5781
- if (!supported) throw new Error("Clipboard not available in this runtime");
6086
+ if (!canWrite) throw new Error("Clipboard not available in this runtime");
5782
6087
  const bytes = encodeTextBytes(text);
5783
6088
  const maxBytes = resolveMaxBytes(options.maxBytes, 100 * 1024);
5784
6089
  if (bytes.byteLength > maxBytes) throw new Error(`OSC52 clipboard payload too large: ${bytes.byteLength} bytes`);
@@ -6133,6 +6438,120 @@ function normalizeTerminalLinkOpener(opener) {
6133
6438
  return opener;
6134
6439
  }
6135
6440
 
6441
+ //#endregion
6442
+ //#region src/renderer/terminal-graphics-trace.ts
6443
+ const metrics$1 = {
6444
+ requests: 0,
6445
+ skippedHidden: 0,
6446
+ skippedScrolling: 0,
6447
+ cacheHits: 0,
6448
+ cacheMisses: 0,
6449
+ cacheStores: 0,
6450
+ rendererRuns: 0,
6451
+ rendererAborts: 0,
6452
+ rendererErrors: 0,
6453
+ queued: 0,
6454
+ queueWaits: 0,
6455
+ queueDeduped: 0,
6456
+ cleared: 0,
6457
+ bytesQueued: 0,
6458
+ totalRendererMs: 0,
6459
+ totalValidateMs: 0,
6460
+ totalQueueWaitMs: 0,
6461
+ maxQueueWaitMs: 0,
6462
+ maxActiveRenders: 0,
6463
+ maxWaitingRenders: 0,
6464
+ maxCacheEntries: 0,
6465
+ maxCacheBytes: 0,
6466
+ scrollStarts: 0,
6467
+ scrollMarks: 0,
6468
+ scrollIdles: 0,
6469
+ totalScrollMs: 0,
6470
+ maxScrollMs: 0
6471
+ };
6472
+ const subscribers = new Set();
6473
+ function nowTerminalGraphicTraceTime() {
6474
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
6475
+ }
6476
+ function recordTerminalGraphicTrace(event) {
6477
+ const normalized = {
6478
+ ...event,
6479
+ timestamp: event.timestamp ?? nowTerminalGraphicTraceTime()
6480
+ };
6481
+ switch (normalized.type) {
6482
+ case "request":
6483
+ metrics$1.requests++;
6484
+ break;
6485
+ case "skip-hidden":
6486
+ metrics$1.skippedHidden++;
6487
+ break;
6488
+ case "skip-scrolling":
6489
+ metrics$1.skippedScrolling++;
6490
+ break;
6491
+ case "cache-hit":
6492
+ metrics$1.cacheHits++;
6493
+ break;
6494
+ case "cache-miss":
6495
+ metrics$1.cacheMisses++;
6496
+ break;
6497
+ case "cache-store":
6498
+ metrics$1.cacheStores++;
6499
+ break;
6500
+ case "renderer-start":
6501
+ metrics$1.rendererRuns++;
6502
+ break;
6503
+ case "renderer-end":
6504
+ metrics$1.totalRendererMs += normalized.durationMs ?? 0;
6505
+ break;
6506
+ case "renderer-abort":
6507
+ metrics$1.rendererAborts++;
6508
+ break;
6509
+ case "renderer-error":
6510
+ metrics$1.rendererErrors++;
6511
+ break;
6512
+ case "validate-end":
6513
+ metrics$1.totalValidateMs += normalized.durationMs ?? 0;
6514
+ break;
6515
+ case "queue":
6516
+ metrics$1.queued++;
6517
+ metrics$1.bytesQueued += normalized.bytes ?? 0;
6518
+ break;
6519
+ case "queue-wait": {
6520
+ const durationMs = normalized.durationMs ?? 0;
6521
+ metrics$1.queueWaits++;
6522
+ metrics$1.totalQueueWaitMs += durationMs;
6523
+ metrics$1.maxQueueWaitMs = Math.max(metrics$1.maxQueueWaitMs, durationMs);
6524
+ break;
6525
+ }
6526
+ case "queue-dedupe":
6527
+ metrics$1.queueDeduped++;
6528
+ break;
6529
+ case "clear":
6530
+ metrics$1.cleared++;
6531
+ break;
6532
+ case "scroll-start":
6533
+ metrics$1.scrollStarts++;
6534
+ break;
6535
+ case "scroll-mark":
6536
+ metrics$1.scrollMarks++;
6537
+ break;
6538
+ case "scroll-idle": {
6539
+ const durationMs = normalized.durationMs ?? 0;
6540
+ metrics$1.scrollIdles++;
6541
+ metrics$1.totalScrollMs += durationMs;
6542
+ metrics$1.maxScrollMs = Math.max(metrics$1.maxScrollMs, durationMs);
6543
+ break;
6544
+ }
6545
+ }
6546
+ if (normalized.active != null && Number.isFinite(normalized.active)) metrics$1.maxActiveRenders = Math.max(metrics$1.maxActiveRenders, Math.max(0, normalized.active));
6547
+ if (normalized.waiting != null && Number.isFinite(normalized.waiting)) metrics$1.maxWaitingRenders = Math.max(metrics$1.maxWaitingRenders, Math.max(0, normalized.waiting));
6548
+ if (normalized.cacheEntries != null && Number.isFinite(normalized.cacheEntries)) metrics$1.maxCacheEntries = Math.max(metrics$1.maxCacheEntries, Math.max(0, normalized.cacheEntries));
6549
+ if (normalized.cacheBytes != null && Number.isFinite(normalized.cacheBytes)) metrics$1.maxCacheBytes = Math.max(metrics$1.maxCacheBytes, Math.max(0, normalized.cacheBytes));
6550
+ for (const subscriber of subscribers) try {
6551
+ subscriber(normalized);
6552
+ } catch {}
6553
+ }
6554
+
6136
6555
  //#endregion
6137
6556
  //#region src/vue/context.ts
6138
6557
  const TerminalContextKey = injectionKey("TerminalContext");
@@ -6143,6 +6562,7 @@ const RenderPlaneContextKey = injectionKey("RenderPlane");
6143
6562
  const ImeAnchorContextKey = injectionKey("ImeAnchor");
6144
6563
  const TInputPluginsContextKey = injectionKey("TInputPlugins");
6145
6564
  const TPathPickerProviderContextKey = injectionKey("TPathPickerProvider");
6565
+ const TerminalGraphicsActivityKey = injectionKey("TerminalGraphicsActivity");
6146
6566
  const DialogContextKey = injectionKey("DialogContext");
6147
6567
 
6148
6568
  //#endregion
@@ -6293,6 +6713,10 @@ function sameRect(a, b) {
6293
6713
  if (!a || !b) return false;
6294
6714
  return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
6295
6715
  }
6716
+ function rectsIntersect(a, b) {
6717
+ if (isEmptyRect(a) || isEmptyRect(b)) return false;
6718
+ 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;
6719
+ }
6296
6720
  function createRenderManager(terminal, options = {}) {
6297
6721
  const widthProvider = options.widthProvider ?? "default";
6298
6722
  const withRenderTextPass = (fn) => withTextRenderPass(fn, widthProvider);
@@ -6300,6 +6724,7 @@ function createRenderManager(terminal, options = {}) {
6300
6724
  const nodes = new Map();
6301
6725
  const planeDirtyStates = new Map();
6302
6726
  const initialSize = terminal.size();
6727
+ let terminalCols = initialSize.cols;
6303
6728
  let terminalRows = initialSize.rows;
6304
6729
  let allRows = Array.from({ length: terminalRows }, (_, index) => index);
6305
6730
  let sortedNodes = [];
@@ -6320,19 +6745,37 @@ function createRenderManager(terminal, options = {}) {
6320
6745
  let disposed = false;
6321
6746
  const stackPathCache = new WeakMap();
6322
6747
  const profiler = createTuiProfiler("render-manager", options.profiler);
6323
- const offResize = terminal.on("resize", ({ rows }) => {
6748
+ const offResize = terminal.on("resize", ({ cols, rows }) => {
6324
6749
  if (disposed) return;
6750
+ const colsChanged = cols !== terminalCols;
6751
+ const prevRows = terminalRows;
6752
+ terminalCols = cols;
6325
6753
  terminalRows = rows;
6326
6754
  allRows = Array.from({ length: terminalRows }, (_, index) => index);
6327
6755
  for (const state of planeDirtyStates.values()) {
6328
- state.allRowsDirty = true;
6756
+ const prevBits = state.dirtyRowBits;
6329
6757
  state.dirtyRowBits = new Uint8Array(terminalRows);
6330
6758
  state.dirtyRowCount = 0;
6331
6759
  state.dirtyMinY = Number.POSITIVE_INFINITY;
6332
6760
  state.dirtyMaxY = -1;
6761
+ const limit = Math.min(terminalRows, prevBits.length);
6762
+ for (let y = 0; y < limit; y++) {
6763
+ if (prevBits[y] !== 1) continue;
6764
+ state.dirtyRowBits[y] = 1;
6765
+ state.dirtyRowCount++;
6766
+ if (y < state.dirtyMinY) state.dirtyMinY = y;
6767
+ if (y > state.dirtyMaxY) state.dirtyMaxY = y;
6768
+ }
6769
+ for (let y = prevRows; y < terminalRows; y++) {
6770
+ if (state.dirtyRowBits[y] === 1) continue;
6771
+ state.dirtyRowBits[y] = 1;
6772
+ state.dirtyRowCount++;
6773
+ if (y < state.dirtyMinY) state.dirtyMinY = y;
6774
+ if (y > state.dirtyMaxY) state.dirtyMaxY = y;
6775
+ }
6333
6776
  }
6334
6777
  rebuildRowBuckets();
6335
- clearTextCaches();
6778
+ if (colsChanged) clearTextCaches();
6336
6779
  });
6337
6780
  const rootStack = Object.freeze({
6338
6781
  id: `s${nextStackId++}`,
@@ -6531,6 +6974,33 @@ function createRenderManager(terminal, options = {}) {
6531
6974
  if (y > state.dirtyMaxY) state.dirtyMaxY = y;
6532
6975
  }
6533
6976
  }
6977
+ function markLowerPlanesForRect(plane, rect) {
6978
+ const planeIndex = TERMINAL_RENDER_PLANES.indexOf(plane);
6979
+ if (planeIndex <= 0) return;
6980
+ for (let i = 0; i < planeIndex; i++) markRect(TERMINAL_RENDER_PLANES[i], rect);
6981
+ }
6982
+ function markRowRange(plane, startY, endY) {
6983
+ const state = getDirtyState(plane);
6984
+ const start = Math.max(0, Math.min(terminalRows, Math.floor(startY)));
6985
+ const end = Math.max(start, Math.min(terminalRows, Math.floor(endY)));
6986
+ for (let y = start; y < end; y++) {
6987
+ if (state.dirtyRowBits[y] === 1) continue;
6988
+ state.dirtyRowBits[y] = 1;
6989
+ state.dirtyRowCount++;
6990
+ if (y < state.dirtyMinY) state.dirtyMinY = y;
6991
+ if (y > state.dirtyMaxY) state.dirtyMaxY = y;
6992
+ }
6993
+ }
6994
+ function markRectHeightDelta(plane, prev, next) {
6995
+ if (!prev || !next) return false;
6996
+ if (prev.x !== next.x || prev.y !== next.y || prev.w !== next.w) return false;
6997
+ const prevBounds = rectToYBounds(prev);
6998
+ const nextBounds = rectToYBounds(next);
6999
+ if (prevBounds.y0 !== nextBounds.y0) return false;
7000
+ if (nextBounds.y1 > prevBounds.y1) markRowRange(plane, prevBounds.y1, nextBounds.y1);
7001
+ else if (prevBounds.y1 > nextBounds.y1) markRowRange(plane, nextBounds.y1, prevBounds.y1);
7002
+ return true;
7003
+ }
6534
7004
  function markRowsForNode(node, rows) {
6535
7005
  if (!rows.length) return false;
6536
7006
  warnIfRowsLookLocal(node, rows);
@@ -6578,6 +7048,7 @@ function createRenderManager(terminal, options = {}) {
6578
7048
  nodes.set(id, full);
6579
7049
  addToRowBuckets(full);
6580
7050
  markRect(full.plane, full.rect);
7051
+ markLowerPlanesForRect(full.plane, full.rect);
6581
7052
  sortedDirty = true;
6582
7053
  return full;
6583
7054
  }
@@ -6589,6 +7060,7 @@ function createRenderManager(terminal, options = {}) {
6589
7060
  const nextPlane = next.plane ?? prev.plane;
6590
7061
  const planeChanged = nextPlane !== prev.plane;
6591
7062
  const hasRect = Object.prototype.hasOwnProperty.call(next, "rect");
7063
+ const paintChanged = Object.prototype.hasOwnProperty.call(next, "paint");
6592
7064
  const nextRect = hasRect ? next.rect ?? null : prev.rect;
6593
7065
  const rectChanged = !sameRect(prev.rect, nextRect);
6594
7066
  const bucketChanged = planeChanged || rectChanged;
@@ -6596,10 +7068,12 @@ function createRenderManager(terminal, options = {}) {
6596
7068
  const dirtyRowsHint = next.dirtyRowsHint;
6597
7069
  const canUseDirtyRowsHint = !sortChanged && !rectChanged && nextPlane === prev.plane && dirtyRowsHint != null && dirtyRowsHint.length > 0;
6598
7070
  if (canUseDirtyRowsHint) markRowsForNode(prev, dirtyRowsHint);
6599
- else {
7071
+ else if (rectChanged && !paintChanged && !sortChanged && !planeChanged && markRectHeightDelta(prev.plane, prev.rect, nextRect)) {} else {
6600
7072
  markRect(prev.plane, prev.rect);
6601
7073
  markRect(nextPlane, nextRect);
6602
7074
  }
7075
+ markLowerPlanesForRect(prev.plane, prev.rect);
7076
+ markLowerPlanesForRect(nextPlane, nextRect);
6603
7077
  if (bucketChanged) removeFromRowBuckets(prev);
6604
7078
  const full = Object.freeze({
6605
7079
  ...prev,
@@ -6629,11 +7103,41 @@ function createRenderManager(terminal, options = {}) {
6629
7103
  if (!node) return false;
6630
7104
  return markRowsForNode(node, rows);
6631
7105
  }
7106
+ function isRectCoveredByHigherNode(id, rect, options$1 = {}) {
7107
+ return higherNodeCoverageRects(id, rect, options$1).length > 0;
7108
+ }
7109
+ function higherNodeCoverageRects(id, rect, options$1 = {}) {
7110
+ if (disposed || isEmptyRect(rect)) return [];
7111
+ const owner = nodes.get(id);
7112
+ if (!owner) return [];
7113
+ const ownerPlaneIndex = TERMINAL_RENDER_PLANES.indexOf(owner.plane);
7114
+ if (ownerPlaneIndex < 0) return [];
7115
+ const out = [];
7116
+ for (const candidate of nodes.values()) {
7117
+ if (candidate.id === id || !candidate.rect || isEmptyRect(candidate.rect)) continue;
7118
+ if (!rectsIntersect(rect, candidate.rect)) continue;
7119
+ const candidatePlaneIndex = TERMINAL_RENDER_PLANES.indexOf(candidate.plane);
7120
+ const covered = candidatePlaneIndex > ownerPlaneIndex || !options$1.ignoreSamePlane && candidatePlaneIndex === ownerPlaneIndex && compareNodes(owner, candidate) < 0;
7121
+ if (!covered) continue;
7122
+ const x0 = Math.max(rect.x, candidate.rect.x);
7123
+ const y0 = Math.max(rect.y, candidate.rect.y);
7124
+ const x1 = Math.min(rect.x + rect.w, candidate.rect.x + candidate.rect.w);
7125
+ const y1 = Math.min(rect.y + rect.h, candidate.rect.y + candidate.rect.h);
7126
+ if (x1 > x0 && y1 > y0) out.push({
7127
+ x: x0,
7128
+ y: y0,
7129
+ w: x1 - x0,
7130
+ h: y1 - y0
7131
+ });
7132
+ }
7133
+ return out;
7134
+ }
6632
7135
  function unregister(id) {
6633
7136
  if (disposed) return;
6634
7137
  const prev = nodes.get(id);
6635
7138
  if (prev) {
6636
7139
  markRect(prev.plane, prev.rect);
7140
+ markLowerPlanesForRect(prev.plane, prev.rect);
6637
7141
  removeFromRowBuckets(prev);
6638
7142
  warnedLocalDirtyRows.delete(id);
6639
7143
  }
@@ -6849,6 +7353,8 @@ function createRenderManager(terminal, options = {}) {
6849
7353
  register,
6850
7354
  update,
6851
7355
  markDirtyRows,
7356
+ isRectCoveredByHigherNode,
7357
+ higherNodeCoverageRects,
6852
7358
  unregister,
6853
7359
  render,
6854
7360
  dispose() {
@@ -7247,6 +7753,8 @@ function resolveSelectionConfig(config) {
7247
7753
  }
7248
7754
  const unsupportedClipboard = {
7249
7755
  supported: false,
7756
+ canRead: false,
7757
+ canWrite: false,
7250
7758
  async readText() {
7251
7759
  return "";
7252
7760
  },
@@ -7254,6 +7762,12 @@ const unsupportedClipboard = {
7254
7762
  throw new Error("Clipboard unavailable");
7255
7763
  }
7256
7764
  };
7765
+ function clipboardCanRead(api) {
7766
+ return api.canRead ?? api.supported;
7767
+ }
7768
+ function clipboardCanWrite(api) {
7769
+ return api.canWrite ?? api.supported;
7770
+ }
7257
7771
  function isPlainObject(v) {
7258
7772
  if (!v || typeof v !== "object") return false;
7259
7773
  const proto = Object.getPrototypeOf(v);
@@ -7285,10 +7799,12 @@ function shallowEqualRecord(a, b) {
7285
7799
  function createTerminalApp(options) {
7286
7800
  const env = (process === null || process === void 0 ? void 0 : process.env) ?? {};
7287
7801
  if (shouldInstallFileWriters(env)) installNodeFileWriters();
7802
+ const debugEnabled = isDebugEnabled();
7803
+ const debugLog$1 = createDebugLogger(debugEnabled);
7288
7804
  const widthProvider = options.widthProvider ?? "default";
7289
7805
  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());
7806
+ const profileLogPath = envString$1(env, "VUE_TUI_PROFILE_LOG_PATH", "DIMCODE_PROFILE_TUI_LOG_PATH");
7807
+ const framePerfLogPath = envString$1(env, "VUE_TUI_FRAME_PERF_LOG_PATH", "DIMCODE_TUI_PERF_LOG") || (profileLogPath.endsWith(".jsonl") ? profileLogPath : defaultVueTuiFramePerfLogPath());
7292
7808
  const framePerfSink = profileEnabled ? createJsonlPerfSink({
7293
7809
  file: framePerfLogPath,
7294
7810
  fileWriter: nodeProfilerFileWriter,
@@ -7339,6 +7855,10 @@ function createTerminalApp(options) {
7339
7855
  }
7340
7856
  });
7341
7857
  const offCommit = terminal.on("commit", ({ dirtyRows, planes, sync }) => {
7858
+ if (debugEnabled) {
7859
+ const size = terminal.size();
7860
+ 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" : ""}`);
7861
+ }
7342
7862
  latency === null || latency === void 0 || latency.recordCommit({
7343
7863
  dirtyRows,
7344
7864
  planes,
@@ -7431,6 +7951,10 @@ function createTerminalApp(options) {
7431
7951
  scheduled = false;
7432
7952
  flushing = true;
7433
7953
  const activePlanes = takeActivePlanes();
7954
+ if (debugEnabled) {
7955
+ const size = terminal.size();
7956
+ 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}`);
7957
+ }
7434
7958
  latency === null || latency === void 0 || latency.recordFlushStart({
7435
7959
  sync,
7436
7960
  activePlanes
@@ -7556,6 +8080,10 @@ function createTerminalApp(options) {
7556
8080
  function invalidate(options$1) {
7557
8081
  if (disposed) return;
7558
8082
  const priority = (options$1 === null || options$1 === void 0 ? void 0 : options$1.priority) ?? "normal";
8083
+ if (debugEnabled) {
8084
+ const size = terminal.size();
8085
+ 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()}`);
8086
+ }
7559
8087
  noteFrameReason(options$1 === null || options$1 === void 0 ? void 0 : options$1.reason);
7560
8088
  latency === null || latency === void 0 || latency.recordSchedulerInvalidate({
7561
8089
  priority,
@@ -7640,6 +8168,7 @@ function createTerminalApp(options) {
7640
8168
  invalidate({ plane: portal.plane });
7641
8169
  return handle;
7642
8170
  } };
8171
+ const clipboard = options.clipboard ?? unsupportedClipboard;
7643
8172
  const rootLayout = shallowReactive({
7644
8173
  originX: 0,
7645
8174
  originY: 0,
@@ -7679,10 +8208,11 @@ function createTerminalApp(options) {
7679
8208
  "chrome"
7680
8209
  ];
7681
8210
  let selectionRenderNodeId = null;
8211
+ const selectionRenderRect = () => null;
7682
8212
  const selection = createTerminalSelectionController({
7683
8213
  terminal,
7684
8214
  overlayTerminal: selectionOverlay,
7685
- clipboard: options.clipboard ?? unsupportedClipboard,
8215
+ clipboard,
7686
8216
  getRow: (y) => readTerminalRowForPlanes(terminal, y, selectionReadPlanes),
7687
8217
  getTextProviders: () => Array.from(selectionTextProviders.values()),
7688
8218
  getOptions: () => {
@@ -7728,12 +8258,7 @@ function createTerminalApp(options) {
7728
8258
  stack: render.rootStack,
7729
8259
  plane: "overlay",
7730
8260
  zIndex: -1e4,
7731
- rect: {
7732
- x: 0,
7733
- y: 0,
7734
- w: options.cols,
7735
- h: options.rows
7736
- },
8261
+ rect: selectionRenderRect(),
7737
8262
  paint: selection.paint
7738
8263
  });
7739
8264
  selectionRenderNodeId = selectionRenderNode.id;
@@ -7848,19 +8373,17 @@ function createTerminalApp(options) {
7848
8373
  ...createDefaultTInputHostAdapter(),
7849
8374
  isTerminalLike: true,
7850
8375
  async readClipboardText() {
7851
- var _options$clipboard;
7852
- if (!((_options$clipboard = options.clipboard) === null || _options$clipboard === void 0 ? void 0 : _options$clipboard.supported)) return "";
8376
+ if (!clipboardCanRead(clipboard)) return "";
7853
8377
  try {
7854
- return await options.clipboard.readText();
8378
+ return await clipboard.readText();
7855
8379
  } catch {
7856
8380
  return "";
7857
8381
  }
7858
8382
  },
7859
8383
  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;
8384
+ if (!text || !clipboardCanWrite(clipboard)) return false;
7862
8385
  try {
7863
- await options.clipboard.writeText(text);
8386
+ await clipboard.writeText(text);
7864
8387
  return true;
7865
8388
  } catch {
7866
8389
  return false;
@@ -7869,6 +8392,7 @@ function createTerminalApp(options) {
7869
8392
  }))] : [defaultTInputHostPlugin]);
7870
8393
  const linkOpener = ref(normalizeTerminalLinkOpener(options.linkOpener));
7871
8394
  const offResize = terminal.on("resize", ({ cols, rows }) => {
8395
+ if (debugEnabled) debugLog$1.render(`app resize event: size=${cols}x${rows}, scheduled=${scheduled}, flushing=${flushing}, queueDepth=${queueDepth()}`);
7872
8396
  rootLayout.clipRect = {
7873
8397
  x: 0,
7874
8398
  y: 0,
@@ -7876,12 +8400,7 @@ function createTerminalApp(options) {
7876
8400
  h: rows
7877
8401
  };
7878
8402
  selection.clear();
7879
- if (selectionRenderNodeId) render.update(selectionRenderNodeId, { rect: {
7880
- x: 0,
7881
- y: 0,
7882
- w: cols,
7883
- h: rows
7884
- } });
8403
+ if (selectionRenderNodeId) render.update(selectionRenderNodeId, { rect: selectionRenderRect() });
7885
8404
  invalidate({ reason: "resize" });
7886
8405
  });
7887
8406
  schedulerApi = {
@@ -7902,6 +8421,7 @@ function createTerminalApp(options) {
7902
8421
  events: shallowRef(events),
7903
8422
  scheduler: schedulerApi,
7904
8423
  runtime,
8424
+ clipboard,
7905
8425
  observability: {
7906
8426
  trace,
7907
8427
  framePerf
@@ -7975,7 +8495,11 @@ const metrics = {
7975
8495
  writeEmaMs: 0,
7976
8496
  writeMode: "stream",
7977
8497
  fps: 0,
7978
- bytesPerSec: 0
8498
+ bytesPerSec: 0,
8499
+ terminalGraphicsDraws: 0,
8500
+ terminalGraphicsClears: 0,
8501
+ terminalGraphicsBytes: 0,
8502
+ terminalGraphicsActive: 0
7979
8503
  };
7980
8504
  let bucketStart = 0;
7981
8505
  let bucketFrames = 0;
@@ -8009,6 +8533,291 @@ function recordStdoutFrame(info) {
8009
8533
  bucketBytes = 0;
8010
8534
  }
8011
8535
  }
8536
+ function recordStdoutTerminalGraphics(info) {
8537
+ metrics.terminalGraphicsDraws += Math.max(0, Math.floor(Number(info.draws) || 0));
8538
+ metrics.terminalGraphicsClears += Math.max(0, Math.floor(Number(info.clears) || 0));
8539
+ metrics.terminalGraphicsBytes += Math.max(0, Math.floor(Number(info.bytes) || 0));
8540
+ metrics.terminalGraphicsActive = Math.max(0, Math.floor(Number(info.active) || 0));
8541
+ }
8542
+
8543
+ //#endregion
8544
+ //#region src/renderer/terminal-graphic-render-queue.ts
8545
+ const CACHE_MISS = Symbol("terminal-graphic-cache-miss");
8546
+ function now() {
8547
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
8548
+ }
8549
+ function createAbortError() {
8550
+ const error = new Error("Terminal graphic render aborted");
8551
+ error.name = "AbortError";
8552
+ return error;
8553
+ }
8554
+ function positiveFiniteInt(value, fallback) {
8555
+ const n = Number(value);
8556
+ if (!Number.isFinite(n)) return fallback;
8557
+ const int = Math.floor(n);
8558
+ return int > 0 ? int : fallback;
8559
+ }
8560
+ function throwIfAborted(signal) {
8561
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) throw createAbortError();
8562
+ }
8563
+ function raceAbort(promise, signal) {
8564
+ if (!signal) return promise;
8565
+ if (signal.aborted) return Promise.reject(createAbortError());
8566
+ return new Promise((resolve, reject) => {
8567
+ const onAbort = () => reject(createAbortError());
8568
+ signal.addEventListener("abort", onAbort, { once: true });
8569
+ promise.then((value) => {
8570
+ signal.removeEventListener("abort", onAbort);
8571
+ resolve(value);
8572
+ }, (error) => {
8573
+ signal.removeEventListener("abort", onAbort);
8574
+ reject(error);
8575
+ });
8576
+ });
8577
+ }
8578
+ function createTerminalGraphicRenderQueue(options = {}) {
8579
+ const maxConcurrency = positiveFiniteInt(options.maxConcurrency, 2);
8580
+ const maxEntries = positiveFiniteInt(options.maxEntries, 128);
8581
+ const maxBytes = positiveFiniteInt(options.maxBytes, 32 * 1024 * 1024);
8582
+ const ttlMs = positiveFiniteInt(options.ttlMs, 5 * 6e4);
8583
+ const dedupeInflight = options.dedupeInflight ?? false;
8584
+ let active = 0;
8585
+ let cacheBytes = 0;
8586
+ let generation = 0;
8587
+ const waiters = [];
8588
+ const cache = new Map();
8589
+ const inflight = new Map();
8590
+ function traceQueueState(event) {
8591
+ recordTerminalGraphicTrace({
8592
+ ...event,
8593
+ active,
8594
+ waiting: waiters.length,
8595
+ cacheEntries: cache.size,
8596
+ cacheBytes
8597
+ });
8598
+ }
8599
+ function evictUntilBudget() {
8600
+ while (cache.size > maxEntries || cacheBytes > maxBytes) {
8601
+ const oldestKey = cache.keys().next().value;
8602
+ if (!oldestKey) break;
8603
+ const entry = cache.get(oldestKey);
8604
+ cache.delete(oldestKey);
8605
+ if (entry) {
8606
+ var _options$onMetric;
8607
+ cacheBytes -= entry.bytes;
8608
+ (_options$onMetric = options.onMetric) === null || _options$onMetric === void 0 || _options$onMetric.call(options, {
8609
+ type: "evict",
8610
+ key: oldestKey,
8611
+ bytes: entry.bytes
8612
+ });
8613
+ }
8614
+ }
8615
+ }
8616
+ function setCache(key, value, bytes) {
8617
+ var _options$onMetric2;
8618
+ const prev = cache.get(key);
8619
+ if (prev) {
8620
+ cache.delete(key);
8621
+ cacheBytes -= prev.bytes;
8622
+ }
8623
+ if (bytes > maxBytes) return;
8624
+ cache.set(key, {
8625
+ value,
8626
+ bytes,
8627
+ expiresAt: Date.now() + ttlMs
8628
+ });
8629
+ cacheBytes += bytes;
8630
+ (_options$onMetric2 = options.onMetric) === null || _options$onMetric2 === void 0 || _options$onMetric2.call(options, {
8631
+ type: "cache-store",
8632
+ key,
8633
+ bytes
8634
+ });
8635
+ traceQueueState({
8636
+ type: "cache-store",
8637
+ id: key,
8638
+ key,
8639
+ bytes
8640
+ });
8641
+ evictUntilBudget();
8642
+ }
8643
+ function getCache(key) {
8644
+ var _options$onMetric3;
8645
+ const entry = cache.get(key);
8646
+ if (!entry) return CACHE_MISS;
8647
+ if (entry.expiresAt < Date.now()) {
8648
+ cache.delete(key);
8649
+ cacheBytes -= entry.bytes;
8650
+ return CACHE_MISS;
8651
+ }
8652
+ cache.delete(key);
8653
+ cache.set(key, entry);
8654
+ (_options$onMetric3 = options.onMetric) === null || _options$onMetric3 === void 0 || _options$onMetric3.call(options, {
8655
+ type: "cache-hit",
8656
+ key
8657
+ });
8658
+ traceQueueState({
8659
+ type: "cache-hit",
8660
+ id: key,
8661
+ key,
8662
+ bytes: entry.bytes
8663
+ });
8664
+ return entry.value;
8665
+ }
8666
+ function release() {
8667
+ active = Math.max(0, active - 1);
8668
+ pump();
8669
+ }
8670
+ function once(fn) {
8671
+ let called = false;
8672
+ return () => {
8673
+ if (called) return;
8674
+ called = true;
8675
+ fn();
8676
+ };
8677
+ }
8678
+ function observeActiveRenderAbort(key, signal) {
8679
+ if (!signal) return () => void 0;
8680
+ const onAbort = () => {
8681
+ var _options$onMetric4;
8682
+ (_options$onMetric4 = options.onMetric) === null || _options$onMetric4 === void 0 || _options$onMetric4.call(options, {
8683
+ type: "render-abort",
8684
+ key
8685
+ });
8686
+ traceQueueState({
8687
+ type: "renderer-abort",
8688
+ id: key,
8689
+ key
8690
+ });
8691
+ };
8692
+ signal.addEventListener("abort", onAbort, { once: true });
8693
+ return () => signal.removeEventListener("abort", onAbort);
8694
+ }
8695
+ function pump() {
8696
+ while (active < maxConcurrency && waiters.length > 0) {
8697
+ const waiter = waiters.shift();
8698
+ active++;
8699
+ traceQueueState({
8700
+ type: "queue-depth",
8701
+ id: waiter.key,
8702
+ key: waiter.key
8703
+ });
8704
+ waiter.start();
8705
+ }
8706
+ }
8707
+ function acquire(key, signal) {
8708
+ throwIfAborted(signal);
8709
+ if (active < maxConcurrency) {
8710
+ active++;
8711
+ traceQueueState({
8712
+ type: "queue-depth",
8713
+ id: key,
8714
+ key
8715
+ });
8716
+ return Promise.resolve(release);
8717
+ }
8718
+ const waitStartedAt = now();
8719
+ return new Promise((resolve, reject) => {
8720
+ let onAbort = () => void 0;
8721
+ const cleanup = () => {
8722
+ signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
8723
+ };
8724
+ const waiter = {
8725
+ key,
8726
+ start: () => {
8727
+ var _options$onMetric5;
8728
+ cleanup();
8729
+ const waitMs = now() - waitStartedAt;
8730
+ (_options$onMetric5 = options.onMetric) === null || _options$onMetric5 === void 0 || _options$onMetric5.call(options, {
8731
+ type: "queue-wait",
8732
+ key,
8733
+ waitMs
8734
+ });
8735
+ traceQueueState({
8736
+ type: "queue-wait",
8737
+ id: key,
8738
+ key,
8739
+ durationMs: waitMs
8740
+ });
8741
+ resolve(release);
8742
+ },
8743
+ reject: (error) => {
8744
+ cleanup();
8745
+ reject(error);
8746
+ }
8747
+ };
8748
+ onAbort = () => {
8749
+ const index = waiters.indexOf(waiter);
8750
+ if (index >= 0) waiters.splice(index, 1);
8751
+ cleanup();
8752
+ reject(createAbortError());
8753
+ };
8754
+ signal === null || signal === void 0 || signal.addEventListener("abort", onAbort, { once: true });
8755
+ waiters.push(waiter);
8756
+ traceQueueState({
8757
+ type: "queue-depth",
8758
+ id: key,
8759
+ key
8760
+ });
8761
+ });
8762
+ }
8763
+ async function cached(key, signal, render, estimateBytes = () => 0, cachedOptions = {}) {
8764
+ throwIfAborted(signal);
8765
+ const cachedValue = getCache(key);
8766
+ if (cachedValue !== CACHE_MISS) return cachedValue;
8767
+ traceQueueState({
8768
+ type: "cache-miss",
8769
+ id: key,
8770
+ key
8771
+ });
8772
+ const shouldDedupeInflight = cachedOptions.dedupeInflight ?? dedupeInflight;
8773
+ const existing = shouldDedupeInflight ? inflight.get(key) : void 0;
8774
+ if (existing) return raceAbort(existing, signal);
8775
+ let promise;
8776
+ promise = (async () => {
8777
+ let releaseSlot = null;
8778
+ let removeAbortObserver = null;
8779
+ const renderGeneration = generation;
8780
+ try {
8781
+ releaseSlot = once(await acquire(key, signal));
8782
+ removeAbortObserver = observeActiveRenderAbort(key, signal);
8783
+ throwIfAborted(signal);
8784
+ const cachedAfterWait = getCache(key);
8785
+ if (cachedAfterWait !== CACHE_MISS) return cachedAfterWait;
8786
+ const value = await render();
8787
+ throwIfAborted(signal);
8788
+ const rawBytes = Math.floor(Number(estimateBytes(value)));
8789
+ const bytes = Number.isFinite(rawBytes) && rawBytes > 0 ? rawBytes : 0;
8790
+ if (renderGeneration === generation) setCache(key, value, bytes);
8791
+ return value;
8792
+ } finally {
8793
+ removeAbortObserver === null || removeAbortObserver === void 0 || removeAbortObserver();
8794
+ releaseSlot === null || releaseSlot === void 0 || releaseSlot();
8795
+ if (shouldDedupeInflight && inflight.get(key) === promise) inflight.delete(key);
8796
+ }
8797
+ })();
8798
+ if (shouldDedupeInflight) inflight.set(key, promise);
8799
+ return raceAbort(promise, signal);
8800
+ }
8801
+ return {
8802
+ cached,
8803
+ clear() {
8804
+ generation++;
8805
+ cache.clear();
8806
+ inflight.clear();
8807
+ const pending = waiters.splice(0);
8808
+ for (const waiter of pending) waiter.reject(createAbortError());
8809
+ cacheBytes = 0;
8810
+ },
8811
+ stats() {
8812
+ return {
8813
+ active,
8814
+ waiting: waiters.length,
8815
+ cacheEntries: cache.size,
8816
+ cacheBytes
8817
+ };
8818
+ }
8819
+ };
8820
+ }
8012
8821
 
8013
8822
  //#endregion
8014
8823
  //#region src/core/ansi-palette.ts
@@ -8172,13 +8981,13 @@ const ANSI16_COLOR_NAMES = Object.freeze([
8172
8981
  "cyanBright",
8173
8982
  "whiteBright"
8174
8983
  ]);
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`;
8984
+ const ESC$1 = "\x1B[";
8985
+ const SGR_RESET = `${ESC$1}0m`;
8986
+ const SGR_BOLD = `${ESC$1}1m`;
8987
+ const SGR_DIM = `${ESC$1}2m`;
8988
+ const SGR_ITALIC = `${ESC$1}3m`;
8989
+ const SGR_UNDERLINE = `${ESC$1}4m`;
8990
+ const SGR_INVERSE = `${ESC$1}7m`;
8182
8991
  function toAnsi8ColorName(name) {
8183
8992
  switch (name) {
8184
8993
  case "black":
@@ -8238,32 +9047,32 @@ const ANSI16_BG_CODE = Object.freeze({
8238
9047
  function ansi8FgOpen(name) {
8239
9048
  const base = toAnsi8ColorName(name);
8240
9049
  const idx = ANSI8_COLOR_NAMES.indexOf(base);
8241
- return `${ESC}${30 + (idx < 0 ? 7 : idx)}m`;
9050
+ return `${ESC$1}${30 + (idx < 0 ? 7 : idx)}m`;
8242
9051
  }
8243
9052
  function ansi8BgOpen(name) {
8244
9053
  const base = toAnsi8ColorName(name);
8245
9054
  const idx = ANSI8_COLOR_NAMES.indexOf(base);
8246
- return `${ESC}${40 + (idx < 0 ? 0 : idx)}m`;
9055
+ return `${ESC$1}${40 + (idx < 0 ? 0 : idx)}m`;
8247
9056
  }
8248
9057
  function ansi16FgOpen(name) {
8249
- return `${ESC}${ANSI16_FG_CODE[name]}m`;
9058
+ return `${ESC$1}${ANSI16_FG_CODE[name]}m`;
8250
9059
  }
8251
9060
  function ansi16BgOpen(name) {
8252
- return `${ESC}${ANSI16_BG_CODE[name]}m`;
9061
+ return `${ESC$1}${ANSI16_BG_CODE[name]}m`;
8253
9062
  }
8254
9063
  function ansi256FgOpen(index) {
8255
9064
  const n = clampAnsiIndex(index);
8256
- return `${ESC}38;5;${n}m`;
9065
+ return `${ESC$1}38;5;${n}m`;
8257
9066
  }
8258
9067
  function ansi256BgOpen(index) {
8259
9068
  const n = clampAnsiIndex(index);
8260
- return `${ESC}48;5;${n}m`;
9069
+ return `${ESC$1}48;5;${n}m`;
8261
9070
  }
8262
9071
  function truecolorFgOpen(rgb) {
8263
- return `${ESC}38;2;${clampByte(rgb.r)};${clampByte(rgb.g)};${clampByte(rgb.b)}m`;
9072
+ return `${ESC$1}38;2;${clampByte(rgb.r)};${clampByte(rgb.g)};${clampByte(rgb.b)}m`;
8264
9073
  }
8265
9074
  function truecolorBgOpen(rgb) {
8266
- return `${ESC}48;2;${clampByte(rgb.r)};${clampByte(rgb.g)};${clampByte(rgb.b)}m`;
9075
+ return `${ESC$1}48;2;${clampByte(rgb.r)};${clampByte(rgb.g)};${clampByte(rgb.b)}m`;
8267
9076
  }
8268
9077
  function ansi256ToRgb(index) {
8269
9078
  const n = clampAnsiIndex(index);
@@ -8417,6 +9226,623 @@ function sanitizeTerminalHref(value, options = {}) {
8417
9226
  return null;
8418
9227
  }
8419
9228
 
9229
+ //#endregion
9230
+ //#region src/renderer/terminal-graphics.ts
9231
+ const TERMINAL_GRAPHICS_PROTOCOL_PRIORITY = [
9232
+ "kitty",
9233
+ "iterm2",
9234
+ "sixel"
9235
+ ];
9236
+ const ESC = "\x1B";
9237
+ const BEL = "\x07";
9238
+ const ST = `${ESC}\\`;
9239
+ const ESC_RE = "\\u001B";
9240
+ const BEL_RE = "\\u0007";
9241
+ const CSI_RE = new RegExp(`${ESC_RE}\\[[0-?]*[ -/]*[@-~]`, "g");
9242
+ const OSC_RE = new RegExp(`${ESC_RE}\\][\\s\\S]*?(?:${BEL_RE}|${ESC_RE}\\\\)`, "g");
9243
+ const DCS_RE = new RegExp(`${ESC_RE}P[\\s\\S]*?${ESC_RE}\\\\`, "g");
9244
+ const APC_RE = new RegExp(`${ESC_RE}_[\\s\\S]*?${ESC_RE}\\\\`, "g");
9245
+ const C1_CSI_RE = new RegExp("\\u009B[0-?]*[ -/]*[@-~]", "g");
9246
+ const C1_OSC_RE = new RegExp(`\\u009D[\\s\\S]*?(?:${BEL_RE}|\\u009C)`, "g");
9247
+ const C1_DCS_RE = new RegExp("\\u0090[\\s\\S]*?\\u009C", "g");
9248
+ const C1_APC_RE = new RegExp("\\u009F[\\s\\S]*?\\u009C", "g");
9249
+ const MAX_TERMINAL_GRAPHICS_SEQUENCE_CHARS = 16 * 1024 * 1024;
9250
+ const MAX_TERMINAL_GRAPHICS_FALLBACK_CHARS = 16384;
9251
+ const MAX_TERMINAL_GRAPHIC_CELLS = 1e4;
9252
+ const TERMINAL_GRAPHICS_REGISTRY = Symbol.for("@simon_he/vue-tui:v1:terminal-graphics");
9253
+ const terminalGraphicsGlobal = globalThis;
9254
+ const terminalGraphicsRegistry = terminalGraphicsGlobal[TERMINAL_GRAPHICS_REGISTRY] ?? {
9255
+ outputs: new WeakMap(),
9256
+ outputVersions: new WeakMap(),
9257
+ outputListeners: new WeakMap()
9258
+ };
9259
+ terminalGraphicsGlobal[TERMINAL_GRAPHICS_REGISTRY] = terminalGraphicsRegistry;
9260
+ const terminalGraphicsOutputs = terminalGraphicsRegistry.outputs;
9261
+ const terminalGraphicsOutputVersions = terminalGraphicsRegistry.outputVersions;
9262
+ const terminalGraphicsOutputListeners = terminalGraphicsRegistry.outputListeners;
9263
+ function notifyTerminalGraphicsOutputChange(terminal) {
9264
+ const version = (terminalGraphicsOutputVersions.get(terminal) ?? 0) + 1;
9265
+ terminalGraphicsOutputVersions.set(terminal, version);
9266
+ const listeners = terminalGraphicsOutputListeners.get(terminal);
9267
+ if (!listeners) return;
9268
+ for (const listener of listeners) listener();
9269
+ }
9270
+ function registerTerminalGraphicsOutput(terminal, output) {
9271
+ const rootTerminal = getRootTerminal(terminal);
9272
+ terminalGraphicsOutputs.set(rootTerminal, output);
9273
+ notifyTerminalGraphicsOutputChange(rootTerminal);
9274
+ return () => {
9275
+ if (terminalGraphicsOutputs.get(rootTerminal) === output) {
9276
+ terminalGraphicsOutputs.delete(rootTerminal);
9277
+ notifyTerminalGraphicsOutputChange(rootTerminal);
9278
+ }
9279
+ };
9280
+ }
9281
+ function envString(env, key) {
9282
+ const value = env[key];
9283
+ return value == null ? void 0 : String(value);
9284
+ }
9285
+ function truthy(value) {
9286
+ if (value == null) return false;
9287
+ return /^(1|true|yes|on)$/i.test(String(value));
9288
+ }
9289
+ function readStdoutIsTTY() {
9290
+ var _maybeProcess$stdout;
9291
+ const maybeProcess = globalThis.process;
9292
+ return Boolean(maybeProcess === null || maybeProcess === void 0 || (_maybeProcess$stdout = maybeProcess.stdout) === null || _maybeProcess$stdout === void 0 ? void 0 : _maybeProcess$stdout.isTTY);
9293
+ }
9294
+ function readProcessEnv() {
9295
+ const maybeProcess = globalThis.process;
9296
+ return (maybeProcess === null || maybeProcess === void 0 ? void 0 : maybeProcess.env) ?? {};
9297
+ }
9298
+ function normalizeMode(value) {
9299
+ const raw = String(value ?? "auto").trim().toLowerCase();
9300
+ if (raw === "auto" || raw === "off" || raw === "none" || raw === "kitty" || raw === "iterm2" || raw === "sixel" || raw === "unicode") return raw === "off" ? "none" : raw;
9301
+ return "auto";
9302
+ }
9303
+ function isTerminalGraphicsProtocol(value) {
9304
+ return value === "kitty" || value === "iterm2" || value === "sixel";
9305
+ }
9306
+ function uniqueProtocols(protocols) {
9307
+ return [...new Set(protocols)];
9308
+ }
9309
+ function pickBestTerminalGraphicsProtocol(protocols) {
9310
+ const available = new Set(protocols);
9311
+ for (const protocol of TERMINAL_GRAPHICS_PROTOCOL_PRIORITY) if (available.has(protocol)) return protocol;
9312
+ return null;
9313
+ }
9314
+ function detectCandidates(env) {
9315
+ const candidates = [];
9316
+ const term = String(envString(env, "TERM") ?? "").toLowerCase();
9317
+ const termProgram = String(envString(env, "TERM_PROGRAM") ?? "").toLowerCase();
9318
+ const termProgramVersion = String(envString(env, "TERM_PROGRAM_VERSION") ?? "").toLowerCase();
9319
+ if (envString(env, "KITTY_WINDOW_ID") || envString(env, "GHOSTTY_RESOURCES_DIR") || /(?:^|-)kitty(?:-|$)/i.test(term) || term.includes("ghostty") || termProgram.includes("ghostty")) candidates.push("kitty");
9320
+ if (termProgram.includes("iterm") || termProgram.includes("wezterm") || envString(env, "WEZTERM_PANE") || envString(env, "WEZTERM_EXECUTABLE")) candidates.push("iterm2");
9321
+ 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");
9322
+ return uniqueProtocols(candidates);
9323
+ }
9324
+ function capabilitiesFor(protocol, input) {
9325
+ const supported = protocol === "kitty" || protocol === "iterm2" || protocol === "sixel";
9326
+ const candidates = uniqueProtocols([...input.candidates ?? (supported ? [protocol] : [])]);
9327
+ return {
9328
+ supported,
9329
+ kitty: protocol === "kitty" || candidates.includes("kitty"),
9330
+ iterm2: protocol === "iterm2" || candidates.includes("iterm2"),
9331
+ sixel: protocol === "sixel" || candidates.includes("sixel"),
9332
+ preferredProtocol: supported ? protocol : null,
9333
+ protocol,
9334
+ candidates,
9335
+ stdoutIsTTY: input.stdoutIsTTY,
9336
+ insideTmux: input.insideTmux,
9337
+ insideScreen: input.insideScreen,
9338
+ insideZellij: input.insideZellij,
9339
+ multiplexer: input.multiplexer,
9340
+ passthrough: input.passthrough,
9341
+ forced: input.forced,
9342
+ reason: input.reason
9343
+ };
9344
+ }
9345
+ function detectTerminalGraphicsCapabilities(options = {}) {
9346
+ const env = options.env ?? readProcessEnv();
9347
+ const stdoutIsTTY = options.stdoutIsTTY ?? options.isTTY ?? readStdoutIsTTY();
9348
+ const term = String(envString(env, "TERM") ?? "").toLowerCase();
9349
+ const insideTmux = Boolean(envString(env, "TMUX")) || term.includes("tmux");
9350
+ const insideScreen = Boolean(envString(env, "STY")) || term.startsWith("screen");
9351
+ const insideZellij = Boolean(envString(env, "ZELLIJ") || envString(env, "ZELLIJ_SESSION_NAME")) || term.includes("zellij");
9352
+ const multiplexer = insideTmux ? "tmux" : insideScreen ? "screen" : insideZellij ? "zellij" : null;
9353
+ const passthroughRequested = options.passthrough ?? (truthy(envString(env, "VUE_TUI_TERMINAL_GRAPHICS_PASSTHROUGH")) || truthy(envString(env, "VUE_TUI_GRAPHICS_TMUX_PASSTHROUGH")));
9354
+ const passthroughSupported = !multiplexer || multiplexer === "tmux";
9355
+ const passthrough = passthroughRequested && passthroughSupported;
9356
+ const protocolOptionProvided = options.protocol != null;
9357
+ const mode = normalizeMode(options.protocol ?? envString(env, "VUE_TUI_TERMINAL_GRAPHICS") ?? envString(env, "VUE_TUI_GRAPHICS_PROTOCOL"));
9358
+ const forceFromOption = options.force === true;
9359
+ const force = options.force ?? truthy(envString(env, "VUE_TUI_GRAPHICS_FORCE"));
9360
+ const base = {
9361
+ stdoutIsTTY,
9362
+ insideTmux,
9363
+ insideScreen,
9364
+ insideZellij,
9365
+ multiplexer,
9366
+ passthrough,
9367
+ forced: force
9368
+ };
9369
+ if (mode === "none") return capabilitiesFor("none", {
9370
+ ...base,
9371
+ reason: protocolOptionProvided ? "disabled-by-option" : "disabled-by-env"
9372
+ });
9373
+ if (!stdoutIsTTY && !force) return capabilitiesFor("unicode", {
9374
+ ...base,
9375
+ reason: "stdout-is-not-tty"
9376
+ });
9377
+ if (truthy(envString(env, "CI")) && !force) return capabilitiesFor("unicode", {
9378
+ ...base,
9379
+ reason: "ci"
9380
+ });
9381
+ if (multiplexer && passthroughRequested && !passthroughSupported && !force) return capabilitiesFor("unicode", {
9382
+ ...base,
9383
+ reason: `${multiplexer}-passthrough-not-implemented`
9384
+ });
9385
+ if (multiplexer && !passthrough && !force) return capabilitiesFor("unicode", {
9386
+ ...base,
9387
+ reason: `${multiplexer}-without-passthrough`
9388
+ });
9389
+ if (mode === "kitty" || mode === "iterm2" || mode === "sixel") return capabilitiesFor(mode, {
9390
+ ...base,
9391
+ candidates: [mode],
9392
+ reason: force ? forceFromOption ? "forced-by-option" : "forced-by-env" : protocolOptionProvided ? "selected-by-option" : "selected-by-env"
9393
+ });
9394
+ if (mode === "unicode") return capabilitiesFor("unicode", {
9395
+ ...base,
9396
+ reason: protocolOptionProvided ? "unicode-forced-by-option" : "unicode-forced-by-env"
9397
+ });
9398
+ const candidates = detectCandidates(env);
9399
+ const preferredProtocol = pickBestTerminalGraphicsProtocol(candidates);
9400
+ const protocol = preferredProtocol ?? "unicode";
9401
+ return capabilitiesFor(protocol, {
9402
+ ...base,
9403
+ candidates,
9404
+ reason: protocol === "unicode" ? "no-supported-graphics-protocol-detected" : "auto-detected"
9405
+ });
9406
+ }
9407
+ function hashTerminalGraphicsString(input) {
9408
+ let hash = 2166136261;
9409
+ for (let index = 0; index < input.length; index += 1) {
9410
+ hash ^= input.charCodeAt(index);
9411
+ hash = Math.imul(hash, 16777619);
9412
+ }
9413
+ return (hash >>> 0).toString(36);
9414
+ }
9415
+ function stableId(input) {
9416
+ return `tg_${hashTerminalGraphicsString(input)}`;
9417
+ }
9418
+ function positiveGraphicInt(value, max) {
9419
+ const n = Math.trunc(Number(value));
9420
+ if (!Number.isFinite(n) || n <= 0) return null;
9421
+ return Math.min(max, n);
9422
+ }
9423
+ function normalizeTerminalGraphicSize(width, height) {
9424
+ const w = positiveGraphicInt(width, MAX_TERMINAL_GRAPHIC_CELLS);
9425
+ const h$1 = positiveGraphicInt(height, MAX_TERMINAL_GRAPHIC_CELLS);
9426
+ if (w == null || h$1 == null) return null;
9427
+ if (w * h$1 > MAX_TERMINAL_GRAPHIC_CELLS) return null;
9428
+ return {
9429
+ width: w,
9430
+ height: h$1
9431
+ };
9432
+ }
9433
+ function withoutNestedEscapes(body) {
9434
+ return !body.includes(ESC) && !body.includes(BEL);
9435
+ }
9436
+ function isBase64ish(value) {
9437
+ return /^[A-Za-z0-9+/=]*$/.test(value);
9438
+ }
9439
+ function parseKittyControls(raw) {
9440
+ const out = new Map();
9441
+ if (!/^[A-Za-z0-9_,=+.\-:]*$/.test(raw)) return null;
9442
+ for (const part of raw.split(",")) {
9443
+ if (!part) continue;
9444
+ const eq = part.indexOf("=");
9445
+ if (eq <= 0) return null;
9446
+ const key = part.slice(0, eq);
9447
+ const value = part.slice(eq + 1);
9448
+ if (!/^[A-Za-z]$/.test(key)) return null;
9449
+ if (!/^[A-Za-z0-9+.\-:]*$/.test(value)) return null;
9450
+ out.set(key, value);
9451
+ }
9452
+ return out;
9453
+ }
9454
+ function parseKittyPackets(sequence) {
9455
+ const packets = [];
9456
+ let index = 0;
9457
+ while (index < sequence.length) {
9458
+ if (!sequence.startsWith(`${ESC}_G`, index)) return null;
9459
+ const end = sequence.indexOf(ST, index + 3);
9460
+ if (end < 0) return null;
9461
+ const body = sequence.slice(index + 3, end);
9462
+ if (!withoutNestedEscapes(body)) return null;
9463
+ const semicolon = body.indexOf(";");
9464
+ const rawControls = semicolon >= 0 ? body.slice(0, semicolon) : body;
9465
+ const payload = semicolon >= 0 ? body.slice(semicolon + 1) : "";
9466
+ const controls = parseKittyControls(rawControls);
9467
+ if (!controls) return null;
9468
+ if (payload && !isBase64ish(payload)) return null;
9469
+ packets.push({
9470
+ controls,
9471
+ payload
9472
+ });
9473
+ index = end + ST.length;
9474
+ }
9475
+ return packets.length ? packets : null;
9476
+ }
9477
+ const KITTY_DRAW_FIRST_CONTROL_KEYS = new Set([
9478
+ "a",
9479
+ "f",
9480
+ "q",
9481
+ "C",
9482
+ "i",
9483
+ "I",
9484
+ "p",
9485
+ "c",
9486
+ "r",
9487
+ "x",
9488
+ "y",
9489
+ "w",
9490
+ "h",
9491
+ "z",
9492
+ "m",
9493
+ "t"
9494
+ ]);
9495
+ const KITTY_DRAW_CONTINUATION_CONTROL_KEYS = new Set([
9496
+ "a",
9497
+ "q",
9498
+ "m"
9499
+ ]);
9500
+ const KITTY_CLEAR_CONTROL_KEYS = new Set([
9501
+ "a",
9502
+ "d",
9503
+ "i",
9504
+ "I",
9505
+ "p",
9506
+ "q"
9507
+ ]);
9508
+ function kittyControlsUseOnly(controls, allowed) {
9509
+ for (const key of controls.keys()) if (!allowed.has(key)) return false;
9510
+ return true;
9511
+ }
9512
+ const KITTY_UINT32_MAX = 4294967295;
9513
+ const KITTY_INT32_MIN = -2147483648;
9514
+ const KITTY_INT32_MAX = 2147483647;
9515
+ function kittyControlIsUint32(controls, key, min = 0) {
9516
+ const value = controls.get(key);
9517
+ if (value == null) return true;
9518
+ if (!/^\d+$/.test(value)) return false;
9519
+ const n = Number(value);
9520
+ return Number.isSafeInteger(n) && n >= min && n <= KITTY_UINT32_MAX;
9521
+ }
9522
+ function kittyControlIsInt32(controls, key) {
9523
+ const value = controls.get(key);
9524
+ if (value == null) return true;
9525
+ if (!/^-?\d+$/.test(value)) return false;
9526
+ const n = Number(value);
9527
+ return Number.isSafeInteger(n) && n >= KITTY_INT32_MIN && n <= KITTY_INT32_MAX;
9528
+ }
9529
+ function kittyControlPositiveInt(controls, key, fallback) {
9530
+ const value = controls.get(key);
9531
+ if (value == null) return fallback;
9532
+ if (!/^\d+$/.test(value)) return null;
9533
+ const n = Number(value);
9534
+ return Number.isSafeInteger(n) && n >= 1 ? n : null;
9535
+ }
9536
+ function kittyControlIsTerminalGraphicCellsBounded(controls) {
9537
+ const columns = kittyControlPositiveInt(controls, "c", 1);
9538
+ const rows = kittyControlPositiveInt(controls, "r", 1);
9539
+ return columns != null && rows != null && columns * rows <= MAX_TERMINAL_GRAPHIC_CELLS;
9540
+ }
9541
+ function validateKittySharedSafeControls(controls) {
9542
+ const q = controls.get("q");
9543
+ if (q != null && !/^[0-2]$/.test(q)) return false;
9544
+ const cursorMove = controls.get("C");
9545
+ if (cursorMove != null && !/^[01]$/.test(cursorMove)) return false;
9546
+ for (const key of [
9547
+ "i",
9548
+ "I",
9549
+ "p"
9550
+ ]) if (!kittyControlIsUint32(controls, key)) return false;
9551
+ for (const key of ["c", "r"]) if (!kittyControlIsUint32(controls, key, 1)) return false;
9552
+ if (!kittyControlIsTerminalGraphicCellsBounded(controls)) return false;
9553
+ for (const key of ["x", "y"]) if (!kittyControlIsUint32(controls, key)) return false;
9554
+ for (const key of ["w", "h"]) if (!kittyControlIsUint32(controls, key, 1)) return false;
9555
+ if (!kittyControlIsInt32(controls, "z")) return false;
9556
+ const transmissionMedium = controls.get("t");
9557
+ if (transmissionMedium != null && transmissionMedium !== "d") return false;
9558
+ return true;
9559
+ }
9560
+ function validateKittySequence(sequence, op = "draw") {
9561
+ const packets = parseKittyPackets(sequence);
9562
+ if (!packets) return false;
9563
+ if (op === "clear") {
9564
+ if (packets.length !== 1) return false;
9565
+ const packet = packets[0];
9566
+ if (!kittyControlsUseOnly(packet.controls, KITTY_CLEAR_CONTROL_KEYS)) return false;
9567
+ if (!validateKittySharedSafeControls(packet.controls)) return false;
9568
+ if (packet.payload) return false;
9569
+ if (packet.controls.get("a") !== "d") return false;
9570
+ const d = packet.controls.get("d");
9571
+ return d == null || /^[aAcCiI]$/.test(d);
9572
+ }
9573
+ const first = packets[0];
9574
+ if (!kittyControlsUseOnly(first.controls, KITTY_DRAW_FIRST_CONTROL_KEYS)) return false;
9575
+ if (!validateKittySharedSafeControls(first.controls)) return false;
9576
+ if (first.controls.get("a") === "p") {
9577
+ if (packets.length !== 1) return false;
9578
+ if (first.payload) return false;
9579
+ if (!first.controls.has("i") && !first.controls.has("I")) return false;
9580
+ return true;
9581
+ }
9582
+ if (first.controls.get("a") !== "T") return false;
9583
+ if (!first.payload) return false;
9584
+ const f = first.controls.get("f");
9585
+ if (f != null && f !== "24" && f !== "32" && f !== "100") return false;
9586
+ const firstMore = first.controls.get("m");
9587
+ if (firstMore != null && firstMore !== "0" && firstMore !== "1") return false;
9588
+ if (packets.length > 1 && firstMore !== "1") return false;
9589
+ for (let i = 1; i < packets.length; i++) {
9590
+ const packet = packets[i];
9591
+ if (!kittyControlsUseOnly(packet.controls, KITTY_DRAW_CONTINUATION_CONTROL_KEYS)) return false;
9592
+ if (!validateKittySharedSafeControls(packet.controls)) return false;
9593
+ const action = packet.controls.get("a");
9594
+ if (action != null && action !== "T") return false;
9595
+ const m = packet.controls.get("m");
9596
+ if (m !== "0" && m !== "1") return false;
9597
+ if (!packet.payload && !(i === packets.length - 1 && m === "0")) return false;
9598
+ }
9599
+ if (packets.length > 1 && packets[packets.length - 1].controls.get("m") === "1") return false;
9600
+ return true;
9601
+ }
9602
+ function validateIterm2Dimension(value) {
9603
+ return /^auto$/i.test(value) || /^(?:[1-9]\d{0,4})(?:px|%)?$/.test(value);
9604
+ }
9605
+ function validateIterm2Params(params) {
9606
+ const seen = new Set();
9607
+ let inline = false;
9608
+ for (const part of params.split(";").filter(Boolean)) {
9609
+ const eq = part.indexOf("=");
9610
+ if (eq <= 0) return false;
9611
+ const key = part.slice(0, eq);
9612
+ const value = part.slice(eq + 1);
9613
+ if (seen.has(key)) return false;
9614
+ seen.add(key);
9615
+ switch (key) {
9616
+ case "inline":
9617
+ if (value !== "1") return false;
9618
+ inline = true;
9619
+ break;
9620
+ case "width":
9621
+ case "height":
9622
+ if (!validateIterm2Dimension(value)) return false;
9623
+ break;
9624
+ case "preserveAspectRatio":
9625
+ case "doNotMoveCursor":
9626
+ if (!/^[01]$/.test(value)) return false;
9627
+ break;
9628
+ default: return false;
9629
+ }
9630
+ }
9631
+ return inline;
9632
+ }
9633
+ function validateIterm2Sequence(sequence, op = "draw") {
9634
+ if (op === "clear") return false;
9635
+ let index = 0;
9636
+ while (index < sequence.length) {
9637
+ const prefix = `${ESC}]1337;File=`;
9638
+ if (!sequence.startsWith(prefix, index)) return false;
9639
+ const belEnd = sequence.indexOf(BEL, index + prefix.length);
9640
+ const stEnd = sequence.indexOf(ST, index + prefix.length);
9641
+ const end = belEnd >= 0 && stEnd >= 0 ? Math.min(belEnd, stEnd) : belEnd >= 0 ? belEnd : stEnd;
9642
+ if (end < 0) return false;
9643
+ const body = sequence.slice(index + `${ESC}]1337;`.length, end);
9644
+ if (!body.startsWith("File=")) return false;
9645
+ if (!withoutNestedEscapes(body)) return false;
9646
+ const colon = body.indexOf(":");
9647
+ if (colon < 0) return false;
9648
+ const params = body.slice(5, colon);
9649
+ const payload = body.slice(colon + 1);
9650
+ if (!/^[A-Za-z0-9_=;,.%+\-:]*$/.test(params)) return false;
9651
+ if (!payload || !isBase64ish(payload)) return false;
9652
+ if (!validateIterm2Params(params)) return false;
9653
+ index = end + (end === stEnd ? ST.length : BEL.length);
9654
+ }
9655
+ return sequence.length > 0;
9656
+ }
9657
+ function validateSixelSequence(sequence, op = "draw") {
9658
+ if (op === "clear") return false;
9659
+ let index = 0;
9660
+ while (index < sequence.length) {
9661
+ if (!sequence.startsWith(`${ESC}P`, index)) return false;
9662
+ const end = sequence.indexOf(ST, index + 2);
9663
+ if (end < 0) return false;
9664
+ const body = sequence.slice(index + 2, end);
9665
+ if (!withoutNestedEscapes(body)) return false;
9666
+ for (let i = 0; i < body.length; i++) {
9667
+ const code = body.charCodeAt(i);
9668
+ if (code < 32 || code > 126) return false;
9669
+ }
9670
+ const q = body.indexOf("q");
9671
+ if (q < 0 || q > 32) return false;
9672
+ const introducer = body.slice(0, q);
9673
+ if (!/^[0-9;?]*$/.test(introducer)) return false;
9674
+ const data = body.slice(q + 1);
9675
+ if (!data.length) return false;
9676
+ index = end + ST.length;
9677
+ }
9678
+ return sequence.length > 0;
9679
+ }
9680
+ function stripTerminalControlChars(value) {
9681
+ let out = "";
9682
+ for (let index = 0; index < value.length; index++) {
9683
+ const code = value.charCodeAt(index);
9684
+ if (code <= 8 || code === 11 || code === 12 || code >= 14 && code <= 31 || code === 127 || code >= 128 && code <= 159) continue;
9685
+ out += value[index];
9686
+ }
9687
+ return out;
9688
+ }
9689
+ function sanitizeTerminalFallbackText(text) {
9690
+ 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);
9691
+ }
9692
+ function validateTerminalGraphicFrame(frame) {
9693
+ const startedAt = nowTerminalGraphicTraceTime();
9694
+ const protocol = isTerminalGraphicsProtocol(frame.protocol) ? frame.protocol : void 0;
9695
+ const recordValidation = () => {
9696
+ recordTerminalGraphicTrace({
9697
+ type: "validate-end",
9698
+ id: frame.id ?? "",
9699
+ protocol,
9700
+ durationMs: nowTerminalGraphicTraceTime() - startedAt,
9701
+ bytes: typeof frame.sequence === "string" ? frame.sequence.length : void 0
9702
+ });
9703
+ };
9704
+ if (!protocol) {
9705
+ recordValidation();
9706
+ return null;
9707
+ }
9708
+ const sequence = String(frame.sequence ?? "");
9709
+ if (sequence.length <= 0 || sequence.length > MAX_TERMINAL_GRAPHICS_SEQUENCE_CHARS) {
9710
+ recordValidation();
9711
+ return null;
9712
+ }
9713
+ const size = normalizeTerminalGraphicSize(frame.width, frame.height);
9714
+ if (!size) {
9715
+ recordValidation();
9716
+ return null;
9717
+ }
9718
+ const ok = protocol === "kitty" ? validateKittySequence(sequence, "draw") : protocol === "iterm2" ? validateIterm2Sequence(sequence, "draw") : validateSixelSequence(sequence, "draw");
9719
+ if (!ok) {
9720
+ recordValidation();
9721
+ return null;
9722
+ }
9723
+ recordValidation();
9724
+ return {
9725
+ ...frame,
9726
+ protocol,
9727
+ id: frame.id || stableId(`${protocol}:${sequence.slice(0, 4096)}`),
9728
+ sequence,
9729
+ fallbackText: sanitizeTerminalFallbackText(frame.fallbackText),
9730
+ width: size.width,
9731
+ height: size.height
9732
+ };
9733
+ }
9734
+ function isSafeTerminalGraphicsSequence(sequence, protocol, op = "draw") {
9735
+ if (!isTerminalGraphicsProtocol(protocol)) return false;
9736
+ if (!sequence || sequence.length > MAX_TERMINAL_GRAPHICS_SEQUENCE_CHARS) return false;
9737
+ return protocol === "kitty" ? validateKittySequence(sequence, op) : protocol === "iterm2" ? validateIterm2Sequence(sequence, op) : validateSixelSequence(sequence, op);
9738
+ }
9739
+ function validateTerminalGraphicsPayload(payload, capabilities) {
9740
+ if (!payload.id.trim()) return false;
9741
+ if (!Number.isFinite(payload.x) || !Number.isFinite(payload.y)) return false;
9742
+ if (!normalizeTerminalGraphicSize(payload.w ?? 1, payload.h ?? 1)) return false;
9743
+ if (!capabilities.supported) return false;
9744
+ if (!isTerminalGraphicsProtocol(payload.protocol)) return false;
9745
+ if (payload.protocol !== capabilities.preferredProtocol) return false;
9746
+ return isSafeTerminalGraphicsSequence(payload.sequence, payload.protocol, payload.op ?? "draw");
9747
+ }
9748
+ function uint32Control(key, value, min = 0) {
9749
+ if (value == null) return "";
9750
+ const n = Math.floor(value);
9751
+ return Number.isSafeInteger(n) && n >= min && n <= KITTY_UINT32_MAX ? `${key}=${n}` : "";
9752
+ }
9753
+ function int32Control(key, value) {
9754
+ if (value == null) return "";
9755
+ const n = Math.floor(value);
9756
+ return Number.isSafeInteger(n) && n >= KITTY_INT32_MIN && n <= KITTY_INT32_MAX ? `${key}=${n}` : "";
9757
+ }
9758
+ function sanitizeBase64(value) {
9759
+ const data = String(value ?? "").replace(/\s+/g, "");
9760
+ return isBase64ish(data) ? data : "";
9761
+ }
9762
+ function kittyGraphicCellControls(columns, rows) {
9763
+ if (columns == null && rows == null) return [];
9764
+ const size = normalizeTerminalGraphicSize(columns ?? 1, rows ?? 1);
9765
+ if (!size) return [];
9766
+ return [columns == null ? "" : `c=${size.width}`, rows == null ? "" : `r=${size.height}`];
9767
+ }
9768
+ function kittyGraphicSourceControls(sourceX, sourceY, sourceWidth, sourceHeight) {
9769
+ return [
9770
+ uint32Control("x", sourceX),
9771
+ uint32Control("y", sourceY),
9772
+ uint32Control("w", sourceWidth, 1),
9773
+ uint32Control("h", sourceHeight, 1)
9774
+ ];
9775
+ }
9776
+ function chunkBase64(value, chunkSize) {
9777
+ const normalizedSize = Math.max(4, Math.floor(chunkSize / 4) * 4);
9778
+ const chunks = [];
9779
+ for (let index = 0; index < value.length; index += normalizedSize) chunks.push(value.slice(index, index + normalizedSize));
9780
+ return chunks.length ? chunks : [""];
9781
+ }
9782
+ function createKittyGraphicsSequence(base64Png, options = {}) {
9783
+ const data = sanitizeBase64(base64Png);
9784
+ if (!data) return "";
9785
+ const chunks = chunkBase64(data, options.chunkSize ?? 4096);
9786
+ const baseControls = [
9787
+ "a=T",
9788
+ "f=100",
9789
+ options.quiet === false ? "" : "q=2",
9790
+ options.noCursorMove === false ? "" : "C=1",
9791
+ uint32Control("i", options.imageId),
9792
+ uint32Control("I", options.imageNumber),
9793
+ uint32Control("p", options.placementId),
9794
+ ...kittyGraphicCellControls(options.columns, options.rows),
9795
+ ...kittyGraphicSourceControls(options.sourceX, options.sourceY, options.sourceWidth, options.sourceHeight),
9796
+ int32Control("z", options.zIndex)
9797
+ ].filter(Boolean);
9798
+ return chunks.map((chunk, index) => {
9799
+ const last = index === chunks.length - 1;
9800
+ const controls = index === 0 ? [...baseControls, chunks.length > 1 ? `m=${last ? 0 : 1}` : ""] : [options.quiet === false ? "" : "q=2", `m=${last ? 0 : 1}`].filter(Boolean);
9801
+ return `${ESC}_G${controls.filter(Boolean).join(",")};${chunk}${ST}`;
9802
+ }).join("");
9803
+ }
9804
+ function createKittyDeleteGraphicsSequence(options = {}) {
9805
+ const controls = ["a=d"];
9806
+ if (options.allVisible) controls.push(`d=${options.freeImageData ? "A" : "a"}`);
9807
+ else if (options.imageId != null) {
9808
+ const imageId = uint32Control("i", options.imageId);
9809
+ const placementId = uint32Control("p", options.placementId);
9810
+ if (!imageId || options.placementId != null && !placementId) return "";
9811
+ controls.push(`d=${options.freeImageData ? "I" : "i"}`);
9812
+ controls.push(imageId);
9813
+ controls.push(placementId);
9814
+ } else if (options.currentCell) controls.push(`d=${options.freeImageData ? "C" : "c"}`);
9815
+ else return "";
9816
+ if (options.quiet !== false) controls.push("q=2");
9817
+ return `${ESC}_G${controls.filter(Boolean).join(",")}${ST}`;
9818
+ }
9819
+ function createIterm2InlineImageSequence(base64Data, options = {}) {
9820
+ const data = sanitizeBase64(base64Data);
9821
+ if (!data) return "";
9822
+ const params = [
9823
+ "inline=1",
9824
+ options.width != null ? `width=${formatItermDimension(options.width)}` : "",
9825
+ options.height != null ? `height=${formatItermDimension(options.height)}` : "",
9826
+ `preserveAspectRatio=${options.preserveAspectRatio === false ? 0 : 1}`,
9827
+ options.doNotMoveCursor === false ? "" : "doNotMoveCursor=1"
9828
+ ].filter(Boolean);
9829
+ return `${ESC}]1337;File=${params.join(";")}:${data}${BEL}`;
9830
+ }
9831
+ function formatItermDimension(value) {
9832
+ if (typeof value === "number") {
9833
+ const n = Math.floor(value);
9834
+ if (!Number.isFinite(n)) return "auto";
9835
+ return String(Math.min(99999, Math.max(1, n)));
9836
+ }
9837
+ const raw = String(value ?? "").trim();
9838
+ return validateIterm2Dimension(raw) ? raw : "auto";
9839
+ }
9840
+ function wrapTerminalGraphicsForMultiplexer(sequence, capabilities) {
9841
+ if (!capabilities.passthrough) return sequence;
9842
+ if (capabilities.multiplexer === "tmux") return `${ESC}Ptmux;${sequence.split(ESC).join(`${ESC}${ESC}`)}${ST}`;
9843
+ return sequence;
9844
+ }
9845
+
8420
9846
  //#endregion
8421
9847
  //#region src/renderer/cli/stdout-renderer.ts
8422
9848
  let debugLog = null;
@@ -8431,7 +9857,27 @@ const SYNC_START = "\x1B[?2026h";
8431
9857
  const SYNC_END = "\x1B[?2026l";
8432
9858
  const OSC8_OPEN = (href) => `\u001B]8;;${href}\u0007`;
8433
9859
  const OSC8_CLOSE = "\x1B]8;;\x07";
9860
+ const CURSOR_SAVE = "\x1B7";
9861
+ const CURSOR_RESTORE = "\x1B8";
9862
+ const CURSOR_HOME = "\x1B[H";
8434
9863
  const stdoutFingerprintOwners = new WeakMap();
9864
+ function isTerminalGraphicsCapabilities(value) {
9865
+ return typeof value === "object" && value !== null && "supported" in value && "protocol" in value && "preferredProtocol" in value && "candidates" in value;
9866
+ }
9867
+ function resolveTerminalGraphicsCapabilities(options, env, outputIsTTY) {
9868
+ const terminalGraphics = options === null || options === void 0 ? void 0 : options.terminalGraphics;
9869
+ if (terminalGraphics === false) return detectTerminalGraphicsCapabilities({
9870
+ env,
9871
+ isTTY: outputIsTTY,
9872
+ protocol: "none"
9873
+ });
9874
+ if (isTerminalGraphicsCapabilities(terminalGraphics)) return terminalGraphics;
9875
+ return detectTerminalGraphicsCapabilities({
9876
+ env,
9877
+ isTTY: outputIsTTY,
9878
+ ...terminalGraphics ?? {}
9879
+ });
9880
+ }
8435
9881
  function createStdoutRenderer(terminal, options) {
8436
9882
  const env = (process === null || process === void 0 ? void 0 : process.env) ?? {};
8437
9883
  if (shouldInstallFileWriters(env)) installNodeFileWriters();
@@ -8449,6 +9895,7 @@ function createStdoutRenderer(terminal, options) {
8449
9895
  const clear = (options === null || options === void 0 ? void 0 : options.clear) ?? true;
8450
9896
  const hideCursor = (options === null || options === void 0 ? void 0 : options.hideCursor) ?? true;
8451
9897
  const altScreen = (options === null || options === void 0 ? void 0 : options.altScreen) ?? outputIsTTY;
9898
+ const keepLineWrapDisabled = altScreen && outputIsTTY;
8452
9899
  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
9900
  let palette = (options === null || options === void 0 ? void 0 : options.palette) ?? null;
8454
9901
  const trackResize = (options === null || options === void 0 ? void 0 : options.trackResize) ?? true;
@@ -8581,6 +10028,7 @@ function createStdoutRenderer(terminal, options) {
8581
10028
  let lastFrameTime = 0;
8582
10029
  let pendingRender = false;
8583
10030
  let renderTimer = null;
10031
+ let rowsOnlyResizeRepaintTimer = null;
8584
10032
  let writeEmaMs = 0;
8585
10033
  let lastCursorX = null;
8586
10034
  let lastCursorY = null;
@@ -8591,7 +10039,381 @@ function createStdoutRenderer(terminal, options) {
8591
10039
  let accumulatedDirtyMax = -1;
8592
10040
  let accumulatedDirtyRowsRequireRepaint = false;
8593
10041
  let accumulatedScrollOperations = null;
10042
+ let allowNextGraphicsOnlyRenderWithoutBaseline = false;
10043
+ let preserveNextRowsOnlyResizeBaseline = false;
10044
+ let deferGraphicsFlushUntilResizeCommit = false;
10045
+ const graphicsCapabilities = resolveTerminalGraphicsCapabilities(options, env, outputIsTTY);
10046
+ const pendingGraphics = new Map();
10047
+ const pendingGraphicClears = new Set();
10048
+ const activeGraphics = new Map();
10049
+ const pendingGraphicSignatures = new Map();
10050
+ const activeGraphicSignatures = new Map();
10051
+ const retainedGraphics = new Map();
10052
+ const terminalResizeDirtyGraphics = new Set();
10053
+ let nextGraphicsOrder = 0;
10054
+ let terminalGraphicsFlushTimer = null;
10055
+ const hasPendingTerminalGraphics = () => pendingGraphics.size > 0 || pendingGraphicClears.size > 0;
10056
+ function canUseTerminalGraphicsProtocol(protocol, capabilities) {
10057
+ return capabilities.supported && isTerminalGraphicsProtocol(protocol) && protocol === capabilities.preferredProtocol;
10058
+ }
10059
+ function requestTerminalGraphicsFlush(defer = false) {
10060
+ if (disposed) return;
10061
+ if (deferGraphicsFlushUntilResizeCommit) {
10062
+ if (isDebugEnabled()) getDebugLog().render("terminal graphics flush deferred until resize commit");
10063
+ return;
10064
+ }
10065
+ if (!defer) {
10066
+ render([], false);
10067
+ return;
10068
+ }
10069
+ if (terminalGraphicsFlushTimer) return;
10070
+ terminalGraphicsFlushTimer = setTimeout(() => {
10071
+ terminalGraphicsFlushTimer = null;
10072
+ if (!disposed) render([], false);
10073
+ }, 0);
10074
+ }
10075
+ function terminalGraphicsPayloadSignature(payload) {
10076
+ return [
10077
+ payload.id,
10078
+ payload.protocol,
10079
+ payload.x,
10080
+ payload.y,
10081
+ payload.w ?? "",
10082
+ payload.h ?? "",
10083
+ hashTerminalGraphicsString(payload.sequence),
10084
+ hashTerminalGraphicsString(payload.resizeSequence ?? ""),
10085
+ hashTerminalGraphicsString(payload.clearSequence ?? "")
10086
+ ].join(":");
10087
+ }
10088
+ function kittyClearSequenceKeepsImageData(sequence) {
10089
+ let index = 0;
10090
+ while (index < sequence.length) {
10091
+ const start = sequence.indexOf("\x1B_G", index);
10092
+ if (start < 0) return false;
10093
+ const end = sequence.indexOf("\x1B\\", start + 3);
10094
+ if (end < 0) return false;
10095
+ const body = sequence.slice(start + 3, end);
10096
+ const controls = body.slice(0, body.indexOf(";") < 0 ? body.length : body.indexOf(";"));
10097
+ const parts = controls.split(",");
10098
+ if (parts.includes("a=d") && parts.includes("d=i")) return true;
10099
+ index = end + 2;
10100
+ }
10101
+ return false;
10102
+ }
10103
+ function retainTerminalGraphic(id, active, signature, options$1 = {}) {
10104
+ if (!signature || active.protocol !== "kitty" || !active.resizeSequence || !active.clearSequence || !kittyClearSequenceKeepsImageData(active.clearSequence)) {
10105
+ retainedGraphics.delete(id);
10106
+ return;
10107
+ }
10108
+ retainedGraphics.set(id, {
10109
+ active,
10110
+ signature,
10111
+ autoRedrawOnResize: Boolean(options$1.autoRedrawOnResize)
10112
+ });
10113
+ }
10114
+ function kittyPlacementKey(sequence) {
10115
+ if (!sequence) return null;
10116
+ const start = sequence.indexOf("\x1B_G");
10117
+ if (start < 0) return null;
10118
+ const end = sequence.indexOf("\x1B\\", start + 3);
10119
+ if (end < 0) return null;
10120
+ const body = sequence.slice(start + 3, end);
10121
+ const controlsRaw = body.slice(0, body.indexOf(";") < 0 ? body.length : body.indexOf(";"));
10122
+ const controls = new Map();
10123
+ for (const part of controlsRaw.split(",")) {
10124
+ const eq = part.indexOf("=");
10125
+ if (eq <= 0) continue;
10126
+ controls.set(part.slice(0, eq), part.slice(eq + 1));
10127
+ }
10128
+ const imageId = controls.get("i");
10129
+ const placementId = controls.get("p");
10130
+ if (controls.get("a") !== "p" || !imageId || !placementId) return null;
10131
+ if (imageId === "0" || placementId === "0") return null;
10132
+ return `${imageId}:${placementId}`;
10133
+ }
10134
+ function canReplaceKittyPlacementWithoutClear(previous, payload) {
10135
+ if (payload.terminalResizeRedraw) return false;
10136
+ if (!payload.resizeRedraw || previous.protocol !== "kitty" || payload.protocol !== "kitty") return false;
10137
+ const nextKey = kittyPlacementKey(payload.resizeSequence ?? payload.sequence);
10138
+ if (!nextKey) return false;
10139
+ return kittyPlacementKey(previous.resizeSequence ?? previous.sequence) === nextKey && (payload.placementMoveWithoutClear || sameGraphicRect(previous, {
10140
+ x: payload.x,
10141
+ y: payload.y,
10142
+ w: payload.w ?? previous.w,
10143
+ h: payload.h ?? previous.h
10144
+ }));
10145
+ }
10146
+ function queueTerminalGraphicClear(id) {
10147
+ if (disposed) return false;
10148
+ const removedPendingDraw = pendingGraphics.delete(`${id}:draw`);
10149
+ pendingGraphicSignatures.delete(id);
10150
+ terminalResizeDirtyGraphics.delete(id);
10151
+ const active = activeGraphics.get(id);
10152
+ if (!active) {
10153
+ retainedGraphics.delete(id);
10154
+ return removedPendingDraw;
10155
+ }
10156
+ if (active.clearSequence) {
10157
+ const payload = {
10158
+ id,
10159
+ x: active.x,
10160
+ y: active.y,
10161
+ w: active.w,
10162
+ h: active.h,
10163
+ protocol: active.protocol,
10164
+ sequence: active.clearSequence,
10165
+ op: "clear",
10166
+ order: nextGraphicsOrder++
10167
+ };
10168
+ if (validateTerminalGraphicsPayload(payload, graphicsCapabilities)) {
10169
+ pendingGraphics.set(`${id}:clear`, payload);
10170
+ recordTerminalGraphicTrace({
10171
+ type: "clear",
10172
+ id,
10173
+ protocol: payload.protocol
10174
+ });
10175
+ requestTerminalGraphicsFlush();
10176
+ return true;
10177
+ }
10178
+ }
10179
+ pendingGraphicClears.add(id);
10180
+ recordTerminalGraphicTrace({
10181
+ type: "clear",
10182
+ id
10183
+ });
10184
+ requestTerminalGraphicsFlush();
10185
+ return true;
10186
+ }
10187
+ function queueTerminalGraphicsForResizeRedraw(size) {
10188
+ if (isDebugEnabled()) getDebugLog().render(`graphics resize redraw queue: size=${size.cols}x${size.rows}, activeGraphics=${activeGraphics.size}, pendingGraphics=${pendingGraphics.size}, pendingClears=${pendingGraphicClears.size}`);
10189
+ for (const [id, active] of activeGraphics) {
10190
+ terminalResizeDirtyGraphics.add(id);
10191
+ pendingGraphics.delete(`${id}:draw`);
10192
+ const anchored = anchoredGraphicRectInViewport(active, size);
10193
+ if (anchored && !active.resizeSequence) {
10194
+ 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`);
10195
+ continue;
10196
+ }
10197
+ const signature = activeGraphicSignatures.get(id);
10198
+ const sequence = active.resizeSequence ?? active.sequence;
10199
+ pendingGraphics.set(`${id}:draw`, {
10200
+ id,
10201
+ x: active.x,
10202
+ y: active.y,
10203
+ w: active.w,
10204
+ h: active.h,
10205
+ protocol: active.protocol,
10206
+ sequence,
10207
+ resizeSequence: active.resizeSequence,
10208
+ clearSequence: active.clearSequence,
10209
+ op: "draw",
10210
+ order: nextGraphicsOrder++,
10211
+ resizeRedraw: true,
10212
+ terminalResizeRedraw: true
10213
+ });
10214
+ if (signature) pendingGraphicSignatures.set(id, signature);
10215
+ 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}`);
10216
+ }
10217
+ for (const [id, retained] of retainedGraphics) {
10218
+ if (activeGraphics.has(id) || !retained.autoRedrawOnResize) continue;
10219
+ const { active, signature } = retained;
10220
+ if (!anchoredGraphicRectInViewport(active, size)) continue;
10221
+ retainedGraphics.delete(id);
10222
+ activeGraphics.set(id, active);
10223
+ activeGraphicSignatures.set(id, signature);
10224
+ pendingGraphicClears.delete(id);
10225
+ pendingGraphics.delete(`${id}:clear`);
10226
+ pendingGraphics.set(`${id}:draw`, {
10227
+ id,
10228
+ x: active.x,
10229
+ y: active.y,
10230
+ w: active.w,
10231
+ h: active.h,
10232
+ protocol: active.protocol,
10233
+ sequence: active.resizeSequence ?? active.sequence,
10234
+ resizeSequence: active.resizeSequence,
10235
+ clearSequence: active.clearSequence,
10236
+ op: "draw",
10237
+ order: nextGraphicsOrder++,
10238
+ resizeRedraw: true,
10239
+ terminalResizeRedraw: true
10240
+ });
10241
+ pendingGraphicSignatures.set(id, signature);
10242
+ 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}`);
10243
+ }
10244
+ }
10245
+ const graphicsOutput = {
10246
+ capabilities: graphicsCapabilities,
10247
+ isActive(id) {
10248
+ if (pendingGraphicClears.has(id) || pendingGraphics.has(`${id}:clear`)) return false;
10249
+ return activeGraphics.has(id) || retainedGraphics.has(id) || pendingGraphics.has(`${id}:draw`);
10250
+ },
10251
+ queue(payload) {
10252
+ if (disposed) return false;
10253
+ const x = Math.floor(payload.x);
10254
+ const y = Math.floor(payload.y);
10255
+ const op = payload.op ?? "draw";
10256
+ const normalized = {
10257
+ ...payload,
10258
+ x,
10259
+ y,
10260
+ w: payload.w == null ? void 0 : Math.floor(payload.w),
10261
+ h: payload.h == null ? void 0 : Math.floor(payload.h),
10262
+ op,
10263
+ order: nextGraphicsOrder++
10264
+ };
10265
+ if (!normalized.id.trim()) return false;
10266
+ if (!Number.isFinite(normalized.x) || !Number.isFinite(normalized.y)) return false;
10267
+ if (!canUseTerminalGraphicsProtocol(normalized.protocol, graphicsCapabilities)) return false;
10268
+ if (op === "draw") {
10269
+ const frame = validateTerminalGraphicFrame({
10270
+ id: normalized.id,
10271
+ protocol: normalized.protocol,
10272
+ sequence: normalized.sequence,
10273
+ fallbackText: normalized.fallbackText,
10274
+ width: normalized.w ?? 1,
10275
+ height: normalized.h ?? 1
10276
+ });
10277
+ if (!frame) return false;
10278
+ if (isDebugEnabled()) {
10279
+ const size = terminal.size();
10280
+ 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}`);
10281
+ }
10282
+ if (!anchoredGraphicRectInViewport({
10283
+ x: normalized.x,
10284
+ y: normalized.y,
10285
+ w: frame.width,
10286
+ h: frame.height
10287
+ }, terminal.size())) {
10288
+ if (isDebugEnabled()) {
10289
+ const size = terminal.size();
10290
+ 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}`);
10291
+ }
10292
+ queueTerminalGraphicClear(frame.id);
10293
+ return false;
10294
+ }
10295
+ const clearSequence = normalized.clearSequence && isSafeTerminalGraphicsSequence(normalized.clearSequence, normalized.protocol, "clear") ? normalized.clearSequence : void 0;
10296
+ const resizeSequence = normalized.resizeSequence && isSafeTerminalGraphicsSequence(normalized.resizeSequence, normalized.protocol, "draw") ? normalized.resizeSequence : void 0;
10297
+ const signature = terminalGraphicsPayloadSignature({
10298
+ ...normalized,
10299
+ id: frame.id,
10300
+ w: frame.width,
10301
+ h: frame.height,
10302
+ sequence: frame.sequence,
10303
+ resizeSequence,
10304
+ clearSequence
10305
+ });
10306
+ const forceTerminalResizeRedraw = terminalResizeDirtyGraphics.has(frame.id);
10307
+ const removedPendingGraphicClear = pendingGraphics.delete(`${frame.id}:clear`);
10308
+ const removedPendingClear = pendingGraphicClears.delete(frame.id);
10309
+ const canceledPendingClear = removedPendingGraphicClear || removedPendingClear;
10310
+ if (!forceTerminalResizeRedraw && !normalized.forceDraw && !canceledPendingClear && (pendingGraphicSignatures.get(frame.id) === signature || activeGraphicSignatures.get(frame.id) === signature)) {
10311
+ recordTerminalGraphicTrace({
10312
+ type: "queue-dedupe",
10313
+ id: frame.id,
10314
+ protocol: normalized.protocol
10315
+ });
10316
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphic queue draw dedupe: id=${frame.id}, x=${normalized.x}, y=${normalized.y}, w=${frame.width}, h=${frame.height}`);
10317
+ return true;
10318
+ }
10319
+ if (!forceTerminalResizeRedraw && !normalized.forceDraw && canceledPendingClear && activeGraphicSignatures.get(frame.id) === signature) {
10320
+ recordTerminalGraphicTrace({
10321
+ type: "queue-dedupe",
10322
+ id: frame.id,
10323
+ protocol: normalized.protocol
10324
+ });
10325
+ 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}`);
10326
+ return true;
10327
+ }
10328
+ const retained = retainedGraphics.get(frame.id);
10329
+ const retainedCanReplacePlacement = retained && resizeSequence && canReplaceKittyPlacementWithoutClear(retained.active, {
10330
+ ...normalized,
10331
+ id: frame.id,
10332
+ w: frame.width,
10333
+ h: frame.height,
10334
+ sequence: resizeSequence,
10335
+ resizeSequence,
10336
+ clearSequence,
10337
+ fallbackText: frame.fallbackText,
10338
+ resizeRedraw: true,
10339
+ placementMoveWithoutClear: true
10340
+ });
10341
+ if (!canceledPendingClear && retained && resizeSequence && (retained.signature === signature || retainedCanReplacePlacement)) {
10342
+ activeGraphics.set(frame.id, retained.active);
10343
+ activeGraphicSignatures.set(frame.id, retained.signature);
10344
+ retainedGraphics.delete(frame.id);
10345
+ pendingGraphics.set(`${frame.id}:draw`, {
10346
+ ...normalized,
10347
+ id: frame.id,
10348
+ w: frame.width,
10349
+ h: frame.height,
10350
+ sequence: resizeSequence,
10351
+ resizeSequence,
10352
+ clearSequence,
10353
+ fallbackText: frame.fallbackText,
10354
+ resizeRedraw: true,
10355
+ placementMoveWithoutClear: true,
10356
+ terminalResizeRedraw: forceTerminalResizeRedraw
10357
+ });
10358
+ pendingGraphicSignatures.set(frame.id, signature);
10359
+ 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}`);
10360
+ recordTerminalGraphicTrace({
10361
+ type: "queue",
10362
+ id: frame.id,
10363
+ protocol: normalized.protocol,
10364
+ bytes: resizeSequence.length
10365
+ });
10366
+ requestTerminalGraphicsFlush(Boolean(normalized.deferFlush));
10367
+ return true;
10368
+ }
10369
+ retainedGraphics.delete(frame.id);
10370
+ pendingGraphics.set(`${frame.id}:draw`, {
10371
+ ...normalized,
10372
+ id: frame.id,
10373
+ w: frame.width,
10374
+ h: frame.height,
10375
+ sequence: frame.sequence,
10376
+ resizeSequence,
10377
+ clearSequence,
10378
+ fallbackText: frame.fallbackText,
10379
+ terminalResizeRedraw: forceTerminalResizeRedraw
10380
+ });
10381
+ pendingGraphicSignatures.set(frame.id, signature);
10382
+ 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}`);
10383
+ recordTerminalGraphicTrace({
10384
+ type: "queue",
10385
+ id: frame.id,
10386
+ protocol: normalized.protocol,
10387
+ bytes: frame.sequence.length
10388
+ });
10389
+ requestTerminalGraphicsFlush(Boolean(normalized.deferFlush));
10390
+ return true;
10391
+ }
10392
+ if (!validateTerminalGraphicsPayload(normalized, graphicsCapabilities)) return false;
10393
+ pendingGraphics.delete(`${normalized.id}:draw`);
10394
+ pendingGraphicSignatures.delete(normalized.id);
10395
+ retainedGraphics.delete(normalized.id);
10396
+ pendingGraphics.set(`${normalized.id}:clear`, {
10397
+ ...normalized,
10398
+ w: normalized.w,
10399
+ h: normalized.h
10400
+ });
10401
+ 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}`);
10402
+ recordTerminalGraphicTrace({
10403
+ type: "clear",
10404
+ id: normalized.id,
10405
+ protocol: normalized.protocol
10406
+ });
10407
+ requestTerminalGraphicsFlush(Boolean(normalized.deferFlush));
10408
+ return true;
10409
+ },
10410
+ clear(id) {
10411
+ return queueTerminalGraphicClear(id);
10412
+ }
10413
+ };
10414
+ const unregisterGraphicsOutput = registerTerminalGraphicsOutput(terminal, graphicsOutput);
8594
10415
  const MIN_FRAME_MS = outputIsTTY ? 16 : 0;
10416
+ const ROWS_ONLY_RESIZE_REPAINT_DELAY_MS = 500;
8595
10417
  function clampCellToViewport(cell, size) {
8596
10418
  const maxX = Math.max(0, Math.floor(size.cols) - 1);
8597
10419
  const maxY = Math.max(0, Math.floor(size.rows) - 1);
@@ -8604,6 +10426,92 @@ function createStdoutRenderer(terminal, options) {
8604
10426
  y
8605
10427
  };
8606
10428
  }
10429
+ function normalizeGraphicRect(rect) {
10430
+ const x0 = Math.floor(rect.x);
10431
+ const y0 = Math.floor(rect.y);
10432
+ const w0 = Math.floor(rect.w);
10433
+ const h0 = Math.floor(rect.h);
10434
+ if (!Number.isFinite(x0) || !Number.isFinite(y0) || !Number.isFinite(w0) || !Number.isFinite(h0)) return null;
10435
+ const size = normalizeTerminalGraphicSize(w0, h0);
10436
+ if (!size) return null;
10437
+ return {
10438
+ x: x0,
10439
+ y: y0,
10440
+ w: size.width,
10441
+ h: size.height
10442
+ };
10443
+ }
10444
+ function clipGraphicRectToViewport(rect, size) {
10445
+ const cols = Math.max(0, Math.floor(size.cols));
10446
+ const rows = Math.max(0, Math.floor(size.rows));
10447
+ if (cols <= 0 || rows <= 0) return null;
10448
+ const normalized = normalizeGraphicRect(rect);
10449
+ if (!normalized) return null;
10450
+ const x0 = normalized.x;
10451
+ const y0 = normalized.y;
10452
+ const w0 = normalized.w;
10453
+ const h0 = normalized.h;
10454
+ const left = Math.max(0, x0);
10455
+ const top = Math.max(0, y0);
10456
+ const right = Math.min(cols, x0 + w0);
10457
+ const bottom = Math.min(rows, y0 + h0);
10458
+ if (right <= left || bottom <= top) return null;
10459
+ return {
10460
+ x: left,
10461
+ y: top,
10462
+ w: right - left,
10463
+ h: bottom - top
10464
+ };
10465
+ }
10466
+ function anchoredGraphicRectInViewport(rect, size) {
10467
+ const cols = Math.max(0, Math.floor(size.cols));
10468
+ const rows = Math.max(0, Math.floor(size.rows));
10469
+ if (cols <= 0 || rows <= 0) return null;
10470
+ const normalized = normalizeGraphicRect(rect);
10471
+ if (!normalized) return null;
10472
+ return normalized.x >= 0 && normalized.y >= 0 && normalized.x < cols && normalized.y < rows ? normalized : null;
10473
+ }
10474
+ function clearGraphicRect(rect) {
10475
+ const blank = " ".repeat(Math.max(0, rect.w));
10476
+ let out$1 = "";
10477
+ for (let row = 0; row < rect.h; row++) out$1 += `\u001B[${rect.y + row + 1};${rect.x + 1}H${blank}`;
10478
+ return out$1;
10479
+ }
10480
+ function sameGraphicRect(a, b) {
10481
+ return Boolean(a && a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h);
10482
+ }
10483
+ function maybeWrapTerminalGraphic(sequence) {
10484
+ return wrapTerminalGraphicsForMultiplexer(sequence, graphicsCapabilities);
10485
+ }
10486
+ function kittyClearTargetsCurrentCell(sequence) {
10487
+ const prefix = "\x1B_G";
10488
+ const terminator = "\x1B\\";
10489
+ let index = 0;
10490
+ while (index < sequence.length) {
10491
+ if (!sequence.startsWith(prefix, index)) return false;
10492
+ const end = sequence.indexOf(terminator, index + prefix.length);
10493
+ if (end < 0) return false;
10494
+ const body = sequence.slice(index + prefix.length, end);
10495
+ const semicolon = body.indexOf(";");
10496
+ const controlsText = semicolon < 0 ? body : body.slice(0, semicolon);
10497
+ let action = "";
10498
+ let deleteMode = "";
10499
+ for (const part of controlsText.split(",")) {
10500
+ const eq = part.indexOf("=");
10501
+ if (eq <= 0) continue;
10502
+ const key = part.slice(0, eq);
10503
+ const value = part.slice(eq + 1);
10504
+ if (key === "a") action = value;
10505
+ else if (key === "d") deleteMode = value;
10506
+ }
10507
+ if (action === "d" && (deleteMode === "c" || deleteMode === "C")) return true;
10508
+ index = end + terminator.length;
10509
+ }
10510
+ return false;
10511
+ }
10512
+ function clearSequenceNeedsVisibleCell(sequence, protocol) {
10513
+ return protocol === "kitty" && kittyClearTargetsCurrentCell(sequence);
10514
+ }
8607
10515
  function resolveColorMode() {
8608
10516
  const opt = (options === null || options === void 0 ? void 0 : options.colorMode) ?? "auto";
8609
10517
  if (opt !== "auto") return opt;
@@ -8755,6 +10663,7 @@ function createStdoutRenderer(terminal, options) {
8755
10663
  const MAX_CELL_TEXT_IDS = 131072;
8756
10664
  let prevOverlayBlockedRows = [];
8757
10665
  let prevOverlayPartialRows = [];
10666
+ let prevOverlayVisibleSpans = new Map();
8758
10667
  const hrefIndex = new Map();
8759
10668
  let nextHrefId = 1;
8760
10669
  const MAX_HREF_IDS = 8192;
@@ -8779,6 +10688,7 @@ function createStdoutRenderer(terminal, options) {
8779
10688
  fpPrevValid = false;
8780
10689
  prevOverlayBlockedRows = [];
8781
10690
  prevOverlayPartialRows = [];
10691
+ prevOverlayVisibleSpans = new Map();
8782
10692
  }
8783
10693
  function cellFingerprintFromStyleKey(_ch, key) {
8784
10694
  return key >>> 0;
@@ -9476,12 +11386,46 @@ function createStdoutRenderer(terminal, options) {
9476
11386
  lastFrameTime = Date.now();
9477
11387
  const renderStart = performance.now();
9478
11388
  const size = terminal.size();
11389
+ const rowsOnlyResizeBaseline = preserveNextRowsOnlyResizeBaseline && fpPrevValid && fpCols === size.cols ? {
11390
+ cols: fpCols,
11391
+ rows: fpRows,
11392
+ fp: prevFP.slice(),
11393
+ hrefIds: prevHrefIds.slice(),
11394
+ textIds: prevTextIds.slice()
11395
+ } : null;
11396
+ preserveNextRowsOnlyResizeBaseline = false;
11397
+ const pendingGraphicClearsToRender = new Set(pendingGraphicClears);
11398
+ let graphicsClearsToRender = Array.from(pendingGraphicClearsToRender);
11399
+ let graphicsEntriesToRender = Array.from(pendingGraphics.entries()).sort(([, a], [, b]) => {
11400
+ const opA = a.op === "clear" ? 0 : 1;
11401
+ const opB = b.op === "clear" ? 0 : 1;
11402
+ return opA - opB || a.order - b.order;
11403
+ });
11404
+ let graphicsToRender = graphicsEntriesToRender.map(([, payload]) => payload);
11405
+ 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);
11406
+ allowNextGraphicsOnlyRenderWithoutBaseline = false;
11407
+ 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}`);
11408
+ let terminalGraphicsBlockScrollRegions = false;
9479
11409
  ensureRowEscapes(Math.max(size.rows, lastRenderedRows));
9480
11410
  ensureFingerprints(size.cols, size.rows);
9481
11411
  const bgSeq = openBg(defaultBg);
9482
11412
  const bgOnlyStyle = { bg: defaultBg };
9483
11413
  const bgKey = styleKeyFromParts({ bg: defaultBg });
9484
11414
  const blankFP = cellFingerprintFromStyleKey(" ", bgKey);
11415
+ if (rowsOnlyResizeBaseline && rowsOnlyResizeBaseline.cols === size.cols) {
11416
+ prevFP.fill(blankFP);
11417
+ prevHrefIds.fill(0);
11418
+ prevTextIds.fill(blankTextId);
11419
+ const rowsToCopy = Math.min(size.rows, rowsOnlyResizeBaseline.rows);
11420
+ for (let y = 0; y < rowsToCopy; y++) {
11421
+ const start = y * size.cols;
11422
+ const end = start + size.cols;
11423
+ prevFP.set(rowsOnlyResizeBaseline.fp.subarray(start, end), start);
11424
+ prevHrefIds.set(rowsOnlyResizeBaseline.hrefIds.subarray(start, end), start);
11425
+ prevTextIds.set(rowsOnlyResizeBaseline.textIds.subarray(start, end), start);
11426
+ }
11427
+ fpPrevValid = true;
11428
+ }
9485
11429
  let dirtySorted = true;
9486
11430
  const forceFullRender = hrefIndexResetRequiresBaseline || fingerprintInternResetRequiresBaseline;
9487
11431
  if (forceFullRender) {
@@ -9507,7 +11451,171 @@ function createStdoutRenderer(terminal, options) {
9507
11451
  if (!outOps.length) return null;
9508
11452
  return outOps;
9509
11453
  })();
11454
+ const terminalGraphicsAffectedRows = new Set();
11455
+ const terminalGraphicTextPreserveSpans = new Map();
11456
+ const addTerminalGraphicRows = (rect) => {
11457
+ if (!rect) return;
11458
+ const visible = clipGraphicRectToViewport(rect, size);
11459
+ if (!visible) return;
11460
+ for (let y = visible.y; y < visible.y + visible.h; y++) terminalGraphicsAffectedRows.add(y);
11461
+ };
11462
+ const addTerminalGraphicTextPreserveSpans = (rect) => {
11463
+ if (!rect) return;
11464
+ const visible = clipGraphicRectToViewport(rect, size);
11465
+ if (!visible) return;
11466
+ const start = Math.max(0, Math.min(size.cols, visible.x));
11467
+ const end = Math.max(start, Math.min(size.cols, visible.x + visible.w));
11468
+ if (end <= start) return;
11469
+ for (let y = visible.y; y < visible.y + visible.h; y++) {
11470
+ const spans = terminalGraphicTextPreserveSpans.get(y);
11471
+ if (spans) spans.push({
11472
+ start,
11473
+ end
11474
+ });
11475
+ else terminalGraphicTextPreserveSpans.set(y, [{
11476
+ start,
11477
+ end
11478
+ }]);
11479
+ }
11480
+ };
11481
+ const payloadRect = (payload) => ({
11482
+ x: payload.x,
11483
+ y: payload.y,
11484
+ w: payload.w ?? 1,
11485
+ h: payload.h ?? 1
11486
+ });
11487
+ const intersectsScrollOperations = (rect) => {
11488
+ if (!rect || !(normalizedScrollOperations === null || normalizedScrollOperations === void 0 ? void 0 : normalizedScrollOperations.length)) return false;
11489
+ const top = rect.y;
11490
+ const bottom = rect.y + rect.h;
11491
+ for (const op of normalizedScrollOperations) if (top < op.endY && bottom > op.startY) return true;
11492
+ return false;
11493
+ };
11494
+ const graphicRectVisible = (rect) => Boolean(rect && clipGraphicRectToViewport(rect, size));
11495
+ const pendingGraphicIds = new Set(graphicsToRender.map((payload) => payload.id));
11496
+ const explicitGraphicClears = new Set(graphicsClearsToRender);
11497
+ if ((normalizedScrollOperations === null || normalizedScrollOperations === void 0 ? void 0 : normalizedScrollOperations.length) && activeGraphics.size > 0) for (const [id, active] of activeGraphics) {
11498
+ if (explicitGraphicClears.has(id) || pendingGraphicIds.has(id)) continue;
11499
+ if (!intersectsScrollOperations(active)) continue;
11500
+ const signature = activeGraphicSignatures.get(id);
11501
+ graphicsToRender.push({
11502
+ id,
11503
+ x: active.x,
11504
+ y: active.y,
11505
+ w: active.w,
11506
+ h: active.h,
11507
+ protocol: active.protocol,
11508
+ sequence: active.sequence,
11509
+ resizeSequence: active.resizeSequence,
11510
+ clearSequence: active.clearSequence,
11511
+ op: "draw",
11512
+ order: nextGraphicsOrder++
11513
+ });
11514
+ if (signature) pendingGraphicSignatures.set(id, signature);
11515
+ pendingGraphicIds.add(id);
11516
+ }
11517
+ for (const [id, active] of activeGraphics) {
11518
+ if (explicitGraphicClears.has(id)) continue;
11519
+ if (pendingGraphicIds.has(id)) continue;
11520
+ if (intersectsScrollOperations(active)) continue;
11521
+ addTerminalGraphicTextPreserveSpans(active);
11522
+ }
11523
+ if ((normalizedScrollOperations === null || normalizedScrollOperations === void 0 ? void 0 : normalizedScrollOperations.length) && activeGraphics.size > 0) for (const active of activeGraphics.values()) {
11524
+ if (!intersectsScrollOperations(active)) continue;
11525
+ terminalGraphicsBlockScrollRegions = true;
11526
+ addTerminalGraphicRows(active);
11527
+ }
11528
+ for (const id of graphicsClearsToRender) {
11529
+ const active = activeGraphics.get(id);
11530
+ addTerminalGraphicRows(active);
11531
+ if (intersectsScrollOperations(active)) terminalGraphicsBlockScrollRegions = true;
11532
+ }
11533
+ for (const payload of graphicsToRender) {
11534
+ const active = activeGraphics.get(payload.id);
11535
+ if (payload.op === "clear") {
11536
+ const rect = active ?? payloadRect(payload);
11537
+ addTerminalGraphicRows(rect);
11538
+ if (intersectsScrollOperations(rect)) terminalGraphicsBlockScrollRegions = true;
11539
+ continue;
11540
+ }
11541
+ const next = payloadRect(payload);
11542
+ if (!payload.allowTextOverlay) addTerminalGraphicTextPreserveSpans(next);
11543
+ addTerminalGraphicRows(active);
11544
+ addTerminalGraphicRows(next);
11545
+ if (intersectsScrollOperations(active) || intersectsScrollOperations(next)) terminalGraphicsBlockScrollRegions = true;
11546
+ }
11547
+ 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({
11548
+ x: payload.x,
11549
+ y: payload.y,
11550
+ w: payload.w ?? 1,
11551
+ h: payload.h ?? 1
11552
+ }, size))))) terminalGraphicsBlockScrollRegions = true;
11553
+ let forceDirtyRowsRepaintForFrame = forceDirtyRowsRepaint;
11554
+ const forceTerminalGraphicsRowRepaint = (y) => forceDirtyRowsRepaintForFrame || terminalGraphicsAffectedRows.has(y) || terminalGraphicTextPreserveSpans.has(y);
11555
+ for (const [y, spans] of terminalGraphicTextPreserveSpans) {
11556
+ spans.sort((a, b) => a.start - b.start || a.end - b.end);
11557
+ const merged = [];
11558
+ for (const span of spans) {
11559
+ const previous = merged[merged.length - 1];
11560
+ if (previous && span.start <= previous.end) previous.end = Math.max(previous.end, span.end);
11561
+ else merged.push({ ...span });
11562
+ }
11563
+ terminalGraphicTextPreserveSpans.set(y, merged);
11564
+ }
11565
+ const mergeSpans = (spans) => {
11566
+ if (!spans.length) return [];
11567
+ const sorted = [...spans].sort((a, b) => a.start - b.start || a.end - b.end);
11568
+ const merged = [];
11569
+ for (const span of sorted) {
11570
+ const start = Math.max(0, Math.min(size.cols, span.start));
11571
+ const end = Math.max(start, Math.min(size.cols, span.end));
11572
+ if (end <= start) continue;
11573
+ const previous = merged[merged.length - 1];
11574
+ if (previous && start <= previous.end) previous.end = Math.max(previous.end, end);
11575
+ else merged.push({
11576
+ start,
11577
+ end
11578
+ });
11579
+ }
11580
+ return merged;
11581
+ };
11582
+ const overlayVisibleSpansForRows = (rows) => {
11583
+ const overlay = getPlaneTerminal(terminal, "overlay");
11584
+ const result = new Map();
11585
+ for (const y of rows) {
11586
+ if (y < 0 || y >= size.rows) continue;
11587
+ const row = overlay.getRow(y);
11588
+ const spans = [];
11589
+ let start = null;
11590
+ let end = 0;
11591
+ for (let x = 0; x < size.cols; x++) {
11592
+ const cell = row[x];
11593
+ const visible = cell != null && !cell.continuation && (cell.ch !== " " || styleKey(cell.style) !== bgKey || renderedHref(cell.style.href) != null);
11594
+ if (visible) {
11595
+ if (start == null) start = x;
11596
+ end = Math.max(end, Math.min(size.cols, x + Math.max(1, cell.width ?? 1)));
11597
+ continue;
11598
+ }
11599
+ if (start != null && x >= end) {
11600
+ spans.push({
11601
+ start,
11602
+ end
11603
+ });
11604
+ start = null;
11605
+ }
11606
+ }
11607
+ if (start != null) spans.push({
11608
+ start,
11609
+ end: Math.max(end, start + 1)
11610
+ });
11611
+ if (spans.length) result.set(y, mergeSpans(spans));
11612
+ }
11613
+ return result;
11614
+ };
11615
+ const overlayCoverage = overlayPlaneCoverageRows(size.rows);
11616
+ const overlayVisibleSpans = overlayVisibleSpansForRows(overlayCoverage.blockedRows);
9510
11617
  let rowsToRender = (() => {
11618
+ if (allowGraphicsOnlyWithoutBaseline) return [];
9511
11619
  if (forceFullRender || !fpPrevValid) return null;
9512
11620
  if (!dirtyRows) return null;
9513
11621
  if (dirtyRows.length === 0) return [];
@@ -9529,7 +11637,7 @@ function createStdoutRenderer(terminal, options) {
9529
11637
  if (!sorted) outRows.sort((a, b) => a - b);
9530
11638
  return outRows;
9531
11639
  })();
9532
- if ((rowsToRender === null || rowsToRender === void 0 ? void 0 : rowsToRender.length) === 0 && !normalizedScrollOperations && !getImeAnchor) {
11640
+ if ((rowsToRender === null || rowsToRender === void 0 ? void 0 : rowsToRender.length) === 0 && !normalizedScrollOperations && !getImeAnchor && graphicsClearsToRender.length === 0 && graphicsToRender.length === 0) {
9533
11641
  if (isDebugEnabled()) getDebugLog().render(" No valid dirty rows; frame skipped");
9534
11642
  cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutNoOutput();
9535
11643
  return;
@@ -9548,10 +11656,13 @@ function createStdoutRenderer(terminal, options) {
9548
11656
  currentHrefIds.set(prevHrefIds);
9549
11657
  currentTextIds.set(prevTextIds);
9550
11658
  }
11659
+ const frameHasTerminalGraphics = activeGraphics.size > 0 || graphicsClearsToRender.length > 0 || graphicsToRender.length > 0;
11660
+ const useFrameSyncOutput = useSyncOutput || isGhostty && frameHasTerminalGraphics;
9551
11661
  const frameParts = [];
9552
- frameParts.push(!isGhostty && useSyncOutput ? SYNC_START : "");
9553
- frameParts.push("\x1B[?7l");
11662
+ frameParts.push(useFrameSyncOutput ? SYNC_START : "");
11663
+ frameParts.push(allowGraphicsOnlyWithoutBaseline || keepLineWrapDisabled ? "" : "\x1B[?7l");
9554
11664
  frameParts.push(SGR_RESET, bgSeq);
11665
+ if (hideCursor && !getImeAnchor) frameParts.push(CURSOR_HOME);
9555
11666
  let lastRenderedY = -1;
9556
11667
  let lastRenderWasFullRow = false;
9557
11668
  let hasFrameOutput = false;
@@ -9566,6 +11677,7 @@ function createStdoutRenderer(terminal, options) {
9566
11677
  inverse: false,
9567
11678
  href: null
9568
11679
  };
11680
+ let overlayTouchedRowSet = null;
9569
11681
  const closeActiveHrefBeforeCursorMove = () => {
9570
11682
  if (!enableOsc8Links || !activeStyle.href) return;
9571
11683
  frameParts.push(OSC8_CLOSE);
@@ -9668,6 +11780,65 @@ function createStdoutRenderer(terminal, options) {
9668
11780
  lastRenderedY = y;
9669
11781
  lastRenderWasFullRow = spanStart === 0 && (spanEnd >= size.cols || shouldClearToEol);
9670
11782
  };
11783
+ const overlayBypassSpansForRow = (y) => {
11784
+ const current = overlayVisibleSpans.get(y) ?? [];
11785
+ const previous = prevOverlayVisibleSpans.get(y) ?? [];
11786
+ return current.length || previous.length ? mergeSpans([...current, ...previous]) : [];
11787
+ };
11788
+ const subtractSpans = (spans, bypass) => {
11789
+ if (!bypass.length) return [...spans];
11790
+ const result = [];
11791
+ for (const span of spans) {
11792
+ let cursor = span.start;
11793
+ for (const cut of bypass) {
11794
+ if (cut.end <= cursor) continue;
11795
+ if (cut.start >= span.end) break;
11796
+ if (cursor < cut.start) result.push({
11797
+ start: cursor,
11798
+ end: Math.min(cut.start, span.end)
11799
+ });
11800
+ cursor = Math.max(cursor, cut.end);
11801
+ if (cursor >= span.end) break;
11802
+ }
11803
+ if (cursor < span.end) result.push({
11804
+ start: cursor,
11805
+ end: span.end
11806
+ });
11807
+ }
11808
+ return result;
11809
+ };
11810
+ const preserveFingerprintSpans = (y, spans) => {
11811
+ if (!fpPrevValid || y < 0 || y >= fpRows || currentFP.length !== prevFP.length) return;
11812
+ const base = y * fpCols;
11813
+ for (const span of spans) {
11814
+ const start = Math.max(0, Math.min(fpCols, span.start));
11815
+ const end = Math.max(start, Math.min(fpCols, span.end));
11816
+ for (let x = start; x < end; x++) {
11817
+ currentFP[base + x] = prevFP[base + x];
11818
+ currentHrefIds[base + x] = prevHrefIds[base + x];
11819
+ currentTextIds[base + x] = prevTextIds[base + x];
11820
+ }
11821
+ }
11822
+ };
11823
+ const renderRowPreservingTerminalGraphics = (y, row) => {
11824
+ const spans = terminalGraphicTextPreserveSpans.get(y);
11825
+ if (!(spans === null || spans === void 0 ? void 0 : spans.length)) {
11826
+ renderRow(y, row);
11827
+ return;
11828
+ }
11829
+ const effectiveSpans = subtractSpans(spans, overlayBypassSpansForRow(y));
11830
+ if (!effectiveSpans.length) {
11831
+ renderRow(y, row);
11832
+ return;
11833
+ }
11834
+ let cursor = 0;
11835
+ for (const span of effectiveSpans) {
11836
+ if (cursor < span.start) renderRow(y, row, cursor, span.start, false);
11837
+ cursor = Math.max(cursor, span.end);
11838
+ }
11839
+ if (cursor < size.cols) renderRow(y, row, cursor, size.cols, false);
11840
+ preserveFingerprintSpans(y, effectiveSpans);
11841
+ };
9671
11842
  const lastRenderableColumnInCurrentRow = (rowY, cols, blankFingerprint) => {
9672
11843
  if (!fpCols || rowY < 0 || rowY >= fpRows) return -1;
9673
11844
  const base = rowY * fpCols;
@@ -9732,16 +11903,15 @@ function createStdoutRenderer(terminal, options) {
9732
11903
  } else for (const span of spansToPaint) renderRow(rowY, row, span.startX, span.endXExclusive, false);
9733
11904
  if (clearStartX != null && !paintTailInsteadOfEscK) renderRow(rowY, row, clearStartX, clearStartX, true);
9734
11905
  };
9735
- const overlayCoverage = overlayPlaneCoverageRows(size.rows);
9736
11906
  const overlayRows = Array.from(new Set([...overlayCoverage.blockedRows, ...prevOverlayBlockedRows])).sort((a, b) => a - b);
9737
11907
  const overlayRowSet = overlayRows.length ? new Set(overlayRows) : null;
9738
11908
  const overlayPartialRows = Array.from(new Set([...overlayCoverage.partialRows, ...prevOverlayPartialRows])).sort((a, b) => a - b);
9739
11909
  const overlayPartialRowSet = overlayPartialRows.length ? new Set(overlayPartialRows) : null;
9740
- const overlayTouchedRowSet = (() => {
11910
+ overlayTouchedRowSet = (() => {
9741
11911
  if (!overlayRowSet && !overlayPartialRowSet) return null;
9742
11912
  return new Set([...overlayRowSet ? Array.from(overlayRowSet) : [], ...overlayPartialRowSet ? Array.from(overlayPartialRowSet) : []]);
9743
11913
  })();
9744
- let explicitScrollOperations = normalizedScrollOperations;
11914
+ let explicitScrollOperations = terminalGraphicsBlockScrollRegions ? null : normalizedScrollOperations;
9745
11915
  let hiddenExplicitDirtyRows = null;
9746
11916
  let allowInferredScrollRegions = true;
9747
11917
  if (explicitScrollOperations && overlayRows.length) {
@@ -9756,6 +11926,7 @@ function createStdoutRenderer(terminal, options) {
9756
11926
  for (const op of explicitScrollOperations) for (let y = op.startY; y < op.endY; y++) expandedRows$1.add(y);
9757
11927
  rowsToRender = Array.from(expandedRows$1).sort((a, b) => a - b);
9758
11928
  dirtySorted = true;
11929
+ forceDirtyRowsRepaintForFrame = true;
9759
11930
  explicitScrollOperations = null;
9760
11931
  hiddenExplicitDirtyRows = null;
9761
11932
  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 +11941,18 @@ function createStdoutRenderer(terminal, options) {
9770
11941
  expandedRows.add(y);
9771
11942
  }
9772
11943
  };
11944
+ if (terminalGraphicsBlockScrollRegions && normalizedScrollOperations && rowsToRender) {
11945
+ includeRowsForPendingScrollOperations(normalizedScrollOperations);
11946
+ rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
11947
+ dirtySorted = true;
11948
+ forceDirtyRowsRepaintForFrame = true;
11949
+ if (isDebugEnabled()) getDebugLog().render(" Scroll regions disabled for frame with active/pending terminal graphics");
11950
+ }
9773
11951
  if (explicitScrollOperations && rowsToRender && (!enableScrollRegions || !fpPrevValid)) {
9774
11952
  includeRowsForPendingScrollOperations(explicitScrollOperations);
9775
11953
  rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
9776
11954
  dirtySorted = true;
11955
+ forceDirtyRowsRepaintForFrame = true;
9777
11956
  explicitScrollOperations = null;
9778
11957
  hiddenExplicitDirtyRows = null;
9779
11958
  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 +11960,7 @@ function createStdoutRenderer(terminal, options) {
9781
11960
  const denseDirtyRows = Boolean(rowsToRender && size.rows > 1 && rowsToRender.length >= size.rows * dirtyFullThreshold);
9782
11961
  if (denseDirtyRows) rowsToRender = null;
9783
11962
  let profiledRowCount = rowsToRender ? rowsToRender.length : size.rows;
9784
- const useScrollRegions = enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && !forceDirtyRowsRepaint && allowInferredScrollRegions;
11963
+ const useScrollRegions = !terminalGraphicsBlockScrollRegions && enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && !forceDirtyRowsRepaint && allowInferredScrollRegions;
9785
11964
  let scrollHandled = false;
9786
11965
  if (enableScrollRegions && explicitScrollOperations && scrollRowsCandidate && fpPrevValid) {
9787
11966
  if (currentFP.length === prevFP.length) {
@@ -9856,9 +12035,9 @@ function createStdoutRenderer(terminal, options) {
9856
12035
  for (const y of explicitDirtyRows) {
9857
12036
  const row = terminal.getRow(y);
9858
12037
  const overlayRow = overlayTouchedRowSet === null || overlayTouchedRowSet === void 0 ? void 0 : overlayTouchedRowSet.has(y);
9859
- if (forceDirtyRowsRepaint || !shouldUseDirtySpans() || overlayRow) {
12038
+ if (forceTerminalGraphicsRowRepaint(y) || !shouldUseDirtySpans() || overlayRow) {
9860
12039
  fingerprintRow(row, y, size.cols);
9861
- renderRow(y, row);
12040
+ renderRowPreservingTerminalGraphics(y, row);
9862
12041
  paintedExplicitRows.push(y);
9863
12042
  continue;
9864
12043
  }
@@ -9935,12 +12114,12 @@ function createStdoutRenderer(terminal, options) {
9935
12114
  for (let y = shift.newRowStart; y < shift.newRowEnd; y++) {
9936
12115
  const row = terminal.getRow(y);
9937
12116
  fingerprintRow(row, y, size.cols);
9938
- renderRow(y, row);
12117
+ renderRowPreservingTerminalGraphics(y, row);
9939
12118
  }
9940
12119
  const extraDirtyRows = overlayRowSet ? shift.extraDirtyRows.filter((y) => !overlayRowSet.has(y) || Boolean(overlayPartialRowSet === null || overlayPartialRowSet === void 0 ? void 0 : overlayPartialRowSet.has(y))) : shift.extraDirtyRows;
9941
12120
  for (const y of extraDirtyRows) {
9942
12121
  const row = terminal.getRow(y);
9943
- if (shouldUseDirtySpans()) {
12122
+ if (!forceTerminalGraphicsRowRepaint(y) && shouldUseDirtySpans()) {
9944
12123
  const spans = resolveChangedSpansAgainstReference(row, y, size.cols, currentFP, currentHrefIds, currentTextIds);
9945
12124
  if (!spans.length) {
9946
12125
  fingerprintRow(row, y, size.cols);
@@ -9952,7 +12131,7 @@ function createStdoutRenderer(terminal, options) {
9952
12131
  continue;
9953
12132
  }
9954
12133
  fingerprintRow(row, y, size.cols);
9955
- renderRow(y, row);
12134
+ renderRowPreservingTerminalGraphics(y, row);
9956
12135
  }
9957
12136
  for (let y = shift.newRowStart; y < shift.newRowEnd; y++) fingerprintRow(terminal.getRow(y), y, size.cols);
9958
12137
  const renderedRows = new Set();
@@ -9972,7 +12151,7 @@ function createStdoutRenderer(terminal, options) {
9972
12151
  for (let y = 0; y < size.rows; y++) {
9973
12152
  const row = terminal.getRow(y);
9974
12153
  fingerprintRow(row, y, size.cols);
9975
- renderRow(y, row);
12154
+ renderRowPreservingTerminalGraphics(y, row);
9976
12155
  }
9977
12156
  } else {
9978
12157
  const overlayDirtyRows = overlayTouchedRowSet ? rowsToRender.filter((y) => overlayTouchedRowSet.has(y)) : [];
@@ -9980,8 +12159,8 @@ function createStdoutRenderer(terminal, options) {
9980
12159
  for (const y of rowsToRender) {
9981
12160
  const row = terminal.getRow(y);
9982
12161
  fingerprintRow(row, y, size.cols);
9983
- if (forceDirtyRowsRepaint || !shouldUseDirtySpans()) {
9984
- renderRow(y, row);
12162
+ if (forceTerminalGraphicsRowRepaint(y) || !shouldUseDirtySpans()) {
12163
+ renderRowPreservingTerminalGraphics(y, row);
9985
12164
  continue;
9986
12165
  }
9987
12166
  const spans = resolveChangedSpans(row, y, size.cols, (dirtyRowSnapshots === null || dirtyRowSnapshots === void 0 ? void 0 : dirtyRowSnapshots.get(y)) ?? null);
@@ -9990,7 +12169,7 @@ function createStdoutRenderer(terminal, options) {
9990
12169
  renderDirtySpans(y, row, spans, rewriteTail);
9991
12170
  }
9992
12171
  }
9993
- if (!rowsToRender && lastRenderedRows > size.rows) {
12172
+ if (!rowsToRender && lastRenderedRows > size.rows && !rowsOnlyResizeBaseline) {
9994
12173
  const extraRows = lastRenderedRows - size.rows;
9995
12174
  if (activeStyleKey !== bgKey) {
9996
12175
  frameParts.push(SGR_RESET, bgSeq);
@@ -10001,7 +12180,8 @@ function createStdoutRenderer(terminal, options) {
10001
12180
  frameParts.push(rowClearToEol[size.rows + i]);
10002
12181
  }
10003
12182
  }
10004
- const nextLastRenderedRows = !rowsToRender ? size.rows : lastRenderedRows;
12183
+ const nextLastRenderedRows = !rowsToRender || rowsToRender.length === 0 ? size.rows : lastRenderedRows;
12184
+ const graphicsOnlyFrame = Boolean(rowsToRender && rowsToRender.length === 0) && !scrollHandled && (graphicsClearsToRender.length > 0 || graphicsToRender.length > 0);
10005
12185
  const commitRenderBaseline = () => {
10006
12186
  lastRenderedRows = nextLastRenderedRows;
10007
12187
  const tmpFP = prevFP;
@@ -10013,10 +12193,258 @@ function createStdoutRenderer(terminal, options) {
10013
12193
  const tmpTextIds = prevTextIds;
10014
12194
  prevTextIds = currentTextIds;
10015
12195
  currentTextIds = tmpTextIds;
10016
- fpPrevValid = !hrefIndexResetRequiresBaseline && !fingerprintInternResetRequiresBaseline;
12196
+ fpPrevValid = graphicsOnlyFrame ? fpPrevValid : !hrefIndexResetRequiresBaseline && !fingerprintInternResetRequiresBaseline;
10017
12197
  prevOverlayBlockedRows = overlayCoverage.blockedRows;
10018
12198
  prevOverlayPartialRows = overlayCoverage.partialRows;
12199
+ prevOverlayVisibleSpans = overlayVisibleSpans;
12200
+ };
12201
+ if (graphicsOnlyFrame && fpPrevValid && currentFP.length === prevFP.length) {
12202
+ currentFP.set(prevFP);
12203
+ currentHrefIds.set(prevHrefIds);
12204
+ currentTextIds.set(prevTextIds);
12205
+ }
12206
+ let renderedRowsLookup;
12207
+ const rowWasPaintedByTextRenderer = (y) => {
12208
+ if (scrollHandled) return true;
12209
+ if (!rowsToRender) return true;
12210
+ renderedRowsLookup ??= new Set(rowsToRender);
12211
+ return renderedRowsLookup.has(y);
10019
12212
  };
12213
+ const clearGraphicRectForRowsNotPainted = (rect) => {
12214
+ if (scrollHandled) return "";
12215
+ const erase = `\u001B[${Math.max(0, rect.w)}X`;
12216
+ const clearStyle = bgSeq;
12217
+ let out$1 = "";
12218
+ for (let row = 0; row < rect.h; row++) {
12219
+ const y = rect.y + row;
12220
+ const spans = terminalGraphicTextPreserveSpans.get(y);
12221
+ 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));
12222
+ if ((!rowsToRender || rowWasPaintedByTextRenderer(y)) && !preservedGraphicSpan) continue;
12223
+ out$1 += `\u001B[${y + 1};${rect.x + 1}H${clearStyle}${erase}${SGR_RESET}`;
12224
+ }
12225
+ return out$1;
12226
+ };
12227
+ const clearGraphicRowBackgroundForRowsNotPainted = (rect) => {
12228
+ if (scrollHandled) return "";
12229
+ const clearStyle = bgSeq;
12230
+ let out$1 = "";
12231
+ const leftW = Math.max(0, Math.min(size.cols, rect.x));
12232
+ const rightX = Math.max(0, Math.min(size.cols, rect.x + rect.w));
12233
+ const rightW = Math.max(0, size.cols - rightX);
12234
+ if (leftW <= 0 && rightW <= 0) return "";
12235
+ for (let row = 0; row < rect.h; row++) {
12236
+ const y = rect.y + row;
12237
+ if (!rowsToRender || rowWasPaintedByTextRenderer(y)) continue;
12238
+ if (leftW > 0) out$1 += `\u001B[${y + 1};1H${clearStyle}\u001B[${leftW}X${SGR_RESET}`;
12239
+ if (rightW > 0) out$1 += `\u001B[${y + 1};${rightX + 1}H${clearStyle}\u001B[${rightW}X${SGR_RESET}`;
12240
+ }
12241
+ return out$1;
12242
+ };
12243
+ let commitTerminalGraphicsState = null;
12244
+ let restoreTerminalGraphicsAfterWriteFailure = null;
12245
+ let terminalGraphicsMetrics = null;
12246
+ const mergeGraphicsQueuedDuringPaint = () => {
12247
+ const seenEntries = new Set(graphicsEntriesToRender.map(([key]) => key));
12248
+ let changed = false;
12249
+ for (const entry of pendingGraphics.entries()) {
12250
+ if (seenEntries.has(entry[0])) continue;
12251
+ graphicsEntriesToRender.push(entry);
12252
+ changed = true;
12253
+ }
12254
+ for (const id of pendingGraphicClears) {
12255
+ if (pendingGraphicClearsToRender.has(id)) continue;
12256
+ pendingGraphicClearsToRender.add(id);
12257
+ graphicsClearsToRender.push(id);
12258
+ }
12259
+ if (!changed) return;
12260
+ graphicsEntriesToRender = graphicsEntriesToRender.sort(([, a], [, b]) => {
12261
+ const opA = a.op === "clear" ? 0 : 1;
12262
+ const opB = b.op === "clear" ? 0 : 1;
12263
+ return opA - opB || a.order - b.order;
12264
+ });
12265
+ graphicsToRender = graphicsEntriesToRender.map(([, payload]) => payload);
12266
+ };
12267
+ mergeGraphicsQueuedDuringPaint();
12268
+ if (graphicsClearsToRender.length > 0 || graphicsToRender.length > 0) {
12269
+ const nextActiveGraphics = new Map(activeGraphics);
12270
+ const nextActiveGraphicSignatures = new Map(activeGraphicSignatures);
12271
+ const nextPendingGraphicSignatures = new Map(pendingGraphicSignatures);
12272
+ let terminalGraphicsDraws = 0;
12273
+ let terminalGraphicsClears = 0;
12274
+ let terminalGraphicsBytes = 0;
12275
+ const countGraphicsBytes = (value) => {
12276
+ terminalGraphicsBytes += Buffer.byteLength(value, "utf8");
12277
+ return value;
12278
+ };
12279
+ const appendTerminalGraphicSequenceAt = (sequence, rect, fallbackCell, skipWithoutRect = false) => {
12280
+ if (!rect && skipWithoutRect) return false;
12281
+ const cell = rect ? {
12282
+ x: rect.x,
12283
+ y: rect.y
12284
+ } : clampCellToViewport(fallbackCell, size);
12285
+ frameParts.push(countGraphicsBytes(`\u001B[${cell.y + 1};${cell.x + 1}H`), countGraphicsBytes(maybeWrapTerminalGraphic(sequence)), countGraphicsBytes(SGR_RESET));
12286
+ return true;
12287
+ };
12288
+ const appendActiveGraphicProtocolClear = (active, rect) => {
12289
+ if (!active.clearSequence) return false;
12290
+ return appendTerminalGraphicSequenceAt(active.clearSequence, rect, {
12291
+ cellX: active.x,
12292
+ cellY: active.y
12293
+ }, clearSequenceNeedsVisibleCell(active.clearSequence, active.protocol));
12294
+ };
12295
+ hasFrameOutput = true;
12296
+ if (enableOsc8Links && activeStyle.href) frameParts.push(OSC8_CLOSE);
12297
+ if (allowGraphicsOnlyWithoutBaseline) frameParts.push(CURSOR_SAVE);
12298
+ activeStyle = {
12299
+ fg: null,
12300
+ bg: defaultBg,
12301
+ bold: false,
12302
+ dim: false,
12303
+ italic: false,
12304
+ underline: false,
12305
+ inverse: false,
12306
+ href: null
12307
+ };
12308
+ activeStyleKey = null;
12309
+ frameParts.push(countGraphicsBytes(SGR_RESET));
12310
+ for (const id of graphicsClearsToRender) {
12311
+ const active = nextActiveGraphics.get(id);
12312
+ if (!active) continue;
12313
+ const visibleRect = clipGraphicRectToViewport(active, size);
12314
+ 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"}`);
12315
+ let wroteClear = false;
12316
+ if (visibleRect) {
12317
+ const clearRect$1 = clearGraphicRectForRowsNotPainted(visibleRect);
12318
+ if (clearRect$1) {
12319
+ frameParts.push(countGraphicsBytes(clearRect$1));
12320
+ wroteClear = true;
12321
+ }
12322
+ }
12323
+ wroteClear = appendActiveGraphicProtocolClear(active, visibleRect) || wroteClear;
12324
+ if (wroteClear) terminalGraphicsClears++;
12325
+ retainedGraphics.delete(id);
12326
+ terminalResizeDirtyGraphics.delete(id);
12327
+ nextActiveGraphics.delete(id);
12328
+ nextActiveGraphicSignatures.delete(id);
12329
+ nextPendingGraphicSignatures.delete(id);
12330
+ }
12331
+ for (const payload of graphicsToRender) {
12332
+ if (payload.op === "clear") {
12333
+ const active = nextActiveGraphics.get(payload.id);
12334
+ const rect$1 = clipGraphicRectToViewport({
12335
+ x: (active === null || active === void 0 ? void 0 : active.x) ?? payload.x,
12336
+ y: (active === null || active === void 0 ? void 0 : active.y) ?? payload.y,
12337
+ w: (active === null || active === void 0 ? void 0 : active.w) ?? payload.w ?? 1,
12338
+ h: (active === null || active === void 0 ? void 0 : active.h) ?? payload.h ?? 1
12339
+ }, size);
12340
+ 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"}`);
12341
+ const clearRect$2 = rect$1 ? clearGraphicRectForRowsNotPainted(rect$1) : "";
12342
+ let wroteClear = false;
12343
+ if (clearRect$2) {
12344
+ frameParts.push(countGraphicsBytes(clearRect$2));
12345
+ wroteClear = true;
12346
+ }
12347
+ wroteClear = appendTerminalGraphicSequenceAt(payload.sequence, rect$1, {
12348
+ cellX: (active === null || active === void 0 ? void 0 : active.x) ?? payload.x,
12349
+ cellY: (active === null || active === void 0 ? void 0 : active.y) ?? payload.y
12350
+ }, clearSequenceNeedsVisibleCell(payload.sequence, payload.protocol)) || wroteClear;
12351
+ if (wroteClear) terminalGraphicsClears++;
12352
+ if (active && payload.retainOnClear) retainTerminalGraphic(payload.id, active, nextActiveGraphicSignatures.get(payload.id));
12353
+ else retainedGraphics.delete(payload.id);
12354
+ terminalResizeDirtyGraphics.delete(payload.id);
12355
+ nextActiveGraphics.delete(payload.id);
12356
+ nextActiveGraphicSignatures.delete(payload.id);
12357
+ nextPendingGraphicSignatures.delete(payload.id);
12358
+ continue;
12359
+ }
12360
+ const previous = nextActiveGraphics.get(payload.id);
12361
+ const rect = anchoredGraphicRectInViewport({
12362
+ x: payload.x,
12363
+ y: payload.y,
12364
+ w: payload.w ?? 1,
12365
+ h: payload.h ?? 1
12366
+ }, size);
12367
+ if (!rect) {
12368
+ if (previous) {
12369
+ const visiblePrevious = clipGraphicRectToViewport(previous, size);
12370
+ let wroteClear = false;
12371
+ if (visiblePrevious) {
12372
+ const clearPrevious = clearGraphicRectForRowsNotPainted(visiblePrevious);
12373
+ if (clearPrevious) {
12374
+ frameParts.push(countGraphicsBytes(clearPrevious));
12375
+ wroteClear = true;
12376
+ }
12377
+ }
12378
+ wroteClear = appendActiveGraphicProtocolClear(previous, visiblePrevious) || wroteClear;
12379
+ if (wroteClear) terminalGraphicsClears++;
12380
+ retainTerminalGraphic(payload.id, previous, nextActiveGraphicSignatures.get(payload.id), { autoRedrawOnResize: true });
12381
+ nextActiveGraphics.delete(payload.id);
12382
+ nextActiveGraphicSignatures.delete(payload.id);
12383
+ }
12384
+ terminalResizeDirtyGraphics.delete(payload.id);
12385
+ nextPendingGraphicSignatures.delete(payload.id);
12386
+ continue;
12387
+ }
12388
+ const previousSignature = previous ? nextActiveGraphicSignatures.get(payload.id) : void 0;
12389
+ const nextSignature = nextPendingGraphicSignatures.get(payload.id) ?? terminalGraphicsPayloadSignature(payload);
12390
+ const canReplacePlacement = previous != null && payload.resizeRedraw && canReplaceKittyPlacementWithoutClear(previous, payload);
12391
+ const resizeRedrawNeedsClear = previous && (payload.resizeRedraw || payload.terminalResizeRedraw) && Boolean(previous.clearSequence) && !canReplacePlacement;
12392
+ if (previous && (!canReplacePlacement && (previousSignature !== nextSignature || !sameGraphicRect(previous, rect)) || resizeRedrawNeedsClear)) {
12393
+ const visiblePrevious = clipGraphicRectToViewport(previous, size);
12394
+ let wroteClear = false;
12395
+ if (visiblePrevious) {
12396
+ const clearPrevious = clearGraphicRectForRowsNotPainted(visiblePrevious);
12397
+ if (clearPrevious) {
12398
+ frameParts.push(countGraphicsBytes(clearPrevious));
12399
+ wroteClear = true;
12400
+ }
12401
+ }
12402
+ wroteClear = appendActiveGraphicProtocolClear(previous, visiblePrevious) || wroteClear;
12403
+ if (wroteClear) terminalGraphicsClears++;
12404
+ }
12405
+ const visibleRect = clipGraphicRectToViewport(rect, size);
12406
+ 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"}`);
12407
+ const clearRect$1 = visibleRect ? clearGraphicRectForRowsNotPainted(visibleRect) : "";
12408
+ const clearRowBackground = visibleRect ? clearGraphicRowBackgroundForRowsNotPainted(visibleRect) : "";
12409
+ const cursor = `\u001B[${rect.y + 1};${rect.x + 1}H`;
12410
+ const sequencePayload = (canReplacePlacement || payload.terminalResizeRedraw) && payload.resizeSequence ? payload.resizeSequence : payload.sequence;
12411
+ const sequence = maybeWrapTerminalGraphic(sequencePayload);
12412
+ frameParts.push(countGraphicsBytes(cursor), countGraphicsBytes(sequence), countGraphicsBytes(SGR_RESET), clearRowBackground ? countGraphicsBytes(clearRowBackground) : "", clearRect$1 ? countGraphicsBytes(clearRect$1) : "");
12413
+ terminalGraphicsDraws++;
12414
+ nextActiveGraphics.set(payload.id, {
12415
+ ...rect,
12416
+ protocol: payload.protocol,
12417
+ sequence: payload.sequence,
12418
+ resizeSequence: payload.resizeSequence ?? (previous === null || previous === void 0 ? void 0 : previous.resizeSequence),
12419
+ clearSequence: payload.clearSequence
12420
+ });
12421
+ nextActiveGraphicSignatures.set(payload.id, nextSignature);
12422
+ nextPendingGraphicSignatures.delete(payload.id);
12423
+ terminalResizeDirtyGraphics.delete(payload.id);
12424
+ }
12425
+ if (allowGraphicsOnlyWithoutBaseline) frameParts.push(CURSOR_RESTORE);
12426
+ restoreTerminalGraphicsAfterWriteFailure = () => {
12427
+ for (const id of graphicsClearsToRender) pendingGraphicClears.add(id);
12428
+ for (const [key, payload] of graphicsEntriesToRender) pendingGraphics.set(key, payload);
12429
+ };
12430
+ commitTerminalGraphicsState = () => {
12431
+ for (const id of pendingGraphicClearsToRender) pendingGraphicClears.delete(id);
12432
+ for (const [key, payload] of graphicsEntriesToRender) if (pendingGraphics.get(key) === payload) pendingGraphics.delete(key);
12433
+ activeGraphics.clear();
12434
+ for (const [id, active] of nextActiveGraphics) activeGraphics.set(id, active);
12435
+ activeGraphicSignatures.clear();
12436
+ for (const [id, signature] of nextActiveGraphicSignatures) activeGraphicSignatures.set(id, signature);
12437
+ pendingGraphicSignatures.clear();
12438
+ for (const [id, signature] of nextPendingGraphicSignatures) pendingGraphicSignatures.set(id, signature);
12439
+ };
12440
+ terminalGraphicsMetrics = {
12441
+ draws: terminalGraphicsDraws,
12442
+ clears: terminalGraphicsClears,
12443
+ bytes: terminalGraphicsBytes,
12444
+ active: nextActiveGraphics.size
12445
+ };
12446
+ if (isDebugEnabled()) getDebugLog().render(`terminal graphics frame: draws=${terminalGraphicsDraws}, clears=${terminalGraphicsClears}, bytes=${terminalGraphicsBytes}, activeNext=${nextActiveGraphics.size}`);
12447
+ }
10020
12448
  let emittedCursorPos = null;
10021
12449
  if (getImeAnchor) {
10022
12450
  const anchor = getImeAnchor();
@@ -10041,8 +12469,9 @@ function createStdoutRenderer(terminal, options) {
10041
12469
  }
10042
12470
  if (enableOsc8Links && activeStyle.href) frameParts.push(OSC8_CLOSE);
10043
12471
  frameParts.push(SGR_RESET);
10044
- frameParts.push("\x1B[?7h");
10045
- frameParts.push(!isGhostty && useSyncOutput ? SYNC_END : "");
12472
+ if (hideCursor && !getImeAnchor) frameParts.push(CURSOR_HOME);
12473
+ frameParts.push(allowGraphicsOnlyWithoutBaseline || keepLineWrapDisabled ? "" : "\x1B[?7h");
12474
+ frameParts.push(useFrameSyncOutput ? SYNC_END : "");
10046
12475
  const frame = frameParts.join("");
10047
12476
  const frameBytes = Buffer.byteLength(frame, "utf8");
10048
12477
  if (profiler) profiler.recordRender({
@@ -10103,10 +12532,11 @@ function createStdoutRenderer(terminal, options) {
10103
12532
  const resetCount = countResets(frame);
10104
12533
  const buildTime = (performance.now() - renderStart).toFixed(2);
10105
12534
  getDebugLog().render(` Frame built: ${frameSize} bytes, ${cursorSeqCount} cursor sequences, ${resetCount} resets, ${buildTime}ms`);
10106
- getDebugLog().render(` Terminal: ${isGhostty ? "GHOSTTY" : "other"}, useSyncOutput: ${useSyncOutput}`);
12535
+ getDebugLog().render(` Terminal: ${isGhostty ? "GHOSTTY" : "other"}, useSyncOutput: ${useSyncOutput}, useFrameSyncOutput: ${useFrameSyncOutput}`);
10107
12536
  }
10108
12537
  const writeStart = performance.now();
10109
12538
  const resolveWriteMode = (frameSizeBytes) => {
12539
+ if (frameHasTerminalGraphics && frameSizeBytes <= syncMaxBytes) return canUseSyncStdout ? "sync" : "stream";
10110
12540
  if (isGhostty) return "chunked";
10111
12541
  const preferChunked = frameSizeBytes >= chunkThresholdBytes || writeEmaMs >= 24;
10112
12542
  if (preferChunked) return "chunked";
@@ -10136,12 +12566,15 @@ function createStdoutRenderer(terminal, options) {
10136
12566
  getDebugLog().render(` Write completed in ${writeTime}ms`);
10137
12567
  }
10138
12568
  } catch (writeError) {
12569
+ restoreTerminalGraphicsAfterWriteFailure === null || restoreTerminalGraphicsAfterWriteFailure === void 0 || restoreTerminalGraphicsAfterWriteFailure();
10139
12570
  if (isDebugEnabled()) getDebugLog().error(`Write ERROR:`, writeError);
10140
12571
  try {
10141
- out.write(`\u001B[?7h${!isGhostty && useSyncOutput ? SYNC_END : ""}`);
12572
+ out.write(`\u001B[?7h${useFrameSyncOutput ? SYNC_END : ""}`);
10142
12573
  } catch {}
10143
12574
  throw writeError;
10144
12575
  }
12576
+ commitTerminalGraphicsState === null || commitTerminalGraphicsState === void 0 || commitTerminalGraphicsState();
12577
+ if (terminalGraphicsMetrics) recordStdoutTerminalGraphics(terminalGraphicsMetrics);
10145
12578
  commitRenderBaseline();
10146
12579
  {
10147
12580
  const writeMs = performance.now() - writeStart;
@@ -10149,6 +12582,9 @@ function createStdoutRenderer(terminal, options) {
10149
12582
  if (emittedCursorPos) {
10150
12583
  lastCursorX = emittedCursorPos.x;
10151
12584
  lastCursorY = emittedCursorPos.y;
12585
+ } else if (hideCursor && !getImeAnchor) {
12586
+ lastCursorX = 0;
12587
+ lastCursorY = 0;
10152
12588
  } else {
10153
12589
  lastCursorX = null;
10154
12590
  lastCursorY = null;
@@ -10315,6 +12751,7 @@ function createStdoutRenderer(terminal, options) {
10315
12751
  */
10316
12752
  function render(dirtyRows, sync, scrollOperations) {
10317
12753
  if (disposed) return;
12754
+ if (dirtyRows == null || sync) deferGraphicsFlushUntilResizeCommit = false;
10318
12755
  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
12756
  if (dirtyRows == null) {
10320
12757
  accumulatedAllRows = true;
@@ -10326,7 +12763,7 @@ function createStdoutRenderer(terminal, options) {
10326
12763
  accumulatedScrollOperations = null;
10327
12764
  } else if (dirtyRows.length === 0) {
10328
12765
  if (scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length) mergeScrollOperations(scrollOperations);
10329
- else if (!accumulatedAllRows && accumulatedDirtyCount === 0 && !getImeAnchor) {
12766
+ else if (!accumulatedAllRows && accumulatedDirtyCount === 0 && !getImeAnchor && !hasPendingTerminalGraphics()) {
10330
12767
  cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutQueued(0);
10331
12768
  return;
10332
12769
  }
@@ -10345,8 +12782,8 @@ function createStdoutRenderer(terminal, options) {
10345
12782
  }
10346
12783
  }
10347
12784
  }
10348
- const now$2 = Date.now();
10349
- const elapsed = now$2 - lastFrameTime;
12785
+ const now$3 = Date.now();
12786
+ const elapsed = now$3 - lastFrameTime;
10350
12787
  const queuedDelayMs = Math.max(0, MIN_FRAME_MS - elapsed);
10351
12788
  if (pendingRender && !sync) {
10352
12789
  cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutQueued(queuedDelayMs);
@@ -10436,13 +12873,94 @@ function createStdoutRenderer(terminal, options) {
10436
12873
  } catch {}
10437
12874
  }
10438
12875
  installFingerprintFn();
12876
+ let suppressNextResizeCommitRender = false;
12877
+ function consumeResizeDirtyState() {
12878
+ suppressNextResizeCommitRender = true;
12879
+ try {
12880
+ terminal.commit({ sync: true });
12881
+ } finally {
12882
+ suppressNextResizeCommitRender = false;
12883
+ }
12884
+ }
12885
+ function clearRowsOnlyResizeRepaintTimer() {
12886
+ if (!rowsOnlyResizeRepaintTimer) return;
12887
+ clearTimeout(rowsOnlyResizeRepaintTimer);
12888
+ rowsOnlyResizeRepaintTimer = null;
12889
+ }
12890
+ function repaintRowsAfterRowsOnlyResize() {
12891
+ if (disposed) return;
12892
+ const { rows } = terminal.size();
12893
+ if (rows <= 0) return;
12894
+ if (isDebugEnabled()) getDebugLog().render(`rows-only resize repaint: rows=${rows}`);
12895
+ preserveNextRowsOnlyResizeBaseline = true;
12896
+ const dirtyRows = Array.from({ length: rows }, (_, y) => y);
12897
+ accumulatedDirtyRowsRequireRepaint = true;
12898
+ deferGraphicsFlushUntilResizeCommit = false;
12899
+ allowNextGraphicsOnlyRenderWithoutBaseline = false;
12900
+ render(dirtyRows, true);
12901
+ }
12902
+ function scheduleRowsOnlyResizeRepaint() {
12903
+ clearRowsOnlyResizeRepaintTimer();
12904
+ if (isDebugEnabled()) getDebugLog().render(`rows-only resize repaint scheduled: delay=${ROWS_ONLY_RESIZE_REPAINT_DELAY_MS}ms`);
12905
+ rowsOnlyResizeRepaintTimer = setTimeout(() => {
12906
+ rowsOnlyResizeRepaintTimer = null;
12907
+ repaintRowsAfterRowsOnlyResize();
12908
+ }, ROWS_ONLY_RESIZE_REPAINT_DELAY_MS);
12909
+ }
10439
12910
  if (altScreen && outputIsTTY) out.write("\x1B[?1049h");
12911
+ if (keepLineWrapDisabled) out.write("\x1B[?7l");
10440
12912
  if (hideCursor) out.write("\x1B[?25l");
10441
12913
  if (clear) out.write(`${SGR_RESET}${openBg(defaultBg)}\u001B[2J\u001B[H${SGR_RESET}`);
10442
12914
  const off = terminal.on("commit", ({ dirtyRows, scrollOperations, sync }) => {
12915
+ if (suppressNextResizeCommitRender) return;
12916
+ const deferRowsOnlyResizeCommit = rowsOnlyResizeRepaintTimer && (dirtyRows == null || dirtyRows.length >= terminal.size().rows);
12917
+ if (deferRowsOnlyResizeCommit) {
12918
+ if (isDebugEnabled()) getDebugLog().render(`rows-only resize commit deferred: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}${sync ? " (sync)" : ""}`);
12919
+ scheduleRowsOnlyResizeRepaint();
12920
+ return;
12921
+ }
10443
12922
  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
12923
  render(dirtyRows, sync, scrollOperations);
10445
12924
  });
12925
+ let terminalResizeCols = terminal.size().cols;
12926
+ let terminalResizeRows = terminal.size().rows;
12927
+ const offTerminalResize = terminal.on("resize", ({ cols, rows }) => {
12928
+ const colsChanged = cols !== terminalResizeCols;
12929
+ const rowsChanged = rows !== terminalResizeRows;
12930
+ const rowsIncreased = rows > terminalResizeRows;
12931
+ terminalResizeCols = cols;
12932
+ terminalResizeRows = rows;
12933
+ if (!colsChanged && !rowsChanged) return;
12934
+ if (pendingRender) {
12935
+ pendingRender = false;
12936
+ if (renderTimer) {
12937
+ clearTimeout(renderTimer);
12938
+ renderTimer = null;
12939
+ }
12940
+ if (isDebugEnabled()) getDebugLog().render("pending stdout render canceled until resize commit");
12941
+ }
12942
+ queueTerminalGraphicsForResizeRedraw({
12943
+ cols,
12944
+ rows
12945
+ });
12946
+ if (!colsChanged) {
12947
+ consumeResizeDirtyState();
12948
+ preserveNextRowsOnlyResizeBaseline = true;
12949
+ if (rowsIncreased) {
12950
+ clearRowsOnlyResizeRepaintTimer();
12951
+ repaintRowsAfterRowsOnlyResize();
12952
+ return;
12953
+ }
12954
+ scheduleRowsOnlyResizeRepaint();
12955
+ if (hasPendingTerminalGraphics()) {
12956
+ deferGraphicsFlushUntilResizeCommit = false;
12957
+ allowNextGraphicsOnlyRenderWithoutBaseline = true;
12958
+ render([], true);
12959
+ }
12960
+ return;
12961
+ }
12962
+ clearRowsOnlyResizeRepaintTimer();
12963
+ });
10446
12964
  const resizeSource = (options === null || options === void 0 ? void 0 : options.output) ?? process.stdout;
10447
12965
  const canTrackResize = Boolean(trackResize && outputIsTTY && typeof (resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.on) === "function");
10448
12966
  const onResize = () => {
@@ -10450,9 +12968,13 @@ function createStdoutRenderer(terminal, options) {
10450
12968
  const rows = Number(resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.rows);
10451
12969
  if (!Number.isFinite(cols) || !Number.isFinite(rows)) return;
10452
12970
  const size = terminal.size();
12971
+ if (isDebugEnabled()) getDebugLog().render(`stdout resize event: output=${cols}x${rows}, terminal=${size.cols}x${size.rows}, trackResize=${trackResize ? "true" : "false"}`);
10453
12972
  if (cols === size.cols && rows === size.rows) return;
12973
+ const colsChanged = cols !== size.cols;
12974
+ const rowsChanged = rows !== size.rows;
10454
12975
  terminal.resize(cols, rows);
10455
- render();
12976
+ consumeResizeDirtyState();
12977
+ if (isDebugEnabled()) getDebugLog().render(`stdout resize consumed: target=${cols}x${rows}, colsChanged=${colsChanged ? "true" : "false"}, rowsChanged=${rowsChanged ? "true" : "false"}`);
10456
12978
  };
10457
12979
  if (canTrackResize) try {
10458
12980
  resizeSource.on("resize", onResize);
@@ -10482,17 +13004,72 @@ function createStdoutRenderer(terminal, options) {
10482
13004
  function dispose() {
10483
13005
  if (disposed) return;
10484
13006
  disposed = true;
13007
+ let disposeError = null;
10485
13008
  if (renderTimer) {
10486
13009
  clearTimeout(renderTimer);
10487
13010
  renderTimer = null;
10488
13011
  }
13012
+ if (terminalGraphicsFlushTimer) {
13013
+ clearTimeout(terminalGraphicsFlushTimer);
13014
+ terminalGraphicsFlushTimer = null;
13015
+ }
13016
+ clearRowsOnlyResizeRepaintTimer();
10489
13017
  accumulatedAllRows = false;
10490
13018
  accumulatedDirtyBits = null;
10491
13019
  accumulatedDirtyCount = 0;
10492
13020
  accumulatedDirtyMin = Number.POSITIVE_INFINITY;
10493
13021
  accumulatedDirtyMax = -1;
10494
13022
  accumulatedDirtyRowsRequireRepaint = false;
13023
+ pendingGraphics.clear();
13024
+ pendingGraphicClears.clear();
13025
+ pendingGraphicSignatures.clear();
13026
+ retainedGraphics.clear();
13027
+ terminalResizeDirtyGraphics.clear();
13028
+ try {
13029
+ if (activeGraphics.size > 0) {
13030
+ const size = terminal.size();
13031
+ const clearParts = [];
13032
+ let terminalGraphicsClears = 0;
13033
+ for (const active of activeGraphics.values()) {
13034
+ const visibleRect = clipGraphicRectToViewport(active, size);
13035
+ let wroteClear = false;
13036
+ if (visibleRect) {
13037
+ clearParts.push(clearGraphicRect(visibleRect));
13038
+ wroteClear = true;
13039
+ }
13040
+ if (active.clearSequence && (visibleRect || !clearSequenceNeedsVisibleCell(active.clearSequence, active.protocol))) {
13041
+ const cell = visibleRect ? {
13042
+ x: visibleRect.x,
13043
+ y: visibleRect.y
13044
+ } : clampCellToViewport({
13045
+ cellX: active.x,
13046
+ cellY: active.y
13047
+ }, size);
13048
+ clearParts.push(`\u001B[${cell.y + 1};${cell.x + 1}H`, maybeWrapTerminalGraphic(active.clearSequence));
13049
+ wroteClear = true;
13050
+ }
13051
+ if (wroteClear) terminalGraphicsClears++;
13052
+ }
13053
+ activeGraphics.clear();
13054
+ activeGraphicSignatures.clear();
13055
+ if (clearParts.length > 0) {
13056
+ const frame = `${SGR_RESET}${clearParts.join("")}${SGR_RESET}`;
13057
+ out.write(frame);
13058
+ recordStdoutTerminalGraphics({
13059
+ clears: terminalGraphicsClears,
13060
+ bytes: Buffer.byteLength(frame, "utf8"),
13061
+ active: 0
13062
+ });
13063
+ } else recordStdoutTerminalGraphics({ active: 0 });
13064
+ }
13065
+ } catch {
13066
+ activeGraphics.clear();
13067
+ activeGraphicSignatures.clear();
13068
+ recordStdoutTerminalGraphics({ active: 0 });
13069
+ }
13070
+ unregisterGraphicsOutput();
10495
13071
  off();
13072
+ offTerminalResize();
10496
13073
  releaseFingerprintFn();
10497
13074
  if (canTrackResize && typeof (resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.off) === "function") try {
10498
13075
  resizeSource.off("resize", onResize);
@@ -10500,9 +13077,15 @@ function createStdoutRenderer(terminal, options) {
10500
13077
  else if (canTrackResize && typeof (resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.removeListener) === "function") try {
10501
13078
  resizeSource.removeListener("resize", onResize);
10502
13079
  } catch {}
10503
- if (hideCursor) out.write("\x1B[?25h");
10504
- if (altScreen && outputIsTTY) out.write("\x1B[?1049l");
13080
+ try {
13081
+ if (keepLineWrapDisabled) out.write("\x1B[?7h");
13082
+ if (hideCursor) out.write("\x1B[?25h");
13083
+ if (altScreen && outputIsTTY) out.write("\x1B[?1049l");
13084
+ } catch (err) {
13085
+ if (disposeError == null) disposeError = err;
13086
+ }
10505
13087
  profiler === null || profiler === void 0 || profiler.dispose();
13088
+ if (disposeError) throw disposeError;
10506
13089
  }
10507
13090
  const updateTheme = (next) => {
10508
13091
  if (disposed) return;
@@ -10517,10 +13100,12 @@ function createStdoutRenderer(terminal, options) {
10517
13100
  fpPrevValid = false;
10518
13101
  prevOverlayBlockedRows = [];
10519
13102
  prevOverlayPartialRows = [];
13103
+ prevOverlayVisibleSpans = new Map();
10520
13104
  render();
10521
13105
  };
10522
13106
  return {
10523
13107
  capabilities: STDOUT_RENDERER_CAPABILITIES,
13108
+ graphicsCapabilities,
10524
13109
  render,
10525
13110
  dispose,
10526
13111
  setCursor,
@@ -10554,4 +13139,4 @@ function createNodeMentionPathProvider() {
10554
13139
  }
10555
13140
 
10556
13141
  //#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 };
13142
+ 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 };