@linxiraos/pi-tui 1.1.26 → 1.2.0

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 (423) hide show
  1. package/CHANGELOG.md +51 -491
  2. package/README.md +13 -7
  3. package/dist/types/active-terminal.d.ts +30 -0
  4. package/dist/types/app-keybindings.d.ts +7 -2
  5. package/dist/types/apps/cleanse-board.d.ts +1 -1
  6. package/dist/types/apps/debug/log-viewer.d.ts +4 -0
  7. package/dist/types/apps/debug/raw-sse-buffer.d.ts +2 -0
  8. package/dist/types/apps/git/git-tui.d.ts +5 -4
  9. package/dist/types/apps/git/help.d.ts +3 -1
  10. package/dist/types/apps/if-bench-board.d.ts +2 -2
  11. package/dist/types/apps/setup-model-picker.d.ts +1 -1
  12. package/dist/types/autocomplete.d.ts +5 -2
  13. package/dist/types/charts/chart-plan.d.ts +674 -0
  14. package/dist/types/charts/chart-svg.d.ts +59 -0
  15. package/dist/types/charts/table-data.d.ts +175 -0
  16. package/dist/types/chat/assistant-message.d.ts +11 -1
  17. package/dist/types/chat/chat-transcript-builder.d.ts +3 -0
  18. package/dist/types/chat/compaction-summary-message.d.ts +1 -0
  19. package/dist/types/chat/execution-shared.d.ts +6 -5
  20. package/dist/types/chat/fence-figure.d.ts +42 -0
  21. package/dist/types/chat/figure-markdown.d.ts +42 -0
  22. package/dist/types/chat/messages.d.ts +1 -1
  23. package/dist/types/chat/svg-figure.d.ts +33 -0
  24. package/dist/types/chat/svg-source.d.ts +49 -0
  25. package/dist/types/chat/table-chart.d.ts +89 -0
  26. package/dist/types/chat/tool-execution.d.ts +2 -0
  27. package/dist/types/chat/user-message.d.ts +17 -4
  28. package/dist/types/chrome/shared.d.ts +9 -0
  29. package/dist/types/chrome/status-notice.d.ts +15 -3
  30. package/dist/types/chrome/transcript-container.d.ts +6 -0
  31. package/dist/types/components/box.d.ts +1 -0
  32. package/dist/types/components/disclosure.d.ts +1 -0
  33. package/dist/types/components/editor.d.ts +23 -5
  34. package/dist/types/components/form.d.ts +1 -0
  35. package/dist/types/components/image.d.ts +26 -44
  36. package/dist/types/components/input.d.ts +2 -1
  37. package/dist/types/components/key-value-list.d.ts +1 -0
  38. package/dist/types/components/layout/row.d.ts +1 -0
  39. package/dist/types/components/layout/stack.d.ts +1 -0
  40. package/dist/types/components/loader.d.ts +5 -6
  41. package/dist/types/components/markdown.d.ts +47 -5
  42. package/dist/types/components/scroll-view.d.ts +1 -0
  43. package/dist/types/components/section.d.ts +1 -0
  44. package/dist/types/components/table.d.ts +1 -0
  45. package/dist/types/components/text.d.ts +1 -0
  46. package/dist/types/components/tree-view.d.ts +7 -0
  47. package/dist/types/components/truncated-text.d.ts +1 -0
  48. package/dist/types/fuzzy.d.ts +29 -1
  49. package/dist/types/key-hint-format.d.ts +9 -1
  50. package/dist/types/keybinding-matchers.d.ts +3 -0
  51. package/dist/types/keybindings.d.ts +1 -1
  52. package/dist/types/kitty-graphics.d.ts +2 -2
  53. package/dist/types/loop-watchdog.d.ts +8 -7
  54. package/dist/types/multiplexers/cmux.d.ts +16 -0
  55. package/dist/types/multiplexers/herdr.d.ts +26 -0
  56. package/dist/types/multiplexers/notify.d.ts +13 -0
  57. package/dist/types/multiplexers/orca.d.ts +12 -0
  58. package/dist/types/multiplexers/screen.d.ts +9 -0
  59. package/dist/types/multiplexers/tmux.d.ts +29 -0
  60. package/dist/types/multiplexers/types.d.ts +100 -0
  61. package/dist/types/multiplexers/wmux.d.ts +12 -0
  62. package/dist/types/multiplexers/zellij.d.ts +12 -0
  63. package/dist/types/native/apply.d.ts +4 -3
  64. package/dist/types/native/backend.d.ts +5 -4
  65. package/dist/types/native/blobs.d.ts +7 -3
  66. package/dist/types/native/encode.d.ts +8 -6
  67. package/dist/types/native/node.d.ts +36 -5
  68. package/dist/types/native/overlay.d.ts +8 -3
  69. package/dist/types/native/settle.d.ts +4 -3
  70. package/dist/types/overlays/agent-hub-projection.d.ts +6 -5
  71. package/dist/types/overlays/agent-hub-types.d.ts +9 -0
  72. package/dist/types/overlays/agent-hub.d.ts +3 -0
  73. package/dist/types/overlays/agent-transcript-viewer.d.ts +12 -2
  74. package/dist/types/overlays/agents-hub.d.ts +9 -0
  75. package/dist/types/overlays/annotation-overlay.d.ts +19 -2
  76. package/dist/types/overlays/annotation-types.d.ts +2 -0
  77. package/dist/types/overlays/ask-dialog.d.ts +5 -2
  78. package/dist/types/overlays/btw-history-panel.d.ts +13 -1
  79. package/dist/types/overlays/copy-selector.d.ts +1 -0
  80. package/dist/types/overlays/history-search.d.ts +10 -0
  81. package/dist/types/overlays/hook-selector.d.ts +9 -4
  82. package/dist/types/overlays/hub-frame.d.ts +4 -1
  83. package/dist/types/overlays/jobs-panel.d.ts +1 -7
  84. package/dist/types/overlays/model-browser.d.ts +54 -7
  85. package/dist/types/overlays/model-hub.d.ts +37 -0
  86. package/dist/types/overlays/oauth-selector.d.ts +0 -1
  87. package/dist/types/overlays/plan-review-overlay.d.ts +3 -2
  88. package/dist/types/overlays/report-panel.d.ts +68 -0
  89. package/dist/types/overlays/rewind-selector.d.ts +1 -0
  90. package/dist/types/overlays/session-observer-registry.d.ts +2 -0
  91. package/dist/types/overlays/session-selector.d.ts +6 -2
  92. package/dist/types/overlays/settings-defs.d.ts +1 -0
  93. package/dist/types/overlays/thinking-selector.d.ts +2 -2
  94. package/dist/types/overlays/usage-dashboard.d.ts +12 -8
  95. package/dist/types/overlays/usage-display.d.ts +3 -1
  96. package/dist/types/prompt/composer-cache.d.ts +0 -16
  97. package/dist/types/prompt/composer.d.ts +18 -3
  98. package/dist/types/prompt/custom-editor.d.ts +25 -13
  99. package/dist/types/prompt/image-source.d.ts +2 -1
  100. package/dist/types/prompt/interactive-context-helpers.d.ts +5 -1
  101. package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
  102. package/dist/types/prompt/welcome.d.ts +7 -2
  103. package/dist/types/render/composite.d.ts +2 -0
  104. package/dist/types/render/output-pane.d.ts +1 -0
  105. package/dist/types/render/render-utils.d.ts +1 -0
  106. package/dist/types/render/tool-card.d.ts +6 -0
  107. package/dist/types/render/width-aware-text.d.ts +2 -0
  108. package/dist/types/setup/scenes/types.d.ts +1 -0
  109. package/dist/types/space-hold.d.ts +40 -25
  110. package/dist/types/status-line/component.d.ts +3 -0
  111. package/dist/types/status-line/context-usage.d.ts +7 -5
  112. package/dist/types/status-line/host.d.ts +4 -1
  113. package/dist/types/status-line/segments.d.ts +2 -0
  114. package/dist/types/status-line/types.d.ts +14 -3
  115. package/dist/types/terminal-capabilities.d.ts +41 -36
  116. package/dist/types/terminal-multiplexer.d.ts +127 -14
  117. package/dist/types/terminal.d.ts +23 -16
  118. package/dist/types/theme/active-symbols.d.ts +1 -1
  119. package/dist/types/theme/schema.d.ts +11 -2
  120. package/dist/types/theme/symbols.d.ts +1 -1
  121. package/dist/types/theme/theme-class.d.ts +7 -0
  122. package/dist/types/theme/theme.d.ts +1 -1
  123. package/dist/types/theme/tui-adapters.d.ts +3 -0
  124. package/dist/types/thinking.d.ts +1 -1
  125. package/dist/types/tmux.d.ts +11 -4
  126. package/dist/types/tools/bash.d.ts +7 -2
  127. package/dist/types/tools/daemon.d.ts +1 -1
  128. package/dist/types/tools/eval.d.ts +22 -3
  129. package/dist/types/tools/glob.d.ts +4 -0
  130. package/dist/types/tools/native-view.d.ts +47 -0
  131. package/dist/types/tools/output-meta.d.ts +11 -1
  132. package/dist/types/tools/renderer.d.ts +37 -2
  133. package/dist/types/tools/streaming-output.d.ts +20 -1
  134. package/dist/types/tools/task.d.ts +2 -3
  135. package/dist/types/tools/write.d.ts +9 -2
  136. package/dist/types/tools/yield.d.ts +22 -0
  137. package/dist/types/tui.d.ts +36 -0
  138. package/dist/types/utils.d.ts +6 -0
  139. package/dist/types/vim.d.ts +19 -5
  140. package/dist/types/windows-input-mode.d.ts +33 -1
  141. package/package.json +13 -9
  142. package/src/active-terminal.d.ts +30 -0
  143. package/src/active-terminal.ts +80 -0
  144. package/src/app-keybindings.d.ts +7 -2
  145. package/src/app-keybindings.ts +7 -2
  146. package/src/apps/autoresearch-dashboard.ts +7 -3
  147. package/src/apps/cleanse-board.ts +2 -2
  148. package/src/apps/cleanse-picker.ts +1 -1
  149. package/src/apps/debug/log-viewer.ts +188 -72
  150. package/src/apps/debug/raw-sse-buffer.ts +8 -0
  151. package/src/apps/debug/raw-sse.ts +39 -5
  152. package/src/apps/git/diff-pane.ts +96 -23
  153. package/src/apps/git/git-tui.ts +23 -16
  154. package/src/apps/git/help.ts +29 -20
  155. package/src/apps/git/sidebar.ts +8 -3
  156. package/src/apps/if-bench-board.ts +2 -2
  157. package/src/apps/ps-top.ts +2 -2
  158. package/src/apps/setup-model-picker.ts +1 -1
  159. package/src/autocomplete.d.ts +5 -2
  160. package/src/autocomplete.ts +67 -43
  161. package/src/bracketed-paste.ts +10 -3
  162. package/src/charts/chart-plan.d.ts +674 -0
  163. package/src/charts/chart-plan.ts +5081 -0
  164. package/src/charts/chart-svg.d.ts +59 -0
  165. package/src/charts/chart-svg.ts +4657 -0
  166. package/src/charts/table-data.d.ts +175 -0
  167. package/src/charts/table-data.ts +1478 -0
  168. package/src/chat/advisor-message.ts +11 -0
  169. package/src/chat/assistant-message.d.ts +11 -1
  170. package/src/chat/assistant-message.ts +245 -54
  171. package/src/chat/chat-transcript-builder.d.ts +3 -0
  172. package/src/chat/chat-transcript-builder.ts +9 -3
  173. package/src/chat/compaction-summary-message.d.ts +1 -0
  174. package/src/chat/compaction-summary-message.ts +8 -0
  175. package/src/chat/execution-shared.d.ts +6 -5
  176. package/src/chat/execution-shared.ts +42 -25
  177. package/src/chat/fence-figure.d.ts +42 -0
  178. package/src/chat/fence-figure.ts +80 -0
  179. package/src/chat/figure-markdown.d.ts +42 -0
  180. package/src/chat/figure-markdown.ts +174 -0
  181. package/src/chat/messages.d.ts +1 -1
  182. package/src/chat/messages.ts +1 -1
  183. package/src/chat/read-tool-group.ts +6 -4
  184. package/src/chat/skill-message.ts +6 -0
  185. package/src/chat/svg-figure.d.ts +33 -0
  186. package/src/chat/svg-figure.ts +399 -0
  187. package/src/chat/svg-source.d.ts +49 -0
  188. package/src/chat/svg-source.ts +188 -0
  189. package/src/chat/table-chart.d.ts +89 -0
  190. package/src/chat/table-chart.ts +294 -0
  191. package/src/chat/tool-execution.d.ts +2 -0
  192. package/src/chat/tool-execution.ts +86 -7
  193. package/src/chat/user-message.d.ts +17 -4
  194. package/src/chat/user-message.ts +71 -8
  195. package/src/chrome/shared.ts +53 -0
  196. package/src/chrome/status-notice.ts +25 -7
  197. package/src/chrome/transcript-container.d.ts +6 -0
  198. package/src/chrome/transcript-container.ts +162 -19
  199. package/src/components/box.d.ts +1 -0
  200. package/src/components/box.ts +5 -0
  201. package/src/components/disclosure.d.ts +1 -0
  202. package/src/components/disclosure.ts +8 -0
  203. package/src/components/editor.d.ts +23 -5
  204. package/src/components/editor.ts +316 -47
  205. package/src/components/form.d.ts +1 -0
  206. package/src/components/form.ts +8 -0
  207. package/src/components/image.d.ts +26 -44
  208. package/src/components/image.ts +115 -152
  209. package/src/components/input.d.ts +2 -1
  210. package/src/components/input.ts +17 -7
  211. package/src/components/key-value-list.d.ts +1 -0
  212. package/src/components/key-value-list.ts +4 -0
  213. package/src/components/layout/row.d.ts +1 -0
  214. package/src/components/layout/row.ts +7 -0
  215. package/src/components/layout/stack.d.ts +1 -0
  216. package/src/components/layout/stack.ts +7 -0
  217. package/src/components/loader.d.ts +5 -6
  218. package/src/components/loader.ts +10 -17
  219. package/src/components/markdown.d.ts +47 -5
  220. package/src/components/markdown.ts +786 -303
  221. package/src/components/scroll-view.d.ts +1 -0
  222. package/src/components/scroll-view.ts +5 -0
  223. package/src/components/section.d.ts +1 -0
  224. package/src/components/section.ts +5 -0
  225. package/src/components/settings-list.ts +2 -0
  226. package/src/components/table.d.ts +1 -0
  227. package/src/components/table.ts +4 -0
  228. package/src/components/text.d.ts +1 -0
  229. package/src/components/text.ts +7 -0
  230. package/src/components/tree-view.d.ts +7 -0
  231. package/src/components/tree-view.ts +38 -8
  232. package/src/components/truncated-text.d.ts +1 -0
  233. package/src/components/truncated-text.ts +5 -0
  234. package/src/desktop-notify.ts +3 -2
  235. package/src/fuzzy.d.ts +29 -1
  236. package/src/fuzzy.ts +115 -30
  237. package/src/hotkeys-markdown.ts +9 -2
  238. package/src/key-hint-format.d.ts +9 -1
  239. package/src/key-hint-format.ts +26 -11
  240. package/src/keybinding-matchers.d.ts +3 -0
  241. package/src/keybinding-matchers.ts +6 -2
  242. package/src/keybindings.d.ts +1 -1
  243. package/src/keybindings.ts +1 -1
  244. package/src/kitty-graphics.d.ts +2 -2
  245. package/src/kitty-graphics.ts +11 -5
  246. package/src/loop-watchdog.d.ts +8 -7
  247. package/src/loop-watchdog.ts +18 -13
  248. package/src/multiplexers/cmux.d.ts +16 -0
  249. package/src/multiplexers/cmux.ts +29 -0
  250. package/src/multiplexers/herdr.d.ts +26 -0
  251. package/src/multiplexers/herdr.ts +64 -0
  252. package/src/multiplexers/notify.d.ts +13 -0
  253. package/src/multiplexers/notify.ts +24 -0
  254. package/src/multiplexers/orca.d.ts +12 -0
  255. package/src/multiplexers/orca.ts +16 -0
  256. package/src/multiplexers/screen.d.ts +9 -0
  257. package/src/multiplexers/screen.ts +15 -0
  258. package/src/multiplexers/tmux.d.ts +29 -0
  259. package/src/multiplexers/tmux.ts +40 -0
  260. package/src/multiplexers/types.d.ts +100 -0
  261. package/src/multiplexers/types.ts +110 -0
  262. package/src/multiplexers/wmux.d.ts +12 -0
  263. package/src/multiplexers/wmux.ts +16 -0
  264. package/src/multiplexers/zellij.d.ts +12 -0
  265. package/src/multiplexers/zellij.ts +24 -0
  266. package/src/native/apply.d.ts +4 -3
  267. package/src/native/apply.ts +4 -3
  268. package/src/native/backend.d.ts +5 -4
  269. package/src/native/backend.ts +199 -22
  270. package/src/native/blobs.d.ts +7 -3
  271. package/src/native/blobs.ts +47 -12
  272. package/src/native/encode.d.ts +8 -6
  273. package/src/native/encode.ts +76 -34
  274. package/src/native/node.d.ts +36 -5
  275. package/src/native/node.ts +31 -5
  276. package/src/native/overlay.d.ts +8 -3
  277. package/src/native/overlay.ts +17 -4
  278. package/src/native/reconcile.ts +16 -4
  279. package/src/native/settle.d.ts +4 -3
  280. package/src/native/settle.ts +4 -3
  281. package/src/overlays/agent-hub-projection.d.ts +6 -5
  282. package/src/overlays/agent-hub-projection.ts +124 -13
  283. package/src/overlays/agent-hub-types.d.ts +9 -0
  284. package/src/overlays/agent-hub-types.ts +6 -0
  285. package/src/overlays/agent-hub.ts +48 -29
  286. package/src/overlays/agent-transcript-viewer.ts +93 -23
  287. package/src/overlays/agents-hub.ts +28 -8
  288. package/src/overlays/annotation-overlay.ts +293 -90
  289. package/src/overlays/annotation-types.ts +2 -0
  290. package/src/overlays/ask-dialog.ts +10 -17
  291. package/src/overlays/btw-history-panel.ts +136 -39
  292. package/src/overlays/copy-selector.d.ts +1 -0
  293. package/src/overlays/copy-selector.ts +32 -8
  294. package/src/overlays/extensions/extension-dashboard.ts +4 -4
  295. package/src/overlays/extensions/state-manager.ts +52 -12
  296. package/src/overlays/history-search.ts +100 -7
  297. package/src/overlays/hook-selector.ts +12 -5
  298. package/src/overlays/hub-frame.ts +19 -1
  299. package/src/overlays/jobs-panel.ts +33 -52
  300. package/src/overlays/model-browser.ts +367 -122
  301. package/src/overlays/model-hub.ts +435 -59
  302. package/src/overlays/move-overlay.ts +28 -14
  303. package/src/overlays/oauth-selector.ts +54 -70
  304. package/src/overlays/pause-screen.ts +2 -2
  305. package/src/overlays/plan-review-overlay.ts +208 -70
  306. package/src/overlays/plugin-settings.ts +3 -3
  307. package/src/overlays/report-panel.ts +191 -0
  308. package/src/overlays/rewind-selector.ts +88 -17
  309. package/src/overlays/session-observer-registry.d.ts +2 -0
  310. package/src/overlays/session-observer-registry.ts +10 -2
  311. package/src/overlays/session-selector.ts +64 -26
  312. package/src/overlays/settings-defs.ts +5 -4
  313. package/src/overlays/settings-selector.ts +33 -8
  314. package/src/overlays/snapcompact-shape-preview.ts +2 -3
  315. package/src/overlays/stats-notice.ts +1 -1
  316. package/src/overlays/thinking-selector.ts +11 -10
  317. package/src/overlays/tree-selector.ts +41 -20
  318. package/src/overlays/usage-dashboard.ts +117 -58
  319. package/src/overlays/usage-display.ts +10 -4
  320. package/src/overlays/usage-row.ts +22 -8
  321. package/src/prompt/attachment-chips.ts +15 -11
  322. package/src/prompt/composer-attachments.ts +6 -4
  323. package/src/prompt/composer-cache.ts +7 -86
  324. package/src/prompt/composer.ts +37 -26
  325. package/src/prompt/custom-editor.ts +282 -105
  326. package/src/prompt/image-source.ts +2 -1
  327. package/src/prompt/interactive-context-helpers.ts +32 -18
  328. package/src/prompt/macos-spelling.ts +7 -0
  329. package/src/prompt/magic-keywords.ts +11 -2
  330. package/src/prompt/model-mention-autocomplete.ts +14 -14
  331. package/src/prompt/queued-messages.ts +6 -4
  332. package/src/prompt/tips.txt +13 -9
  333. package/src/prompt/welcome.ts +64 -46
  334. package/src/prompt/word-completion.ts +38 -5
  335. package/src/render/composite.d.ts +2 -0
  336. package/src/render/composite.ts +64 -0
  337. package/src/render/output-pane.d.ts +1 -0
  338. package/src/render/output-pane.ts +6 -0
  339. package/src/render/render-utils.d.ts +1 -0
  340. package/src/render/render-utils.ts +16 -1
  341. package/src/render/tool-card.d.ts +6 -0
  342. package/src/render/tool-card.ts +15 -0
  343. package/src/render/width-aware-text.d.ts +2 -0
  344. package/src/render/width-aware-text.ts +5 -0
  345. package/src/setup/scenes/model.ts +2 -1
  346. package/src/setup/scenes/types.ts +1 -0
  347. package/src/space-hold.d.ts +40 -25
  348. package/src/space-hold.ts +156 -52
  349. package/src/status-line/component.ts +292 -78
  350. package/src/status-line/context-usage.ts +21 -46
  351. package/src/status-line/host.ts +4 -1
  352. package/src/status-line/segments.ts +40 -22
  353. package/src/status-line/types.ts +15 -3
  354. package/src/terminal-capabilities.d.ts +41 -36
  355. package/src/terminal-capabilities.ts +137 -213
  356. package/src/terminal-multiplexer.d.ts +127 -14
  357. package/src/terminal-multiplexer.ts +112 -33
  358. package/src/terminal.d.ts +23 -16
  359. package/src/terminal.ts +203 -99
  360. package/src/theme/active-symbols.d.ts +1 -1
  361. package/src/theme/active-symbols.ts +1 -1
  362. package/src/theme/schema-validation.ts +1 -0
  363. package/src/theme/schema.d.ts +11 -2
  364. package/src/theme/schema.ts +14 -1
  365. package/src/theme/session-color.ts +1 -7
  366. package/src/theme/symbols.d.ts +1 -1
  367. package/src/theme/symbols.ts +11 -4
  368. package/src/theme/theme-class.d.ts +7 -0
  369. package/src/theme/theme-class.ts +26 -1
  370. package/src/theme/theme-schema.json +5 -1
  371. package/src/theme/theme.d.ts +1 -1
  372. package/src/theme/theme.ts +8 -4
  373. package/src/theme/tui-adapters.d.ts +3 -0
  374. package/src/theme/tui-adapters.ts +68 -0
  375. package/src/thinking.d.ts +1 -1
  376. package/src/thinking.ts +1 -1
  377. package/src/tmux.d.ts +11 -4
  378. package/src/tmux.ts +47 -11
  379. package/src/tools/ast-edit.ts +59 -35
  380. package/src/tools/ast-grep.ts +35 -16
  381. package/src/tools/bash.d.ts +7 -2
  382. package/src/tools/bash.ts +116 -47
  383. package/src/tools/daemon.d.ts +1 -1
  384. package/src/tools/daemon.ts +1 -1
  385. package/src/tools/default-renderer.ts +35 -19
  386. package/src/tools/edit.ts +142 -120
  387. package/src/tools/eval-format/index.ts +28 -2
  388. package/src/tools/eval-format/javascript.ts +7 -5
  389. package/src/tools/eval.d.ts +22 -3
  390. package/src/tools/eval.ts +364 -208
  391. package/src/tools/fetch.ts +5 -6
  392. package/src/tools/glob.d.ts +4 -0
  393. package/src/tools/glob.ts +33 -65
  394. package/src/tools/grep.ts +81 -48
  395. package/src/tools/index.ts +2 -0
  396. package/src/tools/lsp.ts +81 -59
  397. package/src/tools/mcp.ts +111 -68
  398. package/src/tools/native-view.d.ts +47 -0
  399. package/src/tools/native-view.ts +102 -1
  400. package/src/tools/output-meta.d.ts +11 -1
  401. package/src/tools/output-meta.ts +23 -3
  402. package/src/tools/read.ts +7 -5
  403. package/src/tools/renderer.d.ts +37 -2
  404. package/src/tools/renderer.ts +42 -2
  405. package/src/tools/streaming-output.d.ts +20 -1
  406. package/src/tools/streaming-output.ts +154 -50
  407. package/src/tools/task.d.ts +2 -3
  408. package/src/tools/task.ts +115 -62
  409. package/src/tools/todo.ts +119 -30
  410. package/src/tools/wait.ts +34 -24
  411. package/src/tools/web-search.ts +1 -1
  412. package/src/tools/write.d.ts +9 -2
  413. package/src/tools/write.ts +147 -95
  414. package/src/tools/yield.d.ts +22 -0
  415. package/src/tools/yield.ts +225 -0
  416. package/src/tui.d.ts +36 -0
  417. package/src/tui.ts +666 -217
  418. package/src/utils.d.ts +6 -0
  419. package/src/utils.ts +19 -0
  420. package/src/vim.d.ts +19 -5
  421. package/src/vim.ts +333 -18
  422. package/src/windows-input-mode.d.ts +33 -1
  423. package/src/windows-input-mode.ts +212 -6
