@deepseek-harness-tui/dsh-tui 0.10.0-beta.3 → 0.10.0-beta.4

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 (80) hide show
  1. package/README.md +1 -1
  2. package/lib/types/commands.d.ts.map +1 -1
  3. package/lib/types/commands.js +4 -1
  4. package/lib/types/components/MessageList.d.ts.map +1 -1
  5. package/lib/types/components/MessageList.js +53 -28
  6. package/lib/types/components/PromptInput.d.ts +19 -1
  7. package/lib/types/components/PromptInput.d.ts.map +1 -1
  8. package/lib/types/components/PromptInput.js +34 -11
  9. package/lib/types/components/approvals/ApprovalPanel.d.ts +4 -1
  10. package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
  11. package/lib/types/components/approvals/ApprovalPanel.js +2 -2
  12. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  13. package/lib/types/components/messages/AssistantToolUseMessage.js +6 -4
  14. package/lib/types/components/questions/AskUserQuestionPanel.d.ts +4 -0
  15. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  16. package/lib/types/components/questions/AskUserQuestionPanel.js +6 -1
  17. package/lib/types/components/smoothReveal.d.ts +0 -40
  18. package/lib/types/components/smoothReveal.d.ts.map +1 -1
  19. package/lib/types/components/smoothReveal.js +28 -2
  20. package/lib/types/dsh-adapter/agent-view.d.ts +66 -0
  21. package/lib/types/dsh-adapter/agent-view.d.ts.map +1 -0
  22. package/lib/types/dsh-adapter/agent-view.js +153 -0
  23. package/lib/types/dsh-adapter/approvals.d.ts +24 -1
  24. package/lib/types/dsh-adapter/approvals.d.ts.map +1 -1
  25. package/lib/types/dsh-adapter/approvals.js +37 -1
  26. package/lib/types/dsh-adapter/channel.d.ts +128 -0
  27. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  28. package/lib/types/dsh-adapter/channel.js +988 -28
  29. package/lib/types/dsh-adapter/inject-channel.d.ts +108 -0
  30. package/lib/types/dsh-adapter/inject-channel.d.ts.map +1 -0
  31. package/lib/types/dsh-adapter/inject-channel.js +247 -0
  32. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  33. package/lib/types/dsh-adapter/plugin.js +26 -6
  34. package/lib/types/dsh-adapter/providerWizard.d.ts +86 -6
  35. package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
  36. package/lib/types/dsh-adapter/providerWizard.js +684 -186
  37. package/lib/types/history.d.ts +3 -1
  38. package/lib/types/history.d.ts.map +1 -1
  39. package/lib/types/history.js +80 -44
  40. package/lib/types/hooks/useExternalVersion.d.ts +32 -0
  41. package/lib/types/hooks/useExternalVersion.d.ts.map +1 -0
  42. package/lib/types/hooks/useExternalVersion.js +53 -0
  43. package/lib/types/hooks/useRevealVersion.d.ts +21 -0
  44. package/lib/types/hooks/useRevealVersion.d.ts.map +1 -0
  45. package/lib/types/hooks/useRevealVersion.js +33 -0
  46. package/lib/types/i18n.d.ts +410 -1
  47. package/lib/types/i18n.d.ts.map +1 -1
  48. package/lib/types/i18n.js +117 -1
  49. package/lib/types/ink/components/ClockContext.d.ts +6 -0
  50. package/lib/types/ink/components/ClockContext.d.ts.map +1 -1
  51. package/lib/types/ink/components/ClockContext.js +24 -3
  52. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
  53. package/lib/types/ink/components/ScrollBox.js +12 -2
  54. package/lib/types/ink/flush-tick.d.ts +26 -0
  55. package/lib/types/ink/flush-tick.d.ts.map +1 -0
  56. package/lib/types/ink/flush-tick.js +31 -0
  57. package/lib/types/ink/ink.d.ts.map +1 -1
  58. package/lib/types/ink/ink.js +67 -8
  59. package/lib/types/ink/output.js +5 -2
  60. package/lib/types/ink/render-node-to-output.d.ts +46 -0
  61. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  62. package/lib/types/ink/render-node-to-output.js +62 -1
  63. package/lib/types/ink/selection.d.ts +65 -2
  64. package/lib/types/ink/selection.d.ts.map +1 -1
  65. package/lib/types/ink/selection.js +172 -3
  66. package/lib/types/ink/update-overflow-guard.d.ts +63 -0
  67. package/lib/types/ink/update-overflow-guard.d.ts.map +1 -0
  68. package/lib/types/ink/update-overflow-guard.js +165 -0
  69. package/lib/types/screens/AgentView.d.ts +23 -0
  70. package/lib/types/screens/AgentView.d.ts.map +1 -0
  71. package/lib/types/screens/AgentView.js +546 -0
  72. package/lib/types/screens/Chat.d.ts +10 -1
  73. package/lib/types/screens/Chat.d.ts.map +1 -1
  74. package/lib/types/screens/Chat.js +220 -19
  75. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  76. package/lib/types/screens/StatusLine.js +4 -1
  77. package/lib/types/sessionHistory.d.ts +18 -0
  78. package/lib/types/sessionHistory.d.ts.map +1 -1
  79. package/lib/types/sessionHistory.js +65 -0
  80. package/package.json +8 -6
package/README.md CHANGED
@@ -54,7 +54,7 @@
54
54
 
55
55
  - **终端交互**:低资源占用,长会话稳定可靠;多种主题切换,样式美观,实时显示工作状态、TPS、缓存命中率等
56
56
  推理等级、输入/输出 token 与 Git/会话信息;终端卡多行命令可经 `/settings` 折叠为首行 + 计数提示(Ctrl+O 或点击卡片展开);全屏模式下悬停在截断的工具卡标题、用户消息或会话标题上约 600ms,浮层显示完整内容。
