@oh-my-pi/pi-tui 18.4.2 → 18.4.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 (463) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/types/apps/cleanse-board.d.ts +17 -0
  3. package/dist/types/apps/git/help.d.ts +16 -0
  4. package/dist/types/apps/git/sidebar.d.ts +18 -0
  5. package/dist/types/apps/live-visualizer.d.ts +10 -0
  6. package/dist/types/apps/ps-top.d.ts +15 -0
  7. package/dist/types/apps/standalone-picker.d.ts +3 -0
  8. package/dist/types/autocomplete.d.ts +8 -0
  9. package/dist/types/chat/advisor-message.d.ts +12 -2
  10. package/dist/types/chat/assistant-message.d.ts +20 -0
  11. package/dist/types/chat/bash-execution.d.ts +9 -0
  12. package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
  13. package/dist/types/chat/collab-prompt-message.d.ts +4 -0
  14. package/dist/types/chat/compaction-summary-message.d.ts +15 -0
  15. package/dist/types/chat/eval-execution.d.ts +8 -0
  16. package/dist/types/chat/execution-shared.d.ts +58 -0
  17. package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
  18. package/dist/types/chat/read-tool-group.d.ts +15 -0
  19. package/dist/types/chat/skill-message.d.ts +9 -0
  20. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
  21. package/dist/types/chat/thinking-display.d.ts +2 -0
  22. package/dist/types/chat/todo-reminder.d.ts +4 -1
  23. package/dist/types/chat/tool-execution.d.ts +25 -1
  24. package/dist/types/chat/transcript-actions.d.ts +37 -0
  25. package/dist/types/chat/transcript-entry.d.ts +3 -1
  26. package/dist/types/chat/user-message.d.ts +25 -0
  27. package/dist/types/chrome/collab-qrcode.d.ts +7 -0
  28. package/dist/types/chrome/context-thresholds.d.ts +11 -0
  29. package/dist/types/chrome/countdown-timer.d.ts +9 -3
  30. package/dist/types/chrome/diff.d.ts +12 -0
  31. package/dist/types/chrome/dynamic-border.d.ts +3 -0
  32. package/dist/types/chrome/format.d.ts +6 -0
  33. package/dist/types/chrome/message-divider.d.ts +13 -0
  34. package/dist/types/chrome/message-frame.d.ts +10 -0
  35. package/dist/types/chrome/message-notice.d.ts +23 -0
  36. package/dist/types/chrome/overlay-box.d.ts +11 -2
  37. package/dist/types/chrome/qrcode.d.ts +7 -0
  38. package/dist/types/chrome/segment-track.d.ts +7 -0
  39. package/dist/types/chrome/selector-helpers.d.ts +0 -6
  40. package/dist/types/chrome/status-notice.d.ts +14 -0
  41. package/dist/types/chrome/streaming-panel.d.ts +3 -0
  42. package/dist/types/chrome/tool-activity.d.ts +3 -0
  43. package/dist/types/chrome/transcript-container.d.ts +13 -0
  44. package/dist/types/chrome/transcript-status.d.ts +15 -3
  45. package/dist/types/components/box.d.ts +7 -0
  46. package/dist/types/components/disclosure.d.ts +9 -0
  47. package/dist/types/components/editor.d.ts +42 -0
  48. package/dist/types/components/form.d.ts +14 -0
  49. package/dist/types/components/image.d.ts +7 -0
  50. package/dist/types/components/input.d.ts +20 -1
  51. package/dist/types/components/key-value-list.d.ts +3 -0
  52. package/dist/types/components/layout/geometry.d.ts +16 -0
  53. package/dist/types/components/layout/row.d.ts +7 -0
  54. package/dist/types/components/layout/split-pane.d.ts +8 -0
  55. package/dist/types/components/layout/stack.d.ts +6 -0
  56. package/dist/types/components/loader.d.ts +48 -0
  57. package/dist/types/components/markdown.d.ts +8 -0
  58. package/dist/types/components/metric.d.ts +7 -0
  59. package/dist/types/components/progress-bar.d.ts +3 -0
  60. package/dist/types/components/scroll-view.d.ts +6 -0
  61. package/dist/types/components/section.d.ts +3 -0
  62. package/dist/types/components/select-list.d.ts +33 -0
  63. package/dist/types/components/settings-list.d.ts +68 -0
  64. package/dist/types/components/spacer.d.ts +4 -0
  65. package/dist/types/components/tab-bar.d.ts +9 -0
  66. package/dist/types/components/table.d.ts +7 -0
  67. package/dist/types/components/text.d.ts +6 -0
  68. package/dist/types/components/tree-view.d.ts +9 -0
  69. package/dist/types/components/truncated-text.d.ts +3 -0
  70. package/dist/types/components/wizard-step.d.ts +6 -0
  71. package/dist/types/hotkeys-markdown.d.ts +24 -0
  72. package/dist/types/index.d.ts +5 -0
  73. package/dist/types/latex-block.d.ts +7 -0
  74. package/dist/types/loop-watchdog.d.ts +9 -0
  75. package/dist/types/native/apply.d.ts +39 -0
  76. package/dist/types/native/backend.d.ts +94 -0
  77. package/dist/types/native/blobs.d.ts +29 -0
  78. package/dist/types/native/describe.d.ts +56 -0
  79. package/dist/types/native/encode.d.ts +50 -0
  80. package/dist/types/native/icons.d.ts +18 -0
  81. package/dist/types/native/memo.d.ts +27 -0
  82. package/dist/types/native/node.d.ts +107 -0
  83. package/dist/types/native/overlay.d.ts +64 -0
  84. package/dist/types/native/picker.d.ts +124 -0
  85. package/dist/types/native/reconcile.d.ts +41 -0
  86. package/dist/types/native/settle.d.ts +11 -0
  87. package/dist/types/native/spans.d.ts +21 -0
  88. package/dist/types/native/state.d.ts +6 -0
  89. package/dist/types/native/tone.d.ts +24 -0
  90. package/dist/types/overlays/advisor-config.d.ts +20 -0
  91. package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
  92. package/dist/types/overlays/agent-hub.d.ts +10 -0
  93. package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
  94. package/dist/types/overlays/agents-hub.d.ts +8 -0
  95. package/dist/types/overlays/annotation-overlay.d.ts +3 -0
  96. package/dist/types/overlays/ask-dialog.d.ts +23 -0
  97. package/dist/types/overlays/bordered-loader.d.ts +3 -0
  98. package/dist/types/overlays/btw-history-panel.d.ts +9 -0
  99. package/dist/types/overlays/btw-panel.d.ts +3 -0
  100. package/dist/types/overlays/cleanse-panel.d.ts +5 -2
  101. package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
  102. package/dist/types/overlays/copy-selector.d.ts +38 -0
  103. package/dist/types/overlays/error-banner.d.ts +9 -0
  104. package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
  105. package/dist/types/overlays/history-search.d.ts +18 -0
  106. package/dist/types/overlays/hook-editor.d.ts +28 -1
  107. package/dist/types/overlays/hook-input.d.ts +3 -0
  108. package/dist/types/overlays/hook-selector.d.ts +10 -0
  109. package/dist/types/overlays/hub-frame.d.ts +18 -0
  110. package/dist/types/overlays/jobs-panel.d.ts +24 -0
  111. package/dist/types/overlays/login-dialog.d.ts +10 -0
  112. package/dist/types/overlays/logout-account-selector.d.ts +4 -0
  113. package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
  114. package/dist/types/overlays/model-browser.d.ts +69 -0
  115. package/dist/types/overlays/model-hub.d.ts +7 -0
  116. package/dist/types/overlays/model-picker.d.ts +9 -1
  117. package/dist/types/overlays/move-overlay.d.ts +3 -0
  118. package/dist/types/overlays/oauth-selector.d.ts +4 -0
  119. package/dist/types/overlays/omfg-panel.d.ts +3 -0
  120. package/dist/types/overlays/pause-screen.d.ts +12 -0
  121. package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
  122. package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
  123. package/dist/types/overlays/plugin-selector.d.ts +4 -0
  124. package/dist/types/overlays/plugin-settings.d.ts +30 -0
  125. package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
  126. package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
  127. package/dist/types/overlays/rewind-selector.d.ts +5 -0
  128. package/dist/types/overlays/session-info-overlay.d.ts +19 -1
  129. package/dist/types/overlays/session-selector.d.ts +64 -1
  130. package/dist/types/overlays/settings-defs.d.ts +14 -0
  131. package/dist/types/overlays/settings-selector.d.ts +21 -0
  132. package/dist/types/overlays/show-images-selector.d.ts +4 -0
  133. package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
  134. package/dist/types/overlays/stats-notice.d.ts +8 -0
  135. package/dist/types/overlays/theme-selector.d.ts +11 -0
  136. package/dist/types/overlays/thinking-selector.d.ts +4 -0
  137. package/dist/types/overlays/tree-selector.d.ts +60 -2
  138. package/dist/types/overlays/usage-dashboard.d.ts +26 -3
  139. package/dist/types/overlays/usage-row.d.ts +36 -2
  140. package/dist/types/prompt/attachment-chips.d.ts +8 -0
  141. package/dist/types/prompt/composer-attachments.d.ts +15 -3
  142. package/dist/types/prompt/composer.d.ts +18 -1
  143. package/dist/types/prompt/custom-editor.d.ts +48 -2
  144. package/dist/types/prompt/editor-top-gap.d.ts +2 -0
  145. package/dist/types/prompt/macos-spelling.d.ts +6 -0
  146. package/dist/types/prompt/magic-keywords.d.ts +9 -0
  147. package/dist/types/prompt/queued-messages.d.ts +23 -0
  148. package/dist/types/prompt/skill-tokens.d.ts +4 -2
  149. package/dist/types/prompt/welcome.d.ts +16 -0
  150. package/dist/types/render/code-cell.d.ts +10 -0
  151. package/dist/types/render/file-list.d.ts +8 -0
  152. package/dist/types/render/hyperlink.d.ts +14 -0
  153. package/dist/types/render/output-block.d.ts +43 -0
  154. package/dist/types/render/output-pane.d.ts +17 -0
  155. package/dist/types/render/tool-card.d.ts +8 -0
  156. package/dist/types/render/tree-list.d.ts +8 -0
  157. package/dist/types/render/width-aware-text.d.ts +7 -0
  158. package/dist/types/setup/scenes/outro.d.ts +7 -0
  159. package/dist/types/setup/scenes/sign-in.d.ts +16 -9
  160. package/dist/types/setup/scenes/splash.d.ts +8 -0
  161. package/dist/types/setup/scenes/types.d.ts +6 -27
  162. package/dist/types/setup/wizard-overlay.d.ts +9 -0
  163. package/dist/types/status-line/component.d.ts +49 -5
  164. package/dist/types/status-line/context-usage.d.ts +20 -0
  165. package/dist/types/status-line/footer.d.ts +8 -0
  166. package/dist/types/status-line/metrics.d.ts +1 -2
  167. package/dist/types/status-line/segments.d.ts +10 -1
  168. package/dist/types/status-line/types.d.ts +40 -1
  169. package/dist/types/stdin-buffer.d.ts +10 -7
  170. package/dist/types/terminal-capabilities.d.ts +1 -1
  171. package/dist/types/terminal.d.ts +36 -0
  172. package/dist/types/theme/defaults/index.d.ts +100 -0
  173. package/dist/types/theme/schema.d.ts +2 -0
  174. package/dist/types/theme/shimmer.d.ts +8 -0
  175. package/dist/types/theme/theme-class.d.ts +2 -0
  176. package/dist/types/theme/theme.d.ts +33 -1
  177. package/dist/types/tools/ask.d.ts +3 -0
  178. package/dist/types/tools/ast-edit.d.ts +10 -1
  179. package/dist/types/tools/ast-grep.d.ts +10 -1
  180. package/dist/types/tools/autoresearch.d.ts +21 -4
  181. package/dist/types/tools/bash.d.ts +30 -1
  182. package/dist/types/tools/cfg-render.d.ts +5 -1
  183. package/dist/types/tools/debug.d.ts +3 -1
  184. package/dist/types/tools/default-renderer.d.ts +6 -1
  185. package/dist/types/tools/edit.d.ts +14 -1
  186. package/dist/types/tools/eval.d.ts +12 -1
  187. package/dist/types/tools/fetch.d.ts +16 -1
  188. package/dist/types/tools/find.d.ts +15 -1
  189. package/dist/types/tools/github.d.ts +3 -0
  190. package/dist/types/tools/glob.d.ts +14 -1
  191. package/dist/types/tools/goal.d.ts +3 -1
  192. package/dist/types/tools/grep.d.ts +14 -1
  193. package/dist/types/tools/grouped-file-output.d.ts +58 -0
  194. package/dist/types/tools/json-tree.d.ts +20 -0
  195. package/dist/types/tools/lsp.d.ts +3 -10
  196. package/dist/types/tools/mcp.d.ts +16 -1
  197. package/dist/types/tools/memory.d.ts +26 -0
  198. package/dist/types/tools/native-view.d.ts +90 -0
  199. package/dist/types/tools/proc-render.d.ts +7 -1
  200. package/dist/types/tools/read.d.ts +23 -1
  201. package/dist/types/tools/renderer.d.ts +57 -0
  202. package/dist/types/tools/report-tool-issue.d.ts +3 -0
  203. package/dist/types/tools/resolve.d.ts +14 -1
  204. package/dist/types/tools/task.d.ts +3 -0
  205. package/dist/types/tools/think.d.ts +3 -0
  206. package/dist/types/tools/todo.d.ts +12 -0
  207. package/dist/types/tools/vibe.d.ts +3 -1
  208. package/dist/types/tools/wait.d.ts +3 -0
  209. package/dist/types/tools/web-search-types.d.ts +90 -0
  210. package/dist/types/tools/web-search.d.ts +9 -168
  211. package/dist/types/tools/write.d.ts +14 -1
  212. package/dist/types/tools/xdev.d.ts +13 -0
  213. package/dist/types/tui.d.ts +49 -0
  214. package/package.json +9 -9
  215. package/src/apps/autoresearch-dashboard.ts +362 -4
  216. package/src/apps/cleanse-board.ts +219 -1
  217. package/src/apps/cleanse-picker.ts +9 -1
  218. package/src/apps/debug/log-viewer.ts +15 -15
  219. package/src/apps/debug/protocol-probe.ts +2 -20
  220. package/src/apps/git/avatar.ts +2 -22
  221. package/src/apps/git/git-tui.ts +406 -59
  222. package/src/apps/git/help.ts +130 -0
  223. package/src/apps/git/sidebar.ts +491 -0
  224. package/src/apps/live-visualizer.ts +65 -0
  225. package/src/apps/ps-top.ts +448 -17
  226. package/src/apps/session-picker.ts +1 -0
  227. package/src/apps/setup-model-picker.ts +5 -1
  228. package/src/apps/standalone-picker.ts +55 -1
  229. package/src/autocomplete.ts +44 -1
  230. package/src/chat/advisor-message.ts +72 -3
  231. package/src/chat/assistant-message.ts +538 -51
  232. package/src/chat/background-tan-message.ts +4 -4
  233. package/src/chat/bash-execution.ts +81 -0
  234. package/src/chat/cache-invalidation-marker.ts +8 -0
  235. package/src/chat/chat-transcript-builder.ts +37 -6
  236. package/src/chat/collab-prompt-message.ts +15 -3
  237. package/src/chat/compaction-summary-message.ts +74 -1
  238. package/src/chat/custom-message.ts +3 -1
  239. package/src/chat/eval-execution.ts +57 -0
  240. package/src/chat/execution-shared.ts +153 -1
  241. package/src/chat/hook-message.ts +1 -0
  242. package/src/chat/late-diagnostics-message.ts +85 -2
  243. package/src/chat/read-tool-group.ts +341 -1
  244. package/src/chat/served-model-marker.ts +8 -0
  245. package/src/chat/skill-message.ts +63 -0
  246. package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
  247. package/src/chat/thinking-display.ts +73 -11
  248. package/src/chat/todo-reminder.ts +34 -4
  249. package/src/chat/tool-execution.ts +385 -10
  250. package/src/chat/transcript-actions.ts +38 -0
  251. package/src/chat/transcript-browser.ts +2 -9
  252. package/src/chat/transcript-entry.ts +10 -1
  253. package/src/chat/transcript-render-helpers.ts +16 -15
  254. package/src/chat/ttsr-notification.ts +38 -1
  255. package/src/chat/user-message.ts +167 -4
  256. package/src/chrome/collab-qrcode.ts +27 -2
  257. package/src/chrome/context-thresholds.ts +34 -0
  258. package/src/chrome/countdown-timer.ts +24 -1
  259. package/src/chrome/diff.ts +60 -1
  260. package/src/chrome/dynamic-border.ts +7 -0
  261. package/src/chrome/format.ts +12 -0
  262. package/src/chrome/message-divider.ts +33 -1
  263. package/src/chrome/message-frame.ts +53 -10
  264. package/src/chrome/message-notice.ts +71 -0
  265. package/src/chrome/overlay-box.ts +45 -2
  266. package/src/chrome/qrcode.ts +28 -0
  267. package/src/chrome/segment-track.ts +26 -0
  268. package/src/chrome/selector-helpers.ts +1 -17
  269. package/src/chrome/status-notice.ts +46 -0
  270. package/src/chrome/streaming-panel.ts +25 -1
  271. package/src/chrome/tool-activity.ts +20 -0
  272. package/src/chrome/transcript-container.ts +139 -17
  273. package/src/chrome/transcript-status.ts +40 -11
  274. package/src/components/box.ts +51 -0
  275. package/src/components/disclosure.ts +33 -0
  276. package/src/components/editor.ts +239 -36
  277. package/src/components/form.ts +93 -9
  278. package/src/components/image.ts +30 -0
  279. package/src/components/input.ts +77 -26
  280. package/src/components/key-value-list.ts +18 -0
  281. package/src/components/layout/geometry.ts +44 -0
  282. package/src/components/layout/row.ts +30 -0
  283. package/src/components/layout/split-pane.ts +65 -0
  284. package/src/components/layout/stack.ts +27 -0
  285. package/src/components/loader.ts +183 -1
  286. package/src/components/markdown.ts +131 -56
  287. package/src/components/metric.ts +39 -0
  288. package/src/components/progress-bar.ts +28 -0
  289. package/src/components/scroll-view.ts +23 -0
  290. package/src/components/section.ts +16 -0
  291. package/src/components/select-list.ts +174 -25
  292. package/src/components/settings-list.ts +264 -7
  293. package/src/components/spacer.ts +17 -0
  294. package/src/components/tab-bar.ts +42 -0
  295. package/src/components/table.ts +34 -0
  296. package/src/components/text.ts +23 -0
  297. package/src/components/tree-view.ts +65 -0
  298. package/src/components/truncated-text.ts +14 -0
  299. package/src/components/wizard-step.ts +18 -9
  300. package/src/debug-server.ts +14 -7
  301. package/src/hotkeys-markdown.ts +251 -69
  302. package/src/index.ts +6 -0
  303. package/src/latex-block.ts +11 -0
  304. package/src/loop-watchdog.ts +38 -10
  305. package/src/native/apply.ts +275 -0
  306. package/src/native/backend.ts +682 -0
  307. package/src/native/blobs.ts +70 -0
  308. package/src/native/describe.ts +124 -0
  309. package/src/native/encode.ts +233 -0
  310. package/src/native/icons.ts +184 -0
  311. package/src/native/memo.ts +64 -0
  312. package/src/native/node.ts +112 -0
  313. package/src/native/overlay.ts +131 -0
  314. package/src/native/picker.ts +333 -0
  315. package/src/native/reconcile.ts +1063 -0
  316. package/src/native/settle.ts +23 -0
  317. package/src/native/spans.ts +308 -0
  318. package/src/native/state.ts +28 -0
  319. package/src/native/tone.ts +79 -0
  320. package/src/overlays/advisor-config.ts +420 -0
  321. package/src/overlays/agent-hub-renderer.ts +155 -18
  322. package/src/overlays/agent-hub.ts +1037 -44
  323. package/src/overlays/agent-transcript-viewer.ts +129 -0
  324. package/src/overlays/agents-hub.ts +688 -53
  325. package/src/overlays/annotation-overlay.ts +391 -52
  326. package/src/overlays/ask-dialog.ts +435 -24
  327. package/src/overlays/bordered-loader.ts +17 -0
  328. package/src/overlays/btw-history-panel.ts +182 -0
  329. package/src/overlays/btw-panel.ts +68 -2
  330. package/src/overlays/cleanse-panel.ts +105 -15
  331. package/src/overlays/codex-reset-fireworks.ts +65 -5
  332. package/src/overlays/composer-shape-preview.ts +26 -1
  333. package/src/overlays/copy-selector.ts +545 -23
  334. package/src/overlays/error-banner.ts +44 -0
  335. package/src/overlays/extensions/extension-list.ts +23 -17
  336. package/src/overlays/history-search.ts +175 -33
  337. package/src/overlays/hook-editor.ts +113 -6
  338. package/src/overlays/hook-input.ts +22 -1
  339. package/src/overlays/hook-selector.ts +311 -22
  340. package/src/overlays/hub-frame.ts +80 -0
  341. package/src/overlays/jobs-panel.ts +112 -0
  342. package/src/overlays/login-dialog.ts +183 -1
  343. package/src/overlays/logout-account-selector.ts +102 -4
  344. package/src/overlays/mcp-add-wizard.ts +232 -53
  345. package/src/overlays/model-browser.ts +681 -22
  346. package/src/overlays/model-hub.ts +1402 -78
  347. package/src/overlays/model-picker.ts +184 -7
  348. package/src/overlays/move-overlay.ts +45 -0
  349. package/src/overlays/oauth-selector.ts +227 -23
  350. package/src/overlays/omfg-panel.ts +86 -2
  351. package/src/overlays/pause-screen.ts +55 -4
  352. package/src/overlays/plan-review-overlay.ts +352 -0
  353. package/src/overlays/plan-save-overlay.ts +45 -0
  354. package/src/overlays/plugin-selector.ts +21 -1
  355. package/src/overlays/plugin-settings.ts +195 -5
  356. package/src/overlays/queue-mode-selector.ts +19 -1
  357. package/src/overlays/reset-usage-selector.ts +110 -40
  358. package/src/overlays/rewind-selector.ts +458 -44
  359. package/src/overlays/session-account-selector.ts +1 -1
  360. package/src/overlays/session-info-overlay.ts +137 -1
  361. package/src/overlays/session-selector.ts +807 -46
  362. package/src/overlays/settings-defs.ts +41 -0
  363. package/src/overlays/settings-selector.ts +585 -33
  364. package/src/overlays/show-images-selector.ts +18 -1
  365. package/src/overlays/snapcompact-shape-preview.ts +48 -3
  366. package/src/overlays/stats-notice.ts +31 -0
  367. package/src/overlays/theme-selector.ts +31 -1
  368. package/src/overlays/thinking-selector.ts +22 -1
  369. package/src/overlays/tree-selector.ts +570 -110
  370. package/src/overlays/usage-dashboard.ts +587 -12
  371. package/src/overlays/usage-row.ts +161 -25
  372. package/src/prompt/attachment-chips.ts +60 -0
  373. package/src/prompt/composer-attachments.ts +31 -9
  374. package/src/prompt/composer.ts +55 -2
  375. package/src/prompt/custom-editor.ts +302 -4
  376. package/src/prompt/editor-top-gap.ts +8 -0
  377. package/src/prompt/macos-spelling.ts +24 -9
  378. package/src/prompt/magic-keywords.ts +22 -1
  379. package/src/prompt/model-mention-autocomplete.ts +1 -0
  380. package/src/prompt/queued-messages.ts +101 -0
  381. package/src/prompt/skill-tokens.ts +5 -3
  382. package/src/prompt/welcome.ts +213 -6
  383. package/src/render/code-cell.ts +103 -2
  384. package/src/render/file-list.ts +29 -0
  385. package/src/render/hyperlink.ts +32 -0
  386. package/src/render/output-block.ts +110 -0
  387. package/src/render/output-pane.ts +37 -0
  388. package/src/render/tool-card.ts +137 -0
  389. package/src/render/tree-list.ts +39 -0
  390. package/src/render/width-aware-text.ts +20 -0
  391. package/src/setup/scenes/composer.ts +29 -0
  392. package/src/setup/scenes/glyph.ts +15 -0
  393. package/src/setup/scenes/model.ts +36 -8
  394. package/src/setup/scenes/outro.ts +28 -1
  395. package/src/setup/scenes/sign-in.ts +138 -24
  396. package/src/setup/scenes/splash.ts +28 -1
  397. package/src/setup/scenes/theme.ts +88 -3
  398. package/src/setup/scenes/types.ts +7 -28
  399. package/src/setup/startup-splash.ts +46 -9
  400. package/src/setup/wizard-overlay.ts +140 -23
  401. package/src/setup/wizard.ts +1 -1
  402. package/src/status-line/component.ts +414 -18
  403. package/src/status-line/context-usage.ts +279 -50
  404. package/src/status-line/footer.ts +94 -1
  405. package/src/status-line/metrics.ts +3 -6
  406. package/src/status-line/segments.ts +463 -97
  407. package/src/status-line/separators.ts +3 -16
  408. package/src/status-line/types.ts +43 -1
  409. package/src/stdin-buffer.ts +27 -21
  410. package/src/terminal-capabilities.ts +7 -0
  411. package/src/terminal.ts +153 -11
  412. package/src/theme/defaults/dark-neon-noir.json +100 -0
  413. package/src/theme/defaults/index.ts +2 -0
  414. package/src/theme/defaults/light-prism.json +5 -5
  415. package/src/theme/defaults/onyx.json +62 -62
  416. package/src/theme/mermaid-cache.ts +10 -1
  417. package/src/theme/schema.ts +15 -0
  418. package/src/theme/shimmer.ts +33 -0
  419. package/src/theme/theme-class.ts +24 -19
  420. package/src/theme/theme.ts +111 -9
  421. package/src/theme/tui-adapters.ts +14 -1
  422. package/src/tools/ask.ts +157 -1
  423. package/src/tools/ast-edit.ts +174 -1
  424. package/src/tools/ast-grep.ts +82 -2
  425. package/src/tools/autoresearch.ts +149 -1
  426. package/src/tools/bash.ts +233 -62
  427. package/src/tools/cfg-render.ts +66 -1
  428. package/src/tools/debug.ts +78 -1
  429. package/src/tools/default-renderer.ts +35 -1
  430. package/src/tools/edit.ts +357 -31
  431. package/src/tools/eval.ts +257 -1
  432. package/src/tools/fetch.ts +102 -3
  433. package/src/tools/find.ts +127 -1
  434. package/src/tools/github.ts +119 -10
  435. package/src/tools/glob.ts +116 -1
  436. package/src/tools/goal.ts +56 -1
  437. package/src/tools/grep.ts +83 -2
  438. package/src/tools/grouped-file-output.ts +205 -0
  439. package/src/tools/json-tree.ts +116 -1
  440. package/src/tools/lsp.ts +318 -1
  441. package/src/tools/mcp.ts +80 -1
  442. package/src/tools/memory.ts +93 -11
  443. package/src/tools/native-view.ts +292 -0
  444. package/src/tools/proc-render.ts +221 -1
  445. package/src/tools/read.ts +199 -4
  446. package/src/tools/renderer.ts +61 -0
  447. package/src/tools/report-tool-issue.ts +7 -1
  448. package/src/tools/resolve.ts +63 -1
  449. package/src/tools/streaming-output.ts +79 -16
  450. package/src/tools/task.ts +406 -4
  451. package/src/tools/think.ts +19 -1
  452. package/src/tools/todo.ts +89 -0
  453. package/src/tools/vibe.ts +230 -2
  454. package/src/tools/wait.ts +181 -1
  455. package/src/tools/web-search-types.ts +99 -0
  456. package/src/tools/web-search.ts +111 -170
  457. package/src/tools/write.ts +144 -5
  458. package/src/tools/xdev.ts +64 -1
  459. package/src/tui.ts +295 -4
  460. package/dist/types/setup/scenes/providers.d.ts +0 -3
  461. package/dist/types/setup/scenes/web-search.d.ts +0 -22
  462. package/src/setup/scenes/providers.ts +0 -110
  463. package/src/setup/scenes/web-search.ts +0 -165
