@simon_he/vue-tui 0.0.5 → 0.0.7

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 (4) hide show
  1. package/dist/index.cjs +2262 -266
  2. package/dist/index.d.ts +180 -106
  3. package/dist/index.js +1966 -217
  4. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -361,7 +361,7 @@ __export(path_suggest_exports, {
361
361
  });
362
362
  function isNodeLike() {
363
363
  var _a2;
364
- const proc = import_node_process3.default;
364
+ const proc = import_node_process4.default;
365
365
  return typeof ((_a2 = proc == null ? void 0 : proc.versions) == null ? void 0 : _a2.node) === "string";
366
366
  }
367
367
  function loadGitignoreRulesAsync(workspaceAbs) {
@@ -559,16 +559,16 @@ async function suggestPaths2(options) {
559
559
  }
560
560
  function resolveUserPath2(workspaceAbs, input) {
561
561
  var _a2;
562
- const env = ((_a2 = import_node_process3.default) == null ? void 0 : _a2.env) ?? {};
562
+ const env = ((_a2 = import_node_process4.default) == null ? void 0 : _a2.env) ?? {};
563
563
  return resolveUserPath(workspaceAbs, input, {
564
564
  homeDir: String(env.HOME || env.USERPROFILE || "")
565
565
  });
566
566
  }
567
- var import_node_process3, DEFAULT_IGNORED_SEGMENTS, EMPTY_GITIGNORE_RULES, gitignoreRulesCache, gitignoreRulesLoading;
567
+ var import_node_process4, DEFAULT_IGNORED_SEGMENTS, EMPTY_GITIGNORE_RULES, gitignoreRulesCache, gitignoreRulesLoading;
568
568
  var init_path_suggest = __esm({
569
569
  "src/cli/path-suggest.ts"() {
570
570
  "use strict";
571
- import_node_process3 = __toESM(require("process"), 1);
571
+ import_node_process4 = __toESM(require("process"), 1);
572
572
  init_path();
573
573
  init_path_suggest_core();
574
574
  init_path_suggest_core();
@@ -638,6 +638,7 @@ __export(src_exports, {
638
638
  defaultTInputHostPlugin: () => defaultTInputHostPlugin,
639
639
  detectTerminalColorCapability: () => detectTerminalColorCapability,
640
640
  formatInlineCellLine: () => formatInlineCellLine,
641
+ getCliLatencyProfiler: () => getCliLatencyProfiler,
641
642
  getStdoutRendererMetrics: () => getStdoutRendererMetrics,
642
643
  lintJsonText: () => lintJsonText,
643
644
  normalizeNewlines: () => normalizeNewlines,
@@ -1017,7 +1018,359 @@ function ansiColors(level) {
1017
1018
  }
1018
1019
 
1019
1020
  // src/cli/input.ts
1020
- var import_node_process2 = __toESM(require("process"), 1);
1021
+ var import_node_process3 = __toESM(require("process"), 1);
1022
+
1023
+ // src/observability/cli-latency.ts
1024
+ var import_node_fs = require("fs");
1025
+ var import_node_process = __toESM(require("process"), 1);
1026
+ var EVENT_OP_ID = Symbol("dimcode.cliLatencyOpId");
1027
+ var DEFAULT_LOG_PATH = "/tmp/dimcode-cli-latency.jsonl";
1028
+ var INVALIDATE_ASSOCIATION_WINDOW_MS = 32;
1029
+ var MAX_OP_AGE_MS = 5e3;
1030
+ var nextOpId = 1;
1031
+ var singleton;
1032
+ function now() {
1033
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
1034
+ }
1035
+ function diffMs(end, start) {
1036
+ if (end == null || start == null)
1037
+ return null;
1038
+ return Math.max(0, end - start);
1039
+ }
1040
+ function parseEnabled(value) {
1041
+ const raw = String(value ?? "").trim().toLowerCase();
1042
+ return raw === "1" || raw === "true" || raw === "yes";
1043
+ }
1044
+ function isTrackedEventType(type) {
1045
+ return type === "keydown" || type === "beforeinput" || type === "input" || type === "paste";
1046
+ }
1047
+ function defineOpId(target, id) {
1048
+ try {
1049
+ Object.defineProperty(target, EVENT_OP_ID, {
1050
+ configurable: true,
1051
+ enumerable: false,
1052
+ writable: true,
1053
+ value: id
1054
+ });
1055
+ } catch {
1056
+ }
1057
+ }
1058
+ function readOpId(target) {
1059
+ const value = target == null ? void 0 : target[EVENT_OP_ID];
1060
+ return typeof value === "number" ? value : null;
1061
+ }
1062
+ function createDisabledProfiler() {
1063
+ return null;
1064
+ }
1065
+ function createProfiler() {
1066
+ var _a2, _b2, _c;
1067
+ const env = ((_a2 = import_node_process.default) == null ? void 0 : _a2.env) ?? {};
1068
+ if (!parseEnabled(env.DIMCODE_PROFILE_INPUT_LATENCY))
1069
+ return createDisabledProfiler();
1070
+ const logPath = String(
1071
+ env.DIMCODE_PROFILE_INPUT_LATENCY_LOG_PATH ?? DEFAULT_LOG_PATH
1072
+ ).trim() || DEFAULT_LOG_PATH;
1073
+ const ops = /* @__PURE__ */ new Map();
1074
+ const pendingCommitIds = /* @__PURE__ */ new Set();
1075
+ const pendingWriteIds = /* @__PURE__ */ new Set();
1076
+ let currentDispatchOpId = null;
1077
+ let lastDispatchOpId = null;
1078
+ let lastDispatchEndedAt = null;
1079
+ let currentFlushIds = [];
1080
+ let lastRawInputAt = null;
1081
+ const emit = (record) => {
1082
+ try {
1083
+ (0, import_node_fs.appendFileSync)(logPath, `${JSON.stringify(record)}
1084
+ `);
1085
+ } catch {
1086
+ }
1087
+ };
1088
+ const cleanupOp = (id) => {
1089
+ ops.delete(id);
1090
+ pendingCommitIds.delete(id);
1091
+ pendingWriteIds.delete(id);
1092
+ if (currentDispatchOpId === id)
1093
+ currentDispatchOpId = null;
1094
+ if (lastDispatchOpId === id) {
1095
+ lastDispatchOpId = null;
1096
+ lastDispatchEndedAt = null;
1097
+ }
1098
+ if (currentFlushIds.includes(id))
1099
+ currentFlushIds = currentFlushIds.filter((value) => value !== id);
1100
+ };
1101
+ const flushOp = (op, outcome) => {
1102
+ const totalStart = op.rawInputAt ?? op.stdinDispatchAt ?? op.dispatchStartAt;
1103
+ emit({
1104
+ ts: op.createdAtIso,
1105
+ id: op.id,
1106
+ event: {
1107
+ type: op.eventType,
1108
+ key: op.key,
1109
+ code: op.code,
1110
+ defaultPrevented: op.defaultPrevented,
1111
+ parser: op.parser
1112
+ },
1113
+ operation: op.operation,
1114
+ stages: {
1115
+ rawInputAt: op.rawInputAt,
1116
+ stdinDispatchAt: op.stdinDispatchAt,
1117
+ dispatchStartAt: op.dispatchStartAt,
1118
+ dispatchEndAt: op.dispatchEndAt,
1119
+ invalidateAt: op.invalidateAt,
1120
+ flushStartAt: op.flushStartAt,
1121
+ flushEndAt: op.flushEndAt,
1122
+ commitAt: op.commitAt,
1123
+ stdoutRenderStartAt: op.stdoutRenderStartAt,
1124
+ writeStartAt: op.writeStartAt,
1125
+ writeEndAt: op.writeEndAt
1126
+ },
1127
+ timingsMs: {
1128
+ inputParseWait: diffMs(op.stdinDispatchAt, op.rawInputAt),
1129
+ eventDispatch: diffMs(op.dispatchEndAt, op.dispatchStartAt),
1130
+ dispatchToInvalidate: diffMs(op.invalidateAt, op.dispatchEndAt),
1131
+ dispatchToFlush: diffMs(op.flushStartAt, op.dispatchEndAt),
1132
+ flushToCommitDone: diffMs(op.flushEndAt, op.flushStartAt),
1133
+ commitToRender: diffMs(op.stdoutRenderStartAt, op.commitAt),
1134
+ renderToWriteStart: diffMs(op.writeStartAt, op.stdoutRenderStartAt),
1135
+ write: op.writeDurationMs,
1136
+ totalToWrite: diffMs(op.writeEndAt, totalStart)
1137
+ },
1138
+ scheduler: {
1139
+ invalidatePriority: op.invalidatePriority,
1140
+ invalidatePlane: op.invalidatePlane,
1141
+ commitSync: op.commitSync,
1142
+ commitPlaneCount: op.commitPlaneCount,
1143
+ commitDirtyRows: op.commitDirtyRows,
1144
+ stdoutQueuedDelayMs: op.stdoutQueuedDelayMs,
1145
+ writeMode: op.writeMode,
1146
+ writeBytes: op.writeBytes
1147
+ },
1148
+ outcome
1149
+ });
1150
+ cleanupOp(op.id);
1151
+ };
1152
+ const expireOldOps = () => {
1153
+ const currentTime = now();
1154
+ for (const op of ops.values()) {
1155
+ if (currentTime - op.createdAt < MAX_OP_AGE_MS)
1156
+ continue;
1157
+ flushOp(op, op.writeEndAt != null ? "completed" : "timeout");
1158
+ }
1159
+ };
1160
+ const flushPendingOps = (outcome) => {
1161
+ for (const op of Array.from(ops.values()))
1162
+ flushOp(op, op.writeEndAt != null ? "completed" : outcome);
1163
+ };
1164
+ const ensureOp = (event) => {
1165
+ const type = String((event == null ? void 0 : event.type) ?? "");
1166
+ if (!isTrackedEventType(type))
1167
+ return null;
1168
+ const existingId = readOpId(event);
1169
+ if (existingId != null) {
1170
+ return ops.get(existingId) ?? null;
1171
+ }
1172
+ const id = nextOpId++;
1173
+ const op = {
1174
+ id,
1175
+ createdAt: now(),
1176
+ createdAtIso: (/* @__PURE__ */ new Date()).toISOString(),
1177
+ eventType: type,
1178
+ key: String((event == null ? void 0 : event.key) ?? ""),
1179
+ code: String((event == null ? void 0 : event.code) ?? ""),
1180
+ operation: null,
1181
+ rawInputAt: null,
1182
+ stdinDispatchAt: null,
1183
+ dispatchStartAt: null,
1184
+ dispatchEndAt: null,
1185
+ invalidateAt: null,
1186
+ invalidatePriority: null,
1187
+ invalidatePlane: null,
1188
+ flushStartAt: null,
1189
+ flushEndAt: null,
1190
+ commitAt: null,
1191
+ commitSync: null,
1192
+ commitPlaneCount: null,
1193
+ commitDirtyRows: null,
1194
+ stdoutQueuedDelayMs: null,
1195
+ stdoutRenderStartAt: null,
1196
+ writeStartAt: null,
1197
+ writeEndAt: null,
1198
+ writeDurationMs: null,
1199
+ writeMode: null,
1200
+ writeBytes: null,
1201
+ defaultPrevented: null,
1202
+ parser: null
1203
+ };
1204
+ ops.set(id, op);
1205
+ defineOpId(event, id);
1206
+ expireOldOps();
1207
+ return op;
1208
+ };
1209
+ const forIds = (ids, fn) => {
1210
+ for (const id of ids) {
1211
+ const op = ops.get(id);
1212
+ if (!op)
1213
+ continue;
1214
+ fn(op);
1215
+ }
1216
+ };
1217
+ const resolveActiveOp = () => {
1218
+ if (currentDispatchOpId != null)
1219
+ return ops.get(currentDispatchOpId) ?? null;
1220
+ if (lastDispatchOpId != null && lastDispatchEndedAt != null && now() - lastDispatchEndedAt <= INVALIDATE_ASSOCIATION_WINDOW_MS) {
1221
+ return ops.get(lastDispatchOpId) ?? null;
1222
+ }
1223
+ return null;
1224
+ };
1225
+ (_c = (_b2 = import_node_process.default) == null ? void 0 : _b2.once) == null ? void 0 : _c.call(_b2, "exit", () => {
1226
+ flushPendingOps("process-exit");
1227
+ });
1228
+ return {
1229
+ enabled: true,
1230
+ recordRawInput(_info) {
1231
+ lastRawInputAt = now();
1232
+ expireOldOps();
1233
+ },
1234
+ recordStdinDispatch(event, info) {
1235
+ const op = ensureOp(event);
1236
+ if (!op)
1237
+ return;
1238
+ if (op.rawInputAt == null && lastRawInputAt != null)
1239
+ op.rawInputAt = lastRawInputAt;
1240
+ if (op.stdinDispatchAt == null)
1241
+ op.stdinDispatchAt = now();
1242
+ if (!op.parser && (info == null ? void 0 : info.parser))
1243
+ op.parser = String(info.parser);
1244
+ },
1245
+ recordEventDispatchStart(event) {
1246
+ const op = ensureOp(event);
1247
+ if (!op)
1248
+ return;
1249
+ currentDispatchOpId = op.id;
1250
+ if (op.dispatchStartAt == null)
1251
+ op.dispatchStartAt = now();
1252
+ },
1253
+ recordEventDispatchEnd(event, info) {
1254
+ const op = ensureOp(event);
1255
+ if (!op)
1256
+ return;
1257
+ op.defaultPrevented = Boolean(info.defaultPrevented);
1258
+ if (op.dispatchEndAt == null)
1259
+ op.dispatchEndAt = now();
1260
+ currentDispatchOpId = currentDispatchOpId === op.id ? null : currentDispatchOpId;
1261
+ lastDispatchOpId = op.id;
1262
+ lastDispatchEndedAt = op.dispatchEndAt;
1263
+ },
1264
+ recordSchedulerInvalidate(info) {
1265
+ const op = resolveActiveOp();
1266
+ if (!op)
1267
+ return;
1268
+ if (op.invalidateAt == null)
1269
+ op.invalidateAt = now();
1270
+ if (!op.invalidatePriority && (info == null ? void 0 : info.priority))
1271
+ op.invalidatePriority = String(info.priority);
1272
+ if (!op.invalidatePlane && (info == null ? void 0 : info.plane))
1273
+ op.invalidatePlane = String(info.plane);
1274
+ pendingCommitIds.add(op.id);
1275
+ },
1276
+ recordFlushStart(info) {
1277
+ currentFlushIds = Array.from(pendingCommitIds);
1278
+ if (!currentFlushIds.length)
1279
+ return;
1280
+ const flushTime = now();
1281
+ forIds(currentFlushIds, (op) => {
1282
+ if (op.flushStartAt == null)
1283
+ op.flushStartAt = flushTime;
1284
+ if (op.commitSync == null && (info == null ? void 0 : info.sync) != null)
1285
+ op.commitSync = Boolean(info.sync);
1286
+ if (op.commitPlaneCount == null && Array.isArray(info == null ? void 0 : info.activePlanes)) {
1287
+ op.commitPlaneCount = info.activePlanes.length;
1288
+ }
1289
+ });
1290
+ },
1291
+ recordFlushEnd() {
1292
+ if (!currentFlushIds.length)
1293
+ return;
1294
+ const flushTime = now();
1295
+ forIds(currentFlushIds, (op) => {
1296
+ if (op.flushEndAt == null)
1297
+ op.flushEndAt = flushTime;
1298
+ });
1299
+ currentFlushIds = [];
1300
+ },
1301
+ recordCommit(info) {
1302
+ if (!pendingCommitIds.size)
1303
+ return;
1304
+ const commitTime = now();
1305
+ const ids = Array.from(pendingCommitIds);
1306
+ pendingCommitIds.clear();
1307
+ forIds(ids, (op) => {
1308
+ if (op.flushEndAt == null && op.flushStartAt != null)
1309
+ op.flushEndAt = commitTime;
1310
+ if (op.commitAt == null)
1311
+ op.commitAt = commitTime;
1312
+ op.commitSync = (info == null ? void 0 : info.sync) == null ? op.commitSync : Boolean(info.sync);
1313
+ op.commitPlaneCount = Array.isArray(info == null ? void 0 : info.planes) ? info.planes.length : op.commitPlaneCount;
1314
+ op.commitDirtyRows = Array.isArray(info == null ? void 0 : info.dirtyRows) ? info.dirtyRows.length : (info == null ? void 0 : info.dirtyRows) === null ? null : op.commitDirtyRows;
1315
+ pendingWriteIds.add(op.id);
1316
+ });
1317
+ },
1318
+ recordStdoutQueued(delayMs) {
1319
+ if (!pendingWriteIds.size)
1320
+ return;
1321
+ forIds(pendingWriteIds, (op) => {
1322
+ if (op.stdoutQueuedDelayMs == null)
1323
+ op.stdoutQueuedDelayMs = Math.max(0, delayMs);
1324
+ });
1325
+ },
1326
+ recordStdoutRenderStart() {
1327
+ if (!pendingWriteIds.size)
1328
+ return;
1329
+ const renderTime = now();
1330
+ forIds(pendingWriteIds, (op) => {
1331
+ if (op.stdoutRenderStartAt == null)
1332
+ op.stdoutRenderStartAt = renderTime;
1333
+ });
1334
+ },
1335
+ recordStdoutNoOutput() {
1336
+ if (!pendingWriteIds.size)
1337
+ return;
1338
+ const ids = Array.from(pendingWriteIds);
1339
+ pendingWriteIds.clear();
1340
+ forIds(ids, (op) => flushOp(op, "no-output"));
1341
+ },
1342
+ recordStdoutWrite(info) {
1343
+ if (!pendingWriteIds.size)
1344
+ return;
1345
+ const writeEndTime = now();
1346
+ const ids = Array.from(pendingWriteIds);
1347
+ pendingWriteIds.clear();
1348
+ forIds(ids, (op) => {
1349
+ op.writeEndAt = writeEndTime;
1350
+ op.writeDurationMs = Math.max(0, info.durationMs);
1351
+ op.writeStartAt = writeEndTime - op.writeDurationMs;
1352
+ op.writeMode = String(info.mode ?? "");
1353
+ op.writeBytes = Math.max(0, Math.floor(info.bytes));
1354
+ flushOp(op, "completed");
1355
+ });
1356
+ },
1357
+ markOperation(operation) {
1358
+ const nextOperation = String(operation ?? "").trim();
1359
+ if (!nextOperation)
1360
+ return;
1361
+ const op = resolveActiveOp();
1362
+ if (!op)
1363
+ return;
1364
+ op.operation = nextOperation;
1365
+ }
1366
+ };
1367
+ }
1368
+ function getCliLatencyProfiler() {
1369
+ if (singleton !== void 0)
1370
+ return singleton;
1371
+ singleton = createProfiler();
1372
+ return singleton;
1373
+ }
1021
1374
 
1022
1375
  // src/utils/newlines.ts
1023
1376
  function normalizeNewlines(text) {
@@ -1474,13 +1827,13 @@ function parseLegacySequence(sequence) {
1474
1827
  }
1475
1828
 
1476
1829
  // src/cli/parse-mouse.ts
1477
- var import_node_fs = require("fs");
1478
- var import_node_process = __toESM(require("process"), 1);
1830
+ var import_node_fs2 = require("fs");
1831
+ var import_node_process2 = __toESM(require("process"), 1);
1479
1832
  function parseMouseSgr(sequence) {
1480
1833
  var _a2;
1481
1834
  if (!sequence.startsWith("\x1B[<"))
1482
1835
  return { handled: false, event: null };
1483
- const env = ((_a2 = import_node_process.default) == null ? void 0 : _a2.env) ?? {};
1836
+ const env = ((_a2 = import_node_process2.default) == null ? void 0 : _a2.env) ?? {};
1484
1837
  const invertWheel = String(env.DIMCODE_WHEEL_INVERT ?? env.VUE_TUI_WHEEL_INVERT ?? "") === "1";
1485
1838
  const mouseDebugEnabled = String(env.DIMCODE_MOUSE_DEBUG ?? env.VUE_TUI_MOUSE_DEBUG ?? "") === "1";
1486
1839
  const mouseDebugPath = String(
@@ -1490,7 +1843,7 @@ function parseMouseSgr(sequence) {
1490
1843
  if (!mouseDebugEnabled)
1491
1844
  return;
1492
1845
  try {
1493
- (0, import_node_fs.appendFileSync)(mouseDebugPath, `${msg}
1846
+ (0, import_node_fs2.appendFileSync)(mouseDebugPath, `${msg}
1494
1847
  `);
1495
1848
  } catch {
1496
1849
  }
@@ -1619,6 +1972,7 @@ var import_node_events = require("events");
1619
1972
  var ESC3 = "\x1B";
1620
1973
  var BRACKETED_PASTE_START = "\x1B[200~";
1621
1974
  var BRACKETED_PASTE_END = "\x1B[201~";
1975
+ var ESC_CONTINUATION_RESCUE_WINDOW_MS = 64;
1622
1976
  function looksLikeQuotedPathPaste(value) {
1623
1977
  if (!value)
1624
1978
  return false;
@@ -1741,11 +2095,19 @@ function extractCompleteSequences(buffer) {
1741
2095
  }
1742
2096
  return { sequences, remainder: "" };
1743
2097
  }
2098
+ function looksLikeSplitEscapeContinuation(value) {
2099
+ const first = value[0];
2100
+ if (first !== "[" && first !== "]" && first !== "P" && first !== "_" && first !== "O") {
2101
+ return false;
2102
+ }
2103
+ return isCompleteSequence(`${ESC3}${value}`) !== "not-escape";
2104
+ }
1744
2105
  var StdinBuffer = class extends import_node_events.EventEmitter {
1745
2106
  buffer = "";
1746
2107
  timeout = null;
1747
2108
  timeoutMs;
1748
2109
  escTimeoutMs;
2110
+ pendingEscContinuationAt = null;
1749
2111
  pasteMode = false;
1750
2112
  pasteBuffer = "";
1751
2113
  constructor(options = {}) {
@@ -1769,6 +2131,12 @@ var StdinBuffer = class extends import_node_events.EventEmitter {
1769
2131
  } else {
1770
2132
  str = data;
1771
2133
  }
2134
+ if (this.pendingEscContinuationAt != null) {
2135
+ const canRescue = Date.now() - this.pendingEscContinuationAt <= ESC_CONTINUATION_RESCUE_WINDOW_MS;
2136
+ this.pendingEscContinuationAt = null;
2137
+ if (canRescue && looksLikeSplitEscapeContinuation(str))
2138
+ str = `${ESC3}${str}`;
2139
+ }
1772
2140
  if (str.length === 0 && this.buffer.length === 0) {
1773
2141
  this.emit("data", "");
1774
2142
  return;
@@ -1830,7 +2198,10 @@ var StdinBuffer = class extends import_node_events.EventEmitter {
1830
2198
  if (this.buffer.length > 0) {
1831
2199
  const timeoutMs = this.buffer === ESC3 ? this.escTimeoutMs : this.timeoutMs;
1832
2200
  this.timeout = setTimeout(() => {
2201
+ const rescuableLoneEsc = this.buffer === ESC3;
1833
2202
  const flushed = this.flush();
2203
+ if (rescuableLoneEsc && flushed.length === 1 && flushed[0] === ESC3)
2204
+ this.pendingEscContinuationAt = Date.now();
1834
2205
  for (const sequence of flushed)
1835
2206
  this.emit("data", sequence);
1836
2207
  }, timeoutMs);
@@ -1853,6 +2224,7 @@ var StdinBuffer = class extends import_node_events.EventEmitter {
1853
2224
  this.timeout = null;
1854
2225
  }
1855
2226
  this.buffer = "";
2227
+ this.pendingEscContinuationAt = null;
1856
2228
  this.pasteMode = false;
1857
2229
  this.pasteBuffer = "";
1858
2230
  }
@@ -1865,6 +2237,10 @@ var StdinBuffer = class extends import_node_events.EventEmitter {
1865
2237
  };
1866
2238
 
1867
2239
  // src/cli/input.ts
2240
+ var KITTY_KEYBOARD_PROTOCOL_ENABLE = "\x1B[>1u";
2241
+ var KITTY_KEYBOARD_PROTOCOL_DISABLE = "\x1B[<u";
2242
+ var XTERM_MODIFY_OTHER_KEYS_ENABLE = "\x1B[>4;2m";
2243
+ var XTERM_MODIFY_OTHER_KEYS_DISABLE = "\x1B[>4n";
1868
2244
  function isPrintable(ch) {
1869
2245
  if (ch.length === 0)
1870
2246
  return false;
@@ -1888,13 +2264,64 @@ function ctrlKeyFromChar(ch) {
1888
2264
  return String.fromCharCode(code + 96);
1889
2265
  return null;
1890
2266
  }
2267
+ function isUnhandledCtrlC(event) {
2268
+ return event.type === "keydown" && (event.key === "c" || event.key === "C") && event.ctrlKey === true && event.metaKey !== true && event.altKey !== true && event.shiftKey !== true;
2269
+ }
2270
+ function parseKeyboardProtocol(value) {
2271
+ const normalized = String(value ?? "").trim().toLowerCase();
2272
+ if (normalized === "auto" || normalized === "kitty" || normalized === "xterm" || normalized === "off") {
2273
+ return normalized;
2274
+ }
2275
+ return null;
2276
+ }
2277
+ function detectKeyboardProtocol(env) {
2278
+ const termProgram = String(env.TERM_PROGRAM ?? "").trim().toLowerCase();
2279
+ const term = String(env.TERM ?? "").trim().toLowerCase();
2280
+ const hasKittyProtocolTerminal = "GHOSTTY_RESOURCES_DIR" in env || "WEZTERM_PANE" in env || "KITTY_WINDOW_ID" in env || "KITTY_INSTALLATION_DIR" in env || "ALACRITTY_WINDOW_ID" in env || "ALACRITTY_LOG" in env || termProgram.includes("ghostty") || termProgram.includes("wezterm") || termProgram.includes("kitty") || termProgram.includes("alacritty");
2281
+ if (hasKittyProtocolTerminal)
2282
+ return "kitty";
2283
+ const isScreenLike = "TMUX" in env || term.includes("screen") || term.includes("tmux");
2284
+ const hasXtermKeyboardTerminal = termProgram.includes("iterm") || termProgram.includes("apple_terminal") || termProgram.includes("vscode") || term.includes("xterm") && !isScreenLike;
2285
+ return hasXtermKeyboardTerminal ? "xterm" : "off";
2286
+ }
2287
+ function resolveKeyboardProtocol(options) {
2288
+ var _a2;
2289
+ const configured = parseKeyboardProtocol(options.keyboardProtocol) ?? "auto";
2290
+ if (configured !== "auto")
2291
+ return configured;
2292
+ const envOverride = parseKeyboardProtocol((_a2 = options.env) == null ? void 0 : _a2.DIMCODE_KEYBOARD_PROTOCOL);
2293
+ if (envOverride && envOverride !== "auto")
2294
+ return envOverride;
2295
+ return detectKeyboardProtocol(options.env ?? {});
2296
+ }
2297
+ function getKeyboardProtocolSequences(protocol) {
2298
+ if (protocol === "kitty") {
2299
+ return {
2300
+ enable: KITTY_KEYBOARD_PROTOCOL_ENABLE,
2301
+ disable: KITTY_KEYBOARD_PROTOCOL_DISABLE
2302
+ };
2303
+ }
2304
+ if (protocol === "xterm") {
2305
+ return {
2306
+ enable: XTERM_MODIFY_OTHER_KEYS_ENABLE,
2307
+ disable: XTERM_MODIFY_OTHER_KEYS_DISABLE
2308
+ };
2309
+ }
2310
+ return null;
2311
+ }
1891
2312
  function createStdinDriver(options) {
1892
- const stdin = options.stdin ?? import_node_process2.default.stdin;
1893
- const stdout = options.stdout ?? import_node_process2.default.stdout;
2313
+ const stdin = options.stdin ?? import_node_process3.default.stdin;
2314
+ const stdout = options.stdout ?? import_node_process3.default.stdout;
1894
2315
  if (!stdin || !stdout)
1895
2316
  throw new Error("createStdinDriver requires Node process.stdin/stdout");
2317
+ const env = options.env ?? import_node_process3.default.env ?? {};
1896
2318
  const enableMouse = options.enableMouse ?? true;
1897
2319
  const enableMouseMotion = options.enableMouseMotion ?? false;
2320
+ const keyboardProtocol = resolveKeyboardProtocol({
2321
+ keyboardProtocol: options.keyboardProtocol,
2322
+ env
2323
+ });
2324
+ const keyboardProtocolSequences = getKeyboardProtocolSequences(keyboardProtocol);
1898
2325
  let disposed = false;
1899
2326
  let swallowNextLF = false;
1900
2327
  let lastMouseDown = null;
@@ -1903,61 +2330,69 @@ function createStdinDriver(options) {
1903
2330
  timeout: 50,
1904
2331
  // When mouse tracking is enabled, CSI mouse reports are frequent and we really want to avoid
1905
2332
  // leaking "[<...;...;...M" into focused inputs if the leading ESC gets chunk-split.
1906
- // Keep this low to reduce perceived latency for plain Esc while still covering split CSI.
1907
- escTimeout: enableMouse ? 80 : 20
2333
+ // Keep this well below one frame so plain Esc remains responsive while the
2334
+ // stdin buffer rescue path still covers delayed CSI continuations.
2335
+ escTimeout: enableMouse ? 8 : 6
1908
2336
  });
1909
- const handlePlainChar = (ch) => {
2337
+ const latency = getCliLatencyProfiler();
2338
+ const dispatchEvent = (event, parser) => {
1910
2339
  var _a2;
2340
+ latency == null ? void 0 : latency.recordStdinDispatch(event, { parser });
2341
+ const prevented = Boolean(options.dispatch(event));
2342
+ if (isUnhandledCtrlC(event) && !prevented)
2343
+ (_a2 = options.onExit) == null ? void 0 : _a2.call(options);
2344
+ return prevented;
2345
+ };
2346
+ const handlePlainChar = (ch) => {
1911
2347
  if (swallowNextLF) {
1912
2348
  swallowNextLF = false;
1913
2349
  if (ch === "\n")
1914
2350
  return;
1915
2351
  }
1916
2352
  if (ch === "") {
1917
- const prevented = Boolean(
1918
- options.dispatch(keyEvent("c", "", { ctrlKey: true }))
1919
- );
1920
- if (!prevented)
1921
- (_a2 = options.onExit) == null ? void 0 : _a2.call(options);
2353
+ dispatchEvent(keyEvent("c", "", { ctrlKey: true }), "plain");
1922
2354
  return;
1923
2355
  }
1924
2356
  if (ch === "\r") {
1925
2357
  swallowNextLF = true;
1926
- options.dispatch(keyEvent("Enter", "Enter"));
2358
+ dispatchEvent(keyEvent("Enter", "Enter"), "plain");
1927
2359
  return;
1928
2360
  }
1929
2361
  if (ch === "\n") {
1930
- options.dispatch({
1931
- type: "input",
1932
- data: "\n",
1933
- inputType: "insertLineBreak",
1934
- text: "\n"
1935
- });
2362
+ dispatchEvent(
2363
+ {
2364
+ type: "input",
2365
+ data: "\n",
2366
+ inputType: "insertLineBreak",
2367
+ text: "\n"
2368
+ },
2369
+ "plain"
2370
+ );
1936
2371
  return;
1937
2372
  }
1938
2373
  const ctrlKey = ctrlKeyFromChar(ch);
1939
2374
  if (ctrlKey) {
1940
2375
  if (ctrlKey === "i") {
1941
- options.dispatch(keyEvent("Tab", "Tab"));
2376
+ dispatchEvent(keyEvent("Tab", "Tab"), "plain");
1942
2377
  return;
1943
2378
  }
1944
2379
  if (ctrlKey === "h") {
1945
- options.dispatch(keyEvent("Backspace", "Backspace"));
2380
+ dispatchEvent(keyEvent("Backspace", "Backspace"), "plain");
1946
2381
  return;
1947
2382
  }
1948
- options.dispatch(keyEvent(ctrlKey, "", { ctrlKey: true }));
2383
+ dispatchEvent(keyEvent(ctrlKey, "", { ctrlKey: true }), "plain");
1949
2384
  return;
1950
2385
  }
1951
2386
  if (ch === "\x7F") {
1952
- options.dispatch(keyEvent("Backspace", "Backspace"));
2387
+ dispatchEvent(keyEvent("Backspace", "Backspace"), "plain");
1953
2388
  return;
1954
2389
  }
1955
2390
  if (ch === " ") {
1956
- options.dispatch(keyEvent("Tab", "Tab"));
2391
+ dispatchEvent(keyEvent("Tab", "Tab"), "plain");
1957
2392
  return;
1958
2393
  }
1959
2394
  if (isPrintable(ch))
1960
- options.dispatch(keyEvent(ch));
2395
+ dispatchEvent(keyEvent(ch), "plain");
1961
2396
  };
1962
2397
  const handleMouseEvent = (event) => {
1963
2398
  const ev = event;
@@ -2039,22 +2474,22 @@ function createStdinDriver(options) {
2039
2474
  const kitty = parseKittySequence(sequence);
2040
2475
  if (kitty.handled) {
2041
2476
  if (kitty.event)
2042
- options.dispatch(kitty.event);
2477
+ dispatchEvent(kitty.event, "kitty");
2043
2478
  return;
2044
2479
  }
2045
2480
  const legacy = parseLegacySequence(sequence);
2046
2481
  if (legacy.handled) {
2047
2482
  if (legacy.event)
2048
- options.dispatch(legacy.event);
2483
+ dispatchEvent(legacy.event, "legacy");
2049
2484
  return;
2050
2485
  }
2051
2486
  if (sequence.startsWith("\x1B")) {
2052
2487
  if (sequence.length === 2 && isPrintable(sequence[1])) {
2053
- options.dispatch(keyEvent(sequence[1], "", { altKey: true }));
2488
+ dispatchEvent(keyEvent(sequence[1], "", { altKey: true }), "alt");
2054
2489
  return;
2055
2490
  }
2056
2491
  if (sequence === "\x1B") {
2057
- options.dispatch(keyEvent("Escape", "Escape"));
2492
+ dispatchEvent(keyEvent("Escape", "Escape"), "escape");
2058
2493
  return;
2059
2494
  }
2060
2495
  return;
@@ -2066,7 +2501,7 @@ function createStdinDriver(options) {
2066
2501
  stdinBuffer.on("paste", (data) => {
2067
2502
  const pastedText = normalizeNewlines(data);
2068
2503
  if (pastedText)
2069
- options.dispatch({ type: "paste", text: pastedText });
2504
+ dispatchEvent({ type: "paste", text: pastedText }, "paste");
2070
2505
  });
2071
2506
  const decodeBytes = (bytes) => {
2072
2507
  if (bytes.length === 1 && bytes[0] > 127) {
@@ -2081,6 +2516,7 @@ function createStdinDriver(options) {
2081
2516
  const onData = (chunk) => {
2082
2517
  if (disposed)
2083
2518
  return;
2519
+ latency == null ? void 0 : latency.recordRawInput();
2084
2520
  if (typeof chunk === "string") {
2085
2521
  stdinBuffer.process(chunk);
2086
2522
  return;
@@ -2111,6 +2547,8 @@ function createStdinDriver(options) {
2111
2547
  if (stdout.isTTY) {
2112
2548
  stdout.write("\x1B[?2004h");
2113
2549
  stdout.write("\x1B[?1004h");
2550
+ if (keyboardProtocolSequences)
2551
+ stdout.write(keyboardProtocolSequences.enable);
2114
2552
  if (enableMouse) {
2115
2553
  stdout.write("\x1B[?1007h");
2116
2554
  if (enableMouseMotion)
@@ -2140,6 +2578,8 @@ function createStdinDriver(options) {
2140
2578
  if (stdout.isTTY) {
2141
2579
  stdout.write("\x1B[?2004l");
2142
2580
  stdout.write("\x1B[?1004l");
2581
+ if (keyboardProtocolSequences)
2582
+ stdout.write(keyboardProtocolSequences.disable);
2143
2583
  if (enableMouse) {
2144
2584
  stdout.write("\x1B[?1007l");
2145
2585
  if (enableMouseMotion)
@@ -2662,12 +3102,53 @@ function createGridBuffer(cols, rows) {
2662
3102
  cursorStyle: DEFAULT_STYLE,
2663
3103
  scrollback: [],
2664
3104
  scrollbackLimit: 1e3,
2665
- rowPool: []
3105
+ rowPool: [],
3106
+ soaFingerprints: null,
3107
+ fingerprintFn: null
2666
3108
  };
2667
3109
  }
2668
3110
  function clamp(n, min, max) {
2669
3111
  return Math.max(min, Math.min(max, n));
2670
3112
  }
3113
+ function setFingerprintFn(buffer, fn) {
3114
+ buffer.fingerprintFn = fn;
3115
+ const len = buffer.rows * buffer.cols;
3116
+ if (!len) {
3117
+ buffer.soaFingerprints = null;
3118
+ return;
3119
+ }
3120
+ buffer.soaFingerprints = new Uint32Array(len);
3121
+ for (let y = 0; y < buffer.rows; y++) {
3122
+ const row = getBufferRow(buffer, y);
3123
+ const physY = physicalRowIndex(buffer, y);
3124
+ const base = physY * buffer.cols;
3125
+ for (let x = 0; x < buffer.cols; x++) {
3126
+ const cell = row[x];
3127
+ buffer.soaFingerprints[base + x] = fn(cell.ch, cell.style);
3128
+ }
3129
+ }
3130
+ }
3131
+ function getRowFingerprints(buffer, y) {
3132
+ if (!buffer.soaFingerprints)
3133
+ return null;
3134
+ const physY = physicalRowIndex(buffer, y);
3135
+ const base = physY * buffer.cols;
3136
+ return buffer.soaFingerprints.subarray(base, base + buffer.cols);
3137
+ }
3138
+ function updateCellFingerprint(buffer, physY, x, ch, style) {
3139
+ if (!buffer.soaFingerprints || !buffer.fingerprintFn)
3140
+ return;
3141
+ buffer.soaFingerprints[physY * buffer.cols + x] = buffer.fingerprintFn(ch, style);
3142
+ }
3143
+ function updateRangeFingerprint(buffer, physY, x0, x1, ch, style) {
3144
+ if (!buffer.soaFingerprints || !buffer.fingerprintFn)
3145
+ return;
3146
+ const fp = buffer.fingerprintFn(ch, style);
3147
+ const base = physY * buffer.cols;
3148
+ for (let x = x0; x < x1; x++) {
3149
+ buffer.soaFingerprints[base + x] = fp;
3150
+ }
3151
+ }
2671
3152
  function physicalRowIndex(buffer, y) {
2672
3153
  return (buffer.gridStart + y) % buffer.rows;
2673
3154
  }
@@ -2740,9 +3221,17 @@ function putCell(buffer, x, y, ch, style) {
2740
3221
  markDirty(buffer, y);
2741
3222
  return;
2742
3223
  }
2743
- row[x] = createCell(ch, style);
3224
+ const cell = createCell(ch, style);
3225
+ row[x] = cell;
2744
3226
  if (width === 2 && x + 1 < buffer.cols)
2745
3227
  row[x + 1] = createContinuationCell(style);
3228
+ if (buffer.soaFingerprints) {
3229
+ const physY = physicalRowIndex(buffer, y);
3230
+ updateCellFingerprint(buffer, physY, x, cell.ch, cell.style);
3231
+ if (width === 2 && x + 1 < buffer.cols) {
3232
+ updateCellFingerprint(buffer, physY, x + 1, "", cell.style);
3233
+ }
3234
+ }
2746
3235
  markDirty(buffer, y);
2747
3236
  }
2748
3237
  function fillRect(buffer, x, y, w, h18, ch = " ", style) {
@@ -2766,6 +3255,10 @@ function fillRect(buffer, x, y, w, h18, ch = " ", style) {
2766
3255
  row.fill(fillCell, x0, x1);
2767
3256
  if (x1 < buffer.cols && ((_b2 = row[x1]) == null ? void 0 : _b2.continuation))
2768
3257
  row[x1] = blank;
3258
+ if (buffer.soaFingerprints) {
3259
+ const physY = physicalRowIndex(buffer, yy);
3260
+ updateRangeFingerprint(buffer, physY, x0, x1, fillCell.ch, fillCell.style);
3261
+ }
2769
3262
  markDirty(buffer, yy);
2770
3263
  }
2771
3264
  return;
@@ -2834,6 +3327,19 @@ function blankRow(buffer) {
2834
3327
  pooled[x] = blank;
2835
3328
  return pooled;
2836
3329
  }
3330
+ function recomputeFingerprintsForRows(buffer, startY, endY) {
3331
+ if (!buffer.soaFingerprints || !buffer.fingerprintFn)
3332
+ return;
3333
+ for (let y = startY; y < endY; y++) {
3334
+ const row = getBufferRow(buffer, y);
3335
+ const physY = physicalRowIndex(buffer, y);
3336
+ const base = physY * buffer.cols;
3337
+ for (let x = 0; x < buffer.cols; x++) {
3338
+ const cell = row[x];
3339
+ buffer.soaFingerprints[base + x] = buffer.fingerprintFn(cell.ch, cell.style);
3340
+ }
3341
+ }
3342
+ }
2837
3343
  function scrollBuffer(buffer, lines) {
2838
3344
  const n = Math.trunc(lines);
2839
3345
  if (n === 0 || buffer.rows === 0)
@@ -2868,6 +3374,70 @@ function scrollBuffer(buffer, lines) {
2868
3374
  markAllDirty(buffer);
2869
3375
  buffer.cursorY = clamp(buffer.cursorY - n, 0, Math.max(0, buffer.rows - 1));
2870
3376
  }
3377
+ function scrollBufferRegion(buffer, startY, endY, lines) {
3378
+ const n = Math.trunc(lines);
3379
+ if (n === 0 || buffer.rows === 0)
3380
+ return [];
3381
+ const start = clamp(Math.floor(startY), 0, buffer.rows);
3382
+ const end = clamp(Math.floor(endY), 0, buffer.rows);
3383
+ const height = end - start;
3384
+ if (height <= 0)
3385
+ return [];
3386
+ const absDelta = Math.abs(n);
3387
+ const insertedRows = [];
3388
+ const replaceRegionWithBlankRows = () => {
3389
+ for (let y = start; y < end; y++) {
3390
+ const physY = physicalRowIndex(buffer, y);
3391
+ buffer.rowPool.push(buffer.grid[physY]);
3392
+ buffer.grid[physY] = blankRow(buffer);
3393
+ markDirty(buffer, y);
3394
+ insertedRows.push(y);
3395
+ }
3396
+ };
3397
+ if (absDelta >= height) {
3398
+ replaceRegionWithBlankRows();
3399
+ recomputeFingerprintsForRows(buffer, start, end);
3400
+ if (buffer.cursorY >= start && buffer.cursorY < end)
3401
+ buffer.cursorY = clamp(buffer.cursorY - n, start, Math.max(start, end - 1));
3402
+ return insertedRows;
3403
+ }
3404
+ const regionRows = [];
3405
+ for (let y = start; y < end; y++)
3406
+ regionRows.push(getBufferRow(buffer, y));
3407
+ const nextRows = Array.from({ length: height }, () => []);
3408
+ if (n > 0) {
3409
+ const movedCount = height - n;
3410
+ for (let i = 0; i < movedCount; i++)
3411
+ nextRows[i] = regionRows[i + n];
3412
+ for (let i = 0; i < n; i++) {
3413
+ buffer.rowPool.push(regionRows[i]);
3414
+ const y = end - n + i;
3415
+ nextRows[movedCount + i] = blankRow(buffer);
3416
+ markDirty(buffer, y);
3417
+ insertedRows.push(y);
3418
+ }
3419
+ } else {
3420
+ const insertedCount = -n;
3421
+ const movedCount = height - insertedCount;
3422
+ for (let i = 0; i < insertedCount; i++) {
3423
+ buffer.rowPool.push(regionRows[height - insertedCount + i]);
3424
+ const y = start + i;
3425
+ nextRows[i] = blankRow(buffer);
3426
+ markDirty(buffer, y);
3427
+ insertedRows.push(y);
3428
+ }
3429
+ for (let i = 0; i < movedCount; i++)
3430
+ nextRows[insertedCount + i] = regionRows[i];
3431
+ }
3432
+ for (let i = 0; i < height; i++) {
3433
+ const physY = physicalRowIndex(buffer, start + i);
3434
+ buffer.grid[physY] = nextRows[i];
3435
+ }
3436
+ recomputeFingerprintsForRows(buffer, start, end);
3437
+ if (buffer.cursorY >= start && buffer.cursorY < end)
3438
+ buffer.cursorY = clamp(buffer.cursorY - n, start, Math.max(start, end - 1));
3439
+ return insertedRows;
3440
+ }
2871
3441
  function resizeBuffer(buffer, cols, rows) {
2872
3442
  var _a2;
2873
3443
  const nextCols = Math.max(0, Math.floor(cols));
@@ -2904,6 +3474,22 @@ function resizeBuffer(buffer, cols, rows) {
2904
3474
  buffer.dirtyAll = nextRows > 0;
2905
3475
  buffer.cursorX = clamp(buffer.cursorX, 0, Math.max(0, nextCols));
2906
3476
  buffer.cursorY = clamp(buffer.cursorY, 0, Math.max(0, nextRows - 1));
3477
+ if (buffer.fingerprintFn) {
3478
+ const len = nextRows * nextCols;
3479
+ if (len > 0) {
3480
+ buffer.soaFingerprints = new Uint32Array(len);
3481
+ for (let y = 0; y < nextRows; y++) {
3482
+ const row = nextGrid[y];
3483
+ const base = y * nextCols;
3484
+ for (let x = 0; x < nextCols; x++) {
3485
+ const cell = row[x];
3486
+ buffer.soaFingerprints[base + x] = buffer.fingerprintFn(cell.ch, cell.style);
3487
+ }
3488
+ }
3489
+ } else {
3490
+ buffer.soaFingerprints = null;
3491
+ }
3492
+ }
2907
3493
  }
2908
3494
  function snapshotText(buffer) {
2909
3495
  const out = [];
@@ -3092,6 +3678,40 @@ function scrollPlaneCoverage(state, lines) {
3092
3678
  for (let i = 0; i < -n; i++)
3093
3679
  state.coverage.unshift(new Uint8Array(cols));
3094
3680
  }
3681
+ function scrollPlaneCoverageRegion(state, startY, endY, lines) {
3682
+ const n = Math.trunc(lines);
3683
+ if (n === 0 || state.coverage.length === 0)
3684
+ return;
3685
+ const start = Math.max(0, Math.min(state.coverage.length, Math.floor(startY)));
3686
+ const end = Math.max(0, Math.min(state.coverage.length, Math.floor(endY)));
3687
+ const height = end - start;
3688
+ if (height <= 0)
3689
+ return;
3690
+ const cols = state.buffer.cols;
3691
+ if (Math.abs(n) >= height) {
3692
+ for (let y = start; y < end; y++)
3693
+ state.coverage[y] = new Uint8Array(cols);
3694
+ return;
3695
+ }
3696
+ const region = state.coverage.slice(start, end);
3697
+ const next = Array.from({ length: height }, () => new Uint8Array(0));
3698
+ if (n > 0) {
3699
+ const movedCount = height - n;
3700
+ for (let i = 0; i < movedCount; i++)
3701
+ next[i] = region[i + n];
3702
+ for (let i = 0; i < n; i++)
3703
+ next[movedCount + i] = new Uint8Array(cols);
3704
+ } else {
3705
+ const insertedCount = -n;
3706
+ const movedCount = height - insertedCount;
3707
+ for (let i = 0; i < insertedCount; i++)
3708
+ next[i] = new Uint8Array(cols);
3709
+ for (let i = 0; i < movedCount; i++)
3710
+ next[insertedCount + i] = region[i];
3711
+ }
3712
+ for (let i = 0; i < height; i++)
3713
+ state.coverage[start + i] = next[i];
3714
+ }
3095
3715
  function resizePlaneState(state, cols, rows) {
3096
3716
  resizeBuffer(state.buffer, cols, rows);
3097
3717
  state.coverage = resizeCoverage(state.coverage, state.buffer.cols, state.buffer.rows);
@@ -3127,6 +3747,14 @@ function resetPlaneRowsForRender(terminal, plane, dirtyRows) {
3127
3747
  const internals = terminal[TERMINAL_PLANE_INTERNALS];
3128
3748
  internals == null ? void 0 : internals.resetRowsForRender(plane, dirtyRows);
3129
3749
  }
3750
+ function getPlaneRowCoverageKind(terminal, plane, y) {
3751
+ const internals = terminal[TERMINAL_PLANE_INTERNALS];
3752
+ return (internals == null ? void 0 : internals.getRowCoverageKind(plane, y)) ?? 0;
3753
+ }
3754
+ function scrollPlaneRows(terminal, plane, startY, endY, lines) {
3755
+ const internals = terminal[TERMINAL_PLANE_INTERNALS];
3756
+ internals == null ? void 0 : internals.scrollRows(plane, startY, endY, lines);
3757
+ }
3130
3758
  function createTerminal(opts) {
3131
3759
  const emitter = new Emitter();
3132
3760
  const compositeBuffer = createGridBuffer(opts.cols, opts.rows);
@@ -3136,6 +3764,7 @@ function createTerminal(opts) {
3136
3764
  let pendingCommit = false;
3137
3765
  let pendingCommitAllPlanes = false;
3138
3766
  let pendingCommitPlanes = null;
3767
+ const pendingPlaneScrollOps = /* @__PURE__ */ new Map();
3139
3768
  const planeTerminals = /* @__PURE__ */ new Map();
3140
3769
  let base;
3141
3770
  compositeBuffer.cursorVisible = false;
@@ -3274,8 +3903,171 @@ function createTerminal(opts) {
3274
3903
  scrollBuffer(state.buffer, lines);
3275
3904
  scrollPlaneCoverage(state, lines);
3276
3905
  }
3906
+ function normalizeScrollOperation(startY, endY, delta, totalRows) {
3907
+ const start = Math.max(0, Math.min(totalRows, Math.floor(startY)));
3908
+ const end = Math.max(0, Math.min(totalRows, Math.floor(endY)));
3909
+ const height = end - start;
3910
+ const lines = Math.trunc(delta);
3911
+ if (height <= 0 || lines === 0 || Math.abs(lines) >= height)
3912
+ return null;
3913
+ return { startY: start, endY: end, delta: lines };
3914
+ }
3915
+ function recordPendingScrollOp(plane, op) {
3916
+ const prev = pendingPlaneScrollOps.get(plane);
3917
+ if (prev && prev.startY === op.startY && prev.endY === op.endY) {
3918
+ const nextDelta = prev.delta + op.delta;
3919
+ if (nextDelta === 0) {
3920
+ pendingPlaneScrollOps.delete(plane);
3921
+ return;
3922
+ }
3923
+ const next = normalizeScrollOperation(
3924
+ op.startY,
3925
+ op.endY,
3926
+ nextDelta,
3927
+ compositeBuffer.rows
3928
+ );
3929
+ if (!next) {
3930
+ pendingPlaneScrollOps.delete(plane);
3931
+ return;
3932
+ }
3933
+ pendingPlaneScrollOps.set(plane, next);
3934
+ return;
3935
+ }
3936
+ pendingPlaneScrollOps.set(plane, op);
3937
+ }
3938
+ function takePendingScrollOps(planes) {
3939
+ const planesToTake = (planes == null ? void 0 : planes.length) ? planes : TERMINAL_RENDER_PLANES;
3940
+ const out = [];
3941
+ for (const plane of planesToTake) {
3942
+ const op = pendingPlaneScrollOps.get(plane);
3943
+ if (!op)
3944
+ continue;
3945
+ pendingPlaneScrollOps.delete(plane);
3946
+ out.push({ plane, ...op });
3947
+ }
3948
+ return out.length ? out : null;
3949
+ }
3950
+ function higherPlanesFor(plane) {
3951
+ const planeIndex = TERMINAL_RENDER_PLANES.indexOf(plane);
3952
+ if (planeIndex < 0)
3953
+ return [];
3954
+ return TERMINAL_RENDER_PLANES.slice(planeIndex + 1);
3955
+ }
3956
+ function higherPlaneCoverageKind(plane, y) {
3957
+ var _a2;
3958
+ const higherPlanes = higherPlanesFor(plane);
3959
+ if (!higherPlanes.length || y < 0 || y >= compositeBuffer.rows)
3960
+ return 0;
3961
+ let covered = 0;
3962
+ for (let x = 0; x < compositeBuffer.cols; x++) {
3963
+ let cellCovered = false;
3964
+ for (const higherPlane of higherPlanes) {
3965
+ const state = planeStates.get(higherPlane);
3966
+ if ((_a2 = state == null ? void 0 : state.coverage[y]) == null ? void 0 : _a2[x]) {
3967
+ cellCovered = true;
3968
+ break;
3969
+ }
3970
+ }
3971
+ if (cellCovered)
3972
+ covered++;
3973
+ }
3974
+ if (covered === 0)
3975
+ return 0;
3976
+ if (covered >= compositeBuffer.cols)
3977
+ return 2;
3978
+ return 1;
3979
+ }
3980
+ function prepareCompositeScrollOps(pendingOps) {
3981
+ if (!(pendingOps == null ? void 0 : pendingOps.length)) {
3982
+ return {
3983
+ scrollOperations: null,
3984
+ extraDirtyRows: []
3985
+ };
3986
+ }
3987
+ const scrollOperations = [];
3988
+ const extraDirtyRows = /* @__PURE__ */ new Set();
3989
+ for (const pending of pendingOps) {
3990
+ const blockedRows = /* @__PURE__ */ new Set();
3991
+ const partiallyBlockedRows = /* @__PURE__ */ new Set();
3992
+ for (let y = pending.startY; y < pending.endY; y++) {
3993
+ const coverageKind = higherPlaneCoverageKind(pending.plane, y);
3994
+ if (coverageKind === 0)
3995
+ continue;
3996
+ blockedRows.add(y);
3997
+ if (coverageKind === 1)
3998
+ partiallyBlockedRows.add(y);
3999
+ }
4000
+ if (!blockedRows.size) {
4001
+ scrollOperations.push({
4002
+ startY: pending.startY,
4003
+ endY: pending.endY,
4004
+ delta: pending.delta
4005
+ });
4006
+ continue;
4007
+ }
4008
+ const absDelta = Math.abs(pending.delta);
4009
+ for (let y = pending.startY; y < pending.endY; y++) {
4010
+ if (partiallyBlockedRows.has(y))
4011
+ extraDirtyRows.add(y);
4012
+ }
4013
+ let bandStart = -1;
4014
+ const flushBand = (bandEnd) => {
4015
+ if (bandStart < 0 || bandEnd <= bandStart)
4016
+ return;
4017
+ const bandHeight = bandEnd - bandStart;
4018
+ if (absDelta >= bandHeight) {
4019
+ for (let y = bandStart; y < bandEnd; y++)
4020
+ extraDirtyRows.add(y);
4021
+ bandStart = -1;
4022
+ return;
4023
+ }
4024
+ scrollOperations.push({
4025
+ startY: bandStart,
4026
+ endY: bandEnd,
4027
+ delta: pending.delta
4028
+ });
4029
+ if (pending.delta > 0) {
4030
+ for (let y = bandEnd - absDelta; y < bandEnd; y++)
4031
+ extraDirtyRows.add(y);
4032
+ } else {
4033
+ for (let y = bandStart; y < bandStart + absDelta; y++)
4034
+ extraDirtyRows.add(y);
4035
+ }
4036
+ bandStart = -1;
4037
+ };
4038
+ for (let y = pending.startY; y < pending.endY; y++) {
4039
+ if (blockedRows.has(y)) {
4040
+ flushBand(y);
4041
+ continue;
4042
+ }
4043
+ if (bandStart < 0)
4044
+ bandStart = y;
4045
+ }
4046
+ flushBand(pending.endY);
4047
+ }
4048
+ return {
4049
+ scrollOperations: scrollOperations.length ? scrollOperations : null,
4050
+ extraDirtyRows: Array.from(extraDirtyRows).sort((a, b) => a - b)
4051
+ };
4052
+ }
4053
+ function scrollRowsForPlane(plane, startY, endY, lines) {
4054
+ const state = getPlaneState(plane);
4055
+ const op = normalizeScrollOperation(
4056
+ startY,
4057
+ endY,
4058
+ lines,
4059
+ state.buffer.rows
4060
+ );
4061
+ if (!op)
4062
+ return;
4063
+ scrollBufferRegion(state.buffer, op.startY, op.endY, op.delta);
4064
+ scrollPlaneCoverageRegion(state, op.startY, op.endY, op.delta);
4065
+ recordPendingScrollOp(plane, op);
4066
+ }
3277
4067
  function composeRows(rows) {
3278
4068
  const blank = createBlankCell();
4069
+ const fpFn = compositeBuffer.fingerprintFn;
4070
+ const fpArr = compositeBuffer.soaFingerprints;
3279
4071
  const rowList = rows ?? Array.from({ length: compositeBuffer.rows }, (_, index) => index);
3280
4072
  for (const y of rowList) {
3281
4073
  if (y < 0 || y >= compositeBuffer.rows)
@@ -3310,6 +4102,14 @@ function createTerminal(opts) {
3310
4102
  }
3311
4103
  }
3312
4104
  }
4105
+ if (fpFn && fpArr) {
4106
+ const physY = (compositeBuffer.gridStart + y) % compositeBuffer.rows;
4107
+ const base2 = physY * cols;
4108
+ for (let x = 0; x < cols; x++) {
4109
+ const cell = dst[x];
4110
+ fpArr[base2 + x] = fpFn(cell.ch, cell.style);
4111
+ }
4112
+ }
3313
4113
  markDirty(compositeBuffer, y);
3314
4114
  }
3315
4115
  }
@@ -3506,6 +4306,14 @@ function createTerminal(opts) {
3506
4306
  return all.slice(
3507
4307
  Math.max(0, all.length - Math.max(0, Math.floor(count)))
3508
4308
  );
4309
+ },
4310
+ setFingerprintFn(fn) {
4311
+ assertNotDisposed();
4312
+ base.setFingerprintFn(fn);
4313
+ },
4314
+ getRowFingerprints(y) {
4315
+ assertNotDisposed();
4316
+ return base.getRowFingerprints(y);
3509
4317
  }
3510
4318
  };
3511
4319
  planeTerminals.set(plane, api);
@@ -3580,6 +4388,9 @@ function createTerminal(opts) {
3580
4388
  return pendingDirtyRowsForPlanes((meta == null ? void 0 : meta.planes) ?? null);
3581
4389
  }
3582
4390
  const planesToCompose = ((_b2 = meta == null ? void 0 : meta.planes) == null ? void 0 : _b2.length) ? meta.planes : TERMINAL_RENDER_PLANES;
4391
+ const pendingScrollOps = takePendingScrollOps(planesToCompose);
4392
+ const preparedScroll = prepareCompositeScrollOps(pendingScrollOps);
4393
+ const scrollOperations = preparedScroll.scrollOperations;
3583
4394
  let composeAllRows = false;
3584
4395
  const dirtyRows = /* @__PURE__ */ new Set();
3585
4396
  for (const plane of planesToCompose) {
@@ -3594,12 +4405,23 @@ function createTerminal(opts) {
3594
4405
  for (const y of rows)
3595
4406
  dirtyRows.add(y);
3596
4407
  }
4408
+ for (const y of preparedScroll.extraDirtyRows)
4409
+ dirtyRows.add(y);
3597
4410
  const normalizedDirtyRows = composeAllRows ? null : Array.from(dirtyRows).sort((a, b) => a - b);
4411
+ if (scrollOperations) {
4412
+ for (const op of scrollOperations)
4413
+ scrollBufferRegion(compositeBuffer, op.startY, op.endY, op.delta);
4414
+ }
3598
4415
  if (normalizedDirtyRows !== null && normalizedDirtyRows.length === 0) {
3599
4416
  const pendingCompositeRows = collectAndClearDirtyRows(compositeBuffer);
3600
4417
  if (pendingCompositeRows !== null && pendingCompositeRows.length === 0)
3601
4418
  return pendingCompositeRows;
3602
- emitter.emit("commit", { dirtyRows: pendingCompositeRows, planes: (meta == null ? void 0 : meta.planes) ?? null });
4419
+ emitter.emit("commit", {
4420
+ dirtyRows: pendingCompositeRows,
4421
+ planes: (meta == null ? void 0 : meta.planes) ?? null,
4422
+ sync: meta == null ? void 0 : meta.sync,
4423
+ scrollOperations
4424
+ });
3603
4425
  return pendingCompositeRows;
3604
4426
  }
3605
4427
  composeRows(normalizedDirtyRows);
@@ -3609,7 +4431,12 @@ function createTerminal(opts) {
3609
4431
  const committedRows = collectAndClearDirtyRows(compositeBuffer);
3610
4432
  if (committedRows !== null && committedRows.length === 0)
3611
4433
  return committedRows;
3612
- emitter.emit("commit", { dirtyRows: committedRows, planes: (meta == null ? void 0 : meta.planes) ?? null });
4434
+ emitter.emit("commit", {
4435
+ dirtyRows: committedRows,
4436
+ planes: (meta == null ? void 0 : meta.planes) ?? null,
4437
+ sync: meta == null ? void 0 : meta.sync,
4438
+ scrollOperations
4439
+ });
3613
4440
  return committedRows;
3614
4441
  },
3615
4442
  on(event, cb) {
@@ -3675,6 +4502,14 @@ function createTerminal(opts) {
3675
4502
  return all.slice(
3676
4503
  Math.max(0, all.length - Math.max(0, Math.floor(count)))
3677
4504
  );
4505
+ },
4506
+ setFingerprintFn(fn) {
4507
+ assertNotDisposed();
4508
+ setFingerprintFn(compositeBuffer, fn);
4509
+ },
4510
+ getRowFingerprints(y) {
4511
+ assertNotDisposed();
4512
+ return getRowFingerprints(compositeBuffer, y);
3678
4513
  }
3679
4514
  };
3680
4515
  base[TERMINAL_PLANE_INTERNALS] = {
@@ -3689,6 +4524,27 @@ function createTerminal(opts) {
3689
4524
  }
3690
4525
  for (const y of rows)
3691
4526
  clearPlaneCoverageRow(state, y);
4527
+ },
4528
+ getRowCoverageKind(plane, y) {
4529
+ const state = getPlaneState(plane);
4530
+ if (y < 0 || y >= state.coverage.length)
4531
+ return 0;
4532
+ const row = state.coverage[y];
4533
+ if (!(row == null ? void 0 : row.length))
4534
+ return 0;
4535
+ let covered = 0;
4536
+ for (let x = 0; x < row.length; x++) {
4537
+ if (row[x])
4538
+ covered++;
4539
+ }
4540
+ if (covered === 0)
4541
+ return 0;
4542
+ if (covered >= row.length)
4543
+ return 2;
4544
+ return 1;
4545
+ },
4546
+ scrollRows(plane, startY, endY, lines) {
4547
+ scrollRowsForPlane(plane, startY, endY, lines);
3692
4548
  }
3693
4549
  };
3694
4550
  return base;
@@ -3797,8 +4653,8 @@ function createHeadlessRoot() {
3797
4653
  }
3798
4654
 
3799
4655
  // src/events/manager/cli-event-manager.ts
3800
- var import_node_fs2 = require("fs");
3801
- var import_node_process4 = __toESM(require("process"), 1);
4656
+ var import_node_fs3 = require("fs");
4657
+ var import_node_process5 = __toESM(require("process"), 1);
3802
4658
  function contains(rect, x, y) {
3803
4659
  return x >= rect.x && y >= rect.y && x < rect.x + rect.w && y < rect.y + rect.h;
3804
4660
  }
@@ -3811,7 +4667,7 @@ function area(rect) {
3811
4667
  function isVisible(node) {
3812
4668
  return node.visible !== false;
3813
4669
  }
3814
- function now() {
4670
+ function now2() {
3815
4671
  return typeof performance !== "undefined" ? performance.now() : Date.now();
3816
4672
  }
3817
4673
  function rectRowRange(rect) {
@@ -3833,6 +4689,7 @@ function createCliEventManager(options) {
3833
4689
  let hoverPath = [];
3834
4690
  const record = options == null ? void 0 : options.record;
3835
4691
  const onFocusChange = options == null ? void 0 : options.onFocusChange;
4692
+ const latency = getCliLatencyProfiler();
3836
4693
  const MODAL_FOCUS_Z_THRESHOLD = 100;
3837
4694
  const MODAL_ROOT_MIN_AREA = 40;
3838
4695
  function focusLockTarget() {
@@ -4014,7 +4871,7 @@ function createCliEventManager(options) {
4014
4871
  bubbles: true,
4015
4872
  cancelable: true,
4016
4873
  defaultPrevented: false,
4017
- timeStamp: now(),
4874
+ timeStamp: now2(),
4018
4875
  __stopped: false,
4019
4876
  stopPropagation() {
4020
4877
  this.__stopped = true;
@@ -4038,10 +4895,10 @@ function createCliEventManager(options) {
4038
4895
  handler(event);
4039
4896
  } catch (err) {
4040
4897
  try {
4041
- const env = (_a2 = import_node_process4.default) == null ? void 0 : _a2.env;
4898
+ const env = (_a2 = import_node_process5.default) == null ? void 0 : _a2.env;
4042
4899
  if ((env == null ? void 0 : env.DIMCODE_DEBUG) === "1") {
4043
4900
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[1].slice(0, -1);
4044
- (0, import_node_fs2.appendFileSync)(
4901
+ (0, import_node_fs3.appendFileSync)(
4045
4902
  "/tmp/goatchain-debug.log",
4046
4903
  `[${timestamp}] [EVENT-MGR] ERROR in handler ${node.id}.${handlerKey}: ${err}
4047
4904
  `
@@ -4275,57 +5132,67 @@ function createCliEventManager(options) {
4275
5132
  },
4276
5133
  dispatch(event) {
4277
5134
  record == null ? void 0 : record(event);
4278
- if (event.type === "keydown" || event.type === "keyup") {
4279
- return dispatchToFocused(event.type, event);
4280
- }
4281
- if (event.type === "pointerdown" || event.type === "pointermove" || event.type === "pointerup" || event.type === "click" || event.type === "dblclick" || event.type === "contextmenu" || event.type === "wheel") {
4282
- if (event.type === "pointerdown") {
4283
- const list = candidatesAt(event.cellX, event.cellY);
4284
- const target = pickTarget(list);
4285
- if (target == null ? void 0 : target.focusable)
4286
- setFocus(target.id);
4287
- capturedId = (target == null ? void 0 : target.id) ?? null;
4288
- updateHover(target, event);
4289
- dispatchPointerEvent("pointerdown", event, target);
4290
- return false;
4291
- }
4292
- if (event.type === "pointermove" && capturedId) {
4293
- const target = nodes.get(capturedId) ?? null;
4294
- if (!target)
4295
- return false;
4296
- updateHover(target, event);
4297
- const path = ancestorsForTarget(target);
4298
- const ev = buildPointerEvent("pointermove", path, event);
4299
- dispatchWithPhases("pointermove", path, target, ev);
4300
- return ev.defaultPrevented;
4301
- }
4302
- if (event.type === "pointermove") {
4303
- const list = candidatesAt(event.cellX, event.cellY);
4304
- const target = pickTarget(list);
4305
- updateHover(target, event);
4306
- dispatchPointerEvent("pointermove", event, target);
4307
- return false;
5135
+ latency == null ? void 0 : latency.recordEventDispatchStart(event);
5136
+ let prevented = false;
5137
+ try {
5138
+ if (event.type === "keydown" || event.type === "keyup") {
5139
+ prevented = dispatchToFocused(event.type, event);
5140
+ return prevented;
4308
5141
  }
4309
- if (event.type === "pointerup" && capturedId) {
4310
- const target = nodes.get(capturedId) ?? null;
4311
- const path = target ? ancestorsForTarget(target) : [];
4312
- const ev = buildPointerEvent("pointerup", path, event);
4313
- dispatchWithPhases("pointerup", path, target, ev);
4314
- capturedId = null;
4315
- return ev.defaultPrevented;
5142
+ if (event.type === "pointerdown" || event.type === "pointermove" || event.type === "pointerup" || event.type === "click" || event.type === "dblclick" || event.type === "contextmenu" || event.type === "wheel") {
5143
+ if (event.type === "pointerdown") {
5144
+ const list = candidatesAt(event.cellX, event.cellY);
5145
+ const target = pickTarget(list);
5146
+ if (target == null ? void 0 : target.focusable)
5147
+ setFocus(target.id);
5148
+ capturedId = (target == null ? void 0 : target.id) ?? null;
5149
+ updateHover(target, event);
5150
+ dispatchPointerEvent("pointerdown", event, target);
5151
+ return prevented;
5152
+ }
5153
+ if (event.type === "pointermove" && capturedId) {
5154
+ const target = nodes.get(capturedId) ?? null;
5155
+ if (!target)
5156
+ return prevented;
5157
+ updateHover(target, event);
5158
+ const path = ancestorsForTarget(target);
5159
+ const ev = buildPointerEvent("pointermove", path, event);
5160
+ dispatchWithPhases("pointermove", path, target, ev);
5161
+ prevented = ev.defaultPrevented;
5162
+ return prevented;
5163
+ }
5164
+ if (event.type === "pointermove") {
5165
+ const list = candidatesAt(event.cellX, event.cellY);
5166
+ const target = pickTarget(list);
5167
+ updateHover(target, event);
5168
+ dispatchPointerEvent("pointermove", event, target);
5169
+ return prevented;
5170
+ }
5171
+ if (event.type === "pointerup" && capturedId) {
5172
+ const target = nodes.get(capturedId) ?? null;
5173
+ const path = target ? ancestorsForTarget(target) : [];
5174
+ const ev = buildPointerEvent("pointerup", path, event);
5175
+ dispatchWithPhases("pointerup", path, target, ev);
5176
+ capturedId = null;
5177
+ prevented = ev.defaultPrevented;
5178
+ return prevented;
5179
+ }
5180
+ if (event.type === "pointerup") {
5181
+ dispatchPointerEvent("pointerup", event);
5182
+ capturedId = null;
5183
+ return prevented;
5184
+ }
5185
+ dispatchPointerEvent(event.type, event);
5186
+ return prevented;
4316
5187
  }
4317
- if (event.type === "pointerup") {
4318
- dispatchPointerEvent("pointerup", event);
4319
- capturedId = null;
4320
- return false;
5188
+ if (event.type === "beforeinput" || event.type === "input" || event.type === "compositionstart" || event.type === "compositionupdate" || event.type === "compositionend" || event.type === "paste") {
5189
+ prevented = dispatchToFocusedText(event.type, event);
5190
+ return prevented;
4321
5191
  }
4322
- dispatchPointerEvent(event.type, event);
4323
- return false;
4324
- }
4325
- if (event.type === "beforeinput" || event.type === "input" || event.type === "compositionstart" || event.type === "compositionupdate" || event.type === "compositionend" || event.type === "paste") {
4326
- return dispatchToFocusedText(event.type, event);
5192
+ return prevented;
5193
+ } finally {
5194
+ latency == null ? void 0 : latency.recordEventDispatchEnd(event, { defaultPrevented: prevented });
4327
5195
  }
4328
- return false;
4329
5196
  },
4330
5197
  debugNodes() {
4331
5198
  return Array.from(nodes.values()).map((n) => ({
@@ -4360,7 +5227,7 @@ function containsRect2(outer, inner) {
4360
5227
  function area2(rect) {
4361
5228
  return Math.max(0, rect.w) * Math.max(0, rect.h);
4362
5229
  }
4363
- function now2() {
5230
+ function now3() {
4364
5231
  return typeof performance !== "undefined" ? performance.now() : Date.now();
4365
5232
  }
4366
5233
  function rectRowRange2(rect) {
@@ -4383,9 +5250,9 @@ function createEventManager(container, metrics2, options) {
4383
5250
  let hoverPath = [];
4384
5251
  let lastPointerMoveEvent = null;
4385
5252
  const rawRecord = options == null ? void 0 : options.record;
4386
- const recordStart = now2();
5253
+ const recordStart = now3();
4387
5254
  const record = rawRecord ? (event) => {
4388
- rawRecord({ ...event, time: now2() - recordStart });
5255
+ rawRecord({ ...event, time: now3() - recordStart });
4389
5256
  } : void 0;
4390
5257
  const onFocusChange = options == null ? void 0 : options.onFocusChange;
4391
5258
  const textInputTarget = (options == null ? void 0 : options.textInputTarget) ?? null;
@@ -4609,7 +5476,7 @@ function createEventManager(container, metrics2, options) {
4609
5476
  bubbles: true,
4610
5477
  cancelable: true,
4611
5478
  defaultPrevented: false,
4612
- timeStamp: now2(),
5479
+ timeStamp: now3(),
4613
5480
  __stopped: false,
4614
5481
  stopPropagation() {
4615
5482
  this.__stopped = true;
@@ -5479,15 +6346,15 @@ function createTraceStore(opts) {
5479
6346
  }
5480
6347
 
5481
6348
  // src/observability/tui-profiler.ts
5482
- var import_node_fs3 = require("fs");
5483
- var import_node_process5 = __toESM(require("process"), 1);
6349
+ var import_node_fs4 = require("fs");
6350
+ var import_node_process6 = __toESM(require("process"), 1);
5484
6351
  function defaultNow() {
5485
6352
  const p = globalThis.performance;
5486
6353
  if (p && typeof p.now === "function")
5487
6354
  return p.now();
5488
6355
  return Date.now();
5489
6356
  }
5490
- function parseEnabled(v) {
6357
+ function parseEnabled2(v) {
5491
6358
  return String(v ?? "").trim() === "1";
5492
6359
  }
5493
6360
  function parseLogFormat(v) {
@@ -5512,8 +6379,8 @@ function formatClockTime(ms) {
5512
6379
  }
5513
6380
  function createTuiProfiler(name) {
5514
6381
  var _a2, _b2;
5515
- const env = (_a2 = import_node_process5.default) == null ? void 0 : _a2.env;
5516
- if (!parseEnabled(env == null ? void 0 : env.DIMCODE_PROFILE_TUI))
6382
+ const env = (_a2 = import_node_process6.default) == null ? void 0 : _a2.env;
6383
+ if (!parseEnabled2(env == null ? void 0 : env.DIMCODE_PROFILE_TUI))
5517
6384
  return null;
5518
6385
  const format = parseLogFormat(env == null ? void 0 : env.DIMCODE_PROFILE_TUI_FORMAT);
5519
6386
  const logDest = parseLogDest(env == null ? void 0 : env.DIMCODE_PROFILE_TUI_LOG_DEST);
@@ -5536,8 +6403,8 @@ function createTuiProfiler(name) {
5536
6403
  const invalidatePlaneCounts = /* @__PURE__ */ new Map();
5537
6404
  const renderPlaneCounts = /* @__PURE__ */ new Map();
5538
6405
  const logEveryMs = Number((env == null ? void 0 : env.DIMCODE_PROFILE_TUI_LOG_EVERY_MS) ?? 1e3);
5539
- const now3 = defaultNow;
5540
- let lastLogAt = now3();
6406
+ const now4 = defaultNow;
6407
+ let lastLogAt = now4();
5541
6408
  function incrementPlaneCount(target, key) {
5542
6409
  const normalized = String(key ?? "all").trim() || "all";
5543
6410
  target.set(normalized, (target.get(normalized) ?? 0) + 1);
@@ -5546,7 +6413,7 @@ function createTuiProfiler(name) {
5546
6413
  const dest = logDest;
5547
6414
  if ((dest === "file" || dest === "both") && logPath) {
5548
6415
  try {
5549
- (0, import_node_fs3.appendFileSync)(logPath, `${line}
6416
+ (0, import_node_fs4.appendFileSync)(logPath, `${line}
5550
6417
  `);
5551
6418
  } catch {
5552
6419
  }
@@ -5556,7 +6423,7 @@ function createTuiProfiler(name) {
5556
6423
  }
5557
6424
  }
5558
6425
  function flushLog() {
5559
- const at = now3();
6426
+ const at = now4();
5560
6427
  const elapsed = at - lastLogAt;
5561
6428
  if (elapsed <= 0)
5562
6429
  return;
@@ -5638,7 +6505,7 @@ function createTuiProfiler(name) {
5638
6505
  );
5639
6506
  (_b2 = timer.unref) == null ? void 0 : _b2.call(timer);
5640
6507
  return {
5641
- now: now3,
6508
+ now: now4,
5642
6509
  recordInvalidate(info) {
5643
6510
  invalidates++;
5644
6511
  incrementPlaneCount(invalidatePlaneCounts, (info == null ? void 0 : info.plane) ?? "all");
@@ -6028,19 +6895,16 @@ var RenderStackKey = Symbol(
6028
6895
  "RenderStack"
6029
6896
  );
6030
6897
 
6031
- // src/vue/render/render-manager.ts
6032
- var import_node_process7 = __toESM(require("process"), 1);
6033
-
6034
6898
  // src/core/debug-logger.ts
6035
- var import_node_fs4 = require("fs");
6036
- var import_node_process6 = __toESM(require("process"), 1);
6899
+ var import_node_fs5 = require("fs");
6900
+ var import_node_process7 = __toESM(require("process"), 1);
6037
6901
  var LOG_FILE = "/tmp/goatchain-debug.log";
6038
6902
  var enabled = false;
6039
6903
  function createDebugLogger(enable = false) {
6040
6904
  enabled = enable;
6041
6905
  if (enabled) {
6042
6906
  try {
6043
- (0, import_node_fs4.writeFileSync)(
6907
+ (0, import_node_fs5.writeFileSync)(
6044
6908
  LOG_FILE,
6045
6909
  `=== GoatChain Debug Log Started at ${(/* @__PURE__ */ new Date()).toISOString()} ===
6046
6910
 
@@ -6073,13 +6937,13 @@ function write(data) {
6073
6937
  if (!enabled)
6074
6938
  return;
6075
6939
  try {
6076
- (0, import_node_fs4.appendFileSync)(LOG_FILE, data);
6940
+ (0, import_node_fs5.appendFileSync)(LOG_FILE, data);
6077
6941
  } catch {
6078
6942
  }
6079
6943
  }
6080
6944
  function isDebugEnabled() {
6081
6945
  var _a2;
6082
- const env = (_a2 = import_node_process6.default) == null ? void 0 : _a2.env;
6946
+ const env = (_a2 = import_node_process7.default) == null ? void 0 : _a2.env;
6083
6947
  return (env == null ? void 0 : env.DIMCODE_DEBUG) === "1" || (env == null ? void 0 : env.DEBUG) === "1";
6084
6948
  }
6085
6949
 
@@ -6615,6 +7479,30 @@ function createRenderManager(terminal) {
6615
7479
  }
6616
7480
  }
6617
7481
  }
7482
+ function markRows(plane, rows) {
7483
+ if (!rows.length)
7484
+ return;
7485
+ const state = getDirtyState(plane);
7486
+ for (let i = 0; i < rows.length; i++) {
7487
+ const y = Math.floor(rows[i] ?? -1);
7488
+ if (y < 0 || y >= terminalRows)
7489
+ continue;
7490
+ if (state.dirtyRowBits[y] === 0) {
7491
+ state.dirtyRowBits[y] = 1;
7492
+ state.dirtyRowCount++;
7493
+ if (y < state.dirtyMinY)
7494
+ state.dirtyMinY = y;
7495
+ if (y > state.dirtyMaxY)
7496
+ state.dirtyMaxY = y;
7497
+ }
7498
+ }
7499
+ }
7500
+ function scrollPlane(plane, startY, endY, delta) {
7501
+ scrollPlaneRows(terminal, plane, startY, endY, delta);
7502
+ }
7503
+ function invalidatePlane(plane) {
7504
+ getDirtyState(plane).allRowsDirty = true;
7505
+ }
6618
7506
  function register(node) {
6619
7507
  const id = `r${nextNodeId++}`;
6620
7508
  const rect = node.rect ?? null;
@@ -6645,8 +7533,14 @@ function createRenderManager(terminal) {
6645
7533
  const nextPlane = next.plane ?? prev.plane;
6646
7534
  const nextRect = next.rect ?? prev.rect;
6647
7535
  const { y0, y1 } = rectToYBounds(nextRect);
6648
- markRect(prev.plane, prev.rect);
6649
- markRect(nextPlane, nextRect);
7536
+ const dirtyRowsHint = next.dirtyRowsHint;
7537
+ const canUseDirtyRowsHint = nextPlane === prev.plane && dirtyRowsHint != null && dirtyRowsHint.length > 0;
7538
+ if (canUseDirtyRowsHint) {
7539
+ markRows(nextPlane, dirtyRowsHint);
7540
+ } else {
7541
+ markRect(prev.plane, prev.rect);
7542
+ markRect(nextPlane, nextRect);
7543
+ }
6650
7544
  const full = Object.freeze({
6651
7545
  ...prev,
6652
7546
  stack: next.stack ?? prev.stack,
@@ -6713,11 +7607,10 @@ function createRenderManager(terminal) {
6713
7607
  return false;
6714
7608
  }
6715
7609
  function render(options) {
6716
- var _a2;
6717
7610
  const renderStart = profiler == null ? void 0 : profiler.now();
6718
7611
  const activePlanes = (options == null ? void 0 : options.activePlanes) ?? null;
6719
7612
  const requestedPlanes = activePlanes ?? TERMINAL_RENDER_PLANES;
6720
- const env = (_a2 = import_node_process7.default) == null ? void 0 : _a2.env;
7613
+ const env = process == null ? void 0 : process.env;
6721
7614
  if ((env == null ? void 0 : env.DIMCODE_DEBUG) === "1")
6722
7615
  renderMgrDebugLog.render("[RENDER-MANAGER] render() called");
6723
7616
  if (!hasPendingDirtyWork(requestedPlanes)) {
@@ -6847,6 +7740,8 @@ function createRenderManager(terminal) {
6847
7740
  return {
6848
7741
  rootStack,
6849
7742
  createStack,
7743
+ invalidatePlane,
7744
+ scrollPlane,
6850
7745
  register,
6851
7746
  update,
6852
7747
  unregister,
@@ -6903,6 +7798,7 @@ function createTerminalApp(options) {
6903
7798
  const trace = createTraceStore({
6904
7799
  enabled: Boolean(globalThis.__VT_DEBUG_TRACE__)
6905
7800
  });
7801
+ const latency = getCliLatencyProfiler();
6906
7802
  const profiler = createTuiProfiler("cli-scheduler");
6907
7803
  const events = createCliEventManager({
6908
7804
  record: (event) => {
@@ -6917,7 +7813,8 @@ function createTerminalApp(options) {
6917
7813
  }
6918
7814
  });
6919
7815
  const render = createRenderManager(terminal);
6920
- const offCommit = terminal.on("commit", ({ dirtyRows, planes }) => {
7816
+ const offCommit = terminal.on("commit", ({ dirtyRows, planes, sync }) => {
7817
+ latency == null ? void 0 : latency.recordCommit({ dirtyRows, planes, sync });
6921
7818
  if (!trace.enabled.value)
6922
7819
  return;
6923
7820
  trace.push({
@@ -6935,10 +7832,10 @@ function createTerminalApp(options) {
6935
7832
  let lastFlushAtMs = 0;
6936
7833
  let scheduledAtMs = 0;
6937
7834
  let timer = null;
7835
+ let pendingInvalidateAfterFlush = false;
6938
7836
  let pendingInvalidateAllPlanes = false;
6939
7837
  const pendingInvalidatePlanes = /* @__PURE__ */ new Set();
6940
7838
  const env = ((_a2 = import_node_process8.default) == null ? void 0 : _a2.env) ?? {};
6941
- const isGhostty = "GHOSTTY_RESOURCES_DIR" in env;
6942
7839
  const throttleMs = (() => {
6943
7840
  const raw = env.DIMCODE_TUI_THROTTLE_MS;
6944
7841
  if (!raw)
@@ -6950,7 +7847,7 @@ function createTerminalApp(options) {
6950
7847
  var _a3, _b2;
6951
7848
  if (!((_b2 = (_a3 = import_node_process8.default) == null ? void 0 : _a3.stdout) == null ? void 0 : _b2.isTTY))
6952
7849
  return 0;
6953
- return isGhostty ? 24 : 16;
7850
+ return 16;
6954
7851
  })();
6955
7852
  function queueInvalidatePlane(plane) {
6956
7853
  if (!plane) {
@@ -6974,17 +7871,26 @@ function createTerminalApp(options) {
6974
7871
  pendingInvalidatePlanes.clear();
6975
7872
  return activePlanes;
6976
7873
  }
6977
- function flush() {
7874
+ function flush(sync) {
6978
7875
  if (disposed)
6979
7876
  return;
6980
7877
  scheduled = false;
6981
7878
  flushing = true;
6982
7879
  const activePlanes = takeActivePlanes();
6983
- render.render({ activePlanes });
6984
- terminal.commit({ planes: activePlanes });
6985
- flushing = false;
6986
- lastFlushAtMs = Date.now();
6987
- scheduledAtMs = 0;
7880
+ latency == null ? void 0 : latency.recordFlushStart({ sync, activePlanes });
7881
+ try {
7882
+ render.render({ activePlanes });
7883
+ terminal.commit({ planes: activePlanes, sync });
7884
+ } finally {
7885
+ latency == null ? void 0 : latency.recordFlushEnd();
7886
+ flushing = false;
7887
+ lastFlushAtMs = Date.now();
7888
+ scheduledAtMs = 0;
7889
+ }
7890
+ if (pendingInvalidateAfterFlush) {
7891
+ pendingInvalidateAfterFlush = false;
7892
+ flush(true);
7893
+ }
6988
7894
  }
6989
7895
  function clearScheduledTimer() {
6990
7896
  if (!timer)
@@ -7029,16 +7935,27 @@ function createTerminalApp(options) {
7029
7935
  return;
7030
7936
  clearScheduledTimer();
7031
7937
  scheduled = false;
7032
- flush();
7938
+ flush(true);
7033
7939
  }
7034
7940
  function invalidate(options2) {
7035
7941
  if (disposed)
7036
7942
  return;
7037
- if (flushing)
7038
- return;
7039
7943
  const priority = (options2 == null ? void 0 : options2.priority) ?? "normal";
7944
+ latency == null ? void 0 : latency.recordSchedulerInvalidate({
7945
+ priority,
7946
+ plane: (options2 == null ? void 0 : options2.plane) ?? null
7947
+ });
7948
+ if (flushing) {
7949
+ queueInvalidatePlane(options2 == null ? void 0 : options2.plane);
7950
+ pendingInvalidateAfterFlush = true;
7951
+ return;
7952
+ }
7040
7953
  queueInvalidatePlane(options2 == null ? void 0 : options2.plane);
7041
7954
  profiler == null ? void 0 : profiler.recordInvalidate({ plane: (options2 == null ? void 0 : options2.plane) ?? null });
7955
+ if (priority === "high") {
7956
+ flushNow();
7957
+ return;
7958
+ }
7042
7959
  const nowMs = Date.now();
7043
7960
  const throttleDelayMs = throttleMs > 0 ? Math.max(0, lastFlushAtMs + throttleMs - nowMs) : 0;
7044
7961
  const laneDelayMs = priority === "low" ? Math.max(throttleDelayMs, frameThrottleMs || 16) : priority === "normal" ? Math.max(throttleDelayMs, frameThrottleMs) : 0;
@@ -7181,6 +8098,7 @@ function createTerminalApp(options) {
7181
8098
  if (disposed)
7182
8099
  return;
7183
8100
  disposed = true;
8101
+ clearScheduledTimer();
7184
8102
  if (mounted)
7185
8103
  app.unmount();
7186
8104
  offResize == null ? void 0 : offResize();
@@ -7287,6 +8205,7 @@ function createStdoutRenderer(terminal, options) {
7287
8205
  let palette = options == null ? void 0 : options.palette;
7288
8206
  const trackResize = (options == null ? void 0 : options.trackResize) ?? true;
7289
8207
  const getImeAnchor = options == null ? void 0 : options.getImeAnchor;
8208
+ const cliLatency = getCliLatencyProfiler();
7290
8209
  const profiler = createTuiProfiler("stdout-renderer");
7291
8210
  function resolveUseSyncOutput() {
7292
8211
  var _a3;
@@ -7298,15 +8217,15 @@ function createStdoutRenderer(terminal, options) {
7298
8217
  const v = String(envValue).trim().toLowerCase();
7299
8218
  return v === "1" || v === "true" || v === "yes";
7300
8219
  }
7301
- const term = String(env2.TERM ?? "").toLowerCase();
7302
- const termProgram = String(env2.TERM_PROGRAM ?? "").toLowerCase();
8220
+ const term2 = String(env2.TERM ?? "").toLowerCase();
8221
+ const termProgram2 = String(env2.TERM_PROGRAM ?? "").toLowerCase();
7303
8222
  const termProgramVersion = env2.TERM_PROGRAM_VERSION;
7304
8223
  const isGhostty2 = "GHOSTTY_RESOURCES_DIR" in env2;
7305
- const isProblematicTerminal = isGhostty2 || termProgram.includes("apple_terminal") || term.includes("screen") && !term.includes("tmux");
8224
+ const isProblematicTerminal = isGhostty2 || termProgram2.includes("apple_terminal") || term2.includes("screen") && !term2.includes("tmux");
7306
8225
  if (isProblematicTerminal) {
7307
8226
  return false;
7308
8227
  }
7309
- const isGoodTerminal = termProgram.includes("iterm") && termProgramVersion && compareVersion(String(termProgramVersion), "3.5.0") >= 0;
8228
+ const isGoodTerminal = termProgram2.includes("iterm") && termProgramVersion && compareVersion(String(termProgramVersion), "3.5.0") >= 0;
7310
8229
  if (isGoodTerminal) {
7311
8230
  return true;
7312
8231
  }
@@ -7354,13 +8273,16 @@ function createStdoutRenderer(terminal, options) {
7354
8273
  return 0.6;
7355
8274
  })();
7356
8275
  const disableCursorPos = isGhostty && ((_d = (_c = import_node_process9.default) == null ? void 0 : _c.env) == null ? void 0 : _d.DIMCODE_DISABLE_CURSOR_POS) === "1";
7357
- const overlayTerminal = getPlaneTerminal(terminal, "overlay");
8276
+ const termProgram = String(env.TERM_PROGRAM ?? "").trim().toLowerCase();
8277
+ const term = String(env.TERM ?? "").trim().toLowerCase();
8278
+ const isVscodeTerminal = termProgram === "vscode" || "VSCODE_PID" in env || "VSCODE_IPC_HOOK_CLI" in env;
8279
+ const useConservativeDirtyRowRendering = out.isTTY !== false && !isVscodeTerminal && (isGhostty || "KITTY_WINDOW_ID" in env || "ALACRITTY_WINDOW_ID" in env || "ALACRITTY_LOG" in env || "WEZTERM_PANE" in env || "WEZTERM_EXECUTABLE" in env || termProgram.includes("kitty") || termProgram.includes("alacritty") || termProgram.includes("wezterm") || term.includes("kitty") || term.includes("alacritty"));
7358
8280
  const enableOsc8Links = (() => {
7359
8281
  if (!out.isTTY)
7360
8282
  return false;
7361
8283
  const raw = String(env.DIMCODE_TUI_LINKS ?? "").trim().toLowerCase();
7362
8284
  if (!raw) {
7363
- return !(String(env.TERM_PROGRAM ?? "").trim().toLowerCase() === "vscode" || "VSCODE_PID" in env || "VSCODE_IPC_HOOK_CLI" in env);
8285
+ return !isVscodeTerminal;
7364
8286
  }
7365
8287
  return !(raw === "0" || raw === "false" || raw === "no");
7366
8288
  })();
@@ -7369,12 +8291,16 @@ function createStdoutRenderer(terminal, options) {
7369
8291
  let pendingRender = false;
7370
8292
  let renderTimer = null;
7371
8293
  let writeEmaMs = 0;
8294
+ let lastCursorX = null;
8295
+ let lastCursorY = null;
7372
8296
  let accumulatedAllRows = false;
7373
8297
  let accumulatedDirtyBits = null;
7374
8298
  let accumulatedDirtyCount = 0;
7375
8299
  let accumulatedDirtyMin = Number.POSITIVE_INFINITY;
7376
8300
  let accumulatedDirtyMax = -1;
7377
- const MIN_FRAME_MS = !out.isTTY ? 0 : isGhostty ? 24 : 16;
8301
+ let accumulatedScrollOperations = null;
8302
+ let accumulatedSkipUnchangedDirtyRows = true;
8303
+ const MIN_FRAME_MS = !out.isTTY ? 0 : 16;
7378
8304
  function clampCellToViewport(cell, size) {
7379
8305
  const maxX = Math.max(0, Math.floor(size.cols) - 1);
7380
8306
  const maxY = Math.max(0, Math.floor(size.rows) - 1);
@@ -7398,17 +8324,35 @@ function createStdoutRenderer(terminal, options) {
7398
8324
  }
7399
8325
  const colorMode = resolveColorMode();
7400
8326
  const enableDim = colorMode === "truecolor";
8327
+ const COLOR_INDEX = {
8328
+ black: 1,
8329
+ red: 2,
8330
+ green: 3,
8331
+ yellow: 4,
8332
+ blue: 5,
8333
+ magenta: 6,
8334
+ cyan: 7,
8335
+ white: 8,
8336
+ blackBright: 9,
8337
+ redBright: 10,
8338
+ greenBright: 11,
8339
+ yellowBright: 12,
8340
+ blueBright: 13,
8341
+ magentaBright: 14,
8342
+ cyanBright: 15,
8343
+ whiteBright: 16
8344
+ };
7401
8345
  let styleKeyCache2 = /* @__PURE__ */ new WeakMap();
7402
8346
  function styleKey2(style) {
7403
8347
  const cached = styleKeyCache2.get(style);
7404
- if (cached)
8348
+ if (cached !== void 0)
7405
8349
  return cached;
7406
- const key = `${style.fg ?? ""}|${style.bg ?? defaultBg}|${style.bold ? 1 : 0}|${enableDim && style.dim ? 1 : 0}|${style.italic ? 1 : 0}|${style.underline ? 1 : 0}|${style.inverse ? 1 : 0}|${style.href ?? ""}`;
8350
+ const key = (COLOR_INDEX[style.fg] ?? 0) | (COLOR_INDEX[style.bg ?? defaultBg] ?? 0) << 5 | (style.bold ? 1 << 10 : 0) | (enableDim && style.dim ? 1 << 11 : 0) | (style.italic ? 1 << 12 : 0) | (style.underline ? 1 << 13 : 0) | (style.inverse ? 1 << 14 : 0) | (style.href ? 1 << 15 : 0);
7407
8351
  styleKeyCache2.set(style, key);
7408
8352
  return key;
7409
8353
  }
7410
8354
  function styleKeyFromParts(style) {
7411
- return `${style.fg ?? ""}|${style.bg ?? defaultBg}|${style.bold ? 1 : 0}|${enableDim && style.dim ? 1 : 0}|${style.italic ? 1 : 0}|${style.underline ? 1 : 0}|${style.inverse ? 1 : 0}|${style.href ?? ""}`;
8355
+ return (COLOR_INDEX[style.fg] ?? 0) | (COLOR_INDEX[style.bg ?? defaultBg] ?? 0) << 5 | (style.bold ? 1 << 10 : 0) | (enableDim && style.dim ? 1 << 11 : 0) | (style.italic ? 1 << 12 : 0) | (style.underline ? 1 << 13 : 0) | (style.inverse ? 1 << 14 : 0) | (style.href ? 1 << 15 : 0);
7412
8356
  }
7413
8357
  function openColor(fg) {
7414
8358
  if (!fg)
@@ -7455,7 +8399,47 @@ function createStdoutRenderer(terminal, options) {
7455
8399
  return result;
7456
8400
  }
7457
8401
  let lastRenderedRows = 0;
7458
- let renderedRowCache = [];
8402
+ let fpCols = 0;
8403
+ let fpRows = 0;
8404
+ let currentFP = new Uint32Array(0);
8405
+ let prevFP = new Uint32Array(0);
8406
+ let fpPrevValid = false;
8407
+ let prevOverlayBlockedRows = [];
8408
+ let prevOverlayPartialRows = [];
8409
+ function ensureFingerprints(cols, rows) {
8410
+ if (cols === fpCols && rows === fpRows)
8411
+ return;
8412
+ fpCols = cols;
8413
+ fpRows = rows;
8414
+ const len = cols * rows;
8415
+ currentFP = new Uint32Array(len);
8416
+ prevFP = new Uint32Array(len);
8417
+ fpPrevValid = false;
8418
+ prevOverlayBlockedRows = [];
8419
+ prevOverlayPartialRows = [];
8420
+ }
8421
+ function charHash16(ch) {
8422
+ if (ch.length <= 1)
8423
+ return ch.charCodeAt(0) || 0;
8424
+ let h18 = 33052;
8425
+ for (let i = 0; i < ch.length; i++) {
8426
+ h18 ^= ch.charCodeAt(i);
8427
+ h18 = h18 * 257 & 65535;
8428
+ }
8429
+ return h18;
8430
+ }
8431
+ function fingerprintRow(row, y, cols) {
8432
+ const rowFP = terminal.getRowFingerprints(y);
8433
+ if (rowFP && rowFP.length >= cols) {
8434
+ currentFP.set(rowFP.subarray(0, cols), y * fpCols);
8435
+ return;
8436
+ }
8437
+ const base = y * fpCols;
8438
+ for (let x = 0; x < cols; x++) {
8439
+ const cell = row[x];
8440
+ currentFP[base + x] = styleKey2(cell.style) << 16 | charHash16(cell.ch);
8441
+ }
8442
+ }
7459
8443
  const rowCursorToCol1 = [];
7460
8444
  const rowClearToEol = [];
7461
8445
  const rowTextPartsScratch = [];
@@ -7474,14 +8458,6 @@ function createStdoutRenderer(terminal, options) {
7474
8458
  rowClearToEol[y] = `\x1B[${y + 1};1H\x1B[K`;
7475
8459
  }
7476
8460
  };
7477
- const ensureRenderedRowCache = (rows) => {
7478
- if (renderedRowCache.length > rows) {
7479
- renderedRowCache.length = rows;
7480
- return;
7481
- }
7482
- while (renderedRowCache.length < rows)
7483
- renderedRowCache.push([]);
7484
- };
7485
8461
  const expandSpanStart = (row, startX) => {
7486
8462
  var _a3;
7487
8463
  let x = Math.max(0, Math.min(row.length, startX));
@@ -7501,40 +8477,296 @@ function createStdoutRenderer(terminal, options) {
7501
8477
  }
7502
8478
  return x;
7503
8479
  };
7504
- const resolveChangedSpan = (row, prevRow) => {
7505
- if (!prevRow || prevRow.length !== row.length) {
8480
+ const resolveChangedSpan = (row, y, cols) => {
8481
+ if (!fpCols || y >= fpRows || !fpPrevValid)
8482
+ return row.length ? { startX: 0, endXExclusive: row.length } : null;
8483
+ const base = y * fpCols;
8484
+ let startX = -1;
8485
+ let endXExclusive = -1;
8486
+ for (let x = 0; x < cols; x++) {
8487
+ if (currentFP[base + x] === prevFP[base + x])
8488
+ continue;
8489
+ if (startX === -1)
8490
+ startX = x;
8491
+ endXExclusive = x + 1;
8492
+ }
8493
+ if (startX === -1)
8494
+ return null;
8495
+ const expandedStart = expandSpanStart(row, startX);
8496
+ const expandedEnd = expandSpanEnd(row, endXExclusive);
8497
+ return expandedEnd > expandedStart ? { startX: expandedStart, endXExclusive: expandedEnd } : null;
8498
+ };
8499
+ const resolveChangedSpanAgainstFill = (row, y, cols, fillFingerprint) => {
8500
+ if (!fpCols || y >= fpRows)
7506
8501
  return row.length ? { startX: 0, endXExclusive: row.length } : null;
8502
+ const base = y * fpCols;
8503
+ let startX = -1;
8504
+ let endXExclusive = -1;
8505
+ for (let x = 0; x < cols; x++) {
8506
+ if (currentFP[base + x] === fillFingerprint)
8507
+ continue;
8508
+ if (startX === -1)
8509
+ startX = x;
8510
+ endXExclusive = x + 1;
7507
8511
  }
8512
+ if (startX === -1)
8513
+ return null;
8514
+ const expandedStart = expandSpanStart(row, startX);
8515
+ const expandedEnd = expandSpanEnd(row, endXExclusive);
8516
+ return expandedEnd > expandedStart ? { startX: expandedStart, endXExclusive: expandedEnd } : null;
8517
+ };
8518
+ const resolveChangedSpanAgainstReference = (row, y, cols) => {
8519
+ if (!fpCols || y >= fpRows)
8520
+ return row.length ? { startX: 0, endXExclusive: row.length } : null;
8521
+ const rowFP = terminal.getRowFingerprints(y);
8522
+ const base = y * fpCols;
7508
8523
  let startX = -1;
7509
8524
  let endXExclusive = -1;
7510
- for (let x = 0; x < row.length; x++) {
7511
- if (row[x] === prevRow[x])
8525
+ for (let x = 0; x < cols; x++) {
8526
+ const fingerprint = rowFP && rowFP.length >= cols ? rowFP[x] : styleKey2(row[x].style) << 16 | charHash16(row[x].ch);
8527
+ if (fingerprint === currentFP[base + x])
8528
+ continue;
8529
+ if (startX === -1)
8530
+ startX = x;
8531
+ endXExclusive = x + 1;
8532
+ }
8533
+ if (startX === -1)
8534
+ return null;
8535
+ const expandedStart = expandSpanStart(row, startX);
8536
+ const expandedEnd = expandSpanEnd(row, endXExclusive);
8537
+ return expandedEnd > expandedStart ? { startX: expandedStart, endXExclusive: expandedEnd } : null;
8538
+ };
8539
+ const rowMatchesPreviousFrame = (y, cols) => {
8540
+ if (!fpCols || y < 0 || y >= fpRows || !fpPrevValid)
8541
+ return false;
8542
+ const rowFP = terminal.getRowFingerprints(y);
8543
+ const row = rowFP && rowFP.length >= cols ? null : terminal.getRow(y);
8544
+ const base = y * fpCols;
8545
+ for (let x = 0; x < cols; x++) {
8546
+ const fingerprint = rowFP && rowFP.length >= cols ? rowFP[x] : styleKey2(row[x].style) << 16 | charHash16(row[x].ch);
8547
+ if (fingerprint !== prevFP[base + x])
8548
+ return false;
8549
+ }
8550
+ return true;
8551
+ };
8552
+ const rowHasNonDefaultBlankCell = (source, y, cols) => {
8553
+ if (!fpCols || y < 0 || y >= fpRows)
8554
+ return false;
8555
+ const blankFP = styleKeyFromParts({ bg: defaultBg }) << 16 | charHash16(" ");
8556
+ const base = y * fpCols;
8557
+ const limit = Math.min(cols, fpCols);
8558
+ for (let x = 0; x < limit; x++) {
8559
+ if (source[base + x] !== blankFP)
8560
+ return true;
8561
+ }
8562
+ return false;
8563
+ };
8564
+ function overlayPlaneCoverageRows(totalRows) {
8565
+ const blockedRows = [];
8566
+ const partialRows = [];
8567
+ for (let y = 0; y < totalRows; y++) {
8568
+ const kind = getPlaneRowCoverageKind(terminal, "overlay", y);
8569
+ if (!kind)
8570
+ continue;
8571
+ blockedRows.push(y);
8572
+ if (kind === 1)
8573
+ partialRows.push(y);
8574
+ }
8575
+ return { blockedRows, partialRows };
8576
+ }
8577
+ const enableScrollRegions = (() => {
8578
+ const raw = String(env.DIMCODE_TUI_SCROLL_REGIONS ?? "").trim();
8579
+ if (raw === "0" || raw === "false")
8580
+ return false;
8581
+ if (isGhostty || isVscodeTerminal)
8582
+ return false;
8583
+ return out.isTTY !== false;
8584
+ })();
8585
+ function prepareExplicitScrollOperations(operations, blockedRows) {
8586
+ if (!operations.length || !(blockedRows == null ? void 0 : blockedRows.size)) {
8587
+ return {
8588
+ operations,
8589
+ hiddenRows: /* @__PURE__ */ new Set(),
8590
+ trimmed: false,
8591
+ blockedInterior: false
8592
+ };
8593
+ }
8594
+ const hiddenRows = /* @__PURE__ */ new Set();
8595
+ const prepared = [];
8596
+ let trimmed = false;
8597
+ for (const op of operations) {
8598
+ let startY = op.startY;
8599
+ let endY = op.endY;
8600
+ while (startY < endY && blockedRows.has(startY)) {
8601
+ hiddenRows.add(startY);
8602
+ startY++;
8603
+ trimmed = true;
8604
+ }
8605
+ while (endY > startY && blockedRows.has(endY - 1)) {
8606
+ hiddenRows.add(endY - 1);
8607
+ endY--;
8608
+ trimmed = true;
8609
+ }
8610
+ for (let y = startY; y < endY; y++) {
8611
+ if (!blockedRows.has(y))
8612
+ continue;
8613
+ return {
8614
+ operations: null,
8615
+ hiddenRows,
8616
+ trimmed,
8617
+ blockedInterior: true
8618
+ };
8619
+ }
8620
+ if (endY <= startY || Math.abs(op.delta) >= endY - startY) {
8621
+ return {
8622
+ operations: null,
8623
+ hiddenRows,
8624
+ trimmed,
8625
+ blockedInterior: false
8626
+ };
8627
+ }
8628
+ prepared.push(
8629
+ startY === op.startY && endY === op.endY ? op : { startY, endY, delta: op.delta }
8630
+ );
8631
+ }
8632
+ return {
8633
+ operations: prepared,
8634
+ hiddenRows,
8635
+ trimmed,
8636
+ blockedInterior: false
8637
+ };
8638
+ }
8639
+ function largestDirtyBand(rows, blockedRows) {
8640
+ if (rows.length === 0)
8641
+ return null;
8642
+ const blocked = (blockedRows == null ? void 0 : blockedRows.length) ? new Set(blockedRows) : null;
8643
+ let bestStart = -1;
8644
+ let bestEnd = -1;
8645
+ let bandStart = -1;
8646
+ let prev = -1;
8647
+ for (let i = 0; i < rows.length; i++) {
8648
+ const y = rows[i];
8649
+ if (blocked == null ? void 0 : blocked.has(y)) {
8650
+ if (bandStart !== -1 && prev + 1 - bandStart > bestEnd - bestStart) {
8651
+ bestStart = bandStart;
8652
+ bestEnd = prev + 1;
8653
+ }
8654
+ bandStart = -1;
8655
+ prev = -1;
8656
+ continue;
8657
+ }
8658
+ if (bandStart === -1) {
8659
+ bandStart = y;
8660
+ prev = y;
8661
+ if (bestStart === -1) {
8662
+ bestStart = y;
8663
+ bestEnd = y + 1;
8664
+ }
8665
+ continue;
8666
+ }
8667
+ if (y === prev + 1) {
8668
+ prev = y;
7512
8669
  continue;
7513
- if (startX === -1)
7514
- startX = x;
7515
- endXExclusive = x + 1;
8670
+ }
8671
+ if (prev + 1 - bandStart > bestEnd - bestStart) {
8672
+ bestStart = bandStart;
8673
+ bestEnd = prev + 1;
8674
+ }
8675
+ bandStart = y;
8676
+ prev = y;
8677
+ }
8678
+ if (bandStart !== -1 && prev + 1 - bandStart > bestEnd - bestStart) {
8679
+ bestStart = bandStart;
8680
+ bestEnd = prev + 1;
7516
8681
  }
7517
- if (startX === -1 || endXExclusive === -1)
8682
+ if (bestStart === -1)
7518
8683
  return null;
7519
- const expandedStart = Math.min(
7520
- expandSpanStart(row, startX),
7521
- expandSpanStart(prevRow, startX)
7522
- );
7523
- const expandedEnd = Math.max(
7524
- expandSpanEnd(row, endXExclusive),
7525
- expandSpanEnd(prevRow, endXExclusive)
8684
+ const outsideRows = [];
8685
+ for (const y of rows) {
8686
+ if ((blocked == null ? void 0 : blocked.has(y)) || y < bestStart || y >= bestEnd) {
8687
+ outsideRows.push(y);
8688
+ }
8689
+ }
8690
+ return { start: bestStart, end: bestEnd, outsideRows };
8691
+ }
8692
+ function detectScrollShift(cols, dirtyRows, blockedRows) {
8693
+ if (!fpPrevValid || !fpCols || cols !== fpCols)
8694
+ return null;
8695
+ const band = largestDirtyBand(dirtyRows, blockedRows);
8696
+ if (!band || band.end - band.start < 3)
8697
+ return null;
8698
+ const maxDelta = Math.min(
8699
+ 12,
8700
+ Math.max(5, band.end - band.start - 1)
7526
8701
  );
7527
- return expandedEnd > expandedStart ? { startX: expandedStart, endXExclusive: expandedEnd } : null;
7528
- };
7529
- function overlayPlaneHasVisibleContent() {
7530
- const lines = overlayTerminal.snapshot().lines;
7531
- for (const line of lines) {
7532
- for (let i = 0; i < line.length; i++) {
7533
- if (line.charCodeAt(i) !== 32)
7534
- return true;
8702
+ for (let delta = -maxDelta; delta <= maxDelta; delta++) {
8703
+ if (delta === 0)
8704
+ continue;
8705
+ let matches = 0;
8706
+ let checked = 0;
8707
+ let mismatches = 0;
8708
+ for (let y = band.start; y < band.end; y++) {
8709
+ const srcY = y + delta;
8710
+ if (srcY < band.start || srcY >= band.end)
8711
+ continue;
8712
+ const informative = rowHasNonDefaultBlankCell(currentFP, y, cols) || rowHasNonDefaultBlankCell(prevFP, srcY, cols);
8713
+ if (!informative)
8714
+ continue;
8715
+ checked++;
8716
+ const curBase = y * fpCols;
8717
+ const prevBase = srcY * fpCols;
8718
+ let rowMatch = true;
8719
+ for (let x = 0; x < cols; x++) {
8720
+ if (currentFP[curBase + x] !== prevFP[prevBase + x]) {
8721
+ rowMatch = false;
8722
+ break;
8723
+ }
8724
+ }
8725
+ if (rowMatch) {
8726
+ matches++;
8727
+ } else {
8728
+ mismatches++;
8729
+ }
8730
+ }
8731
+ if (checked > 0 && matches >= 3 && mismatches <= Math.ceil(checked * 0.2)) {
8732
+ const absDelta = Math.abs(delta);
8733
+ let newRowStart;
8734
+ let newRowEnd;
8735
+ if (delta > 0) {
8736
+ newRowStart = Math.max(band.start, band.end - absDelta);
8737
+ newRowEnd = band.end;
8738
+ } else {
8739
+ newRowStart = band.start;
8740
+ newRowEnd = Math.min(band.end, band.start + absDelta);
8741
+ }
8742
+ const extraDirty = [...band.outsideRows];
8743
+ for (let y = band.start; y < band.end; y++) {
8744
+ const srcY = y + delta;
8745
+ if (srcY < band.start || srcY >= band.end)
8746
+ continue;
8747
+ const curBase = y * fpCols;
8748
+ const prevBase = srcY * fpCols;
8749
+ let rowMatch = true;
8750
+ for (let x = 0; x < cols; x++) {
8751
+ if (currentFP[curBase + x] !== prevFP[prevBase + x]) {
8752
+ rowMatch = false;
8753
+ break;
8754
+ }
8755
+ }
8756
+ if (!rowMatch)
8757
+ extraDirty.push(y);
8758
+ }
8759
+ return {
8760
+ regionStart: band.start,
8761
+ regionEnd: band.end,
8762
+ delta,
8763
+ newRowStart,
8764
+ newRowEnd,
8765
+ extraDirtyRows: extraDirty
8766
+ };
7535
8767
  }
7536
8768
  }
7537
- return false;
8769
+ return null;
7538
8770
  }
7539
8771
  function writeChunked(data) {
7540
8772
  if (!isGhostty) {
@@ -7569,7 +8801,7 @@ function createStdoutRenderer(terminal, options) {
7569
8801
  getDebugLog().error(`writeChunked: write error`, e);
7570
8802
  }
7571
8803
  }
7572
- function doRender(dirtyRows) {
8804
+ function doRender(dirtyRows, scrollOperations, skipUnchangedDirtyRows = false) {
7573
8805
  if (isDebugEnabled()) {
7574
8806
  getDebugLog().render(
7575
8807
  `doRender() START: dirtyRows=${(dirtyRows == null ? void 0 : dirtyRows.length) ?? "null"}`
@@ -7577,21 +8809,63 @@ function createStdoutRenderer(terminal, options) {
7577
8809
  }
7578
8810
  if (disposed)
7579
8811
  return;
8812
+ cliLatency == null ? void 0 : cliLatency.recordStdoutRenderStart();
7580
8813
  pendingRender = false;
7581
8814
  accumulatedAllRows = false;
7582
8815
  accumulatedDirtyBits = null;
7583
8816
  accumulatedDirtyCount = 0;
7584
8817
  accumulatedDirtyMin = Number.POSITIVE_INFINITY;
7585
8818
  accumulatedDirtyMax = -1;
8819
+ accumulatedScrollOperations = null;
8820
+ accumulatedSkipUnchangedDirtyRows = true;
7586
8821
  lastFrameTime = Date.now();
7587
8822
  const renderStart = performance.now();
7588
8823
  const size = terminal.size();
7589
8824
  ensureRowEscapes(Math.max(size.rows, lastRenderedRows));
7590
- ensureRenderedRowCache(size.rows);
8825
+ ensureFingerprints(size.cols, size.rows);
7591
8826
  const bgSeq = openBg(defaultBg);
7592
8827
  const bgOnlyStyle = { bg: defaultBg };
7593
8828
  const bgKey = styleKeyFromParts({ bg: defaultBg });
8829
+ const blankFP = bgKey << 16 | charHash16(" ");
8830
+ const cellFingerprint = (cell) => styleKey2(cell.style) << 16 | charHash16(cell.ch);
8831
+ const lastOccupiedColumnInRow = (row, cols) => {
8832
+ const limit = Math.min(row.length, cols);
8833
+ for (let x = limit - 1; x >= 0; x--) {
8834
+ if (cellFingerprint(row[x]) !== blankFP)
8835
+ return x;
8836
+ }
8837
+ return -1;
8838
+ };
8839
+ const lastOccupiedColumnInReference = (source, y, cols) => {
8840
+ if (!fpCols || y < 0 || y >= fpRows)
8841
+ return -1;
8842
+ const limit = Math.min(fpCols, cols);
8843
+ const base = y * fpCols;
8844
+ for (let x = limit - 1; x >= 0; x--) {
8845
+ if (source[base + x] !== blankFP)
8846
+ return x;
8847
+ }
8848
+ return -1;
8849
+ };
8850
+ const shouldRewriteRowTail = (row, y, cols, reference) => lastOccupiedColumnInRow(row, cols) < lastOccupiedColumnInReference(reference, y, cols);
7594
8851
  let dirtySorted = true;
8852
+ const normalizedScrollOperations = (() => {
8853
+ if (!(scrollOperations == null ? void 0 : scrollOperations.length))
8854
+ return null;
8855
+ const outOps = [];
8856
+ for (const op of scrollOperations) {
8857
+ const startY = Math.max(0, Math.min(size.rows, Math.floor(op.startY)));
8858
+ const endY = Math.max(0, Math.min(size.rows, Math.floor(op.endY)));
8859
+ const delta = Math.trunc(op.delta);
8860
+ if (endY <= startY || delta === 0 || Math.abs(delta) >= endY - startY)
8861
+ continue;
8862
+ outOps.push({ startY, endY, delta });
8863
+ }
8864
+ if (!outOps.length)
8865
+ return null;
8866
+ outOps.sort((a, b) => a.startY - b.startY);
8867
+ return outOps;
8868
+ })();
7595
8869
  let rowsToRender = (() => {
7596
8870
  if (!dirtyRows || dirtyRows.length === 0)
7597
8871
  return null;
@@ -7617,13 +8891,16 @@ function createStdoutRenderer(terminal, options) {
7617
8891
  outRows.sort((a, b) => a - b);
7618
8892
  return outRows;
7619
8893
  })();
7620
- if (rowsToRender && rowsToRender.length >= size.rows * dirtyFullThreshold) {
7621
- rowsToRender = null;
8894
+ if (fpPrevValid && rowsToRender) {
8895
+ currentFP.set(prevFP);
7622
8896
  }
7623
8897
  const frameParts = [];
7624
8898
  frameParts.push(!isGhostty && useSyncOutput ? SYNC_START : "");
7625
8899
  frameParts.push("\x1B[?7l");
7626
8900
  frameParts.push(SGR_RESET, bgSeq);
8901
+ let lastRenderedY = -1;
8902
+ let lastRenderWasFullRow = false;
8903
+ let hasFrameOutput = false;
7627
8904
  let activeStyleKey = null;
7628
8905
  let activeStyle = {
7629
8906
  fg: null,
@@ -7688,13 +8965,17 @@ function createStdoutRenderer(terminal, options) {
7688
8965
  Math.min(row.length, Math.floor(endXExclusive))
7689
8966
  );
7690
8967
  if (spanStart >= spanEnd && !clearToEol) {
7691
- renderedRowCache[y] = row.slice();
7692
8968
  return;
7693
8969
  }
8970
+ hasFrameOutput = true;
7694
8971
  if (!disableCursorPos) {
7695
- frameParts.push(
7696
- spanStart === 0 ? rowCursorToCol1[y] : `\x1B[${y + 1};${spanStart + 1}H`
7697
- );
8972
+ if (spanStart === 0 && lastRenderWasFullRow && y === lastRenderedY + 1) {
8973
+ frameParts.push("\r\n");
8974
+ } else {
8975
+ frameParts.push(
8976
+ spanStart === 0 ? rowCursorToCol1[y] : `\x1B[${y + 1};${spanStart + 1}H`
8977
+ );
8978
+ }
7698
8979
  }
7699
8980
  let currentKey = null;
7700
8981
  let currentStyle = null;
@@ -7718,7 +8999,7 @@ function createStdoutRenderer(terminal, options) {
7718
8999
  }
7719
9000
  continue;
7720
9001
  }
7721
- if (key === currentKey) {
9002
+ if (key === currentKey && !(key & 32768 && nextStyle.href !== (currentStyle == null ? void 0 : currentStyle.href))) {
7722
9003
  currentTextParts.push(ch);
7723
9004
  if (needsWideCursorFix(cell, ch)) {
7724
9005
  currentTextParts.push(
@@ -7750,39 +9031,342 @@ function createStdoutRenderer(terminal, options) {
7750
9031
  }
7751
9032
  frameParts.push("\x1B[K");
7752
9033
  }
7753
- renderedRowCache[y] = row.slice();
9034
+ lastRenderedY = y;
9035
+ lastRenderWasFullRow = spanStart === 0 && clearToEol;
7754
9036
  };
7755
- if (!rowsToRender) {
9037
+ const overlayCoverage = overlayPlaneCoverageRows(size.rows);
9038
+ const overlayRows = Array.from(
9039
+ /* @__PURE__ */ new Set([
9040
+ ...overlayCoverage.blockedRows,
9041
+ ...prevOverlayBlockedRows
9042
+ ])
9043
+ ).sort((a, b) => a - b);
9044
+ const overlayRowSet = overlayRows.length ? new Set(overlayRows) : null;
9045
+ const overlayPartialRows = Array.from(
9046
+ /* @__PURE__ */ new Set([
9047
+ ...overlayCoverage.partialRows,
9048
+ ...prevOverlayPartialRows
9049
+ ])
9050
+ ).sort((a, b) => a - b);
9051
+ const overlayPartialRowSet = overlayPartialRows.length ? new Set(overlayPartialRows) : null;
9052
+ const overlayTouchedRowSet = (() => {
9053
+ if (!overlayRowSet && !overlayPartialRowSet)
9054
+ return null;
9055
+ return /* @__PURE__ */ new Set([
9056
+ ...overlayRowSet ? Array.from(overlayRowSet) : [],
9057
+ ...overlayPartialRowSet ? Array.from(overlayPartialRowSet) : []
9058
+ ]);
9059
+ })();
9060
+ let explicitScrollOperations = normalizedScrollOperations;
9061
+ let hiddenExplicitDirtyRows = null;
9062
+ let allowInferredScrollRegions = true;
9063
+ if (explicitScrollOperations && overlayRows.length) {
9064
+ const preparedExplicit = prepareExplicitScrollOperations(
9065
+ explicitScrollOperations,
9066
+ overlayRowSet
9067
+ );
9068
+ hiddenExplicitDirtyRows = preparedExplicit.hiddenRows.size ? preparedExplicit.hiddenRows : null;
9069
+ if (preparedExplicit.operations) {
9070
+ explicitScrollOperations = preparedExplicit.operations;
9071
+ if (preparedExplicit.trimmed && isDebugEnabled()) {
9072
+ getDebugLog().render(
9073
+ ` Explicit scroll ops clipped around overlay rows: ${normalizedScrollOperations.map((op, index) => `${op.startY}-${op.endY - 1}:${op.delta}->${explicitScrollOperations[index].startY}-${explicitScrollOperations[index].endY - 1}:${explicitScrollOperations[index].delta}`).join(", ")}`
9074
+ );
9075
+ }
9076
+ } else {
9077
+ allowInferredScrollRegions = false;
9078
+ const expandedRows = new Set(rowsToRender ?? []);
9079
+ for (const op of explicitScrollOperations) {
9080
+ for (let y = op.startY; y < op.endY; y++)
9081
+ expandedRows.add(y);
9082
+ }
9083
+ rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
9084
+ dirtySorted = true;
9085
+ explicitScrollOperations = null;
9086
+ hiddenExplicitDirtyRows = null;
9087
+ if (isDebugEnabled()) {
9088
+ getDebugLog().render(
9089
+ 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"
9090
+ );
9091
+ }
9092
+ }
9093
+ }
9094
+ const scrollRowsCandidate = rowsToRender;
9095
+ if (explicitScrollOperations && rowsToRender && (!enableScrollRegions || !fpPrevValid)) {
9096
+ const expandedRows = new Set(rowsToRender);
9097
+ for (const op of explicitScrollOperations) {
9098
+ for (let y = op.startY; y < op.endY; y++) {
9099
+ if (hiddenExplicitDirtyRows == null ? void 0 : hiddenExplicitDirtyRows.has(y))
9100
+ continue;
9101
+ expandedRows.add(y);
9102
+ }
9103
+ }
9104
+ rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
9105
+ dirtySorted = true;
9106
+ explicitScrollOperations = null;
9107
+ hiddenExplicitDirtyRows = null;
9108
+ if (isDebugEnabled()) {
9109
+ getDebugLog().render(
9110
+ 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"
9111
+ );
9112
+ }
9113
+ }
9114
+ const denseDirtyRows = Boolean(
9115
+ rowsToRender && rowsToRender.length >= size.rows * dirtyFullThreshold
9116
+ );
9117
+ if (denseDirtyRows) {
9118
+ rowsToRender = null;
9119
+ }
9120
+ let profiledRowCount = rowsToRender ? rowsToRender.length : size.rows;
9121
+ const useScrollRegions = enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && allowInferredScrollRegions;
9122
+ let scrollHandled = false;
9123
+ if (enableScrollRegions && explicitScrollOperations && scrollRowsCandidate && fpPrevValid) {
9124
+ if (currentFP.length === prevFP.length)
9125
+ currentFP.set(prevFP);
9126
+ const insertedRows = /* @__PURE__ */ new Set();
9127
+ const explicitRowsToRender = /* @__PURE__ */ new Set();
9128
+ for (const y of scrollRowsCandidate) {
9129
+ if (hiddenExplicitDirtyRows == null ? void 0 : hiddenExplicitDirtyRows.has(y))
9130
+ continue;
9131
+ explicitRowsToRender.add(y);
9132
+ }
9133
+ if (isDebugEnabled()) {
9134
+ getDebugLog().render(
9135
+ ` Explicit scroll ops: ${explicitScrollOperations.map((op) => `${op.startY}-${op.endY - 1}:${op.delta}`).join(", ")}`
9136
+ );
9137
+ }
9138
+ for (const op of explicitScrollOperations) {
9139
+ hasFrameOutput = true;
9140
+ frameParts.push(`\x1B[${op.startY + 1};${op.endY}r`);
9141
+ if (op.delta > 0) {
9142
+ frameParts.push(`\x1B[${op.endY};1H`);
9143
+ frameParts.push(`\x1B[${op.delta}S`);
9144
+ for (let y = op.startY; y < op.endY - op.delta; y++) {
9145
+ const dstBase = y * fpCols;
9146
+ const srcBase = (y + op.delta) * fpCols;
9147
+ for (let x = 0; x < size.cols; x++)
9148
+ currentFP[dstBase + x] = prevFP[srcBase + x];
9149
+ }
9150
+ for (let y = op.endY - op.delta; y < op.endY; y++) {
9151
+ const base = y * fpCols;
9152
+ insertedRows.add(y);
9153
+ explicitRowsToRender.add(y);
9154
+ for (let x = 0; x < size.cols; x++)
9155
+ currentFP[base + x] = blankFP;
9156
+ }
9157
+ } else {
9158
+ const absDelta = -op.delta;
9159
+ frameParts.push(`\x1B[${op.startY + 1};1H`);
9160
+ frameParts.push(`\x1B[${absDelta}T`);
9161
+ for (let y = op.endY - 1; y >= op.startY + absDelta; y--) {
9162
+ const dstBase = y * fpCols;
9163
+ const srcBase = (y - absDelta) * fpCols;
9164
+ for (let x = 0; x < size.cols; x++)
9165
+ currentFP[dstBase + x] = prevFP[srcBase + x];
9166
+ }
9167
+ for (let y = op.startY; y < op.startY + absDelta; y++) {
9168
+ const base = y * fpCols;
9169
+ insertedRows.add(y);
9170
+ explicitRowsToRender.add(y);
9171
+ for (let x = 0; x < size.cols; x++)
9172
+ currentFP[base + x] = blankFP;
9173
+ }
9174
+ }
9175
+ frameParts.push("\x1B[r");
9176
+ }
9177
+ const explicitDirtyRows = Array.from(explicitRowsToRender).sort(
9178
+ (a, b) => a - b
9179
+ );
9180
+ const overlayDirtyRows = overlayTouchedRowSet ? explicitDirtyRows.filter((y) => overlayTouchedRowSet.has(y)) : [];
9181
+ const paintedExplicitRows = [];
9182
+ for (const y of explicitDirtyRows) {
9183
+ const row = terminal.getRow(y);
9184
+ const overlayRow = overlayTouchedRowSet == null ? void 0 : overlayTouchedRowSet.has(y);
9185
+ if (overlayRow || useConservativeDirtyRowRendering) {
9186
+ fingerprintRow(row, y, size.cols);
9187
+ renderRow2(y, row);
9188
+ paintedExplicitRows.push(y);
9189
+ continue;
9190
+ }
9191
+ const span = (() => {
9192
+ if (insertedRows.has(y)) {
9193
+ fingerprintRow(row, y, size.cols);
9194
+ return resolveChangedSpanAgainstFill(row, y, size.cols, blankFP);
9195
+ }
9196
+ return resolveChangedSpanAgainstReference(row, y, size.cols);
9197
+ })();
9198
+ if (!span) {
9199
+ if (!skipUnchangedDirtyRows)
9200
+ fingerprintRow(row, y, size.cols);
9201
+ if (!skipUnchangedDirtyRows) {
9202
+ renderRow2(y, row);
9203
+ paintedExplicitRows.push(y);
9204
+ }
9205
+ continue;
9206
+ }
9207
+ const rewriteTail = !insertedRows.has(y) && shouldRewriteRowTail(row, y, size.cols, currentFP);
9208
+ fingerprintRow(row, y, size.cols);
9209
+ renderRow2(
9210
+ y,
9211
+ row,
9212
+ span.startX,
9213
+ rewriteTail ? row.length : span.endXExclusive,
9214
+ rewriteTail || span.endXExclusive >= row.length
9215
+ );
9216
+ paintedExplicitRows.push(y);
9217
+ }
9218
+ if (isDebugEnabled() && overlayDirtyRows.length) {
9219
+ getDebugLog().render(
9220
+ ` Explicit scroll ops overlay rows: [${overlayDirtyRows.join(", ")}]`
9221
+ );
9222
+ }
9223
+ profiledRowCount = explicitDirtyRows.length;
9224
+ scrollHandled = true;
9225
+ } else if (useScrollRegions && scrollRowsCandidate) {
9226
+ for (let y = 0; y < size.rows; y++) {
9227
+ fingerprintRow(terminal.getRow(y), y, size.cols);
9228
+ }
9229
+ const shift = detectScrollShift(
9230
+ size.cols,
9231
+ scrollRowsCandidate,
9232
+ overlayRows
9233
+ );
9234
+ if (shift) {
9235
+ if (currentFP.length === prevFP.length)
9236
+ currentFP.set(prevFP);
9237
+ hasFrameOutput = true;
9238
+ if (isDebugEnabled()) {
9239
+ getDebugLog().render(
9240
+ ` Scroll region: delta=${shift.delta}, region=${shift.regionStart}-${shift.regionEnd - 1}, new rows=${shift.newRowStart}-${shift.newRowEnd - 1}`
9241
+ );
9242
+ }
9243
+ frameParts.push(`\x1B[${shift.regionStart + 1};${shift.regionEnd}r`);
9244
+ if (shift.delta > 0) {
9245
+ frameParts.push(`\x1B[${shift.regionEnd};1H`);
9246
+ frameParts.push(`\x1B[${shift.delta}S`);
9247
+ } else {
9248
+ frameParts.push(`\x1B[${shift.regionStart + 1};1H`);
9249
+ frameParts.push(`\x1B[${-shift.delta}T`);
9250
+ }
9251
+ frameParts.push("\x1B[r");
9252
+ if (shift.delta > 0) {
9253
+ for (let y = shift.regionStart; y < shift.regionEnd - shift.delta; y++) {
9254
+ const dstBase = y * fpCols;
9255
+ const srcBase = (y + shift.delta) * fpCols;
9256
+ for (let x = 0; x < size.cols; x++)
9257
+ currentFP[dstBase + x] = prevFP[srcBase + x];
9258
+ }
9259
+ for (let y = shift.regionEnd - shift.delta; y < shift.regionEnd; y++) {
9260
+ const base = y * fpCols;
9261
+ for (let x = 0; x < size.cols; x++)
9262
+ currentFP[base + x] = blankFP;
9263
+ }
9264
+ } else {
9265
+ const absDelta = -shift.delta;
9266
+ for (let y = shift.regionEnd - 1; y >= shift.regionStart + absDelta; y--) {
9267
+ const dstBase = y * fpCols;
9268
+ const srcBase = (y - absDelta) * fpCols;
9269
+ for (let x = 0; x < size.cols; x++)
9270
+ currentFP[dstBase + x] = prevFP[srcBase + x];
9271
+ }
9272
+ for (let y = shift.regionStart; y < shift.regionStart + absDelta; y++) {
9273
+ const base = y * fpCols;
9274
+ for (let x = 0; x < size.cols; x++)
9275
+ currentFP[base + x] = blankFP;
9276
+ }
9277
+ }
9278
+ for (let y = shift.newRowStart; y < shift.newRowEnd; y++) {
9279
+ const row = terminal.getRow(y);
9280
+ fingerprintRow(row, y, size.cols);
9281
+ renderRow2(y, row);
9282
+ }
9283
+ const extraDirtyRows = overlayRowSet ? shift.extraDirtyRows.filter(
9284
+ (y) => !overlayRowSet.has(y) || Boolean(overlayPartialRowSet == null ? void 0 : overlayPartialRowSet.has(y))
9285
+ ) : shift.extraDirtyRows;
9286
+ for (const y of extraDirtyRows) {
9287
+ const row = terminal.getRow(y);
9288
+ const overlayRow = overlayTouchedRowSet == null ? void 0 : overlayTouchedRowSet.has(y);
9289
+ if (overlayRow || useConservativeDirtyRowRendering) {
9290
+ fingerprintRow(row, y, size.cols);
9291
+ renderRow2(y, row);
9292
+ continue;
9293
+ }
9294
+ const span = resolveChangedSpanAgainstReference(row, y, size.cols);
9295
+ if (!span) {
9296
+ if (!skipUnchangedDirtyRows) {
9297
+ fingerprintRow(row, y, size.cols);
9298
+ renderRow2(y, row);
9299
+ }
9300
+ continue;
9301
+ }
9302
+ const rewriteTail = shouldRewriteRowTail(row, y, size.cols, currentFP);
9303
+ renderRow2(
9304
+ y,
9305
+ row,
9306
+ span.startX,
9307
+ rewriteTail ? row.length : span.endXExclusive,
9308
+ rewriteTail || span.endXExclusive >= row.length
9309
+ );
9310
+ fingerprintRow(row, y, size.cols);
9311
+ }
9312
+ for (let y = shift.newRowStart; y < shift.newRowEnd; y++) {
9313
+ fingerprintRow(terminal.getRow(y), y, size.cols);
9314
+ }
9315
+ const renderedRows = /* @__PURE__ */ new Set();
9316
+ for (let y = shift.newRowStart; y < shift.newRowEnd; y++)
9317
+ renderedRows.add(y);
9318
+ for (const y of extraDirtyRows)
9319
+ renderedRows.add(y);
9320
+ profiledRowCount = renderedRows.size;
9321
+ scrollHandled = true;
9322
+ }
9323
+ }
9324
+ if (!scrollHandled && denseDirtyRows && scrollRowsCandidate && useScrollRegions) {
9325
+ rowsToRender = scrollRowsCandidate;
9326
+ profiledRowCount = rowsToRender.length;
9327
+ if (isDebugEnabled()) {
9328
+ getDebugLog().render(
9329
+ ` Dense dirty rows fell back to partial render: ${rowsToRender.length} rows`
9330
+ );
9331
+ }
9332
+ }
9333
+ if (scrollHandled) {
9334
+ } else if (!rowsToRender) {
7756
9335
  if (isDebugEnabled())
7757
9336
  getDebugLog().render(` Full render: ${size.rows} rows`);
7758
9337
  for (let y = 0; y < size.rows; y++) {
7759
9338
  const row = terminal.getRow(y);
9339
+ fingerprintRow(row, y, size.cols);
7760
9340
  renderRow2(y, row);
7761
9341
  }
7762
9342
  } else {
7763
- const repaintFullDirtyRows = overlayPlaneHasVisibleContent();
9343
+ const overlayDirtyRows = overlayTouchedRowSet ? rowsToRender.filter((y) => overlayTouchedRowSet.has(y)) : [];
7764
9344
  if (isDebugEnabled()) {
7765
9345
  getDebugLog().render(
7766
- ` Partial render: ${rowsToRender.length} dirty rows: [${rowsToRender.join(", ")}]${repaintFullDirtyRows ? " (overlay-full-row)" : ""}`
9346
+ ` Partial render: ${rowsToRender.length} dirty rows: [${rowsToRender.join(", ")}]${overlayDirtyRows.length ? ` (overlay-full-row: [${overlayDirtyRows.join(", ")}])` : ""}`
7767
9347
  );
7768
9348
  }
7769
9349
  for (const y of rowsToRender) {
7770
9350
  const row = terminal.getRow(y);
7771
- if (repaintFullDirtyRows) {
9351
+ fingerprintRow(row, y, size.cols);
9352
+ const overlayRow = overlayTouchedRowSet == null ? void 0 : overlayTouchedRowSet.has(y);
9353
+ if (overlayRow || useConservativeDirtyRowRendering) {
7772
9354
  renderRow2(y, row);
7773
9355
  continue;
7774
9356
  }
7775
- const span = resolveChangedSpan(row, renderedRowCache[y]);
9357
+ const span = resolveChangedSpan(row, y, size.cols);
7776
9358
  if (!span) {
7777
- renderRow2(y, row);
9359
+ if (!skipUnchangedDirtyRows)
9360
+ renderRow2(y, row);
7778
9361
  continue;
7779
9362
  }
9363
+ const rewriteTail = shouldRewriteRowTail(row, y, size.cols, prevFP);
7780
9364
  renderRow2(
7781
9365
  y,
7782
9366
  row,
7783
9367
  span.startX,
7784
- span.endXExclusive,
7785
- span.endXExclusive >= row.length
9368
+ rewriteTail ? row.length : span.endXExclusive,
9369
+ rewriteTail || span.endXExclusive >= row.length
7786
9370
  );
7787
9371
  }
7788
9372
  }
@@ -7793,20 +9377,36 @@ function createStdoutRenderer(terminal, options) {
7793
9377
  activeStyleKey = bgKey;
7794
9378
  }
7795
9379
  for (let i = 0; i < extraRows; i++) {
9380
+ hasFrameOutput = true;
7796
9381
  frameParts.push(rowClearToEol[size.rows + i]);
7797
9382
  }
7798
9383
  }
7799
9384
  if (!rowsToRender)
7800
9385
  lastRenderedRows = size.rows;
7801
- if (!rowsToRender || renderedRowCache.length !== size.rows)
7802
- renderedRowCache.length = size.rows;
9386
+ const tmpFP = prevFP;
9387
+ prevFP = currentFP;
9388
+ currentFP = tmpFP;
9389
+ fpPrevValid = true;
9390
+ prevOverlayBlockedRows = overlayCoverage.blockedRows;
9391
+ prevOverlayPartialRows = overlayCoverage.partialRows;
9392
+ let emittedCursorPos = null;
7803
9393
  if (getImeAnchor) {
7804
9394
  const anchor = getImeAnchor();
7805
9395
  if (anchor) {
7806
9396
  const { x, y } = clampCellToViewport(anchor, size);
7807
- frameParts.push(`\x1B[${y + 1};${x + 1}H`);
9397
+ if (hasFrameOutput || x !== lastCursorX || y !== lastCursorY) {
9398
+ hasFrameOutput = true;
9399
+ emittedCursorPos = { x, y };
9400
+ frameParts.push(`\x1B[${y + 1};${x + 1}H`);
9401
+ }
7808
9402
  }
7809
9403
  }
9404
+ if (!hasFrameOutput) {
9405
+ if (isDebugEnabled())
9406
+ getDebugLog().render(" No-op frame skipped");
9407
+ cliLatency == null ? void 0 : cliLatency.recordStdoutNoOutput();
9408
+ return;
9409
+ }
7810
9410
  if (enableOsc8Links && activeStyle.href)
7811
9411
  frameParts.push(OSC8_CLOSE);
7812
9412
  frameParts.push(SGR_RESET);
@@ -7816,9 +9416,9 @@ function createStdoutRenderer(terminal, options) {
7816
9416
  if (profiler) {
7817
9417
  profiler.recordRender({
7818
9418
  durationMs: profiler.now() - renderStart,
7819
- rows: rowsToRender ? rowsToRender.length : size.rows,
9419
+ rows: profiledRowCount,
7820
9420
  nodes: 0,
7821
- fullRepaint: !rowsToRender,
9421
+ fullRepaint: !rowsToRender && !scrollHandled,
7822
9422
  sorted: dirtySorted
7823
9423
  });
7824
9424
  }
@@ -7944,6 +9544,13 @@ function createStdoutRenderer(terminal, options) {
7944
9544
  {
7945
9545
  const writeMs = performance.now() - writeStart;
7946
9546
  writeEmaMs = writeEmaMs === 0 ? writeMs : writeEmaMs * 0.85 + writeMs * 0.15;
9547
+ if (emittedCursorPos) {
9548
+ lastCursorX = emittedCursorPos.x;
9549
+ lastCursorY = emittedCursorPos.y;
9550
+ } else {
9551
+ lastCursorX = null;
9552
+ lastCursorY = null;
9553
+ }
7947
9554
  recordStdoutFrame({
7948
9555
  at: Date.now(),
7949
9556
  bytes: frame.length,
@@ -7958,6 +9565,11 @@ function createStdoutRenderer(terminal, options) {
7958
9565
  mode: writeMode === "sync" ? "sync" : writeMode === "chunked" ? "chunked" : "stream"
7959
9566
  });
7960
9567
  }
9568
+ cliLatency == null ? void 0 : cliLatency.recordStdoutWrite({
9569
+ durationMs: writeMs,
9570
+ bytes: frame.length,
9571
+ mode: writeMode === "sync" ? "sync" : writeMode === "chunked" ? "chunked" : "stream"
9572
+ });
7961
9573
  }
7962
9574
  if (isDebugEnabled()) {
7963
9575
  const totalTime = (performance.now() - renderStart).toFixed(2);
@@ -7965,7 +9577,7 @@ function createStdoutRenderer(terminal, options) {
7965
9577
  getDebugLog().render(`doRender() END`);
7966
9578
  }
7967
9579
  }
7968
- function render(dirtyRows) {
9580
+ function render(dirtyRows, sync, scrollOperations, skipUnchangedDirtyRows = false) {
7969
9581
  if (disposed)
7970
9582
  return;
7971
9583
  if (isDebugEnabled()) {
@@ -7973,6 +9585,24 @@ function createStdoutRenderer(terminal, options) {
7973
9585
  `render() called: dirtyRows=${(dirtyRows == null ? void 0 : dirtyRows.length) ?? "null"}, pending=${pendingRender}, elapsed=${Date.now() - lastFrameTime}ms`
7974
9586
  );
7975
9587
  }
9588
+ const isSmallSyncPatch = Boolean(
9589
+ sync && (dirtyRows == null ? void 0 : dirtyRows.length) && dirtyRows.length <= 12 && !(scrollOperations == null ? void 0 : scrollOperations.length)
9590
+ );
9591
+ if (isSmallSyncPatch)
9592
+ skipUnchangedDirtyRows = false;
9593
+ if ((dirtyRows == null ? void 0 : dirtyRows.length) && skipUnchangedDirtyRows && fpPrevValid && !(scrollOperations == null ? void 0 : scrollOperations.length) && dirtyRows.length < terminal.size().rows) {
9594
+ const cols = terminal.size().cols;
9595
+ const nextDirtyRows = dirtyRows.filter((y) => !rowMatchesPreviousFrame(y, cols));
9596
+ if (nextDirtyRows.length === 0) {
9597
+ if (isDebugEnabled()) {
9598
+ getDebugLog().render(
9599
+ `Skipped unchanged dirty commit: ${dirtyRows.length} rows`
9600
+ );
9601
+ }
9602
+ return;
9603
+ }
9604
+ dirtyRows = nextDirtyRows;
9605
+ }
7976
9606
  const ensureDirtyBits = (rowCount) => {
7977
9607
  if (!accumulatedDirtyBits || accumulatedDirtyBits.length !== rowCount) {
7978
9608
  accumulatedDirtyBits = new Uint8Array(rowCount);
@@ -8003,12 +9633,43 @@ function createStdoutRenderer(terminal, options) {
8003
9633
  out2.length = outLen;
8004
9634
  return outLen ? out2 : null;
8005
9635
  };
9636
+ const buildAccumulatedScrollOperations = () => {
9637
+ return (accumulatedScrollOperations == null ? void 0 : accumulatedScrollOperations.length) ? accumulatedScrollOperations : null;
9638
+ };
9639
+ const mergeScrollOperations = (next) => {
9640
+ if (!(next == null ? void 0 : next.length) || accumulatedAllRows)
9641
+ return;
9642
+ const merged = accumulatedScrollOperations ? accumulatedScrollOperations.slice() : [];
9643
+ for (const op of next) {
9644
+ const startY = Math.floor(op.startY);
9645
+ const endY = Math.floor(op.endY);
9646
+ const delta = Math.trunc(op.delta);
9647
+ if (endY <= startY || delta === 0)
9648
+ continue;
9649
+ const existingIndex = merged.findIndex(
9650
+ (existing) => existing.startY === startY && existing.endY === endY
9651
+ );
9652
+ if (existingIndex >= 0) {
9653
+ const existing = merged[existingIndex];
9654
+ const nextDelta = existing.delta + delta;
9655
+ if (nextDelta === 0)
9656
+ merged.splice(existingIndex, 1);
9657
+ else
9658
+ merged[existingIndex] = { startY, endY, delta: nextDelta };
9659
+ } else {
9660
+ merged.push({ startY, endY, delta });
9661
+ }
9662
+ }
9663
+ accumulatedScrollOperations = merged.length ? merged : null;
9664
+ };
8006
9665
  if (!dirtyRows || dirtyRows.length === 0) {
8007
9666
  accumulatedAllRows = true;
8008
9667
  accumulatedDirtyBits = null;
8009
9668
  accumulatedDirtyCount = 0;
8010
9669
  accumulatedDirtyMin = Number.POSITIVE_INFINITY;
8011
9670
  accumulatedDirtyMax = -1;
9671
+ accumulatedScrollOperations = null;
9672
+ accumulatedSkipUnchangedDirtyRows = false;
8012
9673
  } else if (!accumulatedAllRows) {
8013
9674
  const rowCount = terminal.size().rows;
8014
9675
  const bits = ensureDirtyBits(rowCount);
@@ -8025,14 +9686,35 @@ function createStdoutRenderer(terminal, options) {
8025
9686
  accumulatedDirtyMax = y;
8026
9687
  }
8027
9688
  }
9689
+ mergeScrollOperations(scrollOperations);
9690
+ accumulatedSkipUnchangedDirtyRows && (accumulatedSkipUnchangedDirtyRows = skipUnchangedDirtyRows);
9691
+ }
9692
+ const now4 = Date.now();
9693
+ const elapsed = now4 - lastFrameTime;
9694
+ const queuedDelayMs = Math.max(0, MIN_FRAME_MS - elapsed);
9695
+ if (pendingRender && !sync) {
9696
+ cliLatency == null ? void 0 : cliLatency.recordStdoutQueued(queuedDelayMs);
9697
+ return;
8028
9698
  }
8029
- if (pendingRender)
9699
+ if (sync) {
9700
+ if (pendingRender) {
9701
+ pendingRender = false;
9702
+ if (renderTimer) {
9703
+ clearTimeout(renderTimer);
9704
+ renderTimer = null;
9705
+ }
9706
+ }
9707
+ const rows = buildAccumulatedRows();
9708
+ const pendingScrolls = buildAccumulatedScrollOperations();
9709
+ cliLatency == null ? void 0 : cliLatency.recordStdoutQueued(0);
9710
+ doRender(rows, pendingScrolls, accumulatedSkipUnchangedDirtyRows);
8030
9711
  return;
8031
- const now3 = Date.now();
8032
- const elapsed = now3 - lastFrameTime;
9712
+ }
8033
9713
  if (elapsed >= MIN_FRAME_MS) {
8034
9714
  const rows = buildAccumulatedRows();
8035
- doRender(rows);
9715
+ const pendingScrolls = buildAccumulatedScrollOperations();
9716
+ cliLatency == null ? void 0 : cliLatency.recordStdoutQueued(0);
9717
+ doRender(rows, pendingScrolls, accumulatedSkipUnchangedDirtyRows);
8036
9718
  } else {
8037
9719
  pendingRender = true;
8038
9720
  if (renderTimer)
@@ -8041,11 +9723,16 @@ function createStdoutRenderer(terminal, options) {
8041
9723
  renderTimer = null;
8042
9724
  if (!disposed) {
8043
9725
  const rows = buildAccumulatedRows();
8044
- doRender(rows);
9726
+ const pendingScrolls = buildAccumulatedScrollOperations();
9727
+ doRender(rows, pendingScrolls, accumulatedSkipUnchangedDirtyRows);
8045
9728
  }
8046
- }, MIN_FRAME_MS - elapsed);
9729
+ }, queuedDelayMs);
9730
+ cliLatency == null ? void 0 : cliLatency.recordStdoutQueued(queuedDelayMs);
8047
9731
  }
8048
9732
  }
9733
+ terminal.setFingerprintFn((ch, style) => {
9734
+ return styleKey2(style) << 16 | charHash16(ch);
9735
+ });
8049
9736
  if (altScreen && out.isTTY)
8050
9737
  out.write("\x1B[?1049h");
8051
9738
  if (hideCursor)
@@ -8053,13 +9740,13 @@ function createStdoutRenderer(terminal, options) {
8053
9740
  if (clear) {
8054
9741
  out.write(`${SGR_RESET}${openBg(defaultBg)}\x1B[2J\x1B[H${SGR_RESET}`);
8055
9742
  }
8056
- const off = terminal.on("commit", ({ dirtyRows }) => {
9743
+ const off = terminal.on("commit", ({ dirtyRows, scrollOperations, sync }) => {
8057
9744
  if (isDebugEnabled()) {
8058
9745
  getDebugLog().render(
8059
- `Commit event: dirtyRows=${(dirtyRows == null ? void 0 : dirtyRows.length) ?? "null"}, rows=${(dirtyRows == null ? void 0 : dirtyRows.join(",")) ?? "all"}`
9746
+ `Commit event: dirtyRows=${(dirtyRows == null ? void 0 : dirtyRows.length) ?? "null"}, rows=${(dirtyRows == null ? void 0 : dirtyRows.join(",")) ?? "all"}${(scrollOperations == null ? void 0 : scrollOperations.length) ? `, scrollOps=${scrollOperations.map((op) => `${op.startY}-${op.endY - 1}:${op.delta}`).join("|")}` : ""}${sync ? " (sync)" : ""}`
8060
9747
  );
8061
9748
  }
8062
- render(dirtyRows);
9749
+ render(dirtyRows, sync, scrollOperations, true);
8063
9750
  });
8064
9751
  const resizeSource = (options == null ? void 0 : options.output) ?? import_node_process9.default.stdout;
8065
9752
  const canTrackResize = Boolean(
@@ -8093,6 +9780,8 @@ function createStdoutRenderer(terminal, options) {
8093
9780
  { cols: size.cols, rows: size.rows }
8094
9781
  );
8095
9782
  out.write(`\x1B[${cy + 1};${cx + 1}H`);
9783
+ lastCursorX = cx;
9784
+ lastCursorY = cy;
8096
9785
  }
8097
9786
  function showCursor(visible) {
8098
9787
  if (disposed)
@@ -8137,7 +9826,10 @@ function createStdoutRenderer(terminal, options) {
8137
9826
  if ("palette" in next)
8138
9827
  palette = next.palette;
8139
9828
  styleKeyCache2 = /* @__PURE__ */ new WeakMap();
8140
- renderedRowCache = [];
9829
+ fpRows = 0;
9830
+ fpPrevValid = false;
9831
+ prevOverlayBlockedRows = [];
9832
+ prevOverlayPartialRows = [];
8141
9833
  render();
8142
9834
  };
8143
9835
  return { render, dispose, setCursor, showCursor, updateTheme };
@@ -8324,6 +10016,12 @@ var rowCache = /* @__PURE__ */ new WeakMap();
8324
10016
  function segmentsToKey(segments) {
8325
10017
  return segments.map((s) => `${s.key}:${s.text}:${s.cols}:${s.wide}`).join("|");
8326
10018
  }
10019
+ function isPlainStyle(style) {
10020
+ return !style.fg && !style.bg && !style.bold && !style.dim && !style.italic && !style.underline && !style.inverse && !style.href;
10021
+ }
10022
+ function isTransparentBlankRow(segments) {
10023
+ return segments.length === 1 && isPlainStyle(segments[0].style) && segments[0].text.trim().length === 0;
10024
+ }
8327
10025
  function renderRow(terminal, metrics2, wideScaleX, y, lineEl) {
8328
10026
  const segments = computeRowSegments(terminal, y);
8329
10027
  const newKey = segmentsToKey(segments);
@@ -8331,6 +10029,10 @@ function renderRow(terminal, metrics2, wideScaleX, y, lineEl) {
8331
10029
  if (cachedKey === newKey)
8332
10030
  return;
8333
10031
  rowCache.set(lineEl, newKey);
10032
+ if (isTransparentBlankRow(segments)) {
10033
+ lineEl.replaceChildren();
10034
+ return;
10035
+ }
8334
10036
  const fragment = document.createDocumentFragment();
8335
10037
  for (const seg of segments) {
8336
10038
  const span = document.createElement("span");
@@ -8353,6 +10055,7 @@ function createDomRenderer(terminal, container) {
8353
10055
  container.style.whiteSpace = "pre";
8354
10056
  container.style.display = "inline-block";
8355
10057
  container.style.overflow = "hidden";
10058
+ container.style.position = "relative";
8356
10059
  container.style.userSelect = "text";
8357
10060
  container.style.outline = "none";
8358
10061
  container.style.contain = "layout style";
@@ -8360,21 +10063,105 @@ function createDomRenderer(terminal, container) {
8360
10063
  installAnsiPaletteCssVars(container);
8361
10064
  let metrics2 = measureCell(container);
8362
10065
  let wideScaleX = 1;
8363
- let lineEls = [];
10066
+ const planeLayers = /* @__PURE__ */ new Map();
8364
10067
  let raf = 0;
8365
- const pending = /* @__PURE__ */ new Set();
10068
+ const pending = /* @__PURE__ */ new Map();
8366
10069
  let disposed = false;
10070
+ function applyPlaneOffset(plane, offsetPx) {
10071
+ const layer = planeLayers.get(plane);
10072
+ if (!layer)
10073
+ return;
10074
+ const next = Number.isFinite(offsetPx) ? offsetPx : 0;
10075
+ if (layer.offsetPx === next)
10076
+ return;
10077
+ layer.offsetPx = next;
10078
+ layer.contentEl.style.transform = next ? `translateY(${next}px)` : "translateY(0px)";
10079
+ }
10080
+ function applyPlaneViewport(plane, viewport) {
10081
+ const layer = planeLayers.get(plane);
10082
+ if (!layer)
10083
+ return;
10084
+ const totalHeight = terminal.size().rows * metrics2.cellHeight;
10085
+ if (!viewport) {
10086
+ layer.viewportTopPx = 0;
10087
+ layer.viewportHeightPx = null;
10088
+ layer.el.style.clipPath = "none";
10089
+ return;
10090
+ }
10091
+ const topPx = Math.max(
10092
+ 0,
10093
+ Math.min(totalHeight, Math.floor(viewport.topPx))
10094
+ );
10095
+ const heightPx = Math.max(
10096
+ 0,
10097
+ Math.min(totalHeight - topPx, Math.floor(viewport.heightPx))
10098
+ );
10099
+ layer.viewportTopPx = topPx;
10100
+ layer.viewportHeightPx = heightPx;
10101
+ if (topPx === 0 && heightPx >= totalHeight) {
10102
+ layer.el.style.clipPath = "none";
10103
+ return;
10104
+ }
10105
+ const bottomPx = Math.max(0, totalHeight - topPx - heightPx);
10106
+ layer.el.style.clipPath = `inset(${topPx}px 0px ${bottomPx}px 0px)`;
10107
+ }
8367
10108
  function rebuildLines() {
8368
10109
  const { rows, cols } = terminal.size();
8369
10110
  container.style.width = `${cols * metrics2.cellWidth}px`;
8370
10111
  container.style.height = `${rows * metrics2.cellHeight}px`;
8371
10112
  const fragment = document.createDocumentFragment();
8372
- lineEls = Array.from({ length: rows }, () => {
8373
- const line = document.createElement("div");
8374
- line.style.cssText = `height:${metrics2.cellHeight}px;width:${cols * metrics2.cellWidth}px;overflow:hidden;contain:layout style`;
8375
- fragment.appendChild(line);
8376
- return line;
8377
- });
10113
+ for (let i = 0; i < TERMINAL_RENDER_PLANES.length; i++) {
10114
+ const plane = TERMINAL_RENDER_PLANES[i];
10115
+ const planeTerminal = getPlaneTerminal(terminal, plane);
10116
+ let layer = planeLayers.get(plane);
10117
+ if (!layer) {
10118
+ const el = document.createElement("div");
10119
+ const contentEl = document.createElement("div");
10120
+ el.dataset.vtPlane = plane;
10121
+ el.style.pointerEvents = "none";
10122
+ contentEl.style.position = "absolute";
10123
+ contentEl.style.left = "0";
10124
+ contentEl.style.top = "0";
10125
+ layer = {
10126
+ el,
10127
+ contentEl,
10128
+ lines: [],
10129
+ offsetPx: 0,
10130
+ terminal: planeTerminal,
10131
+ viewportTopPx: 0,
10132
+ viewportHeightPx: null
10133
+ };
10134
+ planeLayers.set(plane, layer);
10135
+ }
10136
+ layer.terminal = planeTerminal;
10137
+ layer.el.style.position = "absolute";
10138
+ layer.el.style.left = "0";
10139
+ layer.el.style.top = "0";
10140
+ layer.el.style.width = `${cols * metrics2.cellWidth}px`;
10141
+ layer.el.style.height = `${rows * metrics2.cellHeight}px`;
10142
+ layer.el.style.overflow = "hidden";
10143
+ layer.el.style.zIndex = String(i);
10144
+ layer.contentEl.style.width = `${cols * metrics2.cellWidth}px`;
10145
+ layer.contentEl.style.height = `${rows * metrics2.cellHeight}px`;
10146
+ applyPlaneOffset(plane, layer.offsetPx);
10147
+ applyPlaneViewport(
10148
+ plane,
10149
+ layer.viewportHeightPx == null ? null : {
10150
+ topPx: layer.viewportTopPx,
10151
+ heightPx: layer.viewportHeightPx
10152
+ }
10153
+ );
10154
+ const lineFragment = document.createDocumentFragment();
10155
+ layer.lines = Array.from({ length: rows }, () => {
10156
+ const line = document.createElement("div");
10157
+ line.style.cssText = `height:${metrics2.cellHeight}px;width:${cols * metrics2.cellWidth}px;overflow:hidden;contain:layout style`;
10158
+ lineFragment.appendChild(line);
10159
+ return line;
10160
+ });
10161
+ layer.contentEl.replaceChildren(lineFragment);
10162
+ layer.el.replaceChildren(layer.contentEl);
10163
+ fragment.appendChild(layer.el);
10164
+ }
8378
10165
  container.replaceChildren(fragment);
8379
10166
  }
8380
10167
  function refresh() {
@@ -8383,28 +10170,54 @@ function createDomRenderer(terminal, container) {
8383
10170
  wideScaleX = wideW > 0 ? Math.max(1, Math.min(2, metrics2.cellWidth * 2 / wideW)) : 1;
8384
10171
  rebuildLines();
8385
10172
  const size = terminal.size();
8386
- for (let y = 0; y < size.rows; y++)
8387
- renderRow(terminal, metrics2, wideScaleX, y, lineEls[y]);
10173
+ for (const plane of TERMINAL_RENDER_PLANES) {
10174
+ const layer = planeLayers.get(plane);
10175
+ if (!layer)
10176
+ continue;
10177
+ for (let y = 0; y < size.rows; y++)
10178
+ renderRow(layer.terminal, metrics2, wideScaleX, y, layer.lines[y]);
10179
+ }
8388
10180
  }
8389
10181
  function flushPending() {
8390
10182
  raf = 0;
8391
10183
  if (disposed)
8392
10184
  return;
8393
- for (const y of pending) {
8394
- const line = lineEls[y];
8395
- if (line)
8396
- renderRow(terminal, metrics2, wideScaleX, y, line);
10185
+ for (const plane of TERMINAL_RENDER_PLANES) {
10186
+ const layer = planeLayers.get(plane);
10187
+ const rows = pending.get(plane);
10188
+ if (!layer || !(rows == null ? void 0 : rows.size))
10189
+ continue;
10190
+ for (const y of rows) {
10191
+ const line = layer.lines[y];
10192
+ if (line)
10193
+ renderRow(layer.terminal, metrics2, wideScaleX, y, line);
10194
+ }
10195
+ rows.clear();
8397
10196
  }
8398
- pending.clear();
8399
10197
  }
8400
- const offCommit = terminal.on("commit", ({ dirtyRows }) => {
10198
+ const offCommit = terminal.on("commit", ({ dirtyRows, planes }) => {
10199
+ const activePlanes = (planes == null ? void 0 : planes.length) ? planes : TERMINAL_RENDER_PLANES;
8401
10200
  if (dirtyRows === null) {
8402
10201
  const size = terminal.size();
8403
- for (let y = 0; y < size.rows; y++)
8404
- pending.add(y);
10202
+ for (const plane of activePlanes) {
10203
+ let rows = pending.get(plane);
10204
+ if (!rows) {
10205
+ rows = /* @__PURE__ */ new Set();
10206
+ pending.set(plane, rows);
10207
+ }
10208
+ for (let y = 0; y < size.rows; y++)
10209
+ rows.add(y);
10210
+ }
8405
10211
  } else {
8406
- for (const y of dirtyRows)
8407
- pending.add(y);
10212
+ for (const plane of activePlanes) {
10213
+ let rows = pending.get(plane);
10214
+ if (!rows) {
10215
+ rows = /* @__PURE__ */ new Set();
10216
+ pending.set(plane, rows);
10217
+ }
10218
+ for (const y of dirtyRows)
10219
+ rows.add(y);
10220
+ }
8408
10221
  }
8409
10222
  if (!raf) {
8410
10223
  raf = -1;
@@ -8433,7 +10246,14 @@ function createDomRenderer(terminal, container) {
8433
10246
  if (raf > 0)
8434
10247
  cancelAnimationFrame(raf);
8435
10248
  pending.clear();
10249
+ planeLayers.clear();
8436
10250
  container.replaceChildren();
10251
+ },
10252
+ setPlaneOffset(plane, offsetPx) {
10253
+ applyPlaneOffset(plane, offsetPx);
10254
+ },
10255
+ setPlaneViewport(plane, viewport) {
10256
+ applyPlaneViewport(plane, viewport);
8437
10257
  }
8438
10258
  };
8439
10259
  }
@@ -8602,23 +10422,39 @@ function useTerminal() {
8602
10422
 
8603
10423
  // src/vue/composables/use-render-node.ts
8604
10424
  var pendingInvalidateByScheduler = /* @__PURE__ */ new WeakMap();
8605
- function requestBatchedInvalidate(scheduler, plane) {
10425
+ function mergePriority(prev, next) {
10426
+ if (prev === "high" || next === "high")
10427
+ return "high";
10428
+ if (prev === "normal" || next === "normal")
10429
+ return "normal";
10430
+ return "low";
10431
+ }
10432
+ function requestBatchedInvalidate(scheduler, plane, priority) {
8606
10433
  let state = pendingInvalidateByScheduler.get(scheduler);
8607
10434
  if (!state) {
8608
- state = { queued: false, plane: null };
10435
+ state = { queued: false, plane: null, priority: "low" };
8609
10436
  pendingInvalidateByScheduler.set(scheduler, state);
8610
10437
  }
8611
10438
  if (state.queued) {
8612
10439
  state.plane = state.plane == null || state.plane === plane ? plane : null;
10440
+ state.priority = mergePriority(state.priority, priority);
8613
10441
  return;
8614
10442
  }
8615
10443
  state.plane = plane;
10444
+ state.priority = priority;
8616
10445
  state.queued = true;
8617
10446
  queueMicrotask(() => {
8618
10447
  state.queued = false;
8619
10448
  const queuedPlane = state.plane;
10449
+ const queuedPriority = state.priority;
8620
10450
  state.plane = null;
8621
- scheduler.invalidate(queuedPlane ? { plane: queuedPlane } : void 0);
10451
+ state.priority = "low";
10452
+ scheduler.invalidate(
10453
+ queuedPlane || queuedPriority !== "normal" ? {
10454
+ plane: queuedPlane ?? void 0,
10455
+ priority: queuedPriority
10456
+ } : void 0
10457
+ );
8622
10458
  });
8623
10459
  }
8624
10460
  function useRenderNode(getOptions) {
@@ -8626,11 +10462,13 @@ function useRenderNode(getOptions) {
8626
10462
  const parentStack = useRenderStack();
8627
10463
  const plane = (0, import_vue7.inject)(RenderPlaneContextKey, (0, import_vue7.ref)("default"));
8628
10464
  const id = (0, import_vue7.ref)(null);
10465
+ const lastPlane = (0, import_vue7.ref)(plane.value);
8629
10466
  const options = (0, import_vue7.computed)(() => getOptions());
8630
10467
  const stop = (0, import_vue7.watchEffect)(() => {
8631
10468
  const opt = options.value;
8632
10469
  void opt.deps;
8633
10470
  const stack = opt.stack ?? parentStack.value;
10471
+ lastPlane.value = plane.value;
8634
10472
  if (!stack)
8635
10473
  return;
8636
10474
  if (!id.value) {
@@ -8642,23 +10480,24 @@ function useRenderNode(getOptions) {
8642
10480
  paint: opt.paint
8643
10481
  });
8644
10482
  id.value = node.id;
8645
- requestBatchedInvalidate(scheduler, plane.value);
10483
+ requestBatchedInvalidate(scheduler, plane.value, opt.priority ?? "normal");
8646
10484
  return;
8647
10485
  }
8648
10486
  render.update(id.value, {
8649
10487
  stack,
8650
10488
  zIndex: opt.zIndex ?? 0,
8651
10489
  rect: opt.rect ?? null,
10490
+ dirtyRowsHint: opt.dirtyRowsHint,
8652
10491
  plane: plane.value,
8653
10492
  paint: opt.paint
8654
10493
  });
8655
- requestBatchedInvalidate(scheduler, plane.value);
10494
+ requestBatchedInvalidate(scheduler, plane.value, opt.priority ?? "normal");
8656
10495
  });
8657
10496
  (0, import_vue7.onBeforeUnmount)(() => {
8658
10497
  stop();
8659
10498
  if (id.value) {
8660
10499
  render.unregister(id.value);
8661
- requestBatchedInvalidate(scheduler, plane.value);
10500
+ requestBatchedInvalidate(scheduler, lastPlane.value, "normal");
8662
10501
  }
8663
10502
  });
8664
10503
  return { id };
@@ -9902,6 +11741,7 @@ function mentionIndexAt2(value, mentionToken, index) {
9902
11741
  return countMentionTokens2(value, mentionToken, index);
9903
11742
  }
9904
11743
  function createPromptMentionPlugin(options = {}) {
11744
+ const latency = getCliLatencyProfiler();
9905
11745
  return {
9906
11746
  name: "promptMention",
9907
11747
  install: (ctx) => {
@@ -9910,6 +11750,8 @@ function createPromptMentionPlugin(options = {}) {
9910
11750
  ctx.render.rootStack,
9911
11751
  1e4
9912
11752
  );
11753
+ let lastTopClampedRect = null;
11754
+ let lastPromptRect = null;
9913
11755
  const derivedStyleCache = /* @__PURE__ */ new WeakMap();
9914
11756
  const reactiveProps = (0, import_vue10.reactive)({
9915
11757
  promptSuggestions: getProps().promptSuggestions,
@@ -9957,6 +11799,25 @@ function createPromptMentionPlugin(options = {}) {
9957
11799
  multilineToken: "\uFFFC",
9958
11800
  mentionToken: ctx.mentionToken
9959
11801
  });
11802
+ (0, import_vue10.watch)(
11803
+ () => {
11804
+ if (!ctx.visible.value || !promptMention.promptVisible.value)
11805
+ return null;
11806
+ const rect = promptMention.promptRect.value;
11807
+ return [
11808
+ Math.floor(rect.x),
11809
+ Math.floor(rect.y),
11810
+ Math.floor(rect.w),
11811
+ Math.floor(rect.h)
11812
+ ].join(":");
11813
+ },
11814
+ (nextRectKey, prevRectKey) => {
11815
+ if (!prevRectKey || nextRectKey === prevRectKey)
11816
+ return;
11817
+ ctx.render.invalidatePlane("default");
11818
+ ctx.scheduler.invalidate({ priority: "high" });
11819
+ }
11820
+ );
9960
11821
  ctx.registerChipStyleProvider({
9961
11822
  getStyle: (baseStyle, chip) => {
9962
11823
  if (chip.kind !== "mention")
@@ -10043,6 +11904,12 @@ function createPromptMentionPlugin(options = {}) {
10043
11904
  query: ctx0.query
10044
11905
  });
10045
11906
  if (handled !== false) {
11907
+ const value = ctx.getValue();
11908
+ const before2 = value.slice(0, ctx0.tokenStart);
11909
+ const after2 = value.slice(ctx0.tokenEnd);
11910
+ const nextValue2 = `${before2}${after2}`;
11911
+ ctx.pushUndoSnapshot(nextValue2);
11912
+ ctx.applyEdit(nextValue2, ctx0.tokenStart);
10046
11913
  promptMention.promptSuppressedKey.value = ctx0.key;
10047
11914
  ctx.scheduler.invalidate();
10048
11915
  return;
@@ -10086,8 +11953,17 @@ function createPromptMentionPlugin(options = {}) {
10086
11953
  var _a2, _b2, _c, _d;
10087
11954
  if (!promptMention.promptVisible.value)
10088
11955
  return false;
11956
+ const markPromptOperation = (suffix) => {
11957
+ const ctx0 = promptMention.activeContext.value;
11958
+ const mentionTrigger = getProps().mentionTrigger || "@";
11959
+ const skillTrigger = getProps().skillTrigger || "";
11960
+ const tokenText = String((ctx0 == null ? void 0 : ctx0.tokenText) ?? "");
11961
+ const scope = tokenText.startsWith(mentionTrigger) ? "prompt.mention" : skillTrigger && tokenText.startsWith(skillTrigger) ? "prompt.skill" : "prompt.command";
11962
+ latency == null ? void 0 : latency.markOperation(`${scope}.${suffix}`);
11963
+ };
10089
11964
  const len = promptMention.promptMatches.value.length;
10090
11965
  if (e.key === "ArrowDown") {
11966
+ markPromptOperation("navigate.next");
10091
11967
  e.preventDefault();
10092
11968
  if (len <= 0) {
10093
11969
  promptMention.promptActive.value = 0;
@@ -10104,6 +11980,7 @@ function createPromptMentionPlugin(options = {}) {
10104
11980
  return true;
10105
11981
  }
10106
11982
  if (e.key === "ArrowUp") {
11983
+ markPromptOperation("navigate.prev");
10107
11984
  e.preventDefault();
10108
11985
  if (len <= 0) {
10109
11986
  promptMention.promptActive.value = 0;
@@ -10139,11 +12016,13 @@ function createPromptMentionPlugin(options = {}) {
10139
12016
  const candidate = String(((_c = match == null ? void 0 : match.item) == null ? void 0 : _c.value) ?? "");
10140
12017
  if (!typed || !candidate.startsWith(typed))
10141
12018
  return false;
12019
+ markPromptOperation("accept");
10142
12020
  e.preventDefault();
10143
12021
  acceptPrompt(promptMention.promptActive.value);
10144
12022
  return true;
10145
12023
  }
10146
12024
  if (e.key === "Tab" && (isMention || isSkill)) {
12025
+ markPromptOperation("accept");
10147
12026
  e.preventDefault();
10148
12027
  if (len === 0)
10149
12028
  return true;
@@ -10160,11 +12039,13 @@ function createPromptMentionPlugin(options = {}) {
10160
12039
  }
10161
12040
  if (len === 0)
10162
12041
  return false;
12042
+ markPromptOperation("accept");
10163
12043
  e.preventDefault();
10164
12044
  acceptPrompt(promptMention.promptActive.value);
10165
12045
  return true;
10166
12046
  }
10167
12047
  if (e.key === "Escape") {
12048
+ markPromptOperation("dismiss");
10168
12049
  e.preventDefault();
10169
12050
  promptMention.promptSuppressedKey.value = ((_d = promptMention.activeContext.value) == null ? void 0 : _d.key) ?? null;
10170
12051
  ctx.scheduler.invalidate();
@@ -10176,6 +12057,28 @@ function createPromptMentionPlugin(options = {}) {
10176
12057
  useRenderNode(() => ({
10177
12058
  zIndex: getProps().zIndex + 5,
10178
12059
  stack: promptOverlayStack,
12060
+ dirtyRowsHint: (() => {
12061
+ const currentRect = ctx.visible.value && promptMention.promptVisible.value ? {
12062
+ y: Math.floor(promptMention.promptRect.value.y),
12063
+ h: Math.max(0, Math.floor(promptMention.promptRect.value.h))
12064
+ } : null;
12065
+ const rects = [lastPromptRect, currentRect].filter(Boolean);
12066
+ if (!rects.length)
12067
+ return void 0;
12068
+ const maxRows = ctx.terminal.size().rows;
12069
+ const start = Math.max(
12070
+ 0,
12071
+ Math.min(...rects.map((rect) => rect.y)) - 1
12072
+ );
12073
+ const end = Math.min(
12074
+ maxRows,
12075
+ Math.max(...rects.map((rect) => rect.y + rect.h)) + 1
12076
+ );
12077
+ if (end <= start)
12078
+ return void 0;
12079
+ return Array.from({ length: end - start }, (_, index) => start + index);
12080
+ })(),
12081
+ priority: "high",
10179
12082
  rect: ctx.visible.value && promptMention.promptVisible.value ? promptMention.promptRect.value : { x: 0, y: 0, w: 0, h: 0 },
10180
12083
  deps: [
10181
12084
  ctx.visible.value,
@@ -10199,6 +12102,10 @@ function createPromptMentionPlugin(options = {}) {
10199
12102
  const r = promptMention.promptRect.value;
10200
12103
  if (r.w < 3 || r.h < 3)
10201
12104
  return;
12105
+ lastPromptRect = {
12106
+ y: Math.floor(r.y),
12107
+ h: Math.max(0, Math.floor(r.h))
12108
+ };
10202
12109
  const base = getProps().style ?? ctx.defaultStyle.value;
10203
12110
  const selectedOverride = { bg: "blue" };
10204
12111
  const styleKey2 = base && typeof base === "object" ? base : null;
@@ -10231,6 +12138,18 @@ function createPromptMentionPlugin(options = {}) {
10231
12138
  const contentW = Math.max(0, innerW - sidePad * 2);
10232
12139
  const contentX = x0 + 1 + sidePad;
10233
12140
  const borderStyle = derived.borderStyle;
12141
+ if (lastTopClampedRect && y0 > 0) {
12142
+ ctx.terminal.write(spaces(lastTopClampedRect.w), {
12143
+ x: lastTopClampedRect.x,
12144
+ y: 0,
12145
+ style: getProps().style ?? ctx.defaultStyle.value
12146
+ });
12147
+ lastTopClampedRect = null;
12148
+ }
12149
+ if (y0 === 0)
12150
+ lastTopClampedRect = { x: x0, w };
12151
+ else
12152
+ lastTopClampedRect = null;
10234
12153
  ctx.terminal.write(`\u250C${repeatChar("\u2500", innerW)}\u2510`, {
10235
12154
  x: x0,
10236
12155
  y: y0,
@@ -12376,6 +14295,8 @@ var TerminalProvider = (0, import_vue19.defineComponent)({
12376
14295
  let rafToken = 0;
12377
14296
  let pendingInvalidate = false;
12378
14297
  let timer = null;
14298
+ let holdNormalInvalidates = false;
14299
+ let holdReleaseToken = 0;
12379
14300
  let pendingInvalidateAllPlanes = false;
12380
14301
  const pendingInvalidatePlanes = /* @__PURE__ */ new Set();
12381
14302
  let imeComposing = false;
@@ -12406,12 +14327,12 @@ var TerminalProvider = (0, import_vue19.defineComponent)({
12406
14327
  pendingInvalidatePlanes.clear();
12407
14328
  return activePlanes;
12408
14329
  }
12409
- function flush() {
14330
+ function flush(sync = false) {
12410
14331
  if (unmounting)
12411
14332
  return;
12412
14333
  const activePlanes = takeActivePlanes();
12413
14334
  render.render({ activePlanes });
12414
- terminal.commit({ planes: activePlanes });
14335
+ terminal.commit({ planes: activePlanes, sync });
12415
14336
  updateImePositionAfterFlush == null ? void 0 : updateImePositionAfterFlush();
12416
14337
  }
12417
14338
  function clearTimer() {
@@ -12420,6 +14341,18 @@ var TerminalProvider = (0, import_vue19.defineComponent)({
12420
14341
  clearTimeout(timer);
12421
14342
  timer = null;
12422
14343
  }
14344
+ function scheduleNormalInvalidateRelease() {
14345
+ const token = ++holdReleaseToken;
14346
+ void (0, import_vue19.nextTick)(() => {
14347
+ if (token !== holdReleaseToken)
14348
+ return;
14349
+ holdNormalInvalidates = false;
14350
+ if (unmounting || !pendingInvalidate)
14351
+ return;
14352
+ pendingInvalidate = false;
14353
+ invalidate({ priority: "normal" });
14354
+ });
14355
+ }
12423
14356
  function flushNow() {
12424
14357
  if (unmounting)
12425
14358
  return;
@@ -12431,7 +14364,9 @@ var TerminalProvider = (0, import_vue19.defineComponent)({
12431
14364
  raf = 0;
12432
14365
  }
12433
14366
  clearTimer();
12434
- flush();
14367
+ holdNormalInvalidates = true;
14368
+ scheduleNormalInvalidateRelease();
14369
+ flush(true);
12435
14370
  }
12436
14371
  function invalidate(options) {
12437
14372
  if (unmounting)
@@ -12443,6 +14378,10 @@ var TerminalProvider = (0, import_vue19.defineComponent)({
12443
14378
  flushNow();
12444
14379
  return;
12445
14380
  }
14381
+ if (holdNormalInvalidates) {
14382
+ pendingInvalidate = true;
14383
+ return;
14384
+ }
12446
14385
  if (priority === "low") {
12447
14386
  if (timer || raf)
12448
14387
  return;
@@ -13022,6 +14961,8 @@ var TerminalProvider = (0, import_vue19.defineComponent)({
13022
14961
  (0, import_vue19.onBeforeUnmount)(() => {
13023
14962
  unmounting = true;
13024
14963
  offCommit == null ? void 0 : offCommit();
14964
+ clearTimer();
14965
+ holdReleaseToken++;
13025
14966
  if (raf > 0)
13026
14967
  cancelAnimationFrame(raf);
13027
14968
  raf = 0;
@@ -14024,7 +15965,10 @@ var TInput = (0, import_vue21.defineComponent)({
14024
15965
  emit("input", nextValue);
14025
15966
  if (commit)
14026
15967
  emit("change", nextValue);
14027
- scheduler.invalidate();
15968
+ scheduler.flushNow();
15969
+ void (0, import_vue21.nextTick)(() => {
15970
+ scheduler.flushNow();
15971
+ });
14028
15972
  }
14029
15973
  function applyMove(nextCursor, extend) {
14030
15974
  const prev = cursor.value;
@@ -14058,7 +16002,7 @@ var TInput = (0, import_vue21.defineComponent)({
14058
16002
  }
14059
16003
  ensureCursorVisible();
14060
16004
  syncImeAnchorNow();
14061
- scheduler.invalidate();
16005
+ scheduler.flushNow();
14062
16006
  }
14063
16007
  (0, import_vue21.watch)(
14064
16008
  [() => absRect.value.w, () => absRect.value.h, () => wrapMode.value],
@@ -14192,7 +16136,11 @@ var TInput = (0, import_vue21.defineComponent)({
14192
16136
  terminal,
14193
16137
  scheduler,
14194
16138
  defaultStyle,
14195
- render,
16139
+ render: {
16140
+ rootStack: render.rootStack,
16141
+ createStack: render.createStack,
16142
+ invalidatePlane: render.invalidatePlane
16143
+ },
14196
16144
  visible,
14197
16145
  rawAbsRect,
14198
16146
  eventZ,
@@ -14306,7 +16254,7 @@ var TInput = (0, import_vue21.defineComponent)({
14306
16254
  emitMentions(nextMentions);
14307
16255
  ensureCursorVisible();
14308
16256
  syncImeAnchorNow();
14309
- scheduler.invalidate();
16257
+ scheduler.flushNow();
14310
16258
  }
14311
16259
  function removePendingPasteImage(placeholderPath) {
14312
16260
  const current = getCurrentMentions();
@@ -15124,10 +17072,12 @@ var TInput = (0, import_vue21.defineComponent)({
15124
17072
  clearAll();
15125
17073
  return;
15126
17074
  }
15127
- focused.value = false;
15128
- anchor.value = null;
15129
- emit("blur");
15130
- scheduler.invalidate();
17075
+ const manager = events.value;
17076
+ if (manager && manager.getFocused()) {
17077
+ manager.focus(null);
17078
+ return;
17079
+ }
17080
+ applyBlur();
15131
17081
  return;
15132
17082
  }
15133
17083
  if (isPrintableKey(e)) {
@@ -15390,6 +17340,7 @@ var TInput = (0, import_vue21.defineComponent)({
15390
17340
  var _a2;
15391
17341
  return {
15392
17342
  zIndex: props.zIndex,
17343
+ priority: pendingValue.value != null ? "high" : "normal",
15393
17344
  rect: visible.value ? absRect.value : { x: 0, y: 0, w: 0, h: 0 },
15394
17345
  deps: [
15395
17346
  visible.value,
@@ -15417,6 +17368,7 @@ var TInput = (0, import_vue21.defineComponent)({
15417
17368
  blinkOn.value,
15418
17369
  defaultStyle.value,
15419
17370
  ((_a2 = chipStyleProvider.value) == null ? void 0 : _a2.version.value) ?? 0,
17371
+ pendingValue.value,
15420
17372
  props.skillHighlightStyle,
15421
17373
  props.skillTrigger
15422
17374
  ],
@@ -15986,26 +17938,34 @@ var import_vue24 = require("vue");
15986
17938
 
15987
17939
  // src/vue/utils/wheel-scroll.ts
15988
17940
  var LINE_UNIT_THRESHOLD = 3;
15989
- var PIXELS_PER_LINE = 100;
17941
+ var PIXELS_PER_LINE = 0.75;
15990
17942
  var ACCEL_WINDOW_MS = 120;
15991
- var MAX_ACCEL = 2.5;
17943
+ var MAX_ACCEL = 26;
15992
17944
  var EDGE_BOUNCE_MS = 120;
15993
17945
  var MIN_TICK_INTERVAL_MS = 6;
15994
17946
  function clamp5(n, min, max) {
15995
17947
  return Math.max(min, Math.min(max, n));
15996
17948
  }
15997
- function isLineUnitDelta(deltaY) {
17949
+ function isLineUnitDelta(deltaY, mode = "auto") {
17950
+ if (mode === "line")
17951
+ return true;
17952
+ if (mode === "pixel")
17953
+ return false;
15998
17954
  const abs = Math.abs(deltaY);
15999
17955
  return abs > 0 && abs <= LINE_UNIT_THRESHOLD && Number.isInteger(deltaY);
16000
17956
  }
16001
- function normalizeDelta(deltaY) {
17957
+ function normalizeDelta(deltaY, mode = "auto") {
17958
+ if (mode === "line")
17959
+ return deltaY;
17960
+ if (mode === "pixel")
17961
+ return deltaY / PIXELS_PER_LINE;
16002
17962
  const abs = Math.abs(deltaY);
16003
17963
  if (abs <= LINE_UNIT_THRESHOLD && Number.isInteger(deltaY))
16004
17964
  return deltaY;
16005
17965
  return deltaY / PIXELS_PER_LINE;
16006
17966
  }
16007
- function accelFactor(now3, lastAt) {
16008
- const dt = now3 - lastAt;
17967
+ function accelFactor(now4, lastAt) {
17968
+ const dt = now4 - lastAt;
16009
17969
  if (!Number.isFinite(dt) || dt <= 0 || dt > ACCEL_WINDOW_MS)
16010
17970
  return 1;
16011
17971
  const t = 1 - dt / ACCEL_WINDOW_MS;
@@ -16016,25 +17976,30 @@ function createWheelScrollState() {
16016
17976
  accumulator: 0,
16017
17977
  lastAt: 0,
16018
17978
  lastEdgeDir: 0,
16019
- lastEdgeAt: 0
17979
+ lastEdgeAt: 0,
17980
+ lastLineUnitDir: 0
16020
17981
  };
16021
17982
  }
16022
- function applyWheelScroll(state, deltaY, scrollTop, maxTop, now3 = Date.now()) {
17983
+ function applyWheelScroll(state, deltaY, scrollTop, maxTop, now4 = Date.now(), deltaMode = "auto", options = {}) {
16023
17984
  if (!Number.isFinite(deltaY) || deltaY === 0 || maxTop <= 0) {
16024
17985
  if (maxTop <= 0) {
16025
17986
  state.accumulator = 0;
16026
17987
  state.lastEdgeDir = 0;
16027
17988
  state.lastEdgeAt = 0;
17989
+ state.lastLineUnitDir = 0;
16028
17990
  }
16029
17991
  return { nextTop: scrollTop, dir: 0, lines: 0 };
16030
17992
  }
16031
- const dt = state.lastAt ? now3 - state.lastAt : Infinity;
16032
- const lineUnits = isLineUnitDelta(deltaY);
16033
- if (lineUnits && dt !== Infinity && dt >= 0 && dt < MIN_TICK_INTERVAL_MS)
17993
+ const dt = state.lastAt ? now4 - state.lastAt : Infinity;
17994
+ const lineUnits = isLineUnitDelta(deltaY, deltaMode);
17995
+ const inputDir = Math.sign(normalizeDelta(deltaY, deltaMode));
17996
+ if (lineUnits && dt !== Infinity && dt >= 0 && dt < MIN_TICK_INTERVAL_MS && inputDir === state.lastLineUnitDir) {
16034
17997
  return { nextTop: scrollTop, dir: 0, lines: 0 };
16035
- const accel = lineUnits ? 1 : state.lastAt ? accelFactor(now3, state.lastAt) : 1;
16036
- state.lastAt = now3;
16037
- state.accumulator += normalizeDelta(deltaY) * accel;
17998
+ }
17999
+ state.lastLineUnitDir = lineUnits ? inputDir : 0;
18000
+ const accel = lineUnits ? 1 : options.disableAcceleration ? 1 : state.lastAt ? accelFactor(now4, state.lastAt) : 1;
18001
+ state.lastAt = now4;
18002
+ state.accumulator += normalizeDelta(deltaY, deltaMode) * accel;
16038
18003
  const lines = Math.trunc(state.accumulator);
16039
18004
  if (lines === 0)
16040
18005
  return { nextTop: scrollTop, dir: 0, lines: 0 };
@@ -16044,11 +18009,11 @@ function applyWheelScroll(state, deltaY, scrollTop, maxTop, now3 = Date.now()) {
16044
18009
  const atBottom = scrollTop >= maxTop;
16045
18010
  if (dir < 0 && atTop || dir > 0 && atBottom) {
16046
18011
  state.lastEdgeDir = dir;
16047
- state.lastEdgeAt = now3;
18012
+ state.lastEdgeAt = now4;
16048
18013
  state.accumulator = 0;
16049
18014
  return { nextTop: scrollTop, dir: 0, lines: 0 };
16050
18015
  }
16051
- if (state.lastEdgeDir !== 0 && now3 - state.lastEdgeAt < EDGE_BOUNCE_MS && dir === -state.lastEdgeDir && (atTop || atBottom)) {
18016
+ if (state.lastEdgeDir !== 0 && now4 - state.lastEdgeAt < EDGE_BOUNCE_MS && dir === -state.lastEdgeDir && (atTop || atBottom)) {
16052
18017
  return { nextTop: scrollTop, dir: 0, lines: 0 };
16053
18018
  }
16054
18019
  const unclamped = scrollTop + lines;
@@ -16071,6 +18036,18 @@ function applyWheelScroll(state, deltaY, scrollTop, maxTop, now3 = Date.now()) {
16071
18036
  function clamp6(n, min, max) {
16072
18037
  return Math.max(min, Math.min(max, n));
16073
18038
  }
18039
+ function getWheelScrollInput(e) {
18040
+ const deltaY = Number(e.deltaY ?? 0);
18041
+ const deltaMode = typeof e.deltaMode === "number" ? e.deltaMode : void 0;
18042
+ if (Number.isInteger(deltaY) && deltaY !== 0 && Math.abs(deltaY) >= 100 && Math.abs(deltaY) % 100 === 0 && (deltaMode == null || deltaMode === 0)) {
18043
+ return { deltaY: deltaY / 100, mode: "line" };
18044
+ }
18045
+ if (deltaMode === 1)
18046
+ return { deltaY, mode: "line" };
18047
+ if (deltaMode === 0)
18048
+ return { deltaY, mode: "pixel" };
18049
+ return { deltaY, mode: "auto" };
18050
+ }
16074
18051
  var TList = (0, import_vue24.defineComponent)({
16075
18052
  name: "TList",
16076
18053
  props: {
@@ -16249,7 +18226,8 @@ var TList = (0, import_vue24.defineComponent)({
16249
18226
  commit(idx);
16250
18227
  },
16251
18228
  wheel: (e) => {
16252
- const delta = e.deltaY ?? 0;
18229
+ const { deltaY, mode } = getWheelScrollInput(e);
18230
+ const delta = deltaY;
16253
18231
  if (!delta)
16254
18232
  return;
16255
18233
  const h18 = Math.max(0, props.h);
@@ -16258,7 +18236,9 @@ var TList = (0, import_vue24.defineComponent)({
16258
18236
  wheelState,
16259
18237
  delta,
16260
18238
  scrollTop.value,
16261
- maxTop
18239
+ maxTop,
18240
+ Date.now(),
18241
+ mode
16262
18242
  );
16263
18243
  if (!dir || nextTop === scrollTop.value)
16264
18244
  return;
@@ -16552,6 +18532,18 @@ init_path();
16552
18532
  function clamp7(n, min, max) {
16553
18533
  return Math.max(min, Math.min(max, n));
16554
18534
  }
18535
+ function getWheelScrollInput2(e) {
18536
+ const deltaY = Number(e.deltaY ?? 0);
18537
+ const deltaMode = typeof e.deltaMode === "number" ? e.deltaMode : void 0;
18538
+ if (Number.isInteger(deltaY) && deltaY !== 0 && Math.abs(deltaY) >= 100 && Math.abs(deltaY) % 100 === 0 && (deltaMode == null || deltaMode === 0)) {
18539
+ return { deltaY: deltaY / 100, mode: "line" };
18540
+ }
18541
+ if (deltaMode === 1)
18542
+ return { deltaY, mode: "line" };
18543
+ if (deltaMode === 0)
18544
+ return { deltaY, mode: "pixel" };
18545
+ return { deltaY, mode: "auto" };
18546
+ }
16555
18547
  function isCtrlLike(e) {
16556
18548
  return Boolean(e.ctrlKey || e.metaKey);
16557
18549
  }
@@ -16917,7 +18909,8 @@ var TPathPicker = (0, import_vue26.defineComponent)({
16917
18909
  await trySelect(s.absPath);
16918
18910
  },
16919
18911
  wheel: (e) => {
16920
- const delta = e.deltaY ?? 0;
18912
+ const { deltaY, mode } = getWheelScrollInput2(e);
18913
+ const delta = deltaY;
16921
18914
  if (!delta)
16922
18915
  return;
16923
18916
  const h18 = Math.max(0, listH.value);
@@ -16926,7 +18919,9 @@ var TPathPicker = (0, import_vue26.defineComponent)({
16926
18919
  wheelState,
16927
18920
  delta,
16928
18921
  scrollTop.value,
16929
- maxTop
18922
+ maxTop,
18923
+ Date.now(),
18924
+ mode
16930
18925
  );
16931
18926
  if (!dir || nextTop === scrollTop.value)
16932
18927
  return;
@@ -18073,6 +20068,7 @@ var TRouterView = (0, import_vue32.defineComponent)({
18073
20068
  defaultTInputHostPlugin,
18074
20069
  detectTerminalColorCapability,
18075
20070
  formatInlineCellLine,
20071
+ getCliLatencyProfiler,
18076
20072
  getStdoutRendererMetrics,
18077
20073
  lintJsonText,
18078
20074
  normalizeNewlines,