57
- - **功能全面**:`/resume` 按工作目录分类浏览、搜索与预览历史会话(左键恢复、右键弹出操作菜单;可固定常用会话——「已固定」分组置顶显示,行内 ★ 或 `Ctrl+P` 切换,持久化到 `~/.dsh-tui`),另有 `/new`、`/compact`、`/export`、`/btw`,模型热切换,原生subagent,会话fork,自动更新,输入框 `/vim` vim 编辑模式、鼠标选区编辑(拖选高亮、Shift+click 扩展、双击选词、Ctrl+C 复制选区)与全屏草稿编辑(`Ctrl+Shift+E` 或输入行 `⛶` 按钮:行号 + 当前行高亮、Enter 换行、Ctrl+Enter 发送、滚轮滚动、点击/拖选,长草稿独占整屏;`/settings` 可关);可在vs code中[以vscode插件形式启动](docs/vscode.md),已上架 VS Code Marketplace。
57
+ - **功能全面**:`/resume` 按工作目录分类浏览、搜索与预览历史会话(左键恢复、右键弹出操作菜单;可固定常用会话——「已固定」分组置顶显示,行内 ★ 或 `Ctrl+P` 切换,持久化到 `~/.dsh-tui`),另有 `/agentview` 会话总览(CC 同款 agent view:空输入 `←` 一键后台化,后台会话派发/预览/回复/停止一站式管理)、`/new`、`/compact`、`/export`、`/btw`,模型热切换,原生subagent,会话fork,自动更新,输入框 `/vim` vim 编辑模式、鼠标选区编辑(拖选高亮、Shift+click 扩展、双击选词、Ctrl+C 复制选区)与全屏草稿编辑(`Ctrl+Shift+E` 或输入行 `⛶` 按钮:行号 + 当前行高亮、Enter 换行、Ctrl+Enter 发送、滚轮滚动、点击/拖选,长草稿独占整屏;`/settings` 可关);可在vs code中[以vscode插件形式启动](docs/vscode.md),已上架 VS Code Marketplace。
58
58
  - **扩展丰富**:原生浏览器交互,compter use等大量附属功能性扩展
59
59
  - **技能归 DSH 管理**:`/skills` 展示当前 profile、用户与项目发现的技能;dsh-TUI 不预装通用技能。
