@oh-my-pi/pi-tui 18.4.3 → 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 (447) hide show
  1. package/CHANGELOG.md +36 -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/todo-reminder.d.ts +4 -1
  22. package/dist/types/chat/tool-execution.d.ts +25 -1
  23. package/dist/types/chat/transcript-actions.d.ts +37 -0
  24. package/dist/types/chat/transcript-entry.d.ts +3 -1
  25. package/dist/types/chat/user-message.d.ts +25 -0
  26. package/dist/types/chrome/collab-qrcode.d.ts +7 -0
  27. package/dist/types/chrome/context-thresholds.d.ts +11 -0
  28. package/dist/types/chrome/countdown-timer.d.ts +9 -3
  29. package/dist/types/chrome/diff.d.ts +12 -0
  30. package/dist/types/chrome/dynamic-border.d.ts +3 -0
  31. package/dist/types/chrome/format.d.ts +6 -0
  32. package/dist/types/chrome/message-divider.d.ts +13 -0
  33. package/dist/types/chrome/message-frame.d.ts +10 -0
  34. package/dist/types/chrome/message-notice.d.ts +23 -0
  35. package/dist/types/chrome/overlay-box.d.ts +11 -2
  36. package/dist/types/chrome/qrcode.d.ts +7 -0
  37. package/dist/types/chrome/segment-track.d.ts +7 -0
  38. package/dist/types/chrome/selector-helpers.d.ts +0 -6
  39. package/dist/types/chrome/status-notice.d.ts +14 -0
  40. package/dist/types/chrome/streaming-panel.d.ts +3 -0
  41. package/dist/types/chrome/tool-activity.d.ts +3 -0
  42. package/dist/types/chrome/transcript-container.d.ts +13 -0
  43. package/dist/types/chrome/transcript-status.d.ts +15 -3
  44. package/dist/types/components/box.d.ts +7 -0
  45. package/dist/types/components/disclosure.d.ts +9 -0
  46. package/dist/types/components/editor.d.ts +37 -0
  47. package/dist/types/components/form.d.ts +14 -0
  48. package/dist/types/components/image.d.ts +7 -0
  49. package/dist/types/components/input.d.ts +20 -1
  50. package/dist/types/components/key-value-list.d.ts +3 -0
  51. package/dist/types/components/layout/geometry.d.ts +16 -0
  52. package/dist/types/components/layout/row.d.ts +7 -0
  53. package/dist/types/components/layout/split-pane.d.ts +8 -0
  54. package/dist/types/components/layout/stack.d.ts +6 -0
  55. package/dist/types/components/loader.d.ts +48 -0
  56. package/dist/types/components/markdown.d.ts +8 -0
  57. package/dist/types/components/metric.d.ts +7 -0
  58. package/dist/types/components/progress-bar.d.ts +3 -0
  59. package/dist/types/components/scroll-view.d.ts +6 -0
  60. package/dist/types/components/section.d.ts +3 -0
  61. package/dist/types/components/select-list.d.ts +33 -0
  62. package/dist/types/components/settings-list.d.ts +68 -0
  63. package/dist/types/components/spacer.d.ts +4 -0
  64. package/dist/types/components/tab-bar.d.ts +9 -0
  65. package/dist/types/components/table.d.ts +7 -0
  66. package/dist/types/components/text.d.ts +6 -0
  67. package/dist/types/components/tree-view.d.ts +9 -0
  68. package/dist/types/components/truncated-text.d.ts +3 -0
  69. package/dist/types/components/wizard-step.d.ts +6 -0
  70. package/dist/types/hotkeys-markdown.d.ts +24 -0
  71. package/dist/types/index.d.ts +5 -0
  72. package/dist/types/latex-block.d.ts +7 -0
  73. package/dist/types/native/apply.d.ts +39 -0
  74. package/dist/types/native/backend.d.ts +94 -0
  75. package/dist/types/native/blobs.d.ts +29 -0
  76. package/dist/types/native/describe.d.ts +56 -0
  77. package/dist/types/native/encode.d.ts +50 -0
  78. package/dist/types/native/icons.d.ts +18 -0
  79. package/dist/types/native/memo.d.ts +27 -0
  80. package/dist/types/native/node.d.ts +107 -0
  81. package/dist/types/native/overlay.d.ts +64 -0
  82. package/dist/types/native/picker.d.ts +124 -0
  83. package/dist/types/native/reconcile.d.ts +41 -0
  84. package/dist/types/native/settle.d.ts +11 -0
  85. package/dist/types/native/spans.d.ts +21 -0
  86. package/dist/types/native/state.d.ts +6 -0
  87. package/dist/types/native/tone.d.ts +24 -0
  88. package/dist/types/overlays/advisor-config.d.ts +20 -0
  89. package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
  90. package/dist/types/overlays/agent-hub.d.ts +10 -0
  91. package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
  92. package/dist/types/overlays/agents-hub.d.ts +8 -0
  93. package/dist/types/overlays/annotation-overlay.d.ts +3 -0
  94. package/dist/types/overlays/ask-dialog.d.ts +23 -0
  95. package/dist/types/overlays/bordered-loader.d.ts +3 -0
  96. package/dist/types/overlays/btw-history-panel.d.ts +9 -0
  97. package/dist/types/overlays/btw-panel.d.ts +3 -0
  98. package/dist/types/overlays/cleanse-panel.d.ts +5 -2
  99. package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
  100. package/dist/types/overlays/copy-selector.d.ts +38 -0
  101. package/dist/types/overlays/error-banner.d.ts +9 -0
  102. package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
  103. package/dist/types/overlays/history-search.d.ts +18 -0
  104. package/dist/types/overlays/hook-editor.d.ts +28 -1
  105. package/dist/types/overlays/hook-input.d.ts +3 -0
  106. package/dist/types/overlays/hook-selector.d.ts +10 -0
  107. package/dist/types/overlays/hub-frame.d.ts +18 -0
  108. package/dist/types/overlays/jobs-panel.d.ts +24 -0
  109. package/dist/types/overlays/login-dialog.d.ts +10 -0
  110. package/dist/types/overlays/logout-account-selector.d.ts +4 -0
  111. package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
  112. package/dist/types/overlays/model-browser.d.ts +69 -0
  113. package/dist/types/overlays/model-hub.d.ts +7 -0
  114. package/dist/types/overlays/model-picker.d.ts +7 -0
  115. package/dist/types/overlays/move-overlay.d.ts +3 -0
  116. package/dist/types/overlays/oauth-selector.d.ts +4 -0
  117. package/dist/types/overlays/omfg-panel.d.ts +3 -0
  118. package/dist/types/overlays/pause-screen.d.ts +12 -0
  119. package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
  120. package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
  121. package/dist/types/overlays/plugin-selector.d.ts +4 -0
  122. package/dist/types/overlays/plugin-settings.d.ts +30 -0
  123. package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
  124. package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
  125. package/dist/types/overlays/rewind-selector.d.ts +5 -0
  126. package/dist/types/overlays/session-info-overlay.d.ts +19 -1
  127. package/dist/types/overlays/session-selector.d.ts +64 -1
  128. package/dist/types/overlays/settings-defs.d.ts +14 -0
  129. package/dist/types/overlays/settings-selector.d.ts +21 -0
  130. package/dist/types/overlays/show-images-selector.d.ts +4 -0
  131. package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
  132. package/dist/types/overlays/stats-notice.d.ts +8 -0
  133. package/dist/types/overlays/theme-selector.d.ts +11 -0
  134. package/dist/types/overlays/thinking-selector.d.ts +4 -0
  135. package/dist/types/overlays/tree-selector.d.ts +60 -2
  136. package/dist/types/overlays/usage-dashboard.d.ts +26 -3
  137. package/dist/types/overlays/usage-row.d.ts +36 -2
  138. package/dist/types/prompt/attachment-chips.d.ts +8 -0
  139. package/dist/types/prompt/composer-attachments.d.ts +15 -3
  140. package/dist/types/prompt/composer.d.ts +18 -1
  141. package/dist/types/prompt/custom-editor.d.ts +48 -2
  142. package/dist/types/prompt/editor-top-gap.d.ts +2 -0
  143. package/dist/types/prompt/macos-spelling.d.ts +6 -0
  144. package/dist/types/prompt/magic-keywords.d.ts +9 -0
  145. package/dist/types/prompt/queued-messages.d.ts +23 -0
  146. package/dist/types/prompt/skill-tokens.d.ts +4 -2
  147. package/dist/types/prompt/welcome.d.ts +16 -0
  148. package/dist/types/render/code-cell.d.ts +10 -0
  149. package/dist/types/render/file-list.d.ts +8 -0
  150. package/dist/types/render/hyperlink.d.ts +14 -0
  151. package/dist/types/render/output-block.d.ts +43 -0
  152. package/dist/types/render/output-pane.d.ts +17 -0
  153. package/dist/types/render/tool-card.d.ts +8 -0
  154. package/dist/types/render/tree-list.d.ts +8 -0
  155. package/dist/types/render/width-aware-text.d.ts +7 -0
  156. package/dist/types/setup/scenes/outro.d.ts +7 -0
  157. package/dist/types/setup/scenes/sign-in.d.ts +7 -0
  158. package/dist/types/setup/scenes/splash.d.ts +8 -0
  159. package/dist/types/setup/scenes/types.d.ts +6 -1
  160. package/dist/types/setup/wizard-overlay.d.ts +9 -0
  161. package/dist/types/status-line/component.d.ts +49 -5
  162. package/dist/types/status-line/context-usage.d.ts +20 -0
  163. package/dist/types/status-line/footer.d.ts +8 -0
  164. package/dist/types/status-line/metrics.d.ts +1 -2
  165. package/dist/types/status-line/segments.d.ts +10 -1
  166. package/dist/types/status-line/types.d.ts +40 -1
  167. package/dist/types/terminal-capabilities.d.ts +1 -1
  168. package/dist/types/terminal.d.ts +27 -0
  169. package/dist/types/theme/defaults/index.d.ts +100 -0
  170. package/dist/types/theme/schema.d.ts +2 -0
  171. package/dist/types/theme/shimmer.d.ts +8 -0
  172. package/dist/types/theme/theme-class.d.ts +2 -0
  173. package/dist/types/theme/theme.d.ts +33 -1
  174. package/dist/types/tools/ask.d.ts +3 -0
  175. package/dist/types/tools/ast-edit.d.ts +10 -1
  176. package/dist/types/tools/ast-grep.d.ts +10 -1
  177. package/dist/types/tools/autoresearch.d.ts +21 -4
  178. package/dist/types/tools/bash.d.ts +30 -1
  179. package/dist/types/tools/cfg-render.d.ts +5 -1
  180. package/dist/types/tools/debug.d.ts +3 -1
  181. package/dist/types/tools/default-renderer.d.ts +6 -1
  182. package/dist/types/tools/edit.d.ts +14 -1
  183. package/dist/types/tools/eval.d.ts +12 -1
  184. package/dist/types/tools/fetch.d.ts +16 -1
  185. package/dist/types/tools/find.d.ts +15 -1
  186. package/dist/types/tools/github.d.ts +3 -0
  187. package/dist/types/tools/glob.d.ts +14 -1
  188. package/dist/types/tools/goal.d.ts +3 -1
  189. package/dist/types/tools/grep.d.ts +14 -1
  190. package/dist/types/tools/grouped-file-output.d.ts +58 -0
  191. package/dist/types/tools/json-tree.d.ts +20 -0
  192. package/dist/types/tools/lsp.d.ts +3 -10
  193. package/dist/types/tools/mcp.d.ts +16 -1
  194. package/dist/types/tools/memory.d.ts +26 -0
  195. package/dist/types/tools/native-view.d.ts +90 -0
  196. package/dist/types/tools/proc-render.d.ts +7 -1
  197. package/dist/types/tools/read.d.ts +23 -1
  198. package/dist/types/tools/renderer.d.ts +57 -0
  199. package/dist/types/tools/report-tool-issue.d.ts +3 -0
  200. package/dist/types/tools/resolve.d.ts +14 -1
  201. package/dist/types/tools/task.d.ts +3 -0
  202. package/dist/types/tools/think.d.ts +3 -0
  203. package/dist/types/tools/todo.d.ts +12 -0
  204. package/dist/types/tools/vibe.d.ts +3 -1
  205. package/dist/types/tools/wait.d.ts +3 -0
  206. package/dist/types/tools/web-search.d.ts +8 -0
  207. package/dist/types/tools/write.d.ts +14 -1
  208. package/dist/types/tools/xdev.d.ts +13 -0
  209. package/dist/types/tui.d.ts +49 -0
  210. package/package.json +9 -9
  211. package/src/apps/autoresearch-dashboard.ts +362 -4
  212. package/src/apps/cleanse-board.ts +219 -1
  213. package/src/apps/cleanse-picker.ts +9 -1
  214. package/src/apps/debug/log-viewer.ts +15 -15
  215. package/src/apps/debug/protocol-probe.ts +2 -20
  216. package/src/apps/git/avatar.ts +2 -22
  217. package/src/apps/git/git-tui.ts +406 -59
  218. package/src/apps/git/help.ts +130 -0
  219. package/src/apps/git/sidebar.ts +491 -0
  220. package/src/apps/live-visualizer.ts +65 -0
  221. package/src/apps/ps-top.ts +448 -17
  222. package/src/apps/session-picker.ts +1 -0
  223. package/src/apps/setup-model-picker.ts +5 -1
  224. package/src/apps/standalone-picker.ts +55 -1
  225. package/src/autocomplete.ts +44 -1
  226. package/src/chat/advisor-message.ts +72 -3
  227. package/src/chat/assistant-message.ts +346 -11
  228. package/src/chat/background-tan-message.ts +4 -4
  229. package/src/chat/bash-execution.ts +81 -0
  230. package/src/chat/cache-invalidation-marker.ts +8 -0
  231. package/src/chat/chat-transcript-builder.ts +37 -6
  232. package/src/chat/collab-prompt-message.ts +15 -3
  233. package/src/chat/compaction-summary-message.ts +74 -1
  234. package/src/chat/custom-message.ts +3 -1
  235. package/src/chat/eval-execution.ts +57 -0
  236. package/src/chat/execution-shared.ts +153 -1
  237. package/src/chat/hook-message.ts +1 -0
  238. package/src/chat/late-diagnostics-message.ts +85 -2
  239. package/src/chat/read-tool-group.ts +341 -1
  240. package/src/chat/served-model-marker.ts +8 -0
  241. package/src/chat/skill-message.ts +63 -0
  242. package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
  243. package/src/chat/todo-reminder.ts +34 -4
  244. package/src/chat/tool-execution.ts +385 -10
  245. package/src/chat/transcript-actions.ts +38 -0
  246. package/src/chat/transcript-browser.ts +2 -9
  247. package/src/chat/transcript-entry.ts +10 -1
  248. package/src/chat/transcript-render-helpers.ts +16 -15
  249. package/src/chat/ttsr-notification.ts +38 -1
  250. package/src/chat/user-message.ts +167 -4
  251. package/src/chrome/collab-qrcode.ts +27 -2
  252. package/src/chrome/context-thresholds.ts +34 -0
  253. package/src/chrome/countdown-timer.ts +24 -1
  254. package/src/chrome/diff.ts +60 -1
  255. package/src/chrome/dynamic-border.ts +7 -0
  256. package/src/chrome/format.ts +12 -0
  257. package/src/chrome/message-divider.ts +33 -1
  258. package/src/chrome/message-frame.ts +53 -10
  259. package/src/chrome/message-notice.ts +71 -0
  260. package/src/chrome/overlay-box.ts +45 -2
  261. package/src/chrome/qrcode.ts +28 -0
  262. package/src/chrome/segment-track.ts +26 -0
  263. package/src/chrome/selector-helpers.ts +1 -17
  264. package/src/chrome/status-notice.ts +46 -0
  265. package/src/chrome/streaming-panel.ts +25 -1
  266. package/src/chrome/tool-activity.ts +20 -0
  267. package/src/chrome/transcript-container.ts +37 -0
  268. package/src/chrome/transcript-status.ts +40 -11
  269. package/src/components/box.ts +51 -0
  270. package/src/components/disclosure.ts +33 -0
  271. package/src/components/editor.ts +216 -27
  272. package/src/components/form.ts +93 -9
  273. package/src/components/image.ts +30 -0
  274. package/src/components/input.ts +77 -26
  275. package/src/components/key-value-list.ts +18 -0
  276. package/src/components/layout/geometry.ts +44 -0
  277. package/src/components/layout/row.ts +30 -0
  278. package/src/components/layout/split-pane.ts +65 -0
  279. package/src/components/layout/stack.ts +27 -0
  280. package/src/components/loader.ts +183 -1
  281. package/src/components/markdown.ts +21 -0
  282. package/src/components/metric.ts +39 -0
  283. package/src/components/progress-bar.ts +28 -0
  284. package/src/components/scroll-view.ts +23 -0
  285. package/src/components/section.ts +16 -0
  286. package/src/components/select-list.ts +174 -25
  287. package/src/components/settings-list.ts +264 -7
  288. package/src/components/spacer.ts +17 -0
  289. package/src/components/tab-bar.ts +42 -0
  290. package/src/components/table.ts +34 -0
  291. package/src/components/text.ts +23 -0
  292. package/src/components/tree-view.ts +65 -0
  293. package/src/components/truncated-text.ts +14 -0
  294. package/src/components/wizard-step.ts +18 -9
  295. package/src/debug-server.ts +14 -7
  296. package/src/hotkeys-markdown.ts +251 -69
  297. package/src/index.ts +6 -0
  298. package/src/latex-block.ts +11 -0
  299. package/src/native/apply.ts +275 -0
  300. package/src/native/backend.ts +682 -0
  301. package/src/native/blobs.ts +70 -0
  302. package/src/native/describe.ts +124 -0
  303. package/src/native/encode.ts +233 -0
  304. package/src/native/icons.ts +184 -0
  305. package/src/native/memo.ts +64 -0
  306. package/src/native/node.ts +112 -0
  307. package/src/native/overlay.ts +131 -0
  308. package/src/native/picker.ts +333 -0
  309. package/src/native/reconcile.ts +1063 -0
  310. package/src/native/settle.ts +23 -0
  311. package/src/native/spans.ts +308 -0
  312. package/src/native/state.ts +28 -0
  313. package/src/native/tone.ts +79 -0
  314. package/src/overlays/advisor-config.ts +420 -0
  315. package/src/overlays/agent-hub-renderer.ts +155 -18
  316. package/src/overlays/agent-hub.ts +1037 -44
  317. package/src/overlays/agent-transcript-viewer.ts +129 -0
  318. package/src/overlays/agents-hub.ts +688 -53
  319. package/src/overlays/annotation-overlay.ts +391 -52
  320. package/src/overlays/ask-dialog.ts +435 -24
  321. package/src/overlays/bordered-loader.ts +17 -0
  322. package/src/overlays/btw-history-panel.ts +182 -0
  323. package/src/overlays/btw-panel.ts +68 -2
  324. package/src/overlays/cleanse-panel.ts +105 -15
  325. package/src/overlays/codex-reset-fireworks.ts +65 -5
  326. package/src/overlays/composer-shape-preview.ts +26 -1
  327. package/src/overlays/copy-selector.ts +545 -23
  328. package/src/overlays/error-banner.ts +44 -0
  329. package/src/overlays/extensions/extension-list.ts +23 -17
  330. package/src/overlays/history-search.ts +175 -33
  331. package/src/overlays/hook-editor.ts +113 -6
  332. package/src/overlays/hook-input.ts +22 -1
  333. package/src/overlays/hook-selector.ts +311 -22
  334. package/src/overlays/hub-frame.ts +80 -0
  335. package/src/overlays/jobs-panel.ts +112 -0
  336. package/src/overlays/login-dialog.ts +183 -1
  337. package/src/overlays/logout-account-selector.ts +102 -4
  338. package/src/overlays/mcp-add-wizard.ts +232 -53
  339. package/src/overlays/model-browser.ts +681 -22
  340. package/src/overlays/model-hub.ts +1402 -78
  341. package/src/overlays/model-picker.ts +175 -1
  342. package/src/overlays/move-overlay.ts +45 -0
  343. package/src/overlays/oauth-selector.ts +227 -23
  344. package/src/overlays/omfg-panel.ts +86 -2
  345. package/src/overlays/pause-screen.ts +55 -4
  346. package/src/overlays/plan-review-overlay.ts +352 -0
  347. package/src/overlays/plan-save-overlay.ts +45 -0
  348. package/src/overlays/plugin-selector.ts +21 -1
  349. package/src/overlays/plugin-settings.ts +195 -5
  350. package/src/overlays/queue-mode-selector.ts +19 -1
  351. package/src/overlays/reset-usage-selector.ts +110 -40
  352. package/src/overlays/rewind-selector.ts +458 -44
  353. package/src/overlays/session-account-selector.ts +1 -1
  354. package/src/overlays/session-info-overlay.ts +137 -1
  355. package/src/overlays/session-selector.ts +807 -46
  356. package/src/overlays/settings-defs.ts +41 -0
  357. package/src/overlays/settings-selector.ts +583 -23
  358. package/src/overlays/show-images-selector.ts +18 -1
  359. package/src/overlays/snapcompact-shape-preview.ts +48 -3
  360. package/src/overlays/stats-notice.ts +31 -0
  361. package/src/overlays/theme-selector.ts +31 -1
  362. package/src/overlays/thinking-selector.ts +22 -1
  363. package/src/overlays/tree-selector.ts +570 -110
  364. package/src/overlays/usage-dashboard.ts +587 -12
  365. package/src/overlays/usage-row.ts +161 -25
  366. package/src/prompt/attachment-chips.ts +60 -0
  367. package/src/prompt/composer-attachments.ts +31 -9
  368. package/src/prompt/composer.ts +55 -2
  369. package/src/prompt/custom-editor.ts +302 -4
  370. package/src/prompt/editor-top-gap.ts +8 -0
  371. package/src/prompt/macos-spelling.ts +24 -9
  372. package/src/prompt/magic-keywords.ts +22 -1
  373. package/src/prompt/model-mention-autocomplete.ts +1 -0
  374. package/src/prompt/queued-messages.ts +101 -0
  375. package/src/prompt/skill-tokens.ts +5 -3
  376. package/src/prompt/welcome.ts +213 -6
  377. package/src/render/code-cell.ts +103 -2
  378. package/src/render/file-list.ts +29 -0
  379. package/src/render/hyperlink.ts +32 -0
  380. package/src/render/output-block.ts +110 -0
  381. package/src/render/output-pane.ts +37 -0
  382. package/src/render/tool-card.ts +137 -0
  383. package/src/render/tree-list.ts +39 -0
  384. package/src/render/width-aware-text.ts +20 -0
  385. package/src/setup/scenes/composer.ts +29 -0
  386. package/src/setup/scenes/glyph.ts +15 -0
  387. package/src/setup/scenes/model.ts +36 -8
  388. package/src/setup/scenes/outro.ts +28 -1
  389. package/src/setup/scenes/sign-in.ts +122 -13
  390. package/src/setup/scenes/splash.ts +28 -1
  391. package/src/setup/scenes/theme.ts +88 -3
  392. package/src/setup/scenes/types.ts +7 -1
  393. package/src/setup/startup-splash.ts +46 -9
  394. package/src/setup/wizard-overlay.ts +140 -23
  395. package/src/status-line/component.ts +398 -16
  396. package/src/status-line/context-usage.ts +279 -50
  397. package/src/status-line/footer.ts +94 -1
  398. package/src/status-line/metrics.ts +3 -6
  399. package/src/status-line/segments.ts +463 -97
  400. package/src/status-line/separators.ts +3 -16
  401. package/src/status-line/types.ts +43 -1
  402. package/src/terminal-capabilities.ts +7 -0
  403. package/src/terminal.ts +130 -2
  404. package/src/theme/defaults/dark-neon-noir.json +100 -0
  405. package/src/theme/defaults/index.ts +2 -0
  406. package/src/theme/defaults/light-prism.json +5 -5
  407. package/src/theme/defaults/onyx.json +62 -62
  408. package/src/theme/schema.ts +15 -0
  409. package/src/theme/shimmer.ts +33 -0
  410. package/src/theme/theme-class.ts +24 -19
  411. package/src/theme/theme.ts +111 -9
  412. package/src/tools/ask.ts +157 -1
  413. package/src/tools/ast-edit.ts +174 -1
  414. package/src/tools/ast-grep.ts +82 -2
  415. package/src/tools/autoresearch.ts +149 -1
  416. package/src/tools/bash.ts +180 -49
  417. package/src/tools/cfg-render.ts +66 -1
  418. package/src/tools/debug.ts +78 -1
  419. package/src/tools/default-renderer.ts +35 -1
  420. package/src/tools/edit.ts +247 -2
  421. package/src/tools/eval.ts +257 -1
  422. package/src/tools/fetch.ts +102 -3
  423. package/src/tools/find.ts +127 -1
  424. package/src/tools/github.ts +119 -10
  425. package/src/tools/glob.ts +116 -1
  426. package/src/tools/goal.ts +56 -1
  427. package/src/tools/grep.ts +83 -2
  428. package/src/tools/grouped-file-output.ts +205 -0
  429. package/src/tools/json-tree.ts +116 -1
  430. package/src/tools/lsp.ts +318 -1
  431. package/src/tools/mcp.ts +80 -1
  432. package/src/tools/memory.ts +93 -11
  433. package/src/tools/native-view.ts +292 -0
  434. package/src/tools/proc-render.ts +221 -1
  435. package/src/tools/read.ts +199 -4
  436. package/src/tools/renderer.ts +61 -0
  437. package/src/tools/report-tool-issue.ts +7 -1
  438. package/src/tools/resolve.ts +63 -1
  439. package/src/tools/task.ts +406 -4
  440. package/src/tools/think.ts +19 -1
  441. package/src/tools/todo.ts +89 -0
  442. package/src/tools/vibe.ts +230 -2
  443. package/src/tools/wait.ts +181 -1
  444. package/src/tools/web-search.ts +113 -1
  445. package/src/tools/write.ts +144 -5
  446. package/src/tools/xdev.ts +64 -1
  447. package/src/tui.ts +294 -3
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Keyboard shortcuts sheet of the git TUI (`?`).
3
+ *
4
+ * The keys are the git TUI's own (see `git-tui.ts` and `sidebar.ts`); this
5
+ * sheet only lists them. ANSI draws grouped `key description` rows; the
6
+ * native render is the hotkeys sheet (NATIVE_REDESIGN §9.7): a glass sheet
7
+ * titled "Keyboard shortcuts" with one section per group and a two-column
8
+ * table whose key cells carry the `key` span token, so Tern draws keycaps.
9
+ */
10
+ import { formatKeyHint } from "../../app-keybindings";
11
+ import type { KeyName } from "../../key-hint-format";
12
+ import { matchesKey } from "../../keys";
13
+ import { col, node, span } from "../../native/describe";
14
+ import type { NativeNode } from "../../native/node";
15
+ import { theme } from "../../theme/theme";
16
+ import type { Component } from "../../tui";
17
+ import { truncateToWidth, visibleWidth } from "../../utils";
18
+
19
+ interface Shortcut {
20
+ readonly keys: readonly KeyName[];
21
+ readonly label: string;
22
+ }
23
+
24
+ const GROUPS: readonly { readonly title: string; readonly rows: readonly Shortcut[] }[] = [
25
+ {
26
+ title: "Move",
27
+ rows: [
28
+ { keys: ["up", "down"], label: "Move the selection" },
29
+ { keys: ["left", "right"], label: "Fold or unfold a folder or section" },
30
+ { keys: ["enter"], label: "Open the file in the diff" },
31
+ { keys: ["tab"], label: "Switch between the diff and the changes" },
32
+ { keys: ["]", "["], label: "Next or previous file" },
33
+ { keys: ["alt+down", "alt+up"], label: "Next or previous change" },
34
+ { keys: ["g", "shift+g"], label: "First or last row" },
35
+ ],
36
+ },
37
+ {
38
+ title: "Stage",
39
+ rows: [
40
+ { keys: ["space"], label: "Stage or unstage the row" },
41
+ { keys: ["s", "u"], label: "Stage or unstage (the hunk or selected lines in the diff)" },
42
+ { keys: ["shift+up", "shift+down"], label: "Select lines in the diff" },
43
+ { keys: ["x"], label: "Discard the hunk or selected lines (press twice)" },
44
+ { keys: ["delete"], label: "Discard the file or folder (press twice)" },
45
+ ],
46
+ },
47
+ {
48
+ title: "View",
49
+ rows: [
50
+ { keys: ["v"], label: "Cycle the diff view" },
51
+ { keys: ["1", "2", "3", "4"], label: "File, split, inline or hunk view" },
52
+ { keys: ["w"], label: "Wrap long lines" },
53
+ { keys: ["b"], label: "Ignore whitespace, then formatting" },
54
+ { keys: ["t"], label: "Paths or folder tree" },
55
+ { keys: ["r"], label: "Refresh" },
56
+ ],
57
+ },
58
+ {
59
+ title: "Commit",
60
+ rows: [
61
+ { keys: ["c"], label: "Write the commit message (empty: generate one)" },
62
+ { keys: ["escape"], label: "Leave the field" },
63
+ { keys: ["q"], label: "Quit" },
64
+ ],
65
+ },
66
+ ];
67
+
68
+ /** The shortcuts overlay; any of `?`, escape or `q` closes it. */
69
+ export class GitHelpSheet implements Component {
70
+ readonly nativeOverlay = { role: "omp.overlay.hotkeys", head: "Keyboard shortcuts", size: "lg" } as const;
71
+ readonly #close: () => void;
72
+ #native: NativeNode | undefined;
73
+
74
+ constructor(close: () => void) {
75
+ this.#close = close;
76
+ }
77
+
78
+ handleInput(data: string): void {
79
+ if (data === "?" || data === "q" || matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) this.#close();
80
+ }
81
+
82
+ describe(): NativeNode {
83
+ this.#native ??= col(
84
+ GROUPS.map(group =>
85
+ node(
86
+ "section",
87
+ { head: group.title, role: "omp.hotkeys.group" },
88
+ [
89
+ node("table", {
90
+ role: "omp.hotkeys.table",
91
+ cols: [
92
+ { id: "keys", head: "Keys" },
93
+ { id: "does", head: "Action", grow: 1 },
94
+ ],
95
+ rows: group.rows.map((row, index) => ({
96
+ id: String(index),
97
+ cells: { keys: row.keys.map(key => span(formatKeyHint(key), "key")), does: row.label },
98
+ })),
99
+ }),
100
+ ],
101
+ group.title,
102
+ ),
103
+ ),
104
+ { gap: "lg", role: "omp.app.git.help" },
105
+ );
106
+ return this.#native;
107
+ }
108
+
109
+ render(width: number): readonly string[] {
110
+ const keyWidth = Math.max(
111
+ ...GROUPS.flatMap(group => group.rows.map(row => visibleWidth(row.keys.map(formatKeyHint).join(" ")))),
112
+ );
113
+ const lines = [truncateToWidth(` ${theme.bold("Keyboard shortcuts")}`, width), ""];
114
+ for (const group of GROUPS) {
115
+ lines.push(truncateToWidth(` ${theme.fg("muted", group.title)}`, width));
116
+ for (const row of group.rows) {
117
+ const keys = row.keys.map(formatKeyHint).join(" ");
118
+ const pad = " ".repeat(Math.max(1, keyWidth - visibleWidth(keys) + 2));
119
+ lines.push(truncateToWidth(` ${theme.fg("accent", keys)}${pad}${row.label}`, width));
120
+ }
121
+ lines.push("");
122
+ }
123
+ lines.push(truncateToWidth(` ${theme.fg("dim", `${formatKeyHint("escape")} close`)}`, width));
124
+ return lines;
125
+ }
126
+
127
+ invalidate(): void {
128
+ this.#native = undefined;
129
+ }
130
+ }
@@ -17,6 +17,10 @@ import { MenuSelection } from "../../components/menu-selection";
17
17
  import { ScrollView } from "../../components/scroll-view";
