@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
@@ -1,8 +1,14 @@
1
1
  import { Text } from "../components/text";
2
2
  import { type Component, Container } from "../tui";
3
- import { MessageNoticeComponent, type MessageNoticePresentation } from "../chrome/message-notice";
3
+ import {
4
+ MessageNoticeComponent,
5
+ type MessageNoticeNativePresentation,
6
+ type MessageNoticePresentation,
7
+ } from "../chrome/message-notice";
4
8
  import { theme } from "../theme";
5
9
  import { expandKeyHint } from "../render/render-utils";
10
+ import { span, text } from "../native/describe";
11
+ import type { NativeNode } from "../native/node";
6
12
 
7
13
  /** Rule fields shown in rewind notifications. */
8
14
  export interface NotificationRule {
@@ -30,6 +36,8 @@ export class TtsrNotificationComponent extends Container {
30
36
  this.#rules = [...rules];
31
37
  this.#notice = new MessageNoticeComponent({
32
38
  presentation: context => this.#presentation(context.expanded),
39
+ nativePresentation: () => this.#nativePresentation(),
40
+ role: "omp.notice.ttsr",
33
41
  });
34
42
  this.addChild(this.#notice);
35
43
  }
@@ -57,6 +65,35 @@ export class TtsrNotificationComponent extends Container {
57
65
  return this.#notice.isExpanded();
58
66
  }
59
67
 
68
+ /**
69
+ * An inline warning notice, "Rule applied: no-unwrap", with every rule's
70
+ * full description as the disclosure below it.
71
+ */
72
+ #nativePresentation(): MessageNoticeNativePresentation {
73
+ const single = this.#rules.length === 1 ? this.#rules[0] : undefined;
74
+ const head = single
75
+ ? [span("Rule applied: ", "warning"), span(single.name, "mono strong")]
76
+ : [
77
+ span(`${this.#rules.length} rules applied: `, "warning"),
78
+ span(this.#rules.map(rule => rule.name).join(", "), "mono strong"),
79
+ ];
80
+ const body: NativeNode[] = [];
81
+ for (const rule of this.#rules) {
82
+ const desc = (rule.description || rule.content)?.trim();
83
+ if (single) {
84
+ if (desc) body.push(text([span(desc, "em")], { wrap: "word", key: rule.name }));
85
+ continue;
86
+ }
87
+ body.push(
88
+ text(desc ? [span(rule.name, "strong"), span(": "), span(desc, "em")] : [span(rule.name, "strong")], {
89
+ wrap: "word",
90
+ key: rule.name,
91
+ }),
92
+ );
93
+ }
94
+ return { head, body, inline: { icon: "shield-alert" } };
95
+ }
96
+
60
97
  #presentation(expanded: boolean): MessageNoticePresentation {
61
98
  // fg colors conflict with inverse, so styling inside the block is limited
62
99
  // to bold (names) and italic (descriptions).
@@ -3,6 +3,8 @@ import { type Component, Container } from "../tui";
3
3
  import { Disclosure } from "../components/disclosure";
4
4
  import { Markdown } from "../components/markdown";
5
5
  import { formatBytes } from "@oh-my-pi/pi-utils";
6
+ import type { ImageContent } from "@oh-my-pi/pi-ai";
7
+ import type { TspSpan } from "@oh-my-pi/pi-wire";
6
8
  import { ensureThemeSync, getMarkdownTheme, theme } from "../theme";