60
60
 
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;AAElF,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS,qBAAqB,EAAE,CAAA;AAEpG;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,YAAY,EA4DxC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAElD,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAEpD,CAAA;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAIhG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAIhD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,OAAO,CAKT;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,YAAY,EAAE,CAKhB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,SAAS,YAAY,EAAmB,EAC/C,QAAQ,GAAE,eAA0B,GACnC,iBAAiB,EAAE,CAuCrB"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;AAElF,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS,qBAAqB,EAAE,CAAA;AAEpG;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,YAAY,EA+DxC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAElD,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAEpD,CAAA;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAIhG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAIhD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,OAAO,CAKT;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,YAAY,EAAE,CAKhB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,SAAS,YAAY,EAAmB,EAC/C,QAAQ,GAAE,eAA0B,GACnC,iBAAiB,EAAE,CAuCrB"}
@@ -33,6 +33,9 @@ export const LOCAL_COMMANDS = [
33
33
  { name: 'export', description: 'Export the conversation to a markdown file' },
34
34
  { name: 'btw', description: 'Ask a quick side question without interrupting the conversation' },
35
35
  { name: 'trace', description: 'Show the session event trace timeline' },
36
+ { name: 'agentview', description: 'Open the agent view (all sessions)' },
37
+ { name: 'bg', description: 'Background this session and open agent view' },
38
+ { name: 'background', description: 'Background this session and open agent view', tag: 'alias of /bg' },
36
39
  // Session / environment
37
40
  { name: 'context', description: 'Show loaded context details' },
38
41
  { name: 'status', description: 'Show session status' },
@@ -56,7 +59,7 @@ export const LOCAL_COMMANDS = [
56
59
  { name: 'tokens', description: 'Show session token usage' },
57
60
  // Account / policy
58
61
  { name: 'balance', description: 'Show DeepSeek account balance' },
59
- { name: 'provider', description: 'Add an LLM provider (catalog or custom API endpoint)' },
62
+ { name: 'provider', description: 'Add, edit or delete an LLM provider (catalog or custom API endpoint)' },
60
63
  { name: 'login', description: 'Show API credential status' },
61
64
  { name: 'logout', description: 'Clear the API credential' },
62
65
  { name: 'add-dir', description: 'Show the filesystem policy scope' },
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ import { SubagentMessage } from './Chat/SubagentMessage.js';
11
11
  import { JobCard } from './Chat/JobCard.js';
12
12
  import { isMinimalMode } from '../minimalMode.js';
13
13
  import { noteFrameCause, noteListGeometry } from '../ink/geometry-trace.js';
14
+ import { getTerminalFlushTick } from '../ink/flush-tick.js';
14
15
  import { InterruptedByUser } from './InterruptedByUser.js';
15
16
  import { LogoV2 } from './LogoV2.js';
16
17
  import { StreamingMarkdown } from './StreamingMarkdown.js';
@@ -19,7 +20,8 @@ import { stripNarration } from '../utils/narration.js';
19
20
  import { stringWidth } from '../ink/stringWidth.js';
20
21
  import { truncateToWidth } from '../ink/truncateToWidth.js';
21
22
  import { clipPreview } from '../ink/timeline-rail.js';
22
- import { getRevealVersion, revealLengthOf, revealTextOf, subscribeReveal } from './smoothReveal.js';
23
+ import { getRevealVersion, revealLengthOf, revealTextOf } from './smoothReveal.js';
24
+ import { useRevealVersion } from '../hooks/useRevealVersion.js';
23
25
  /**
24
26
  * Transcript rows rendered in the Claude Code visual language: user prompts
25
27
  * on a grey bubble with a `❯` pointer, assistant text with a `●` bullet and
@@ -260,13 +262,17 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
260
262
  };
261
263
  // --- layout virtualization ---------------------------------------------
262
264
  const { columns, rows: termRows } = useTerminalSize();
263
- // Smooth-reveal wakeups: the scheduler's tick bumps this store, re-running
265
+ // Smooth-reveal wakeups: the scheduler's tick bumps this hook, re-running
264
266
  // MessageList so the revealed `text`/line-count reads below feed fresh
265
267
  // values through the same MemoRow-prop pipeline an arriving chunk uses —
266
268
  // memo miss → re-render → post-commit height re-measure. Without this
267
269
  // subscription a reveal living in child state would change row heights
268
270
  // invisibly to the virtualization (stale cached heights → blank bands).
269
- React.useSyncExternalStore(subscribeReveal, getRevealVersion);
271
+ // DefaultLane on purpose (useRevealVersion): a useSyncExternalStore wakeup
272
+ // would force a SyncLane render per 33ms tick, and each such commit ending
273
+ // with streaming work still pending feeds React's nested-update counter
274
+ // until error #185 kills the process (beta.3).
275
+ useRevealVersion();
270
276
  // Measured row heights, remembered after a row unmounts so virtualization
271
277
  // can compute total content height. Bounded: row ids grow monotonically
272
278
  // and rows are never removed from the transcript (foldRows keeps the
@@ -289,15 +295,18 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
289
295
  const paintedOnceRef = React.useRef(new Set());
290
296
  const paintedBaseRef = React.useRef(undefined);
291
297
  /** Window-expansion hold: after the window WIDENS (new rows mounted),
292
- * refuse to tighten for a short hold so the mounted rows actually reach
293
- * the terminal. React commits within one ink frame coalesce — a render
294
- * that mounts rows followed by the measure-tick re-render that drops
295
- * them paints only the DROPPED layout, and never-mounted rows have no
296
- * scrollback copy (preset history at boot vanished — CI
297
- * repro-inline-scrollback). After the hold, tightening is visually
298
- * free: those rows sit in scrollback and the diff skips them. */
298
+ * refuse to tighten until a frame containing that layout has actually
299
+ * been FLUSHED to the terminal (flush-tick based, issue #574). React
300
+ * commits and terminal writes are decoupled — the throttled deferred
301
+ * leading edge lets a later commit supersede the wide one inside the
302
+ * same task, so a wall-clock hold (the old 120ms timer) expires during
303
+ * long cold-cache layout work (~190ms on the repro) and the measure-tick
304
+ * re-render still drops the rows before a single byte of them was
305
+ * written; never-mounted rows have no scrollback copy and preset history
306
+ * vanishes. Once flushed, tightening is visually free: those rows sit in
307
+ * scrollback and the diff skips them. */
299
308
  const lastStartRef = React.useRef(-1);
300
- const holdUntilRef = React.useRef(0);
309
+ const holdFlushTickRef = React.useRef(-1);
301
310
  /** True when frame-budgeted history painting still has batches left
302
311
  * (main-screen open): the layout effect schedules the next slice. */
303
312
  const paintPendingRef = React.useRef(false);
@@ -566,15 +575,18 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
566
575
  }
567
576
  }
568
577
  // Expansion hold — AFTER the extension so it tracks the FINAL window:
569
- // never tighten within the hold window after a widen. React commits
570
- // inside one ink frame coalesce; a mount followed by the measure-tick
571
- // re-render that drops the row paints only the DROPPED layout, and the
572
- // row's painted-once mark (set at the first commit) is a lie.
573
- if (lastStartRef.current >= 0 && start > lastStartRef.current && performance.now() < holdUntilRef.current) {
578
+ // never tighten past a widen until a frame with that layout has been
579
+ // flushed (getTerminalFlushTick advanced since the widen). A mount
580
+ // followed by the measure-tick re-render that drops the row paints only
581
+ // the DROPPED layout, and the row's painted-once mark (set at the first
582
+ // commit) is a lie; holding until the flush makes the mark real.
583
+ if (lastStartRef.current >= 0 &&
584
+ start > lastStartRef.current &&
585
+ getTerminalFlushTick() === holdFlushTickRef.current) {
574
586
  start = lastStartRef.current;
575
587
  }
576
588
  if (lastStartRef.current < 0 || start < lastStartRef.current) {
577
- holdUntilRef.current = performance.now() + 120;
589
+ holdFlushTickRef.current = getTerminalFlushTick();
578
590
  }
579
591
  lastStartRef.current = start;
580
592
  }
@@ -763,18 +775,31 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
763
775
  upId: upTurnIndex === null ? null : timelineTurns[upTurnIndex].id,
764
776
  downId: downTurnIndex === null ? null : timelineTurns[downTurnIndex].id,
765
777
  };
