@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
@@ -24,19 +24,25 @@
24
24
  * every file underneath it.
25
25
  */
26
26
 
27
+ import type { TspSpan } from "@oh-my-pi/pi-wire";
27
28
  import { formatKeyHint, formatKeyHints } from "../../app-keybindings";
28
29
  import { SplitPane } from "../../components/layout/split-pane";
29
30
  import { Stack } from "../../components/layout/stack";
30
31
  import { matchesKey } from "../../keys";
31
32
  import { ProcessTerminal } from "../../terminal";
32
33
  import { routeSgrMouseInput } from "../../mouse";
33
- import { type Component, TUI } from "../../tui";
34
+ import { col, compact, keyed, node, row, span, text } from "../../native/describe";
35
+ import { Memo } from "../../native/memo";
36
+ import type { DescribeContext, NativeNode, NativeUiEvent } from "../../native/node";
37
+ import { actionButton } from "../../native/overlay";
38
+ import { type Component, type OverlayHandle, TUI } from "../../tui";
34
39
  import { truncateToWidth, visibleWidth } from "../../utils";
35
40
  import { theme, warmHighlighter } from "../../theme/theme";
36
41
  import type { AvatarSource } from "./avatar";
37
42
  import { pill, softPill, tintChip } from "./colors";
38
43
  import {
39
44
  buildDiffDocument,
45
+ type DiffDocument,
40
46
  buildLineSelectionPatch,
41
47
  DiffPane,
42
48
  type HunkAction,
@@ -44,8 +50,9 @@ import {
44
50
  type ViewMode,
45
51
  type WhitespaceMode,
46
52
  } from "./diff-pane";
53
+ import { GitHelpSheet } from "./help";
47
54
  import { Sidebar, type SidebarAction, type GitCommitMessage } from "./sidebar";
48
- import type { ChangedFile, FileContents, GitTuiModel } from "./state";
55
+ import type { AssetFileContents, ChangedFile, FileContents, GitTuiModel } from "./state";
49
56
 
50
57
  /** AI staging counts displayed after filtering the working tree. */
51
58
  export interface AiStageOutcome {
@@ -80,6 +87,20 @@ const STATUS_TTL_MS = 6_000;
80
87
 
81
88
  type Focus = "diff" | "sidebar";
82
89
 
90
+ /** Tone of a status message: its colour in the header and its span token natively. */
91
+ type StatusTone = "success" | "warning" | "error" | "accent" | "dim";
92
+
93
+ /** Columns below which the native layout stacks the changes under the diff. */
94
+ const NARROW_COLS = 100;
95
+
96
+ /** The views of the native segmented control, in omp's `1`–`4` order. */
97
+ const VIEW_TABS: readonly { id: ViewMode; label: string }[] = [
98
+ { id: "file", label: "File" },
99
+ { id: "split", label: "Split" },
100
+ { id: "inline", label: "Inline" },
101
+ { id: "hunk", label: "Hunks" },
102
+ ];
103
+
83
104
  interface UiHit {
84
105
  from: number;
85
106
  to: number;
@@ -152,6 +173,7 @@ class GitTuiComponent implements Component {
152
173
  #refreshTimer: NodeJS.Timeout | undefined;
153
174
  #busy = false;
154
175
  #status = "";
176
+ #statusTone: StatusTone = "dim";
155
177
  #statusAt = 0;
156
178
  #statusSticky = false;
157
179
  #centerWidth = 0;
@@ -162,6 +184,10 @@ class GitTuiComponent implements Component {
162
184
  /** After hopping files backwards, land on the last hunk once the diff loads. */
163
185
  #pendingHunkEdge: "last" | null = null;
164
186
  #disposed = false;
187
+ #help: OverlayHandle | null = null;
188
+ readonly #nativeBar = new Memo();
189
+ readonly #nativeBody = new Memo();
190
+ readonly #nativeRoot = new Memo();
165
191
 
166
192
  constructor(ui: TUI, host: GitTuiHost) {
167
193
  this.#ui = ui;
@@ -207,6 +233,7 @@ class GitTuiComponent implements Component {
207
233
  this.#generationAbort?.abort();
208
234
  this.#aiStageAbort?.abort();
209
235
  clearInterval(this.#refreshTimer);
236
+ this.#help?.hide();
210
237
  this.#sidebar.dispose();
211
238
  this.#stack.dispose();
212
239
  }
@@ -341,10 +368,8 @@ class GitTuiComponent implements Component {
341
368
  if (this.#pendingDiscard !== key) {
342
369
  this.#pendingDiscard = key;
343
370
  this.#setStatus(
344
- theme.fg(
345
- "warning",
346
- `Discard changes to ${action.selection.label}? Press ${formatKeyHint("delete")} again to confirm`,
347
- ),
371
+ "warning",
372
+ `Discard changes to ${action.selection.label}? Press ${formatKeyHint("delete")} again to confirm`,
348
373
  );
349
374
  return;
350
375
  }
@@ -356,24 +381,23 @@ class GitTuiComponent implements Component {
356
381
  switch (action.type) {
357
382
  case "discard":
358
383
  await this.#model.discard(action.selection.files);
359
- this.#setStatus(theme.fg("success", `Discarded ${action.selection.label}`));
384
+ this.#setStatus("success", `Discarded ${action.selection.label}`);
360
385
  break;
361
386
  case "stage":
362
387
  await this.#model.stage(action.selection?.files);
363
- this.#setStatus(
364
- theme.fg("success", action.selection ? `Staged ${action.selection.label}` : "Staged all changes"),
365
- );
388
+ this.#setStatus("success", action.selection ? `Staged ${action.selection.label}` : "Staged all changes");
366
389
  break;
367
390
  case "unstage":
368
391
  await this.#model.unstage(action.selection?.files);
369
392
  this.#setStatus(
370
- theme.fg("success", action.selection ? `Unstaged ${action.selection.label}` : "Unstaged all changes"),
393
+ "success",
394
+ action.selection ? `Unstaged ${action.selection.label}` : "Unstaged all changes",
371
395
  );
372
396
  break;
373
397
  case "stage-ai": {
374
398
  const abort = new AbortController();
375
399
  this.#aiStageAbort = abort;
376
- this.#setStatus(theme.fg("accent", `Filtering changes: ${action.prompt}`));
400
+ this.#setStatus("accent", `Filtering changes: ${action.prompt}`);
377
401
  try {
378
402
  const outcome = await this.#host.aiStage({
379
403
  cwd: this.#model.cwd,
@@ -381,11 +405,11 @@ class GitTuiComponent implements Component {
381
405
  files: this.#model.unstaged,
382
406
  signal: abort.signal,
383
407
  onProgress: message => {
384
- if (!this.#disposed) this.#setStatus(theme.fg("dim", message));
408
+ if (!this.#disposed) this.#setStatus("dim", message);
385
409
  },
386
410
  });
387
411
  if (outcome.stagedHunks === 0 && outcome.wholeFiles === 0) {
388
- this.#setStatus(theme.fg("warning", `No changes matched "${action.prompt}"`));
412
+ this.#setStatus("warning", `No changes matched "${action.prompt}"`);
389
413
  } else {
390
414
  const parts: string[] = [];
391
415
  if (outcome.stagedHunks > 0) parts.push(`${outcome.stagedHunks} of ${outcome.totalHunks} hunks`);
@@ -393,10 +417,8 @@ class GitTuiComponent implements Component {
393
417
  parts.push(`${outcome.wholeFiles} whole file${outcome.wholeFiles === 1 ? "" : "s"}`);
394
418
  }
395
419
  this.#setStatus(
396
- theme.fg(
397
- "success",
398
- `Staged ${parts.join(" + ")} (${outcome.matchedFiles}/${outcome.totalFiles} files matched)`,
399
- ),
420
+ "success",
421
+ `Staged ${parts.join(" + ")} (${outcome.matchedFiles}/${outcome.totalFiles} files matched)`,
400
422
  );
401
423
  }
402
424
  } finally {
@@ -408,27 +430,27 @@ class GitTuiComponent implements Component {
408
430
  const abort = new AbortController();
409
431
  this.#generationAbort = abort;
410
432
  this.#sidebar.setGenerating(true);
411
- this.#setStatus(theme.fg("accent", "Generating commit message…"));
433
+ this.#setStatus("accent", "Generating commit message…");
412
434
  try {
413
435
  const generated = await this.#host.generateCommitMessage({
414
436
  cwd: this.#model.cwd,
415
437
  stageIfEmpty: true,
416
438
  signal: abort.signal,
417
439
  onProgress: message => {
418
- if (!this.#disposed) this.#setStatus(theme.fg("dim", message));
440
+ if (!this.#disposed) this.#setStatus("dim", message);
419
441
  },
420
442
  });
421
443
  this.#sidebar.setGeneratedCommit(generated.commit);
422
- this.#setStatus(
423
- generated.validationError
424
- ? theme.fg("warning", `Generated message needs review: ${generated.validationError}`)
425
- : theme.fg(
426
- "success",
427
- generated.stagedAll
428
- ? "Staged all changes and generated commit message"
429
- : "Generated commit message",
430
- ),
431
- );
444
+ if (generated.validationError) {
445
+ this.#setStatus("warning", `Generated message needs review: ${generated.validationError}`);
446
+ } else {
447
+ this.#setStatus(
448
+ "success",
449
+ generated.stagedAll
450
+ ? "Staged all changes and generated commit message"
451
+ : "Generated commit message",
452
+ );
453
+ }
432
454
  } finally {
433
455
  if (this.#generationAbort === abort) this.#generationAbort = null;
434
456
  this.#sidebar.setGenerating(false);
@@ -439,7 +461,7 @@ class GitTuiComponent implements Component {
439
461
  if (action.stageAll) await this.#model.stage();
440
462
  await this.#model.commit(action.message, { amend: action.amend });
441
463
  this.#sidebar.clearForm();
442
- this.#setStatus(theme.fg("success", action.amend ? "Amended commit" : "Created commit"));
464
+ this.#setStatus("success", action.amend ? "Amended commit" : "Created commit");
443
465
  break;
444
466
  }
445
467
  }
@@ -455,7 +477,7 @@ class GitTuiComponent implements Component {
455
477
  if (!hunk.patch) return;
456
478
  if (action === "discard" && this.#pendingDiscard !== hunk.patch) {
457
479
  this.#pendingDiscard = hunk.patch;
458
- this.#setStatus(theme.fg("warning", `Discard hunk? Press ${formatKeyHint("x")} (or click) again to confirm`));
480
+ this.#setStatus("warning", `Discard hunk? Press ${formatKeyHint("x")} (or click) again to confirm`);
459
481
  return;
460
482
  }
461
483
  this.#pendingDiscard = null;
@@ -466,10 +488,8 @@ class GitTuiComponent implements Component {
466
488
  else if (action === "unstage") await this.#model.applyPatch(hunk.patch, { cached: true, reverse: true });
467
489
  else await this.#model.applyPatch(hunk.patch, { reverse: true });
468
490
  this.#setStatus(
469
- theme.fg(
470
- "success",
471
- action === "stage" ? "Staged hunk" : action === "unstage" ? "Unstaged hunk" : "Discarded hunk",
472
- ),
491
+ "success",
492
+ action === "stage" ? "Staged hunk" : action === "unstage" ? "Unstaged hunk" : "Discarded hunk",
473
493
  );
474
494
  await this.#refresh(true);
475
495
  } catch (error) {
@@ -486,12 +506,12 @@ class GitTuiComponent implements Component {
486
506
  const intent = action === "stage" ? "apply" : "revert";
487
507
  const patch = buildLineSelectionPatch(doc, span.from, span.to, intent);
488
508
  if (!patch) {
489
- this.#setStatus(theme.fg("warning", "Selection contains no changes"));
509
+ this.#setStatus("warning", "Selection contains no changes");
490
510
  return;
491
511
  }
492
512
  if (action === "discard" && this.#pendingDiscard !== patch) {
493
513
  this.#pendingDiscard = patch;
494
- this.#setStatus(theme.fg("warning", `Discard selected lines? Press ${formatKeyHint("x")} again to confirm`));
514
+ this.#setStatus("warning", `Discard selected lines? Press ${formatKeyHint("x")} again to confirm`);
495
515
  return;
496
516
  }
497
517
  this.#pendingDiscard = null;
@@ -503,14 +523,12 @@ class GitTuiComponent implements Component {
503
523
  await this.#model.applyPatch(patch, { cached: action !== "discard" });
504
524
  this.#pane.clearSelection();
505
525
  this.#setStatus(
506
- theme.fg(
507
- "success",
508
- action === "stage"
509
- ? "Staged selection"
510
- : action === "unstage"
511
- ? "Unstaged selection"
512
- : "Discarded selection",
513
- ),
526
+ "success",
527
+ action === "stage"
528
+ ? "Staged selection"
529
+ : action === "unstage"
530
+ ? "Unstaged selection"
531
+ : "Discarded selection",
514
532
  );
515
533
  await this.#refresh(true);
516
534
  } catch (error) {
@@ -520,8 +538,9 @@ class GitTuiComponent implements Component {
520
538
  }
521
539
  }
522
540
 
523
- #setStatus(text: string): void {
541
+ #setStatus(tone: StatusTone, text: string): void {
524
542
  this.#status = text;
543
+ this.#statusTone = tone;
525
544
  this.#statusAt = Date.now();
526
545
  this.#statusSticky = false;
527
546
  this.#ui.requestRender();
@@ -529,7 +548,7 @@ class GitTuiComponent implements Component {
529
548
  /** Persistent single-line error status; provider/git messages may span lines. */
530
549
  #setError(error: unknown): void {
531
550
  const message = error instanceof Error ? error.message : String(error);
532
- this.#setStatus(theme.fg("error", message.replace(/\s+/g, " ").trim()));
551
+ this.#setStatus("error", message.replace(/\s+/g, " ").trim());
533
552
  this.#statusSticky = true;
534
553
  }
535
554
 
@@ -567,14 +586,12 @@ class GitTuiComponent implements Component {
567
586
  this.#whitespace =
568
587
  this.#whitespace === "off" ? "whitespace" : this.#whitespace === "whitespace" ? "formatting" : "off";
569
588
  this.#setStatus(
570
- theme.fg(
571
- "dim",
572
- this.#whitespace === "off"
573
- ? "Showing all changes"
574
- : this.#whitespace === "whitespace"
575
- ? "Ignoring whitespace-only line changes"
576
- : "Ignoring formatting and import-only changes",
577
- ),
589
+ "dim",
590
+ this.#whitespace === "off"
591
+ ? "Showing all changes"
592
+ : this.#whitespace === "whitespace"
593
+ ? "Ignoring whitespace-only line changes"
594
+ : "Ignoring formatting and import-only changes",
578
595
  );
579
596
  this.#rebuildDocument();
580
597
  }
@@ -640,6 +657,7 @@ class GitTuiComponent implements Component {
640
657
  }
641
658
  if (data === "b") return this.#cycleWhitespace();
642
659
  if (data === "r") return void this.#refresh(true);
660
+ if (data === "?") return this.#toggleHelp();
643
661
  if (data === "c") {
644
662
  if (this.#sidebar.focusCommitForm()) this.#setFocus("sidebar");
645
663
  return;
@@ -766,14 +784,14 @@ class GitTuiComponent implements Component {
766
784
  right.add(" ").button(softPill(` ${glyphs.close} `), () => this.#done.resolve());
767
785
 
768
786
  // The empty middle carries the key hints (or a fresh status message).
769
- const status = this.#statusSticky || Date.now() - this.#statusAt < STATUS_TTL_MS ? this.#status : "";
787
+ const status = this.#visibleStatus() ? theme.fg(this.#statusTone, this.#status) : "";
770
788
  const middle =
771
789
  status ||
772
790
  theme.fg(
773
791
  "dim",
774
792
  this.#focus === "diff"
775
- ? `${formatKeyHints(["alt+down", "alt+up"])} hunk · ${formatKeyHints(["]", "["])} file · ${formatKeyHints(["shift+up", "shift+down"])} select · ${formatKeyHints(["s", "u"])} stage · ${formatKeyHints(["x", "delete"])} discard · ${formatKeyHint("v")} view · ${formatKeyHint("c")} commit · ${formatKeyHint("q")} quit`
776
- : `${formatKeyHints(["up", "down"])} move · ${formatKeyHints(["left", "right"])} fold · ${formatKeyHint("space")} stage · ${formatKeyHint("delete")} discard · ${formatKeyHint("enter")} open · ${formatKeyHints(["alt+down", "alt+up"])} hunk · ${formatKeyHint("c")} commit · ${formatKeyHint("t")} tree · ${formatKeyHint("q")} quit`,
793
+ ? `${formatKeyHints(["alt+down", "alt+up"])} hunk · ${formatKeyHints(["]", "["])} file · ${formatKeyHints(["shift+up", "shift+down"])} select · ${formatKeyHints(["s", "u"])} stage · ${formatKeyHints(["x", "delete"])} discard · ${formatKeyHint("v")} view · ${formatKeyHint("c")} commit · ${formatKeyHint("?")} keys · ${formatKeyHint("q")} quit`
794
+ : `${formatKeyHints(["up", "down"])} move · ${formatKeyHints(["left", "right"])} fold · ${formatKeyHint("space")} stage · ${formatKeyHint("delete")} discard · ${formatKeyHint("enter")} open · ${formatKeyHints(["alt+down", "alt+up"])} hunk · ${formatKeyHint("c")} commit · ${formatKeyHint("t")} tree · ${formatKeyHint("?")} keys · ${formatKeyHint("q")} quit`,
777
795
  );
778
796
  const free = width - row.width - right.width - 1;
779
797
  const middleText = free > visibleWidth(middle) + 4 ? middle : truncateToWidth(middle, Math.max(0, free - 4));
@@ -854,6 +872,335 @@ class GitTuiComponent implements Component {
854
872
  quit(): void {
855
873
  this.#done.resolve();
856
874
  }
875
+
876
+ /** A fresh or sticky status message shows; older ones fade out. */
877
+ #visibleStatus(): boolean {
878
+ return this.#status !== "" && (this.#statusSticky || Date.now() - this.#statusAt < STATUS_TTL_MS);
879
+ }
880
+
881
+ /** `?`: the keyboard shortcuts sheet over the app. */
882
+ #toggleHelp(): void {
883
+ if (this.#help) {
884
+ this.#help.hide();
885
+ this.#help = null;
886
+ return;
887
+ }
888
+ const sheet = new GitHelpSheet(() => this.#toggleHelp());
889
+ this.#help = this.#ui.showOverlay(sheet, { anchor: "center", width: "80%", maxHeight: "90%" });
890
+ this.#ui.setFocus(sheet);
891
+ this.#ui.requestRender();
892
+ }
893
+
894
+ // ── native ─────────────────────────────────────────────────────────────
895
+
896
+ /**
897
+ * The native git app (NATIVE_REDESIGN §10, Kraken's vocabulary): the diff
898
+ * pane under a toolbar (file, `+a −d`, scope, change navigation, the view
899
+ * segmented control, whitespace/wrap toggles, Stage file, close), and the
900
+ * changes sheet on the right. Narrow panes stack the sheet under the diff.
901
+ */
902
+ describe(cx: DescribeContext): NativeNode {
903
+ const narrow = cx.cols < NARROW_COLS;
904
+ const status = this.#visibleStatus();
905
+ const bar = this.#nativeBar.get(
906
+ [
907
+ this.#currentFile,
908
+ this.#pane.doc,
909
+ this.#pane.mode,
910
+ this.#pane.wrap,
911
+ this.#whitespace,
912
+ this.#contents,
913
+ narrow,
914
+ ],
915
+ () => this.#describeBar(narrow),
916
+ );
917
+ const body = this.#nativeBody.get(
918
+ [
919
+ this.#pane.state,
920
+ this.#pane.doc,
921
+ this.#pane.mode,
922
+ this.#contents,
923
+ this.#currentFile,
924
+ this.#pane.emptyMessage,
925
+ ],
926
+ () => this.#describeBody(),
927
+ );
928
+ const side = this.#sidebar.describe(cx);
929
+ return this.#nativeRoot.get([bar, body, side, narrow, status, this.#status, this.#statusTone], () => {
930
+ const main = col(
931
+ compact([
932
+ bar,
933
+ status &&
934
+ keyed(
935
+ text([span(this.#status, this.#statusTone === "dim" ? "muted" : this.#statusTone)], {
936
+ truncate: "end",
937
+ role: "omp.app.status",
938
+ }),
939
+ "status",
940
+ ),
941
+ body,
942
+ ]),
943
+ { role: "omp.app.git.main" },
944
+ );
945
+ return node(narrow ? "col" : "row", { role: narrow ? "omp.app.git-narrow" : "omp.app.git" }, [
946
+ keyed(main, "main"),
947
+ keyed(side, "side"),
948
+ ]);
949
+ });
950
+ }
951
+
952
+ /** Pointer actions: the toolbar's buttons and segmented control, else the changes sheet's. */
953
+ handleNativeEvent(event: NativeUiEvent): void {
954
+ if ((event.type === "select" || event.type === "activate") && event.key.endsWith("views")) {
955
+ const view = VIEW_TABS.find(tab => tab.id === event.item);
956
+ if (view) this.#setMode(view.id);
957
+ return;
958
+ }
959
+ if (event.type === "action") {
960
+ switch (event.act) {
961
+ case "prev-hunk":
962
+ return this.#jumpHunkOrFile(-1);
963
+ case "next-hunk":
964
+ return this.#jumpHunkOrFile(1);
965
+ case "whitespace":
966
+ return this.#cycleWhitespace();
967
+ case "wrap":
968
+ this.#pane.toggleWrap();
969
+ this.#ui.requestRender();
970
+ return;
971
+ case "stage-file":
972
+ return this.#stageCurrentFile();
973
+ case "help":
974
+ return this.#toggleHelp();
975
+ case "close":
976
+ this.#done.resolve();
977
+ return;
978
+ }
979
+ }
980
+ if (this.#sidebar.handleNativeEvent(event)) {
981
+ if (this.#focus !== "sidebar") this.#setFocus("sidebar");
982
+ else this.#ui.requestRender();
983
+ }
984
+ }
985
+
986
+ #describeBar(narrow: boolean): NativeNode {
987
+ const file = this.#currentFile;
988
+ const doc = this.#pane.doc;
989
+ const left: NativeNode[] = [];
990
+ if (file) {
991
+ const slash = file.path.lastIndexOf("/");
992
+ left.push(
993
+ text([span(file.path.slice(0, slash + 1), "dim"), span(file.path.slice(slash + 1), "strong")], {
994
+ truncate: "start",
995
+ role: "omp.app.git.path",
996
+ title: file.origPath ? `${file.origPath} → ${file.path}` : file.path,
997
+ }),
998
+ );
999
+ if (doc) {
1000
+ left.push(
1001
+ text([span(`+${doc.additions}`, "ins num"), span(" "), span(`−${doc.deletions}`, "del num")], {
1002
+ role: "omp.app.git.stat",
1003
+ }),
1004
+ );
1005
+ }
1006
+ const scope =
1007
+ file.area === "staged"
1008
+ ? { text: "Staged", tone: "success" as const }
1009
+ : file.area === "unstaged"
1010
+ ? { text: file.kind === "untracked" ? "Untracked" : "Unstaged", tone: "warning" as const }
1011
+ : { text: this.#model.headCommit?.shortSha ?? "commit", tone: "accent" as const };
1012
+ left.push(node("badge", { text: scope.text, tone: scope.tone }));
1013
+ } else {
1014
+ left.push(text([span(this.#model.branch ?? "detached", "muted")], { role: "omp.app.git.path" }));
1015
+ }
1016
+ const iconButton = (name: string, act: string, title: string, on?: boolean): NativeNode =>
1017
+ node(
1018
+ "icon",
1019
+ { name, title, aria: title, role: on ? "omp.app.ibtn.set" : "omp.app.ibtn", actions: { click: act } },
1020
+ undefined,
1021
+ act,
1022
+ );
1023
+ const right: NativeNode[] = [
1024
+ row(
1025
+ [
1026
+ iconButton("chev-up", "prev-hunk", `Previous change (${formatKeyHint("alt+up")})`),
1027
+ iconButton("chev", "next-hunk", `Next change (${formatKeyHint("alt+down")})`),
1028
+ ],
1029
+ { gap: "none", align: "center" },
1030
+ ),
1031
+ node(
1032
+ "tabs",
1033
+ {
1034
+ items: VIEW_TABS.map(tab => ({ id: tab.id, label: tab.label })),
1035
+ active: this.#pane.mode,
1036
+ role: "omp.app.seg",
1037
+ },
1038
+ undefined,
1039
+ "views",
1040
+ ),
1041
+ ];
1042
+ if (!narrow) {
1043
+ right.push(
1044
+ iconButton(
1045
+ "type",
1046
+ "whitespace",
1047
+ this.#whitespace === "off"
1048
+ ? `Ignore whitespace (${formatKeyHint("b")})`
1049
+ : this.#whitespace === "whitespace"
1050
+ ? `Ignoring whitespace; also ignore formatting (${formatKeyHint("b")})`
1051
+ : `Ignoring formatting and imports; show all (${formatKeyHint("b")})`,
1052
+ this.#whitespace !== "off",
1053
+ ),
1054
+ iconButton("corner-down-right", "wrap", `Wrap lines (${formatKeyHint("w")})`, this.#pane.wrap),
1055
+ );
1056
+ }
1057
+ if (file?.area === "unstaged" || file?.area === "staged") {
1058
+ right.push(
1059
+ actionButton(file.area === "unstaged" ? "Stage file" : "Unstage file", "stage-file", {
1060
+ keys: "s",
1061
+ title: `${file.area === "unstaged" ? "Stage" : "Unstage"} ${file.path}`,
1062
+ }),
1063
+ );
1064
+ }
1065
+ right.push(
1066
+ iconButton("keyboard", "help", `Keyboard shortcuts (${formatKeyHint("?")})`),
1067
+ iconButton("x", "close", `Close (${formatKeyHint("q")})`),
1068
+ );
1069
+ return keyed(
1070
+ row(
1071
+ [
1072
+ row(left, { gap: "sm", align: "center", role: "omp.app.git.where" }),
1073
+ row(right, { gap: "sm", align: "center", role: "omp.app.git.tools" }),
1074
+ ],
1075
+ { justify: "between", align: "center", role: "omp.app.git.bar" },
1076
+ ),
1077
+ "bar",
1078
+ );
1079
+ }
1080
+
1081
+ /** The diff for the view mode, or the loading, empty and media states. */
1082
+ #describeBody(): NativeNode {
1083
+ const doc = this.#pane.doc;
1084
+ const file = this.#currentFile;
1085
+ if (doc && this.#pane.state === "ready") {
1086
+ return node(
1087
+ "diff",
1088
+ {
1089
+ text: unifiedDiff(doc, this.#pane.mode === "file"),
1090
+ path: doc.filePath,
1091
+ mode: this.#pane.mode === "split" ? "split" : "unified",
1092
+ role: "omp.app.git.diff",
1093
+ },
1094
+ undefined,
1095
+ "diff",
1096
+ );
1097
+ }
1098
+ const contents = this.#contents;
1099
+ if (this.#pane.state === "asset" && file && contents?.kind === "asset") {
1100
+ return keyed(
1101
+ col(
1102
+ [
1103
+ text([span("Binary or media file", "strong")]),
1104
+ node("kv", {
1105
+ items: [
1106
+ { k: "before", v: assetText(contents.old) },
1107
+ { k: "after", v: assetText(contents.new) },
1108
+ ],
1109
+ layout: "grid",
1110
+ }),
1111
+ ],
1112
+ { gap: "sm", role: "omp.app.git.state" },
1113
+ ),
1114
+ "asset",
1115
+ );
1116
+ }
1117
+ if (this.#pane.state === "streaming" || this.#pane.state === "loading") {
1118
+ return keyed(
1119
+ row([node("spinner", { style: "dots" }), text([span(`Loading ${file?.path ?? "diff"}…`, "muted")])], {
1120
+ gap: "sm",
1121
+ align: "center",
1122
+ role: "omp.app.git.state",
1123
+ }),
1124
+ "loading",
1125
+ );
1126
+ }
1127
+ return keyed(
1128
+ col(
1129
+ [
1130
+ text([span(this.#pane.emptyMessage, "strong")]),
1131
+ text([
1132
+ span(
1133
+ this.#model.clean ? "The working tree is clean." : "Pick a file on the right to see its changes.",
1134
+ "muted",
1135
+ ),
1136
+ ]),
1137
+ ],
1138
+ { gap: "xs", role: "omp.app.git.empty" },
1139
+ ),
1140
+ "empty",
1141
+ );
1142
+ }
1143
+ }
1144
+
1145
+ /**
1146
+ * A unified diff of `doc` for the `diff` kind: the tight hunks (Tern folds
1147
+ * the unchanged lines between them), or with `whole` one hunk spanning the
1148
+ * file. Paired changes go out as their `-` block then their `+` block, so the
1149
+ * terminal pairs them for word emphasis.
1150
+ */
1151
+ function unifiedDiff(doc: DiffDocument, whole: boolean): string {
1152
+ const out: string[] = [];
1153
+ const emit = (rows: DiffDocument["rows"]): void => {
1154
+ let dels: string[] = [];
1155
+ let adds: string[] = [];
1156
+ const flush = (): void => {
1157
+ out.push(...dels, ...adds);
1158
+ dels = [];
1159
+ adds = [];
1160
+ };
1161
+ for (const diffRow of rows) {
1162
+ if (diffRow.kind === "context") {
1163
+ flush();
1164
+ out.push(` ${diffRow.newText}`);
1165
+ continue;
1166
+ }
1167
+ if (diffRow.oldNum !== undefined) dels.push(`-${diffRow.oldText}`);
1168
+ if (diffRow.newNum !== undefined) adds.push(`+${diffRow.newText}`);
1169
+ }
1170
+ flush();
1171
+ };
1172
+ if (whole) {
1173
+ const oldCount = doc.rows.filter(diffRow => diffRow.oldNum !== undefined).length;
1174
+ const newCount = doc.rows.filter(diffRow => diffRow.newNum !== undefined).length;
1175
+ out.push(`@@ -1,${oldCount} +1,${newCount} @@`);
1176
+ emit(doc.rows);
1177
+ } else {
1178
+ for (const hunk of doc.hunks) {
1179
+ out.push(hunk.header);
1180
+ emit(hunk.rows);
1181
+ }
1182
+ }
1183
+ return out.join("\n");
1184
+ }
1185
+
1186
+ /** One side of a media/binary change as a short fact line. */
1187
+ function assetText(asset: AssetFileContents["old"]): TspSpan[] {
1188
+ const size = (bytes: number | undefined): string =>
1189
+ bytes === undefined ? "" : bytes < 1024 ? ` · ${bytes} B` : ` · ${(bytes / 1024).toFixed(1)} KB`;
1190
+ switch (asset.kind) {
1191
+ case "empty":
1192
+ return [span("none", "dim")];
1193
+ case "text":
1194
+ return [span(`text${size(asset.byteLength)}`)];
1195
+ case "image":
1196
+ return [span(`image ${asset.image.widthPx}×${asset.image.heightPx}${size(asset.image.byteLength)}`)];
1197
+ case "binary":
1198
+ return [span(`binary${size(asset.byteLength)}`)];
1199
+ case "tooLarge":
1200
+ return [span(`too large to diff${size(asset.byteLength)}`)];
1201
+ case "lfsMissing":
1202
+ return [span(`LFS object ${asset.oid.slice(0, 12)} not fetched${size(asset.byteLength)}`)];
1203
+ }
857
1204
  }
858
1205
 
859
1206
  /**