7
9
  import {
8
10
  attachmentSgr,
@@ -19,6 +21,11 @@ import { expandKeyHint, fileHyperlink } from "../render";
19
21
  import { imageReferenceHyperlink } from "../prompt/image-references";
20
22
  import { highlightMagicKeywords } from "../prompt/magic-keywords";
21
23
  import type { ReactionTarget } from "./reaction";
24
+ import { card, md, node, row, span, text } from "../native/describe";
25
+ import { base64ImageNode } from "../native/blobs";
26
+ import { hasTranscriptActions, runTranscriptAction } from "./transcript-actions";
27
+ import { type NativeChild, type NativeNode, type NativeUiEvent, rootToggleExpanded } from "../native/node";
28
+ import { Memo } from "../native/memo";
22
29
 
23
30
  // OSC 133 shell integration: marks prompt zones for terminal multiplexers.
24
31
  //
@@ -46,12 +53,16 @@ const OSC133_ZONE_CLOSE = OSC133_ZONE_END + OSC133_COMMAND_START + OSC133_COMMAN
46
53
  export interface UserBubbleOptions {
47
54
  /** Materialized `file://` targets per attached image, indexed by chip number. */
48
55
  imageLinks?: readonly (string | undefined)[];
56
+ /** The message's attached images in chip order (`#1` first); a native bubble shows them. */
57
+ images?: readonly ImageContent[];
49
58
  /** Agent-attributed input: dim, flat prose. */
50
59
  synthetic?: boolean;
51
60
  /** Delivered into the response that was streaming; marked `*` at the bubble's top-left. */
52
61
  liveSteered?: boolean;
53
62
  /** SKILL.md path for a skill chip by name; `undefined` leaves the chip unlinked. */
54
63
  skillPath?: (name: string) => string | undefined;
64
+ /** When the message was sent (ms); shown beside the native hover toolbar. */
65
+ timestamp?: number;
55
66
  }
56
67
 
57
68
  /**
@@ -116,7 +127,16 @@ export class UserMessageComponent extends Container implements ReactionTarget {
116
127
  #zoneLines: string[] | undefined;
117
128
  readonly #bgColor: (value: string) => string;
118
129
  readonly #liveSteered: boolean;
130
+ readonly #synthetic: boolean;
131
+ readonly #timestamp: number | undefined;
132
+ readonly #images: readonly ImageContent[];
133
+ readonly #imageLinks: readonly (string | undefined)[] | undefined;
134
+ /** Display text: image markers collapsed to chips and model mentions to their labels. */
135
+ readonly #text: string;
136
+ /** Matches the composer tokens in {@link #text} (chips, skills, this message's mentions). */
137
+ readonly #tokens: RegExp;
119
138
  #reaction: string | undefined;
139
+ #native: NativeNode | undefined;
120
140
 
121
141
  constructor(text: string, options: UserBubbleOptions = {}) {
122
142
  super();
@@ -135,18 +155,107 @@ export class UserMessageComponent extends Container implements ReactionTarget {
135
155
  const bgColor = (value: string) => theme.bg("userMessageBg", value);
136
156
  this.#bgColor = bgColor;
137
157
  this.#liveSteered = options.liveSteered === true;
138
- const md = new Markdown(text, 1, 1, getMarkdownTheme(), {
158
+ this.#synthetic = options.synthetic === true;
159
+ this.#timestamp = options.timestamp;
160
+ this.#images = options.images ?? [];
161
+ this.#imageLinks = options.imageLinks;
162
+ this.#text = text;
163
+ this.#tokens = composerTokenRegex(mentionLabels);
164
+ const markdown = new Markdown(text, 1, 1, getMarkdownTheme(), {
139
165
  bgColor,
140
- color: userBubbleColor(options, composerTokenRegex(mentionLabels)),
166
+ color: userBubbleColor(options, this.#tokens),
141
167
  });
142
- md.setIgnoreTight(true);
143
- this.addChild(md);
168
+ markdown.setIgnoreTight(true);
169
+ this.addChild(markdown);
144
170
  }
145
171
 
146
172
  setReaction(emoji: string): void {
147
173
  if (this.#reaction === emoji) return;
148
174
  this.#reaction = emoji;
149
175
  this.#zoneLines = undefined;
176
+ this.#native = undefined;
177
+ }
178
+
179
+ /**
180
+ * A head-less user-toned frame with the prompt as markdown under its
181
+ * attached images (titled `#N` like their chips, opening the materialized
182
+ * file on click): the fill says "you". A quiet toolbar (time, Copy, Rewind) fades in on hover when the
183
+ * host handles transcript actions. The live-steering marker and the
184
+ * agent's reaction are chips at the bottom-right, so a reaction landing
185
+ * later updates the frame in place, even deep in scrollback.
186
+ */
187
+ override describe(): NativeNode {
188
+ if (this.#native) return this.#native;
189
+ const children: NativeChild[] = [];
190
+ if (!this.#synthetic && hasTranscriptActions()) {
191
+ const tools: NativeChild[] = [];
192
+ if (this.#timestamp !== undefined) {
193
+ const at = new Date(this.#timestamp);
194
+ tools.push(
195
+ text([span(at.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), "dim mono")], {
196
+ role: "omp.user.time",
197
+ title: at.toLocaleString(),
198
+ }),
199
+ );
200
+ }
201
+ tools.push(
202
+ // `copy-message`, not Tern's local `copy` (that would copy the label).
203
+ text("Copy", {
204
+ role: "omp.user.tool",
205
+ actions: { click: "copy-message" },
206
+ title: "Copy message",
207
+ key: "copy",
208
+ }),
209
+ text("Rewind", {
210
+ role: "omp.user.tool",
211
+ actions: { click: "rewind" },
212
+ title: "Rewind the conversation to an earlier message",
213
+ key: "rewind",
214
+ }),
215
+ );
216
+ children.push(node("row", { gap: "xs", role: "omp.user.tools" }, tools, "tools"));
217
+ }
218
+ // Videos keep their chip only: the native image node decodes stills.
219
+ const thumbs: NativeNode[] = [];
220
+ if (!this.#synthetic) {
221
+ this.#images.forEach((image, i) => {
222
+ if (!image.mimeType.startsWith("image/")) return;
223
+ const label = `#${i + 1}`;
224
+ const link = this.#imageLinks?.[i];
225
+ const open = link ? { href: link, actions: { click: "open" } } : {};
226
+ thumbs.push(base64ImageNode(image.data, image.mimeType, { alt: label, title: label, ...open }, label));
227
+ });
228
+ }
229
+ if (thumbs.length > 0) {
230
+ children.push(row(thumbs, { gap: "sm", wrap: true, align: "start", role: "omp.user.images" }));
231
+ }
232
+ const marks = this.#synthetic ? [] : tokenMarks(this.#text, this.#tokens);
233
+ children.push(md(this.#text, marks.length > 0 ? { marks } : undefined));
234
+ const badges: NativeChild[] = [];
235
+ if (this.#liveSteered) {
236
+ badges.push(
237
+ node("badge", {
238
+ text: "steered",
239
+ tone: "accent",
240
+ title: "Delivered into the response that was streaming",
241
+ }),
242
+ );
243
+ }
244
+ if (this.#reaction !== undefined) badges.push(node("badge", { text: this.#reaction, role: "omp.reaction" }));
245
+ if (badges.length > 0)
246
+ children.push(node("row", { gap: "xs", justify: "end", role: "omp.user.badges" }, badges, "badges"));
247
+ this.#native = card(
248
+ { role: this.#synthetic ? "omp.user.synthetic" : "omp.user", tone: this.#synthetic ? "muted" : "user" },
249
+ children,
250
+ );
251
+ return this.#native;
252
+ }
253
+
254
+ /** Hover toolbar clicks: omp's own copy and rewind commands. */
255
+ handleNativeEvent(event: NativeUiEvent): void {
256
+ if (event.type !== "action") return;
257
+ if (event.act === "copy-message") runTranscriptAction({ act: "copy", text: this.#text });
258
+ else if (event.act === "rewind") runTranscriptAction({ act: "rewind" });
150
259
  }
151
260
 
152
261
  /**
@@ -178,6 +287,31 @@ export class UserMessageComponent extends Container implements ReactionTarget {
178
287
  }
179
288
  }
180
289
 
290
+ /**
291
+ * The composer tokens in `text` as `md` marks, styled as the composer
292
+ * decorates them (`CustomEditor.describeDecorations`), so a sent prompt keeps
293
+ * its chips highlighted.
294
+ */
295
+ function tokenMarks(text: string, tokens: RegExp): TspSpan[] {
296
+ // One mark per distinct token: Tern styles every occurrence of its text.
297
+ const styles = new Map<string, string>();
298
+ const mark = (s: string) => (label: string) => {
299
+ styles.set(label, s);
300
+ return "";
301
+ };
302
+ renderPlaceholders(
303
+ text,
304
+ {
305
+ renderText: () => "",
306
+ renderSkill: mark("customMessageLabel strong"),
307
+ renderMention: mark("statusLineModel strong"),
308
+ renderReference: mark("accent strong"),
309
+ },
310
+ tokens,
311
+ );
312
+ return [...styles].map(([t, s]) => ({ t, s }));
313
+ }
314
+
181
315
  /**
182
316
  * Always-visible dim summary row for a collapsed synthetic input. Kept as a
183
317
  * small domain renderer so the width-truncated label never pays Markdown
@@ -220,6 +354,8 @@ class SyntheticSummary implements Component {
220
354
  */
221
355
  export class CollapsedSyntheticMessageComponent implements Component {
222
356
  #disclosure: Disclosure;
357
+ #expanded = false;
358
+ readonly #native = new Memo();
223
359
 
224
360
  readonly #text: string;
225
361
  readonly #imageLinks?: readonly (string | undefined)[];
@@ -238,9 +374,36 @@ export class CollapsedSyntheticMessageComponent implements Component {
238
374
 
239
375
  /** ctrl+o toggle: reveal/hide the full Markdown body. */
240
376
  setExpanded(expanded: boolean): void {
377
+ this.#expanded = expanded;
241
378
  this.#disclosure.setExpanded(expanded);
242
379
  }
243
380
 
381
+ /**
382
+ * A muted collapsible card whose head is the one-line summary. The
383
+ * (potentially huge) markdown body is only described while expanded, so
384
+ * collapsed history costs no layout on either side; a native toggle
385
+ * expands it through {@link handleNativeEvent}.
386
+ */
387
+ describe(): NativeNode {
388
+ return this.#native.get([this.#expanded], () =>
389
+ card(
390
+ {
391
+ role: "omp.user.synthetic",
392
+ tone: "muted",
393
+ head: [span(summarizeSyntheticInput(this.#text), "dim")],
394
+ collapsible: true,
395
+ collapsed: !this.#expanded,
396
+ },
397
+ this.#expanded ? [md(this.#text)] : [],
398
+ ),
399
+ );
400
+ }
401
+
402
+ handleNativeEvent(event: NativeUiEvent): void {
403
+ const expanded = rootToggleExpanded(event);
404
+ if (expanded !== undefined) this.setExpanded(expanded);
405
+ }
406
+
244
407
  setIgnoreTight(ignore: boolean): this {
245
408
  this.#disclosure.setIgnoreTight(ignore);
246
409
  return this;
@@ -3,7 +3,9 @@ import { Ellipsis, truncateToWidth, visibleWidth } from "../utils";
3
3
  import type { AnimationFrame, TranscriptPresentationTarget } from "./transcript-container";
4
4
  import { fgOrPlain } from "../theme/theme";
5
5
  import { urlHyperlinkAlways } from "../render/index";
6
- import { QrCode, renderQrHalfBlocks } from "./qrcode";
6
+ import { QrCode, renderQrHalfBlocks, renderQrText } from "./qrcode";
7
+ import type { DescribeContext, NativeNode } from "../native/node";
8
+ import { code, col, text } from "../native/describe";
7
9
 
8
10
  /** Scheme-less display form of a collab browser deep link, OSC-8 linked. */
9
11
  export function collabBrowserLink(webLink: string, label?: string): string {
@@ -30,12 +32,16 @@ export function collabBrowserLink(webLink: string, label?: string): string {
30
32
  * URL above" is not reachable.
31
33
  */
32
34
  export class CollabQrCodeComponent implements Component, TranscriptPresentationTarget {
35
+ readonly #qr: QrCode;
33
36
  readonly #lines: readonly string[];
34
37
  readonly #minWidth: number;
35
38
  #allocatedRows = Number.POSITIVE_INFINITY;
39
+ /** Native description per appearance: the polarity follows the terminal's foreground. */
40
+ #native: { dark: boolean; node: NativeNode } | undefined;
36
41
 
37
42
  constructor(readonly url: string) {
38
- const rows = renderQrHalfBlocks(QrCode.encodeText(url, "M"));
43
+ this.#qr = QrCode.encodeText(url, "M");
44
+ const rows = renderQrHalfBlocks(this.#qr);
39
45
  this.#lines = rows.map(row => ` ${row}`);
40
46
  this.#minWidth = rows.reduce((max, row) => Math.max(max, visibleWidth(row)), 0) + 1;
41
47
  }
@@ -54,6 +60,25 @@ export class CollabQrCodeComponent implements Component, TranscriptPresentationT
54
60
  return this.#lines;
55
61
  }
56
62
 
63
+ /**
64
+ * The symbol as an unwrapped monospace block plus the join link. Glyphs are
65
+ * drawn in the terminal's foreground, so on a dark appearance the light
66
+ * modules are inked to keep the dark-on-light symbol scanners expect.
67
+ */
68
+ describe(cx: DescribeContext): NativeNode {
69
+ if (this.#native?.dark === cx.dark) return this.#native.node;
70
+ const qr = renderQrText(this.#qr, cx.dark).join("\n");
71
+ const node = col(
72
+ [
73
+ text([{ t: this.url.replace(/^https?:\/\//, ""), s: "accent link", href: this.url }], { wrap: "none" }),
74
+ code(qr, { lang: "text", wrap: false }),
75
+ ],
76
+ { role: "omp.collab.qrcode" },
77
+ );
78
+ this.#native = { dark: cx.dark, node };
79
+ return node;
80
+ }
81
+
57
82
  /** Survives emergency 1-row transcript pressure when this block is otherwise hidden. */
58
83
  renderTranscriptBlockEmergencyRow(width: number): string {
59
84
  return this.#hiddenHint(
@@ -1,4 +1,5 @@
1
1
  import { formatNumber } from "@oh-my-pi/pi-utils";
2
+ import type { TspTone } from "@oh-my-pi/pi-wire";
2
3
  import type { ThemeColor } from "../theme/index";
3
4
  export type ContextUsageLevel = "normal" | "warning" | "purple" | "error";
4
5
 
@@ -54,6 +55,26 @@ export function getContextUsageLevel(contextPercent: number, contextWindow: numb
54
55
  return "normal";
55
56
  }
56
57
 
58
+ /** Used share (0–1) where a level starts: the percent or token threshold, whichever comes first. */
59
+ function thresholdShare(contextWindow: number, percentThreshold: number, tokenThreshold: number): number {
60
+ const percent =
61
+ Number.isFinite(contextWindow) && contextWindow > 0
62
+ ? Math.min(percentThreshold, (tokenThreshold / contextWindow) * 100)
63
+ : percentThreshold;
64
+ return percent / 100;
65
+ }
66
+
67
+ /**
68
+ * `meter` tone switches matching {@link getContextUsageLevel}: warning from the
69
+ * warning level, error from the error level, as shares of `contextWindow`.
70
+ */
71
+ export function getContextMeterThresholds(contextWindow: number): { warn: number; bad: number } {
72
+ return {
73
+ warn: thresholdShare(contextWindow, CONTEXT_WARNING_PERCENT_THRESHOLD, CONTEXT_WARNING_TOKEN_THRESHOLD),
74
+ bad: thresholdShare(contextWindow, CONTEXT_ERROR_PERCENT_THRESHOLD, CONTEXT_ERROR_TOKEN_THRESHOLD),
75
+ };
76
+ }
77
+
57
78
  /**
58
79
  * Format context usage as `<percent>%/<window>` when the model window is known.
59
80
  * Unknown windows render as `<tokens>/?`, because `0.0%/0` suggests a real
@@ -71,6 +92,19 @@ export function formatContextUsage(
71
92
  return `${pct}/${formatNumber(contextWindow)}`;
72
93
  }
73
94
 
95
+ /** Chrome tone for a context-usage level; undefined while usage is normal. */
96
+ export function getContextUsageTone(level: ContextUsageLevel): TspTone | undefined {
97
+ switch (level) {
98
+ case "error":
99
+ return "error";
100
+ case "purple":
101
+ case "warning":
102
+ return "warning";
103
+ case "normal":
104
+ return undefined;
105
+ }
106
+ }
107
+
74
108
  export function getContextUsageThemeColor(level: ContextUsageLevel): ThemeColor {
75
109
  switch (level) {
76
110
  case "error":
@@ -1,12 +1,16 @@
1
1
  /**
2
2
  * Reusable countdown timer for dialog components.
3
3
  */
4
+ import { node } from "../native/describe";
5
+ import type { NativeNode } from "../native/node";
6
+ import { isNativeRendering } from "../native/state";
4
7
  import type { TUI } from "../tui";
5
8
  export class CountdownTimer {
6
9
  #intervalId: NodeJS.Timeout | undefined;
7
10
  #expireTimeoutId: NodeJS.Timeout | undefined;
8
11
  #remainingSeconds: number;
9
12
  #deadlineMs = 0;
13
+ #node: NativeNode | undefined;
10
14
  readonly #initialMs: number;
11
15
 
12
16
  constructor(
@@ -28,6 +32,7 @@ export class CountdownTimer {
28
32
  #start(): void {
29
33
  const now = Date.now();
30
34
  this.#deadlineMs = now + this.#initialMs;
35
+ this.#node = undefined;
31
36
  this.#remainingSeconds = this.#calculateRemainingSeconds(now);
32
37
  this.onTick(this.#remainingSeconds);
33
38
  this.tui?.requestRender();
@@ -37,7 +42,25 @@ export class CountdownTimer {
37
42
  this.onExpire();
38
43
  }, this.#initialMs);
39
44
 
40
- this.#startInterval();
45
+ // The per-second tick only repaints the remaining time; a TSP terminal
46
+ // counts down the described `elapsed` node itself.
47
+ if (!isNativeRendering()) this.#startInterval();
48
+ }
49
+
50
+ /**
51
+ * The remaining time as a terminal-clocked `elapsed` node: its age is the
52
+ * negative remaining time (the start lies in the future), so it counts up
53
+ * toward zero and freezes there at expiry. The age is taken on the first
54
+ * call after a (re)start, when the node is first written, and the node is
55
+ * reused until {@link reset}.
56
+ */
57
+ describe(): NativeNode {
58
+ this.#node ??= node("elapsed", {
59
+ age: Math.min(0, Date.now() - this.#deadlineMs),
60
+ stopped: 0,
61
+ format: "short",
62
+ });
63
+ return this.#node;
41
64
  }
42
65
 
43
66
  #startInterval(): void {
@@ -1,7 +1,10 @@
1
1
  import { diffWords } from "@oh-my-pi/pi-natives";
2
2
  import { DEFAULT_TAB_WIDTH, sanitizeText } from "@oh-my-pi/pi-utils";
3
+ import type { TspDiffHunk, TspProps } from "@oh-my-pi/pi-wire";
3
4
  import { theme as activeTheme, getLanguageFromPath, highlightCode, type Theme } from "../theme/index";
4
- import { type CodeFrameMarker, formatCodeFrameLine, replaceTabs } from "../render/render-utils";
5
+ import { type CodeFrameMarker, formatCodeFrameLine, replaceTabs, shortenPath } from "../render/render-utils";
6
+ import { node } from "../native/describe";
7
+ import type { NativeNode } from "../native/node";
5
8
  /** SGR dim on / normal intensity — additive, preserves fg/bg colors. */
6
9
  const DIM = "\x1b[2m";
7
10
  const DIM_OFF = "\x1b[22m";
@@ -97,6 +100,62 @@ function renderIntraLineDiff(
97
100
  return { removedLine, addedLine };
98
101
  }
99
102
 
103
+ /**
104
+ * Native `diff` node for omp's compact diff text (`+12|added`, `-12|removed`,
105
+ * ` 12|context`, blank or `…` rows between regions). Each contiguous region
106
+ * becomes one hunk; the terminal draws gutters, word emphasis and highlighting.
107
+ * Removed rows carry old line numbers, added rows new ones and context rows
108
+ * old ones, so the other side is derived from the running line delta.
109
+ */
110
+ export function nativeDiff(diffText: string, options: { filePath?: string } & TspProps<"diff"> = {}): NativeNode {
111
+ const { filePath, ...props } = options;
112
+ const hunks: TspDiffHunk[] = [];
113
+ let lines: string[] = [];
114
+ let oldStart = 0;
115
+ let newStart = 0;
116
+ // New-minus-old line offset accumulated over the regions already closed.
117
+ let delta = 0;
118
+ let regionDelta = 0;
119
+ let nextOld = 1;
120
+ const close = (): void => {
121
+ if (lines.length > 0) hunks.push({ oldStart, newStart, lines });
122
+ lines = [];
123
+ delta += regionDelta;
124
+ regionDelta = 0;
125
+ };
126
+ for (const raw of sanitizeText(diffText).split("\n")) {
127
+ const parsed = parseDiffLine(raw);
128
+ const trimmed = raw.trim();
129
+ if (!parsed || (parsed.prefix === " " && (parsed.content === "..." || parsed.content === "…"))) {
130
+ if (trimmed.length === 0 || trimmed === "..." || trimmed === "…") close();
131
+ continue;
132
+ }
133
+ const number = Number.parseInt(parsed.lineNum.trim(), 10);
134
+ if (lines.length === 0) {
135
+ if (Number.isFinite(number)) {
136
+ oldStart = parsed.prefix === "+" ? number - delta : number;
137
+ newStart = parsed.prefix === "+" ? number : number + delta;
138
+ } else {
139
+ oldStart = nextOld;
140
+ newStart = nextOld + delta;
141
+ }
142
+ }
143
+ if (parsed.prefix === "+") regionDelta++;
144
+ else {
145
+ if (parsed.prefix === "-") regionDelta--;
146
+ nextOld = (Number.isFinite(number) ? number : nextOld) + 1;
147
+ }
148
+ lines.push(`${parsed.prefix}${parsed.content}`);
149
+ }
150
+ close();
151
+ return node("diff", {
152
+ ...props,
153
+ hunks,
154
+ path: props.path ?? (filePath ? shortenPath(filePath) : undefined),
155
+ lang: props.lang ?? (filePath ? getLanguageFromPath(filePath) : undefined),
156
+ });
157
+ }
158
+
100
159
  export interface RenderDiffOptions {
101
160
  /** File path used to resolve indentation (.editorconfig + defaults) */
102
161
  filePath?: string;
@@ -1,5 +1,7 @@
1
1
  import type { Component } from "../tui";
2
2
  import { fgOrPlain, theme } from "../theme/index";
3
+ import type { NativeNode } from "../native/node";
4
+ import { EMPTY_NODE } from "../native/describe";
3
5
  /**
4
6
  * Dynamic border component that adjusts to viewport width.
5
7
  *
@@ -23,6 +25,11 @@ export class DynamicBorder implements Component {
23
25
  this.#cachedLines = undefined;
24
26
  }
25
27
 
28
+ /** Native terminals draw their own chrome; a border rule describes nothing. */
29
+ describe(): NativeNode {
30
+ return EMPTY_NODE;
31
+ }
32
+
26
33
  render(width: number): readonly string[] {
27
34
  if (this.#cachedLines && this.#cachedWidth === width) {
28
35
  return this.#cachedLines;
@@ -1,6 +1,8 @@
1
1
  import { renderProgressBar } from "../components/progress-bar";
2
2
  import { shimmerText } from "../theme/shimmer";
3
3
  import { theme as currentTheme, type Theme } from "../theme/theme";
4
+ import type { NativeNode } from "../native/node";
5
+ import { node } from "../native/describe";
4
6
 
5
7
  /** Title-case a provider id for display (`openai-codex` → `Openai Codex`). */
6
8
  export function formatProviderName(provider: string): string {
@@ -40,6 +42,16 @@ function resolveProgressBarTheme(uiTheme: ProgressBarTheme | undefined): Progres
40
42
  return uiTheme ?? currentTheme ?? unstyledProgressBarTheme;
41
43
  }
42
44
 
45
+ /**
46
+ * Native {@link renderAsciiBar}: a terminal-drawn progress bar with the same
47
+ * percent label; `undefined` is indeterminate.
48
+ */
49
+ export function describeAsciiBar(fraction: number | undefined): NativeNode {
50
+ if (fraction === undefined) return node("progress", { value: null });
51
+ const value = Math.min(1, Math.max(0, fraction));
52
+ return node("progress", { value, label: `${Math.round(value * 100)}%` });
53
+ }
54
+
43
55
  /**
44
56
  * Render an ASCII progress bar with a trailing percent label.
45
57
  * `fraction` is clamped to `[0, 1]`. `undefined` renders a dotted placeholder.
@@ -1,6 +1,10 @@
1
1
  import type { Component } from "../tui";
2
- import { type ThemeColor, theme } from "../theme";
2
+ import { getThemeEpoch, type ThemeColor, theme } from "../theme";
3
3
  import { truncateToWidth } from "../utils";
4
+ import { node, span, text } from "../native/describe";
5
+ import type { NativeNode } from "../native/node";
6
+ import { plainText } from "../native/spans";
7
+ import { Memo } from "../native/memo";
4
8
 
5
9
  /** Presentation policy for a compact message divider. */
6
10
  export interface MessageDividerOptions {
@@ -11,6 +15,13 @@ export interface MessageDividerOptions {
11
15
  readonly ruleWidth?: number;
12
16
  /** Preserve an intentionally untruncated legacy label at very narrow widths. */
13
17
  readonly truncateWhenNarrow?: boolean;
18
+ /** Semantic role of the native node. */
19
+ readonly role?: string;
20
+ /**
21
+ * Draw as an inline notice (this named icon and a one-line label without
22
+ * the leading glyph) instead of a labelled rule.
23
+ */
24
+ readonly native?: { readonly icon: string; readonly label: () => string };
14
25
  }
15
26
 
16
27
  /**
@@ -20,6 +31,7 @@ export interface MessageDividerOptions {
20
31
  export class MessageDividerComponent implements Component {
21
32
  readonly #options: MessageDividerOptions;
22
33
  #cache: { width: number; lines: readonly string[] } | undefined;
34
+ readonly #native = new Memo();
23
35
 
24
36
  constructor(options: MessageDividerOptions) {
25
37
  this.#options = options;
@@ -27,6 +39,26 @@ export class MessageDividerComponent implements Component {
27
39
 
28
40
  invalidate(): void {
29
41
  this.#cache = undefined;
42
+ this.#native.clear();
43
+ }
44
+
45
+ /** An inline notice, else a labelled `rule`; the label is theme-aware, so it rebuilds on theme changes. */
46
+ describe(): NativeNode {
47
+ const inline = this.#options.native;
48
+ if (inline) {
49
+ return this.#native.get([getThemeEpoch()], () =>
50
+ node("row", { gap: "sm", role: this.#options.role ?? "omp.divider" }, [
51
+ node("icon", { name: inline.icon }),
52
+ text([span(inline.label(), this.#options.labelColor)], { truncate: "end" }),
53
+ ]),
54
+ );
55
+ }
56
+ return this.#native.get([getThemeEpoch()], () =>
57
+ node("rule", {
58
+ role: this.#options.role ?? "omp.divider",
59
+ label: [span(plainText(this.#options.label()), this.#options.labelColor)],
60
+ }),
61
+ );
30
62
  }
31
63
 
32
64
  render(width: number): readonly string[] {