766
- const lastTimelineReportRef = React.useRef('');
778
+ const lastTimelineReportRef = React.useRef(null);
767
779
  React.useEffect(() => {
768
- // O(1) signature: the memo key pins the turns' geometry identity
769
- // (heights/window/base/rows-length) and the three ids pin the
770
- // viewport-derived targets. Any top change bumps the geometry key, so
771
- // the report still fires exactly when the snapshot content changes —
772
- // without rebuilding an O(turns) joined string per commit.
773
- const sig = `${timelineMemoRef.current?.key ?? ''}#a${timeline.activeId}#u${timeline.upId}#d${timeline.downId}`;
774
- if (sig !== lastTimelineReportRef.current) {
775
- lastTimelineReportRef.current = sig;
776
- onTimeline?.(timeline);
777
- }
780
+ // Value-level dedup, not geometry-key-level: the memo key pins to
781
+ // heightsVersion, which a GROWING streamed row bumps on every commit,
782
+ // so a key-pinned signature re-reports an identical snapshot every
783
+ // commit of a long stream. Each report is a setState dispatched into
784
+ // Chat from this passive effect; it lands as pending residue at commit
785
+ // end, keeping the commit dirty and feeding React's nested-update
786
+ // counter (50 consecutive dirty commits → error #185, the beta.3
787
+ // crash). The O(turns) compare below is integer/string-reference
788
+ // compares only — cheap next to the joined-string signature the memo
789
+ // key replaced, and it fires exactly when the snapshot content changes.
790
+ const prev = lastTimelineReportRef.current;
791
+ if (prev !== null &&
792
+ prev.activeId === timeline.activeId &&
793
+ prev.upId === timeline.upId &&
794
+ prev.downId === timeline.downId &&
795
+ prev.turns.length === timeline.turns.length &&
796
+ prev.turns.every((t, i) => {
797
+ const n = timeline.turns[i];
798
+ return t.id === n.id && t.top === n.top && t.folded === n.folded && t.preview === n.preview;
799
+ }))
800
+ return;
801
+ lastTimelineReportRef.current = timeline;
802
+ onTimeline?.(timeline);
778
803
  });
779
804
  // Post-commit: measure mounted rows, derive the content-space base from
780
805
  // the first mounted row's Yoga top, and clamp render-time scrollTop to the
@@ -9,6 +9,13 @@ import type { Channel } from '../dsh-adapter/channel.js';
9
9
  export interface PromptController {
10
10
  hasText(): boolean;
11
11
  clear(): void;
12
+ /**
13
+ * Append `text` at the end of the input (external injection channel; see
14
+ * dsh-adapter/inject-channel.ts). Unlike `fillText`, which replaces the
15
+ * whole value, this accumulates — matching OpenCode's `tui.prompt.append`
16
+ * so repeated editor sends build one prompt. Returns the resulting value.
17
+ */
18
+ append(text: string): string;
12
19
  /**
13
20
  * Copy the active mouse selection to the system clipboard (OSC 52 + the
14
21
  * native fallback) and KEEP the selection for further editing. Returns
@@ -43,6 +50,17 @@ export interface PromptInputProps {
43
50
  onFillConsumed?(): void;
44
51
  /** Double-tap Esc with an empty input: open the rewind picker (CC rewind). */
45
52
  onRewindRequest?(): void;
53
+ /**
54
+ * CC agent-view parity: ← on an EMPTY prompt backgrounds this session and
55
+ * opens the agent view (with text, ← moves the caret as usual).
56
+ */
57
+ onBackgroundRequest?(): void;
58
+ /**
59
+ * Background sessions waiting on the user (agent view "needs input" rows
60
+ * excluding this session); the prompt footer shows the CC-style
61
+ * "← N agents" hint when provided (hidden when undefined).
62
+ */
63
+ backgroundAgentsNeedingInput?: number;
46
64
  /** Filled with the live controller each render (see PromptController). */
47
65
  controllerRef?: React.RefObject<PromptController | null>;
48
66
  }
@@ -77,5 +95,5 @@ export interface PromptInputProps {
77
95
  * working) interrupts the turn and delivers them right away; Ctrl+Enter
78
96
  * aborts the turn and sends the current input immediately.
79
97
  */
80
- export declare function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, selectionActive, fillText, onFillConsumed, onRewindRequest, controllerRef, }: PromptInputProps): React.JSX.Element;
98
+ export declare function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, selectionActive, fillText, onFillConsumed, onRewindRequest, onBackgroundRequest, backgroundAgentsNeedingInput, controllerRef, }: PromptInputProps): React.JSX.Element;
81
99
  //# sourceMappingURL=PromptInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PromptInput.d.ts","sourceRoot":"","sources":["../../../src/components/PromptInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAwBzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AA4OxD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,IAAI,OAAO,CAAA;IAClB,KAAK,IAAI,IAAI,CAAA;IACb;;;;;OAKG;IACH,oBAAoB,IAAI,OAAO,CAAA;IAC/B,2EAA2E;IAC3E,SAAS,IAAI,OAAO,CAAA;IACpB;yEACqE;IACrE,SAAS,IAAI,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,IAAI,IAAI,CAAA;IACpB;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACrD,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,IAAI,IAAI,CAAA;IACvB,8EAA8E;IAC9E,eAAe,CAAC,IAAI,IAAI,CAAA;IACxB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,cAAc,EACd,eAAe,EACf,aAAa,GACd,EAAE,gBAAgB,qBA85ElB"}
1
+ {"version":3,"file":"PromptInput.d.ts","sourceRoot":"","sources":["../../../src/components/PromptInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAwBzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AA4OxD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,IAAI,OAAO,CAAA;IAClB,KAAK,IAAI,IAAI,CAAA;IACb;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;IAC5B;;;;;OAKG;IACH,oBAAoB,IAAI,OAAO,CAAA;IAC/B,2EAA2E;IAC3E,SAAS,IAAI,OAAO,CAAA;IACpB;yEACqE;IACrE,SAAS,IAAI,OAAO,CAAA;CAAC;AAEvB,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,IAAI,IAAI,CAAA;IACpB;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACrD,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,IAAI,IAAI,CAAA;IACvB,8EAA8E;IAC9E,eAAe,CAAC,IAAI,IAAI,CAAA;IACxB;;;OAGG;IACH,mBAAmB,CAAC,IAAI,IAAI,CAAA;IAC5B;;;;OAIG;IACH,4BAA4B,CAAC,EAAE,MAAM,CAAA;IACrC,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,4BAA4B,EAC5B,aAAa,GACd,EAAE,gBAAgB,qBA+7ElB"}
@@ -280,7 +280,7 @@ const DOUBLE_CLICK_MS = 500;
280
280
  * working) interrupts the turn and delivers them right away; Ctrl+Enter