@@ -13,18 +13,38 @@ import type { Theme } from "../theme/theme";
13
13
  import { ensureThemeSync, getThemeEpoch, theme } from "../theme/theme";
14
14
  import {
15
15
  type FirstResultViewportRepaint,
16
+ type NativeToolView,
17
+ type RenderResultContextOptions,
16
18
  type ToolActivitySummary,
17
19
  type ToolRenderer,
18
20
  toolRenderers,
19
21
  } from "../tools/index";
20
- import { BASH_DEFAULT_PREVIEW_LINES } from "../tools/bash";
21
- import { formatDefaultToolExecution } from "../tools/default-renderer";
22
+ import { describeDefaultToolExecution, formatDefaultToolExecution } from "../tools/default-renderer";
23
+ import { INTENT_FIELD, type TspCardStatus, type TspPreview, type TspText, type TspTone } from "@oh-my-pi/pi-wire";
24
+ import { card, col, EMPTY_NODE, node, span, text, withHidden } from "../native/describe";
25
+ import {
26
+ type DescribeContext,
27
+ type NativeChild,
28
+ type NativeNode,
29
+ type NativeUiEvent,
30
+ rootToggleExpanded,
31
+ } from "../native/node";
32
+ import { plainText } from "../native/spans";
33
+ import { diagnosticsSection, displayPath, toolHead } from "../tools/native-view";
34
+ import type { FileDiagnosticsResult } from "../tools/lsp";
35
+ import { NativeImageCache } from "../native/blobs";
36
+ import { Memo } from "../native/memo";
22
37
  import { type EditMode, type PerFileDiffPreview, renderStreamingFallback } from "../tools/edit";