@@ -1,10 +1,19 @@
1
1
  import type { SpinnerFramesOverride } from "./symbols";
2
2
  export type ColorValue = string | number;
3
+ /**
4
+ * Tokens a theme is allowed to omit. They resolve to the terminal default
5
+ * foreground, so `Theme.fg()` stays total for callers that paint a token a theme
6
+ * never declared (e.g. a `modelTags.<role>.color` reference) while
7
+ * `Theme.hasColor()` still reports them as unset.
8
+ */
9
+ export declare const OPTIONAL_THEME_COLOR_RECORD: {
10
+ assistantMessageText: true;
11
+ };
3
12
  export interface ThemeJson {
4
13
  $schema?: string;
5
14
  name: string;
6
15
  vars?: Record<string, ColorValue>;
7
- colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax"> & {
16
+ colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax" | keyof typeof OPTIONAL_THEME_COLOR_RECORD> & Partial<Record<keyof typeof OPTIONAL_THEME_COLOR_RECORD, ColorValue>> & {
8
17
  thinkingMax?: ColorValue;
9
18
  };
10
19
  export?: {
@@ -36,7 +45,7 @@ export interface ThemeJson {
36
45
  spinnerFrames?: SpinnerFramesOverride;
37
46
  };
38
47
  }
39
- export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "thinkingMax" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
48
+ export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "assistantMessageText" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "thinkingMax" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
40
49
  /** Check if a string is a valid ThemeColor value */
