@crazyhappyone/dsh-tui 0.1.0-alpha.17 → 0.1.0-alpha.18

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 (2) hide show
  1. package/dist/index.js +562 -241
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -38172,10 +38172,6 @@ var import_react40 = __toESM(require_react(), 1);
38172
38172
 
38173
38173
  // ../deepseek-harness/packages/tui/tui-render/src/content.ts
38174
38174
  var GRAPHEME = new Intl.Segmenter(void 0, { granularity: "grapheme" });
38175
- function firstGrapheme(text4) {
38176
- for (const part of GRAPHEME.segment(text4)) return part.segment;
38177
- return text4.slice(0, 1);
38178
- }
38179
38175
  function escapeContent(text4) {
38180
38176
  return text4.replace(
38181
38177
  /[\u0000-\u0008\u000B-\u001F\u007F]/g,
@@ -38185,7 +38181,20 @@ function escapeContent(text4) {
38185
38181
  var WIDE_SYMBOLS_OR_EMOJIS = /[\u26A0\u26A1\u2699\u2139\u23F1\u2328\u2709\u270F\u2712\u2702\u26C8\u2764\u2B50]/gu;
38186
38182
  function displayWidth(text4) {
38187
38183
  if (text4 === "") return 0;
38188
- if (/^[\x20-\x7e]*$/u.test(text4)) return text4.length;
38184
+ let cols = 0;
38185
+ let simple = true;
38186
+ for (let i = 0; i < text4.length; i += 1) {
38187
+ const code2 = text4.charCodeAt(i);
38188
+ if (code2 >= 32 && code2 <= 126) {
38189
+ cols += 1;
38190
+ } else if (code2 >= 19968 && code2 <= 40959 || code2 >= 13312 && code2 <= 19903 || code2 >= 12288 && code2 <= 12351 || code2 >= 65281 && code2 <= 65376) {
38191
+ cols += 2;
38192
+ } else {
38193
+ simple = false;
38194
+ break;
38195
+ }
38196
+ }
38197
+ if (simple) return cols;
38189
38198
  const base = stringWidth(text4);
38190
38199
  if (!/[\u26A0\u26A1\u2699\u2139\u23F1\u2328\u2709\u270F\u2712\u2702\u26C8\u2764\u2B50]/u.test(text4)) {
38191
38200
  return base;
@@ -38267,17 +38276,27 @@ function wrapDisplayLines(text4, maxCols) {
38267
38276
  out.push(line8);
38268
38277
  continue;
38269
38278
  }
38270
- let rest = line8;
38271
- while (rest !== "") {
38272
- const chunk = wcwidthSafeSlice(rest, maxCols);
38273
- if (chunk === "") {
38274
- const first = firstGrapheme(rest);
38275
- out.push(first);
38276
- rest = rest.slice(first.length);
38277
- continue;
38279
+ if (/^[\x20-\x7e]*$/u.test(line8)) {
38280
+ for (let i = 0; i < line8.length; i += maxCols) {
38281
+ out.push(line8.slice(i, i + maxCols));
38278
38282
  }
38279
- out.push(chunk);
38280
- rest = rest.slice(chunk.length);
38283
+ continue;
38284
+ }
38285
+ let curLine = "";
38286
+ let curCols = 0;
38287
+ for (const { segment: segment2 } of GRAPHEME.segment(line8)) {
38288
+ const width = displayWidth(segment2);
38289
+ if (curCols + width > maxCols && curLine !== "") {
38290
+ out.push(curLine);
38291
+ curLine = segment2;
38292
+ curCols = width;
38293
+ } else {
38294
+ curLine += segment2;
38295
+ curCols += width;
38296
+ }
38297
+ }
38298
+ if (curLine !== "") {
38299
+ out.push(curLine);
38281
38300
  }
38282
38301
  }
38283
38302
  return out;
@@ -50186,6 +50205,84 @@ function textLines(text4, prefix = "") {
50186
50205
  return { text: prefix + text4.slice(lastStart, end < 0 ? text4.length : end), token: "codeBg" };
50187
50206
  });
50188
50207
  }
50208
+ function splitDiffLines(text4) {
50209
+ if (text4.length === 0) return [];
50210
+ const normalized = text4.endsWith("\n") ? text4.slice(0, -1) : text4;
50211
+ return normalized.split(/\r?\n/u);
50212
+ }
50213
+ function diffLines(oldLines, newLines) {
50214
+ if (oldLines.length === 0) return newLines.map((line8) => `+${line8}`);
50215
+ if (newLines.length === 0) return oldLines.map((line8) => `-${line8}`);
50216
+ const m = oldLines.length;
50217
+ const n = newLines.length;
50218
+ if (m * n > 5e5) {
50219
+ return [...oldLines.map((l) => `-${l}`), ...newLines.map((l) => `+${l}`)];
50220
+ }
50221
+ let prefixCount = 0;
50222
+ while (prefixCount < m && prefixCount < n && oldLines[prefixCount] === newLines[prefixCount]) {
50223
+ prefixCount++;
50224
+ }
50225
+ let suffixCount = 0;
50226
+ while (suffixCount < m - prefixCount && suffixCount < n - prefixCount && oldLines[m - 1 - suffixCount] === newLines[n - 1 - suffixCount]) {
50227
+ suffixCount++;
50228
+ }
50229
+ const trimmedOld = oldLines.slice(prefixCount, m - suffixCount);
50230
+ const trimmedNew = newLines.slice(prefixCount, n - suffixCount);
50231
+ const midM = trimmedOld.length;
50232
+ const midN = trimmedNew.length;
50233
+ const middle = [];
50234
+ if (midM === 0) {
50235
+ for (const line8 of trimmedNew) middle.push(`+${line8}`);
50236
+ } else if (midN === 0) {
50237
+ for (const line8 of trimmedOld) middle.push(`-${line8}`);
50238
+ } else {
50239
+ const stride = midN + 1;
50240
+ const dp = new Int32Array((midM + 1) * stride);
50241
+ for (let i2 = 0; i2 < midM; i2++) {
50242
+ for (let j2 = 0; j2 < midN; j2++) {
50243
+ const dest = (i2 + 1) * stride + (j2 + 1);
50244
+ if (trimmedOld[i2] === trimmedNew[j2]) {
50245
+ dp[dest] = (dp[i2 * stride + j2] ?? 0) + 1;
50246
+ } else {
50247
+ const up = dp[i2 * stride + (j2 + 1)] ?? 0;
50248
+ const left = dp[(i2 + 1) * stride + j2] ?? 0;
50249
+ dp[dest] = up > left ? up : left;
50250
+ }
50251
+ }
50252
+ }
50253
+ const rev = [];
50254
+ let i = midM;
50255
+ let j = midN;
50256
+ while (i > 0 || j > 0) {
50257
+ if (i > 0 && j > 0 && trimmedOld[i - 1] === trimmedNew[j - 1]) {
50258
+ rev.push(` ${trimmedOld[i - 1]}`);
50259
+ i--;
50260
+ j--;
50261
+ } else if (j > 0 && (i === 0 || (dp[i * stride + (j - 1)] ?? 0) >= (dp[(i - 1) * stride + j] ?? 0))) {
50262
+ rev.push(`+${trimmedNew[j - 1]}`);
50263
+ j--;
50264
+ } else if (i > 0) {
50265
+ rev.push(`-${trimmedOld[i - 1]}`);
50266
+ i--;
50267
+ }
50268
+ }
50269
+ for (let k = rev.length - 1; k >= 0; k--) {
50270
+ const item = rev[k];
50271
+ if (item !== void 0) middle.push(item);
50272
+ }
50273
+ }
50274
+ const result = [];
50275
+ for (let k = 0; k < prefixCount; k++) {
50276
+ result.push(` ${oldLines[k]}`);
50277
+ }
50278
+ for (const line8 of middle) {
50279
+ result.push(line8);
50280
+ }
50281
+ for (let k = m - suffixCount; k < m; k++) {
50282
+ result.push(` ${oldLines[k]}`);
50283
+ }
50284
+ return result;
50285
+ }
50189
50286
  function createToolBodyDocument(card, options) {
50190
50287
  const rows = new RowSequence();
50191
50288
  const section = (label, source) => {
@@ -50211,10 +50308,62 @@ function createToolBodyDocument(card, options) {
50211
50308
  const diffs = result?.card === "diff" ? result.diffs : card.callView?.card === "diff" ? card.callView.diffs : [];
50212
50309
  if (diffs.length === 0) break;
50213
50310
  const source = new RowSequence();
50311
+ let lastPath;
50214
50312
  for (const diff2 of diffs) {
50215
- source.push({ text: `--- ${diff2.path}`, token: "codeBg" });
50216
- if (diff2.oldText !== null) source.append(textLines(diff2.oldText, "- "));
50217
- source.append(textLines(diff2.newText, "+ "));
50313
+ if (diff2.path !== lastPath) {
50314
+ source.push({ text: `--- ${diff2.path}`, token: "codeBg" });
50315
+ lastPath = diff2.path;
50316
+ }
50317
+ const oldStart = diff2.oldStart ?? (diff2.oldText === null ? 0 : 1);
50318
+ const newStart = diff2.newStart ?? 1;
50319
+ let lines;
50320
+ let oldLinesCount = diff2.oldLines;
50321
+ let newLinesCount = diff2.newLines;
50322
+ if (diff2.lines !== void 0 && diff2.lines.length > 0) {
50323
+ lines = diff2.lines;
50324
+ oldLinesCount ??= diff2.oldText === null ? 0 : splitDiffLines(diff2.oldText).length;
50325
+ newLinesCount ??= splitDiffLines(diff2.newText).length;
50326
+ } else if (diff2.oldText === null) {
50327
+ const split = splitDiffLines(diff2.newText);
50328
+ lines = split.map((line8) => `+${line8}`);
50329
+ oldLinesCount = 0;
50330
+ newLinesCount = split.length;
50331
+ } else {
50332
+ const oldSplit = splitDiffLines(diff2.oldText);
50333
+ const newSplit = splitDiffLines(diff2.newText);
50334
+ lines = diffLines(oldSplit, newSplit);
50335
+ oldLinesCount ??= oldSplit.length;
50336
+ newLinesCount ??= newSplit.length;
50337
+ }
50338
+ const oldHunk = `${oldStart}${oldLinesCount !== 1 ? `,${oldLinesCount}` : ""}`;
50339
+ const newHunk = `${newStart}${newLinesCount !== 1 ? `,${newLinesCount}` : ""}`;
50340
+ source.push({ text: `@@ -${oldHunk} +${newHunk} @@`, token: "codeBg" });
50341
+ const maxLine = Math.max(oldStart + oldLinesCount, newStart + newLinesCount, 1);
50342
+ const gutterWidth = Math.max(3, String(maxLine).length);
50343
+ let curOld = oldStart === 0 ? 1 : oldStart;
50344
+ let curNew = newStart === 0 ? 1 : newStart;
50345
+ for (const raw of lines) {
50346
+ if (raw.startsWith("\\")) continue;
50347
+ if (raw.startsWith("-")) {
50348
+ const num = String(curOld).padStart(gutterWidth, " ");
50349
+ source.push({ text: `${num} \u2502 - ${raw.slice(1)}`, token: "codeBg" });
50350
+ curOld++;
50351
+ } else if (raw.startsWith("+")) {
50352
+ const num = String(curNew).padStart(gutterWidth, " ");
50353
+ source.push({ text: `${num} \u2502 + ${raw.slice(1)}`, token: "codeBg" });
50354
+ curNew++;
50355
+ } else if (raw.startsWith(" ")) {
50356
+ const num = String(curNew).padStart(gutterWidth, " ");
50357
+ source.push({ text: `${num} \u2502 ${raw.slice(1)}`, token: "codeBg" });
50358
+ curOld++;
50359
+ curNew++;
50360
+ } else {
50361
+ const num = String(curNew).padStart(gutterWidth, " ");
50362
+ source.push({ text: `${num} \u2502 ${raw}`, token: "codeBg" });
50363
+ curOld++;
50364
+ curNew++;
50365
+ }
50366
+ }
50218
50367
  }
50219
50368
  section("diff", source.build());
50220
50369
  break;
@@ -50586,7 +50735,9 @@ var ToolRowCache = class {
50586
50735
  }
50587
50736
  if (cached !== void 0) this.remove(key, cached);
50588
50737
  const document4 = expanded ? createToolBodyDocument(card, { diagnostics: false, includeArguments: false, locale }) : void 0;
50589
- const window2 = document4 === void 0 ? void 0 : planToolBodyWindow(document4, { line: 0, offset: 0 }, Math.max(1, width - 2), this.policy.previewRows);
50738
+ const isDiff = card.callView?.card === "diff" || card.resultView?.card === "diff";
50739
+ const rowBudget = isDiff ? this.policy.diffPreviewRows : this.policy.previewRows;
50740
+ const window2 = document4 === void 0 ? void 0 : planToolBodyWindow(document4, { line: 0, offset: 0 }, Math.max(1, width - 2), rowBudget);
50590
50741
  const entry = {
50591
50742
  card,
50592
50743
  document: document4,
@@ -50651,14 +50802,14 @@ var RENDER_POLICY_DEFAULT_SCROLL_STEP_PER_FRAME = 1;
50651
50802
  var RENDER_POLICY_DEFAULT_SCROLL_WHEEL_ROWS = 3;
50652
50803
  var RENDER_POLICY_DEFAULT_SCROLL_CATCH_UP_THRESHOLD = 10;
50653
50804
  var RENDER_POLICY_DEFAULT_SCROLL_MAX_CATCH_UP_STEP = 8;
50654
- var RENDER_POLICY_DEFAULT_CACHE_MAX_ROWS = 4096;
50655
- var RENDER_POLICY_DEFAULT_CACHE_MAX_BYTES = 4 * 1024 * 1024;
50805
+ var RENDER_POLICY_DEFAULT_CACHE_MAX_ROWS = 65536;
50806
+ var RENDER_POLICY_DEFAULT_CACHE_MAX_BYTES = 32 * 1024 * 1024;
50656
50807
  var RENDER_POLICY_DEFAULT_TRANSCRIPT_OVERSCAN = 16;
50657
50808
  var RENDER_POLICY_MAX_OVERSCAN = 50;
50658
50809
  var RENDER_POLICY_MAX_CACHE_ROWS = 1e6;
50659
- var RENDER_POLICY_MAX_CACHE_BYTES = 32 * 1024 * 1024;
50810
+ var RENDER_POLICY_MAX_CACHE_BYTES = 64 * 1024 * 1024;
50660
50811
  function toolPolicyDefaults() {
50661
- return { previewRows: 6, detailPageRows: 40, cacheEntries: 128, cacheRows: 2048 };
50812
+ return { previewRows: 6, diffPreviewRows: 200, detailPageRows: 40, cacheEntries: 512, cacheRows: 16384 };
50662
50813
  }
50663
50814
  function renderPolicyDefaults() {
50664
50815
  return {
@@ -51167,6 +51318,9 @@ function reduceTranscriptViewport(state, action) {
51167
51318
  state.viewportRows
51168
51319
  );
51169
51320
  const follow = offsetFromBottom === 0 ? true : action.delta > 0 ? false : state.follow;
51321
+ if (offsetFromBottom === state.offsetFromBottom && follow === state.follow && (follow ? 0 : state.unseenRows) === state.unseenRows) {
51322
+ return state;
51323
+ }
51170
51324
  const nextTop = topRow({ ...state, offsetFromBottom });
51171
51325
  return {
51172
51326
  ...state,
@@ -51183,6 +51337,9 @@ function reduceTranscriptViewport(state, action) {
51183
51337
  state.viewportRows
51184
51338
  );
51185
51339
  const follow = offsetFromBottom === 0;
51340
+ if (offsetFromBottom === state.offsetFromBottom && follow === state.follow && (follow ? 0 : state.unseenRows) === state.unseenRows) {
51341
+ return state;
51342
+ }
51186
51343
  const nextTop = topRow({ ...state, offsetFromBottom });
51187
51344
  return {
51188
51345
  ...state,
@@ -51197,6 +51354,9 @@ function reduceTranscriptViewport(state, action) {
51197
51354
  const fraction = Math.max(0, Math.min(action.fraction, 1));
51198
51355
  const offsetFromBottom = Math.round(available * (1 - fraction));
51199
51356
  const follow = offsetFromBottom === 0;
51357
+ if (offsetFromBottom === state.offsetFromBottom && follow === state.follow && (follow ? 0 : state.unseenRows) === state.unseenRows) {
51358
+ return state;
51359
+ }
51200
51360
  const nextTop = topRow({ ...state, offsetFromBottom });
51201
51361
  return {
51202
51362
  ...state,
@@ -51208,6 +51368,9 @@ function reduceTranscriptViewport(state, action) {
51208
51368
  }
51209
51369
  case "edge": {
51210
51370
  if (action.edge === "latest") {
51371
+ if (state.follow && state.offsetFromBottom === 0 && state.unseenRows === 0 && state.anchor === void 0) {
51372
+ return state;
51373
+ }
51211
51374
  return {
51212
51375
  ...state,
51213
51376
  follow: true,
@@ -51217,6 +51380,9 @@ function reduceTranscriptViewport(state, action) {
51217
51380
  };
51218
51381
  }
51219
51382
  const offsetFromBottom = maxOffset(state.contentRows, state.viewportRows);
51383
+ if (!state.follow && state.offsetFromBottom === offsetFromBottom) {
51384
+ return state;
51385
+ }
51220
51386
  return {
51221
51387
  ...state,
51222
51388
  follow: false,
@@ -51224,8 +51390,12 @@ function reduceTranscriptViewport(state, action) {
51224
51390
  anchor: anchorForRow(0, state.blocks)
51225
51391
  };
51226
51392
  }
51227
- case "reset":
51393
+ case "reset": {
51394
+ if (state.follow && state.offsetFromBottom === 0 && state.unseenRows === 0 && state.contentRows === 0 && state.viewportRows === 0 && state.blocks.length === 0 && state.anchor === void 0) {
51395
+ return state;
51396
+ }
51228
51397
  return EMPTY_TRANSCRIPT_VIEWPORT;
51398
+ }
51229
51399
  }
51230
51400
  }
51231
51401
  function physicalScrollRailGeometry(contentRows, viewportRows, offsetFromBottom) {
@@ -51452,8 +51622,11 @@ function setVisibleFrameSnapshot(snapshot) {
51452
51622
  function visibleFrameSnapshot() {
51453
51623
  return publishedSnapshot;
51454
51624
  }
51625
+ var lineIdentityCache = /* @__PURE__ */ new WeakMap();
51455
51626
  function physicalLineIdentity(line8) {
51456
- return JSON.stringify([
51627
+ let cached = lineIdentityCache.get(line8);
51628
+ if (cached !== void 0) return cached;
51629
+ cached = JSON.stringify([
51457
51630
  line8.text,
51458
51631
  line8.displayWidth,
51459
51632
  line8.background ?? "bg",
@@ -51465,6 +51638,8 @@ function physicalLineIdentity(line8) {
51465
51638
  span.href ?? ""
51466
51639
  ])
51467
51640
  ]);
51641
+ lineIdentityCache.set(line8, cached);
51642
+ return cached;
51468
51643
  }
51469
51644
  function createFrameSnapshotRow(input) {
51470
51645
  return Object.freeze({
@@ -51472,18 +51647,29 @@ function createFrameSnapshotRow(input) {
51472
51647
  identity: physicalLineIdentity(input.line)
51473
51648
  });
51474
51649
  }
51475
- function geometryIdentity(geometry) {
51476
- return JSON.stringify(geometry);
51650
+ function sameGeometry(a, b) {
51651
+ if (a === b) return true;
51652
+ if (a.columns !== b.columns || a.rows !== b.rows || a.transcriptTop !== b.transcriptTop || a.transcriptLeft !== b.transcriptLeft || a.transcriptWidth !== b.transcriptWidth || a.transcriptRows !== b.transcriptRows) return false;
51653
+ const rA = a.rail;
51654
+ const rB = b.rail;
51655
+ if (rA === rB) return true;
51656
+ if (rA === void 0 || rB === void 0) return false;
51657
+ return rA.col === rB.col && rA.topRow === rB.topRow && rA.rows === rB.rows;
51477
51658
  }
51478
51659
  function screenRowKey(row) {
51479
- return `${String(row.row)}:${String(row.col)}`;
51660
+ return row.row << 16 | row.col;
51480
51661
  }
51481
51662
  function paintedColumns(line8) {
51482
51663
  return line8.backgroundColumns ?? line8.displayWidth;
51483
51664
  }
51484
51665
  function diffVisibleFrameSnapshots(previous3, next) {
51485
- const forced = previous3 === void 0 || geometryIdentity(previous3.geometry) !== geometryIdentity(next.geometry);
51486
- const oldRows = new Map(previous3?.rows.map((row) => [screenRowKey(row), row]) ?? []);
51666
+ const forced = previous3 === void 0 || !sameGeometry(previous3.geometry, next.geometry);
51667
+ const oldRows = /* @__PURE__ */ new Map();
51668
+ if (previous3 !== void 0) {
51669
+ for (const row of previous3.rows) {
51670
+ oldRows.set(screenRowKey(row), row);
51671
+ }
51672
+ }
51487
51673
  const changes = [];
51488
51674
  let unchangedRows = 0;
51489
51675
  for (const row of next.rows) {
@@ -51551,10 +51737,16 @@ function railRow(row, col, text4, tier) {
51551
51737
  const guard = col > 1 ? railCell(row, col - 1, " ", tier) : "";
51552
51738
  return guard + railCell(row, col, text4, tier);
51553
51739
  }
51554
- function railOverlay(tier) {
51555
- let cells = "";
51740
+ function sameRail(a, b) {
51741
+ if (a === b) return true;
51742
+ if (a === void 0 || b === void 0) return false;
51743
+ return a.col === b.col && a.topRow === b.topRow && a.rows === b.rows && a.thumbStart === b.thumbStart && a.thumbRows === b.thumbRows;
51744
+ }
51745
+ function railOverlay(tier, force = false) {
51556
51746
  const next = frameRail;
51557
51747
  const previous3 = paintedFrameRail;
51748
+ if (!force && sameRail(previous3, next)) return "";
51749
+ let cells = "";
51558
51750
  const geometry = visibleFrameSnapshot()?.geometry;
51559
51751
  const clearPreviousRow = (row, col) => {
51560
51752
  if (geometry !== void 0 && (col > geometry.columns || row < geometry.transcriptTop || row >= geometry.transcriptTop + geometry.transcriptRows || row > geometry.rows)) return "";
@@ -51583,17 +51775,40 @@ function railOverlay(tier) {
51583
51775
  paintedFrameRail = next;
51584
51776
  return cells === "" ? "" : `\x1B7${cells}\x1B8`;
51585
51777
  }
51778
+ var paintedPhysicalLineCache = /* @__PURE__ */ new WeakMap();
51586
51779
  function paintPhysicalLine(line8, tier) {
51780
+ const hyperlinks2 = hyperlinksEnabled();
51781
+ const cacheKey = `${tier}:${hyperlinks2 ? 1 : 0}`;
51782
+ let byTier = paintedPhysicalLineCache.get(line8);
51783
+ if (byTier === void 0) {
51784
+ byTier = /* @__PURE__ */ new Map();
51785
+ paintedPhysicalLineCache.set(line8, byTier);
51786
+ }
51787
+ const cached = byTier.get(cacheKey);
51788
+ if (cached !== void 0) return cached;
51587
51789
  const parts = line8.spans.map((span) => {
51588
51790
  const text4 = styled(span.text, span.token, tier, span.bold);
51589
- return span.href !== void 0 && hyperlinksEnabled() && isOsc8Href(span.href) ? wrapOsc8(text4, span.href) : text4;
51791
+ return span.href !== void 0 && hyperlinks2 && isOsc8Href(span.href) ? wrapOsc8(text4, span.href) : text4;
51590
51792
  });
51591
- return line8.background !== void 0 && line8.background !== "bg" ? paintBackgroundRow(
51793
+ const painted = line8.background !== void 0 && line8.background !== "bg" ? paintBackgroundRow(
51592
51794
  parts,
51593
51795
  line8.background,
51594
51796
  line8.backgroundColumns ?? Math.max(1, line8.displayWidth),
51595
51797
  tier
51596
51798
  ) : paintRow(parts, tier);
51799
+ byTier.set(cacheKey, painted);
51800
+ return painted;
51801
+ }
51802
+ var blankPadCache = /* @__PURE__ */ new Map();
51803
+ function blankPad(count, tier) {
51804
+ if (count <= 0) return "";
51805
+ const key = `${tier}:${String(count)}`;
51806
+ let cached = blankPadCache.get(key);
51807
+ if (cached === void 0) {
51808
+ cached = styled(" ".repeat(count), "bg", tier);
51809
+ blankPadCache.set(key, cached);
51810
+ }
51811
+ return cached;
51597
51812
  }
51598
51813
  function transcriptOverlay(tier, synchronizedFrameEnded) {
51599
51814
  const next = visibleFrameSnapshot();
@@ -51602,6 +51817,9 @@ function transcriptOverlay(tier, synchronizedFrameEnded) {
51602
51817
  paintedTranscriptRepaintKey = void 0;
51603
51818
  return "";
51604
51819
  }
51820
+ if (paintedVisibleFrame === next && (next.repaintKey === void 0 || next.repaintKey === paintedTranscriptRepaintKey)) {
51821
+ return "";
51822
+ }
51605
51823
  const diff2 = diffVisibleFrameSnapshots(paintedVisibleFrame, next);
51606
51824
  if (diff2.forced && !synchronizedFrameEnded) return "";
51607
51825
  const repaintAll = synchronizedFrameEnded && (diff2.forced || next.repaintKey !== void 0 && next.repaintKey !== paintedTranscriptRepaintKey);
@@ -51610,7 +51828,7 @@ function transcriptOverlay(tier, synchronizedFrameEnded) {
51610
51828
  let cells = "";
51611
51829
  if (repaintAll) {
51612
51830
  const { columns, transcriptRows, transcriptTop } = next.geometry;
51613
- const blank = styled(" ".repeat(columns), "bg", tier);
51831
+ const blank = blankPad(columns, tier);
51614
51832
  for (let index2 = 0; index2 < transcriptRows; index2 += 1) {
51615
51833
  cells += `\x1B[${String(transcriptTop + index2)};1H${blank}`;
51616
51834
  }
@@ -51629,7 +51847,7 @@ function transcriptOverlay(tier, synchronizedFrameEnded) {
51629
51847
  cells += `\x1B[${String(change.row)};${String(change.col)}H` + paintPhysicalLine(change.line, tier);
51630
51848
  }
51631
51849
  if (padColumns > 0) {
51632
- cells += `\x1B[${String(change.row)};${String(change.col + nextWidth)}H` + styled(" ".repeat(padColumns), "bg", tier);
51850
+ cells += `\x1B[${String(change.row)};${String(change.col + nextWidth)}H` + blankPad(padColumns, tier);
51633
51851
  }
51634
51852
  }
51635
51853
  return cells === "" ? "" : `\x1B7${cells}\x1B8`;
@@ -51644,7 +51862,7 @@ function publishedCaretBytes() {
51644
51862
  }
51645
51863
  function writePublishedFrameSnapshot(stdout, tier = currentTier()) {
51646
51864
  const transcript = transcriptOverlay(tier, true);
51647
- const rail = railOverlay(tier);
51865
+ const rail = railOverlay(tier, false);
51648
51866
  const overlay = transcript + rail;
51649
51867
  if (overlay === "") return;
51650
51868
  const bytes = START_SYNC + overlay + caretSuffix() + END_SYNC;
@@ -51675,7 +51893,7 @@ function transformFrameChunk(chunk, tier = currentTier(), publishFrame = true) {
51675
51893
  }
51676
51894
  const isFrame = out.includes(END_SYNC) || out.includes(SHOW_CURSOR) || out.includes(HIDE_CURSOR);
51677
51895
  if (isFrame && publishFrame) {
51678
- const overlay = transcriptOverlay(tier, out.includes(END_SYNC)) + railOverlay(tier) + caretSuffix();
51896
+ const overlay = transcriptOverlay(tier, out.includes(END_SYNC)) + railOverlay(tier, true) + caretSuffix();
51679
51897
  const syncIndex = out.lastIndexOf(END_SYNC);
51680
51898
  out = syncIndex < 0 ? out + overlay : out.slice(0, syncIndex) + overlay + out.slice(syncIndex);
51681
51899
  }
@@ -54186,7 +54404,15 @@ function latestAssistantId(history, activeTurn) {
54186
54404
  }
54187
54405
  return void 0;
54188
54406
  }
54407
+ var baseMarkdownLineCache = /* @__PURE__ */ new WeakMap();
54189
54408
  function markdownLineToPhysicalLine(blockId, line8) {
54409
+ let byBlock = baseMarkdownLineCache.get(line8);
54410
+ if (byBlock === void 0) {
54411
+ byBlock = /* @__PURE__ */ new Map();
54412
+ baseMarkdownLineCache.set(line8, byBlock);
54413
+ }
54414
+ const cached = byBlock.get(blockId);
54415
+ if (cached !== void 0) return cached;
54190
54416
  const segments = [];
54191
54417
  for (const span of line8.spans) {
54192
54418
  const text4 = displayColumnSlice(line8.text, span.start, span.end);
@@ -54200,7 +54426,7 @@ function markdownLineToPhysicalLine(blockId, line8) {
54200
54426
  }
54201
54427
  const sourceStart = Math.max(0, line8.sourceStart);
54202
54428
  const sourceEnd = Math.max(sourceStart, line8.sourceEnd < 0 ? sourceStart : line8.sourceEnd);
54203
- return createPhysicalLine({
54429
+ const created = createPhysicalLine({
54204
54430
  blockId,
54205
54431
  spans: segments.length === 0 ? [{ text: "", token: "fg", bold: false }] : segments,
54206
54432
  sourceStart,
@@ -54209,6 +54435,8 @@ function markdownLineToPhysicalLine(blockId, line8) {
54209
54435
  ...line8.background === void 0 ? {} : { background: line8.background },
54210
54436
  ...line8.backgroundColumns === void 0 ? {} : { backgroundColumns: line8.backgroundColumns }
54211
54437
  });
54438
+ byBlock.set(blockId, created);
54439
+ return created;
54212
54440
  }
54213
54441
  function overlayPromptOnSpans(spans, prompt) {
54214
54442
  const result = [];
@@ -54244,7 +54472,38 @@ function overlayPromptOnSpans(spans, prompt) {
54244
54472
  });
54245
54473
  return result;
54246
54474
  }
54475
+ var frameLineCache = /* @__PURE__ */ new WeakMap();
54247
54476
  function framePhysicalLine(blockId, line8, lead, active, prompt) {
54477
+ if (prompt === void 0) {
54478
+ let byLine = frameLineCache.get(line8);
54479
+ if (byLine === void 0) {
54480
+ byLine = /* @__PURE__ */ new Map();
54481
+ frameLineCache.set(line8, byLine);
54482
+ }
54483
+ const key = `${blockId}:${lead}:${active ? "1" : "0"}`;
54484
+ const cached = byLine.get(key);
54485
+ if (cached !== void 0) return cached;
54486
+ const base2 = markdownLineToPhysicalLine(blockId, line8);
54487
+ const spans2 = lead === "" ? base2.spans : [
54488
+ {
54489
+ text: lead,
54490
+ token: lead.trim() === "" ? "bg" : "accentText",
54491
+ bold: active && lead.trim() !== ""
54492
+ },
54493
+ ...base2.spans
54494
+ ];
54495
+ const created = lead === "" ? base2 : createPhysicalLine({
54496
+ blockId,
54497
+ spans: spans2,
54498
+ sourceStart: base2.sourceStart,
54499
+ sourceEnd: base2.sourceEnd,
54500
+ blockRow: base2.blockRow,
54501
+ ...base2.background === void 0 ? {} : { background: base2.background },
54502
+ ...base2.backgroundColumns === void 0 ? {} : { backgroundColumns: base2.backgroundColumns }
54503
+ });
54504
+ byLine.set(key, created);
54505
+ return created;
54506
+ }
54248
54507
  const base = markdownLineToPhysicalLine(blockId, line8);
54249
54508
  const initialSpans = [
54250
54509
  ...lead === "" ? [] : [{
@@ -54254,7 +54513,7 @@ function framePhysicalLine(blockId, line8, lead, active, prompt) {
54254
54513
  }],
54255
54514
  ...base.spans
54256
54515
  ];
54257
- const spans = prompt !== void 0 ? overlayPromptOnSpans(initialSpans, prompt) : initialSpans;
54516
+ const spans = overlayPromptOnSpans(initialSpans, prompt);
54258
54517
  return createPhysicalLine({
54259
54518
  blockId,
54260
54519
  spans,
@@ -54355,6 +54614,9 @@ function cachedPaintedRenderLine(line8, cacheKey, hyperlinks2) {
54355
54614
  variants.set(cacheKey, painted);
54356
54615
  return painted;
54357
54616
  }
54617
+ function areSlicedLinesBlockPropsEqual(prev, next) {
54618
+ return prev.lines === next.lines && prev.sliceStart === next.sliceStart && prev.sliceEnd === next.sliceEnd && prev.textRanges === next.textRanges && prev.tailRow === next.tailRow && prev.tail === next.tail && prev.prefix.first === next.prefix.first && prev.prefix.rest === next.prefix.rest;
54619
+ }
54358
54620
  var SlicedLinesBlock = (0, import_react37.memo)(function SlicedLinesBlock2(props) {
54359
54621
  const { lines, sliceStart, sliceEnd, prefix, textRanges, tailRow, tail } = props;
54360
54622
  const end = Math.min(lines.length, Math.max(0, sliceEnd));
@@ -54383,7 +54645,28 @@ var SlicedLinesBlock = (0, import_react37.memo)(function SlicedLinesBlock2(props
54383
54645
  return null;
54384
54646
  }
54385
54647
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { flexDirection: "column", width: "100%", children: out });
54386
- });
54648
+ }, areSlicedLinesBlockPropsEqual);
54649
+ function projectBlockEntry(deps, ownerId, entry, scope, state, active) {
54650
+ if (entry.kind === "tool-card" && entry.meta?.toolCard !== void 0) {
54651
+ return { lines: deps.toolRows.rows(entry.id, entry.meta.toolCard, scope.width, scope.fold.tools, deps.locale) };
54652
+ }
54653
+ if (entry.kind === "reasoning" && entry.meta?.reasoningExpanded === true && entry.source !== "") {
54654
+ const rows = new RowSequence();
54655
+ const header = `${entry.meta.reasoningLive === true ? "" : "\u25BE "}\u273B ${tuiCopy("reasoning", deps.locale)} (${((entry.meta.reasoningDurationMs ?? 0) / 1e3).toFixed(1)}s)`;
54656
+ rows.push({ ...GAP_LINE, text: header, displayWidth: displayWidth(header), spans: [{ start: 0, end: displayWidth(header), token: "fgDim", bold: false }] });
54657
+ rows.append(deps.plainRows.rows(entry.id, entry.source, Math.max(1, scope.width - 4)));
54658
+ return { lines: rows.build() };
54659
+ }
54660
+ const projection = projectBlockRows(entry, scope, state);
54661
+ deps.storeBlocks.set(entry.id, {
54662
+ ownerId,
54663
+ entry,
54664
+ scope,
54665
+ lines: projection.lines,
54666
+ active
54667
+ });
54668
+ return projection;
54669
+ }
54387
54670
  function StreamView({
54388
54671
  model,
54389
54672
  presenters,
@@ -54402,6 +54685,7 @@ function StreamView({
54402
54685
  const policy = renderPolicy ?? renderPolicyDefaults();
54403
54686
  const toolRows = (0, import_react37.useMemo)(() => new ToolRowCache(policy.tools), [
54404
54687
  policy.tools.previewRows,
54688
+ policy.tools.diffPreviewRows,
54405
54689
  policy.tools.cacheEntries,
54406
54690
  policy.tools.cacheRows
54407
54691
  ]);
@@ -54454,6 +54738,7 @@ function StreamView({
54454
54738
  const presentationQueue = (0, import_react37.useRef)(void 0);
54455
54739
  const frameArbiter = (0, import_react37.useRef)(void 0);
54456
54740
  const pendingScrollInputAt = (0, import_react37.useRef)(void 0);
54741
+ const lastDispatchedOffset = (0, import_react37.useRef)(viewport.offsetFromBottom);
54457
54742
  (0, import_react37.useLayoutEffect)(() => {
54458
54743
  const scroll = createScrollScheduler({
54459
54744
  frameIntervalMs: policy.scroll.frameIntervalMs,
@@ -54485,12 +54770,11 @@ function StreamView({
54485
54770
  now: () => performance.now(),
54486
54771
  demandDriven: true
54487
54772
  });
54488
- let lastDispatchedOffset = -1;
54489
54773
  const unsubscribe = arbiter.onPublish((snapshot) => {
54490
54774
  const presented = snapshot.stream.at(-1);
54491
54775
  if (presented !== void 0) setPresentedEntryRows(presented);
54492
- if (snapshot.scroll.presented !== lastDispatchedOffset) {
54493
- lastDispatchedOffset = snapshot.scroll.presented;
54776
+ if (snapshot.scroll.presented !== lastDispatchedOffset.current) {
54777
+ lastDispatchedOffset.current = snapshot.scroll.presented;
54494
54778
  dispatchViewport({
54495
54779
  kind: "offset",
54496
54780
  offsetFromBottom: snapshot.scroll.presented
@@ -54607,7 +54891,8 @@ function StreamView({
54607
54891
  () => assistantProseScope(settledBlockRowsScope),
54608
54892
  [settledBlockRowsScope]
54609
54893
  );
54610
- const entryRows = (0, import_react37.useMemo)(() => {
54894
+ const hasActiveTurn = activeTurn !== void 0;
54895
+ const settledEntryRows = (0, import_react37.useMemo)(() => {
54611
54896
  const definitions = /* @__PURE__ */ new Map();
54612
54897
  const batchPresenters = presenters === void 0 ? void 0 : { get(name2) {
54613
54898
  if (!definitions.has(name2)) definitions.set(name2, presenters.get(name2));
@@ -54617,27 +54902,8 @@ function StreamView({
54617
54902
  const textRanges = /* @__PURE__ */ new Map();
54618
54903
  const storeBlocks = /* @__PURE__ */ new Map();
54619
54904
  const projectorCache = projectorStates.current;
54620
- const project = (ownerId, entry, scope, state, active) => {
54621
- if (entry.kind === "tool-card" && entry.meta?.toolCard !== void 0) {
54622
- return { lines: toolRows.rows(entry.id, entry.meta.toolCard, scope.width, scope.fold.tools, locale) };
54623
- }
54624
- if (entry.kind === "reasoning" && entry.meta?.reasoningExpanded === true && entry.source !== "") {
54625
- const rows2 = new RowSequence();
54626
- const header = `${entry.meta.reasoningLive === true ? "" : "\u25BE "}\u273B ${tuiCopy("reasoning", locale)} (${((entry.meta.reasoningDurationMs ?? 0) / 1e3).toFixed(1)}s)`;
54627
- rows2.push({ ...GAP_LINE, text: header, displayWidth: displayWidth(header), spans: [{ start: 0, end: displayWidth(header), token: "fgDim", bold: false }] });
54628
- rows2.append(plainRows.rows(entry.id, entry.source, Math.max(1, scope.width - 4)));
54629
- return { lines: rows2.build() };
54630
- }
54631
- const projection = projectBlockRows(entry, scope, state);
54632
- storeBlocks.set(entry.id, {
54633
- ownerId,
54634
- entry,
54635
- scope,
54636
- lines: projection.lines,
54637
- active
54638
- });
54639
- return projection;
54640
- };
54905
+ const project = (ownerId, entry, scope, state, active) => projectBlockEntry({ toolRows, plainRows, locale, storeBlocks }, ownerId, entry, scope, state, active);
54906
+ const latestAssistant = hasActiveTurn ? void 0 : latestAssistantId(history, void 0);
54641
54907
  for (const [index2, row] of transcript.entries()) {
54642
54908
  const id = transcriptBlockId(row);
54643
54909
  if (row.kind === "compaction") {
@@ -54656,7 +54922,7 @@ function StreamView({
54656
54922
  continue;
54657
54923
  }
54658
54924
  if (row.message.kind === "user") {
54659
- const hasSubsequent = index2 < transcript.length - 1 || activeTurn !== void 0;
54925
+ const hasSubsequent = index2 < transcript.length - 1 || hasActiveTurn;
54660
54926
  const projection = project(id, {
54661
54927
  id,
54662
54928
  kind: "user",
@@ -54734,7 +55000,7 @@ function StreamView({
54734
55000
  ranges.push({ start, end: rows2.length });
54735
55001
  }
54736
55002
  const tailMeta = {
54737
- ...latestAssistantId(history, activeTurn) === row.message.id ? { turnTailCompletionBoundary: true } : {},
55003
+ ...latestAssistant === row.message.id ? { turnTailCompletionBoundary: true } : {},
54738
55004
  ...(() => {
54739
55005
  const stats = formatTurnTailStats({
54740
55006
  turnOrdinal: row.message.turnOrdinal,
@@ -54764,123 +55030,154 @@ function StreamView({
54764
55030
  map.set(id, rows2.build());
54765
55031
  if (ranges.length > 0) textRanges.set(id, ranges);
54766
55032
  }
54767
- if (activeTurn !== void 0) {
54768
- const id = `assistant-turn-${String(activeTurn.turn)}`;
54769
- const rawParts = partsFromTurn(activeTurn);
54770
- const visibleParts = displayedParts(rawParts, reasoningExpanded);
54771
- if (status === "generating" && visibleParts.length === 0) {
54772
- const liveMs = liveDurationMs ?? activeTurn.reasoningDurationMs;
54773
- const fish = getSwimmingFishFrame(liveMs);
54774
- map.set(id, project(id, {
54775
- id,
54776
- kind: "active-placeholder",
54777
- source: "",
55033
+ return { rows: map, textRanges, storeBlocks };
55034
+ }, [
55035
+ contentWidth,
55036
+ expandedCompactionId,
55037
+ hasActiveTurn,
55038
+ history,
55039
+ locale,
55040
+ mode,
55041
+ plainRows,
55042
+ presenterCache,
55043
+ presenters,
55044
+ reasoningExpanded,
55045
+ settledAssistantBlockRowsScope,
55046
+ settledBlockRowsScope,
55047
+ toolCardsExpanded,
55048
+ toolRows,
55049
+ transcript
55050
+ ]);
55051
+ const activeTurnRows = (0, import_react37.useMemo)(() => {
55052
+ if (activeTurn === void 0) return void 0;
55053
+ const definitions = /* @__PURE__ */ new Map();
55054
+ const batchPresenters = presenters === void 0 ? void 0 : { get(name2) {
55055
+ if (!definitions.has(name2)) definitions.set(name2, presenters.get(name2));
55056
+ return definitions.get(name2);
55057
+ } };
55058
+ const storeBlocks = /* @__PURE__ */ new Map();
55059
+ const projectorCache = projectorStates.current;
55060
+ const project = (ownerId, entry, scope, state, active) => projectBlockEntry({ toolRows, plainRows, locale, storeBlocks }, ownerId, entry, scope, state, active);
55061
+ const id = `assistant-turn-${String(activeTurn.turn)}`;
55062
+ const rawParts = partsFromTurn(activeTurn);
55063
+ const visibleParts = displayedParts(rawParts, reasoningExpanded);
55064
+ if (status === "generating" && visibleParts.length === 0) {
55065
+ const liveMs = liveDurationMs ?? activeTurn.reasoningDurationMs;
55066
+ const fish = getSwimmingFishFrame(liveMs);
55067
+ const lines = project(id, {
55068
+ id,
55069
+ kind: "active-placeholder",
55070
+ source: "",
55071
+ meta: {
55072
+ activePlaceholder: `${fish} \u25CF \u6B63\u5728\u5904\u7406\u2026 (${formatSeconds(
55073
+ liveMs
55074
+ )}s)`
55075
+ }
55076
+ }, blockRowsScope, void 0, true).lines;
55077
+ return { id, lines, ranges: [], storeBlocks };
55078
+ }
55079
+ const parts = displayedParts(
55080
+ compactToolParts(rawParts, toolCardsExpanded, batchPresenters, mode, presenterCache),
55081
+ reasoningExpanded
55082
+ );
55083
+ const rows2 = new RowSequence();
55084
+ const ranges = [];
55085
+ let textIndex = 0;
55086
+ let lastVisiblePart = -1;
55087
+ for (const [partIndex, part] of parts.entries()) {
55088
+ if (isVisiblePart(part)) lastVisiblePart = partIndex;
55089
+ }
55090
+ for (const [partIndex, part] of parts.entries()) {
55091
+ if (turnPartGap(parts, partIndex) > 0) rows2.push(GAP_LINE);
55092
+ if (part.kind === "reasoning") {
55093
+ const live = status === "generating" && partIndex === lastVisiblePart;
55094
+ rows2.append(project(id, {
55095
+ id: `${id}-r-${String(partIndex)}`,
55096
+ kind: "reasoning",
55097
+ source: part.text,
54778
55098
  meta: {
54779
- activePlaceholder: `${fish} \u25CF \u6B63\u5728\u5904\u7406\u2026 (${formatSeconds(
54780
- liveMs
54781
- )}s)`
54782
- }
54783
- }, blockRowsScope, void 0, true).lines);
54784
- } else {
54785
- const parts = displayedParts(
54786
- compactToolParts(rawParts, toolCardsExpanded, batchPresenters, mode, presenterCache),
54787
- reasoningExpanded
54788
- );
54789
- const rows2 = new RowSequence();
54790
- const ranges = [];
54791
- let textIndex = 0;
54792
- let lastVisiblePart = -1;
54793
- for (const [partIndex, part] of parts.entries()) {
54794
- if (isVisiblePart(part)) lastVisiblePart = partIndex;
54795
- }
54796
- for (const [partIndex, part] of parts.entries()) {
54797
- if (turnPartGap(parts, partIndex) > 0) rows2.push(GAP_LINE);
54798
- if (part.kind === "reasoning") {
54799
- const live = status === "generating" && partIndex === lastVisiblePart;
54800
- rows2.append(project(id, {
54801
- id: `${id}-r-${String(partIndex)}`,
54802
- kind: "reasoning",
54803
- source: part.text,
54804
- meta: {
54805
- reasoningDurationMs: live ? liveDurationMs ?? part.durationMs : part.durationMs,
54806
- reasoningExpanded,
54807
- reasoningLive: live
54808
- }
54809
- }, blockRowsScope, void 0, status === "generating").lines);
54810
- continue;
54811
- }
54812
- if (part.kind === "tool-summary") {
54813
- rows2.append(project(id, {
54814
- id: `${id}-tool-summary`,
54815
- kind: "tool-summary",
54816
- source: toolSummaryText(part.summary, contentWidth),
54817
- meta: { toolSummaryStatus: toolSummaryStatus(part.summary) }
54818
- }, blockRowsScope, void 0, status === "generating").lines);
54819
- continue;
55099
+ reasoningDurationMs: live ? liveDurationMs ?? part.durationMs : part.durationMs,
55100
+ reasoningExpanded,
55101
+ reasoningLive: live
54820
55102
  }
54821
- if (part.kind === "card") {
54822
- const card = part.card;
54823
- rows2.append(project(id, {
54824
- id: `${id}-c-${card.callId}`,
54825
- kind: "tool-card",
54826
- source: "",
54827
- meta: {
54828
- toolCard: {
54829
- name: card.name,
54830
- arguments: card.arguments,
54831
- status: card.status,
54832
- ...card.resultText === void 0 ? {} : { resultText: card.resultText },
54833
- ...card.meta === void 0 ? {} : { meta: card.meta },
54834
- ...card.error === void 0 ? {} : { error: card.error },
54835
- ...card.callView === void 0 ? {} : { callView: card.callView },
54836
- ...card.resultView === void 0 ? {} : { resultView: card.resultView }
54837
- }
54838
- }
54839
- }, blockRowsScope, void 0, status === "generating").lines);
54840
- continue;
55103
+ }, blockRowsScope, void 0, status === "generating").lines);
55104
+ continue;
55105
+ }
55106
+ if (part.kind === "tool-summary") {
55107
+ rows2.append(project(id, {
55108
+ id: `${id}-tool-summary`,
55109
+ kind: "tool-summary",
55110
+ source: toolSummaryText(part.summary, contentWidth),
55111
+ meta: { toolSummaryStatus: toolSummaryStatus(part.summary) }
55112
+ }, blockRowsScope, void 0, status === "generating").lines);
55113
+ continue;
55114
+ }
55115
+ if (part.kind === "card") {
55116
+ const card = part.card;
55117
+ rows2.append(project(id, {
55118
+ id: `${id}-c-${card.callId}`,
55119
+ kind: "tool-card",
55120
+ source: "",
55121
+ meta: {
55122
+ toolCard: {
55123
+ name: card.name,
55124
+ arguments: card.arguments,
55125
+ status: card.status,
55126
+ ...card.resultText === void 0 ? {} : { resultText: card.resultText },
55127
+ ...card.meta === void 0 ? {} : { meta: card.meta },
55128
+ ...card.error === void 0 ? {} : { error: card.error },
55129
+ ...card.callView === void 0 ? {} : { callView: card.callView },
55130
+ ...card.resultView === void 0 ? {} : { resultView: card.resultView }
55131
+ }
54841
55132
  }
54842
- const start = rows2.length;
54843
- const partId = `${id}-t-${String(textIndex++)}`;
54844
- rows2.append(project(id, {
54845
- id: partId,
54846
- kind: "assistant-prose",
54847
- source: part.text
54848
- }, assistantBlockRowsScope, projectorStateFor(
54849
- projectorStates.current,
54850
- partId,
54851
- assistantBlockRowsScope
54852
- ), status === "generating").lines);
54853
- ranges.push({ start, end: rows2.length });
54854
- }
54855
- map.set(id, rows2.build());
54856
- if (ranges.length > 0) {
54857
- textRanges.set(id, ranges);
54858
- }
55133
+ }, blockRowsScope, void 0, status === "generating").lines);
55134
+ continue;
54859
55135
  }
54860
- }
54861
- return { rows: map, textRanges, storeBlocks };
55136
+ const start = rows2.length;
55137
+ const partId = `${id}-t-${String(textIndex++)}`;
55138
+ rows2.append(project(id, {
55139
+ id: partId,
55140
+ kind: "assistant-prose",
55141
+ source: part.text
55142
+ }, assistantBlockRowsScope, projectorStateFor(
55143
+ projectorCache,
55144
+ partId,
55145
+ assistantBlockRowsScope
55146
+ ), status === "generating").lines);
55147
+ ranges.push({ start, end: rows2.length });
55148
+ }
55149
+ return { id, lines: rows2.build(), ranges, storeBlocks };
54862
55150
  }, [
54863
- activeVersion,
54864
55151
  activeTurn,
55152
+ activeVersion,
54865
55153
  assistantBlockRowsScope,
54866
55154
  blockRowsScope,
54867
55155
  contentWidth,
54868
- expandedCompactionId,
54869
- history,
54870
55156
  liveDurationMs,
55157
+ locale,
55158
+ mode,
55159
+ plainRows,
55160
+ presenterCache,
54871
55161
  presenters,
54872
55162
  reasoningExpanded,
54873
55163
  status,
54874
- settledAssistantBlockRowsScope,
54875
- settledBlockRowsScope,
54876
55164
  toolCardsExpanded,
54877
- toolRows,
54878
- plainRows,
54879
- presenterCache,
54880
- locale,
54881
- transcript,
54882
- mode
55165
+ toolRows
54883
55166
  ]);
55167
+ const entryRows = (0, import_react37.useMemo)(() => {
55168
+ if (activeTurnRows === void 0) return settledEntryRows;
55169
+ const map = new Map(settledEntryRows.rows);
55170
+ map.set(activeTurnRows.id, activeTurnRows.lines);
55171
+ const textRanges = new Map(settledEntryRows.textRanges);
55172
+ if (activeTurnRows.ranges.length > 0) {
55173
+ textRanges.set(activeTurnRows.id, activeTurnRows.ranges);
55174
+ }
55175
+ const storeBlocks = new Map(settledEntryRows.storeBlocks);
55176
+ for (const [key, val] of activeTurnRows.storeBlocks) {
55177
+ storeBlocks.set(key, val);
55178
+ }
55179
+ return { rows: map, textRanges, storeBlocks };
55180
+ }, [settledEntryRows, activeTurnRows]);
54884
55181
  const [presentedEntryRows, setPresentedEntryRows] = (0, import_react37.useState)(entryRows);
54885
55182
  const activeEntryRows = status === "generating" ? presentedEntryRows : entryRows;
54886
55183
  (0, import_react37.useLayoutEffect)(() => {
@@ -55050,26 +55347,24 @@ function StreamView({
55050
55347
  ));
55051
55348
  lastProjectorMetrics.current = totals;
55052
55349
  }, [entryRows, frameMetrics]);
55350
+ const settledRenderEntries = (0, import_react37.useMemo)(() => transcript.flatMap((row, index2) => {
55351
+ const hasSubsequent = index2 < transcript.length - 1 || hasActiveTurn;
55352
+ const isUserWithGap = row.kind === "message" && row.message.kind === "user" && hasSubsequent;
55353
+ const gapRows = isUserWithGap ? 0 : hasSubsequent ? 2 : 0;
55354
+ const id = transcriptBlockId(row);
55355
+ const lines = settledEntryRows.rows.get(id) ?? [];
55356
+ return [{
55357
+ kind: "row",
55358
+ id,
55359
+ version: `${transcriptBlockVersion(row, revisions)}\0${hasSubsequent ? "gap" : "tail"}`,
55360
+ gapRows,
55361
+ estimatedRows: lines.length + gapRows,
55362
+ lines,
55363
+ row
55364
+ }];
55365
+ }), [hasActiveTurn, revisions, settledEntryRows, transcript]);
55053
55366
  const renderEntries = (0, import_react37.useMemo)(() => [
55054
- ...transcript.flatMap((row, index2) => {
55055
- const hasSubsequent = index2 < transcript.length - 1 || activeTurn !== void 0;
55056
- const isUserWithGap = row.kind === "message" && row.message.kind === "user" && hasSubsequent;
55057
- const gapRows = isUserWithGap ? 0 : hasSubsequent ? 2 : 0;
55058
- const id = transcriptBlockId(row);
55059
- if (activeTurn !== void 0 && id === `assistant-turn-${String(activeTurn.turn)}`) {
55060
- return [];
55061
- }
55062
- const lines = activeEntryRows.rows.get(id) ?? [];
55063
- return [{
55064
- kind: "row",
55065
- id,
55066
- version: `${transcriptBlockVersion(row, revisions)}\0${hasSubsequent ? "gap" : "tail"}`,
55067
- gapRows,
55068
- estimatedRows: lines.length + gapRows,
55069
- lines,
55070
- row
55071
- }];
55072
- }),
55367
+ ...settledRenderEntries.filter((entry) => activeTurn === void 0 || entry.id !== `assistant-turn-${String(activeTurn.turn)}`),
55073
55368
  ...activeTurn === void 0 ? [] : [{
55074
55369
  kind: "active",
55075
55370
  id: `assistant-turn-${String(activeTurn.turn)}`,
@@ -55083,7 +55378,7 @@ function StreamView({
55083
55378
  activeTurn,
55084
55379
  activeVersion,
55085
55380
  activeEntryRows,
55086
- transcript
55381
+ settledRenderEntries
55087
55382
  ]);
55088
55383
  const layoutScope = [
55089
55384
  contentWidth,
@@ -55181,16 +55476,22 @@ function StreamView({
55181
55476
  const idleHome = transcript.length === 0 && activeTurn === void 0;
55182
55477
  const generating = status === "generating";
55183
55478
  const latestSettledAssistantId = activeTurn === void 0 ? history.findLast((message) => message.kind === "assistant")?.id : void 0;
55184
- (0, import_react37.useLayoutEffect)(() => {
55185
- if (viewportCommand === void 0) return;
55186
- if (viewportCommand.sequence === lastCommandSequence.current) return;
55187
- if (viewportCommand.kind !== "reset" && (viewport.viewportRows === 0 || viewport.contentRows === 0)) return;
55188
- lastCommandSequence.current = viewportCommand.sequence;
55479
+ const viewportStateRef = (0, import_react37.useRef)(viewport);
55480
+ viewportStateRef.current = viewport;
55481
+ const virtualContentRowsRef = (0, import_react37.useRef)(virtualContentRows);
55482
+ virtualContentRowsRef.current = virtualContentRows;
55483
+ const effectiveViewportRowsRef = (0, import_react37.useRef)(effectiveViewportRows);
55484
+ effectiveViewportRowsRef.current = effectiveViewportRows;
55485
+ const executeViewportCommand = (0, import_react37.useCallback)((command) => {
55486
+ if (command.sequence === lastCommandSequence.current) return;
55487
+ const vp = viewportStateRef.current;
55488
+ if (command.kind !== "reset" && (vp.viewportRows === 0 || vp.contentRows === 0)) return;
55489
+ lastCommandSequence.current = command.sequence;
55189
55490
  const scheduler = scrollScheduler.current;
55190
55491
  const arbiter = frameArbiter.current;
55191
55492
  if (scheduler === void 0 || arbiter === void 0) return;
55192
- const effectiveContentRows = Math.max(viewport.contentRows, virtualContentRows);
55193
- const maximum = Math.max(0, effectiveContentRows - effectiveViewportRows);
55493
+ const effectiveContentRows = Math.max(vp.contentRows, virtualContentRowsRef.current);
55494
+ const maximum = Math.max(0, effectiveContentRows - effectiveViewportRowsRef.current);
55194
55495
  const clamp = (value) => Math.max(0, Math.min(maximum, value));
55195
55496
  const advance = (delta) => {
55196
55497
  const presented = scheduler.getPresented();
@@ -55201,43 +55502,53 @@ function StreamView({
55201
55502
  };
55202
55503
  if (pendingScrollInputAt.current !== void 0) frameMetrics?.recordCoalescedInput();
55203
55504
  pendingScrollInputAt.current ??= performance.now();
55204
- switch (viewportCommand.kind) {
55505
+ switch (command.kind) {
55205
55506
  case "scroll":
55206
- advance(viewportCommand.delta);
55507
+ advance(command.delta);
55207
55508
  break;
55208
55509
  case "page":
55209
- advance(viewportCommand.delta * Math.max(1, viewport.viewportRows - 1));
55510
+ advance(command.delta * Math.max(1, vp.viewportRows - 1));
55210
55511
  break;
55211
55512
  case "position":
55212
55513
  scheduler.snapTo(clamp(Math.round(
55213
- maximum * (1 - Math.max(0, Math.min(1, viewportCommand.fraction)))
55514
+ maximum * (1 - Math.max(0, Math.min(1, command.fraction)))
55214
55515
  )));
55215
55516
  break;
55216
55517
  case "edge":
55217
- scheduler.snapTo(viewportCommand.edge === "latest" ? 0 : maximum);
55518
+ scheduler.snapTo(command.edge === "latest" ? 0 : maximum);
55218
55519
  break;
55219
55520
  case "reset":
55220
55521
  scheduler.snapTo(0);
55522
+ lastDispatchedOffset.current = 0;
55221
55523
  pendingScrollInputAt.current = void 0;
55222
55524
  dispatchViewport({ kind: "reset" });
55223
55525
  return;
55224
55526
  }
55527
+ const presentedOffset = scheduler.getPresented();
55528
+ lastDispatchedOffset.current = presentedOffset;
55225
55529
  dispatchViewport({
55226
55530
  kind: "offset",
55227
- offsetFromBottom: scheduler.getPresented()
55531
+ offsetFromBottom: presentedOffset
55228
55532
  });
55229
55533
  arbiter.requestScroll();
55534
+ }, [frameMetrics]);
55535
+ (0, import_react37.useLayoutEffect)(() => {
55536
+ if (viewportCommand === void 0) return;
55537
+ if (viewportCommand.sequence === lastCommandSequence.current) return;
55538
+ executeViewportCommand(viewportCommand);
55230
55539
  }, [
55540
+ viewportCommand,
55541
+ executeViewportCommand,
55231
55542
  viewport.contentRows,
55232
55543
  viewport.viewportRows,
55233
55544
  effectiveViewportRows,
55234
- virtualContentRows,
55235
- viewportCommand
55545
+ virtualContentRows
55236
55546
  ]);
55237
55547
  (0, import_react37.useLayoutEffect)(() => {
55238
55548
  const scheduler = scrollScheduler.current;
55239
55549
  if (scheduler === void 0) return;
55240
- const maximum = Math.max(0, viewport.contentRows - viewport.viewportRows);
55550
+ const effectiveContentRows = Math.max(viewport.contentRows, virtualContentRows);
55551
+ const maximum = Math.max(0, effectiveContentRows - effectiveViewportRows);
55241
55552
  const presented = scheduler.getPresented();
55242
55553
  if (presented === viewport.offsetFromBottom) return;
55243
55554
  if (scheduler.isAnimating()) {
@@ -55245,19 +55556,23 @@ function StreamView({
55245
55556
  } else {
55246
55557
  scheduler.snapTo(Math.max(0, Math.min(maximum, viewport.offsetFromBottom)));
55247
55558
  }
55559
+ lastDispatchedOffset.current = viewport.offsetFromBottom;
55248
55560
  }, [
55561
+ effectiveViewportRows,
55249
55562
  viewport.contentRows,
55250
55563
  viewport.offsetFromBottom,
55251
- viewport.viewportRows
55564
+ virtualContentRows
55252
55565
  ]);
55253
55566
  (0, import_react37.useLayoutEffect)(() => {
55254
55567
  scrollScheduler.current?.snapTo(viewport.offsetFromBottom);
55568
+ lastDispatchedOffset.current = viewport.offsetFromBottom;
55255
55569
  }, [columns, rows]);
55256
55570
  (0, import_react37.useLayoutEffect)(() => {
55257
55571
  if (!motionPaused) return;
55258
55572
  const scheduler = scrollScheduler.current;
55259
55573
  if (scheduler === void 0) return;
55260
55574
  scheduler.snapTo(viewport.offsetFromBottom);
55575
+ lastDispatchedOffset.current = viewport.offsetFromBottom;
55261
55576
  }, [motionPaused, viewport.offsetFromBottom]);
55262
55577
  (0, import_react37.useLayoutEffect)(() => {
55263
55578
  const viewportElement = viewportRef.current;
@@ -55268,50 +55583,55 @@ function StreamView({
55268
55583
  const contentRows = layouts.at(-1) === void 0 ? 0 : layouts.at(-1).top + layouts.at(-1).rows;
55269
55584
  const contentChanged = contentRevision !== lastContentRevision.current;
55270
55585
  lastContentRevision.current = contentRevision;
55271
- dispatchViewport({
55272
- kind: "layout",
55273
- contentRows,
55274
- viewportRows: viewportBox.height,
55275
- blocks: layouts,
55276
- unseenRowsAdded: contentChanged ? Math.max(0, contentRows - viewport.contentRows) : 0
55277
- });
55278
- if (frameMetrics !== void 0) {
55279
- let mounted = 0;
55280
- for (const layoutIndex of visibleIndexes) {
55281
- const entry = renderEntries[layoutIndex];
55282
- if (entry === void 0) continue;
55283
- const layout = virtualLayouts[layoutIndex];
55284
- if (layout === void 0) {
55285
- continue;
55286
- }
55287
- let window2;
55288
- if (physicalViewport) {
55289
- window2 = sliceWindow(
55290
- entry.lines.length,
55291
- layout.top,
55292
- overscanTop,
55293
- overscanBottom
55294
- );
55295
- }
55296
- mounted += window2 === void 0 ? entry.lines.length : Math.max(0, window2.end - window2.start);
55297
- }
55298
- frameMetrics.addMountedRows(mounted);
55586
+ if (contentChanged || viewport.viewportRows !== viewportBox.height || viewport.contentRows !== contentRows) {
55587
+ dispatchViewport({
55588
+ kind: "layout",
55589
+ contentRows,
55590
+ viewportRows: viewportBox.height,
55591
+ blocks: layouts,
55592
+ unseenRowsAdded: contentChanged ? Math.max(0, contentRows - viewport.contentRows) : 0
55593
+ });
55299
55594
  }
55300
55595
  }, [
55301
55596
  columns,
55302
- rows,
55303
- layoutKey,
55304
- effectiveViewportRows,
55305
- frameMetrics,
55597
+ contentRevision,
55306
55598
  layoutInputs,
55599
+ layoutKey,
55307
55600
  layoutScope,
55601
+ rows,
55602
+ viewport.contentRows,
55603
+ viewport.viewportRows
55604
+ ]);
55605
+ (0, import_react37.useLayoutEffect)(() => {
55606
+ if (frameMetrics === void 0) return;
55607
+ let mounted = 0;
55608
+ for (const layoutIndex of visibleIndexes) {
55609
+ const entry = renderEntries[layoutIndex];
55610
+ if (entry === void 0) continue;
55611
+ const layout = virtualLayouts[layoutIndex];
55612
+ if (layout === void 0) {
55613
+ continue;
55614
+ }
55615
+ let window2;
55616
+ if (physicalViewport) {
55617
+ window2 = sliceWindow(
55618
+ entry.lines.length,
55619
+ layout.top,
55620
+ overscanTop,
55621
+ overscanBottom
55622
+ );
55623
+ }
55624
+ mounted += window2 === void 0 ? entry.lines.length : Math.max(0, window2.end - window2.start);
55625
+ }
55626
+ frameMetrics.addMountedRows(mounted);
55627
+ }, [
55628
+ frameMetrics,
55308
55629
  overscanBottom,
55309
- overscanRows,
55310
55630
  overscanTop,
55311
55631
  physicalViewport,
55312
55632
  renderEntries,
55313
55633
  virtualLayouts,
55314
- visibleEntries
55634
+ visibleIndexes
55315
55635
  ]);
55316
55636
  const rail = scrollbar ? physicalScrollRailGeometry(
55317
55637
  viewport.contentRows,
@@ -60317,6 +60637,7 @@ var Config = z.object({
60317
60637
  }),
60318
60638
  tools: z.object({
60319
60639
  previewRows: z.number().min(1).step(1).max(50).default(toolPolicyDefaults().previewRows),
60640
+ diffPreviewRows: z.number().min(1).step(1).max(2e3).default(toolPolicyDefaults().diffPreviewRows),
60320
60641
  detailPageRows: z.number().min(1).step(1).max(200).default(toolPolicyDefaults().detailPageRows),
60321
60642
  cacheEntries: z.number().min(1).step(1).max(4096).default(toolPolicyDefaults().cacheEntries),
60322
60643
  cacheRows: z.number().min(1).step(1).max(RENDER_POLICY_MAX_CACHE_ROWS).default(toolPolicyDefaults().cacheRows)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crazyhappyone/dsh-tui",
3
- "version": "0.1.0-alpha.17",
3
+ "version": "0.1.0-alpha.18",
4
4
  "description": "Source-runtime launcher for the DeepSeek Harness terminal interface",
5
5
  "type": "module",
6
6
  "license": "MIT",