23
- import { EVAL_DEFAULT_PREVIEW_LINES } from "../tools/eval";
24
38
  import { taskCardAgentIds } from "../tools/task";
25
39
  import { TODO_STRIKE_TOTAL_FRAMES, type TodoToolDetails } from "../tools/todo";
40
+ import { isNativeRendering } from "../native/state";
26
41
  import { isWaitingPollDetails } from "../tools/wait";
27
- import { formatStatusIcon, replaceTabs, resolveImageOptions } from "../render/render-utils";
42
+ import {
43
+ DEFAULT_TERMINAL_PREVIEW_LINES,
44
+ formatStatusIcon,
45
+ replaceTabs,
46
+ resolveImageOptions,
47
+ } from "../render/render-utils";
28
48
  import type { XdevMountedState } from "../tools/xdev";
29
49
  import { isFramedBlockComponent, markFramedBlockComponent, renderStatusLine, WidthAwareText } from "../render/index";
30
50
  import { cachedPngConversion, convertImageToPngShared, imagePayloadKey } from "./image-loading";
@@ -85,6 +105,17 @@ function resolveEditModeForTool(toolName: string, tool: AgentTool | undefined):
85
105
 
86
106
  type ToolRendererStage = "call" | "result";
87
107
 
108
+ /** A renderer's preview as the fallback card's clamp (`tail` becomes a head clamp of the same size). */
109
+ function cardPreview(preview: NativeToolView["preview"]): TspPreview | undefined {
110
+ if (preview === undefined) return { lines: DEFAULT_TERMINAL_PREVIEW_LINES };
111
+ if (preview === "none") return undefined;
112
+ if (preview !== "auto" && "tail" in preview) return { lines: preview.tail };
113
+ return preview;
114
+ }
115
+
116
+ /** The describe hooks of a registry renderer or of a custom tool object. */
117
+ type ToolDescriber = Pick<ToolRenderer, "describeCall" | "describeResult" | "mergeCallAndResult">;
118
+
88
119
  class SafeToolRendererComponent implements Component {
89
120
  #toolName: string;
90
121
  #stage: ToolRendererStage;
@@ -182,6 +213,15 @@ export interface ToolExecutionHandle extends Component {
182
213
  seal(): void;
183
214
  }
184
215
 
216
+ /** Card tone per status when the renderer doesn't choose one; settled cards stay neutral. */
217
+ const NATIVE_STATUS_TONE: Record<TspCardStatus, TspTone | undefined> = {
218
+ pending: "pending",
219
+ running: "pending",
220
+ done: undefined,
221
+ error: "error",
222
+ cancelled: "info",
223
+ };
224
+
185
225
  /** Phase-locked spinner glyph index shared by every live tool block so parallel
186
226
  * spinners advance in lockstep instead of each tracking its own start time. */
187
227
  export function sharedSpinnerFrame(frameCount: number, now: number = performance.now()): number {
@@ -198,8 +238,14 @@ let sharedSpinnerTimer: NodeJS.Timeout | undefined;
198
238
 
199
239
  /** Arm the shared spinner ticker if it is not already running. */
200
240
  function ensureSharedSpinnerTicker(): void {
201
- if (sharedSpinnerTimer) return;
241
+ if (sharedSpinnerTimer || isNativeRendering()) return;
202
242
  sharedSpinnerTimer = setInterval(() => {
243
+ // A native surface opened while blocks were live: the terminal clocks
244
+ // their spinners, so the repaint ticker has nothing left to do.
245
+ if (isNativeRendering()) {
246
+ stopSharedSpinnerTicker();
247
+ return;
248
+ }
203
249
  const frame = sharedSpinnerFrame(theme.spinnerFrames.length);
204
250
  // Removing the current block mid-iteration is safe on a Set.
205
251
  for (const block of liveSpinnerBlocks) block.tickSpinner(frame);
@@ -331,6 +377,13 @@ export class ToolExecutionComponent extends Container {
331
377
  // Execution start on the presentation clock (performance.now domain, the
332
378
  // same domain as AnimationFrame.now supplied by the transcript allocator).
333
379
  #executionStartedAtNow: number | undefined;
380
+ // When the call settled (final result or seal), same clock; freezes the native elapsed timer.
381
+ #executionEndedAtNow: number | undefined;
382
+ readonly #toolCallId: string | undefined;
383
+ readonly #native = new Memo();
384
+ // LSP diagnostics that arrived after the result (native only), shown in the frame.
385
+ #lateDiagnostics: readonly FileDiagnosticsResult[] | undefined;
386
+ readonly #nativeImages = new NativeImageCache();
334
387
  // Wall clock captured whenever a task card is rebuilt.
335
388
  #taskRenderNowMs = Date.now();
336
389
  // Set on each `render()` when the last painted pending shape must be
@@ -360,10 +413,11 @@ export class ToolExecutionComponent extends Container {
360
413
  tool: AgentTool | undefined,
361
414
  ui: ToolExecutionUi,
362
415
  _cwd: string = getProjectDir(),
363
- _toolCallId?: string,
416
+ toolCallId?: string,
364
417
  ) {
365
418
  super();
366
419
  ensureThemeSync();
420
+ this.#toolCallId = toolCallId;
367
421
  this.#toolName = toolName;
368
422
  this.#toolLabel = tool?.label ?? toolName;
369
423
  this.#renderer = options.useBuiltInRenderer === false ? undefined : toolRenderers[toolName];
@@ -522,6 +576,7 @@ export class ToolExecutionComponent extends Container {
522
576
  if (!isPartial) {
523
577
  this.#argsComplete = true;
524
578
  this.#previewReady?.resolve();
579
+ this.#executionEndedAtNow ??= performance.now();
525
580
  }
526
581
  this.#updateSpinnerAnimation();
527
582
  this.#updateTodoStrikeAnimation();
@@ -625,7 +680,9 @@ export class ToolExecutionComponent extends Container {
625
680
  this.#toolName !== "todo" &&
626
681
  !isBackgroundAsyncRunning &&
627
682
  (pendingCallConsumesSpinner || partialResultConsumesSpinner);
628
- const needsSpinner = isStreamingArgs || isLivePartialTool || this.#displaceableByToolName === "wait";
683
+ // TSP terminals clock spinners themselves; the frame counter is ANSI-only.
684
+ const needsSpinner =
685
+ !isNativeRendering() && (isStreamingArgs || isLivePartialTool || this.#displaceableByToolName === "wait");
629
686
  if (needsSpinner && !this.#spinnerActive) {
630
687
  const frameCount = theme.spinnerFrames.length;
631
688
  const frame = sharedSpinnerFrame(frameCount);
@@ -658,7 +715,8 @@ export class ToolExecutionComponent extends Container {
658
715
  }
659
716
 
660
717
  #updateTodoStrikeAnimation(): void {
661
- if (this.#toolName !== "todo" || this.#isPartial || this.#result?.isError) {
718
+ // TSP terminals draw the completion strike from the `del` token; no repaint ticks.
719
+ if (this.#toolName !== "todo" || this.#isPartial || this.#result?.isError || isNativeRendering()) {
662
720
  this.#stopTodoStrikeAnimation();
663
721
  return;
664
722
  }
@@ -738,6 +796,7 @@ export class ToolExecutionComponent extends Container {
738
796
  this.#sealed = true;
739
797
  this.#blockVersion++;
740
798
  this.#displaceableByToolName = undefined;
799
+ this.#executionEndedAtNow ??= performance.now();
741
800
  this.stopAnimation();
742
801
  this.#updateDisplay();
743
802
  this.#ui.requestRender();
@@ -791,6 +850,322 @@ export class ToolExecutionComponent extends Container {
791
850
  this.#updateDisplay();
792
851
  }
793
852
 
853
+ /** Mirrors the card collapsed or expanded in the terminal, so `Ctrl+O` and native toggles share one state. */
854
+ handleNativeEvent(event: NativeUiEvent): void {
855
+ const expanded = rootToggleExpanded(event);
856
+ if (expanded !== undefined) this.setExpanded(expanded);
857
+ }
858
+
859
+ /** The file paths this call targets (edit/write/apply_patch args), display-relative. */
860
+ #targetPaths(): string[] {
861
+ const args = this.#args;
862
+ if (!isRecord(args)) return [];
863
+ const out: string[] = [];
864
+ const add = (value: unknown) => {
865
+ if (typeof value === "string" && value) out.push(displayPath(value));
866
+ };
867
+ add(args.path);
868
+ add(args.file_path);
869
+ for (const key of ["edits", "files"] as const) {
870
+ const list = args[key];
871
+ if (Array.isArray(list)) for (const entry of list) if (isRecord(entry)) add(entry.path);
872
+ }
873
+ return out;
874
+ }
875
+
876
+ /** Whether this call targets `filePath` (the late-diagnostics router matches with it). */
877
+ matchesPath(filePath: string): boolean {
878
+ const wanted = displayPath(filePath);
879
+ return this.#targetPaths().includes(wanted);
880
+ }
881
+
882
+ /**
883
+ * Attaches LSP diagnostics that arrived after the result into this call's
884
+ * frame (§7.3: a diagnostics section plus a count chip in the head). Native
885
+ * rendering only; returns false when nothing matched, so the caller shows
886
+ * its standalone notice instead.
887
+ */
888
+ attachLateDiagnostics(files: readonly (FileDiagnosticsResult & { readonly path: string })[]): boolean {
889
+ if (!isNativeRendering()) return false;
890
+ const mine = files.filter(file => this.matchesPath(file.path));
891
+ if (mine.length === 0) return false;
892
+ this.#lateDiagnostics = mine;
893
+ this.#blockVersion++;
894
+ this.#displayInputVersion++;
895
+ this.invalidate();
896
+ return true;
897
+ }
898
+
899
+ /**
900
+ * The tool card: `card` role `omp.tool.<name>` with the renderer's
901
+ * {@link NativeToolView} (head spans, body nodes), a status chip, an
902
+ * `elapsed` timer in the head and terminal-local collapse clamped to the
903
+ * view's preview. Renderers without describe hooks (and extension tools
904
+ * with only render hooks) get the generic card. Hidden tool activity stays
905
+ * mounted so toggling it is one prop change.
906
+ */
907
+ override describe(cx?: DescribeContext): NativeNode {
908
+ if (this.#toolName === "wait" && this.#isBenignSkip()) return EMPTY_NODE;
909
+ const dataFirst = cx?.supports("tool") === true;
910
+ const key = [
911
+ dataFirst,
912
+ this.#resultVersion,
913
+ this.#expanded,
914
+ this.#isPartial,
915
+ this.#argsComplete,
916
+ this.#executionStarted,
917
+ this.#sealed,
918
+ this.#parkedBackground,
919
+ this.#showImages,
920
+ this.#displayInputVersion,
921
+ this.#toolActivityVisible,
922
+ getThemeEpoch(),
923
+ ];
924
+ return this.#native.get(key, () =>
925
+ withHidden(dataFirst ? this.#describeTool() : this.#describeCard(), !this.#toolActivityVisible),
926
+ );
927
+ }
928
+
929
+ /** Milliseconds since execution started (running) or its total (settled); undefined before it starts. */
930
+ #elapsedMs(status: TspCardStatus): number | undefined {
931
+ const started = this.#executionStartedAtNow;
932
+ if (started === undefined) return undefined;
933
+ const ended = status === "running" ? undefined : this.#executionEndedAtNow;
934
+ return Math.max(0, Math.round((ended ?? performance.now()) - started));
935
+ }
936
+
937
+ /** The late diagnostics section and head chip, when LSP reported after the result. */
938
+ #lateDiagnosticsParts(): { section?: NativeNode; chip?: { text: string; tone: TspTone } } {
939
+ const files = this.#lateDiagnostics;
940
+ if (!files || files.length === 0) return {};
941
+ const sections = files.map(file => diagnosticsSection(file)).filter((n): n is NativeNode => n !== undefined);
942
+ const count = files.reduce((sum, file) => sum + file.messages.length, 0);
943
+ const errored = files.some(file => file.errored);
944
+ const section =
945
+ sections.length === 1
946
+ ? sections[0]
947
+ : node("col", { gap: "sm", role: "omp.tool.diagnostics" }, sections, "late");
948
+ return {
949
+ section,
950
+ chip: { text: count === 1 ? "1 diagnostic" : `${count} diagnostics`, tone: errored ? "error" : "warning" },
951
+ };
952
+ }
953
+
954
+ /**
955
+ * The data-first `tool` node (§7.2): the renderer's head data, status,
956
+ * timer, exit and intent as props; its body as borderless children.
957
+ */
958
+ #describeTool(): NativeNode {
959
+ const view = this.#nativeView();
960
+ const status = this.#nativeStatus();
961
+ const head = view.tool;
962
+ const late = this.#lateDiagnosticsParts();
963
+ const body: NativeChild[] = [...(view.body ?? []), ...this.#nativeResultImages()];
964
+ if (late.section) body.push(late.section);
965
+ const inline = view.inline === true;
966
+ const hasBody = body.length > 0;
967
+ const ms = this.#elapsedMs(status);
968
+ const live = status === "running" || status === "pending";
969
+ const intent = isRecord(this.#args) ? this.#args[INTENT_FIELD] : undefined;
970
+ const badges = [...(head?.badges ?? []), ...(late.chip ? [late.chip] : [])];
971
+ const title: TspText = head?.title ?? (head ? this.#toolLabel : (view.head ?? this.#toolLabel));
972
+ const preview = view.preview ?? (inline ? "none" : { lines: DEFAULT_TERMINAL_PREVIEW_LINES });
973
+ return node(
974
+ "tool",
975
+ {
976
+ role: `omp.tool.${this.#toolName}`,
977
+ key: this.#toolCallId,
978
+ name: this.#toolName,
979
+ // `title` here is the head verb (TspToolProps), not the common tooltip.
980
+ title: title as string,
981
+ target: head?.target,
982
+ targetKind: head?.targetKind,
983
+ lang: head?.lang,
984
+ href: head?.href,
985
+ meta: head?.meta,
986
+ badges: badges.length > 0 ? badges : undefined,
987
+ note: head?.note ?? (status === "cancelled" && !this.#isBenignSkip() ? "cancelled" : undefined),
988
+ exit: head?.exit ?? undefined,
989
+ status,
990
+ age: live ? ms : undefined,
991
+ took: live ? undefined : ms,
992
+ intent: typeof intent === "string" && intent ? plainText(intent) : undefined,
993
+ frame: inline ? "inline" : "card",
994
+ collapsible: hasBody,
995
+ collapsed: hasBody ? !this.#expanded : undefined,
996
+ preview: hasBody ? (preview === "auto" ? { lines: DEFAULT_TERMINAL_PREVIEW_LINES } : preview) : undefined,
997
+ tools: view.tools,
998
+ tone: view.tone,
999
+ },
1000
+ body,
1001
+ );
1002
+ }
1003
+
1004
+ /** The fallback card head: the renderer's spans, else spans from its data head. */
1005
+ #fallbackHead(view: NativeToolView): NativeToolView["head"] {
1006
+ if (view.head !== undefined) return view.head;
1007
+ const head = view.tool;
1008
+ if (!head) return undefined;
1009
+ const title =
1010
+ typeof head.title === "string"
1011
+ ? head.title
1012
+ : head.title
1013
+ ? plainText(head.title.map(s => s.t).join(""))
1014
+ : this.#toolLabel;
1015
+ const target = typeof head.target === "string" ? span(head.target, "muted") : head.target?.map(s => ({ ...s }));
1016
+ const meta = (head.meta ?? []).map(m => (typeof m === "string" ? m : m.map(s => s.t).join("")));
1017
+ const spans = toolHead(title, ...(Array.isArray(target) ? target : [target]), ...meta);
1018
+ if (head.exit) spans.push(span(" "), span(`exit ${head.exit}`, "error"));
1019
+ if (head.note)
1020
+ spans.push(
1021
+ span(" "),
1022
+ span(typeof head.note === "string" ? head.note : head.note.map(s => s.t).join(""), "muted"),
1023
+ );
1024
+ return spans;
1025
+ }
1026
+
1027
+ #describeCard(): NativeNode {
1028
+ const view = this.#nativeView();
1029
+ const status = this.#nativeStatus();
1030
+ const late = this.#lateDiagnosticsParts();
1031
+ const fallbackHead = this.#fallbackHead(view);
1032
+ const headChildren: NativeChild[] = [
1033
+ // One line; paths drop whole directories first (path-aware middle cut), the full head in the tooltip.
1034
+ text(fallbackHead ?? [span(this.#toolLabel, "toolTitle")], {
1035
+ lines: 1,
1036
+ truncate: "middle",
1037
+ grow: 1,
1038
+ title: typeof fallbackHead === "string" ? fallbackHead : fallbackHead?.map(s => s.t).join(""),
1039
+ }),
1040
+ ];
1041
+ const started = this.#executionStartedAtNow;
1042
+ if (started !== undefined) {
1043
+ const ended = status === "running" ? undefined : this.#executionEndedAtNow;
1044
+ const took = Math.max(0, Math.round((ended ?? performance.now()) - started));
1045
+ headChildren.push(node("elapsed", ended === undefined ? { age: took } : { age: took, stopped: took }));
1046
+ }
1047
+ const children: NativeChild[] = [
1048
+ node("row", { gap: "sm", align: "baseline" }, headChildren, "head"),
1049
+ ...(view.body ?? []),
1050
+ ...this.#nativeResultImages(),
1051
+ ...(late.section ? [late.section] : []),
1052
+ ];
1053
+ const role = `omp.tool.${this.#toolName}`;
1054
+ if (view.inline) return col(children, { role });
1055
+ const hasBody = children.length > 1;
1056
+ return card(
1057
+ {
1058
+ role,
1059
+ key: this.#toolCallId,
1060
+ tone: view.tone ?? NATIVE_STATUS_TONE[status],
1061
+ status,
1062
+ collapsible: hasBody,
1063
+ collapsed: hasBody ? !this.#expanded : undefined,
1064
+ preview: hasBody ? cardPreview(view.preview) : undefined,
1065
+ },
1066
+ children,
1067
+ );
1068
+ }
1069
+
1070
+ #nativeStatus(): TspCardStatus {
1071
+ if (this.#isBenignSkip()) return "cancelled";
1072
+ if (this.#result !== undefined && !this.#isPartial) return this.#result.isError ? "error" : "done";
1073
+ if (this.#sealed) return "cancelled";
1074
+ return this.#result !== undefined || this.#executionStarted ? "running" : "pending";
1075
+ }
1076
+
1077
+ /**
1078
+ * The renderer's semantic view: the call view until a result exists, then
1079
+ * the result view (merged renderers) or the call head over both bodies.
1080
+ * A renderer that throws falls back to the generic card.
1081
+ */
1082
+ #nativeView(): NativeToolView {
1083
+ const options: RenderResultContextOptions = {
1084
+ expanded: this.#expanded,
1085
+ isPartial: this.#isPartial,
1086
+ argsComplete: this.#argsComplete,
1087
+ executionStarted: this.#executionStarted,
1088
+ renderContext: this.#buildRenderContext(),
1089
+ };
1090
+ // Custom tools (MCP, extensions) carry their own describe hooks, mirroring
1091
+ // the ANSI path's preference for the tool's renderCall/renderResult.
1092
+ const toolDescriber = this.#tool as ToolDescriber | undefined;
1093
+ const renderer: ToolDescriber | undefined =
1094
+ toolDescriber?.describeCall || toolDescriber?.describeResult ? toolDescriber : this.#renderer;
1095
+ if (this.#isBenignSkip() || !renderer || (!renderer.describeCall && !renderer.describeResult)) {
1096
+ return this.#defaultNativeView(options);
1097
+ }
1098
+ try {
1099
+ const args = this.#getCallArgsForRender();
1100
+ const result = this.#result;
1101
+ const merged = renderer.mergeCallAndResult === true;
1102
+ const resultView = result
1103
+ ? renderer.describeResult?.(
1104
+ { content: result.content, details: result.details, isError: result.isError },
1105
+ options,
1106
+ args,
1107
+ )
1108
+ : undefined;
1109
+ const callView = !result || !merged || !resultView ? renderer.describeCall?.(args, options) : undefined;
1110
+ if (merged || !callView || !resultView) {
1111
+ return resultView ?? callView ?? { head: [span(this.#toolLabel, "toolTitle")] };
1112
+ }
1113
+ return {
1114
+ tool: callView.tool || resultView.tool ? { ...callView.tool, ...resultView.tool } : undefined,
1115
+ head: callView.head ?? resultView.head,
1116
+ body: [...(callView.body ?? []), ...(resultView.body ?? [])],
1117
+ tone: resultView.tone ?? callView.tone,
1118
+ preview: resultView.preview ?? callView.preview,
1119
+ inline: resultView.inline ?? callView.inline,
1120
+ tools: resultView.tools ?? callView.tools,
1121
+ };
1122
+ } catch (err) {
1123
+ logger.warn("Tool describe failed", { tool: this.#toolName, error: String(err) });
1124
+ return this.#defaultNativeView(options);
1125
+ }
1126
+ }
1127
+
1128
+ #defaultNativeView(options: RenderResultContextOptions): NativeToolView {
1129
+ const result = this.#result;
1130
+ return describeDefaultToolExecution({
1131
+ label: this.#toolLabel,
1132
+ args: this.#args,
1133
+ result: result
1134
+ ? { output: this.#nativeTextOutput(), isError: result.isError, skipped: this.#isBenignSkip() }
1135
+ : undefined,
1136
+ options,
1137
+ });
1138
+ }
1139
+
1140
+ /** Result text for the generic card; images are native nodes unless images are hidden. */
1141
+ #nativeTextOutput(): string {
1142
+ const result = this.#result;
1143
+ if (!result) return "";
1144
+ const output = result.content
1145
+ .filter(block => block.type === "text")
1146
+ .map(block => sanitizeText(block.text || ""))
1147
+ .join("\n");
1148
+ if (this.#showImages) return output;
1149
+ const indicators = this.#getAllImageBlocks()
1150
+ .map(image => {
1151
+ const mimeType = String(image.mimeType);
1152
+ const dims = image.data ? (getImageDimensions(image.data, mimeType) ?? undefined) : undefined;
1153
+ return imageFallback(mimeType, dims);
1154
+ })
1155
+ .join("\n");
1156
+ return indicators ? (output ? `${output}\n${indicators}` : indicators) : output;
1157
+ }
1158
+
1159
+ #nativeResultImages(): NativeNode[] {
1160
+ if (!this.#result || !this.#showImages) return [];
1161
+ const images: NativeNode[] = [];
1162
+ this.#getAllImageBlocks().forEach((image, index) => {
1163
+ if (image.data && image.mimeType)
1164
+ images.push(this.#nativeImages.get(`img${index}`, image.data, image.mimeType));
1165
+ });
1166
+ return images;
1167
+ }
1168
+
794
1169
  /** Apply the transcript allocator's current viewport reservation. */
795
1170
  setTranscriptAllocation(rows: number, frame: AnimationFrame): void {
796
1171
  this.#allocation = Math.max(0, Math.trunc(rows));
@@ -1295,13 +1670,13 @@ export class ToolExecutionComponent extends Container {
1295
1670
  context.output = output;
1296
1671
  }
1297
1672
  context.expanded = this.#expanded;
1298
- context.previewLines = BASH_DEFAULT_PREVIEW_LINES;
1673
+ context.previewLines = DEFAULT_TERMINAL_PREVIEW_LINES;
1299
1674
  context.timeout = normalizeTimeoutSeconds(isRecord(this.#args) ? this.#args.timeout : undefined, 3600);
1300
1675
  } else if (this.#toolName === "eval" && this.#result) {
1301
1676
  const output = this.#getTextOutput().trimEnd();
1302
1677
  context.output = output;
1303
1678
  context.expanded = this.#expanded;
1304
- context.previewLines = EVAL_DEFAULT_PREVIEW_LINES;
1679
+ context.previewLines = DEFAULT_TERMINAL_PREVIEW_LINES;
1305
1680
  } else if (this.#toolName === "task") {
1306
1681
  // Once a result snapshot exists the task renderer's `renderResult`
1307
1682
  // draws every dispatched agent as a progress/result line, so tell
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Pointer actions on transcript blocks in a native terminal (a user message's
3
+ * hover toolbar, an error frame's action row). Blocks describe the controls;
4
+ * the interactive host registers the one handler that runs omp's existing
5
+ * commands for them, so a click and the key binding take the same path.
6
+ */
7
+ export type TranscriptAction =
8
+ /** Retry the last failed turn (`app.retry`). */
9
+ | { readonly act: "retry" }
10
+ /** Open the temporary model picker (`app.model.selectTemporary`). */
11
+ | { readonly act: "switch-model" }
12
+ /** Open the transcript rewind selector. */
13
+ | { readonly act: "rewind" }
14
+ /** Resume the session stored at `path` (a welcome card's recent session). */
15
+ | { readonly act: "resume"; readonly path: string }
16
+ /** Put `text` on the clipboard. */
17
+ | { readonly act: "copy"; readonly text: string };
18
+
19
+ type Handler = (action: TranscriptAction) => void;
20
+
21
+ let handler: Handler | undefined;
22
+
23
+ /** Register (or clear, with `undefined`) the host's handler. */
24
+ export function setTranscriptActionHandler(next: Handler | undefined): void {
25
+ handler = next;
26
+ }
27
+
28
+ /** Run `action` through the host; false when no host handles actions. */
29
+ export function runTranscriptAction(action: TranscriptAction): boolean {
30
+ if (!handler) return false;
31
+ handler(action);
32
+ return true;
33
+ }
34
+
35
+ /** Whether a host handles actions (controls are only described when one does). */
36
+ export function hasTranscriptActions(): boolean {
37
+ return handler !== undefined;
38
+ }
@@ -3,6 +3,7 @@ import { type ScrollRangeAnchor, ScrollView } from "../components/scroll-view";
3
3
  import { DynamicBorder } from "../chrome/dynamic-border";
4
4
  import { truncateToWidth } from "../utils";
5
5
  import { theme } from "../theme/theme";
6
+ import { sameItems } from "../native/memo";
6
7
  import {
7
8
  composeOutlineColumn,
8
9
  type ComposedColumn,
@@ -73,14 +74,6 @@ interface BrowserLayout {
73
74
  bodyHeight: number;
74
75
  }
75
76
 
76
- function sameLines(left: readonly string[], right: readonly string[]): boolean {
77
- if (left.length !== right.length) return false;
78
- for (let index = 0; index < left.length; index++) {
79
- if (left[index] !== right[index]) return false;
80
- }
81
- return true;
82
- }
83
-
84
77
  /** Nearest visible target, preferring the preceding item just like the legacy selectors. */
85
78
  export function nearestVisibleOutlineTarget(visible: readonly boolean[], selected: number): number {
86
79
  if (selected < 0 || selected >= visible.length) return visible.findIndex(Boolean);
@@ -250,7 +243,7 @@ export class TranscriptBrowser implements Component {
250
243
  for (const line of frame.footer) output.push(this.#chromeLine(line, frameWidth));
251
244
  output.push(...border);
252
245
  const cached = this.#cachedLines;
253
- if (cached && sameLines(cached, output)) return cached;
246
+ if (cached && sameItems(cached, output)) return cached;
254
247
  this.#cachedLines = output;
255
248
  return output;
256
249
  }
@@ -1,6 +1,6 @@
1
1
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
2
  import { createCustomMessage } from "@oh-my-pi/pi-agent-core/compaction/messages";
3
- import type { MessageAttribution } from "@oh-my-pi/pi-ai";
3
+ import type { ImageContent, MessageAttribution, TextContent } from "@oh-my-pi/pi-ai";
4
4
  import {
5
5
  type CustomMessage,
6
6
  type CustomMessageContent,
@@ -106,6 +106,15 @@ export function textContent(content: string | ReadonlyArray<{ type: string; text
106
106
  return text;
107
107
  }
108
108
 
109
+ /** Well-formed image blocks in order: a `[Image #N]` marker in the text names the Nth. */
110
+ export function imageContent(content: string | ReadonlyArray<TextContent | ImageContent>): ImageContent[] {
111
+ if (typeof content === "string") return [];
112
+ return content.filter(
113
+ (block): block is ImageContent =>
114
+ block.type === "image" && typeof block.data === "string" && typeof block.mimeType === "string",
115
+ );
116
+ }
117
+
109
118
  /** Join text blocks with spaces and collapse whitespace into a single-line label. */
110
119
  export function userMessageLabel(content: string | ReadonlyArray<{ type: string; text?: string }>): string {
111
120
  return textContent(content, " ").replace(/\s+/g, " ").trim();
@@ -62,18 +62,18 @@ export function buildAsyncResultBlock(message: CustomOrHookMessage): ToolActivit
62
62
  const duration = typeof job.durationMs === "number" ? formatDuration(job.durationMs) : undefined;
63
63
  rows.push({
64
64
  parts: [
65
- theme.fg("success", `${theme.status.done} Background job completed`),
66
- theme.fg("dim", typeLabel),
67
- theme.fg("accent", jobId),
68
- duration ? theme.fg("dim", `(${duration})`) : undefined,
65
+ { text: `${theme.status.done} Background job completed`, color: "success" },
66
+ { text: typeLabel, color: "dim" },
67
+ { text: jobId, color: "accent" },
68
+ duration ? { text: `(${duration})`, color: "dim" } : undefined,
69
69
  ],
70
70
  });
71
71
  if (job.meta?.artifactError) {
72
- rows.push({ parts: [theme.fg("warning", formatArtifactErrorNotice(job.meta.artifactError))] });
72
+ rows.push({ parts: [{ text: formatArtifactErrorNotice(job.meta.artifactError), color: "warning" }] });
73
73
  }
74
74
  }
75
75
  if (details?.meta?.artifactError) {
76
- rows.push({ parts: [theme.fg("warning", formatArtifactErrorNotice(details.meta.artifactError))] });
76
+ rows.push({ parts: [{ text: formatArtifactErrorNotice(details.meta.artifactError), color: "warning" }] });
77
77
  }
78
78
  return new ToolActivityContainer(new TranscriptStatusBlock(rows));
79
79
  }
@@ -88,7 +88,7 @@ export function buildLaunchCompletionBlock(message: CustomOrHookMessage): ToolAc
88
88
  const rows: TranscriptStatusRow[] = [];
89
89
  const daemons = details?.daemons ?? [];
90
90
  if (daemons.length === 0 && typeof message.content === "string") {
91
- rows.push({ parts: [theme.fg("dim", `${theme.status.done} ${message.content}`)] });
91
+ rows.push({ parts: [{ text: `${theme.status.done} ${message.content}`, color: "dim" }] });
92
92
  }
93
93
  for (const daemon of daemons) {
94
94
  const failed = daemon.state === "failed" || (daemon.exitCode !== undefined && daemon.exitCode !== 0);
@@ -99,11 +99,11 @@ export function buildLaunchCompletionBlock(message: CustomOrHookMessage): ToolAc
99
99
  rows.push({
100
100
  parts: [
101
101
  failed
102
- ? theme.fg("error", `${theme.status.error} Supervised process failed`)
103
- : theme.fg("success", `${theme.status.done} Supervised process completed`),
104
- theme.fg("accent", daemon.name),
105
- daemon.exitCode !== undefined ? theme.fg("dim", `(exit ${daemon.exitCode})`) : undefined,
106
- duration ? theme.fg("dim", `(${duration})`) : undefined,
102
+ ? { text: `${theme.status.error} Supervised process failed`, color: "error" }
103
+ : { text: `${theme.status.done} Supervised process completed`, color: "success" },
104
+ { text: daemon.name, color: "accent" },
105
+ daemon.exitCode !== undefined ? { text: `(exit ${daemon.exitCode})`, color: "dim" } : undefined,
106
+ duration ? { text: `(${duration})`, color: "dim" } : undefined,
107
107
  ],
108
108
  });
109
109
  }
@@ -173,9 +173,10 @@ export function buildFileMentionBlock(files: FileMentionMessage["files"], indent
173
173
  }
174
174
  rows.push({
175
175
  parts: [
176
- `${theme.fg("dim", `${theme.tree.last} `)}${theme.fg("muted", "Read")}`,
177
- theme.fg("accent", file.path),
178
- theme.fg("dim", suffix),
176
+ { text: theme.tree.last, color: "dim", rowsOnly: true },
177
+ { text: "Read", color: "muted" },
178
+ { text: file.path, color: "accent" },
179
+ { text: suffix, color: "dim" },
179
180
  ],
180
181
  indent,
181
182
  });