281
281
  * aborts the turn and sends the current input immediately.
282
282
  */
283
- export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, selectionActive, fillText, onFillConsumed, onRewindRequest, controllerRef, }) {
283
+ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, selectionActive, fillText, onFillConsumed, onRewindRequest, onBackgroundRequest, backgroundAgentsNeedingInput, controllerRef, }) {
284
284
  const [themeName] = useTheme();
285
285
  // Raw stdout writer for OSC 52 clipboard writes (selection copy) — must
286
286
  // bypass the frame pipeline; null outside a mounted Ink App.
@@ -406,6 +406,14 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
406
406
  setValue('');
407
407
  setCursor(0);
408
408
  },
409
+ append: (text) => {
410
+ const next = valueRef.current + text;
411
+ valueRef.current = next;
412
+ cursorRef.current = next.length;
413
+ setValue(next);
414
+ setCursor(next.length);
415
+ return next;
416
+ },
409
417
  consumeSelectionCopy: () => {
410
418
  const sel = selectionRef.current;
411
419
  if (!sel)
@@ -672,7 +680,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
672
680
  historyIndex.current = -1;
673
681
  setInput('', 0);
674
682
  setSelectedCommand(0);
675
- appendHistory(trimmed);
683
+ void appendHistory(trimmed);
676
684
  channel.submit(trimmed);
677
685
  if (notice) {
678
686
  channel.notify(notice, { timeoutMs: 2500 });
@@ -698,7 +706,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
698
706
  historyIndex.current = -1;
699
707
  setInput('', 0);
700
708
  setSelectedCommand(0);
701
- appendHistory(trimmed);
709
+ void appendHistory(trimmed);
702
710
  channel.steer(trimmed);
703
711
  channel.notify(t('input-interrupted-next'), { timeoutMs: 2500 });
704
712
  };
@@ -716,7 +724,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
716
724
  historyIndex.current = -1;
717
725
  setInput('', 0);
718
726
  setSelectedCommand(0);
719
- appendHistory(trimmed);
727
+ void appendHistory(trimmed);
720
728
  channel.submit(trimmed);
721
729
  channel.notify(t('input-queued-after-turn'), { timeoutMs: 2500 });
722
730
  };
@@ -762,7 +770,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
762
770
  setInput('', 0);
763
771
  setSelectedCommand(0);
764
772
  setFileSelected(0);
765
- appendHistory(trimmed);
773
+ void appendHistory(trimmed);
766
774
  channel.notify(t('input-interrupt-immediate'), { timeoutMs: 2500 });
767
775
  };
768
776
  /**
@@ -791,7 +799,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
791
799
  historyIndex.current = -1;
792
800
  setInput('', 0);
793
801
  setSelectedCommand(0);
794
- appendHistory(text.trim());
802
+ void appendHistory(text.trim());
795
803
  }
796
804
  return handled;
797
805
  };
@@ -1293,7 +1301,11 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
1293
1301
  return;
1294
1302
  }
1295
1303
  if (key.upArrow) {
1296
- if (fileOverlayOpen) {
1304
+ // A history walk owns the arrows until it returns to the draft: a
1305
+ // recalled entry can itself open the @ menu or the slash menu (e.g.
1306
+ // `/model`), and letting the overlay navigate here strands the stashed
1307
+ // draft — Down would cycle menu rows instead of walking back.
1308
+ if (fileOverlayOpen && historyIndex.current < 0) {
1297
1309
  setFileSelected(index => index <= 0 ? fileMatches.length - 1 : index - 1);
1298
1310
  return;
1299
1311
  }
@@ -1322,7 +1334,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
1322
1334
  setInput(value, prevLineStart + Math.min(cursorColumn(value, cursor), prevLine.length));
1323
1335
  return;
1324
1336
  }
1325
- if (overlayOpen) {
1337
+ if (overlayOpen && historyIndex.current < 0) {
1326
1338
  setSelectedCommand(index => index <= 0 ? suggestions.length - 1 : index - 1);
1327
1339
  return;
1328
1340
  }
@@ -1341,7 +1353,8 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
1341
1353
  return;
1342
1354
  }
1343
1355
  if (key.downArrow) {
1344
- if (fileOverlayOpen) {
1356
+ // Same history-walk ownership as ↑ above.
1357
+ if (fileOverlayOpen && historyIndex.current < 0) {
1345
1358
  setFileSelected(index => index >= fileMatches.length - 1 ? 0 : index + 1);
1346
1359
  return;
1347
1360
  }
@@ -1384,7 +1397,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
1384
1397
  setInput(value, nextLineStart + Math.min(cursorColumn(value, cursor), nextLine.length));
1385
1398
  return;
1386
1399
  }
1387
- if (overlayOpen) {
1400
+ if (overlayOpen && historyIndex.current < 0) {
1388
1401
  setSelectedCommand(index => index >= suggestions.length - 1 ? 0 : index + 1);
1389
1402
  return;
1390
1403
  }
@@ -1416,6 +1429,14 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
1416
1429
  return;
1417
1430
  }
1418
1431
  if (key.leftArrow) {
1432
+ // CC agent-view parity: ← on an EMPTY prompt backgrounds this session
1433
+ // and opens the agent view; with text it moves the caret as usual.
1434
+ // (The command/file overlays both imply non-empty text, so no extra
1435
+ // gate beyond the help menu is needed.)
1436
+ if (value.length === 0 && !helpOpen) {
1437
+ onBackgroundRequest?.();
1438
+ return;
1439
+ }
1419
1440
  // Grapheme-step: skip the whole cluster (surrogate pair, ZWJ emoji,
1420
1441
  // combining mark) so the caret never sits inside one. With a
1421
1442
  // selection, collapse to its start edge instead.
@@ -2397,7 +2418,9 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
2397
2418
  setExpandHovered(true);
2398
2419
  }, onMouseLeave: () => {
2399
2420
  setExpandHovered(false);
2400
- }, children: _jsx(Text, { dimColor: !expandHovered, bold: expandHovered, color: expandHovered ? promptAccent : undefined, children: "\u26F6" }) }))] }) })] }));
2421
+ }, children: _jsx(Text, { dimColor: !expandHovered, bold: expandHovered, color: expandHovered ? promptAccent : undefined, children: "\u26F6" }) }))] }) }), backgroundAgentsNeedingInput !== undefined && (_jsx(Box, { flexDirection: "row", justifyContent: "flex-end", paddingRight: 2, children: _jsx(Text, { dimColor: true, children: backgroundAgentsNeedingInput > 0
2422
+ ? t('input-background-hint-count', { n: backgroundAgentsNeedingInput })
2423
+ : t('input-background-hint-idle') }) }))] }));
2401
2424
  }
2402
2425
  /**
2403
2426
  * Grapheme word-wrap for one logical line: break at the last space when
@@ -15,7 +15,10 @@ import type { ApprovalSnapshot } from '../../dsh-adapter/approvals.js';
15
15
  export type ApprovalPanelProps = {
16
16
  /** The approval to render (from the ApprovalStore snapshot). */