41
50
  export declare function isValidThemeColor(color: string): color is ThemeColor;
42
51
  export type ThemeBg = "selectedBg" | "userMessageBg" | "customMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg" | "statusLineBg";
@@ -6,11 +6,22 @@ import type { SpinnerFramesOverride } from "./symbols";
6
6
 
7
7
  export type ColorValue = string | number;
8
8
 
9
+ /**
10
+ * Tokens a theme is allowed to omit. They resolve to the terminal default
11
+ * foreground, so `Theme.fg()` stays total for callers that paint a token a theme
12
+ * never declared (e.g. a `modelTags.<role>.color` reference) while
13
+ * `Theme.hasColor()` still reports them as unset.
14
+ */
15
+ export const OPTIONAL_THEME_COLOR_RECORD = {
16
+ assistantMessageText: true,
17
+ } satisfies Partial<Record<ThemeColor, true>>;
18
+
9
19
  export interface ThemeJson {
10
20
  $schema?: string;
11
21
  name: string;
12
22
  vars?: Record<string, ColorValue>;
13
- colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax"> & { thinkingMax?: ColorValue };
23
+ colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax" | keyof typeof OPTIONAL_THEME_COLOR_RECORD> &
24
+ Partial<Record<keyof typeof OPTIONAL_THEME_COLOR_RECORD, ColorValue>> & { thinkingMax?: ColorValue };
14
25
  export?: {
15
26
  pageBg?: ColorValue;
16
27
  cardBg?: ColorValue;
@@ -52,6 +63,7 @@ export type ThemeColor =
52
63
  | "muted"
53
64
  | "dim"
54
65
  | "text"
66
+ | "assistantMessageText"
55
67
  | "thinkingText"
56
68
  | "userMessageText"
57
69
  | "customMessageText"
@@ -115,6 +127,7 @@ const THEME_COLOR_RECORD = {
115
127
  muted: true,
116
128
  dim: true,
117
129
  text: true,
130
+ assistantMessageText: true,
118
131
  thinkingText: true,
119
132
  userMessageText: true,
120
133
  customMessageText: true,
@@ -1,5 +1,5 @@
1
1
  import { TERMINAL } from "../terminal-capabilities";
2
- import { hexToOklch, oklchCusp, oklchToHex, relativeLuminance } from "@linxiraos/pi-utils";
2
+ import { hexToOklch, hueDistance, oklchCusp, oklchToHex, relativeLuminance } from "@linxiraos/pi-utils";
3
3
 
4
4
  /**
5
5
  * Derive a stable 32-bit hash from a string using djb2.
@@ -49,12 +49,6 @@ const MIN_HUE_DISTANCE = 10;
49
49
  /** OKLCH chroma threshold below which hue is meaningless (near-gray). */
50
50
  const MIN_CHROMA_FOR_HUE = 0.03;
51
51
 
52
- /** Angular distance between two hue values (0-360). */
53
- function hueDistance(a: number, b: number): number {
54
- const d = Math.abs(a - b);
55
- return Math.min(d, 360 - d);
56
- }
57
-
58
52
  /**
59
53
  * Parse OKLCH hue (0-360) from a hex color string.
60
54
  * Returns undefined for near-gray colors where hue is not meaningful.
@@ -2,7 +2,7 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
2
2
  /**
3
3
  * All available symbol keys organized by category.
4
4
  */
5
- export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "progress.filled" | "progress.empty" | "context.speculation" | "context.compaction" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxDotted.horizontal" | "boxDotted.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.powerlineCapLeft" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.prewalk" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.worktree" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.pin" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.subscription" | "icon.advisor" | "icon.advisorClosed" | "icon.time" | "icon.omp" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.cacheMiss" | "icon.input" | "icon.output" | "icon.throughput" | "icon.intelligence" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.vimNormal" | "icon.vimInsert" | "icon.vimVisual" | "icon.vimVisualLine" | "cmd.action" | "cmd.prompt" | "cmd.extension" | "cmd.settings" | "cmd.gear" | "cmd.shield" | "cmd.wave" | "cmd.compass" | "cmd.inbox" | "cmd.swap" | "cmd.expand" | "cmd.computer" | "cmd.eye" | "cmd.todo" | "cmd.stats" | "cmd.news" | "cmd.keyboard" | "cmd.export" | "cmd.clipboard" | "cmd.share" | "cmd.broadcast" | "cmd.globe" | "cmd.copy" | "cmd.plus" | "cmd.restart" | "cmd.eraser" | "cmd.trash" | "cmd.compress" | "cmd.vibrate" | "cmd.handoff" | "cmd.history" | "cmd.question" | "cmd.rocket" | "cmd.stethoscope" | "cmd.redo" | "cmd.bug" | "cmd.memory" | "cmd.pencil" | "cmd.folderMove" | "cmd.folderPlus" | "cmd.folderMinus" | "cmd.hammer" | "cmd.power" | "cmd.cart" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.max" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "advisor.rail" | "skill.rail" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.julia" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "chip.image" | "chip.video" | "chip.paste" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.launch" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.goal" | "tool.irc" | "tool.delete" | "tool.move" | "key.ctrl" | "key.ctrlMac" | "key.shift" | "key.alt" | "key.altMac" | "key.super" | "key.superMac" | "key.joiner" | "key.enter" | "key.esc" | "key.tab" | "key.backspace" | "key.delete" | "key.space" | "key.up" | "key.down" | "key.left" | "key.right";
5
+ export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "progress.filled" | "progress.empty" | "context.speculation" | "context.compaction" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxDotted.horizontal" | "boxDotted.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.powerlineCapLeft" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.prewalk" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.worktree" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.pin" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.subscription" | "icon.advisor" | "icon.advisorClosed" | "icon.time" | "icon.omp" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.cacheMiss" | "icon.input" | "icon.output" | "icon.throughput" | "icon.intelligence" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.vimNormal" | "icon.vimInsert" | "icon.vimVisual" | "icon.vimVisualLine" | "icon.vimReplace" | "cmd.action" | "cmd.prompt" | "cmd.extension" | "cmd.settings" | "cmd.gear" | "cmd.shield" | "cmd.wave" | "cmd.compass" | "cmd.inbox" | "cmd.swap" | "cmd.expand" | "cmd.computer" | "cmd.eye" | "cmd.todo" | "cmd.stats" | "cmd.news" | "cmd.keyboard" | "cmd.export" | "cmd.clipboard" | "cmd.share" | "cmd.broadcast" | "cmd.globe" | "cmd.copy" | "cmd.plus" | "cmd.restart" | "cmd.eraser" | "cmd.trash" | "cmd.compress" | "cmd.vibrate" | "cmd.handoff" | "cmd.history" | "cmd.question" | "cmd.rocket" | "cmd.stethoscope" | "cmd.redo" | "cmd.bug" | "cmd.memory" | "cmd.pencil" | "cmd.folderMove" | "cmd.folderPlus" | "cmd.folderMinus" | "cmd.hammer" | "cmd.power" | "cmd.cart" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.max" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "advisor.rail" | "skill.rail" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.julia" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "chip.image" | "chip.video" | "chip.paste" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.launch" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.goal" | "tool.irc" | "tool.delete" | "tool.move" | "key.ctrl" | "key.ctrlMac" | "key.shift" | "key.alt" | "key.altMac" | "key.super" | "key.superMac" | "key.joiner" | "key.enter" | "key.esc" | "key.tab" | "key.backspace" | "key.delete" | "key.space" | "key.up" | "key.down" | "key.left" | "key.right";
6
6
  export type SymbolMap = Record<SymbolKey, string>;
7
7
  /**
8
8
  * Icon vocabulary for slash-command autocomplete type indicators. Each name
@@ -129,6 +129,7 @@ export type SymbolKey =
129
129
  | "icon.vimInsert"
130
130
  | "icon.vimVisual"
131
131
  | "icon.vimVisualLine"
132
+ | "icon.vimReplace"
132
133
  // Slash-command type indicators (autocomplete); names without an existing
133
134
  // icon.* equivalent — see SlashCommandIconName for the full vocabulary.
134
135
  | "cmd.action"
@@ -511,6 +512,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
511
512
  "icon.vimInsert": "▎",
512
513
  "icon.vimVisual": "◉",
513
514
  "icon.vimVisualLine": "≡",
515
+ "icon.vimReplace": "▁",
514
516
  // Slash-command type indicators
515
517
  "cmd.action": "❯",
516
518
  "cmd.prompt": "✎",
@@ -921,6 +923,8 @@ const NERD_SYMBOLS: SymbolMap = {
921
923
  "icon.vimVisual": "\uf06e",
922
924
  // pick:  (nf-fa-bars) | alt:  (nf-fa-align_left)
923
925
  "icon.vimVisualLine": "\uf0c9",
926
+ // pick:  (nf-fa-exchange) | alt:  (nf-fa-repeat)
927
+ "icon.vimReplace": "\uf0ec",
924
928
  // Slash-command type indicators
925
929
  // pick: (nf-cod-terminal) | alt: (nf-fa-terminal)
926
930
  "cmd.action": "\uea85",
@@ -1235,9 +1239,11 @@ const ASCII_SYMBOLS: SymbolMap = {
1235
1239
  "sep.pipe": " | ",
1236
1240
  // Icons
1237
1241
  "icon.model": "[M]",
1238
- "icon.plan": "plan",
1239
- "icon.prewalk": "prewalk",
1240
- "icon.goal": "goal",
1242
+ // Mode icons are always followed by their word label ("Plan", "Prewalk", "Goal");
1243
+ // an ASCII word here would just repeat it, so render the label alone.
1244
+ "icon.plan": "",
1245
+ "icon.prewalk": "",
1246
+ "icon.goal": "",
1241
1247
  "icon.pause": "||",
1242
1248
  "icon.loop": "loop",
1243
1249
  "icon.folder": "[D]",
@@ -1256,7 +1262,7 @@ const ASCII_SYMBOLS: SymbolMap = {
1256
1262
  "icon.advisor": "(adv)",
1257
1263
  "icon.advisorClosed": "(adv)",
1258
1264
  "icon.time": "t:",
1259
- "icon.omp": "pi",
1265
+ "icon.omp": "zeta",
1260
1266
  "icon.ghost": "@",
1261
1267
  "icon.agents": "AG",
1262
1268
  "icon.job": "bg",
@@ -1287,6 +1293,7 @@ const ASCII_SYMBOLS: SymbolMap = {
1287
1293
  "icon.vimInsert": "I",
1288
1294
  "icon.vimVisual": "V",
1289
1295
  "icon.vimVisualLine": "L",
1296
+ "icon.vimReplace": "R",
1290
1297
  // Slash-command type indicators — unused; the icon column is disabled in ASCII mode
1291
1298
  "cmd.action": "",
1292
1299
  "cmd.prompt": "",
@@ -54,6 +54,13 @@ export declare class Theme {
54
54
  * against. Built once per instance and frozen; callers share the object.
55
55
  */
56
56
  get sessionAccentInputs(): SessionAccentTheme;
57
+ /**
58
+ * True when the theme paints `color` with a real foreground — i.e. the token
59
+ * exists and is not the empty string (terminal default, `\x1b[39m`). Optional
60
+ * tokens absent from the theme JSON are also false. Callers use this to keep
61
+ * terminal-default behavior byte-for-byte when an optional token is unset.
62
+ */
63
+ hasColor(color: ThemeColor): boolean;
57
64
  fg(color: ThemeColor, text: string): string;
58
65
  /** Apply a foreground, replacing terminal-default tokens with the theme's contrast-safe fallback. */
59
66
  fgResolved(color: ThemeColor, text: string): string;
@@ -2,7 +2,13 @@ import { colorLuma, relativeLuminance } from "@linxiraos/pi-utils/color";
2
2
  import * as logger from "@linxiraos/pi-utils/logger";
3
3
  import chalk from "@linxiraos/pi-utils/chalk";
4
4
  import { bgAnsi, colorToAnsi, fgAnsi, resolveToHex } from "./color";
5
- import { type ColorMode, isValidThemeColor, type ThemeBg, type ThemeColor } from "./schema";
5
+ import {
6
+ type ColorMode,
7
+ isValidThemeColor,
8
+ OPTIONAL_THEME_COLOR_RECORD,
9
+ type ThemeBg,
10
+ type ThemeColor,
11
+ } from "./schema";
6
12
  import type { SessionAccentTheme } from "./session-color";
7
13
  import {
8
14
  type SlashCommandIconName,
@@ -204,6 +210,14 @@ export class Theme {
204
210
  this.#fgColors[key] = fgAnsi(value, mode);
205
211
  this.#hexFgColors[key] = hex;
206
212
  }
213
+ // A theme may omit an optional token, but callers can still name it (a
214
+ // `modelTags.<role>.color` reference is validated against the theme color
215
+ // list). Resolve those to the terminal default so `fg()` stays total while
216
+ // `hasColor()` reports them as unset.
217
+ for (const key of Object.keys(OPTIONAL_THEME_COLOR_RECORD) as ThemeColor[]) {
218
+ this.#fgColors[key] ??= "\x1b[39m";
219
+ this.#hexFgColors[key] ??= "";
220
+ }
207
221
  this.#bgColors = {} as Record<ThemeBg, string>;
208
222
  this.#hexBgColors = {} as Record<ThemeBg, string>;
209
223
  for (const [key, value] of Object.entries(bgColors) as [ThemeBg, string | number][]) {
@@ -322,6 +336,17 @@ export class Theme {
322
336
  return this.#sessionAccentInputs;
323
337
  }
324
338
 
339
+ /**
340
+ * True when the theme paints `color` with a real foreground — i.e. the token
341
+ * exists and is not the empty string (terminal default, `\x1b[39m`). Optional
342
+ * tokens absent from the theme JSON are also false. Callers use this to keep
343
+ * terminal-default behavior byte-for-byte when an optional token is unset.
344
+ */
345
+ hasColor(color: ThemeColor): boolean {
346
+ const ansi = this.#fgColors[color];
347
+ return ansi !== undefined && ansi !== "\x1b[39m";
348
+ }
349
+
325
350
  fg(color: ThemeColor, text: string): string {
326
351
  const ansi = this.#fgColors[color];
327
352
  if (!ansi) throw new Error(`Unknown theme color: ${color}`);
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "colors": {
35
35
  "type": "object",
36
- "description": "Theme color definitions (all required except thinkingMax)",
36
+ "description": "Theme color definitions (all required except thinkingMax and assistantMessageText)",
37
37
  "required": [
38
38
  "accent",
39
39
  "border",
@@ -143,6 +143,10 @@
143
143
  "$ref": "#/$defs/colorValue",
144
144
  "description": "Default text color (usually empty string)"
145
145
  },
146
+ "assistantMessageText": {
147
+ "$ref": "#/$defs/colorValue",
148
+ "description": "Assistant paragraph prose color (optional; empty string or omitted keeps the terminal default foreground)"
149
+ },
146
150
  "thinkingText": {
147
151
  "$ref": "#/$defs/colorValue",
148
152
  "description": "Thinking text color"
@@ -6,7 +6,7 @@ export { getAvailableThemes, getAvailableThemesWithPaths, getThemeByName, type T
6
6
  export { isValidThemeColor, type ThemeBg, type ThemeColor } from "./schema";
7
7
  export { getAvailableSymbolPresets, isValidSymbolPreset, type SpinnerType, type SymbolKey, type SymbolPreset, } from "./symbols";
8
8
  export { Theme } from "./theme-class";
9
- export { createHighlightStream, getEditorTheme, getMarkdownTheme, getSelectListTheme, getSettingsListTheme, getSymbolTheme, highlightCode, setMarkdownMermaidRendering, warmHighlighter, } from "./tui-adapters";
9
+ export { createHighlightStream, getEditorTheme, getMarkdownTheme, getMarkdownThemeWithLinkTargets, getSelectListTheme, getSettingsListTheme, getSymbolTheme, highlightCode, setMarkdownMermaidRendering, warmHighlighter, } from "./tui-adapters";
10
10
  /** Active theme; imports remain live across initialization, previews, and theme switches. */
11
11
  export declare var theme: Theme;
12
12
  type ThemeBinding = (value: Theme) => void;
@@ -5,6 +5,7 @@ import type { Terminal, TerminalAppearance } from "../terminal";
5
5
  import { colorLuma } from "@linxiraos/pi-utils/color";
6
6
  import { getCustomThemesDir } from "@linxiraos/pi-utils/dirs";
7
7
  import * as logger from "@linxiraos/pi-utils/logger";
8
+ import { terminalMultiplexerSessions } from "../terminal-multiplexer";
8
9
  import { setActiveSymbolTheme } from "./active-symbols";
9
10
  import { ansi256ToHex, resolveThemeColors, resolveVarRefs } from "./color";
10
11
  import {
@@ -34,6 +35,7 @@ export {
34
35
  createHighlightStream,
35
36
  getEditorTheme,
36
37
  getMarkdownTheme,
38
+ getMarkdownThemeWithLinkTargets,
37
39
  getSelectListTheme,
38
40
  getSettingsListTheme,
39
41
  getSymbolTheme,
@@ -49,10 +51,12 @@ var terminalReportedAppearance: "dark" | "light" | undefined;
49
51
  var macOSReportedAppearance: "dark" | "light" | undefined;
50
52
 
51
53
  function shouldUseMacOSAppearanceFallback(): boolean {
52
- // Zellij currently breaks OSC 11 passthrough on macOS, so terminal-derived
53
- // appearance cannot be trusted there. Fall back to host macOS appearance
54
- // without letting it override valid terminal signals elsewhere.
55
- return process.platform === "darwin" && !!Bun.env.ZELLIJ;
54
+ // Some multiplexers break OSC 11 passthrough on some platforms (Zellij on
55
+ // macOS), so terminal-derived appearance cannot be trusted there. Fall back
56
+ // to host macOS appearance without letting it override valid terminal
57
+ // signals elsewhere.
58
+ const platform = process.platform;
59
+ return terminalMultiplexerSessions().some(multiplexer => multiplexer.backgroundQueryBrokenOn?.includes(platform));
56
60
  }
57
61
 
58
62
  function detectTerminalBackground(): "dark" | "light" {
@@ -16,6 +16,9 @@ export declare function createHighlightStream(lang?: string, highlightTheme?: Th
16
16
  /** Warm native syntax grammars off-thread once per process. */
17
17
  export declare function warmHighlighter(): Promise<void>;
18
18
  export declare function getSymbolTheme(): SymbolTheme;
19
+ /** Only themes assembled here have stable callbacks and private link state. */
20
+ export declare function canCacheMarkdownListItems(candidate: MarkdownTheme): boolean;
21
+ export declare function getMarkdownThemeWithLinkTargets(targets: ReadonlyMap<string, string>): MarkdownTheme;
19
22
  export declare function setMarkdownMermaidRendering(enabled: boolean): void;
20
23
  export declare function getMarkdownTheme(): MarkdownTheme;
21
24
  export declare function getSelectListTheme(): SelectListTheme;
@@ -183,8 +183,75 @@ export function getSymbolTheme(): SymbolTheme {
183
183
 
184
184
  let cachedMarkdownTheme: MarkdownTheme | undefined;
185
185
  let cachedMarkdownThemeRef: Theme | undefined;
186
+ const cacheableMarkdownThemes = new WeakMap<
187
+ MarkdownTheme,
188
+ {
189
+ theme: Theme;
190
+ fields: ReadonlyArray<readonly [PropertyKey, unknown]>;
191
+ symbolFields: ReadonlyArray<readonly [PropertyKey, unknown]>;
192
+ symbolValues: string;
193
+ }
194
+ >();
195
+ const linkTargetSnapshots = new WeakMap<ReadonlyMap<string, string>, ReadonlyMap<string, string>>();
186
196
  let markdownMermaidRendering = true;
187
197
 
198
+ function ownFields(object: object): ReadonlyArray<readonly [PropertyKey, unknown]> {
199
+ return Reflect.ownKeys(object).map(key => [key, Object.getOwnPropertyDescriptor(object, key)?.value]);
200
+ }
201
+
202
+ function hasUnchangedOwnFields(object: object, fields: ReadonlyArray<readonly [PropertyKey, unknown]>): boolean {
203
+ if (Reflect.ownKeys(object).length !== fields.length) return false;
204
+ return fields.every(([key, value]) => {
205
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
206
+ return descriptor !== undefined && "value" in descriptor && descriptor.value === value;
207
+ });
208
+ }
209
+
210
+ /** Only themes assembled here have stable callbacks and private link state. */
211
+ export function canCacheMarkdownListItems(candidate: MarkdownTheme): boolean {
212
+ const registered = cacheableMarkdownThemes.get(candidate);
213
+ if (registered === undefined || registered.theme !== theme || candidate.symbols === undefined) return false;
214
+ if (!hasUnchangedOwnFields(candidate, registered.fields)) return false;
215
+ if (!hasUnchangedOwnFields(candidate.symbols, registered.symbolFields)) return false;
216
+ try {
217
+ return JSON.stringify(candidate.symbols) === registered.symbolValues;
218
+ } catch {
219
+ return false;
220
+ }
221
+ }
222
+
223
+ function registerCacheableMarkdownTheme(markdownTheme: MarkdownTheme): void {
224
+ if (markdownTheme.symbols === undefined) return;
225
+ cacheableMarkdownThemes.set(markdownTheme, {
226
+ theme,
227
+ fields: ownFields(markdownTheme),
228
+ symbolFields: ownFields(markdownTheme.symbols),
229
+ symbolValues: JSON.stringify(markdownTheme.symbols),
230
+ });
231
+ }
232
+
233
+ export function getMarkdownThemeWithLinkTargets(targets: ReadonlyMap<string, string>): MarkdownTheme {
234
+ const base = getMarkdownTheme();
235
+ let snapshot = linkTargetSnapshots.get(targets);
236
+ let matches = snapshot?.size === targets.size;
237
+ if (matches && snapshot !== undefined) {
238
+ for (const [href, target] of targets) {
239
+ if (snapshot.get(href) !== target) {
240
+ matches = false;
241
+ break;
242
+ }
243
+ }
244
+ }
245
+ if (snapshot === undefined || !matches) {
246
+ snapshot = new Map(targets);
247
+ linkTargetSnapshots.set(targets, snapshot);
248
+ }
249
+ const stableSnapshot = snapshot;
250
+ const linked: MarkdownTheme = { ...base, resolveLink: href => stableSnapshot.get(href) };
251
+ if (canCacheMarkdownListItems(base)) registerCacheableMarkdownTheme(linked);
252
+ return linked;
253
+ }
254
+
188
255
  export function setMarkdownMermaidRendering(enabled: boolean): void {
189
256
  if (markdownMermaidRendering === enabled) return;
190
257
  markdownMermaidRendering = enabled;
@@ -247,6 +314,7 @@ export function getMarkdownTheme(): MarkdownTheme {
247
314
  };
248
315
  cachedMarkdownTheme = markdownTheme;
249
316
  cachedMarkdownThemeRef = theme;
317
+ registerCacheableMarkdownTheme(markdownTheme);
250
318
  return markdownTheme;
251
319
  }
252
320
 
package/src/thinking.d.ts CHANGED
@@ -4,7 +4,7 @@ import { ThinkingLevel } from "@linxiraos/pi-agent-core/thinking";
4
4
  import { Effort } from "@linxiraos/pi-catalog/effort";
5
5
  import type { Model } from "@linxiraos/pi-catalog/types";
6
6
  /** Thinking selectors accepted by CLI inputs, in display order. */
7
- export declare const CLI_THINKING_LEVELS: readonly string[];
7
+ export declare const CLI_THINKING_LEVELS: readonly ConfiguredThinkingLevel[];
8
8
  /**
9
9
  * Metadata used to render thinking selector values in the coding-agent UI.
10
10
  */
package/src/thinking.ts CHANGED
@@ -6,7 +6,7 @@ import { clampThinkingLevelForModel, getSupportedEfforts } from "@linxiraos/pi-c
6
6
  import { modelsAreEqual } from "@linxiraos/pi-catalog/models";
7
7
  import type { Model } from "@linxiraos/pi-catalog/types";
8
8
  /** Thinking selectors accepted by CLI inputs, in display order. */
9
- export const CLI_THINKING_LEVELS: readonly string[] = ["off", ...THINKING_EFFORTS, "auto"];
9
+ export const CLI_THINKING_LEVELS: readonly ConfiguredThinkingLevel[] = ["off", ...THINKING_EFFORTS, "auto"];
10
10
 
11
11
  /**
12
12
  * Metadata used to render thinking selector values in the coding-agent UI.
package/src/tmux.d.ts CHANGED
@@ -1,9 +1,16 @@
1
- /** Whether the process is running inside a tmux session. */
2
- export declare function isInsideTmux(env?: NodeJS.ProcessEnv): boolean;
3
- /** Wrap a control sequence in tmux's DCS passthrough envelope. */
4
- export declare function wrapTmuxPassthrough(payload: string): string;
1
+ export { wrapTmuxPassthrough } from "./multiplexers/tmux";
5
2
  /** Pass a control sequence through tmux, leaving direct-terminal output unchanged. */
6
3
  export declare function wrapTmuxPassthroughIfNeeded(payload: string, env?: NodeJS.ProcessEnv): string;
4
+ /**
5
+ * Whether the terminal hosting this tmux session draws TSP programs natively:
6
+ * it sets the session user option `@tern-tsp` while it watches the session.
7
+ */
8
+ export declare function tmuxSessionSpeaksTsp(env?: NodeJS.ProcessEnv): boolean;
9
+ /**
10
+ * Stop (`on` false) or restore (`on` true) tmux turning this pane's APC
11
+ * strings into its title, which would otherwise swallow every TSP message.
12
+ */
13
+ export declare function setTmuxPaneApcTitles(on: boolean, env?: NodeJS.ProcessEnv): void;
7
14
  /**
8
15
  * Resolve the terminal emulator name recorded for this tmux client.
9
16
  *
package/src/tmux.ts CHANGED
@@ -1,19 +1,12 @@
1
1
  import { $which } from "@linxiraos/pi-utils";
2
2
  import { isBunTestRuntime } from "@linxiraos/pi-utils/env";
3
+ import { tmuxMultiplexer, wrapTmuxPassthrough } from "./multiplexers/tmux";
3
4
 
4
- /** Whether the process is running inside a tmux session. */
5
- export function isInsideTmux(env: NodeJS.ProcessEnv = Bun.env): boolean {
6
- return Boolean(env.TMUX);
7
- }
8
-
9
- /** Wrap a control sequence in tmux's DCS passthrough envelope. */
10
- export function wrapTmuxPassthrough(payload: string): string {
11
- return `\x1bPtmux;${payload.replaceAll("\x1b", "\x1b\x1b")}\x1b\\`;
12
- }
5
+ export { wrapTmuxPassthrough } from "./multiplexers/tmux";
13
6
 
14
7
  /** Pass a control sequence through tmux, leaving direct-terminal output unchanged. */
15
8
  export function wrapTmuxPassthroughIfNeeded(payload: string, env: NodeJS.ProcessEnv = Bun.env): string {
16
- return isInsideTmux(env) ? wrapTmuxPassthrough(payload) : payload;
9
+ return tmuxMultiplexer.isInside(env) ? wrapTmuxPassthrough(payload) : payload;
17
10
  }
18
11
 
19
12
  const CLIENT_TERMTYPE_NAME = /^([A-Za-z][A-Za-z0-9._+-]*)(?=\s|\(|$)/u;
@@ -38,6 +31,49 @@ function queryTmuxClientTerminalName(env: NodeJS.ProcessEnv): string | null {
38
31
  }
39
32
  }
40
33
 
34
+ const TMUX_COMMAND_TIMEOUT_MS = 500;
35
+
36
+ /** Run a tmux command against the current server; its stdout, or null on failure. */
37
+ function runTmux(args: string[], env: NodeJS.ProcessEnv): string | null {
38
+ const tmux = $which("tmux", { PATH: env.PATH });
39
+ if (!tmux) return null;
40
+ try {
41
+ const result = Bun.spawnSync([tmux, ...args], {
42
+ env,
43
+ stdout: "pipe",
44
+ stderr: "ignore",
45
+ timeout: TMUX_COMMAND_TIMEOUT_MS,
46
+ killSignal: "SIGKILL",
47
+ });
48
+ return result.exitCode === 0 ? result.stdout.toString() : null;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Whether the terminal hosting this tmux session draws TSP programs natively:
56
+ * it sets the session user option `@tern-tsp` while it watches the session.
57
+ */
58
+ export function tmuxSessionSpeaksTsp(env: NodeJS.ProcessEnv = Bun.env): boolean {
59
+ return (runTmux(["show-options", "-qv", "@tern-tsp"], env)?.trim() ?? "") !== "";
60
+ }
61
+
62
+ /**
63
+ * Stop (`on` false) or restore (`on` true) tmux turning this pane's APC
64
+ * strings into its title, which would otherwise swallow every TSP message.
65
+ */
66
+ export function setTmuxPaneApcTitles(on: boolean, env: NodeJS.ProcessEnv = Bun.env): void {
67
+ const pane = env.TMUX_PANE;
68
+ if (!pane) return;
69
+ runTmux(
70
+ on
71
+ ? ["set-option", "-pu", "-t", pane, "allow-set-title"]
72
+ : ["set-option", "-p", "-t", pane, "allow-set-title", "off"],
73
+ env,
74
+ );
75
+ }
76
+
41
77
  /**
42
78
  * Resolve the terminal emulator name recorded for this tmux client.
43
79
  *
@@ -46,7 +82,7 @@ function queryTmuxClientTerminalName(env: NodeJS.ProcessEnv): string | null {
46
82
  * query runs once per process and degrades to `null` when unavailable.
47
83
  */
48
84
  export function resolveTmuxClientTerminalName(env: NodeJS.ProcessEnv = Bun.env): string | null {
49
- if (!isInsideTmux(env) || isBunTestRuntime()) return null;
85
+ if (!tmuxMultiplexer.isInside(env) || isBunTestRuntime()) return null;
50
86
  if (cachedClientTerminalName === undefined) cachedClientTerminalName = queryTmuxClientTerminalName(env);
51
87
  return cachedClientTerminalName;
52
88
  }