@agimon-ai/doompi-ui 0.0.1-alpha.2 → 0.0.1-alpha.21

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 (327) hide show
  1. package/README.md +21 -18
  2. package/dist/_virtual/_rolldown/runtime.cjs +1 -1
  3. package/dist/adapters/pi/extension.cjs +2 -0
  4. package/dist/adapters/pi/extension.cjs.map +1 -0
  5. package/dist/adapters/pi/extension.d.cts +7 -0
  6. package/dist/adapters/pi/extension.d.cts.map +1 -0
  7. package/dist/adapters/pi/extension.d.mts +7 -0
  8. package/dist/adapters/pi/extension.d.mts.map +1 -0
  9. package/dist/adapters/pi/extension.mjs +2 -0
  10. package/dist/adapters/pi/extension.mjs.map +1 -0
  11. package/dist/adapters/pi/extensionName.cjs +2 -0
  12. package/dist/adapters/pi/extensionName.cjs.map +1 -0
  13. package/dist/adapters/pi/extensionName.d.cts +16 -0
  14. package/dist/adapters/pi/extensionName.d.cts.map +1 -0
  15. package/dist/adapters/pi/extensionName.d.mts +16 -0
  16. package/dist/adapters/pi/extensionName.d.mts.map +1 -0
  17. package/dist/adapters/pi/extensionName.mjs +2 -0
  18. package/dist/adapters/pi/extensionName.mjs.map +1 -0
  19. package/dist/adapters/pi/toolOverrides.cjs +2 -0
  20. package/dist/adapters/pi/toolOverrides.cjs.map +1 -0
  21. package/dist/adapters/pi/toolOverrides.mjs +2 -0
  22. package/dist/adapters/pi/toolOverrides.mjs.map +1 -0
  23. package/dist/adapters/telemetry/logSinkTelemetry.cjs +2 -0
  24. package/dist/adapters/telemetry/logSinkTelemetry.cjs.map +1 -0
  25. package/dist/adapters/telemetry/logSinkTelemetry.d.cts +29 -0
  26. package/dist/adapters/telemetry/logSinkTelemetry.d.cts.map +1 -0
  27. package/dist/adapters/telemetry/logSinkTelemetry.d.mts +29 -0
  28. package/dist/adapters/telemetry/logSinkTelemetry.d.mts.map +1 -0
  29. package/dist/adapters/telemetry/logSinkTelemetry.mjs +2 -0
  30. package/dist/adapters/telemetry/logSinkTelemetry.mjs.map +1 -0
  31. package/dist/components/configOverlay.cjs +0 -0
  32. package/dist/components/configOverlay.d.cts +0 -0
  33. package/dist/components/configOverlay.d.mts +0 -0
  34. package/dist/components/configOverlay.mjs +0 -0
  35. package/dist/components/doomEditor.cjs +1 -2
  36. package/dist/components/doomEditor.d.cts +2 -59
  37. package/dist/components/doomEditor.d.mts +2 -59
  38. package/dist/components/doomEditor.mjs +1 -2
  39. package/dist/components/doomFooter.cjs +1 -2
  40. package/dist/components/doomFooter.d.cts +2 -32
  41. package/dist/components/doomFooter.d.mts +2 -32
  42. package/dist/components/doomFooter.mjs +1 -2
  43. package/dist/components/doomHeader.cjs +1 -2
  44. package/dist/components/doomHeader.d.cts +2 -34
  45. package/dist/components/doomHeader.d.mts +2 -34
  46. package/dist/components/doomHeader.mjs +1 -2
  47. package/dist/components/doomOverlay.cjs +1 -2
  48. package/dist/components/doomOverlay.d.cts +2 -79
  49. package/dist/components/doomOverlay.d.mts +2 -79
  50. package/dist/components/doomOverlay.mjs +1 -2
  51. package/dist/components/leaderHints.cjs +1 -2
  52. package/dist/components/leaderHints.d.cts +2 -37
  53. package/dist/components/leaderHints.d.mts +2 -37
  54. package/dist/components/leaderHints.mjs +1 -2
  55. package/dist/components/toolsOverlay.cjs +1 -2
  56. package/dist/components/toolsOverlay.d.cts +2 -29
  57. package/dist/components/toolsOverlay.d.mts +2 -29
  58. package/dist/components/toolsOverlay.mjs +1 -2
  59. package/dist/config.cjs +1 -2
  60. package/dist/config.d.cts +2 -5
  61. package/dist/config.d.mts +2 -5
  62. package/dist/config.mjs +1 -2
  63. package/dist/extension.cjs +1 -2
  64. package/dist/extension.d.cts +2 -7
  65. package/dist/extension.d.mts +2 -7
  66. package/dist/extension.mjs +1 -2
  67. package/dist/extensionName.cjs +1 -2
  68. package/dist/extensionName.d.cts +2 -5
  69. package/dist/extensionName.d.mts +2 -5
  70. package/dist/extensionName.mjs +1 -2
  71. package/dist/extensions/pi.cjs +1 -1
  72. package/dist/extensions/pi.cjs.map +1 -1
  73. package/dist/extensions/pi.d.cts +1 -1
  74. package/dist/extensions/pi.d.mts +1 -1
  75. package/dist/extensions/pi.mjs +1 -1
  76. package/dist/extensions/pi.mjs.map +1 -1
  77. package/dist/footer.cjs +1 -2
  78. package/dist/footer.d.cts +2 -5
  79. package/dist/footer.d.mts +2 -5
  80. package/dist/footer.mjs +1 -2
  81. package/dist/harnessMetadata.cjs +1 -2
  82. package/dist/harnessMetadata.d.cts +2 -21
  83. package/dist/harnessMetadata.d.mts +2 -21
  84. package/dist/harnessMetadata.mjs +1 -2
  85. package/dist/index.cjs +1 -1
  86. package/dist/index.d.cts +14 -25
  87. package/dist/index.d.mts +14 -25
  88. package/dist/index.mjs +1 -1
  89. package/dist/leaderRegistry.cjs +1 -2
  90. package/dist/leaderRegistry.d.cts +2 -58
  91. package/dist/leaderRegistry.d.mts +2 -58
  92. package/dist/leaderRegistry.mjs +1 -2
  93. package/dist/logSinkTelemetry.cjs +1 -2
  94. package/dist/logSinkTelemetry.d.cts +2 -29
  95. package/dist/logSinkTelemetry.d.mts +2 -29
  96. package/dist/logSinkTelemetry.mjs +1 -2
  97. package/dist/rendering.cjs +1 -2
  98. package/dist/rendering.d.cts +2 -11
  99. package/dist/rendering.d.mts +2 -11
  100. package/dist/rendering.mjs +1 -2
  101. package/dist/services/leader/leaderRegistry.cjs +2 -0
  102. package/dist/services/leader/leaderRegistry.cjs.map +1 -0
  103. package/dist/services/leader/leaderRegistry.d.cts +58 -0
  104. package/dist/services/leader/leaderRegistry.d.cts.map +1 -0
  105. package/dist/services/leader/leaderRegistry.d.mts +58 -0
  106. package/dist/services/leader/leaderRegistry.d.mts.map +1 -0
  107. package/dist/services/leader/leaderRegistry.mjs +2 -0
  108. package/dist/services/leader/leaderRegistry.mjs.map +1 -0
  109. package/dist/services/state/uiState.cjs +2 -0
  110. package/dist/services/state/uiState.cjs.map +1 -0
  111. package/dist/services/state/uiState.d.cts +38 -0
  112. package/dist/services/state/uiState.d.cts.map +1 -0
  113. package/dist/services/state/uiState.d.mts +38 -0
  114. package/dist/services/state/uiState.d.mts.map +1 -0
  115. package/dist/services/state/uiState.mjs +2 -0
  116. package/dist/services/state/uiState.mjs.map +1 -0
  117. package/dist/services/tools/toolInventory.cjs +2 -0
  118. package/dist/services/tools/toolInventory.cjs.map +1 -0
  119. package/dist/services/tools/toolInventory.d.cts +51 -0
  120. package/dist/services/tools/toolInventory.d.cts.map +1 -0
  121. package/dist/services/tools/toolInventory.d.mts +51 -0
  122. package/dist/services/tools/toolInventory.d.mts.map +1 -0
  123. package/dist/services/tools/toolInventory.mjs +2 -0
  124. package/dist/services/tools/toolInventory.mjs.map +1 -0
  125. package/dist/skills.cjs +1 -1
  126. package/dist/skills.d.cts +2 -1
  127. package/dist/skills.d.mts +2 -1
  128. package/dist/skills.mjs +1 -1
  129. package/dist/theme.cjs +1 -2
  130. package/dist/theme.d.cts +2 -95
  131. package/dist/theme.d.mts +2 -95
  132. package/dist/theme.mjs +1 -2
  133. package/dist/toolChrome.cjs +1 -0
  134. package/dist/toolChrome.d.cts +2 -0
  135. package/dist/toolChrome.d.mts +2 -0
  136. package/dist/toolChrome.mjs +1 -0
  137. package/dist/toolInventory.cjs +1 -2
  138. package/dist/toolInventory.d.cts +2 -40
  139. package/dist/toolInventory.d.mts +2 -40
  140. package/dist/toolInventory.mjs +1 -2
  141. package/dist/tui/builtinToolRender.cjs +14 -0
  142. package/dist/tui/builtinToolRender.cjs.map +1 -0
  143. package/dist/tui/builtinToolRender.mjs +14 -0
  144. package/dist/tui/builtinToolRender.mjs.map +1 -0
  145. package/dist/tui/codeGutter.cjs +2 -0
  146. package/dist/tui/codeGutter.cjs.map +1 -0
  147. package/dist/tui/codeGutter.mjs +2 -0
  148. package/dist/tui/codeGutter.mjs.map +1 -0
  149. package/dist/tui/configOverlay.cjs +0 -0
  150. package/dist/tui/configOverlay.cjs.map +1 -0
  151. package/dist/tui/configOverlay.d.cts +0 -0
  152. package/dist/tui/configOverlay.d.cts.map +1 -0
  153. package/dist/tui/configOverlay.d.mts +0 -0
  154. package/dist/tui/configOverlay.d.mts.map +1 -0
  155. package/dist/tui/configOverlay.mjs +0 -0
  156. package/dist/tui/configOverlay.mjs.map +1 -0
  157. package/dist/tui/diffRender.cjs +4 -0
  158. package/dist/tui/diffRender.cjs.map +1 -0
  159. package/dist/tui/diffRender.mjs +4 -0
  160. package/dist/tui/diffRender.mjs.map +1 -0
  161. package/dist/tui/doomEditor.cjs +2 -0
  162. package/dist/tui/doomEditor.cjs.map +1 -0
  163. package/dist/tui/doomEditor.d.cts +63 -0
  164. package/dist/tui/doomEditor.d.cts.map +1 -0
  165. package/dist/tui/doomEditor.d.mts +63 -0
  166. package/dist/tui/doomEditor.d.mts.map +1 -0
  167. package/dist/tui/doomEditor.mjs +2 -0
  168. package/dist/tui/doomEditor.mjs.map +1 -0
  169. package/dist/tui/doomFooter.cjs +2 -0
  170. package/dist/tui/doomFooter.cjs.map +1 -0
  171. package/dist/tui/doomFooter.d.cts +32 -0
  172. package/dist/tui/doomFooter.d.cts.map +1 -0
  173. package/dist/tui/doomFooter.d.mts +32 -0
  174. package/dist/tui/doomFooter.d.mts.map +1 -0
  175. package/dist/tui/doomFooter.mjs +2 -0
  176. package/dist/tui/doomFooter.mjs.map +1 -0
  177. package/dist/tui/doomHeader.cjs +2 -0
  178. package/dist/tui/doomHeader.cjs.map +1 -0
  179. package/dist/tui/doomHeader.d.cts +34 -0
  180. package/dist/tui/doomHeader.d.cts.map +1 -0
  181. package/dist/tui/doomHeader.d.mts +34 -0
  182. package/dist/tui/doomHeader.d.mts.map +1 -0
  183. package/dist/tui/doomHeader.mjs +2 -0
  184. package/dist/tui/doomHeader.mjs.map +1 -0
  185. package/dist/tui/doomOverlay.cjs +2 -0
  186. package/dist/tui/doomOverlay.cjs.map +1 -0
  187. package/dist/tui/doomOverlay.d.cts +84 -0
  188. package/dist/tui/doomOverlay.d.cts.map +1 -0
  189. package/dist/tui/doomOverlay.d.mts +84 -0
  190. package/dist/tui/doomOverlay.d.mts.map +1 -0
  191. package/dist/tui/doomOverlay.mjs +2 -0
  192. package/dist/tui/doomOverlay.mjs.map +1 -0
  193. package/dist/tui/leaderHints.cjs +2 -0
  194. package/dist/tui/leaderHints.cjs.map +1 -0
  195. package/dist/tui/leaderHints.d.cts +36 -0
  196. package/dist/tui/leaderHints.d.cts.map +1 -0
  197. package/dist/tui/leaderHints.d.mts +36 -0
  198. package/dist/tui/leaderHints.d.mts.map +1 -0
  199. package/dist/tui/leaderHints.mjs +2 -0
  200. package/dist/tui/leaderHints.mjs.map +1 -0
  201. package/dist/tui/rendering.cjs +2 -0
  202. package/dist/tui/rendering.cjs.map +1 -0
  203. package/dist/tui/rendering.d.cts +16 -0
  204. package/dist/tui/rendering.d.cts.map +1 -0
  205. package/dist/tui/rendering.d.mts +16 -0
  206. package/dist/tui/rendering.d.mts.map +1 -0
  207. package/dist/tui/rendering.mjs +2 -0
  208. package/dist/tui/rendering.mjs.map +1 -0
  209. package/dist/tui/theme.cjs +2 -0
  210. package/dist/tui/theme.cjs.map +1 -0
  211. package/dist/tui/theme.d.cts +103 -0
  212. package/dist/tui/theme.d.cts.map +1 -0
  213. package/dist/tui/theme.d.mts +103 -0
  214. package/dist/tui/theme.d.mts.map +1 -0
  215. package/dist/tui/theme.mjs +2 -0
  216. package/dist/tui/theme.mjs.map +1 -0
  217. package/dist/tui/toolChrome.cjs +2 -0
  218. package/dist/tui/toolChrome.cjs.map +1 -0
  219. package/dist/tui/toolChrome.d.cts +30 -0
  220. package/dist/tui/toolChrome.d.cts.map +1 -0
  221. package/dist/tui/toolChrome.d.mts +30 -0
  222. package/dist/tui/toolChrome.d.mts.map +1 -0
  223. package/dist/tui/toolChrome.mjs +2 -0
  224. package/dist/tui/toolChrome.mjs.map +1 -0
  225. package/dist/tui/toolsOverlay.cjs +2 -0
  226. package/dist/tui/toolsOverlay.cjs.map +1 -0
  227. package/dist/tui/toolsOverlay.d.cts +29 -0
  228. package/dist/tui/toolsOverlay.d.cts.map +1 -0
  229. package/dist/tui/toolsOverlay.d.mts +29 -0
  230. package/dist/tui/toolsOverlay.d.mts.map +1 -0
  231. package/dist/tui/toolsOverlay.mjs +2 -0
  232. package/dist/tui/toolsOverlay.mjs.map +1 -0
  233. package/dist/types/harnessMetadata.cjs +2 -0
  234. package/dist/types/harnessMetadata.cjs.map +1 -0
  235. package/dist/types/harnessMetadata.d.cts +21 -0
  236. package/dist/types/harnessMetadata.d.cts.map +1 -0
  237. package/dist/types/harnessMetadata.d.mts +21 -0
  238. package/dist/types/harnessMetadata.d.mts.map +1 -0
  239. package/dist/types/harnessMetadata.mjs +2 -0
  240. package/dist/types/harnessMetadata.mjs.map +1 -0
  241. package/dist/uiState.cjs +1 -2
  242. package/dist/uiState.d.cts +2 -37
  243. package/dist/uiState.d.mts +2 -37
  244. package/dist/uiState.mjs +1 -2
  245. package/package.json +19 -12
  246. package/themes/doom-pi-dark.json +32 -30
  247. package/dist/_virtual/_rolldown/runtime.mjs +0 -1
  248. package/dist/components/configOverlay.cjs.map +0 -1
  249. package/dist/components/configOverlay.d.cts.map +0 -1
  250. package/dist/components/configOverlay.d.mts.map +0 -1
  251. package/dist/components/configOverlay.mjs.map +0 -1
  252. package/dist/components/doomEditor.cjs.map +0 -1
  253. package/dist/components/doomEditor.d.cts.map +0 -1
  254. package/dist/components/doomEditor.d.mts.map +0 -1
  255. package/dist/components/doomEditor.mjs.map +0 -1
  256. package/dist/components/doomFooter.cjs.map +0 -1
  257. package/dist/components/doomFooter.d.cts.map +0 -1
  258. package/dist/components/doomFooter.d.mts.map +0 -1
  259. package/dist/components/doomFooter.mjs.map +0 -1
  260. package/dist/components/doomHeader.cjs.map +0 -1
  261. package/dist/components/doomHeader.d.cts.map +0 -1
  262. package/dist/components/doomHeader.d.mts.map +0 -1
  263. package/dist/components/doomHeader.mjs.map +0 -1
  264. package/dist/components/doomOverlay.cjs.map +0 -1
  265. package/dist/components/doomOverlay.d.cts.map +0 -1
  266. package/dist/components/doomOverlay.d.mts.map +0 -1
  267. package/dist/components/doomOverlay.mjs.map +0 -1
  268. package/dist/components/leaderHints.cjs.map +0 -1
  269. package/dist/components/leaderHints.d.cts.map +0 -1
  270. package/dist/components/leaderHints.d.mts.map +0 -1
  271. package/dist/components/leaderHints.mjs.map +0 -1
  272. package/dist/components/toolsOverlay.cjs.map +0 -1
  273. package/dist/components/toolsOverlay.d.cts.map +0 -1
  274. package/dist/components/toolsOverlay.d.mts.map +0 -1
  275. package/dist/components/toolsOverlay.mjs.map +0 -1
  276. package/dist/config.cjs.map +0 -1
  277. package/dist/config.d.cts.map +0 -1
  278. package/dist/config.d.mts.map +0 -1
  279. package/dist/config.mjs.map +0 -1
  280. package/dist/extension.cjs.map +0 -1
  281. package/dist/extension.d.cts.map +0 -1
  282. package/dist/extension.d.mts.map +0 -1
  283. package/dist/extension.mjs.map +0 -1
  284. package/dist/extensionName.cjs.map +0 -1
  285. package/dist/extensionName.d.cts.map +0 -1
  286. package/dist/extensionName.d.mts.map +0 -1
  287. package/dist/extensionName.mjs.map +0 -1
  288. package/dist/footer.cjs.map +0 -1
  289. package/dist/footer.d.cts.map +0 -1
  290. package/dist/footer.d.mts.map +0 -1
  291. package/dist/footer.mjs.map +0 -1
  292. package/dist/harnessMetadata.cjs.map +0 -1
  293. package/dist/harnessMetadata.d.cts.map +0 -1
  294. package/dist/harnessMetadata.d.mts.map +0 -1
  295. package/dist/harnessMetadata.mjs.map +0 -1
  296. package/dist/leaderRegistry.cjs.map +0 -1
  297. package/dist/leaderRegistry.d.cts.map +0 -1
  298. package/dist/leaderRegistry.d.mts.map +0 -1
  299. package/dist/leaderRegistry.mjs.map +0 -1
  300. package/dist/logSinkTelemetry.cjs.map +0 -1
  301. package/dist/logSinkTelemetry.d.cts.map +0 -1
  302. package/dist/logSinkTelemetry.d.mts.map +0 -1
  303. package/dist/logSinkTelemetry.mjs.map +0 -1
  304. package/dist/mode.cjs +0 -2
  305. package/dist/mode.cjs.map +0 -1
  306. package/dist/mode.d.cts +0 -5
  307. package/dist/mode.d.cts.map +0 -1
  308. package/dist/mode.d.mts +0 -5
  309. package/dist/mode.d.mts.map +0 -1
  310. package/dist/mode.mjs +0 -2
  311. package/dist/mode.mjs.map +0 -1
  312. package/dist/rendering.cjs.map +0 -1
  313. package/dist/rendering.d.cts.map +0 -1
  314. package/dist/rendering.d.mts.map +0 -1
  315. package/dist/rendering.mjs.map +0 -1
  316. package/dist/theme.cjs.map +0 -1
  317. package/dist/theme.d.cts.map +0 -1
  318. package/dist/theme.d.mts.map +0 -1
  319. package/dist/theme.mjs.map +0 -1
  320. package/dist/toolInventory.cjs.map +0 -1
  321. package/dist/toolInventory.d.cts.map +0 -1
  322. package/dist/toolInventory.d.mts.map +0 -1
  323. package/dist/toolInventory.mjs.map +0 -1
  324. package/dist/uiState.cjs.map +0 -1
  325. package/dist/uiState.d.cts.map +0 -1
  326. package/dist/uiState.d.mts.map +0 -1
  327. package/dist/uiState.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaderHints.cjs","names":["padLine","alignLine","visibleWidth","fitLine","formatTokens","packSegments"],"sources":["../../src/tui/leaderHints.ts"],"sourcesContent":["import type { ExtensionContext, ReadonlyFooterDataProvider, Theme, ThemeColor } from '@earendil-works/pi-coding-agent';\n\nimport { type Component, visibleWidth } from '@earendil-works/pi-tui';\nimport type { LeaderSnapshot, UiMinorModeStatus } from '../services/state/uiState.ts';\nimport { alignLine, fitLine, formatTokens, packSegments, padLine } from './rendering.ts';\n\n/** The background palette is not exported by name, so take it from the method. */\ntype ThemeBg = Parameters<Theme['bg']>[0];\n\nconst WIDE_HINT_WIDTH = 100;\n/**\n * No frame rows to pay for, so the whole budget is content. Two-line option\n * cards need roughly double the grid rows a single-line list did, plus a blank\n * row of breathing space at the top and bottom.\n */\nconst MAX_WIDGET_LINES = 21;\n/** Leading pad inside the shaded block, mirrored by padLine on the right. */\nconst PANEL_INSET = ' ';\nconst MIN_RENDER_WIDTH = 4;\nconst CONTENT_INSET = 4;\nconst GRID_GAP = 2;\nconst DIAGNOSTIC_LABEL_WIDTH = 11;\nconst DIAGNOSTIC_VALUE_INSET = 15;\nconst MAX_EXTENSION_LINES = 2;\nconst STATUS_SEPARATOR = ' · ';\nconst SEGMENT_GAP = ' ';\n/** The inverse key badge is ` k `, three columns wide. */\nconst KEY_BADGE_WIDTH = 3;\n/** Puts the description under the label rather than under the badge. */\nconst DETAIL_INDENT = ' ';\nconst HORIZONTAL_BORDER = '─';\nconst CONTINUATION_MARKER = '↳';\nconst DIM_COLOR: ThemeColor = 'dim';\nconst TEXT_COLOR: ThemeColor = 'text';\nconst MUTED_COLOR: ThemeColor = 'muted';\nconst ACCENT_COLOR: ThemeColor = 'accent';\nconst WARNING_COLOR: ThemeColor = 'warning';\nconst HEADING_COLOR: ThemeColor = 'mdHeading';\nconst BORDER_COLOR: ThemeColor = 'borderAccent';\nconst MUTED_BORDER_COLOR: ThemeColor = 'borderMuted';\n/** Subtle panel shade; avoid the brighter active/selection surface used by messages. */\nconst PANEL_BACKGROUND: ThemeBg = 'toolPendingBg';\n// The mockup colors each extension status differently so adjacent values stay\n// separable at a glance; cycling this palette reproduces that without hardcoding\n// a color per status key.\nconst STATUS_COLORS: ThemeColor[] = ['mdCode', 'toolDiffAdded', 'warning', 'muted'];\n\nexport class LeaderHints implements Component {\n constructor(\n private readonly theme: Theme,\n private readonly snapshot: LeaderSnapshot,\n private readonly context?: ExtensionContext,\n private readonly footerData?: ReadonlyFooterDataProvider,\n private readonly modes: readonly UiMinorModeStatus[] = [],\n ) {}\n\n invalidate(): void {}\n\n render(width: number): string[] {\n if (width <= 0 || !this.snapshot.active) return [];\n if (width < MIN_RENDER_WIDTH) return [this.theme.fg(BORDER_COLOR, HORIZONTAL_BORDER.repeat(width))];\n\n // A filled block rather than a framed one: the shade alone separates the\n // panel from the transcript, which buys back the two rows a frame costs.\n const contentWidth = Math.max(1, width - CONTENT_INSET);\n const shade = (content: string): string =>\n this.theme.bg(PANEL_BACKGROUND, padLine(`${PANEL_INSET}${content}`, width));\n const badge = this.theme.inverse(this.theme.bold(this.theme.fg(HEADING_COLOR, ' LEADER ')));\n const breadcrumb = `${this.snapshot.prefix.join(' › ')} / ${this.snapshot.label}`;\n const header = shade(\n alignLine(\n `${badge} ${this.theme.bold(this.theme.fg(TEXT_COLOR, breadcrumb))}`,\n this.theme.fg(DIM_COLOR, 'esc cancel · backspace up'),\n contentWidth,\n ),\n );\n const rootTrail = this.snapshot.rootOptions?.map((option) => `${option.key} ${option.label}`).join(' · ');\n const trail =\n width >= WIDE_HINT_WIDTH && this.snapshot.prefix.length > 1 && rootTrail\n ? [shade(this.theme.fg(DIM_COLOR, `root ${rootTrail}`))]\n : [];\n // Options are the interactive surface, so they claim space before the\n // diagnostics do; truncating them would hide a reachable key.\n const optionBudget = Math.max(1, MAX_WIDGET_LINES - 2 - trail.length);\n const optionLines = this.renderOptionGrid(contentWidth).slice(0, optionBudget).map(shade);\n // Four rows are spoken for before diagnostics get any: the blank, header and\n // blank above, and the closing blank below. Counting only two of them let\n // this budget overrun, and the final `slice` did the real cutting silently\n // from the end, which is how the EXTENSIONS row went missing when MODES\n // arrived. Take all four off here so what is dropped is decided in one place.\n const diagnosticsBudget = Math.max(0, MAX_WIDGET_LINES - 4 - trail.length - optionLines.length);\n // Session and extension diagnostics belong to the board opened directly by\n // SPC/^SPC. Sub-boards stay focused on their own actions and navigation.\n const diagnostics =\n this.snapshot.prefix.length === 1\n ? this.renderDiagnostics(contentWidth).slice(0, diagnosticsBudget).map(shade)\n : [];\n // Blank shaded rows top and bottom: the panel butts against the editor above\n // and the modeline below, so the shade needs room before its first glyph.\n const body = [shade(''), header, shade(''), ...optionLines, ...trail, ...diagnostics];\n\n return [...body.slice(0, MAX_WIDGET_LINES - 1), shade('')];\n }\n\n /**\n * Lays options out on a fixed cell grid rather than a packed run of segments,\n * so keys and labels stay column-aligned when the list wraps.\n */\n private renderOptionGrid(width: number): string[] {\n if (this.snapshot.options.length === 0) return [];\n\n // Two lines per option, as the mockup lays them out: the key badge and a\n // bold label, then the description indented under the label.\n const cells = this.snapshot.options.map((option) => {\n const key = this.theme.inverse(this.theme.bold(this.theme.fg(ACCENT_COLOR, ` ${option.key} `)));\n return {\n heading: `${key} ${this.theme.bold(this.theme.fg(TEXT_COLOR, option.label))}`,\n headingWidth: KEY_BADGE_WIDTH + 1 + visibleWidth(option.label),\n detail: option.detail ? `${DETAIL_INDENT}${this.theme.fg(DIM_COLOR, option.detail)}` : '',\n detailWidth: option.detail ? visibleWidth(DETAIL_INDENT) + visibleWidth(option.detail) : 0,\n };\n });\n const widest = Math.max(...cells.map((cell) => Math.max(cell.headingWidth, cell.detailWidth)));\n const cellWidth = Math.min(width, widest);\n const columns = Math.max(1, Math.floor((width + GRID_GAP) / (cellWidth + GRID_GAP)));\n const gap = ' '.repeat(GRID_GAP);\n const rows: string[] = [];\n for (let index = 0; index < cells.length; index += columns) {\n const group = cells.slice(index, index + columns);\n rows.push(fitLine(group.map((cell) => padLine(cell.heading, cellWidth)).join(gap), width));\n // Keep the second row even when every detail is empty. Each option card\n // then retains the same two-line rhythm instead of collapsing terse items\n // into a denser list than items with supporting text.\n rows.push(fitLine(group.map((cell) => padLine(cell.detail, cellWidth)).join(gap), width));\n }\n return rows;\n }\n\n /** Content lines only: the caller paints the shade and pads to full width. */\n private renderDiagnostics(width: number): string[] {\n const session = this.sessionDiagnostics();\n const extensions = this.extensionDiagnostics(width);\n const modes = this.modeDiagnostics();\n if (!session && !modes && extensions.length === 0) return [];\n\n const lines = [this.theme.fg(MUTED_BORDER_COLOR, HORIZONTAL_BORDER.repeat(width))];\n // Above SESSION because it is the more consequential fact: session counters\n // describe what this session has spent, a mode describes what it will do.\n //\n // No blank rows between these three. The diagnostics budget is what is left\n // after the option grid takes its share, which at a full grid is exactly one\n // row per label: a spacer here does not cost breathing room, it costs the\n // EXTENSIONS row entirely. Distinct labels in distinct colours separate them\n // well enough without one.\n if (modes) lines.push(this.diagnosticLine('MODES', modes, WARNING_COLOR));\n if (session) lines.push(this.diagnosticLine('SESSION', session, HEADING_COLOR));\n for (const [index, extension] of extensions.entries()) {\n const isFirst = index === 0;\n lines.push(\n this.diagnosticLine(\n isFirst ? 'EXTENSIONS' : CONTINUATION_MARKER,\n extension,\n isFirst ? ACCENT_COLOR : DIM_COLOR,\n ),\n );\n }\n return lines;\n }\n\n private diagnosticLine(label: string, value: string, color: ThemeColor): string {\n return `${this.theme.bold(this.theme.fg(color, label.padEnd(DIAGNOSTIC_LABEL_WIDTH)))}${value}`;\n }\n\n /**\n * The enabled modes with their detail, which is the long form of what the\n * editor's badge row shows as bare names.\n *\n * This panel is opened deliberately and has room, so it is the right place\n * for `plan (normal - read only)`: the badge row is glanced at mid-typing and\n * only has to answer which modes are on.\n */\n private modeDiagnostics(): string | undefined {\n if (this.modes.length === 0) return undefined;\n return this.modes\n .map((mode) => {\n const label = this.theme.fg(WARNING_COLOR, mode.label.toLowerCase());\n return mode.detail ? `${label}${this.theme.fg(DIM_COLOR, ` (${mode.detail})`)}` : label;\n })\n .join(this.theme.fg(DIM_COLOR, STATUS_SEPARATOR));\n }\n\n private sessionDiagnostics(): string | undefined {\n if (!this.context) return undefined;\n\n let input = 0;\n let output = 0;\n let cacheRead = 0;\n let cacheWrite = 0;\n let cost = 0;\n for (const entry of this.context.sessionManager.getBranch()) {\n if (entry.type !== 'message' || entry.message.role !== 'assistant') continue;\n input += entry.message.usage.input;\n output += entry.message.usage.output;\n cacheRead += entry.message.usage.cacheRead;\n cacheWrite += entry.message.usage.cacheWrite;\n cost += entry.message.usage.cost.total;\n }\n\n const cachedTotal = input + cacheRead + cacheWrite;\n const cacheHit = cachedTotal === 0 ? 0 : (cacheRead / cachedTotal) * 100;\n const usage = this.context.getContextUsage();\n const context =\n usage?.percent === null || usage?.percent === undefined\n ? 'ctx ?'\n : `ctx ${usage.percent.toFixed(1)}% / ${formatTokens(usage.contextWindow)}`;\n // Built as sibling segments rather than nested theme calls so each color\n // closes cleanly instead of resetting the one around it.\n const metrics = this.theme.fg(\n MUTED_COLOR,\n `↑${formatTokens(input)} ↓${formatTokens(output)} R${formatTokens(cacheRead)} W${formatTokens(\n cacheWrite,\n )} CH${cacheHit.toFixed(1)}%`,\n );\n const costText = this.theme.fg(WARNING_COLOR, `$${cost.toFixed(3)}`);\n return `${metrics}${SEGMENT_GAP}${costText}${this.theme.fg(DIM_COLOR, `${STATUS_SEPARATOR}${context}`)}`;\n }\n\n private extensionDiagnostics(width: number): string[] {\n if (!this.footerData) return [];\n\n const statuses = [...this.footerData.getExtensionStatuses().values()];\n const providers = this.footerData.getAvailableProviderCount();\n const values = [...statuses];\n if (providers > 0) values.push(`${providers} provider${providers === 1 ? '' : 's'}`);\n const colored = values.map((value, index) =>\n this.theme.fg(STATUS_COLORS[index % STATUS_COLORS.length] as ThemeColor, value),\n );\n const valueWidth = Math.max(1, width - DIAGNOSTIC_VALUE_INSET);\n return packSegments(colored, valueWidth, this.theme.fg(DIM_COLOR, STATUS_SEPARATOR)).slice(0, MAX_EXTENSION_LINES);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { LeaderSnapshot, UiMinorModeStatus } from "../services/state/uiState.cjs";