17
17
  readonly approval: ApprovalSnapshot;
18
+ /** True when the asking agent is NOT the attached session — a background
19
+ * (agent view) session's ask, answered from the same single panel. */
20
+ readonly background?: boolean;
18
21
  readonly onDecide: (outcome: 'allowed-once' | 'rejected') => void;
19
22
  };
20
- export declare function ApprovalPanel({ approval, onDecide }: ApprovalPanelProps): React.ReactNode;
23
+ export declare function ApprovalPanel({ approval, background, onDecide }: ApprovalPanelProps): React.ReactNode;
21
24
  //# sourceMappingURL=ApprovalPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApprovalPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/approvals/ApprovalPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AAEtE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU,KAAK,IAAI,CAAA;CAClE,CAAA;AAID,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,SAAS,CAkFzF"}
1
+ {"version":3,"file":"ApprovalPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/approvals/ApprovalPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AAEtE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC;2EACuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU,KAAK,IAAI,CAAA;CAClE,CAAA;AAID,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,UAAkB,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,SAAS,CAuF7G"}
@@ -18,7 +18,7 @@ import { isPlainReturnInput } from '../../utils/modifiers.js';
18
18
  import { Divider } from '../design-system/Divider.js';
19
19
  import { POINTER } from '../../cc/figures.js';
20
20
  const OUTCOMES = ['allowed-once', 'rejected'];
21
- export function ApprovalPanel({ approval, onDecide }) {
21
+ export function ApprovalPanel({ approval, background = false, onDecide }) {
22
22
  const [focusIndex, setFocusIndex] = React.useState(0);
23
23
  // Hover highlight per decision row (mouse affordance; the click handler
24
24
  // below mirrors the keyboard Enter on the focused row).
@@ -45,7 +45,7 @@ export function ApprovalPanel({ approval, onDecide }) {
45
45
  }
46
46
  }, { isActive: true });
47
47
  const optionLabels = [t('approval-yes'), t('approval-no')];
48
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Divider, { color: "permission", title: t('approval-waiting', { tool: approval.toolName }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [approval.external === true && (_jsxs(Text, { color: "warning", wrap: "wrap", children: ["[external] ", t('approval-external-hint')] })), approval.command !== undefined && (_jsx(Box, { flexDirection: "column", paddingX: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: approval.command }) })), approval.reason !== undefined && (_jsx(Text, { dimColor: true, wrap: "wrap", children: approval.reason })), _jsx(Text, { dimColor: true, children: t('approval-proceed') })] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: optionLabels.map((label, index) => {
48
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Divider, { color: "permission", title: t('approval-waiting', { tool: approval.toolName }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [background && (_jsx(Text, { color: "warning", children: t('approval-background-agent', { id: approval.agentId.slice(0, 8) }) })), approval.external === true && (_jsxs(Text, { color: "warning", wrap: "wrap", children: ["[external] ", t('approval-external-hint')] })), approval.command !== undefined && (_jsx(Box, { flexDirection: "column", paddingX: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: approval.command }) })), approval.reason !== undefined && (_jsx(Text, { dimColor: true, wrap: "wrap", children: approval.reason })), _jsx(Text, { dimColor: true, children: t('approval-proceed') })] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: optionLabels.map((label, index) => {
49
49
  const focused = index === focusIndex;
50
50
  const hovered = index === hoverIndex;
51
51
  return (_jsxs(Box, { flexDirection: "row", marginTop: focused ? 1 : 0, onClick: () => onDecide(OUTCOMES[index]), onMouseEnter: () => setHoverIndex(index), onMouseLeave: () => setHoverIndex(current => (current === index ? -1 : current)), backgroundColor: hovered && !focused ? 'userMessageBackgroundHover' : undefined, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'claude' : undefined, bold: focused, children: focused ? POINTER : ' ' }) }), _jsxs(Text, { bold: focused, color: focused ? 'claude' : undefined, wrap: "wrap", children: [index + 1, ". ", label] })] }, label));
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;AAMvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAMjE,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,OAAO,EAAE,OAAO,CAAA;IAChB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;OAIG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;8CAC0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,qFAAqF;IACrF,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAsFD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,CAKtD;AA+OD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,SAAS,EACT,OAAO,EACP,UAAkB,EAClB,UAAkB,EAClB,OAAO,EACP,QAAQ,EACR,UAAmB,EACnB,cAAuB,EACvB,UAAU,EACV,mBAA2B,EAC3B,YAAoB,EACpB,KAAa,EACb,aAAa,GACd,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAoOzB"}
1
+ {"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;AAMvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAIjE,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,OAAO,EAAE,OAAO,CAAA;IAChB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;OAIG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;8CAC0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,qFAAqF;IACrF,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAsFD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,CAKtD;AA+OD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,SAAS,EACT,OAAO,EACP,UAAkB,EAClB,UAAkB,EAClB,OAAO,EACP,QAAQ,EACR,UAAmB,EACnB,cAAuB,EACvB,UAAU,EACV,mBAA2B,EAC3B,YAAoB,EACpB,KAAa,EACb,aAAa,GACd,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAoOzB"}
@@ -9,9 +9,8 @@ import { SplitDiffView } from '../SplitDiffView.js';
9
9
  import { SyntaxText } from '../SyntaxText.js';