18
18
  import { TreeView } from "../../components/tree-view";
19
19
  import { matchesKey } from "../../keys";
20
+ import { col, compact, item, keyed, list, node, row, span, stableKey, text } from "../../native/describe";
21
+ import { Memo } from "../../native/memo";
22
+ import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../../native/node";
23
+ import type { TspProps, TspSpan } from "@oh-my-pi/pi-wire";
20
24
  import { TERMINAL } from "../../terminal-capabilities";
21
25
  import { truncateToWidth, visibleWidth } from "../../utils";
22
26
  import { getEditorTheme, theme } from "../../theme/theme";
@@ -79,6 +83,7 @@ const KIND_LETTER: Record<ChangedFile["kind"], string> = {
79
83
  conflicted: "U",
80
84
  };
81
85
 
86
+ /** Status tones of the native change pills (the ANSI letter colours). */
82
87
  const KIND_COLOR: Record<ChangedFile["kind"], "warning" | "success" | "error" | "accent" | "muted"> = {
83
88
  modified: "warning",
84
89
  added: "success",
@@ -231,6 +236,43 @@ class GitFileTree {
231
236
  }
232
237
  }
233
238
 
239
+ /** A quiet text button (`Stage all`): a clickable row the role sheet draws as `.btn.quiet`. */
240
+ function quietButton(label: string, act: string, title: string): NativeNode {
241
+ return node("row", { role: "omp.app.qbtn", actions: { click: act }, title }, [text(label)], act);
242
+ }
243
+
244
+ /** A text field at rest: its value (or placeholder) as text; a click starts editing it. */
245
+ function fieldText(
246
+ value: string,
247
+ placeholder: string,
248
+ act: string,
249
+ selected: boolean,
250
+ role = "omp.app.git.field",
251
+ ): NativeNode {
252
+ return text(value ? value : [span(placeholder, "dim")], {
253
+ truncate: "end",
254
+ role: selected ? `${role}.on` : role,
255
+ actions: { click: act },
256
+ });
257
+ }
258
+
259
+ /** Props of an `input` node described by a text field, minus identity. */
260
+ function inputProps(described: NativeNode): TspProps<"input"> {
261
+ return described.k === "input" ? { ...described.p } : {};
262
+ }
263
+
264
+ /** The `editor` node inside an {@link Editor}'s description (the editor itself, or the column holding it). */
265
+ function editorNode(described: NativeNode): NativeNode | undefined {
266
+ if (described.k === "editor") return described;
267
+ for (const child of described.c ?? []) if ("k" in child && child.k === "editor") return child;
268
+ return undefined;
269
+ }
270
+
271
+ /** Props of a described `editor` node. */
272
+ function editorProps(described: NativeNode): TspProps<"editor"> {
273
+ return described.k === "editor" ? { ...described.p } : {};
274
+ }
275
+
234
276
  /** File row: status letter, dimmed directory, bright basename, +/− counts. */