2
+ import { ExtensionContext, ReadonlyFooterDataProvider, Theme } from "@earendil-works/pi-coding-agent";
3
+ import { Component } from "@earendil-works/pi-tui";
4
+ //#region src/tui/leaderHints.d.ts
5
+ declare class LeaderHints implements Component {
6
+ private readonly theme;
7
+ private readonly snapshot;
8
+ private readonly context?;
9
+ private readonly footerData?;
10
+ private readonly modes;
11
+ constructor(theme: Theme, snapshot: LeaderSnapshot, context?: ExtensionContext | undefined, footerData?: ReadonlyFooterDataProvider | undefined, modes?: readonly UiMinorModeStatus[]);
12
+ invalidate(): void;
13
+ render(width: number): string[];
14
+ /**
15
+ * Lays options out on a fixed cell grid rather than a packed run of segments,
16
+ * so keys and labels stay column-aligned when the list wraps.
17
+ */
18
+ private renderOptionGrid;
19
+ /** Content lines only: the caller paints the shade and pads to full width. */
20
+ private renderDiagnostics;
21
+ private diagnosticLine;
22
+ /**
23
+ * The enabled modes with their detail, which is the long form of what the
24
+ * editor's badge row shows as bare names.
25
+ *
26
+ * This panel is opened deliberately and has room, so it is the right place
27
+ * for `plan (normal - read only)`: the badge row is glanced at mid-typing and
28
+ * only has to answer which modes are on.
29
+ */
30
+ private modeDiagnostics;
31
+ private sessionDiagnostics;
32
+ private extensionDiagnostics;
33
+ }
34
+ //#endregion
35
+ export { LeaderHints };
36
+ //# sourceMappingURL=leaderHints.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaderHints.d.cts","names":[],"sources":["../../src/tui/leaderHints.ts"],"mappings":";;;;cA+Ca,uBAAuB;mBAEf;mBACA;mBACA;mBACA;mBACA;EAJA,YAAA,OAAO,OACP,UAAU,gBACV,UAAU,8BACV,aAAa,wCACb,iBAAgB;EAGnC;EAEA,OAAO;;;;;UAkDC;;UA+BA;UA8BA;;;;;;;;;UAYA;UAUA;UAoCA"}
@@ -0,0 +1,36 @@
1
+ import { LeaderSnapshot, UiMinorModeStatus } from "../services/state/uiState.mjs";
2
+ import { Component } from "@earendil-works/pi-tui";
3
+ import { ExtensionContext, ReadonlyFooterDataProvider, Theme } from "@earendil-works/pi-coding-agent";
4
+ //#region src/tui/leaderHints.d.ts
5
+ declare class LeaderHints implements Component {
6
+ private readonly theme;
7
+ private readonly snapshot;
8
+ private readonly context?;
9
+ private readonly footerData?;
10
+ private readonly modes;
11
+ constructor(theme: Theme, snapshot: LeaderSnapshot, context?: ExtensionContext | undefined, footerData?: ReadonlyFooterDataProvider | undefined, modes?: readonly UiMinorModeStatus[]);
12
+ invalidate(): void;
13
+ render(width: number): string[];
14
+ /**
15
+ * Lays options out on a fixed cell grid rather than a packed run of segments,
16
+ * so keys and labels stay column-aligned when the list wraps.
17
+ */
18
+ private renderOptionGrid;
19
+ /** Content lines only: the caller paints the shade and pads to full width. */
20
+ private renderDiagnostics;
21
+ private diagnosticLine;
22
+ /**
23
+ * The enabled modes with their detail, which is the long form of what the
24
+ * editor's badge row shows as bare names.
25
+ *
26
+ * This panel is opened deliberately and has room, so it is the right place
27
+ * for `plan (normal - read only)`: the badge row is glanced at mid-typing and
28
+ * only has to answer which modes are on.
29
+ */
30
+ private modeDiagnostics;
31
+ private sessionDiagnostics;
32
+ private extensionDiagnostics;
33
+ }
34
+ //#endregion
35
+ export { LeaderHints };
36
+ //# sourceMappingURL=leaderHints.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaderHints.d.mts","names":[],"sources":["../../src/tui/leaderHints.ts"],"mappings":";;;;cA+Ca,uBAAuB;mBAEf;mBACA;mBACA;mBACA;mBACA;EAJA,YAAA,OAAO,OACP,UAAU,gBACV,UAAU,8BACV,aAAa,wCACb,iBAAgB;EAGnC;EAEA,OAAO;;;;;UAkDC;;UA+BA;UA8BA;;;;;;;;;UAYA;UAUA;UAoCA"}
@@ -0,0 +1,2 @@
1
+ import{alignLine as e,fitLine as t,formatTokens as n,packSegments as r,padLine as i}from"./rendering.mjs";import{visibleWidth as a}from"@earendil-works/pi-tui";const o=` · `,s=` `,c=`text`,l=`accent`,u=`warning`,d=`mdHeading`,f=[`mdCode`,`toolDiffAdded`,`warning`,`muted`];var p=class{theme;snapshot;context;footerData;modes;constructor(e,t,n,r,i=[]){this.theme=e,this.snapshot=t,this.context=n,this.footerData=r,this.modes=i}invalidate(){}render(t){if(t<=0||!this.snapshot.active)return[];if(t<4)return[this.theme.fg(`borderAccent`,`─`.repeat(t))];let n=Math.max(1,t-4),r=e=>this.theme.bg(`toolPendingBg`,i(` ${e}`,t)),a=this.theme.inverse(this.theme.bold(this.theme.fg(d,` LEADER `))),o=`${this.snapshot.prefix.join(` › `)} / ${this.snapshot.label}`,s=r(e(`${a} ${this.theme.bold(this.theme.fg(c,o))}`,this.theme.fg(`dim`,`esc cancel · backspace up`),n)),l=this.snapshot.rootOptions?.map(e=>`${e.key} ${e.label}`).join(` · `),u=t>=100&&this.snapshot.prefix.length>1&&l?[r(this.theme.fg(`dim`,`root ${l}`))]:[],f=Math.max(1,19-u.length),p=this.renderOptionGrid(n).slice(0,f).map(r),m=Math.max(0,17-u.length-p.length),h=this.snapshot.prefix.length===1?this.renderDiagnostics(n).slice(0,m).map(r):[];return[...[r(``),s,r(``),...p,...u,...h].slice(0,20),r(``)]}renderOptionGrid(e){if(this.snapshot.options.length===0)return[];let n=this.snapshot.options.map(e=>({heading:`${this.theme.inverse(this.theme.bold(this.theme.fg(l,` ${e.key} `)))} ${this.theme.bold(this.theme.fg(c,e.label))}`,headingWidth:4+a(e.label),detail:e.detail?`${s}${this.theme.fg(`dim`,e.detail)}`:``,detailWidth:e.detail?a(s)+a(e.detail):0})),r=Math.max(...n.map(e=>Math.max(e.headingWidth,e.detailWidth))),o=Math.min(e,r),u=Math.max(1,Math.floor((e+2)/(o+2))),d=` `.repeat(2),f=[];for(let r=0;r<n.length;r+=u){let a=n.slice(r,r+u);f.push(t(a.map(e=>i(e.heading,o)).join(d),e)),f.push(t(a.map(e=>i(e.detail,o)).join(d),e))}return f}renderDiagnostics(e){let t=this.sessionDiagnostics(),n=this.extensionDiagnostics(e),r=this.modeDiagnostics();if(!t&&!r&&n.length===0)return[];let i=[this.theme.fg(`borderMuted`,`─`.repeat(e))];r&&i.push(this.diagnosticLine(`MODES`,r,u)),t&&i.push(this.diagnosticLine(`SESSION`,t,d));for(let[e,t]of n.entries()){let n=e===0;i.push(this.diagnosticLine(n?`EXTENSIONS`:`↳`,t,n?l:`dim`))}return i}diagnosticLine(e,t,n){return`${this.theme.bold(this.theme.fg(n,e.padEnd(11)))}${t}`}modeDiagnostics(){if(this.modes.length!==0)return this.modes.map(e=>{let t=this.theme.fg(u,e.label.toLowerCase());return e.detail?`${t}${this.theme.fg(`dim`,` (${e.detail})`)}`:t}).join(this.theme.fg(`dim`,o))}sessionDiagnostics(){if(!this.context)return;let e=0,t=0,r=0,i=0,a=0;for(let n of this.context.sessionManager.getBranch())n.type===`message`&&n.message.role===`assistant`&&(e+=n.message.usage.input,t+=n.message.usage.output,r+=n.message.usage.cacheRead,i+=n.message.usage.cacheWrite,a+=n.message.usage.cost.total);let s=e+r+i,c=s===0?0:r/s*100,l=this.context.getContextUsage(),d=l?.percent===null||l?.percent===void 0?`ctx ?`:`ctx ${l.percent.toFixed(1)}% / ${n(l.contextWindow)}`;return`${this.theme.fg(`muted`,`↑${n(e)} ↓${n(t)} R${n(r)} W${n(i)} CH${c.toFixed(1)}%`)} ${this.theme.fg(u,`$${a.toFixed(3)}`)}${this.theme.fg(`dim`,`${o}${d}`)}`}extensionDiagnostics(e){if(!this.footerData)return[];let t=[...this.footerData.getExtensionStatuses().values()],n=this.footerData.getAvailableProviderCount(),i=[...t];n>0&&i.push(`${n} provider${n===1?``:`s`}`);let a=i.map((e,t)=>this.theme.fg(f[t%f.length],e)),s=Math.max(1,e-15);return r(a,s,this.theme.fg(`dim`,o)).slice(0,2)}};export{p as LeaderHints};
2
+ //# sourceMappingURL=leaderHints.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaderHints.mjs","names":[],"sources":["../../src/tui/leaderHints.ts"],"sourcesContent":["import type { ExtensionContext, ReadonlyFooterDataProvider, Theme, ThemeColor } from '@earendil-works/pi-coding-agent';\n\nimport { type Component, visibleWidth } from '@earendil-works/pi-tui';\nimport type { LeaderSnapshot, UiMinorModeStatus } from '../services/state/uiState.ts';\nimport { alignLine, fitLine, formatTokens, packSegments, padLine } from './rendering.ts';\n\n/** The background palette is not exported by name, so take it from the method. */\ntype ThemeBg = Parameters<Theme['bg']>[0];\n\nconst WIDE_HINT_WIDTH = 100;\n/**\n * No frame rows to pay for, so the whole budget is content. Two-line option\n * cards need roughly double the grid rows a single-line list did, plus a blank\n * row of breathing space at the top and bottom.\n */\nconst MAX_WIDGET_LINES = 21;\n/** Leading pad inside the shaded block, mirrored by padLine on the right. */\nconst PANEL_INSET = ' ';\nconst MIN_RENDER_WIDTH = 4;\nconst CONTENT_INSET = 4;\nconst GRID_GAP = 2;\nconst DIAGNOSTIC_LABEL_WIDTH = 11;\nconst DIAGNOSTIC_VALUE_INSET = 15;\nconst MAX_EXTENSION_LINES = 2;\nconst STATUS_SEPARATOR = ' · ';\nconst SEGMENT_GAP = ' ';\n/** The inverse key badge is ` k `, three columns wide. */\nconst KEY_BADGE_WIDTH = 3;\n/** Puts the description under the label rather than under the badge. */\nconst DETAIL_INDENT = ' ';\nconst HORIZONTAL_BORDER = '─';\nconst CONTINUATION_MARKER = '↳';\nconst DIM_COLOR: ThemeColor = 'dim';\nconst TEXT_COLOR: ThemeColor = 'text';\nconst MUTED_COLOR: ThemeColor = 'muted';\nconst ACCENT_COLOR: ThemeColor = 'accent';\nconst WARNING_COLOR: ThemeColor = 'warning';\nconst HEADING_COLOR: ThemeColor = 'mdHeading';\nconst BORDER_COLOR: ThemeColor = 'borderAccent';\nconst MUTED_BORDER_COLOR: ThemeColor = 'borderMuted';\n/** Subtle panel shade; avoid the brighter active/selection surface used by messages. */\nconst PANEL_BACKGROUND: ThemeBg = 'toolPendingBg';\n// The mockup colors each extension status differently so adjacent values stay\n// separable at a glance; cycling this palette reproduces that without hardcoding\n// a color per status key.\nconst STATUS_COLORS: ThemeColor[] = ['mdCode', 'toolDiffAdded', 'warning', 'muted'];\n\nexport class LeaderHints implements Component {\n constructor(\n private readonly theme: Theme,\n private readonly snapshot: LeaderSnapshot,\n private readonly context?: ExtensionContext,\n private readonly footerData?: ReadonlyFooterDataProvider,\n private readonly modes: readonly UiMinorModeStatus[] = [],\n ) {}\n\n invalidate(): void {}\n\n render(width: number): string[] {\n if (width <= 0 || !this.snapshot.active) return [];\n if (width < MIN_RENDER_WIDTH) return [this.theme.fg(BORDER_COLOR, HORIZONTAL_BORDER.repeat(width))];\n\n // A filled block rather than a framed one: the shade alone separates the\n // panel from the transcript, which buys back the two rows a frame costs.\n const contentWidth = Math.max(1, width - CONTENT_INSET);\n const shade = (content: string): string =>\n this.theme.bg(PANEL_BACKGROUND, padLine(`${PANEL_INSET}${content}`, width));\n const badge = this.theme.inverse(this.theme.bold(this.theme.fg(HEADING_COLOR, ' LEADER ')));\n const breadcrumb = `${this.snapshot.prefix.join(' › ')} / ${this.snapshot.label}`;\n const header = shade(\n alignLine(\n `${badge} ${this.theme.bold(this.theme.fg(TEXT_COLOR, breadcrumb))}`,\n this.theme.fg(DIM_COLOR, 'esc cancel · backspace up'),\n contentWidth,\n ),\n );\n const rootTrail = this.snapshot.rootOptions?.map((option) => `${option.key} ${option.label}`).join(' · ');\n const trail =\n width >= WIDE_HINT_WIDTH && this.snapshot.prefix.length > 1 && rootTrail\n ? [shade(this.theme.fg(DIM_COLOR, `root ${rootTrail}`))]\n : [];\n // Options are the interactive surface, so they claim space before the\n // diagnostics do; truncating them would hide a reachable key.\n const optionBudget = Math.max(1, MAX_WIDGET_LINES - 2 - trail.length);\n const optionLines = this.renderOptionGrid(contentWidth).slice(0, optionBudget).map(shade);\n // Four rows are spoken for before diagnostics get any: the blank, header and\n // blank above, and the closing blank below. Counting only two of them let\n // this budget overrun, and the final `slice` did the real cutting silently\n // from the end, which is how the EXTENSIONS row went missing when MODES\n // arrived. Take all four off here so what is dropped is decided in one place.\n const diagnosticsBudget = Math.max(0, MAX_WIDGET_LINES - 4 - trail.length - optionLines.length);\n // Session and extension diagnostics belong to the board opened directly by\n // SPC/^SPC. Sub-boards stay focused on their own actions and navigation.\n const diagnostics =\n this.snapshot.prefix.length === 1\n ? this.renderDiagnostics(contentWidth).slice(0, diagnosticsBudget).map(shade)\n : [];\n // Blank shaded rows top and bottom: the panel butts against the editor above\n // and the modeline below, so the shade needs room before its first glyph.\n const body = [shade(''), header, shade(''), ...optionLines, ...trail, ...diagnostics];\n\n return [...body.slice(0, MAX_WIDGET_LINES - 1), shade('')];\n }\n\n /**\n * Lays options out on a fixed cell grid rather than a packed run of segments,\n * so keys and labels stay column-aligned when the list wraps.\n */\n private renderOptionGrid(width: number): string[] {\n if (this.snapshot.options.length === 0) return [];\n\n // Two lines per option, as the mockup lays them out: the key badge and a\n // bold label, then the description indented under the label.\n const cells = this.snapshot.options.map((option) => {\n const key = this.theme.inverse(this.theme.bold(this.theme.fg(ACCENT_COLOR, ` ${option.key} `)));\n return {\n heading: `${key} ${this.theme.bold(this.theme.fg(TEXT_COLOR, option.label))}`,\n headingWidth: KEY_BADGE_WIDTH + 1 + visibleWidth(option.label),\n detail: option.detail ? `${DETAIL_INDENT}${this.theme.fg(DIM_COLOR, option.detail)}` : '',\n detailWidth: option.detail ? visibleWidth(DETAIL_INDENT) + visibleWidth(option.detail) : 0,\n };\n });\n const widest = Math.max(...cells.map((cell) => Math.max(cell.headingWidth, cell.detailWidth)));\n const cellWidth = Math.min(width, widest);\n const columns = Math.max(1, Math.floor((width + GRID_GAP) / (cellWidth + GRID_GAP)));\n const gap = ' '.repeat(GRID_GAP);\n const rows: string[] = [];\n for (let index = 0; index < cells.length; index += columns) {\n const group = cells.slice(index, index + columns);\n rows.push(fitLine(group.map((cell) => padLine(cell.heading, cellWidth)).join(gap), width));\n // Keep the second row even when every detail is empty. Each option card\n // then retains the same two-line rhythm instead of collapsing terse items\n // into a denser list than items with supporting text.\n rows.push(fitLine(group.map((cell) => padLine(cell.detail, cellWidth)).join(gap), width));\n }\n return rows;\n }\n\n /** Content lines only: the caller paints the shade and pads to full width. */\n private renderDiagnostics(width: number): string[] {\n const session = this.sessionDiagnostics();\n const extensions = this.extensionDiagnostics(width);\n const modes = this.modeDiagnostics();\n if (!session && !modes && extensions.length === 0) return [];\n\n const lines = [this.theme.fg(MUTED_BORDER_COLOR, HORIZONTAL_BORDER.repeat(width))];\n // Above SESSION because it is the more consequential fact: session counters\n // describe what this session has spent, a mode describes what it will do.\n //\n // No blank rows between these three. The diagnostics budget is what is left\n // after the option grid takes its share, which at a full grid is exactly one\n // row per label: a spacer here does not cost breathing room, it costs the\n // EXTENSIONS row entirely. Distinct labels in distinct colours separate them\n // well enough without one.\n if (modes) lines.push(this.diagnosticLine('MODES', modes, WARNING_COLOR));\n if (session) lines.push(this.diagnosticLine('SESSION', session, HEADING_COLOR));\n for (const [index, extension] of extensions.entries()) {\n const isFirst = index === 0;\n lines.push(\n this.diagnosticLine(\n isFirst ? 'EXTENSIONS' : CONTINUATION_MARKER,\n extension,\n isFirst ? ACCENT_COLOR : DIM_COLOR,\n ),\n );\n }\n return lines;\n }\n\n private diagnosticLine(label: string, value: string, color: ThemeColor): string {\n return `${this.theme.bold(this.theme.fg(color, label.padEnd(DIAGNOSTIC_LABEL_WIDTH)))}${value}`;\n }\n\n /**\n * The enabled modes with their detail, which is the long form of what the\n * editor's badge row shows as bare names.\n *\n * This panel is opened deliberately and has room, so it is the right place\n * for `plan (normal - read only)`: the badge row is glanced at mid-typing and\n * only has to answer which modes are on.\n */\n private modeDiagnostics(): string | undefined {\n if (this.modes.length === 0) return undefined;\n return this.modes\n .map((mode) => {\n const label = this.theme.fg(WARNING_COLOR, mode.label.toLowerCase());\n return mode.detail ? `${label}${this.theme.fg(DIM_COLOR, ` (${mode.detail})`)}` : label;\n })\n .join(this.theme.fg(DIM_COLOR, STATUS_SEPARATOR));\n }\n\n private sessionDiagnostics(): string | undefined {\n if (!this.context) return undefined;\n\n let input = 0;\n let output = 0;\n let cacheRead = 0;\n let cacheWrite = 0;\n let cost = 0;\n for (const entry of this.context.sessionManager.getBranch()) {\n if (entry.type !== 'message' || entry.message.role !== 'assistant') continue;\n input += entry.message.usage.input;\n output += entry.message.usage.output;\n cacheRead += entry.message.usage.cacheRead;\n cacheWrite += entry.message.usage.cacheWrite;\n cost += entry.message.usage.cost.total;\n }\n\n const cachedTotal = input + cacheRead + cacheWrite;\n const cacheHit = cachedTotal === 0 ? 0 : (cacheRead / cachedTotal) * 100;\n const usage = this.context.getContextUsage();\n const context =\n usage?.percent === null || usage?.percent === undefined\n ? 'ctx ?'\n : `ctx ${usage.percent.toFixed(1)}% / ${formatTokens(usage.contextWindow)}`;\n // Built as sibling segments rather than nested theme calls so each color\n // closes cleanly instead of resetting the one around it.\n const metrics = this.theme.fg(\n MUTED_COLOR,\n `↑${formatTokens(input)} ↓${formatTokens(output)} R${formatTokens(cacheRead)} W${formatTokens(\n cacheWrite,\n )} CH${cacheHit.toFixed(1)}%`,\n );\n const costText = this.theme.fg(WARNING_COLOR, `$${cost.toFixed(3)}`);\n return `${metrics}${SEGMENT_GAP}${costText}${this.theme.fg(DIM_COLOR, `${STATUS_SEPARATOR}${context}`)}`;\n }\n\n private extensionDiagnostics(width: number): string[] {\n if (!this.footerData) return [];\n\n const statuses = [...this.footerData.getExtensionStatuses().values()];\n const providers = this.footerData.getAvailableProviderCount();\n const values = [...statuses];\n if (providers > 0) values.push(`${providers} provider${providers === 1 ? '' : 's'}`);\n const colored = values.map((value, index) =>\n this.theme.fg(STATUS_COLORS[index % STATUS_COLORS.length] as ThemeColor, value),\n );\n const valueWidth = Math.max(1, width - DIAGNOSTIC_VALUE_INSET);\n return packSegments(colored, valueWidth, this.theme.fg(DIM_COLOR, STATUS_SEPARATOR)).slice(0, MAX_EXTENSION_LINES);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ let e=require("@earendil-works/pi-tui");const t=1e3,n=1e6;function r(e){return e<t?String(e):e<n?`${(e/t).toFixed(+(e<1e4))}k`:`${(e/n).toFixed(1)}M`}function i(t,n){return(0,e.truncateToWidth)(t,Math.max(0,n),``)}function a(t,n){let r=Math.max(0,n),a=i(t,r);return`${a}${` `.repeat(Math.max(0,r-(0,e.visibleWidth)(a)))}`}function o(t,n,r=`…`){let i=Math.max(0,n),a=(0,e.truncateToWidth)(t,i,r).replaceAll(`\x1B[0m`,``);return`${a}${` `.repeat(Math.max(0,i-(0,e.visibleWidth)(a)))}`}function s(t,n,r){let a=Math.max(0,r),o=(0,e.visibleWidth)(n);if(!n||o>=a)return i(n||t,a);let s=i(t,Math.max(0,a-o-1));return i(`${s}${` `.repeat(Math.max(1,a-(0,e.visibleWidth)(s)-o))}${n}`,a)}function c(t,n,r=`│`,o=`│`){let s=Math.max(0,n),c=(0,e.visibleWidth)(r)+(0,e.visibleWidth)(o);return s<=c?i(`${r}${o}`,s):`${r}${a(t,s-c)}${o}`}function l(t,n,r=` `){let a=Math.max(0,n);if(a===0)return[];let o=[],s=``;for(let n of t){let t=s?`${s}${r}${n}`:n;if((0,e.visibleWidth)(t)<=a){s=t;continue}s&&o.push(i(s,a)),s=i(n,a)}return s&&o.push(s),o}exports.alignLine=s,exports.fitLine=i,exports.fitStyledLine=o,exports.formatTokens=r,exports.frameLine=c,exports.packSegments=l,exports.padLine=a;
2
+ //# sourceMappingURL=rendering.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendering.cjs","names":["truncateToWidth","visibleWidth"],"sources":["../../src/tui/rendering.ts"],"sourcesContent":["import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nconst HARD_RESET = '\\x1b[0m';\nconst DEFAULT_ELLIPSIS = '…';\nconst THOUSAND = 1000;\nconst MILLION = 1_000_000;\n/** Below this many tokens the \"k\" form keeps a decimal, above it rounds. */\nconst K_DECIMAL_LIMIT = 10_000;\n\n/** Compact token count, e.g. `812`, `4.2k`, `186k`, `1.3M`. */\nexport function formatTokens(value: number): string {\n if (value < THOUSAND) return String(value);\n if (value < MILLION) return `${(value / THOUSAND).toFixed(value < K_DECIMAL_LIMIT ? 1 : 0)}k`;\n return `${(value / MILLION).toFixed(1)}M`;\n}\n\nexport function fitLine(text: string, width: number): string {\n return truncateToWidth(text, Math.max(0, width), '');\n}\n\nexport function padLine(text: string, width: number): string {\n const safeWidth = Math.max(0, width);\n const fitted = fitLine(text, safeWidth);\n return `${fitted}${' '.repeat(Math.max(0, safeWidth - visibleWidth(fitted)))}`;\n}\n\n/**\n * Truncate and pad a themed row without retaining pi-tui's injected hard reset.\n * The reset would cancel an outer background before this function's padding.\n */\nexport function fitStyledLine(text: string, width: number, ellipsis: string = DEFAULT_ELLIPSIS): string {\n const safeWidth = Math.max(0, width);\n const clipped = truncateToWidth(text, safeWidth, ellipsis).replaceAll(HARD_RESET, '');\n return `${clipped}${' '.repeat(Math.max(0, safeWidth - visibleWidth(clipped)))}`;\n}\n\nexport function alignLine(left: string, right: string, width: number): string {\n const safeWidth = Math.max(0, width);\n const rightWidth = visibleWidth(right);\n if (!right || rightWidth >= safeWidth) return fitLine(right || left, safeWidth);\n\n const maxLeftWidth = Math.max(0, safeWidth - rightWidth - 1);\n const fittedLeft = fitLine(left, maxLeftWidth);\n const gap = ' '.repeat(Math.max(1, safeWidth - visibleWidth(fittedLeft) - rightWidth));\n return fitLine(`${fittedLeft}${gap}${right}`, safeWidth);\n}\n\nexport function frameLine(content: string, width: number, left = '│', right = '│'): string {\n const safeWidth = Math.max(0, width);\n const frameWidth = visibleWidth(left) + visibleWidth(right);\n if (safeWidth <= frameWidth) return fitLine(`${left}${right}`, safeWidth);\n return `${left}${padLine(content, safeWidth - frameWidth)}${right}`;\n}\n\nexport function packSegments(segments: string[], width: number, separator = ' '): string[] {\n const safeWidth = Math.max(0, width);\n if (safeWidth === 0) return [];\n\n const lines: string[] = [];\n let current = '';\n for (const segment of segments) {\n const candidate = current ? `${current}${separator}${segment}` : segment;\n if (visibleWidth(candidate) <= safeWidth) {\n current = candidate;\n continue;\n }\n if (current) lines.push(fitLine(current, safeWidth));\n current = fitLine(segment, safeWidth);\n }\n if (current) lines.push(current);\n return lines;\n}\n"],"mappings":"wCAEA,MAEM,EAAW,IACX,EAAU,IAKhB,SAAgB,EAAa,EAAuB,CAGlD,OAFI,EAAQ,EAAiB,OAAO,CAAK,EACrC,EAAQ,EAAgB,IAAI,EAAQ,EAAA,CAAU,QAAQ,IAAQ,IAAuB,EAAE,GACpF,IAAI,EAAQ,EAAA,CAAS,QAAQ,CAAC,EAAE,EACzC,CAEA,SAAgB,EAAQ,EAAc,EAAuB,CAC3D,OAAA,EAAOA,EAAAA,gBAAAA,CAAgB,EAAM,KAAK,IAAI,EAAG,CAAK,EAAG,EAAE,CACrD,CAEA,SAAgB,EAAQ,EAAc,EAAuB,CAC3D,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,EAAS,EAAQ,EAAM,CAAS,EACtC,MAAO,GAAG,IAAS,IAAI,OAAO,KAAK,IAAI,EAAG,GAAA,EAAYC,EAAAA,aAAAA,CAAa,CAAM,CAAC,CAAC,GAC7E,CAMA,SAAgB,EAAc,EAAc,EAAe,EAAmB,IAA0B,CACtG,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,GAAA,EAAUD,EAAAA,gBAAAA,CAAgB,EAAM,EAAW,CAAQ,CAAC,CAAC,WAAW,UAAY,EAAE,EACpF,MAAO,GAAG,IAAU,IAAI,OAAO,KAAK,IAAI,EAAG,GAAA,EAAYC,EAAAA,aAAAA,CAAa,CAAO,CAAC,CAAC,GAC/E,CAEA,SAAgB,EAAU,EAAc,EAAe,EAAuB,CAC5E,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,GAAA,EAAaA,EAAAA,aAAAA,CAAa,CAAK,EACrC,GAAI,CAAC,GAAS,GAAc,EAAW,OAAO,EAAQ,GAAS,EAAM,CAAS,EAG9E,IAAM,EAAa,EAAQ,EADN,KAAK,IAAI,EAAG,EAAY,EAAa,CACd,CAAC,EAE7C,OAAO,EAAQ,GAAG,IADN,IAAI,OAAO,KAAK,IAAI,EAAG,GAAA,EAAYA,EAAAA,aAAAA,CAAa,CAAU,EAAI,CAAU,CACnD,IAAI,IAAS,CAAS,CACzD,CAEA,SAAgB,EAAU,EAAiB,EAAe,EAAO,IAAK,EAAQ,IAAa,CACzF,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,GAAA,EAAaA,EAAAA,aAAAA,CAAa,CAAI,GAAA,EAAIA,EAAAA,aAAAA,CAAa,CAAK,EAE1D,OADI,GAAa,EAAmB,EAAQ,GAAG,IAAO,IAAS,CAAS,EACjE,GAAG,IAAO,EAAQ,EAAS,EAAY,CAAU,IAAI,GAC9D,CAEA,SAAgB,EAAa,EAAoB,EAAe,EAAY,KAAgB,CAC1F,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EACnC,GAAI,IAAc,EAAG,MAAO,CAAC,EAE7B,IAAM,EAAkB,CAAC,EACrB,EAAU,GACd,IAAK,IAAM,KAAW,EAAU,CAC9B,IAAM,EAAY,EAAU,GAAG,IAAU,IAAY,IAAY,EACjE,IAAA,EAAIA,EAAAA,aAAAA,CAAa,CAAS,GAAK,EAAW,CACxC,EAAU,EACV,QACF,CACI,GAAS,EAAM,KAAK,EAAQ,EAAS,CAAS,CAAC,EACnD,EAAU,EAAQ,EAAS,CAAS,CACtC,CAEA,OADI,GAAS,EAAM,KAAK,CAAO,EACxB,CACT"}
@@ -0,0 +1,16 @@
1
+ //#region src/tui/rendering.d.ts
2
+ /** Compact token count, e.g. `812`, `4.2k`, `186k`, `1.3M`. */
3
+ declare function formatTokens(value: number): string;
4
+ declare function fitLine(text: string, width: number): string;
5
+ declare function padLine(text: string, width: number): string;
6
+ /**
7
+ * Truncate and pad a themed row without retaining pi-tui's injected hard reset.
8
+ * The reset would cancel an outer background before this function's padding.
9
+ */
10
+ declare function fitStyledLine(text: string, width: number, ellipsis?: string): string;
11
+ declare function alignLine(left: string, right: string, width: number): string;
12
+ declare function frameLine(content: string, width: number, left?: string, right?: string): string;
13
+ declare function packSegments(segments: string[], width: number, separator?: string): string[];
14
+ //#endregion
15
+ export { alignLine, fitLine, fitStyledLine, formatTokens, frameLine, packSegments, padLine };
16
+ //# sourceMappingURL=rendering.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendering.d.cts","names":[],"sources":["../../src/tui/rendering.ts"],"mappings":";;iBAUgB,aAAa;iBAMb,QAAQ,cAAc;iBAItB,QAAQ,cAAc;;;;;iBAUtB,cAAc,cAAc,eAAe;iBAM3C,UAAU,cAAc,eAAe;iBAWvC,UAAU,iBAAiB,eAAe,eAAY;iBAOtD,aAAa,oBAAoB,eAAe"}
@@ -0,0 +1,16 @@
1
+ //#region src/tui/rendering.d.ts
2
+ /** Compact token count, e.g. `812`, `4.2k`, `186k`, `1.3M`. */
3
+ declare function formatTokens(value: number): string;
4
+ declare function fitLine(text: string, width: number): string;
5
+ declare function padLine(text: string, width: number): string;
6
+ /**
7
+ * Truncate and pad a themed row without retaining pi-tui's injected hard reset.
8
+ * The reset would cancel an outer background before this function's padding.
9
+ */
10
+ declare function fitStyledLine(text: string, width: number, ellipsis?: string): string;
11
+ declare function alignLine(left: string, right: string, width: number): string;
12
+ declare function frameLine(content: string, width: number, left?: string, right?: string): string;
13
+ declare function packSegments(segments: string[], width: number, separator?: string): string[];
14
+ //#endregion
15
+ export { alignLine, fitLine, fitStyledLine, formatTokens, frameLine, packSegments, padLine };
16
+ //# sourceMappingURL=rendering.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendering.d.mts","names":[],"sources":["../../src/tui/rendering.ts"],"mappings":";;iBAUgB,aAAa;iBAMb,QAAQ,cAAc;iBAItB,QAAQ,cAAc;;;;;iBAUtB,cAAc,cAAc,eAAe;iBAM3C,UAAU,cAAc,eAAe;iBAWvC,UAAU,iBAAiB,eAAe,eAAY;iBAOtD,aAAa,oBAAoB,eAAe"}
@@ -0,0 +1,2 @@
1
+ import{truncateToWidth as e,visibleWidth as t}from"@earendil-works/pi-tui";const n=1e3,r=1e6;function i(e){return e<n?String(e):e<r?`${(e/n).toFixed(+(e<1e4))}k`:`${(e/r).toFixed(1)}M`}function a(t,n){return e(t,Math.max(0,n),``)}function o(e,n){let r=Math.max(0,n),i=a(e,r);return`${i}${` `.repeat(Math.max(0,r-t(i)))}`}function s(n,r,i=`…`){let a=Math.max(0,r),o=e(n,a,i).replaceAll(`\x1B[0m`,``);return`${o}${` `.repeat(Math.max(0,a-t(o)))}`}function c(e,n,r){let i=Math.max(0,r),o=t(n);if(!n||o>=i)return a(n||e,i);let s=a(e,Math.max(0,i-o-1));return a(`${s}${` `.repeat(Math.max(1,i-t(s)-o))}${n}`,i)}function l(e,n,r=`│`,i=`│`){let s=Math.max(0,n),c=t(r)+t(i);return s<=c?a(`${r}${i}`,s):`${r}${o(e,s-c)}${i}`}function u(e,n,r=` `){let i=Math.max(0,n);if(i===0)return[];let o=[],s=``;for(let n of e){let e=s?`${s}${r}${n}`:n;if(t(e)<=i){s=e;continue}s&&o.push(a(s,i)),s=a(n,i)}return s&&o.push(s),o}export{c as alignLine,a as fitLine,s as fitStyledLine,i as formatTokens,l as frameLine,u as packSegments,o as padLine};
2
+ //# sourceMappingURL=rendering.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendering.mjs","names":[],"sources":["../../src/tui/rendering.ts"],"sourcesContent":["import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nconst HARD_RESET = '\\x1b[0m';\nconst DEFAULT_ELLIPSIS = '…';\nconst THOUSAND = 1000;\nconst MILLION = 1_000_000;\n/** Below this many tokens the \"k\" form keeps a decimal, above it rounds. */\nconst K_DECIMAL_LIMIT = 10_000;\n\n/** Compact token count, e.g. `812`, `4.2k`, `186k`, `1.3M`. */\nexport function formatTokens(value: number): string {\n if (value < THOUSAND) return String(value);\n if (value < MILLION) return `${(value / THOUSAND).toFixed(value < K_DECIMAL_LIMIT ? 1 : 0)}k`;\n return `${(value / MILLION).toFixed(1)}M`;\n}\n\nexport function fitLine(text: string, width: number): string {\n return truncateToWidth(text, Math.max(0, width), '');\n}\n\nexport function padLine(text: string, width: number): string {\n const safeWidth = Math.max(0, width);\n const fitted = fitLine(text, safeWidth);\n return `${fitted}${' '.repeat(Math.max(0, safeWidth - visibleWidth(fitted)))}`;\n}\n\n/**\n * Truncate and pad a themed row without retaining pi-tui's injected hard reset.\n * The reset would cancel an outer background before this function's padding.\n */\nexport function fitStyledLine(text: string, width: number, ellipsis: string = DEFAULT_ELLIPSIS): string {\n const safeWidth = Math.max(0, width);\n const clipped = truncateToWidth(text, safeWidth, ellipsis).replaceAll(HARD_RESET, '');\n return `${clipped}${' '.repeat(Math.max(0, safeWidth - visibleWidth(clipped)))}`;\n}\n\nexport function alignLine(left: string, right: string, width: number): string {\n const safeWidth = Math.max(0, width);\n const rightWidth = visibleWidth(right);\n if (!right || rightWidth >= safeWidth) return fitLine(right || left, safeWidth);\n\n const maxLeftWidth = Math.max(0, safeWidth - rightWidth - 1);\n const fittedLeft = fitLine(left, maxLeftWidth);\n const gap = ' '.repeat(Math.max(1, safeWidth - visibleWidth(fittedLeft) - rightWidth));\n return fitLine(`${fittedLeft}${gap}${right}`, safeWidth);\n}\n\nexport function frameLine(content: string, width: number, left = '│', right = '│'): string {\n const safeWidth = Math.max(0, width);\n const frameWidth = visibleWidth(left) + visibleWidth(right);\n if (safeWidth <= frameWidth) return fitLine(`${left}${right}`, safeWidth);\n return `${left}${padLine(content, safeWidth - frameWidth)}${right}`;\n}\n\nexport function packSegments(segments: string[], width: number, separator = ' '): string[] {\n const safeWidth = Math.max(0, width);\n if (safeWidth === 0) return [];\n\n const lines: string[] = [];\n let current = '';\n for (const segment of segments) {\n const candidate = current ? `${current}${separator}${segment}` : segment;\n if (visibleWidth(candidate) <= safeWidth) {\n current = candidate;\n continue;\n }\n if (current) lines.push(fitLine(current, safeWidth));\n current = fitLine(segment, safeWidth);\n }\n if (current) lines.push(current);\n return lines;\n}\n"],"mappings":"2EAEA,MAEM,EAAW,IACX,EAAU,IAKhB,SAAgB,EAAa,EAAuB,CAGlD,OAFI,EAAQ,EAAiB,OAAO,CAAK,EACrC,EAAQ,EAAgB,IAAI,EAAQ,EAAA,CAAU,QAAQ,IAAQ,IAAuB,EAAE,GACpF,IAAI,EAAQ,EAAA,CAAS,QAAQ,CAAC,EAAE,EACzC,CAEA,SAAgB,EAAQ,EAAc,EAAuB,CAC3D,OAAO,EAAgB,EAAM,KAAK,IAAI,EAAG,CAAK,EAAG,EAAE,CACrD,CAEA,SAAgB,EAAQ,EAAc,EAAuB,CAC3D,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,EAAS,EAAQ,EAAM,CAAS,EACtC,MAAO,GAAG,IAAS,IAAI,OAAO,KAAK,IAAI,EAAG,EAAY,EAAa,CAAM,CAAC,CAAC,GAC7E,CAMA,SAAgB,EAAc,EAAc,EAAe,EAAmB,IAA0B,CACtG,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,EAAU,EAAgB,EAAM,EAAW,CAAQ,CAAC,CAAC,WAAW,UAAY,EAAE,EACpF,MAAO,GAAG,IAAU,IAAI,OAAO,KAAK,IAAI,EAAG,EAAY,EAAa,CAAO,CAAC,CAAC,GAC/E,CAEA,SAAgB,EAAU,EAAc,EAAe,EAAuB,CAC5E,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,EAAa,EAAa,CAAK,EACrC,GAAI,CAAC,GAAS,GAAc,EAAW,OAAO,EAAQ,GAAS,EAAM,CAAS,EAG9E,IAAM,EAAa,EAAQ,EADN,KAAK,IAAI,EAAG,EAAY,EAAa,CACd,CAAC,EAE7C,OAAO,EAAQ,GAAG,IADN,IAAI,OAAO,KAAK,IAAI,EAAG,EAAY,EAAa,CAAU,EAAI,CAAU,CACnD,IAAI,IAAS,CAAS,CACzD,CAEA,SAAgB,EAAU,EAAiB,EAAe,EAAO,IAAK,EAAQ,IAAa,CACzF,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,EAAa,EAAa,CAAI,EAAI,EAAa,CAAK,EAE1D,OADI,GAAa,EAAmB,EAAQ,GAAG,IAAO,IAAS,CAAS,EACjE,GAAG,IAAO,EAAQ,EAAS,EAAY,CAAU,IAAI,GAC9D,CAEA,SAAgB,EAAa,EAAoB,EAAe,EAAY,KAAgB,CAC1F,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EACnC,GAAI,IAAc,EAAG,MAAO,CAAC,EAE7B,IAAM,EAAkB,CAAC,EACrB,EAAU,GACd,IAAK,IAAM,KAAW,EAAU,CAC9B,IAAM,EAAY,EAAU,GAAG,IAAU,IAAY,IAAY,EACjE,GAAI,EAAa,CAAS,GAAK,EAAW,CACxC,EAAU,EACV,QACF,CACI,GAAS,EAAM,KAAK,EAAQ,EAAS,CAAS,CAAC,EACnD,EAAU,EAAQ,EAAS,CAAS,CACtC,CAEA,OADI,GAAS,EAAM,KAAK,CAAO,EACxB,CACT"}
@@ -0,0 +1,2 @@
1
+ const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("node:fs");n=e.__toESM(n,1);const r=`doom-pi-dark`,i=[`accent`,`mdCode`,`toolDiffAdded`,`warning`,`mdHeading`,`syntaxNumber`];function a(e){let t=2166136261;for(let n of e)t^=n.charCodeAt(0),t=Math.imul(t,16777619);return(t>>>0)%i.length}function o(e){return i[a(e)]}function s(e,t){let n=a(e);for(let e=0;e<i.length;e++){let r=i[(n+e)%i.length];if(!t.has(r))return r}return i[n]}const c={$schema:`https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json`,name:r,vars:{bg:`#282c34`,bgAlt:`#21242b`,bgActive:`#3e4451`,bgHover:`#3e4451`,fg:`#bbc2cf`,fgAlt:`#9ca0a4`,grey:`#3f444a`,greyAlt:`#5b6268`,blue:`#51afef`,darkBlue:`#2257a0`,cyan:`#46d9ff`,green:`#98be65`,lightGreen:`#a8cc8a`,teal:`#4db5bd`,orange:`#da8548`,red:`#ff6c6b`,violet:`#a9a1e1`,magenta:`#c678dd`,yellow:`#ecbe7b`,lightYellow:`#f0c674`,comment:`#5b6268`,diffAddedBg:`#1b2315`,diffRemovedBg:`#2b1c20`},colors:{accent:`blue`,border:`greyAlt`,borderAccent:`blue`,borderMuted:`grey`,success:`green`,error:`red`,warning:`yellow`,muted:`fgAlt`,dim:`comment`,text:`fg`,thinkingText:`fgAlt`,selectedBg:`bgHover`,userMessageBg:`bgActive`,userMessageText:`fg`,customMessageBg:`#2d253f`,customMessageText:`fg`,customMessageLabel:`violet`,toolPendingBg:`bgAlt`,toolSuccessBg:`diffAddedBg`,toolErrorBg:`diffRemovedBg`,toolTitle:`blue`,toolOutput:`fg`,mdHeading:`magenta`,mdLink:`blue`,mdLinkUrl:`comment`,mdCode:`cyan`,mdCodeBlock:`lightYellow`,mdCodeBlockBorder:`greyAlt`,mdQuote:`fgAlt`,mdQuoteBorder:`greyAlt`,mdHr:`greyAlt`,mdListBullet:`blue`,toolDiffAdded:`lightGreen`,toolDiffRemoved:`red`,toolDiffContext:`fgAlt`,syntaxComment:`comment`,syntaxKeyword:`blue`,syntaxFunction:`magenta`,syntaxVariable:`violet`,syntaxString:`green`,syntaxNumber:`orange`,syntaxType:`yellow`,syntaxOperator:`orange`,syntaxPunctuation:`fgAlt`,thinkingOff:`grey`,thinkingMinimal:`fgAlt`,thinkingLow:`darkBlue`,thinkingMedium:`blue`,thinkingHigh:`teal`,thinkingXhigh:`magenta`,thinkingMax:`red`,bashMode:`lightGreen`},export:{pageBg:`bg`,cardBg:`bgAlt`,infoBg:`#343225`}};async function l(e){let i=t.default.join(e,`${r}.json`);return await n.default.promises.writeFile(i,`${JSON.stringify(c,null,2)}\n`,{mode:384}),i}exports.DEFAULT_THEME=c,exports.DEFAULT_THEME_NAME=r,exports.agentIdentityColor=o,exports.allocateAgentIdentityColor=s,exports.writeDefaultTheme=l;
2
+ //# sourceMappingURL=theme.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.cjs","names":["path","fs"],"sources":["../../src/tui/theme.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { FooterTextColor } from '@agimon-ai/doompi-extension-contracts/footer';\nimport type { ThemeColor } from '@earendil-works/pi-coding-agent';\n\nexport const DEFAULT_THEME_NAME = 'doom-pi-dark';\n\nconst AGENT_IDENTITY_COLORS = [\n 'accent',\n 'mdCode',\n 'toolDiffAdded',\n 'warning',\n 'mdHeading',\n 'syntaxNumber',\n] as const satisfies readonly (FooterTextColor & ThemeColor)[];\n\nfunction agentIdentityColorIndex(identity: string): number {\n let hash = 2_166_136_261;\n for (const character of identity) {\n hash ^= character.charCodeAt(0);\n hash = Math.imul(hash, 16_777_619);\n }\n return (hash >>> 0) % AGENT_IDENTITY_COLORS.length;\n}\n\n/** Stable semantic color shared by every UI surface that identifies an agent run. */\nexport function agentIdentityColor(identity: string): FooterTextColor {\n return AGENT_IDENTITY_COLORS[agentIdentityColorIndex(identity)]!;\n}\n\n/**\n * Prefer the identity's stable color, then walk the palette to avoid reusing a\n * color already visible on the same surface. Once every color is occupied,\n * reuse the stable preference rather than changing existing assignments.\n */\nexport function allocateAgentIdentityColor(identity: string, occupied: ReadonlySet<FooterTextColor>): FooterTextColor {\n const preferredIndex = agentIdentityColorIndex(identity);\n for (let offset = 0; offset < AGENT_IDENTITY_COLORS.length; offset++) {\n const color = AGENT_IDENTITY_COLORS[(preferredIndex + offset) % AGENT_IDENTITY_COLORS.length]!;\n if (!occupied.has(color)) return color;\n }\n return AGENT_IDENTITY_COLORS[preferredIndex]!;\n}\n\nexport const DEFAULT_THEME = {\n $schema:\n 'https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json',\n name: DEFAULT_THEME_NAME,\n // Doom One's graphite base and saturated syntax ladder. Variable names are\n // semantic to this package so every TUI surface can share the palette.\n vars: {\n bg: '#282c34',\n bgAlt: '#21242b',\n bgActive: '#3e4451',\n bgHover: '#3e4451',\n fg: '#bbc2cf',\n fgAlt: '#9ca0a4',\n grey: '#3f444a',\n greyAlt: '#5b6268',\n blue: '#51afef',\n darkBlue: '#2257a0',\n cyan: '#46d9ff',\n green: '#98be65',\n lightGreen: '#a8cc8a',\n teal: '#4db5bd',\n orange: '#da8548',\n red: '#ff6c6b',\n violet: '#a9a1e1',\n magenta: '#c678dd',\n yellow: '#ecbe7b',\n lightYellow: '#f0c674',\n comment: '#5b6268',\n // Diff bands sink below `bg` and carry their signal in hue, not lightness,\n // so a banded row reads as a recessed well and syntax highlighting keeps at\n // least the contrast it has off the band. Blending Doom One's pale green and\n // red toward `bg` the way Magit does lightens instead, and `comment` goes.\n diffAddedBg: '#1b2315',\n diffRemovedBg: '#2b1c20',\n },\n colors: {\n accent: 'blue',\n border: 'greyAlt',\n borderAccent: 'blue',\n borderMuted: 'grey',\n success: 'green',\n error: 'red',\n warning: 'yellow',\n muted: 'fgAlt',\n dim: 'comment',\n text: 'fg',\n thinkingText: 'fgAlt',\n\n selectedBg: 'bgHover',\n userMessageBg: 'bgActive',\n userMessageText: 'fg',\n customMessageBg: '#2d253f',\n customMessageText: 'fg',\n customMessageLabel: 'violet',\n toolPendingBg: 'bgAlt',\n // Doubles as the added/removed band behind a diff row, so it stays a wash\n // rather than a fill: syntax highlighting has to remain legible on top.\n toolSuccessBg: 'diffAddedBg',\n toolErrorBg: 'diffRemovedBg',\n toolTitle: 'blue',\n toolOutput: 'fg',\n\n mdHeading: 'magenta',\n mdLink: 'blue',\n mdLinkUrl: 'comment',\n mdCode: 'cyan',\n mdCodeBlock: 'lightYellow',\n mdCodeBlockBorder: 'greyAlt',\n mdQuote: 'fgAlt',\n mdQuoteBorder: 'greyAlt',\n mdHr: 'greyAlt',\n mdListBullet: 'blue',\n\n toolDiffAdded: 'lightGreen',\n toolDiffRemoved: 'red',\n toolDiffContext: 'fgAlt',\n\n syntaxComment: 'comment',\n syntaxKeyword: 'blue',\n syntaxFunction: 'magenta',\n syntaxVariable: 'violet',\n syntaxString: 'green',\n syntaxNumber: 'orange',\n syntaxType: 'yellow',\n syntaxOperator: 'orange',\n syntaxPunctuation: 'fgAlt',\n\n // A rising ramp from quiet graphite through the Doom One accent ladder.\n thinkingOff: 'grey',\n thinkingMinimal: 'fgAlt',\n thinkingLow: 'darkBlue',\n thinkingMedium: 'blue',\n thinkingHigh: 'teal',\n thinkingXhigh: 'magenta',\n thinkingMax: 'red',\n\n bashMode: 'lightGreen',\n },\n export: {\n pageBg: 'bg',\n cardBg: 'bgAlt',\n infoBg: '#343225',\n },\n} as const;\n\nexport async function writeDefaultTheme(temporaryDirectory: string): Promise<string> {\n const themePath = path.join(temporaryDirectory, `${DEFAULT_THEME_NAME}.json`);\n await fs.promises.writeFile(themePath, `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`, { mode: 0o600 });\n return themePath;\n}\n"],"mappings":"2IAKA,MAAa,EAAqB,eAE5B,EAAwB,CAC5B,SACA,SACA,gBACA,UACA,YACA,cACF,EAEA,SAAS,EAAwB,EAA0B,CACzD,IAAI,EAAO,WACX,IAAK,IAAM,KAAa,EACtB,GAAQ,EAAU,WAAW,CAAC,EAC9B,EAAO,KAAK,KAAK,EAAM,QAAU,EAEnC,OAAQ,IAAS,GAAK,EAAsB,MAC9C,CAGA,SAAgB,EAAmB,EAAmC,CACpE,OAAO,EAAsB,EAAwB,CAAQ,EAC/D,CAOA,SAAgB,EAA2B,EAAkB,EAAyD,CACpH,IAAM,EAAiB,EAAwB,CAAQ,EACvD,IAAK,IAAI,EAAS,EAAG,EAAS,EAAsB,OAAQ,IAAU,CACpE,IAAM,EAAQ,GAAuB,EAAiB,GAAU,EAAsB,QACtF,GAAI,CAAC,EAAS,IAAI,CAAK,EAAG,OAAO,CACnC,CACA,OAAO,EAAsB,EAC/B,CAEA,MAAa,EAAgB,CAC3B,QACE,+HACF,KAAM,EAGN,KAAM,CACJ,GAAI,UACJ,MAAO,UACP,SAAU,UACV,QAAS,UACT,GAAI,UACJ,MAAO,UACP,KAAM,UACN,QAAS,UACT,KAAM,UACN,SAAU,UACV,KAAM,UACN,MAAO,UACP,WAAY,UACZ,KAAM,UACN,OAAQ,UACR,IAAK,UACL,OAAQ,UACR,QAAS,UACT,OAAQ,UACR,YAAa,UACb,QAAS,UAKT,YAAa,UACb,cAAe,SACjB,EACA,OAAQ,CACN,OAAQ,OACR,OAAQ,UACR,aAAc,OACd,YAAa,OACb,QAAS,QACT,MAAO,MACP,QAAS,SACT,MAAO,QACP,IAAK,UACL,KAAM,KACN,aAAc,QAEd,WAAY,UACZ,cAAe,WACf,gBAAiB,KACjB,gBAAiB,UACjB,kBAAmB,KACnB,mBAAoB,SACpB,cAAe,QAGf,cAAe,cACf,YAAa,gBACb,UAAW,OACX,WAAY,KAEZ,UAAW,UACX,OAAQ,OACR,UAAW,UACX,OAAQ,OACR,YAAa,cACb,kBAAmB,UACnB,QAAS,QACT,cAAe,UACf,KAAM,UACN,aAAc,OAEd,cAAe,aACf,gBAAiB,MACjB,gBAAiB,QAEjB,cAAe,UACf,cAAe,OACf,eAAgB,UAChB,eAAgB,SAChB,aAAc,QACd,aAAc,SACd,WAAY,SACZ,eAAgB,SAChB,kBAAmB,QAGnB,YAAa,OACb,gBAAiB,QACjB,YAAa,WACb,eAAgB,OAChB,aAAc,OACd,cAAe,UACf,YAAa,MAEb,SAAU,YACZ,EACA,OAAQ,CACN,OAAQ,KACR,OAAQ,QACR,OAAQ,SACV,CACF,EAEA,eAAsB,EAAkB,EAA6C,CACnF,IAAM,EAAYA,EAAAA,QAAK,KAAK,EAAoB,GAAG,EAAmB,MAAM,EAE5E,OADA,MAAMC,EAAAA,QAAG,SAAS,UAAU,EAAW,GAAG,KAAK,UAAU,EAAe,KAAM,CAAC,EAAE,IAAK,CAAE,KAAM,GAAM,CAAC,EAC9F,CACT"}
@@ -0,0 +1,103 @@
1
+ import { FooterTextColor } from "@agimon-ai/doompi-extension-contracts/footer";
2
+ //#region src/tui/theme.d.ts
3
+ declare const DEFAULT_THEME_NAME = "doom-pi-dark";
4
+ /** Stable semantic color shared by every UI surface that identifies an agent run. */
5
+ declare function agentIdentityColor(identity: string): FooterTextColor;
6
+ /**
7
+ * Prefer the identity's stable color, then walk the palette to avoid reusing a
8
+ * color already visible on the same surface. Once every color is occupied,
9
+ * reuse the stable preference rather than changing existing assignments.
10
+ */
11
+ declare function allocateAgentIdentityColor(identity: string, occupied: ReadonlySet<FooterTextColor>): FooterTextColor;
12
+ declare const DEFAULT_THEME: {
13
+ readonly $schema: "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json";
14
+ readonly name: "doom-pi-dark";
15
+ readonly vars: {
16
+ readonly bg: "#282c34";
17
+ readonly bgAlt: "#21242b";
18
+ readonly bgActive: "#3e4451";
19
+ readonly bgHover: "#3e4451";
20
+ readonly fg: "#bbc2cf";
21
+ readonly fgAlt: "#9ca0a4";
22
+ readonly grey: "#3f444a";
23
+ readonly greyAlt: "#5b6268";
24
+ readonly blue: "#51afef";
25
+ readonly darkBlue: "#2257a0";
26
+ readonly cyan: "#46d9ff";
27
+ readonly green: "#98be65";
28
+ readonly lightGreen: "#a8cc8a";
29
+ readonly teal: "#4db5bd";
30
+ readonly orange: "#da8548";
31
+ readonly red: "#ff6c6b";
32
+ readonly violet: "#a9a1e1";
33
+ readonly magenta: "#c678dd";
34
+ readonly yellow: "#ecbe7b";
35
+ readonly lightYellow: "#f0c674";
36
+ readonly comment: "#5b6268";
37
+ readonly diffAddedBg: "#1b2315";
38
+ readonly diffRemovedBg: "#2b1c20";
39
+ };
40
+ readonly colors: {
41
+ readonly accent: "blue";
42
+ readonly border: "greyAlt";
43
+ readonly borderAccent: "blue";
44
+ readonly borderMuted: "grey";
45
+ readonly success: "green";
46
+ readonly error: "red";
47
+ readonly warning: "yellow";
48
+ readonly muted: "fgAlt";
49
+ readonly dim: "comment";
50
+ readonly text: "fg";
51
+ readonly thinkingText: "fgAlt";
52
+ readonly selectedBg: "bgHover";
53
+ readonly userMessageBg: "bgActive";
54
+ readonly userMessageText: "fg";
55
+ readonly customMessageBg: "#2d253f";
56
+ readonly customMessageText: "fg";
57
+ readonly customMessageLabel: "violet";
58
+ readonly toolPendingBg: "bgAlt";
59
+ readonly toolSuccessBg: "diffAddedBg";
60
+ readonly toolErrorBg: "diffRemovedBg";
61
+ readonly toolTitle: "blue";
62
+ readonly toolOutput: "fg";
63
+ readonly mdHeading: "magenta";
64
+ readonly mdLink: "blue";
65
+ readonly mdLinkUrl: "comment";
66
+ readonly mdCode: "cyan";
67
+ readonly mdCodeBlock: "lightYellow";
68
+ readonly mdCodeBlockBorder: "greyAlt";
69
+ readonly mdQuote: "fgAlt";
70
+ readonly mdQuoteBorder: "greyAlt";
71
+ readonly mdHr: "greyAlt";
72
+ readonly mdListBullet: "blue";
73
+ readonly toolDiffAdded: "lightGreen";
74
+ readonly toolDiffRemoved: "red";
75
+ readonly toolDiffContext: "fgAlt";
76
+ readonly syntaxComment: "comment";
77
+ readonly syntaxKeyword: "blue";
78
+ readonly syntaxFunction: "magenta";
79
+ readonly syntaxVariable: "violet";
80
+ readonly syntaxString: "green";
81
+ readonly syntaxNumber: "orange";
82
+ readonly syntaxType: "yellow";
83
+ readonly syntaxOperator: "orange";
84
+ readonly syntaxPunctuation: "fgAlt";
85
+ readonly thinkingOff: "grey";
86
+ readonly thinkingMinimal: "fgAlt";
87
+ readonly thinkingLow: "darkBlue";
88
+ readonly thinkingMedium: "blue";
89
+ readonly thinkingHigh: "teal";
90
+ readonly thinkingXhigh: "magenta";
91
+ readonly thinkingMax: "red";
92
+ readonly bashMode: "lightGreen";
93
+ };
94
+ readonly export: {
95
+ readonly pageBg: "bg";
96
+ readonly cardBg: "bgAlt";
97
+ readonly infoBg: "#343225";
98
+ };
99
+ };
100
+ declare function writeDefaultTheme(temporaryDirectory: string): Promise<string>;
101
+ //#endregion
102
+ export { DEFAULT_THEME, DEFAULT_THEME_NAME, agentIdentityColor, allocateAgentIdentityColor, writeDefaultTheme };
103
+ //# sourceMappingURL=theme.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.cts","names":[],"sources":["../../src/tui/theme.ts"],"mappings":";;cAKa;;iBAqBG,mBAAmB,mBAAmB;;;;;;iBAStC,2BAA2B,kBAAkB,UAAU,YAAY,mBAAmB;cASzF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyGS,kBAAkB,6BAA6B"}
@@ -0,0 +1,103 @@
1
+ import { FooterTextColor } from "@agimon-ai/doompi-extension-contracts/footer";
2
+ //#region src/tui/theme.d.ts
3
+ declare const DEFAULT_THEME_NAME = "doom-pi-dark";
4
+ /** Stable semantic color shared by every UI surface that identifies an agent run. */
5
+ declare function agentIdentityColor(identity: string): FooterTextColor;
6
+ /**
7
+ * Prefer the identity's stable color, then walk the palette to avoid reusing a
8
+ * color already visible on the same surface. Once every color is occupied,
9
+ * reuse the stable preference rather than changing existing assignments.
10
+ */
11
+ declare function allocateAgentIdentityColor(identity: string, occupied: ReadonlySet<FooterTextColor>): FooterTextColor;
12
+ declare const DEFAULT_THEME: {
13
+ readonly $schema: "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json";
14
+ readonly name: "doom-pi-dark";
15
+ readonly vars: {
16
+ readonly bg: "#282c34";
17
+ readonly bgAlt: "#21242b";
18
+ readonly bgActive: "#3e4451";
19
+ readonly bgHover: "#3e4451";
20
+ readonly fg: "#bbc2cf";
21
+ readonly fgAlt: "#9ca0a4";
22
+ readonly grey: "#3f444a";
23
+ readonly greyAlt: "#5b6268";
24
+ readonly blue: "#51afef";
25
+ readonly darkBlue: "#2257a0";
26
+ readonly cyan: "#46d9ff";
27
+ readonly green: "#98be65";
28
+ readonly lightGreen: "#a8cc8a";
29
+ readonly teal: "#4db5bd";
30
+ readonly orange: "#da8548";
31
+ readonly red: "#ff6c6b";
32
+ readonly violet: "#a9a1e1";
33
+ readonly magenta: "#c678dd";
34
+ readonly yellow: "#ecbe7b";
35
+ readonly lightYellow: "#f0c674";
36
+ readonly comment: "#5b6268";
37
+ readonly diffAddedBg: "#1b2315";
38
+ readonly diffRemovedBg: "#2b1c20";
39
+ };
40
+ readonly colors: {
41
+ readonly accent: "blue";
42
+ readonly border: "greyAlt";
43
+ readonly borderAccent: "blue";
44
+ readonly borderMuted: "grey";
45
+ readonly success: "green";
46
+ readonly error: "red";
47
+ readonly warning: "yellow";
48
+ readonly muted: "fgAlt";
49
+ readonly dim: "comment";
50
+ readonly text: "fg";
51
+ readonly thinkingText: "fgAlt";
52
+ readonly selectedBg: "bgHover";
53
+ readonly userMessageBg: "bgActive";
54
+ readonly userMessageText: "fg";
55
+ readonly customMessageBg: "#2d253f";
56
+ readonly customMessageText: "fg";
57
+ readonly customMessageLabel: "violet";
58
+ readonly toolPendingBg: "bgAlt";
59
+ readonly toolSuccessBg: "diffAddedBg";
60
+ readonly toolErrorBg: "diffRemovedBg";
61
+ readonly toolTitle: "blue";
62
+ readonly toolOutput: "fg";
63
+ readonly mdHeading: "magenta";
64
+ readonly mdLink: "blue";
65
+ readonly mdLinkUrl: "comment";
66
+ readonly mdCode: "cyan";
67
+ readonly mdCodeBlock: "lightYellow";
68
+ readonly mdCodeBlockBorder: "greyAlt";
69
+ readonly mdQuote: "fgAlt";
70
+ readonly mdQuoteBorder: "greyAlt";
71
+ readonly mdHr: "greyAlt";
72
+ readonly mdListBullet: "blue";
73
+ readonly toolDiffAdded: "lightGreen";
74
+ readonly toolDiffRemoved: "red";
75
+ readonly toolDiffContext: "fgAlt";
76
+ readonly syntaxComment: "comment";
77
+ readonly syntaxKeyword: "blue";
78
+ readonly syntaxFunction: "magenta";
79
+ readonly syntaxVariable: "violet";
80
+ readonly syntaxString: "green";
81
+ readonly syntaxNumber: "orange";
82
+ readonly syntaxType: "yellow";
83
+ readonly syntaxOperator: "orange";
84
+ readonly syntaxPunctuation: "fgAlt";
85
+ readonly thinkingOff: "grey";
86
+ readonly thinkingMinimal: "fgAlt";
87
+ readonly thinkingLow: "darkBlue";
88
+ readonly thinkingMedium: "blue";
89
+ readonly thinkingHigh: "teal";
90
+ readonly thinkingXhigh: "magenta";
91
+ readonly thinkingMax: "red";
92
+ readonly bashMode: "lightGreen";
93
+ };
94
+ readonly export: {
95
+ readonly pageBg: "bg";
96
+ readonly cardBg: "bgAlt";
97
+ readonly infoBg: "#343225";
98
+ };
99
+ };
100
+ declare function writeDefaultTheme(temporaryDirectory: string): Promise<string>;
101
+ //#endregion
102
+ export { DEFAULT_THEME, DEFAULT_THEME_NAME, agentIdentityColor, allocateAgentIdentityColor, writeDefaultTheme };
103
+ //# sourceMappingURL=theme.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.mts","names":[],"sources":["../../src/tui/theme.ts"],"mappings":";;cAKa;;iBAqBG,mBAAmB,mBAAmB;;;;;;iBAStC,2BAA2B,kBAAkB,UAAU,YAAY,mBAAmB;cASzF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyGS,kBAAkB,6BAA6B"}
@@ -0,0 +1,2 @@
1
+ import e from"node:path";import t from"node:fs";const n=`doom-pi-dark`,r=[`accent`,`mdCode`,`toolDiffAdded`,`warning`,`mdHeading`,`syntaxNumber`];function i(e){let t=2166136261;for(let n of e)t^=n.charCodeAt(0),t=Math.imul(t,16777619);return(t>>>0)%r.length}function a(e){return r[i(e)]}function o(e,t){let n=i(e);for(let e=0;e<r.length;e++){let i=r[(n+e)%r.length];if(!t.has(i))return i}return r[n]}const s={$schema:`https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json`,name:n,vars:{bg:`#282c34`,bgAlt:`#21242b`,bgActive:`#3e4451`,bgHover:`#3e4451`,fg:`#bbc2cf`,fgAlt:`#9ca0a4`,grey:`#3f444a`,greyAlt:`#5b6268`,blue:`#51afef`,darkBlue:`#2257a0`,cyan:`#46d9ff`,green:`#98be65`,lightGreen:`#a8cc8a`,teal:`#4db5bd`,orange:`#da8548`,red:`#ff6c6b`,violet:`#a9a1e1`,magenta:`#c678dd`,yellow:`#ecbe7b`,lightYellow:`#f0c674`,comment:`#5b6268`,diffAddedBg:`#1b2315`,diffRemovedBg:`#2b1c20`},colors:{accent:`blue`,border:`greyAlt`,borderAccent:`blue`,borderMuted:`grey`,success:`green`,error:`red`,warning:`yellow`,muted:`fgAlt`,dim:`comment`,text:`fg`,thinkingText:`fgAlt`,selectedBg:`bgHover`,userMessageBg:`bgActive`,userMessageText:`fg`,customMessageBg:`#2d253f`,customMessageText:`fg`,customMessageLabel:`violet`,toolPendingBg:`bgAlt`,toolSuccessBg:`diffAddedBg`,toolErrorBg:`diffRemovedBg`,toolTitle:`blue`,toolOutput:`fg`,mdHeading:`magenta`,mdLink:`blue`,mdLinkUrl:`comment`,mdCode:`cyan`,mdCodeBlock:`lightYellow`,mdCodeBlockBorder:`greyAlt`,mdQuote:`fgAlt`,mdQuoteBorder:`greyAlt`,mdHr:`greyAlt`,mdListBullet:`blue`,toolDiffAdded:`lightGreen`,toolDiffRemoved:`red`,toolDiffContext:`fgAlt`,syntaxComment:`comment`,syntaxKeyword:`blue`,syntaxFunction:`magenta`,syntaxVariable:`violet`,syntaxString:`green`,syntaxNumber:`orange`,syntaxType:`yellow`,syntaxOperator:`orange`,syntaxPunctuation:`fgAlt`,thinkingOff:`grey`,thinkingMinimal:`fgAlt`,thinkingLow:`darkBlue`,thinkingMedium:`blue`,thinkingHigh:`teal`,thinkingXhigh:`magenta`,thinkingMax:`red`,bashMode:`lightGreen`},export:{pageBg:`bg`,cardBg:`bgAlt`,infoBg:`#343225`}};async function c(r){let i=e.join(r,`${n}.json`);return await t.promises.writeFile(i,`${JSON.stringify(s,null,2)}\n`,{mode:384}),i}export{s as DEFAULT_THEME,n as DEFAULT_THEME_NAME,a as agentIdentityColor,o as allocateAgentIdentityColor,c as writeDefaultTheme};
2
+ //# sourceMappingURL=theme.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.mjs","names":[],"sources":["../../src/tui/theme.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { FooterTextColor } from '@agimon-ai/doompi-extension-contracts/footer';\nimport type { ThemeColor } from '@earendil-works/pi-coding-agent';\n\nexport const DEFAULT_THEME_NAME = 'doom-pi-dark';\n\nconst AGENT_IDENTITY_COLORS = [\n 'accent',\n 'mdCode',\n 'toolDiffAdded',\n 'warning',\n 'mdHeading',\n 'syntaxNumber',\n] as const satisfies readonly (FooterTextColor & ThemeColor)[];\n\nfunction agentIdentityColorIndex(identity: string): number {\n let hash = 2_166_136_261;\n for (const character of identity) {\n hash ^= character.charCodeAt(0);\n hash = Math.imul(hash, 16_777_619);\n }\n return (hash >>> 0) % AGENT_IDENTITY_COLORS.length;\n}\n\n/** Stable semantic color shared by every UI surface that identifies an agent run. */\nexport function agentIdentityColor(identity: string): FooterTextColor {\n return AGENT_IDENTITY_COLORS[agentIdentityColorIndex(identity)]!;\n}\n\n/**\n * Prefer the identity's stable color, then walk the palette to avoid reusing a\n * color already visible on the same surface. Once every color is occupied,\n * reuse the stable preference rather than changing existing assignments.\n */\nexport function allocateAgentIdentityColor(identity: string, occupied: ReadonlySet<FooterTextColor>): FooterTextColor {\n const preferredIndex = agentIdentityColorIndex(identity);\n for (let offset = 0; offset < AGENT_IDENTITY_COLORS.length; offset++) {\n const color = AGENT_IDENTITY_COLORS[(preferredIndex + offset) % AGENT_IDENTITY_COLORS.length]!;\n if (!occupied.has(color)) return color;\n }\n return AGENT_IDENTITY_COLORS[preferredIndex]!;\n}\n\nexport const DEFAULT_THEME = {\n $schema:\n 'https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json',\n name: DEFAULT_THEME_NAME,\n // Doom One's graphite base and saturated syntax ladder. Variable names are\n // semantic to this package so every TUI surface can share the palette.\n vars: {\n bg: '#282c34',\n bgAlt: '#21242b',\n bgActive: '#3e4451',\n bgHover: '#3e4451',\n fg: '#bbc2cf',\n fgAlt: '#9ca0a4',\n grey: '#3f444a',\n greyAlt: '#5b6268',\n blue: '#51afef',\n darkBlue: '#2257a0',\n cyan: '#46d9ff',\n green: '#98be65',\n lightGreen: '#a8cc8a',\n teal: '#4db5bd',\n orange: '#da8548',\n red: '#ff6c6b',\n violet: '#a9a1e1',\n magenta: '#c678dd',\n yellow: '#ecbe7b',\n lightYellow: '#f0c674',\n comment: '#5b6268',\n // Diff bands sink below `bg` and carry their signal in hue, not lightness,\n // so a banded row reads as a recessed well and syntax highlighting keeps at\n // least the contrast it has off the band. Blending Doom One's pale green and\n // red toward `bg` the way Magit does lightens instead, and `comment` goes.\n diffAddedBg: '#1b2315',\n diffRemovedBg: '#2b1c20',\n },\n colors: {\n accent: 'blue',\n border: 'greyAlt',\n borderAccent: 'blue',\n borderMuted: 'grey',\n success: 'green',\n error: 'red',\n warning: 'yellow',\n muted: 'fgAlt',\n dim: 'comment',\n text: 'fg',\n thinkingText: 'fgAlt',\n\n selectedBg: 'bgHover',\n userMessageBg: 'bgActive',\n userMessageText: 'fg',\n customMessageBg: '#2d253f',\n customMessageText: 'fg',\n customMessageLabel: 'violet',\n toolPendingBg: 'bgAlt',\n // Doubles as the added/removed band behind a diff row, so it stays a wash\n // rather than a fill: syntax highlighting has to remain legible on top.\n toolSuccessBg: 'diffAddedBg',\n toolErrorBg: 'diffRemovedBg',\n toolTitle: 'blue',\n toolOutput: 'fg',\n\n mdHeading: 'magenta',\n mdLink: 'blue',\n mdLinkUrl: 'comment',\n mdCode: 'cyan',\n mdCodeBlock: 'lightYellow',\n mdCodeBlockBorder: 'greyAlt',\n mdQuote: 'fgAlt',\n mdQuoteBorder: 'greyAlt',\n mdHr: 'greyAlt',\n mdListBullet: 'blue',\n\n toolDiffAdded: 'lightGreen',\n toolDiffRemoved: 'red',\n toolDiffContext: 'fgAlt',\n\n syntaxComment: 'comment',\n syntaxKeyword: 'blue',\n syntaxFunction: 'magenta',\n syntaxVariable: 'violet',\n syntaxString: 'green',\n syntaxNumber: 'orange',\n syntaxType: 'yellow',\n syntaxOperator: 'orange',\n syntaxPunctuation: 'fgAlt',\n\n // A rising ramp from quiet graphite through the Doom One accent ladder.\n thinkingOff: 'grey',\n thinkingMinimal: 'fgAlt',\n thinkingLow: 'darkBlue',\n thinkingMedium: 'blue',\n thinkingHigh: 'teal',\n thinkingXhigh: 'magenta',\n thinkingMax: 'red',\n\n bashMode: 'lightGreen',\n },\n export: {\n pageBg: 'bg',\n cardBg: 'bgAlt',\n infoBg: '#343225',\n },\n} as const;\n\nexport async function writeDefaultTheme(temporaryDirectory: string): Promise<string> {\n const themePath = path.join(temporaryDirectory, `${DEFAULT_THEME_NAME}.json`);\n await fs.promises.writeFile(themePath, `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`, { mode: 0o600 });\n return themePath;\n}\n"],"mappings":"gDAKA,MAAa,EAAqB,eAE5B,EAAwB,CAC5B,SACA,SACA,gBACA,UACA,YACA,cACF,EAEA,SAAS,EAAwB,EAA0B,CACzD,IAAI,EAAO,WACX,IAAK,IAAM,KAAa,EACtB,GAAQ,EAAU,WAAW,CAAC,EAC9B,EAAO,KAAK,KAAK,EAAM,QAAU,EAEnC,OAAQ,IAAS,GAAK,EAAsB,MAC9C,CAGA,SAAgB,EAAmB,EAAmC,CACpE,OAAO,EAAsB,EAAwB,CAAQ,EAC/D,CAOA,SAAgB,EAA2B,EAAkB,EAAyD,CACpH,IAAM,EAAiB,EAAwB,CAAQ,EACvD,IAAK,IAAI,EAAS,EAAG,EAAS,EAAsB,OAAQ,IAAU,CACpE,IAAM,EAAQ,GAAuB,EAAiB,GAAU,EAAsB,QACtF,GAAI,CAAC,EAAS,IAAI,CAAK,EAAG,OAAO,CACnC,CACA,OAAO,EAAsB,EAC/B,CAEA,MAAa,EAAgB,CAC3B,QACE,+HACF,KAAM,EAGN,KAAM,CACJ,GAAI,UACJ,MAAO,UACP,SAAU,UACV,QAAS,UACT,GAAI,UACJ,MAAO,UACP,KAAM,UACN,QAAS,UACT,KAAM,UACN,SAAU,UACV,KAAM,UACN,MAAO,UACP,WAAY,UACZ,KAAM,UACN,OAAQ,UACR,IAAK,UACL,OAAQ,UACR,QAAS,UACT,OAAQ,UACR,YAAa,UACb,QAAS,UAKT,YAAa,UACb,cAAe,SACjB,EACA,OAAQ,CACN,OAAQ,OACR,OAAQ,UACR,aAAc,OACd,YAAa,OACb,QAAS,QACT,MAAO,MACP,QAAS,SACT,MAAO,QACP,IAAK,UACL,KAAM,KACN,aAAc,QAEd,WAAY,UACZ,cAAe,WACf,gBAAiB,KACjB,gBAAiB,UACjB,kBAAmB,KACnB,mBAAoB,SACpB,cAAe,QAGf,cAAe,cACf,YAAa,gBACb,UAAW,OACX,WAAY,KAEZ,UAAW,UACX,OAAQ,OACR,UAAW,UACX,OAAQ,OACR,YAAa,cACb,kBAAmB,UACnB,QAAS,QACT,cAAe,UACf,KAAM,UACN,aAAc,OAEd,cAAe,aACf,gBAAiB,MACjB,gBAAiB,QAEjB,cAAe,UACf,cAAe,OACf,eAAgB,UAChB,eAAgB,SAChB,aAAc,QACd,aAAc,SACd,WAAY,SACZ,eAAgB,SAChB,kBAAmB,QAGnB,YAAa,OACb,gBAAiB,QACjB,YAAa,WACb,eAAgB,OAChB,aAAc,OACd,cAAe,UACf,YAAa,MAEb,SAAU,YACZ,EACA,OAAQ,CACN,OAAQ,KACR,OAAQ,QACR,OAAQ,SACV,CACF,EAEA,eAAsB,EAAkB,EAA6C,CACnF,IAAM,EAAY,EAAK,KAAK,EAAoB,GAAG,EAAmB,MAAM,EAE5E,OADA,MAAM,EAAG,SAAS,UAAU,EAAW,GAAG,KAAK,UAAU,EAAe,KAAM,CAAC,EAAE,IAAK,CAAE,KAAM,GAAM,CAAC,EAC9F,CACT"}
@@ -0,0 +1,2 @@
1
+ let e=require("@earendil-works/pi-tui");var t=class{text;constructor(e){this.text=e}render(t){if(t<=0)return[];let n=Math.max(1,t-2);return(0,e.wrapTextWithAnsi)(this.text,n).map(e=>` ${e}`)}invalidate(){}},n=class{lines;theme;options;constructor(e,t,n={}){this.lines=e,this.theme=t,this.options=n}render(t){if(t<=0)return[];if(t<3)return this.lines.map(n=>(0,e.truncateToWidth)(n,t,`…`));let n=t-2,r=this.options.wrap?this.lines.flatMap(t=>(0,e.wrapTextWithAnsi)(t,n)):this.lines.map(t=>(0,e.truncateToWidth)(t,n,`…`)),i=[this.theme.fg(`borderMuted`,`─`.repeat(n)),...r].map(e=>` ${e}`);return this.options.trailingBlank!==!1&&i.push(``),i}invalidate(){}};function r(e,t){return t.inverse(t.bold(t.fg(`mdHeading`,` ${e.toUpperCase()} `)))}function i(e,t,n,i=`accent`){let a=r(e,n);return t.length>0?`${a} ${n.fg(i,t)}`:a}exports.DoomToolCall=t,exports.DoomToolResult=n,exports.renderToolBadge=r,exports.renderToolHeading=i;
2
+ //# sourceMappingURL=toolChrome.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolChrome.cjs","names":["wrapTextWithAnsi","truncateToWidth"],"sources":["../../src/tui/toolChrome.ts"],"sourcesContent":["import type { Theme, ThemeColor } from '@earendil-works/pi-coding-agent';\nimport { type Component, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';\n\nconst TOOL_BADGE_COLOR: ThemeColor = 'mdHeading';\nconst TOOL_PURPOSE_COLOR: ThemeColor = 'accent';\n\n/** Shared width-aware call surface with the standard one-column Doom inset. */\nexport class DoomToolCall implements Component {\n constructor(private readonly text: string) {}\n\n render(width: number): string[] {\n if (width <= 0) return [];\n const contentWidth = Math.max(1, width - 2);\n return wrapTextWithAnsi(this.text, contentWidth).map((line) => ` ${line}`);\n }\n\n invalidate(): void {\n // Call text is immutable and reflows from the current width on every render.\n }\n}\n\nexport interface DoomToolResultOptions {\n wrap?: boolean;\n trailingBlank?: boolean;\n}\n\n/** Neutral self-owned result frame for compact custom tools. */\nexport class DoomToolResult implements Component {\n constructor(\n private readonly lines: readonly string[],\n private readonly theme: Theme,\n private readonly options: DoomToolResultOptions = {},\n ) {}\n\n render(width: number): string[] {\n if (width <= 0) return [];\n if (width < 3) return this.lines.map((line) => truncateToWidth(line, width, '…'));\n\n const contentWidth = width - 2;\n const content = this.options.wrap\n ? this.lines.flatMap((line) => wrapTextWithAnsi(line, contentWidth))\n : this.lines.map((line) => truncateToWidth(line, contentWidth, '…'));\n const divider = this.theme.fg('borderMuted', '─'.repeat(contentWidth));\n const rendered = [divider, ...content].map((line) => ` ${line}`);\n if (this.options.trailingBlank !== false) rendered.push('');\n return rendered;\n }\n\n invalidate(): void {\n // Result text is immutable and reflows from the current width on every render.\n }\n}\n\n/** Doom-style inverse badge used to identify every self-rendered tool. */\nexport function renderToolBadge(label: string, theme: Theme): string {\n return theme.inverse(theme.bold(theme.fg(TOOL_BADGE_COLOR, ` ${label.toUpperCase()} `)));\n}\n\n/** Keep the tool identity visually separate from its path, command, or action. */\nexport function renderToolHeading(\n label: string,\n purpose: string,\n theme: Theme,\n purposeColor: ThemeColor = TOOL_PURPOSE_COLOR,\n): string {\n const badge = renderToolBadge(label, theme);\n return purpose.length > 0 ? `${badge} ${theme.fg(purposeColor, purpose)}` : badge;\n}\n"],"mappings":"wCAOA,IAAa,EAAb,KAA+C,CAChB,KAA7B,YAAY,EAA+B,CAAd,KAAA,KAAA,CAAe,CAE5C,OAAO,EAAyB,CAC9B,GAAI,GAAS,EAAG,MAAO,CAAC,EACxB,IAAM,EAAe,KAAK,IAAI,EAAG,EAAQ,CAAC,EAC1C,OAAA,EAAOA,EAAAA,iBAAAA,CAAiB,KAAK,KAAM,CAAY,CAAC,CAAC,IAAK,GAAS,IAAI,GAAM,CAC3E,CAEA,YAAmB,CAEnB,CACF,EAQa,EAAb,KAAiD,CAE5B,MACA,MACA,QAHnB,YACE,EACA,EACA,EAAkD,CAAC,EACnD,CAHiB,KAAA,MAAA,EACA,KAAA,MAAA,EACA,KAAA,QAAA,CAChB,CAEH,OAAO,EAAyB,CAC9B,GAAI,GAAS,EAAG,MAAO,CAAC,EACxB,GAAI,EAAQ,EAAG,OAAO,KAAK,MAAM,IAAK,IAAA,EAASC,EAAAA,gBAAAA,CAAgB,EAAM,EAAO,GAAG,CAAC,EAEhF,IAAM,EAAe,EAAQ,EACvB,EAAU,KAAK,QAAQ,KACzB,KAAK,MAAM,QAAS,IAAA,EAASD,EAAAA,iBAAAA,CAAiB,EAAM,CAAY,CAAC,EACjE,KAAK,MAAM,IAAK,IAAA,EAASC,EAAAA,gBAAAA,CAAgB,EAAM,EAAc,GAAG,CAAC,EAE/D,EAAW,CADD,KAAK,MAAM,GAAG,cAAe,IAAI,OAAO,CAAY,CAC5C,EAAG,GAAG,CAAO,CAAC,CAAC,IAAK,GAAS,IAAI,GAAM,EAE/D,OADI,KAAK,QAAQ,gBAAkB,IAAO,EAAS,KAAK,EAAE,EACnD,CACT,CAEA,YAAmB,CAEnB,CACF,EAGA,SAAgB,EAAgB,EAAe,EAAsB,CACnE,OAAO,EAAM,QAAQ,EAAM,KAAK,EAAM,GAAG,YAAkB,IAAI,EAAM,YAAY,EAAE,EAAE,CAAC,CAAC,CACzF,CAGA,SAAgB,EACd,EACA,EACA,EACA,EAA2B,SACnB,CACR,IAAM,EAAQ,EAAgB,EAAO,CAAK,EAC1C,OAAO,EAAQ,OAAS,EAAI,GAAG,EAAM,GAAG,EAAM,GAAG,EAAc,CAAO,IAAM,CAC9E"}
@@ -0,0 +1,30 @@
1
+ import { Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
2
+ import { Component } from "@earendil-works/pi-tui";
3
+ //#region src/tui/toolChrome.d.ts
4
+ /** Shared width-aware call surface with the standard one-column Doom inset. */
5
+ declare class DoomToolCall implements Component {
6
+ private readonly text;
7
+ constructor(text: string);
8
+ render(width: number): string[];
9
+ invalidate(): void;
10
+ }
11
+ interface DoomToolResultOptions {
12
+ wrap?: boolean;
13
+ trailingBlank?: boolean;
14
+ }
15
+ /** Neutral self-owned result frame for compact custom tools. */
16
+ declare class DoomToolResult implements Component {
17
+ private readonly lines;
18
+ private readonly theme;
19
+ private readonly options;
20
+ constructor(lines: readonly string[], theme: Theme, options?: DoomToolResultOptions);
21
+ render(width: number): string[];
22
+ invalidate(): void;
23
+ }
24
+ /** Doom-style inverse badge used to identify every self-rendered tool. */
25
+ declare function renderToolBadge(label: string, theme: Theme): string;
26
+ /** Keep the tool identity visually separate from its path, command, or action. */
27
+ declare function renderToolHeading(label: string, purpose: string, theme: Theme, purposeColor?: ThemeColor): string;
28
+ //#endregion
29
+ export { DoomToolCall, DoomToolResult, DoomToolResultOptions, renderToolBadge, renderToolHeading };
30
+ //# sourceMappingURL=toolChrome.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolChrome.d.cts","names":[],"sources":["../../src/tui/toolChrome.ts"],"mappings":";;;;cAOa,wBAAwB;mBACN;EAAA,YAAA;EAE7B,OAAO;EAMP;;UAKe;EACf;EACA;;;cAIW,0BAA0B;mBAElB;mBACA;mBACA;EAFA,YAAA,0BACA,OAAO,OACP,UAAS;EAG5B,OAAO;EAcP;;;iBAMc,gBAAgB,eAAe,OAAO;;iBAKtC,kBACd,eACA,iBACA,OAAO,OACP,eAAc"}
@@ -0,0 +1,30 @@
1
+ import { Component } from "@earendil-works/pi-tui";
2
+ import { Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
3
+ //#region src/tui/toolChrome.d.ts
4
+ /** Shared width-aware call surface with the standard one-column Doom inset. */
5
+ declare class DoomToolCall implements Component {
6
+ private readonly text;
7
+ constructor(text: string);
8
+ render(width: number): string[];
9
+ invalidate(): void;
10
+ }
11
+ interface DoomToolResultOptions {
12
+ wrap?: boolean;
13
+ trailingBlank?: boolean;
14
+ }
15
+ /** Neutral self-owned result frame for compact custom tools. */
16
+ declare class DoomToolResult implements Component {
17
+ private readonly lines;
18
+ private readonly theme;
19
+ private readonly options;
20
+ constructor(lines: readonly string[], theme: Theme, options?: DoomToolResultOptions);
21
+ render(width: number): string[];
22
+ invalidate(): void;
23
+ }
24
+ /** Doom-style inverse badge used to identify every self-rendered tool. */
25
+ declare function renderToolBadge(label: string, theme: Theme): string;
26
+ /** Keep the tool identity visually separate from its path, command, or action. */
27
+ declare function renderToolHeading(label: string, purpose: string, theme: Theme, purposeColor?: ThemeColor): string;
28
+ //#endregion
29
+ export { DoomToolCall, DoomToolResult, DoomToolResultOptions, renderToolBadge, renderToolHeading };
30
+ //# sourceMappingURL=toolChrome.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolChrome.d.mts","names":[],"sources":["../../src/tui/toolChrome.ts"],"mappings":";;;;cAOa,wBAAwB;mBACN;EAAA,YAAA;EAE7B,OAAO;EAMP;;UAKe;EACf;EACA;;;cAIW,0BAA0B;mBAElB;mBACA;mBACA;EAFA,YAAA,0BACA,OAAO,OACP,UAAS;EAG5B,OAAO;EAcP;;;iBAMc,gBAAgB,eAAe,OAAO;;iBAKtC,kBACd,eACA,iBACA,OAAO,OACP,eAAc"}