10
10
  import { useTooltip } from '../Tooltip.js';
11
11
  import { formatDuration } from '../../cc/format.js';
12
- import { getRevealVersion, revealLinesOf, snapReveal, subscribeReveal } from '../smoothReveal.js';
13
- const NOOP_REVEAL_SUBSCRIBE = (_listener) => () => { };
14
- const getNoRevealVersion = () => 0;
12
+ import { revealLinesOf, snapReveal } from '../smoothReveal.js';
13
+ import { useRevealVersion } from '../../hooks/useRevealVersion.js';
15
14
  /** Tool display names: DSH emits lowercase tool ids (`bash`); Claude Code
16
15
  * shows capitalized names (`Bash`). Map the common ones, fall back to the
17
16
  * id with its first letter uppercased. */
@@ -276,7 +275,10 @@ export function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected =
276
275
  // MessageList owns the single production subscription and passes a version
277
276
  // prop only to active reveal rows. Standalone consumers keep the fallback
278
277
  // subscription so the component contract remains self-contained.
279
- React.useSyncExternalStore(revealVersion === undefined ? subscribeReveal : NOOP_REVEAL_SUBSCRIBE, revealVersion === undefined ? getRevealVersion : getNoRevealVersion);
278
+ // DefaultLane on purpose (useRevealVersion): a useSyncExternalStore wakeup
279
+ // forces a SyncLane render per tick, and repeated sync commits ending with
280
+ // streaming work pending feed React's nested-update counter (error #185).
281
+ useRevealVersion(revealVersion === undefined);
280
282
  const isRunning = tool.status === 'running';
281
283
  const isError = tool.status === 'error';
282
284
  const displayArgs = verbose ? tool.argsFull ?? tool.argsText : tool.argsText;