235
277
  function fileRowText(file: ChangedFile, width: number, selected: boolean, focused: boolean, depth?: number): string {
236
278
  const prefix = `${theme.fg(KIND_COLOR[file.kind], KIND_LETTER[file.kind])} `;
@@ -355,6 +397,9 @@ export class Sidebar {
355
397
  | undefined;
356
398
  /** Tree depth per target key (file/dir rows only); parent-jump for `←`. */
357
399
  readonly #entryDepth = new Map<string, number>();
400
+ /** Native item key → the target it selects, refreshed by every {@link describe}. */
401
+ readonly #nativeTargets = new Map<string, Target>();
402
+ readonly #nativeMemo = new Memo();
358
403
  readonly #scrollView = new ScrollView([], { height: 1, totalRows: 0, scrollbar: "never" });
359
404
  /** One-shot: the next render scrolls the selected row into view. Set on
360
405
  * explicit selection changes so wheel scrolling can roam freely. */
@@ -892,6 +937,452 @@ export class Sidebar {
892
937
  }
893
938
  }
894
939
 
940
+ // ── native ─────────────────────────────────────────────────────────────
941
+
942
+ /**
943
+ * The changes sheet (NATIVE_REDESIGN §10): a head with the change count,
944
+ * the branch and a Path/Tree segmented control; one list per section under
945
+ * a quiet head whose `Stage all`/`Unstage all` show on hover; the commit
946
+ * composer docked below. A clean tree shows the HEAD commit instead. Text
947
+ * fields are live `input`/`editor` nodes only while they own the keys, so
948
+ * the terminal's caret follows the keyboard.
949
+ */
950
+ describe(cx: DescribeContext): NativeNode {
951
+ this.#rebuildTargets();
952
+ const selected = this.selected;
953
+ const selectedKey = selected ? targetKey(selected) : undefined;
954
+ const editing = this.editing ? selected?.kind : undefined;
955
+ const summaryInput = editing === "summary" ? this.summary.input.describe(cx) : undefined;
956
+ const aiInput = editing === "stage-ai-input" ? this.aiInput.input.describe(cx) : undefined;
957
+ const descriptionEditor = editing === "description" ? editorNode(this.description.describe(cx)) : undefined;
958
+ return this.#nativeMemo.get(
959
+ [
960
+ this.#model.clean,
961
+ this.#model.unstaged,
962
+ this.#model.staged,
963
+ this.#model.headCommit,
964
+ this.#model.branch,
965
+ selectedKey,
966
+ this.focused,
967
+ this.viewStyle,
968
+ this.#treeVersion,
969
+ this.amend,
970
+ this.generating,
971
+ this.#aiPromptOpen,
972
+ this.summary.getValue(),
973
+ this.description.getText(),
974
+ summaryInput,
975
+ aiInput,
976
+ descriptionEditor,
977
+ ],
978
+ () => {
979
+ this.#nativeTargets.clear();
980
+ const on = (target: Target): boolean => this.focused && selectedKey === targetKey(target);
981
+ const children = this.#model.clean
982
+ ? this.#describeHead(selectedKey)
983
+ : [
984
+ this.#describeChangesHead(),
985
+ keyed(
986
+ col(
987
+ [
988
+ ...this.#describeSection("unstaged", selectedKey, on, aiInput),
989
+ ...this.#describeSection("staged", selectedKey, on, undefined),
990
+ ],
991
+ { role: "omp.app.git.changes", gap: "sm" },
992
+ ),
993
+ "changes",
994
+ ),
995
+ this.#describeCommit(on, summaryInput, descriptionEditor),
996
+ ];
997
+ return col(children, { role: "omp.app.git.side" });
998
+ },
999
+ );
1000
+ }
1001
+
1002
+ /**
1003
+ * Pointer actions on the changes sheet: list clicks select (a folder also
1004
+ * folds, a second click on the selected file stages it, as the ANSI click
1005
+ * does), double clicks activate, the segmented control switches Path/Tree,
1006
+ * and the quiet buttons run their row's action. False when not the
1007
+ * sidebar's event.
1008
+ */
1009
+ handleNativeEvent(event: NativeUiEvent): boolean {
1010
+ if (event.type === "select" || event.type === "activate") {
1011
+ if (event.item === "path" || event.item === "tree") {
1012
+ this.viewStyle = event.item;
1013
+ this.#treeVersion++;
1014
+ this.#requestRender();
1015
+ return true;
1016
+ }
1017
+ const target = this.#nativeTargets.get(event.item);
1018
+ if (!target) return false;
1019
+ const wasSelected = this.selected !== undefined && targetKey(this.selected) === targetKey(target);
1020
+ this.#select(target);
1021
+ if (event.type === "activate" || target.kind === "dir" || (target.kind === "file" && wasSelected)) {
1022
+ this.#activate(target);
1023
+ }
1024
+ return true;
1025
+ }
1026
+ if (event.type !== "action") return false;
1027
+ switch (event.act) {
1028
+ case "fold-unstaged":
1029
+ case "fold-staged":
1030
+ this.#toggleSection(event.act === "fold-unstaged" ? "unstaged" : "staged");
1031
+ return true;
1032
+ case "stage-all":
1033
+ case "unstage-all":
1034
+ case "stage-ai":
1035
+ this.#activate({ kind: event.act });
1036
+ return true;
1037
+ case "amend":
1038
+ this.#select({ kind: "amend" });
1039
+ this.#toggleAmend();
1040
+ return true;
1041
+ case "commit":
1042
+ this.#select({ kind: "commit-button" });
1043
+ this.#submitCommit();
1044
+ return true;
1045
+ case "edit-summary":
1046
+ this.#select({ kind: "summary" });
1047
+ return true;
1048
+ case "edit-description":
1049
+ this.#select({ kind: "description" });
1050
+ return true;
1051
+ case "edit-ai":
1052
+ this.#select({ kind: "stage-ai-input" });
1053
+ return true;
1054
+ default:
1055
+ return false;
1056
+ }
1057
+ }
1058
+
1059
+ /** Item key of `target` in the native lists, registered for event lookup. */
1060
+ #nativeKey(target: Target): string {
1061
+ const key = stableKey(targetKey(target));
1062
+ this.#nativeTargets.set(key, target);
1063
+ return key;
1064
+ }
1065
+
1066
+ /** `3 changes on main` and the Path/Tree segmented control. */
1067
+ #describeChangesHead(): NativeNode {
1068
+ const total = this.#model.unstaged.length + this.#model.staged.length;
1069
+ const head = row(
1070
+ [
1071
+ row(
1072
+ compact([
1073
+ text([span(`${total} change${total === 1 ? "" : "s"}`, "strong")]),
1074
+ this.#model.branch !== null && text([span("on", "muted")]),
1075
+ this.#model.branch !== null &&
1076
+ text([span(this.#model.branch, "mono")], { role: "omp.app.git.branch", truncate: "middle" }),
1077
+ ]),
1078
+ { gap: "xs", align: "center", role: "omp.app.git.title" },
1079
+ ),
1080
+ this.#describeStyleToggle(),
1081
+ ],
1082
+ { justify: "between", align: "center", role: "omp.app.git.head" },
1083
+ );
1084
+ return keyed(head, "head");
1085
+ }
1086
+
1087
+ #describeStyleToggle(): NativeNode {
1088
+ return node(
1089
+ "tabs",
1090
+ {
1091
+ items: [
1092
+ { id: "path", label: "Path" },
1093
+ { id: "tree", label: "Tree" },
1094
+ ],
1095
+ active: this.viewStyle,
1096
+ role: "omp.app.seg",
1097
+ },
1098
+ undefined,
1099
+ "style",
1100
+ );
1101
+ }
1102
+
1103
+ /** One section: a quiet head (fold chevron, name, count, hover actions) over its file list. */
1104
+ #describeSection(
1105
+ area: SectionTarget["area"],
1106
+ selectedKey: string | undefined,
1107
+ on: (target: Target) => boolean,
1108
+ aiInput: NativeNode | undefined,
1109
+ ): NativeChild[] {
1110
+ const files = area === "unstaged" ? this.#model.unstaged : this.#model.staged;
1111
+ const folded = this.#collapsedSections.has(area);
1112
+ const section: SectionTarget = { kind: "section", area };
1113
+ const actions: NativeNode[] =
1114
+ area === "unstaged"
1115
+ ? [
1116
+ quietButton("Stage all", "stage-all", "Stage every change (space on the head)"),
1117
+ node(
1118
+ "icon",
1119
+ {
1120
+ name: "wand",
1121
+ role: "omp.app.ibtn",
1122
+ title: "Stage by description…",
1123
+ aria: "Stage by description",
1124
+ actions: { click: "stage-ai" },
1125
+ },
1126
+ undefined,
1127
+ "ai",
1128
+ ),
1129
+ ]
1130
+ : [quietButton("Unstage all", "unstage-all", "Unstage every change (space on the head)")];
1131
+ const head = row(
1132
+ [
1133
+ row(
1134
+ [
1135
+ node("icon", { name: folded ? "chev-r" : "chev" }),
1136
+ text(area === "unstaged" ? "Unstaged" : "Staged"),
1137
+ text([span(String(files.length), "num")], { role: "omp.app.git.count" }),
1138
+ ],
1139
+ { gap: "xs", align: "center", actions: { click: `fold-${area}` }, title: folded ? "Show" : "Hide" },
1140
+ ),
1141
+ row(files.length > 0 ? actions : [], { gap: "xs", align: "center", role: "omp.app.git.acts" }),
1142
+ ],
1143
+ {
1144
+ justify: "between",
1145
+ align: "center",
1146
+ role: on(section) ? "omp.app.git.sechead.on" : "omp.app.git.sechead",
1147
+ },
1148
+ );
1149
+ const out: NativeChild[] = [keyed(head, `${area}-head`)];
1150
+ if (area === "unstaged" && this.#aiPromptOpen) {
1151
+ const target: Target = { kind: "stage-ai-input" };
1152
+ out.push(
1153
+ keyed(
1154
+ aiInput
1155
+ ? node("input", {
1156
+ ...inputProps(aiInput),
1157
+ placeholder: "What should we stage?",
1158
+ role: "omp.app.git.field.on",
1159
+ })
1160
+ : fieldText(this.aiInput.getValue(), "What should we stage?", "edit-ai", on(target)),
1161
+ "ai",
1162
+ ),
1163
+ );
1164
+ }
1165
+ if (!folded) {
1166
+ const entries = this.#fileEntries(files, area);
1167
+ const selected = entries.find(entry => targetKey(entry.target) === selectedKey);
1168
+ out.push(
1169
+ list(
1170
+ entries.map(entry => this.#describeEntry(entry)),
1171
+ {
1172
+ selected: selected ? this.#nativeKey(selected.target) : null,
1173
+ empty: area === "unstaged" ? "Nothing to stage" : "Nothing staged yet",
1174
+ role: "omp.app.git.files",
1175
+ actions: { click: "select", dblclick: "activate" },
1176
+ },
1177
+ area,
1178
+ ),
1179
+ );
1180
+ }
1181
+ return out;
1182
+ }
1183
+
1184
+ /** A file row (status pill, name, `+a −d`) or a folder row of the tree view. */
1185
+ #describeEntry(entry: SidebarFileEntry): NativeNode {
1186
+ const depth = Math.min(entry.depth ?? 0, 8);
1187
+ const key = this.#nativeKey(entry.target);
1188
+ const file = entry.file;
1189
+ if (!file) {
1190
+ return item(key, {
1191
+ label: [span(entry.dirName ?? "")],
1192
+ icon: "folder",
1193
+ role: `omp.app.git.dir-d${depth}`,
1194
+ tone: entry.collapsed ? "muted" : undefined,
1195
+ });
1196
+ }
1197
+ const slash = file.path.lastIndexOf("/");
1198
+ const label =
1199
+ this.viewStyle === "tree" || slash < 0
1200
+ ? [span(file.path.slice(slash + 1))]
1201
+ : [span(file.path.slice(0, slash + 1), "dim"), span(file.path.slice(slash + 1))];
1202
+ const stats: TspSpan[] = [];
1203
+ if (file.additions) stats.push(span(`+${file.additions}`, "ins num"));
1204
+ if (file.deletions) stats.push(span(`${stats.length > 0 ? " " : ""}−${file.deletions}`, "del num"));
1205
+ return item(key, {
1206
+ label,
1207
+ detail: file.origPath ? [span(`from ${file.origPath}`, "dim")] : undefined,
1208
+ value: stats.length > 0 ? stats : undefined,
1209
+ hint: [KIND_LETTER[file.kind]],
1210
+ tone: KIND_COLOR[file.kind],
1211
+ role: `omp.app.git.file-d${this.viewStyle === "tree" ? depth : 0}`,
1212
+ title: `${file.kind}: ${file.path}`,
1213
+ });
1214
+ }
1215
+
1216
+ /**
1217
+ * The commit composer (skill §5 "Forms docked in a sheet"): a borderless
1218
+ * summary with a counter only while typing, the body below, a quiet
1219
+ * `Amend` toggle and the neutral commit button.
1220
+ */
1221
+ #describeCommit(
1222
+ on: (target: Target) => boolean,
1223
+ summaryInput: NativeNode | undefined,
1224
+ descriptionEditor: NativeNode | undefined,
1225
+ ): NativeNode {
1226
+ const summary = this.summary.getValue();
1227
+ const description = this.description.getText();
1228
+ const left = SUMMARY_LIMIT - summary.length;
1229
+ const summaryField = summaryInput
1230
+ ? row(
1231
+ [
1232
+ node("input", { ...inputProps(summaryInput), placeholder: "Summary", grow: 1 }),
1233
+ text([span(String(left), left < 0 ? "warning num" : "num")], { role: "omp.app.git.count" }),
1234
+ ],
1235
+ { gap: "sm", align: "center", role: "omp.app.git.summary.on" },
1236
+ )
1237
+ : fieldText(summary, "Summary", "edit-summary", on({ kind: "summary" }), "omp.app.git.summary");
1238
+ const bodyField = descriptionEditor
1239
+ ? node("editor", {
1240
+ ...editorProps(descriptionEditor),
1241
+ placeholder: "Description",
1242
+ maxLines: 5,
1243
+ role: "omp.app.git.body.on",
1244
+ })
1245
+ : fieldText(
1246
+ description.split("\n", 1)[0] + (description.includes("\n") ? " …" : ""),
1247
+ "Description",
1248
+ "edit-description",
1249
+ on({ kind: "description" }),
1250
+ "omp.app.git.body",
1251
+ );
1252
+ const hasChanges = this.#model.staged.length > 0 || this.#model.unstaged.length > 0 || this.amend;
1253
+ const canCommit =
1254
+ hasChanges && !this.generating && (summary.trim().length > 0 || description.trim().length === 0);
1255
+ const label = this.generating
1256
+ ? "Generating message…"
1257
+ : summary.trim().length === 0 && description.trim().length === 0
1258
+ ? "Generate message"
1259
+ : this.#model.staged.length > 0
1260
+ ? "Commit"
1261
+ : "Stage all & commit";
1262
+ // The panels' button (omp-panels.css `omp.btn`): accent while the keyboard is on it, muted when it can't run.
1263
+ const commitTone = !canCommit ? "muted" : on({ kind: "commit-button" }) ? "accent" : undefined;
1264
+ return col(
1265
+ [
1266
+ keyed(summaryField, "summary"),
1267
+ keyed(bodyField, "body"),
1268
+ keyed(
1269
+ row(
1270
+ [
1271
+ row([node("icon", { name: this.amend ? "check" : "commit" }), text("Amend")], {
1272
+ gap: "xs",
1273
+ align: "center",
1274
+ role: `omp.app.git.toggle${this.amend ? ".set" : ""}${on({ kind: "amend" }) ? ".on" : ""}`,
1275
+ actions: { click: "amend" },
1276
+ title: "Amend the previous commit",
1277
+ }),
1278
+ row(compact([this.generating && node("spinner", { style: "dots" }), text(label)]), {
1279
+ gap: "xs",
1280
+ align: "center",
1281
+ role: "omp.btn",
1282
+ tone: commitTone,
1283
+ actions: canCommit ? { click: "commit" } : undefined,
1284
+ title:
1285
+ this.#model.staged.length > 0
1286
+ ? "Commit the staged changes"
1287
+ : "Stage everything, then commit",
1288
+ }),
1289
+ ],
1290
+ { justify: "between", align: "center", role: "omp.app.git.foot" },
1291
+ ),
1292
+ "foot",
1293
+ ),
1294
+ ],
1295
+ { gap: "sm", role: "omp.app.git.commit" },
1296
+ );
1297
+ }
1298
+
1299
+ /** Clean tree: the HEAD commit (subject, body, author, parents) over its file list. */
1300
+ #describeHead(selectedKey: string | undefined): NativeChild[] {
1301
+ const head = this.#model.headCommit;
1302
+ if (!head) {
1303
+ return [keyed(text([span("No commits yet", "muted")], { role: "omp.app.git.empty" }), "empty")];
1304
+ }
1305
+ const when = head.authorDate ? new Date(head.authorDate) : null;
1306
+ const initials = head.authorName
1307
+ .split(/\s+/)
1308
+ .filter(Boolean)
1309
+ .slice(0, 2)
1310
+ .map(word => word[0]!.toUpperCase())
1311
+ .join("");
1312
+ const commit = col(
1313
+ compact([
1314
+ text([span(head.subject, "strong")], { wrap: "word", role: "omp.app.git.subject" }),
1315
+ head.body.trim() !== "" && text(head.body.trim(), { wrap: "word", lines: 8, role: "omp.app.git.message" }),
1316
+ row(
1317
+ [
1318
+ text(initials || "?", { role: "omp.app.git.avatar", title: head.authorEmail }),
1319
+ col(
1320
+ compact([
1321
+ text([span(head.authorName, "strong")], { truncate: "end" }),
1322
+ text([span(head.authorEmail, "dim")], { truncate: "end" }),
1323
+ ]),
1324
+ { gap: "none" },
1325
+ ),
1326
+ ],
1327
+ { gap: "sm", align: "center", role: "omp.app.git.author" },
1328
+ ),
1329
+ node("kv", {
1330
+ items: compact([
1331
+ when && !Number.isNaN(when.getTime()) ? { k: "authored", v: when.toLocaleString() } : undefined,
1332
+ head.parents.length > 0
1333
+ ? {
1334
+ k: head.parents.length === 1 ? "parent" : "parents",
1335
+ v: [span(head.parents.map(sha => sha.slice(0, 8)).join(" "), "mono accent")],
1336
+ }
1337
+ : undefined,
1338
+ ]),
1339
+ layout: "grid",
1340
+ }),
1341
+ ]),
1342
+ { gap: "sm", role: "omp.app.git.commit-info" },
1343
+ );
1344
+ const out: NativeChild[] = [keyed(commit, "head")];
1345
+ if (!head.filesLoaded) {
1346
+ out.push(keyed(text([span("Loading changed files…", "muted")]), "loading"));
1347
+ return out;
1348
+ }
1349
+ const additions = head.files.reduce((sum, file) => sum + (file.additions ?? 0), 0);
1350
+ const deletions = head.files.reduce((sum, file) => sum + (file.deletions ?? 0), 0);
1351
+ out.push(
1352
+ keyed(
1353
+ row(
1354
+ [
1355
+ text([
1356
+ span(`${head.files.length} file${head.files.length === 1 ? "" : "s"}`, "strong"),
1357
+ span(" "),
1358
+ span(`+${additions}`, "ins num"),
1359
+ span(" "),
1360
+ span(`−${deletions}`, "del num"),
1361
+ span(` ${head.shortSha}`, "dim mono"),
1362
+ ]),
1363
+ this.#describeStyleToggle(),
1364
+ ],
1365
+ { justify: "between", align: "center", role: "omp.app.git.head" },
1366
+ ),
1367
+ "files-head",
1368
+ ),
1369
+ );
1370
+ const entries = this.#fileEntries(head.files, "commit");
1371
+ const selected = entries.find(entry => targetKey(entry.target) === selectedKey);
1372
+ out.push(
1373
+ list(
1374
+ entries.map(entry => this.#describeEntry(entry)),
1375
+ {
1376
+ selected: selected ? this.#nativeKey(selected.target) : null,
1377
+ role: "omp.app.git.files",
1378
+ actions: { click: "select", dblclick: "activate" },
1379
+ },
1380
+ "commit-files",
1381
+ ),
1382
+ );
1383
+ return out;
1384
+ }
1385
+
895
1386
  /** Wheel scroll over the sidebar. */
896
1387
  handleWheel(delta: number): void {
897
1388
  this.#scrollView.scroll(delta * 3);