@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
@@ -5,7 +5,14 @@ import type { Tool as AiTool, Model } from "@oh-my-pi/pi-ai";
5
5
  import { renderToolExamples } from "@oh-my-pi/pi-ai/dialect";
6
6
  import { toolWireSchema } from "@oh-my-pi/pi-ai/utils/schema";
7
7
  import { formatNumber } from "@oh-my-pi/pi-utils";
8
- import type { Theme } from "../theme";
8
+ import type { Theme, ThemeColor } from "../theme";
9
+ import { Container } from "../tui";
10
+ import { Text } from "../components/text";
11
+ import { Spacer } from "../components/spacer";
12
+ import { DynamicBorder } from "../chrome/dynamic-border";
13
+ import type { TspSpan, TspText } from "@oh-my-pi/pi-wire";
14
+ import type { DescribeContext, NativeNode } from "../native/node";
15
+ import { card, col, node, row, span, text } from "../native/describe";
9
16
 
10
17
  interface ContextSkill {
11
18
  readonly name: string;
@@ -92,6 +99,8 @@ export interface ContextBreakdown {
92
99
  usedTokens: number;
93
100
  autoCompactBufferTokens: number;
94
101
  freeTokens: number;
102
+ /** Where auto-compaction fires, in tokens; undefined when compaction is off. */
103
+ thresholdTokens?: number;
95
104
  /** Estimated snapcompact wire savings; set when requested and a snapcompact.* setting is enabled. */
96
105
  snapcompact?: ContextSavingsEstimate;
97
106
  }
@@ -459,10 +468,12 @@ export function computeContextBreakdown(session: ContextUsageSession, options: C
459
468
  ];
460
469
 
461
470
  let autoCompactBufferTokens = 0;
471
+ let thresholdTokens: number | undefined;
462
472
  if (contextWindow > 0) {
463
473
  const compactionSettings = options.compaction;
464
474
  if (compactionSettings.enabled && compactionSettings.strategy !== "off") {
465
475
  const threshold = resolveThresholdTokens(contextWindow, compactionSettings);
476
+ if (threshold > 0 && threshold <= contextWindow) thresholdTokens = threshold;
466
477
  autoCompactBufferTokens = Math.max(0, contextWindow - threshold);
467
478
  } else {
468
479
  autoCompactBufferTokens = 0;
@@ -483,6 +494,7 @@ export function computeContextBreakdown(session: ContextUsageSession, options: C
483
494
  usedTokens,
484
495
  autoCompactBufferTokens,
485
496
  freeTokens,
497
+ thresholdTokens,
486
498
  snapcompact: options.snapcompact,
487
499
  };
488
500
  }
@@ -566,63 +578,91 @@ function percentString(part: number, whole: number, fractionDigits = 1): string
566
578
  return `${pct.toFixed(fractionDigits)}%`;
567
579
  }
568
580
 
569
- function buildLegendLines(breakdown: ContextBreakdown, theme: Theme): string[] {
570
- const lines: string[] = [];
581
+ /** One styled run of a legend line: bold (`strong`), a theme color, or plain. */
582
+ interface LegendPart {
583
+ t: string;
584
+ s?: ThemeColor | "strong";
585
+ }
586
+
587
+ /** Legend lines as styled runs, shared by the ANSI panel and the native description. */
588
+ function buildLegendParts(breakdown: ContextBreakdown): LegendPart[][] {
589
+ const lines: LegendPart[][] = [];
571
590
  const { model, contextWindow, categories, usedTokens, autoCompactBufferTokens, freeTokens } = breakdown;
572
591
 
573
592
  const modelName = model?.name ?? model?.id ?? "no model";
574
593
  const modelId = model?.id ?? "unknown";
575
594
  const windowLabel = formatNumber(contextWindow).toLowerCase();
576
595
 
577
- lines.push(theme.bold(`${modelName}`) + theme.fg("dim", ` (${windowLabel} context)`));
578
- lines.push(theme.fg("muted", `${modelId}[${windowLabel}]`));
579
- lines.push(
580
- `${theme.bold(formatNumber(usedTokens))}${theme.fg("dim", `/${windowLabel} tokens`)}` +
581
- theme.fg("muted", ` (${percentString(usedTokens, contextWindow)})`),
582
- );
583
- lines.push("");
584
- lines.push(theme.fg("muted", "Estimated usage by category"));
596
+ lines.push([
597
+ { t: `${modelName}`, s: "strong" },
598
+ { t: ` (${windowLabel} context)`, s: "dim" },
599
+ ]);
600
+ lines.push([{ t: `${modelId}[${windowLabel}]`, s: "muted" }]);
601
+ lines.push([
602
+ { t: formatNumber(usedTokens), s: "strong" },
603
+ { t: `/${windowLabel} tokens`, s: "dim" },
604
+ { t: ` (${percentString(usedTokens, contextWindow)})`, s: "muted" },
605
+ ]);
606
+ lines.push([]);
607
+ lines.push([{ t: "Estimated usage by category", s: "muted" }]);
585
608
 
586
609
  for (const category of categories) {
587
- const dot = theme.fg(category.color, category.glyph);
588
- const label = category.label;
589
- const tokens = formatNumber(category.tokens);
590
610
  const pct = percentString(category.tokens, contextWindow);
591
- lines.push(`${dot} ${label}: ${theme.bold(tokens)} ${theme.fg("dim", `tokens (${pct})`)}`);
611
+ lines.push([
612
+ { t: category.glyph, s: category.color },
613
+ { t: ` ${category.label}: ` },
614
+ { t: formatNumber(category.tokens), s: "strong" },
615
+ { t: " " },
616
+ { t: `tokens (${pct})`, s: "dim" },
617
+ ]);
592
618
  }
593
619
 
594
- const freeDot = theme.fg("dim", CELL_FREE);
595
- lines.push(
596
- `${freeDot} Free space: ${theme.bold(formatNumber(freeTokens))} ${theme.fg("dim", `(${percentString(freeTokens, contextWindow)})`)}`,
597
- );
620
+ lines.push([
621
+ { t: CELL_FREE, s: "dim" },
622
+ { t: " Free space: " },
623
+ { t: formatNumber(freeTokens), s: "strong" },
624
+ { t: " " },
625
+ { t: `(${percentString(freeTokens, contextWindow)})`, s: "dim" },
626
+ ]);
598
627
 
599
628
  if (autoCompactBufferTokens > 0) {
600
- const bufferDot = theme.fg("warning", CELL_BUFFER);
601
- lines.push(
602
- `${bufferDot} Autocompact buffer: ${theme.bold(formatNumber(autoCompactBufferTokens))} ${theme.fg(
603
- "dim",
604
- `tokens (${percentString(autoCompactBufferTokens, contextWindow)})`,
605
- )}`,
606
- );
629
+ lines.push([
630
+ { t: CELL_BUFFER, s: "warning" },
631
+ { t: " Autocompact buffer: " },
632
+ { t: formatNumber(autoCompactBufferTokens), s: "strong" },
633
+ { t: " " },
634
+ { t: `tokens (${percentString(autoCompactBufferTokens, contextWindow)})`, s: "dim" },
635
+ ]);
607
636
  }
608
637
 
638
+ const snap = buildSnapcompactParts(breakdown);
639
+ if (snap.length > 0) lines.push([], ...snap);
640
+ return lines;
641
+ }
642
+
643
+ /** Snapcompact savings lines of the legend; empty when no snapcompact setting is on. */
644
+ function buildSnapcompactParts(breakdown: ContextBreakdown): LegendPart[][] {
645
+ const lines: LegendPart[][] = [];
646
+ const { usedTokens } = breakdown;
609
647
  const snap = breakdown.snapcompact;
610
648
  if (snap) {
611
- lines.push("");
612
649
  if (!snap.visionCapable) {
613
- lines.push(theme.fg("muted", "Snapcompact: inactive (model has no image input)"));
650
+ lines.push([{ t: "Snapcompact: inactive (model has no image input)", s: "muted" }]);
614
651
  } else {
615
- lines.push(theme.fg("muted", "Snapcompact (estimated wire savings)"));
652
+ lines.push([{ t: "Snapcompact (estimated wire savings)", s: "muted" }]);
616
653
  if (snap.systemPrompt) {
617
654
  const sp = snap.systemPrompt;
655
+ const scope = sp.scope === "agents-md" ? "AGENTS.md" : "all";
618
656
  if (sp.applied) {
619
- lines.push(
620
- ` System prompt (${sp.scope === "agents-md" ? "AGENTS.md" : "all"}): saves ${theme.bold(`~${formatNumber(sp.savedTokens)}`)} ` +
621
- theme.fg(
622
- "dim",
623
- `(${formatNumber(sp.textTokens)} text → ${sp.frames} frame${sp.frames === 1 ? "" : "s"} ≈ ${formatNumber(sp.imageTokens)})`,
624
- ),
625
- );
657
+ lines.push([
658
+ { t: ` System prompt (${scope}): saves ` },
659
+ { t: `~${formatNumber(sp.savedTokens)}`, s: "strong" },
660
+ { t: " " },
661
+ {
662
+ t: `(${formatNumber(sp.textTokens)} text → ${sp.frames} frame${sp.frames === 1 ? "" : "s"} ≈ ${formatNumber(sp.imageTokens)})`,
663
+ s: "dim",
664
+ },
665
+ ]);
626
666
  } else {
627
667
  const reason =
628
668
  sp.reason === "budget"
@@ -630,29 +670,32 @@ function buildLegendLines(breakdown: ContextBreakdown, theme: Theme): string[] {
630
670
  : sp.reason === "empty"
631
671
  ? "nothing to image"
632
672
  : "frames would not save tokens";
633
- lines.push(
634
- ` System prompt (${sp.scope === "agents-md" ? "AGENTS.md" : "all"}): ${theme.fg("dim", `stays text (${reason})`)}`,
635
- );
673
+ lines.push([{ t: ` System prompt (${scope}): ` }, { t: `stays text (${reason})`, s: "dim" }]);
636
674
  }
637
675
  }
638
676
  if (snap.toolResults) {
639
677
  const tr = snap.toolResults;
640
678
  if (tr.swapped > 0) {
641
- lines.push(
642
- ` Tool results: saves ${theme.bold(`~${formatNumber(tr.savedTokens)}`)} ` +
643
- theme.fg(
644
- "dim",
645
- `(${tr.swapped}/${tr.total} imaged, ${formatNumber(tr.textTokens)} text → ${tr.frames} frames ≈ ${formatNumber(tr.imageTokens)})`,
646
- ),
647
- );
679
+ lines.push([
680
+ { t: " Tool results: saves " },
681
+ { t: `~${formatNumber(tr.savedTokens)}`, s: "strong" },
682
+ { t: " " },
683
+ {
684
+ t: `(${tr.swapped}/${tr.total} imaged, ${formatNumber(tr.textTokens)} text → ${tr.frames} frames ≈ ${formatNumber(tr.imageTokens)})`,
685
+ s: "dim",
686
+ },
687
+ ]);
648
688
  } else {
649
- lines.push(` Tool results: ${theme.fg("dim", `none imaged (${tr.total} in history)`)}`);
689
+ lines.push([{ t: " Tool results: " }, { t: `none imaged (${tr.total} in history)`, s: "dim" }]);
650
690
  }
651
691
  }
652
692
  if (snap.savedTokens > 0) {
653
- lines.push(
654
- ` Next request: ${theme.bold(`~${formatNumber(Math.max(0, usedTokens - snap.savedTokens))}`)} ${theme.fg("dim", "tokens on the wire")}`,
655
- );
693
+ lines.push([
694
+ { t: " Next request: " },
695
+ { t: `~${formatNumber(Math.max(0, usedTokens - snap.savedTokens))}`, s: "strong" },
696
+ { t: " " },
697
+ { t: "tokens on the wire", s: "dim" },
698
+ ]);
656
699
  }
657
700
  }
658
701
  }
@@ -660,6 +703,192 @@ function buildLegendLines(breakdown: ContextBreakdown, theme: Theme): string[] {
660
703
  return lines;
661
704
  }
662
705
 
706
+ function buildLegendLines(breakdown: ContextBreakdown, theme: Theme): string[] {
707
+ return buildLegendParts(breakdown).map(line => {
708
+ let out = "";
709
+ for (const part of line) {
710
+ out += part.s === "strong" ? theme.bold(part.t) : part.s ? theme.fg(part.s, part.t) : part.t;
711
+ }
712
+ return out;
713
+ });
714
+ }
715
+
716
+ /** Stacked meter parts in grid order: the categories, free space as the empty track, then the hatched buffer. */
717
+ function contextMeterParts(
718
+ breakdown: ContextBreakdown,
719
+ ): { value: number; token?: string; label?: string; hatch?: boolean }[] {
720
+ const window = breakdown.contextWindow;
721
+ const parts: { value: number; token?: string; label?: string; hatch?: boolean }[] = breakdown.categories
722
+ .filter(category => category.tokens > 0)
723
+ .map(category => ({
724
+ value: category.tokens / window,
725
+ token: category.color,
726
+ label: `${category.label} · ${formatNumber(category.tokens)} tokens (${percentString(category.tokens, window)})`,
727
+ }));
728
+ parts.push({
729
+ value: breakdown.freeTokens / window,
730
+ token: "track",
731
+ label: `Free space · ${formatNumber(breakdown.freeTokens)} tokens (${percentString(breakdown.freeTokens, window)})`,
732
+ });
733
+ if (breakdown.autoCompactBufferTokens > 0) {
734
+ parts.push({
735
+ value: breakdown.autoCompactBufferTokens / window,
736
+ token: "warning",
737
+ hatch: true,
738
+ label: `Autocompact buffer · ${formatNumber(breakdown.autoCompactBufferTokens)} tokens (${percentString(breakdown.autoCompactBufferTokens, window)})`,
739
+ });
740
+ }
741
+ return parts;
742
+ }
743
+
744
+ /**
745
+ * The `/context` frame for terminals that draw `meter`: a blocks meter
746
+ * beside a legend `kv` (swatch · label → tokens · %), then a full-width bar
747
+ * of the same parts marked where auto-compaction fires, then the snapcompact
748
+ * estimate when one is on. The head names the model and window once.
749
+ */
750
+ function describeContextFrame(breakdown: ContextBreakdown): NativeNode {
751
+ const { contextWindow: window, usedTokens, freeTokens, autoCompactBufferTokens, thresholdTokens } = breakdown;
752
+ const parts = contextMeterParts(breakdown);
753
+ const used = Math.min(1, usedTokens / window);
754
+ const swatch = (token: string, glyph = "■"): TspSpan => span(glyph, token);
755
+ const figure = (tokens: number): TspText => [
756
+ span(formatNumber(tokens)),
757
+ span(` ${percentString(tokens, window)}`, "dim"),
758
+ ];
759
+ const items: { k: TspText; v: TspText }[] = [
760
+ {
761
+ k: [span("Used", "strong")],
762
+ v: [
763
+ span(`${formatNumber(usedTokens)} / ${formatNumber(window).toLowerCase()}`),
764
+ span(` ${percentString(usedTokens, window)}`, "dim"),
765
+ ],
766
+ },
767
+ ];
768
+ for (const category of breakdown.categories) {
769
+ items.push({ k: [swatch(category.color), span(` ${category.label}`)], v: figure(category.tokens) });
770
+ }
771
+ items.push({ k: [swatch("dim", "□"), span(" Free space")], v: figure(freeTokens) });
772
+ if (autoCompactBufferTokens > 0) {
773
+ items.push({ k: [swatch("warning", "▨"), span(" Autocompact buffer")], v: figure(autoCompactBufferTokens) });
774
+ }
775
+ const marks =
776
+ thresholdTokens !== undefined
777
+ ? [
778
+ {
779
+ at: thresholdTokens / window,
780
+ tone: "warning" as const,
781
+ title: `Auto-compaction at ${percentString(thresholdTokens, window, 0)} (${formatNumber(thresholdTokens)} tokens)`,
782
+ },
783
+ ]
784
+ : undefined;
785
+ const children = [
786
+ node(
787
+ "row",
788
+ { role: "omp.context.body" },
789
+ [
790
+ node("meter", { value: used, style: "blocks", size: "lg", parts, aria: "Context usage by category" }),
791
+ node("kv", { items, layout: "grid", role: "omp.context.legend" }),
792
+ ],
793
+ "body",
794
+ ),
795
+ node(
796
+ "meter",
797
+ { value: used, style: "bar", size: "lg", parts, ...(marks ? { marks } : {}), grow: 1 },
798
+ undefined,
799
+ "bar",
800
+ ),
801
+ ];
802
+ const snap = buildSnapcompactParts(breakdown);
803
+ if (snap.length > 0) {
804
+ children.push(
805
+ node(
806
+ "section",
807
+ {},
808
+ snap.map((line, index) => node("text", { spans: line, wrap: "word" }, undefined, `snap-${index}`)),
809
+ "snapcompact",
810
+ ),
811
+ );
812
+ }
813
+ const modelName = breakdown.model?.name ?? breakdown.model?.id ?? "no model";
814
+ return card(
815
+ {
816
+ role: "omp.context",
817
+ head: [
818
+ span("Context", "strong"),
819
+ span(` · ${modelName}`, "muted"),
820
+ span(` · ${formatNumber(window).toLowerCase()}`, "dim"),
821
+ ],
822
+ },
823
+ children,
824
+ );
825
+ }
826
+
827
+ /**
828
+ * Native context-usage panel for terminals without `meter`: the cell grid as
829
+ * unwrapped styled rows beside the legend. The terminal places the legend
830
+ * beside the grid, or below it when narrow.
831
+ */
832
+ export function describeContextUsage(breakdown: ContextBreakdown): NativeNode {
833
+ if (breakdown.contextWindow <= 0) {
834
+ return text([{ t: "Context usage is unavailable: no model is selected for this session.", s: "muted" }]);
835
+ }
836
+ const cells = planCells(breakdown);
837
+ const grid: NativeNode[] = [];
838
+ for (let gridRow = 0; gridRow < GRID_ROWS; gridRow++) {
839
+ const spans: TspSpan[] = [];
840
+ for (let gridCol = 0; gridCol < GRID_COLS; gridCol++) {
841
+ const cell = cells[gridRow * GRID_COLS + gridCol]!;
842
+ spans.push({ t: gridCol === 0 ? cell.glyph : ` ${cell.glyph}`, s: cell.color });
843
+ }
844
+ grid.push(node("text", { spans, wrap: "none" }, undefined, `row-${gridRow}`));
845
+ }
846
+ const legend = buildLegendParts(breakdown).map((line, index) =>
847
+ node("text", { spans: line.length > 0 ? line : [{ t: " " }] }, undefined, `legend-${index}`),
848
+ );
849
+ return row([col(grid), col(legend)], {
850
+ gap: "lg",
851
+ wrap: true,
852
+ role: "omp.context.usage",
853
+ });
854
+ }
855
+
856
+ /**
857
+ * The `/context` transcript block: ANSI renders the titled cell grid between
858
+ * rules; natively it is one frame, drawn with `meter`s where the terminal
859
+ * has them and as the glyph grid otherwise.
860
+ */
861
+ export class ContextUsageView extends Container {
862
+ readonly #breakdown: ContextBreakdown;
863
+ #native: { meter: boolean; node: NativeNode } | undefined;
864
+
865
+ constructor(breakdown: ContextBreakdown, theme: Theme) {
866
+ super();
867
+ this.#breakdown = breakdown;
868
+ this.addChild(new DynamicBorder());
869
+ this.addChild(new Text(theme.bold(theme.fg("accent", "Context Usage")), 1, 0));
870
+ this.addChild(new Spacer(1));
871
+ this.addChild(new Text(renderContextUsage(breakdown, theme), 1, 0));
872
+ this.addChild(new DynamicBorder());
873
+ }
874
+
875
+ override describe(cx: DescribeContext): NativeNode {
876
+ const meter = cx.supports("meter");
877
+ if (this.#native?.meter === meter) return this.#native.node;
878
+ const breakdown = this.#breakdown;
879
+ const described =
880
+ breakdown.contextWindow <= 0
881
+ ? describeContextUsage(breakdown)
882
+ : meter
883
+ ? describeContextFrame(breakdown)
884
+ : card({ role: "omp.context", head: [span("Context usage", "strong")] }, [
885
+ describeContextUsage(breakdown),
886
+ ]);
887
+ this.#native = { meter, node: described };
888
+ return described;
889
+ }
890
+ }
891
+
663
892
  /**
664
893
  * Render a colorful context-usage panel as ANSI text. Output is a series of
665
894
  * lines pairing the grid (left) with the legend (right).
@@ -9,7 +9,15 @@ import { shortenPath } from "../render/render-utils";
9
9
  import { sanitizeStatusText } from "../chrome/shared";
10
10
  import { formatMetric } from "../components/metric";
11
11
  import { formatBillingSummary } from "./metrics";
12
- import { formatContextUsage, getContextUsageLevel, getContextUsageThemeColor } from "../chrome/context-thresholds";
12
+ import {
13
+ formatContextUsage,
14
+ getContextUsageLevel,
15
+ getContextUsageThemeColor,
16
+ getContextUsageTone,
17
+ } from "../chrome/context-thresholds";
18
+ import type { TspProps, TspSpan } from "@oh-my-pi/pi-wire";
19
+ import type { NativeNode } from "../native/node";
20
+ import { col, node, span } from "../native/describe";
13
21
 
14
22
  /**
15
23
  * Footer component that shows pwd, token stats, and context usage
@@ -23,6 +31,7 @@ export class FooterComponent implements Component {
23
31
  #disposed = false;
24
32
  #autoCompactEnabled: boolean = true;
25
33
  #extensionStatuses: Map<string, string> = new Map();
34
+ #nativeMemo: { node: NativeNode; fingerprint: string } | undefined;
26
35
 
27
36
  constructor(
28
37
  private readonly session: FooterSession,
@@ -159,6 +168,90 @@ export class FooterComponent implements Component {
159
168
  return this.#cachedBranch;
160
169
  }
161
170
 
171
+ /**
172
+ * Native footer: a `status` bar (path and branch, usage counters, billing
173
+ * and context on the left, model on the right) plus the extension statuses.
174
+ * Rebuilt per call from cheap session reads; the node identity holds while
175
+ * the description is unchanged.
176
+ */
177
+ describe(): NativeNode {
178
+ const state = this.session.state;
179
+ let input = 0;
180
+ let output = 0;
181
+ let cacheRead = 0;
182
+ let cacheWrite = 0;
183
+ let cost = 0;
184
+ let premiumRequests = 0;
185
+ for (const entry of this.session.sessionManager.getEntries()) {
186
+ if (entry.type === "message" && entry.message?.role === "assistant") {
187
+ input += entry.message.usage.input;
188
+ output += entry.message.usage.output;
189
+ cacheRead += entry.message.usage.cacheRead;
190
+ cacheWrite += entry.message.usage.cacheWrite;
191
+ cost += entry.message.usage.cost.total;
192
+ premiumRequests += entry.message.usage.premiumRequests ?? 0;
193
+ }
194
+ }
195
+ const segs: NativeNode[] = [];
196
+ const seg = (key: string, props: TspProps<"seg">): void => {
197
+ segs.push(node("seg", { role: `omp.footer.${key}`, ...props }, undefined, key));
198
+ };
199
+ const pathSpans: TspSpan[] = [span(shortenPath(getProjectDir()), "path dim")];
200
+ const branch = this.#getCurrentBranch();
201
+ if (branch) pathSpans.push(span(` (${branch})`, "dim"));
202
+ seg("path", { side: "left", priority: 6, icon: "folder", spans: pathSpans });
203
+ const counters: [string, string, number][] = [
204
+ ["input", "↑", input],
205
+ ["output", "↓", output],
206
+ ["cache-read", "R", cacheRead],
207
+ ["cache-write", "W", cacheWrite],
208
+ ];
209
+ for (const [key, glyph, amount] of counters) {
210
+ if (amount) seg(key, { side: "left", priority: 1, spans: [span(`${glyph}${formatNumber(amount)}`, "dim")] });
211
+ }
212
+ const usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;
213
+ const billing = formatBillingSummary({ cost, usingSubscription, premiumRequests, fractionDigits: 3 }, theme);
214
+ if (billing) seg("cost", { side: "left", priority: 3, spans: [span(billing, "dim")] });
215
+ const contextUsage = this.session.getContextUsage();
216
+ const contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;
217
+ const contextPercent = contextWindow > 0 ? (contextUsage?.percent ?? 0) : null;
218
+ const level =
219
+ contextUsage && contextPercent !== null ? getContextUsageLevel(contextPercent, contextWindow) : "normal";
220
+ const contextSpans: TspSpan[] = [
221
+ span(
222
+ formatContextUsage(contextPercent, contextWindow, contextUsage?.tokens ?? 0),
223
+ getContextUsageThemeColor(level),
224
+ ),
225
+ ];
226
+ if (this.#autoCompactEnabled && theme.icon.auto) contextSpans.push(span(` ${theme.icon.auto}`, "dim"));
227
+ const contextProps: TspProps<"seg"> = { side: "left", priority: 4, icon: "context", spans: contextSpans };
228
+ const tone = getContextUsageTone(level);
229
+ if (tone) contextProps.tone = tone;
230
+ seg("context", contextProps);
231
+ let model = state.model?.id || "no-model";
232
+ if (state.model?.thinking) {
233
+ const level = this.session.isAutoThinking
234
+ ? (this.session.autoResolvedThinkingLevel() ?? `${theme.thinking.autoPending} auto`)
235
+ : (state.thinkingLevel ?? ThinkingLevel.Off);
236
+ model += ` • ${level}`;
237
+ }
238
+ seg("model", { side: "right", priority: 5, icon: "model", spans: [span(model, "dim")] });
239
+ const bar = node("status", { role: "omp.footer" }, segs, "bar");
240
+ const children: NativeNode[] = [bar];
241
+ if (this.#extensionStatuses.size > 0) {
242
+ const statuses = Array.from(this.#extensionStatuses.entries())
243
+ .sort(([a], [b]) => a.localeCompare(b))
244
+ .map(([, value]) => sanitizeStatusText(value))
245
+ .join(" ");
246
+ children.push(node("text", { text: statuses, wrap: "none", role: "omp.footer.extensions" }, undefined, "ext"));
247
+ }
248
+ const built = children.length === 1 ? bar : col(children, { role: "omp.footer.panel" });
249
+ const fingerprint = JSON.stringify(built);
250
+ if (this.#nativeMemo?.fingerprint === fingerprint) return this.#nativeMemo.node;
251
+ this.#nativeMemo = { node: built, fingerprint };
252
+ return built;
253
+ }
254
+
162
255
  render(width: number): readonly string[] {
163
256
  const state = this.session.state;
164
257
 
@@ -1,6 +1,8 @@
1
- import { formatNumber } from "@oh-my-pi/pi-utils";
1
+ import { formatNumber, normalizePremiumRequests } from "@oh-my-pi/pi-utils";
2
2
  import type { Theme } from "../theme";
3
3
 
4
+ export { normalizePremiumRequests } from "@oh-my-pi/pi-utils";
5
+
4
6
  /** Inputs whose differences are intentionally preserved between current status segments and the legacy footer. */
5
7
  export interface BillingSummaryOptions {
6
8
  readonly cost: number;
@@ -14,11 +16,6 @@ export interface BillingSummaryOptions {
14
16
  };
15
17
  }
16
18
 
17
- /** Round premium-request counters without losing legitimate fractional requests. */
18
- export function normalizePremiumRequests(value: number): number {
19
- return Math.round((value + Number.EPSILON) * 100) / 100;
20
- }
21
-
22
19
  function formatSpend(amount: number, usingSubscription: boolean, fractionDigits: number, uiTheme: Theme): string {
23
20
  const formatted = amount.toFixed(fractionDigits);
24
21
  if (!usingSubscription) return `$${formatted}`;