@@ -28,6 +28,10 @@ export type AskUserQuestionPanelProps = {
28
28
  * (local wizards, e.g. /provider). Ignored when there are no options —
29
29
  * a text-only question would otherwise be unanswerable. */
30
30
  readonly hideCustomInput?: boolean;
31
+ /** Pre-checked option labels (multi-select) / default-focused option
32
+ * (single-select) shown on first display, before any saved draft — e.g.
33
+ * the models already enabled on a provider being edited. */
34
+ readonly defaultSelected?: readonly string[];
31
35
  /** Presentation intent tag (rc.6): 'plan-review' switches to the
32
36
  * decision-card layout; an intent never changes the protocol. */
33
37
  readonly intent?: {
@@ -1 +1 @@
1
- {"version":3,"file":"AskUserQuestionPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/AskUserQuestionPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAStF,MAAM,MAAM,yBAAyB,GAAG;IACtC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;QAC9B;;oEAE4D;QAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;QAClC;0EACkE;QAClE,QAAQ,CAAC,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7E,CAAA;IACD,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAA;IACrC,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;CACjD,CAAA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,yBAAyB,GAAG,KAAK,CAAC,SAAS,CAkb7C"}
1
+ {"version":3,"file":"AskUserQuestionPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/AskUserQuestionPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAStF,MAAM,MAAM,yBAAyB,GAAG;IACtC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;QAC9B;;oEAE4D;QAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;QAClC;;qEAE6D;QAC7D,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;QAC5C;0EACkE;QAClE,QAAQ,CAAC,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7E,CAAA;IACD,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAA;IACrC,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;CACjD,CAAA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,yBAAyB,GAAG,KAAK,CAAC,SAAS,CAub7C"}
@@ -37,7 +37,12 @@ export function AskUserQuestionPanel({ question, position, total, answered, init
37
37
  const { rows: terminalRows } = useTerminalSize();
38
38
  /** Rows: the real options plus the inline input row at the tail. */
39
39
  const rowCount = options.length + (hideCustomInput ? 0 : 1);
40
- const initialSelected = initialDraft?.selected ?? [];
40
+ // A saved draft wins over the wizard's default selection (returning to a
41
+ // question must restore exactly what the user last had, including an
42
+ // explicit empty answer); the defaults only apply on first display.
43
+ const initialSelected = initialDraft !== undefined
44
+ ? initialDraft.selected
45
+ : question.defaultSelected ?? [];
41
46
  const initialCustom = initialDraft?.custom ?? '';
42
47
  const selectedIndices = options
43
48
  .map((option, index) => initialSelected.includes(option.label) ? index : -1)
@@ -1,43 +1,3 @@
1
- /**
2
- * Smooth streaming reveal — decouples "text the provider has delivered" from
3
- * "text the screen is showing" (the oh-my-pi `display.smoothStreaming` idea,
4
- * see their `StreamingRevealController`). A shared ~30fps scheduler advances
5
- * each reveal cursor by an adaptive catch-up step, so burst-delivered text (a
6
- * slow provider dumping a large batch, or a non-streaming response arriving
7
- * whole) paints as a brief even flow instead of one jarring jump, while
8
- * genuinely streaming text is re-paced to a steady cadence regardless of
9
- * chunk timing.
10
- *
11
- * Step math (per frame): `max(MIN_STEP, ceil(backlog / CATCHUP_FRAMES))` —
12
- * an EXPONENTIAL decay over the backlog (each frame removes ~1/8 of what is
13
- * still unrevealed, the oh-my-pi semantics exactly): a large burst lands its
14
- * first three quarters within a few frames, the remainder tapers, and the
15
- * MIN_STEP floor turns the tail into a steady linear crawl so the reveal
16
- * always finishes. A ~2000-character one-shot delivery paints fully in
17
- * roughly 1.5s; genuinely streaming text (small backlogs) paces at a steady
18
- * per-frame rate. The reveal never runs ahead of what has arrived.
19
- *
20
- * ARCHITECTURE — module-level cursors, not component state. dsh-tui's
21
- * transcript virtualization feeds on MemoRow prop changes (flattened
22
- * primitives) and re-measures mounted rows after every MessageList commit;
23
- * a reveal living in child-component state would change row heights without
24
- * touching those props, and the memo/signature/height-measure chain would
25
- * never notice (stale cached heights → blank bands). Instead the cursors
26
- * live here keyed by row identity, the READS happen during render (they feed
27
- * the same flattened `text` prop the streaming path already uses), and the
28
- * WRITES happen on the scheduler tick, which bumps a version that
29
- * MessageList subscribes to via useSyncExternalStore. A reveal therefore
30
- * rides the exact same update pipeline as an arriving chunk: prop change →
31
- * memo miss → re-render → post-commit measurement. Render-phase reads are
32
- * idempotent and safe to re-run (a discarded concurrent render at worst
33
- * creates a cursor the tick soon retires).
34
- *
35
- * Unit model: reveal positions are UTF-16 code units (Chinese text is one
36
- * unit per character). Surrogate pairs are never split (see {@link
37
- * safeSliceEnd}); grapheme clusters longer than a surrogate pair (emoji ZWJ
38
- * chains) may briefly show a partial cluster mid-reveal — transient frames
39
- * repaint within 33ms, and settled rendering is always the full text.
40
- */
41
1
  /** Reveal tick cadence — ~30fps, matching oh-my-pi's controller. */
42
2
  export declare const REVEAL_FRAME_MS: number;
43
3
  /** Minimum units (code units or lines) advanced per tick while work remains. */
@@ -1 +1 @@
1
- {"version":3,"file":"smoothReveal.d.ts","sourceRoot":"","sources":["../../../src/components/smoothReveal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,oEAAoE;AACpE,eAAO,MAAM,eAAe,QAAY,CAAA;AACxC,gFAAgF;AAChF,eAAO,MAAM,eAAe,IAAI,CAAA;AAChC,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,IAAI,CAAA;AAEtC,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAElD;AAwGD,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKhE;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAMD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;;;;OASG;IACH,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAiC5F;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAI1F;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAqC5F;AAMD,wEAAwE;AACxE,wBAAgB,kBAAkB,IAAI,IAAI,CAUzC;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C"}
1
+ {"version":3,"file":"smoothReveal.d.ts","sourceRoot":"","sources":["../../../src/components/smoothReveal.ts"],"names":[],"mappings":"AA0CA,oEAAoE;AACpE,eAAO,MAAM,eAAe,QAAY,CAAA;AACxC,gFAAgF;AAChF,eAAO,MAAM,eAAe,IAAI,CAAA;AAChC,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,IAAI,CAAA;AAEtC,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAElD;AAiID,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKhE;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAMD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;;;;OASG;IACH,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAiC5F;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAI1F;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAqC5F;AAMD,wEAAwE;AACxE,wBAAgB,kBAAkB,IAAI,IAAI,CAUzC;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C"}
@@ -38,6 +38,7 @@
38
38
  * chains) may briefly show a partial cluster mid-reveal — transient frames
39
39
  * repaint within 33ms, and settled rendering is always the full text.
40
40
  */
41
+ import { registerOverflowQuench, swallowNestedUpdateOverflow } from '../ink/update-overflow-guard.js';
41
42
  /** Reveal tick cadence — ~30fps, matching oh-my-pi's controller. */
42
43
  export const REVEAL_FRAME_MS = 1000 / 30;
43
44
  /** Minimum units (code units or lines) advanced per tick while work remains. */
@@ -108,6 +109,22 @@ function stopRevealTimerIfIdle() {
108
109
  revealTimer = undefined;
109
110
  }
110
111
  }
112
+ // Circuit-breaker quench: a sustained #185 oscillation surfacing at the
113
+ // reveal tick pauses this scheduler for the backoff window (cursors freeze
114
+ // in place; the resume timer — or a later render reading an active cursor —
115
+ // restarts it). Absorbing forever would leave the process alive but laggy.
116
+ registerOverflowQuench('reveal.tick', ms => {
117
+ if (revealTimer !== undefined) {
118
+ clearInterval(revealTimer);
119
+ revealTimer = undefined;
120
+ const resume = setTimeout(() => {
121
+ if (revealTimer === undefined && (textCursors.size > 0 || countCursors.size > 0)) {
122
+ ensureRevealTimer();
123
+ }
124
+ }, ms);
125
+ resume.unref?.();
126
+ }
127
+ });
111
128
  function revealTick() {
112
129
  let advanced = false;
113
130
  for (const [key, cursor] of textCursors) {
@@ -131,8 +148,17 @@ function revealTick() {
131
148
  }
132
149
  if (advanced) {
133
150
  revealVersionValue += 1;
134
- for (const listener of [...revealListeners])
135
- listener();
151
+ // #185 self-heal: a forceStoreRerender enqueue can surface the overflow
152
+ // here; React resets the counter on throw, so absorb and drop the tick.
153
+ for (const listener of [...revealListeners]) {
154
+ try {
155
+ listener();
156
+ }
157
+ catch (error) {
158
+ if (!swallowNestedUpdateOverflow(error, 'reveal.tick'))
159
+ throw error;
160
+ }
161
+ }
136
162
  }
137
163
  else {
138
164
  stopRevealTimerIfIdle();