@openeryc/pi-coding-agent 0.75.26 → 0.75.28

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 (313) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/cli/config-selector.d.ts +1 -13
  3. package/dist/cli/config-selector.d.ts.map +1 -1
  4. package/dist/cli/config-selector.js +4 -29
  5. package/dist/cli/config-selector.js.map +1 -1
  6. package/dist/cli/list-models.d.ts.map +1 -1
  7. package/dist/cli/list-models.js +1 -1
  8. package/dist/cli/list-models.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +0 -2
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +12 -20
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  14. package/dist/core/export-html/tool-renderer.js.map +1 -1
  15. package/dist/core/extensions/loader.d.ts.map +1 -1
  16. package/dist/core/extensions/loader.js +1 -1
  17. package/dist/core/extensions/loader.js.map +1 -1
  18. package/dist/core/extensions/runner.d.ts +1 -1
  19. package/dist/core/extensions/runner.d.ts.map +1 -1
  20. package/dist/core/extensions/runner.js +1 -1
  21. package/dist/core/extensions/runner.js.map +1 -1
  22. package/dist/core/extensions/types.d.ts +1 -1
  23. package/dist/core/extensions/types.d.ts.map +1 -1
  24. package/dist/core/extensions/types.js.map +1 -1
  25. package/dist/core/keybindings.d.ts +7 -135
  26. package/dist/core/keybindings.d.ts.map +1 -1
  27. package/dist/core/keybindings.js +1 -1
  28. package/dist/core/keybindings.js.map +1 -1
  29. package/dist/core/mcp/client.d.ts +30 -11
  30. package/dist/core/mcp/client.d.ts.map +1 -1
  31. package/dist/core/mcp/client.js +55 -110
  32. package/dist/core/mcp/client.js.map +1 -1
  33. package/dist/core/mcp/index.d.ts +1 -3
  34. package/dist/core/mcp/index.d.ts.map +1 -1
  35. package/dist/core/mcp/index.js.map +1 -1
  36. package/dist/core/mcp/manager.d.ts +3 -9
  37. package/dist/core/mcp/manager.d.ts.map +1 -1
  38. package/dist/core/mcp/manager.js +71 -105
  39. package/dist/core/mcp/manager.js.map +1 -1
  40. package/dist/core/tools/bash.d.ts.map +1 -1
  41. package/dist/core/tools/bash.js +1 -1
  42. package/dist/core/tools/bash.js.map +1 -1
  43. package/dist/core/tools/edit.d.ts +1 -1
  44. package/dist/core/tools/edit.d.ts.map +1 -1
  45. package/dist/core/tools/edit.js +1 -1
  46. package/dist/core/tools/edit.js.map +1 -1
  47. package/dist/core/tools/find.d.ts.map +1 -1
  48. package/dist/core/tools/find.js +1 -1
  49. package/dist/core/tools/find.js.map +1 -1
  50. package/dist/core/tools/grep.d.ts.map +1 -1
  51. package/dist/core/tools/grep.js +1 -1
  52. package/dist/core/tools/grep.js.map +1 -1
  53. package/dist/core/tools/index.d.ts +3 -1
  54. package/dist/core/tools/index.d.ts.map +1 -1
  55. package/dist/core/tools/index.js +27 -1
  56. package/dist/core/tools/index.js.map +1 -1
  57. package/dist/core/tools/ls.d.ts.map +1 -1
  58. package/dist/core/tools/ls.js +1 -1
  59. package/dist/core/tools/ls.js.map +1 -1
  60. package/dist/core/tools/read.d.ts.map +1 -1
  61. package/dist/core/tools/read.js +1 -1
  62. package/dist/core/tools/read.js.map +1 -1
  63. package/dist/core/tools/render-utils.d.ts.map +1 -1
  64. package/dist/core/tools/render-utils.js +1 -1
  65. package/dist/core/tools/render-utils.js.map +1 -1
  66. package/dist/core/tools/web-fetch.d.ts +15 -0
  67. package/dist/core/tools/web-fetch.d.ts.map +1 -0
  68. package/dist/core/tools/web-fetch.js +52 -0
  69. package/dist/core/tools/web-fetch.js.map +1 -0
  70. package/dist/core/tools/web-search.d.ts +18 -0
  71. package/dist/core/tools/web-search.d.ts.map +1 -0
  72. package/dist/core/tools/web-search.js +83 -0
  73. package/dist/core/tools/web-search.js.map +1 -0
  74. package/dist/core/tools/write.d.ts.map +1 -1
  75. package/dist/core/tools/write.js +1 -1
  76. package/dist/core/tools/write.js.map +1 -1
  77. package/dist/core/tui-stubs.d.ts +95 -0
  78. package/dist/core/tui-stubs.d.ts.map +1 -0
  79. package/dist/core/tui-stubs.js +90 -0
  80. package/dist/core/tui-stubs.js.map +1 -0
  81. package/dist/index.d.ts +5 -3
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +7 -5
  84. package/dist/index.js.map +1 -1
  85. package/dist/main.d.ts.map +1 -1
  86. package/dist/main.js +92 -106
  87. package/dist/main.js.map +1 -1
  88. package/dist/modes/index.d.ts +0 -4
  89. package/dist/modes/index.d.ts.map +1 -1
  90. package/dist/modes/index.js +0 -4
  91. package/dist/modes/index.js.map +1 -1
  92. package/dist/modes/interactive/components/diff.d.ts +1 -8
  93. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  94. package/dist/modes/interactive/components/diff.js +2 -131
  95. package/dist/modes/interactive/components/diff.js.map +1 -1
  96. package/dist/modes/interactive/components/keybinding-hints.d.ts +2 -12
  97. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  98. package/dist/modes/interactive/components/keybinding-hints.js +4 -33
  99. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  100. package/dist/modes/interactive/components/visual-truncate.d.ts +1 -19
  101. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  102. package/dist/modes/interactive/components/visual-truncate.js +4 -31
  103. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  104. package/dist/modes/interactive/theme/theme.d.ts +27 -89
  105. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  106. package/dist/modes/interactive/theme/theme.js +44 -992
  107. package/dist/modes/interactive/theme/theme.js.map +1 -1
  108. package/dist/package-manager-cli.d.ts.map +1 -1
  109. package/dist/package-manager-cli.js +1 -1
  110. package/dist/package-manager-cli.js.map +1 -1
  111. package/npm-shrinkwrap.json +240 -246
  112. package/package.json +6 -7
  113. package/dist/cli/session-picker.d.ts +0 -9
  114. package/dist/cli/session-picker.d.ts.map +0 -1
  115. package/dist/cli/session-picker.js +0 -35
  116. package/dist/cli/session-picker.js.map +0 -1
  117. package/dist/core/mcp/types.d.ts +0 -86
  118. package/dist/core/mcp/types.d.ts.map +0 -1
  119. package/dist/core/mcp/types.js +0 -8
  120. package/dist/core/mcp/types.js.map +0 -1
  121. package/dist/modes/interactive/assets/clankolas.png +0 -0
  122. package/dist/modes/interactive/components/armin.d.ts +0 -34
  123. package/dist/modes/interactive/components/armin.d.ts.map +0 -1
  124. package/dist/modes/interactive/components/armin.js +0 -333
  125. package/dist/modes/interactive/components/armin.js.map +0 -1
  126. package/dist/modes/interactive/components/assistant-message.d.ts +0 -20
  127. package/dist/modes/interactive/components/assistant-message.d.ts.map +0 -1
  128. package/dist/modes/interactive/components/assistant-message.js +0 -121
  129. package/dist/modes/interactive/components/assistant-message.js.map +0 -1
  130. package/dist/modes/interactive/components/bash-execution.d.ts +0 -34
  131. package/dist/modes/interactive/components/bash-execution.d.ts.map +0 -1
  132. package/dist/modes/interactive/components/bash-execution.js +0 -175
  133. package/dist/modes/interactive/components/bash-execution.js.map +0 -1
  134. package/dist/modes/interactive/components/bordered-loader.d.ts +0 -16
  135. package/dist/modes/interactive/components/bordered-loader.d.ts.map +0 -1
  136. package/dist/modes/interactive/components/bordered-loader.js +0 -54
  137. package/dist/modes/interactive/components/bordered-loader.js.map +0 -1
  138. package/dist/modes/interactive/components/branch-summary-message.d.ts +0 -16
  139. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +0 -1
  140. package/dist/modes/interactive/components/branch-summary-message.js +0 -44
  141. package/dist/modes/interactive/components/branch-summary-message.js.map +0 -1
  142. package/dist/modes/interactive/components/compaction-summary-message.d.ts +0 -16
  143. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +0 -1
  144. package/dist/modes/interactive/components/compaction-summary-message.js +0 -45
  145. package/dist/modes/interactive/components/compaction-summary-message.js.map +0 -1
  146. package/dist/modes/interactive/components/config-selector.d.ts +0 -71
  147. package/dist/modes/interactive/components/config-selector.d.ts.map +0 -1
  148. package/dist/modes/interactive/components/config-selector.js +0 -506
  149. package/dist/modes/interactive/components/config-selector.js.map +0 -1
  150. package/dist/modes/interactive/components/countdown-timer.d.ts +0 -14
  151. package/dist/modes/interactive/components/countdown-timer.d.ts.map +0 -1
  152. package/dist/modes/interactive/components/countdown-timer.js +0 -33
  153. package/dist/modes/interactive/components/countdown-timer.js.map +0 -1
  154. package/dist/modes/interactive/components/custom-editor.d.ts +0 -21
  155. package/dist/modes/interactive/components/custom-editor.d.ts.map +0 -1
  156. package/dist/modes/interactive/components/custom-editor.js +0 -70
  157. package/dist/modes/interactive/components/custom-editor.js.map +0 -1
  158. package/dist/modes/interactive/components/custom-message.d.ts +0 -20
  159. package/dist/modes/interactive/components/custom-message.d.ts.map +0 -1
  160. package/dist/modes/interactive/components/custom-message.js +0 -79
  161. package/dist/modes/interactive/components/custom-message.js.map +0 -1
  162. package/dist/modes/interactive/components/daxnuts.d.ts +0 -23
  163. package/dist/modes/interactive/components/daxnuts.d.ts.map +0 -1
  164. package/dist/modes/interactive/components/daxnuts.js +0 -140
  165. package/dist/modes/interactive/components/daxnuts.js.map +0 -1
  166. package/dist/modes/interactive/components/dynamic-border.d.ts +0 -15
  167. package/dist/modes/interactive/components/dynamic-border.d.ts.map +0 -1
  168. package/dist/modes/interactive/components/dynamic-border.js +0 -21
  169. package/dist/modes/interactive/components/dynamic-border.js.map +0 -1
  170. package/dist/modes/interactive/components/earendil-announcement.d.ts +0 -5
  171. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +0 -1
  172. package/dist/modes/interactive/components/earendil-announcement.js +0 -40
  173. package/dist/modes/interactive/components/earendil-announcement.js.map +0 -1
  174. package/dist/modes/interactive/components/extension-editor.d.ts +0 -20
  175. package/dist/modes/interactive/components/extension-editor.d.ts.map +0 -1
  176. package/dist/modes/interactive/components/extension-editor.js +0 -119
  177. package/dist/modes/interactive/components/extension-editor.js.map +0 -1
  178. package/dist/modes/interactive/components/extension-input.d.ts +0 -23
  179. package/dist/modes/interactive/components/extension-input.d.ts.map +0 -1
  180. package/dist/modes/interactive/components/extension-input.js +0 -61
  181. package/dist/modes/interactive/components/extension-input.js.map +0 -1
  182. package/dist/modes/interactive/components/extension-selector.d.ts +0 -26
  183. package/dist/modes/interactive/components/extension-selector.d.ts.map +0 -1
  184. package/dist/modes/interactive/components/extension-selector.js +0 -83
  185. package/dist/modes/interactive/components/extension-selector.js.map +0 -1
  186. package/dist/modes/interactive/components/footer.d.ts +0 -27
  187. package/dist/modes/interactive/components/footer.d.ts.map +0 -1
  188. package/dist/modes/interactive/components/footer.js +0 -201
  189. package/dist/modes/interactive/components/footer.js.map +0 -1
  190. package/dist/modes/interactive/components/index.d.ts +0 -32
  191. package/dist/modes/interactive/components/index.d.ts.map +0 -1
  192. package/dist/modes/interactive/components/index.js +0 -33
  193. package/dist/modes/interactive/components/index.js.map +0 -1
  194. package/dist/modes/interactive/components/login-dialog.d.ts +0 -46
  195. package/dist/modes/interactive/components/login-dialog.d.ts.map +0 -1
  196. package/dist/modes/interactive/components/login-dialog.js +0 -160
  197. package/dist/modes/interactive/components/login-dialog.js.map +0 -1
  198. package/dist/modes/interactive/components/model-selector.d.ts +0 -47
  199. package/dist/modes/interactive/components/model-selector.d.ts.map +0 -1
  200. package/dist/modes/interactive/components/model-selector.js +0 -278
  201. package/dist/modes/interactive/components/model-selector.js.map +0 -1
  202. package/dist/modes/interactive/components/oauth-selector.d.ts +0 -31
  203. package/dist/modes/interactive/components/oauth-selector.d.ts.map +0 -1
  204. package/dist/modes/interactive/components/oauth-selector.js +0 -165
  205. package/dist/modes/interactive/components/oauth-selector.js.map +0 -1
  206. package/dist/modes/interactive/components/scoped-models-selector.d.ts +0 -42
  207. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +0 -1
  208. package/dist/modes/interactive/components/scoped-models-selector.js +0 -290
  209. package/dist/modes/interactive/components/scoped-models-selector.js.map +0 -1
  210. package/dist/modes/interactive/components/session-selector-search.d.ts +0 -23
  211. package/dist/modes/interactive/components/session-selector-search.d.ts.map +0 -1
  212. package/dist/modes/interactive/components/session-selector-search.js +0 -155
  213. package/dist/modes/interactive/components/session-selector-search.js.map +0 -1
  214. package/dist/modes/interactive/components/session-selector.d.ts +0 -96
  215. package/dist/modes/interactive/components/session-selector.d.ts.map +0 -1
  216. package/dist/modes/interactive/components/session-selector.js +0 -861
  217. package/dist/modes/interactive/components/session-selector.js.map +0 -1
  218. package/dist/modes/interactive/components/settings-selector.d.ts +0 -69
  219. package/dist/modes/interactive/components/settings-selector.d.ts.map +0 -1
  220. package/dist/modes/interactive/components/settings-selector.js +0 -390
  221. package/dist/modes/interactive/components/settings-selector.js.map +0 -1
  222. package/dist/modes/interactive/components/show-images-selector.d.ts +0 -10
  223. package/dist/modes/interactive/components/show-images-selector.d.ts.map +0 -1
  224. package/dist/modes/interactive/components/show-images-selector.js +0 -39
  225. package/dist/modes/interactive/components/show-images-selector.js.map +0 -1
  226. package/dist/modes/interactive/components/skill-invocation-message.d.ts +0 -17
  227. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +0 -1
  228. package/dist/modes/interactive/components/skill-invocation-message.js +0 -47
  229. package/dist/modes/interactive/components/skill-invocation-message.js.map +0 -1
  230. package/dist/modes/interactive/components/theme-selector.d.ts +0 -11
  231. package/dist/modes/interactive/components/theme-selector.d.ts.map +0 -1
  232. package/dist/modes/interactive/components/theme-selector.js +0 -50
  233. package/dist/modes/interactive/components/theme-selector.js.map +0 -1
  234. package/dist/modes/interactive/components/thinking-selector.d.ts +0 -11
  235. package/dist/modes/interactive/components/thinking-selector.d.ts.map +0 -1
  236. package/dist/modes/interactive/components/thinking-selector.js +0 -51
  237. package/dist/modes/interactive/components/thinking-selector.js.map +0 -1
  238. package/dist/modes/interactive/components/tool-execution.d.ts +0 -63
  239. package/dist/modes/interactive/components/tool-execution.d.ts.map +0 -1
  240. package/dist/modes/interactive/components/tool-execution.js +0 -295
  241. package/dist/modes/interactive/components/tool-execution.js.map +0 -1
  242. package/dist/modes/interactive/components/tree-selector.d.ts +0 -89
  243. package/dist/modes/interactive/components/tree-selector.d.ts.map +0 -1
  244. package/dist/modes/interactive/components/tree-selector.js +0 -1093
  245. package/dist/modes/interactive/components/tree-selector.js.map +0 -1
  246. package/dist/modes/interactive/components/user-message-selector.d.ts +0 -30
  247. package/dist/modes/interactive/components/user-message-selector.d.ts.map +0 -1
  248. package/dist/modes/interactive/components/user-message-selector.js +0 -114
  249. package/dist/modes/interactive/components/user-message-selector.js.map +0 -1
  250. package/dist/modes/interactive/components/user-message.d.ts +0 -10
  251. package/dist/modes/interactive/components/user-message.d.ts.map +0 -1
  252. package/dist/modes/interactive/components/user-message.js +0 -29
  253. package/dist/modes/interactive/components/user-message.js.map +0 -1
  254. package/dist/modes/interactive/interactive-mode.d.ts +0 -375
  255. package/dist/modes/interactive/interactive-mode.d.ts.map +0 -1
  256. package/dist/modes/interactive/interactive-mode.js +0 -4799
  257. package/dist/modes/interactive/interactive-mode.js.map +0 -1
  258. package/dist/modes/interactive/theme/dark.json +0 -86
  259. package/dist/modes/interactive/theme/light.json +0 -85
  260. package/dist/modes/interactive/theme/theme-schema.json +0 -335
  261. package/examples/extensions/border-status-editor.ts +0 -150
  262. package/examples/extensions/built-in-tool-renderer.ts +0 -249
  263. package/examples/extensions/custom-footer.ts +0 -64
  264. package/examples/extensions/custom-header.ts +0 -73
  265. package/examples/extensions/custom-provider-anthropic/index.ts +0 -604
  266. package/examples/extensions/custom-provider-anthropic/package-lock.json +0 -24
  267. package/examples/extensions/custom-provider-anthropic/package.json +0 -19
  268. package/examples/extensions/custom-provider-gitlab-duo/index.ts +0 -349
  269. package/examples/extensions/custom-provider-gitlab-duo/package.json +0 -16
  270. package/examples/extensions/custom-provider-gitlab-duo/test.ts +0 -82
  271. package/examples/extensions/doom-overlay/README.md +0 -46
  272. package/examples/extensions/doom-overlay/doom/build/doom.js +0 -21
  273. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  274. package/examples/extensions/doom-overlay/doom/build.sh +0 -152
  275. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +0 -72
  276. package/examples/extensions/doom-overlay/doom-component.ts +0 -132
  277. package/examples/extensions/doom-overlay/doom-engine.ts +0 -173
  278. package/examples/extensions/doom-overlay/doom-keys.ts +0 -104
  279. package/examples/extensions/doom-overlay/index.ts +0 -74
  280. package/examples/extensions/doom-overlay/wad-finder.ts +0 -51
  281. package/examples/extensions/github-issue-autocomplete.ts +0 -185
  282. package/examples/extensions/handoff.ts +0 -191
  283. package/examples/extensions/interactive-shell.ts +0 -196
  284. package/examples/extensions/message-renderer.ts +0 -59
  285. package/examples/extensions/minimal-mode.ts +0 -426
  286. package/examples/extensions/modal-editor.ts +0 -85
  287. package/examples/extensions/overlay-qa-tests.ts +0 -1379
  288. package/examples/extensions/overlay-test.ts +0 -153
  289. package/examples/extensions/plan-mode/index.ts +0 -340
  290. package/examples/extensions/preset.ts +0 -430
  291. package/examples/extensions/qna.ts +0 -122
  292. package/examples/extensions/question.ts +0 -264
  293. package/examples/extensions/questionnaire.ts +0 -427
  294. package/examples/extensions/rainbow-editor.ts +0 -88
  295. package/examples/extensions/sandbox/index.ts +0 -321
  296. package/examples/extensions/sandbox/package-lock.json +0 -92
  297. package/examples/extensions/sandbox/package.json +0 -19
  298. package/examples/extensions/snake.ts +0 -343
  299. package/examples/extensions/space-invaders.ts +0 -560
  300. package/examples/extensions/ssh.ts +0 -220
  301. package/examples/extensions/status-line.ts +0 -32
  302. package/examples/extensions/structured-output.ts +0 -65
  303. package/examples/extensions/subagent/index.ts +0 -1009
  304. package/examples/extensions/summarize.ts +0 -206
  305. package/examples/extensions/tic-tac-toe.ts +0 -1008
  306. package/examples/extensions/todo.ts +0 -297
  307. package/examples/extensions/tools.ts +0 -141
  308. package/examples/extensions/truncated-tool.ts +0 -195
  309. package/examples/extensions/with-deps/index.ts +0 -32
  310. package/examples/extensions/with-deps/package-lock.json +0 -31
  311. package/examples/extensions/with-deps/package.json +0 -22
  312. package/examples/extensions/working-indicator.ts +0 -123
  313. package/examples/rpc-extension-ui.ts +0 -632
@@ -1,1015 +1,67 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { getCapabilities, } from "@openeryc/pi-tui";
4
- import chalk from "chalk";
5
- import { Type } from "typebox";
6
- import { Compile } from "typebox/compile";
7
- import { getCustomThemesDir, getThemesDir } from "../../../config.js";
8
- import { closeWatcher, watchWithErrorHandler } from "../../../utils/fs-watch.js";
9
- import { highlight, supportsLanguage } from "../../../utils/syntax-highlight.js";
10
- // ============================================================================
11
- // Types & Schema
12
- // ============================================================================
13
- const ColorValueSchema = Type.Union([
14
- Type.String(), // hex "#ff0000", var ref "primary", or empty ""
15
- Type.Integer({ minimum: 0, maximum: 255 }), // 256-color index
16
- ]);
17
- const ThemeJsonSchema = Type.Object({
18
- $schema: Type.Optional(Type.String()),
19
- name: Type.String(),
20
- vars: Type.Optional(Type.Record(Type.String(), ColorValueSchema)),
21
- colors: Type.Object({
22
- // Core UI (10 colors)
23
- accent: ColorValueSchema,
24
- border: ColorValueSchema,
25
- borderAccent: ColorValueSchema,
26
- borderMuted: ColorValueSchema,
27
- success: ColorValueSchema,
28
- error: ColorValueSchema,
29
- warning: ColorValueSchema,
30
- muted: ColorValueSchema,
31
- dim: ColorValueSchema,
32
- text: ColorValueSchema,
33
- thinkingText: ColorValueSchema,
34
- // Backgrounds & Content Text (11 colors)
35
- selectedBg: ColorValueSchema,
36
- userMessageBg: ColorValueSchema,
37
- userMessageText: ColorValueSchema,
38
- customMessageBg: ColorValueSchema,
39
- customMessageText: ColorValueSchema,
40
- customMessageLabel: ColorValueSchema,
41
- toolPendingBg: ColorValueSchema,
42
- toolSuccessBg: ColorValueSchema,
43
- toolErrorBg: ColorValueSchema,
44
- toolTitle: ColorValueSchema,
45
- toolOutput: ColorValueSchema,
46
- // Markdown (10 colors)
47
- mdHeading: ColorValueSchema,
48
- mdLink: ColorValueSchema,
49
- mdLinkUrl: ColorValueSchema,
50
- mdCode: ColorValueSchema,
51
- mdCodeBlock: ColorValueSchema,
52
- mdCodeBlockBorder: ColorValueSchema,
53
- mdQuote: ColorValueSchema,
54
- mdQuoteBorder: ColorValueSchema,
55
- mdHr: ColorValueSchema,
56
- mdListBullet: ColorValueSchema,
57
- // Tool Diffs (3 colors)
58
- toolDiffAdded: ColorValueSchema,
59
- toolDiffRemoved: ColorValueSchema,
60
- toolDiffContext: ColorValueSchema,
61
- // Syntax Highlighting (9 colors)
62
- syntaxComment: ColorValueSchema,
63
- syntaxKeyword: ColorValueSchema,
64
- syntaxFunction: ColorValueSchema,
65
- syntaxVariable: ColorValueSchema,
66
- syntaxString: ColorValueSchema,
67
- syntaxNumber: ColorValueSchema,
68
- syntaxType: ColorValueSchema,
69
- syntaxOperator: ColorValueSchema,
70
- syntaxPunctuation: ColorValueSchema,
71
- // Thinking Level Borders (6 colors)
72
- thinkingOff: ColorValueSchema,
73
- thinkingMinimal: ColorValueSchema,
74
- thinkingLow: ColorValueSchema,
75
- thinkingMedium: ColorValueSchema,
76
- thinkingHigh: ColorValueSchema,
77
- thinkingXhigh: ColorValueSchema,
78
- // Bash Mode (1 color)
79
- bashMode: ColorValueSchema,
80
- }),
81
- export: Type.Optional(Type.Object({
82
- pageBg: Type.Optional(ColorValueSchema),
83
- cardBg: Type.Optional(ColorValueSchema),
84
- infoBg: Type.Optional(ColorValueSchema),
85
- })),
86
- });
87
- const validateThemeJson = Compile(ThemeJsonSchema);
88
- // ============================================================================
89
- // Color Utilities
90
- // ============================================================================
91
- function hexToRgb(hex) {
92
- const cleaned = hex.replace("#", "");
93
- if (cleaned.length !== 6) {
94
- throw new Error(`Invalid hex color: ${hex}`);
95
- }
96
- const r = parseInt(cleaned.substring(0, 2), 16);
97
- const g = parseInt(cleaned.substring(2, 4), 16);
98
- const b = parseInt(cleaned.substring(4, 6), 16);
99
- if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
100
- throw new Error(`Invalid hex color: ${hex}`);
101
- }
102
- return { r, g, b };
103
- }
104
- // The 6x6x6 color cube channel values (indices 0-5)
105
- const CUBE_VALUES = [0, 95, 135, 175, 215, 255];
106
- // Grayscale ramp values (indices 232-255, 24 grays from 8 to 238)
107
- const GRAY_VALUES = Array.from({ length: 24 }, (_, i) => 8 + i * 10);
108
- function findClosestCubeIndex(value) {
109
- let minDist = Infinity;
110
- let minIdx = 0;
111
- for (let i = 0; i < CUBE_VALUES.length; i++) {
112
- const dist = Math.abs(value - CUBE_VALUES[i]);
113
- if (dist < minDist) {
114
- minDist = dist;
115
- minIdx = i;
116
- }
117
- }
118
- return minIdx;
119
- }
120
- function findClosestGrayIndex(gray) {
121
- let minDist = Infinity;
122
- let minIdx = 0;
123
- for (let i = 0; i < GRAY_VALUES.length; i++) {
124
- const dist = Math.abs(gray - GRAY_VALUES[i]);
125
- if (dist < minDist) {
126
- minDist = dist;
127
- minIdx = i;
128
- }
129
- }
130
- return minIdx;
131
- }
132
- function colorDistance(r1, g1, b1, r2, g2, b2) {
133
- // Weighted Euclidean distance (human eye is more sensitive to green)
134
- const dr = r1 - r2;
135
- const dg = g1 - g2;
136
- const db = b1 - b2;
137
- return dr * dr * 0.299 + dg * dg * 0.587 + db * db * 0.114;
138
- }
139
- function rgbTo256(r, g, b) {
140
- // Find closest color in the 6x6x6 cube
141
- const rIdx = findClosestCubeIndex(r);
142
- const gIdx = findClosestCubeIndex(g);
143
- const bIdx = findClosestCubeIndex(b);
144
- const cubeR = CUBE_VALUES[rIdx];
145
- const cubeG = CUBE_VALUES[gIdx];
146
- const cubeB = CUBE_VALUES[bIdx];
147
- const cubeIndex = 16 + 36 * rIdx + 6 * gIdx + bIdx;
148
- const cubeDist = colorDistance(r, g, b, cubeR, cubeG, cubeB);
149
- // Find closest grayscale
150
- const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
151
- const grayIdx = findClosestGrayIndex(gray);
152
- const grayValue = GRAY_VALUES[grayIdx];
153
- const grayIndex = 232 + grayIdx;
154
- const grayDist = colorDistance(r, g, b, grayValue, grayValue, grayValue);
155
- // Check if color has noticeable saturation (hue matters)
156
- // If max-min spread is significant, prefer cube to preserve tint
157
- const maxC = Math.max(r, g, b);
158
- const minC = Math.min(r, g, b);
159
- const spread = maxC - minC;
160
- // Only consider grayscale if color is nearly neutral (spread < 10)
161
- // AND grayscale is actually closer
162
- if (spread < 10 && grayDist < cubeDist) {
163
- return grayIndex;
164
- }
165
- return cubeIndex;
166
- }
167
- function hexTo256(hex) {
168
- const { r, g, b } = hexToRgb(hex);
169
- return rgbTo256(r, g, b);
170
- }
171
- function fgAnsi(color, mode) {
172
- if (color === "")
173
- return "\x1b[39m";
174
- if (typeof color === "number")
175
- return `\x1b[38;5;${color}m`;
176
- if (color.startsWith("#")) {
177
- if (mode === "truecolor") {
178
- const { r, g, b } = hexToRgb(color);
179
- return `\x1b[38;2;${r};${g};${b}m`;
180
- }
181
- else {
182
- const index = hexTo256(color);
183
- return `\x1b[38;5;${index}m`;
184
- }
185
- }
186
- throw new Error(`Invalid color value: ${color}`);
187
- }
188
- function bgAnsi(color, mode) {
189
- if (color === "")
190
- return "\x1b[49m";
191
- if (typeof color === "number")
192
- return `\x1b[48;5;${color}m`;
193
- if (color.startsWith("#")) {
194
- if (mode === "truecolor") {
195
- const { r, g, b } = hexToRgb(color);
196
- return `\x1b[48;2;${r};${g};${b}m`;
197
- }
198
- else {
199
- const index = hexTo256(color);
200
- return `\x1b[48;5;${index}m`;
201
- }
202
- }
203
- throw new Error(`Invalid color value: ${color}`);
204
- }
205
- function resolveVarRefs(value, vars, visited = new Set()) {
206
- if (typeof value === "number" || value === "" || value.startsWith("#")) {
207
- return value;
208
- }
209
- if (visited.has(value)) {
210
- throw new Error(`Circular variable reference detected: ${value}`);
211
- }
212
- if (!(value in vars)) {
213
- throw new Error(`Variable reference not found: ${value}`);
214
- }
215
- visited.add(value);
216
- return resolveVarRefs(vars[value], vars, visited);
217
- }
218
- function resolveThemeColors(colors, vars = {}) {
219
- const resolved = {};
220
- for (const [key, value] of Object.entries(colors)) {
221
- resolved[key] = resolveVarRefs(value, vars);
222
- }
223
- return resolved;
224
- }
225
- // ============================================================================
226
- // Theme Class
227
- // ============================================================================
228
1
  export class Theme {
229
2
  name;
230
3
  sourcePath;
231
4
  sourceInfo;
232
- fgColors;
233
- bgColors;
234
- mode;
235
- constructor(fgColors, bgColors, mode, options = {}) {
236
- this.name = options.name;
237
- this.sourcePath = options.sourcePath;
238
- this.sourceInfo = options.sourceInfo;
239
- this.mode = mode;
240
- this.fgColors = new Map();
241
- for (const [key, value] of Object.entries(fgColors)) {
242
- this.fgColors.set(key, fgAnsi(value, mode));
243
- }
244
- this.bgColors = new Map();
245
- for (const [key, value] of Object.entries(bgColors)) {
246
- this.bgColors.set(key, bgAnsi(value, mode));
247
- }
248
- }
249
- fg(color, text) {
250
- const ansi = this.fgColors.get(color);
251
- if (!ansi)
252
- throw new Error(`Unknown theme color: ${color}`);
253
- return `${ansi}${text}\x1b[39m`; // Reset only foreground color
254
- }
255
- bg(color, text) {
256
- const ansi = this.bgColors.get(color);
257
- if (!ansi)
258
- throw new Error(`Unknown theme background color: ${color}`);
259
- return `${ansi}${text}\x1b[49m`; // Reset only background color
260
- }
261
- bold(text) {
262
- return chalk.bold(text);
263
- }
264
- italic(text) {
265
- return chalk.italic(text);
266
- }
267
- underline(text) {
268
- return chalk.underline(text);
269
- }
270
- inverse(text) {
271
- return chalk.inverse(text);
272
- }
273
- strikethrough(text) {
274
- return chalk.strikethrough(text);
275
- }
276
- getFgAnsi(color) {
277
- const ansi = this.fgColors.get(color);
278
- if (!ansi)
279
- throw new Error(`Unknown theme color: ${color}`);
280
- return ansi;
281
- }
282
- getBgAnsi(color) {
283
- const ansi = this.bgColors.get(color);
284
- if (!ansi)
285
- throw new Error(`Unknown theme background color: ${color}`);
286
- return ansi;
287
- }
288
- getColorMode() {
289
- return this.mode;
290
- }
291
- getThinkingBorderColor(level) {
292
- // Map thinking levels to dedicated theme colors
293
- switch (level) {
294
- case "off":
295
- return (str) => this.fg("thinkingOff", str);
296
- case "minimal":
297
- return (str) => this.fg("thinkingMinimal", str);
298
- case "low":
299
- return (str) => this.fg("thinkingLow", str);
300
- case "medium":
301
- return (str) => this.fg("thinkingMedium", str);
302
- case "high":
303
- return (str) => this.fg("thinkingHigh", str);
304
- case "xhigh":
305
- return (str) => this.fg("thinkingXhigh", str);
306
- default:
307
- return (str) => this.fg("thinkingOff", str);
308
- }
309
- }
310
- getBashModeBorderColor() {
311
- return (str) => this.fg("bashMode", str);
312
- }
313
- }
314
- // ============================================================================
315
- // Theme Loading
316
- // ============================================================================
317
- let BUILTIN_THEMES;
318
- function getBuiltinThemes() {
319
- if (!BUILTIN_THEMES) {
320
- const themesDir = getThemesDir();
321
- const darkPath = path.join(themesDir, "dark.json");
322
- const lightPath = path.join(themesDir, "light.json");
323
- BUILTIN_THEMES = {
324
- dark: JSON.parse(fs.readFileSync(darkPath, "utf-8")),
325
- light: JSON.parse(fs.readFileSync(lightPath, "utf-8")),
326
- };
327
- }
328
- return BUILTIN_THEMES;
329
- }
330
- export function getAvailableThemes() {
331
- const themes = new Set(Object.keys(getBuiltinThemes()));
332
- const customThemesDir = getCustomThemesDir();
333
- if (fs.existsSync(customThemesDir)) {
334
- const files = fs.readdirSync(customThemesDir);
335
- for (const file of files) {
336
- if (file.endsWith(".json")) {
337
- themes.add(file.slice(0, -5));
5
+ fgColors = new Map();
6
+ bgColors = new Map();
7
+ constructor(fgColors, bgColors) {
8
+ if (fgColors) {
9
+ for (const [key] of Object.entries(fgColors)) {
10
+ this.fgColors.set(key, "");
338
11
  }
339
12
  }
340
- }
341
- for (const name of registeredThemes.keys()) {
342
- themes.add(name);
343
- }
344
- return Array.from(themes).sort();
345
- }
346
- export function getAvailableThemesWithPaths() {
347
- const themesDir = getThemesDir();
348
- const customThemesDir = getCustomThemesDir();
349
- const result = [];
350
- // Built-in themes
351
- for (const name of Object.keys(getBuiltinThemes())) {
352
- result.push({ name, path: path.join(themesDir, `${name}.json`) });
353
- }
354
- // Custom themes
355
- if (fs.existsSync(customThemesDir)) {
356
- for (const file of fs.readdirSync(customThemesDir)) {
357
- if (file.endsWith(".json")) {
358
- const name = file.slice(0, -5);
359
- if (!result.some((t) => t.name === name)) {
360
- result.push({ name, path: path.join(customThemesDir, file) });
361
- }
362
- }
363
- }
364
- }
365
- for (const [name, theme] of registeredThemes.entries()) {
366
- if (!result.some((t) => t.name === name)) {
367
- result.push({ name, path: theme.sourcePath });
368
- }
369
- }
370
- return result.sort((a, b) => a.name.localeCompare(b.name));
371
- }
372
- function parseThemeJson(label, json) {
373
- if (!validateThemeJson.Check(json)) {
374
- const errors = Array.from(validateThemeJson.Errors(json));
375
- const missingColors = new Set();
376
- const otherErrors = [];
377
- for (const error of errors) {
378
- if (error.keyword === "required" && error.instancePath === "/colors") {
379
- const requiredProperties = error.params.requiredProperties;
380
- for (const requiredProperty of requiredProperties ?? []) {
381
- missingColors.add(requiredProperty);
382
- }
383
- continue;
13
+ if (bgColors) {
14
+ for (const [key] of Object.entries(bgColors)) {
15
+ this.bgColors.set(key, "");
384
16
  }
385
- const path = error.instancePath || "/";
386
- otherErrors.push(` - ${path}: ${error.message}`);
387
- }
388
- let errorMessage = `Invalid theme "${label}":\n`;
389
- if (missingColors.size > 0) {
390
- errorMessage += "\nMissing required color tokens:\n";
391
- errorMessage += Array.from(missingColors)
392
- .sort()
393
- .map((color) => ` - ${color}`)
394
- .join("\n");
395
- errorMessage += '\n\nPlease add these colors to your theme\'s "colors" object.';
396
- errorMessage += "\nSee the built-in themes (dark.json, light.json) for reference values.";
397
- }
398
- if (otherErrors.length > 0) {
399
- errorMessage += `\n\nOther errors:\n${otherErrors.join("\n")}`;
400
- }
401
- throw new Error(errorMessage);
402
- }
403
- return json;
404
- }
405
- function parseThemeJsonContent(label, content) {
406
- let json;
407
- try {
408
- json = JSON.parse(content);
409
- }
410
- catch (error) {
411
- throw new Error(`Failed to parse theme ${label}: ${error}`);
412
- }
413
- return parseThemeJson(label, json);
414
- }
415
- function loadThemeJson(name) {
416
- const builtinThemes = getBuiltinThemes();
417
- if (name in builtinThemes) {
418
- return builtinThemes[name];
419
- }
420
- const registeredTheme = registeredThemes.get(name);
421
- if (registeredTheme?.sourcePath) {
422
- const content = fs.readFileSync(registeredTheme.sourcePath, "utf-8");
423
- return parseThemeJsonContent(registeredTheme.sourcePath, content);
424
- }
425
- if (registeredTheme) {
426
- throw new Error(`Theme "${name}" does not have a source path for export`);
427
- }
428
- const customThemesDir = getCustomThemesDir();
429
- const themePath = path.join(customThemesDir, `${name}.json`);
430
- if (!fs.existsSync(themePath)) {
431
- throw new Error(`Theme not found: ${name}`);
432
- }
433
- const content = fs.readFileSync(themePath, "utf-8");
434
- return parseThemeJsonContent(name, content);
435
- }
436
- function createTheme(themeJson, mode, sourcePath) {
437
- const colorMode = mode ?? (getCapabilities().trueColor ? "truecolor" : "256color");
438
- const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
439
- const fgColors = {};
440
- const bgColors = {};
441
- const bgColorKeys = new Set([
442
- "selectedBg",
443
- "userMessageBg",
444
- "customMessageBg",
445
- "toolPendingBg",
446
- "toolSuccessBg",
447
- "toolErrorBg",
448
- ]);
449
- for (const [key, value] of Object.entries(resolvedColors)) {
450
- if (bgColorKeys.has(key)) {
451
- bgColors[key] = value;
452
- }
453
- else {
454
- fgColors[key] = value;
455
- }
456
- }
457
- return new Theme(fgColors, bgColors, colorMode, {
458
- name: themeJson.name,
459
- sourcePath,
460
- });
461
- }
462
- export function loadThemeFromPath(themePath, mode) {
463
- const content = fs.readFileSync(themePath, "utf-8");
464
- const themeJson = parseThemeJsonContent(themePath, content);
465
- return createTheme(themeJson, mode, themePath);
466
- }
467
- function loadTheme(name, mode) {
468
- const registeredTheme = registeredThemes.get(name);
469
- if (registeredTheme) {
470
- return registeredTheme;
471
- }
472
- const themeJson = loadThemeJson(name);
473
- return createTheme(themeJson, mode);
474
- }
475
- export function getThemeByName(name) {
476
- try {
477
- return loadTheme(name);
478
- }
479
- catch {
480
- return undefined;
481
- }
482
- }
483
- function getColorFgBgBackgroundIndex(colorfgbg) {
484
- const parts = colorfgbg.split(";");
485
- for (let i = parts.length - 1; i >= 0; i--) {
486
- const bg = parseInt(parts[i].trim(), 10);
487
- if (Number.isInteger(bg) && bg >= 0 && bg <= 255) {
488
- return bg;
489
- }
490
- }
491
- return undefined;
492
- }
493
- function getRgbColorLuminance({ r, g, b }) {
494
- const toLinear = (channel) => {
495
- const value = channel / 255;
496
- return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
497
- };
498
- return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
499
- }
500
- function getAnsiColorLuminance(index) {
501
- return getRgbColorLuminance(hexToRgb(ansi256ToHex(index)));
502
- }
503
- export function getThemeForRgbColor(rgb) {
504
- return getRgbColorLuminance(rgb) >= 0.5 ? "light" : "dark";
505
- }
506
- function parseOscHexChannel(channel) {
507
- if (!/^[0-9a-f]+$/i.test(channel)) {
508
- return undefined;
509
- }
510
- const max = 16 ** channel.length - 1;
511
- if (max <= 0) {
512
- return undefined;
513
- }
514
- return Math.round((parseInt(channel, 16) / max) * 255);
515
- }
516
- export function parseOsc11BackgroundColor(data) {
517
- const match = data.match(/^\x1b\]11;([^\x07\x1b]*)(?:\x07|\x1b\\)$/i);
518
- if (!match) {
519
- return undefined;
520
- }
521
- const value = match[1].trim();
522
- if (value.startsWith("#")) {
523
- const hex = value.slice(1);
524
- if (/^[0-9a-f]{6}$/i.test(hex)) {
525
- return hexToRgb(value);
526
- }
527
- if (/^[0-9a-f]{12}$/i.test(hex)) {
528
- const r = parseOscHexChannel(hex.slice(0, 4));
529
- const g = parseOscHexChannel(hex.slice(4, 8));
530
- const b = parseOscHexChannel(hex.slice(8, 12));
531
- return r !== undefined && g !== undefined && b !== undefined ? { r, g, b } : undefined;
532
- }
533
- return undefined;
534
- }
535
- const rgbValue = value.replace(/^rgba?:/i, "");
536
- const [red, green, blue] = rgbValue.split("/");
537
- if (red === undefined || green === undefined || blue === undefined) {
538
- return undefined;
539
- }
540
- const r = parseOscHexChannel(red);
541
- const g = parseOscHexChannel(green);
542
- const b = parseOscHexChannel(blue);
543
- return r !== undefined && g !== undefined && b !== undefined ? { r, g, b } : undefined;
544
- }
545
- export function detectTerminalBackground(options = {}) {
546
- const env = options.env ?? process.env;
547
- const colorfgbg = env.COLORFGBG || "";
548
- const bg = getColorFgBgBackgroundIndex(colorfgbg);
549
- if (bg !== undefined) {
550
- return {
551
- theme: getAnsiColorLuminance(bg) >= 0.5 ? "light" : "dark",
552
- source: "COLORFGBG",
553
- detail: `background color index ${bg}`,
554
- confidence: "high",
555
- };
556
- }
557
- return {
558
- theme: "dark",
559
- source: "fallback",
560
- detail: "no terminal background hint found",
561
- confidence: "low",
562
- };
563
- }
564
- export function getDefaultTheme() {
565
- return detectTerminalBackground().theme;
566
- }
567
- // ============================================================================
568
- // Global Theme Instance
569
- // ============================================================================
570
- // Use globalThis to share theme across module loaders (tsx + jiti in dev mode)
571
- const THEME_KEY = Symbol.for("@openeryc/pi-coding-agent:theme");
572
- const THEME_KEY_OLD = Symbol.for("@mariozechner/pi-coding-agent:theme");
573
- // Export theme as a getter that reads from globalThis
574
- // This ensures all module instances (tsx, jiti) see the same theme
575
- export const theme = new Proxy({}, {
576
- get(_target, prop) {
577
- const t = globalThis[THEME_KEY];
578
- if (!t)
579
- throw new Error("Theme not initialized. Call initTheme() first.");
580
- return t[prop];
581
- },
582
- });
583
- function setGlobalTheme(t) {
584
- globalThis[THEME_KEY] = t;
585
- globalThis[THEME_KEY_OLD] = t;
586
- }
587
- let currentThemeName;
588
- let themeWatcher;
589
- let themeReloadTimer;
590
- let onThemeChangeCallback;
591
- const registeredThemes = new Map();
592
- export function setRegisteredThemes(themes) {
593
- registeredThemes.clear();
594
- for (const theme of themes) {
595
- if (theme.name) {
596
- registeredThemes.set(theme.name, theme);
597
- }
598
- }
599
- }
600
- export function initTheme(themeName, enableWatcher = false) {
601
- const name = themeName ?? getDefaultTheme();
602
- currentThemeName = name;
603
- try {
604
- setGlobalTheme(loadTheme(name));
605
- if (enableWatcher) {
606
- startThemeWatcher();
607
17
  }
608
18
  }
609
- catch (_error) {
610
- // Theme is invalid - fall back to dark theme silently
611
- currentThemeName = "dark";
612
- setGlobalTheme(loadTheme("dark"));
613
- // Don't start watcher for fallback theme
614
- }
615
- }
616
- export function setTheme(name, enableWatcher = false) {
617
- currentThemeName = name;
618
- try {
619
- setGlobalTheme(loadTheme(name));
620
- if (enableWatcher) {
621
- startThemeWatcher();
622
- }
623
- if (onThemeChangeCallback) {
624
- onThemeChangeCallback();
625
- }
626
- return { success: true };
627
- }
628
- catch (error) {
629
- // Theme is invalid - fall back to dark theme
630
- currentThemeName = "dark";
631
- setGlobalTheme(loadTheme("dark"));
632
- // Don't start watcher for fallback theme
633
- return {
634
- success: false,
635
- error: error instanceof Error ? error.message : String(error),
636
- };
637
- }
638
- }
639
- export function setThemeInstance(themeInstance) {
640
- setGlobalTheme(themeInstance);
641
- currentThemeName = "<in-memory>";
642
- stopThemeWatcher(); // Can't watch a direct instance
643
- if (onThemeChangeCallback) {
644
- onThemeChangeCallback();
645
- }
646
- }
647
- export function onThemeChange(callback) {
648
- onThemeChangeCallback = callback;
649
- }
650
- function startThemeWatcher() {
651
- stopThemeWatcher();
652
- // Only watch if it's a custom theme (not built-in)
653
- if (!currentThemeName || currentThemeName === "dark" || currentThemeName === "light") {
654
- return;
19
+ fg(_color, text) {
20
+ return text;
655
21
  }
656
- const customThemesDir = getCustomThemesDir();
657
- const watchedThemeName = currentThemeName;
658
- const watchedFileName = `${watchedThemeName}.json`;
659
- const themeFile = path.join(customThemesDir, watchedFileName);
660
- // Only watch if the file exists
661
- if (!fs.existsSync(themeFile)) {
662
- return;
22
+ bg(_color, text) {
23
+ return text;
663
24
  }
664
- const scheduleReload = () => {
665
- if (themeReloadTimer) {
666
- clearTimeout(themeReloadTimer);
667
- }
668
- themeReloadTimer = setTimeout(() => {
669
- themeReloadTimer = undefined;
670
- // Ignore stale timers after switching themes or stopping the watcher
671
- if (currentThemeName !== watchedThemeName) {
672
- return;
673
- }
674
- // Keep the last successfully loaded theme active if the file is temporarily missing
675
- if (!fs.existsSync(themeFile)) {
676
- return;
677
- }
678
- try {
679
- // Reload the theme from disk and refresh the registry cache
680
- const reloadedTheme = loadThemeFromPath(themeFile);
681
- registeredThemes.set(watchedThemeName, reloadedTheme);
682
- setGlobalTheme(reloadedTheme);
683
- // Notify callback (to invalidate UI)
684
- if (onThemeChangeCallback) {
685
- onThemeChangeCallback();
686
- }
687
- }
688
- catch (_error) {
689
- // Ignore errors (file might be in invalid state while being edited)
690
- }
691
- }, 100);
25
+ bold(text) {
26
+ return text;
27
+ }
28
+ prompt = {
29
+ user: "",
30
+ userPrefix: "",
31
+ assistant: "",
32
+ assistantPrefix: "",
33
+ thinking: "",
34
+ thinkingPrefix: "",
692
35
  };
693
- themeWatcher =
694
- watchWithErrorHandler(customThemesDir, (_eventType, filename) => {
695
- if (currentThemeName !== watchedThemeName) {
696
- return;
697
- }
698
- if (!filename) {
699
- scheduleReload();
700
- return;
701
- }
702
- if (filename !== watchedFileName) {
703
- return;
704
- }
705
- scheduleReload();
706
- }, () => {
707
- closeWatcher(themeWatcher);
708
- themeWatcher = undefined;
709
- }) ?? undefined;
710
- }
711
- export function stopThemeWatcher() {
712
- if (themeReloadTimer) {
713
- clearTimeout(themeReloadTimer);
714
- themeReloadTimer = undefined;
715
- }
716
- closeWatcher(themeWatcher);
717
- themeWatcher = undefined;
718
- }
719
- // ============================================================================
720
- // HTML Export Helpers
721
- // ============================================================================
722
- /**
723
- * Convert a 256-color index to hex string.
724
- * Indices 0-15: basic colors (approximate)
725
- * Indices 16-231: 6x6x6 color cube
726
- * Indices 232-255: grayscale ramp
727
- */
728
- function ansi256ToHex(index) {
729
- // Basic colors (0-15) - approximate common terminal values
730
- const basicColors = [
731
- "#000000",
732
- "#800000",
733
- "#008000",
734
- "#808000",
735
- "#000080",
736
- "#800080",
737
- "#008080",
738
- "#c0c0c0",
739
- "#808080",
740
- "#ff0000",
741
- "#00ff00",
742
- "#ffff00",
743
- "#0000ff",
744
- "#ff00ff",
745
- "#00ffff",
746
- "#ffffff",
747
- ];
748
- if (index < 16) {
749
- return basicColors[index];
750
- }
751
- // Color cube (16-231): 6x6x6 = 216 colors
752
- if (index < 232) {
753
- const cubeIndex = index - 16;
754
- const r = Math.floor(cubeIndex / 36);
755
- const g = Math.floor((cubeIndex % 36) / 6);
756
- const b = cubeIndex % 6;
757
- const toHex = (n) => (n === 0 ? 0 : 55 + n * 40).toString(16).padStart(2, "0");
758
- return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
759
- }
760
- // Grayscale (232-255): 24 shades
761
- const gray = 8 + (index - 232) * 10;
762
- const grayHex = gray.toString(16).padStart(2, "0");
763
- return `#${grayHex}${grayHex}${grayHex}`;
764
- }
765
- /**
766
- * Get resolved theme colors as CSS-compatible hex strings.
767
- * Used by HTML export to generate CSS custom properties.
768
- */
769
- export function getResolvedThemeColors(themeName) {
770
- const name = themeName ?? currentThemeName ?? getDefaultTheme();
771
- const isLight = name === "light";
772
- const themeJson = loadThemeJson(name);
773
- const resolved = resolveThemeColors(themeJson.colors, themeJson.vars);
774
- // Default text color for empty values (terminal uses default fg color)
775
- const defaultText = isLight ? "#000000" : "#e5e5e7";
776
- const cssColors = {};
777
- for (const [key, value] of Object.entries(resolved)) {
778
- if (typeof value === "number") {
779
- cssColors[key] = ansi256ToHex(value);
780
- }
781
- else if (value === "") {
782
- // Empty means default terminal color - use sensible fallback for HTML
783
- cssColors[key] = defaultText;
784
- }
785
- else {
786
- cssColors[key] = value;
787
- }
788
- }
789
- return cssColors;
790
36
  }
791
- /**
792
- * Check if a theme is a "light" theme (for CSS that needs light/dark variants).
793
- */
794
- export function isLightTheme(themeName) {
795
- // Currently just check the name - could be extended to analyze colors
796
- return themeName === "light";
37
+ export const theme = new Theme();
38
+ export function initTheme(_themeName, _watch) { }
39
+ export function stopThemeWatcher() { }
40
+ export function loadThemeFromPath(_path) {
41
+ return theme;
797
42
  }
798
- /**
799
- * Get explicit export colors from theme JSON, if specified.
800
- * Returns undefined for each color that isn't explicitly set.
801
- */
802
- export function getThemeExportColors(themeName) {
803
- const name = themeName ?? currentThemeName ?? getDefaultTheme();
804
- try {
805
- const themeJson = loadThemeJson(name);
806
- const exportSection = themeJson.export;
807
- if (!exportSection)
808
- return {};
809
- const vars = themeJson.vars ?? {};
810
- const resolve = (value) => {
811
- if (value === undefined)
812
- return undefined;
813
- const resolved = resolveVarRefs(value, vars);
814
- if (typeof resolved === "number")
815
- return ansi256ToHex(resolved);
816
- if (resolved === "")
817
- return undefined;
818
- return resolved;
819
- };
820
- return {
821
- pageBg: resolve(exportSection.pageBg),
822
- cardBg: resolve(exportSection.cardBg),
823
- infoBg: resolve(exportSection.infoBg),
824
- };
825
- }
826
- catch {
827
- return {};
828
- }
43
+ export function getLanguageFromPath(_path) {
44
+ return "text";
829
45
  }
830
- let cachedHighlightThemeFor;
831
- let cachedCliHighlightTheme;
832
- function buildCliHighlightTheme(t) {
833
- return {
834
- keyword: (s) => t.fg("syntaxKeyword", s),
835
- built_in: (s) => t.fg("syntaxType", s),
836
- literal: (s) => t.fg("syntaxNumber", s),
837
- number: (s) => t.fg("syntaxNumber", s),
838
- string: (s) => t.fg("syntaxString", s),
839
- comment: (s) => t.fg("syntaxComment", s),
840
- function: (s) => t.fg("syntaxFunction", s),
841
- title: (s) => t.fg("syntaxFunction", s),
842
- class: (s) => t.fg("syntaxType", s),
843
- type: (s) => t.fg("syntaxType", s),
844
- attr: (s) => t.fg("syntaxVariable", s),
845
- variable: (s) => t.fg("syntaxVariable", s),
846
- params: (s) => t.fg("syntaxVariable", s),
847
- operator: (s) => t.fg("syntaxOperator", s),
848
- punctuation: (s) => t.fg("syntaxPunctuation", s),
849
- };
46
+ export function highlightCode(_code, _language) {
47
+ return _code.split("\n");
850
48
  }
851
- function getCliHighlightTheme(t) {
852
- if (cachedHighlightThemeFor !== t || !cachedCliHighlightTheme) {
853
- cachedHighlightThemeFor = t;
854
- cachedCliHighlightTheme = buildCliHighlightTheme(t);
855
- }
856
- return cachedCliHighlightTheme;
49
+ export function getResolvedThemeColors(_themeName) {
50
+ return {};
857
51
  }
858
- /**
859
- * Highlight code with syntax coloring based on file extension or language.
860
- * Returns array of highlighted lines.
861
- */
862
- export function highlightCode(code, lang) {
863
- // Validate language before highlighting to avoid stderr spam from cli-highlight
864
- const validLang = lang && supportsLanguage(lang) ? lang : undefined;
865
- // Skip highlighting when no valid language is specified. cli-highlight's
866
- // auto-detection is unreliable and can misidentify prose as AppleScript,
867
- // LiveCodeServer, etc., coloring random English words as keywords.
868
- if (!validLang) {
869
- return code.split("\n").map((line) => theme.fg("mdCodeBlock", line));
870
- }
871
- const opts = {
872
- language: validLang,
873
- ignoreIllegals: true,
874
- theme: getCliHighlightTheme(theme),
875
- };
876
- try {
877
- return highlight(code, opts).split("\n");
878
- }
879
- catch {
880
- return code.split("\n");
881
- }
52
+ export function getThemeExportColors(_themeName) {
53
+ return {};
882
54
  }
883
- /**
884
- * Get language identifier from file path extension.
885
- */
886
- export function getLanguageFromPath(filePath) {
887
- const ext = filePath.split(".").pop()?.toLowerCase();
888
- if (!ext)
889
- return undefined;
890
- const extToLang = {
891
- ts: "typescript",
892
- tsx: "typescript",
893
- js: "javascript",
894
- jsx: "javascript",
895
- mjs: "javascript",
896
- cjs: "javascript",
897
- py: "python",
898
- rb: "ruby",
899
- rs: "rust",
900
- go: "go",
901
- java: "java",
902
- kt: "kotlin",
903
- swift: "swift",
904
- c: "c",
905
- h: "c",
906
- cpp: "cpp",
907
- cc: "cpp",
908
- cxx: "cpp",
909
- hpp: "cpp",
910
- cs: "csharp",
911
- php: "php",
912
- sh: "bash",
913
- bash: "bash",
914
- zsh: "bash",
915
- fish: "fish",
916
- ps1: "powershell",
917
- sql: "sql",
918
- html: "html",
919
- htm: "html",
920
- css: "css",
921
- scss: "scss",
922
- sass: "sass",
923
- less: "less",
924
- json: "json",
925
- yaml: "yaml",
926
- yml: "yaml",
927
- toml: "toml",
928
- xml: "xml",
929
- md: "markdown",
930
- markdown: "markdown",
931
- dockerfile: "dockerfile",
932
- makefile: "makefile",
933
- cmake: "cmake",
934
- lua: "lua",
935
- perl: "perl",
936
- r: "r",
937
- scala: "scala",
938
- clj: "clojure",
939
- ex: "elixir",
940
- exs: "elixir",
941
- erl: "erlang",
942
- hs: "haskell",
943
- ml: "ocaml",
944
- vim: "vim",
945
- graphql: "graphql",
946
- proto: "protobuf",
947
- tf: "hcl",
948
- hcl: "hcl",
949
- };
950
- return extToLang[ext];
55
+ export function detectTerminalBackground() {
56
+ return "dark";
951
57
  }
952
- export function getMarkdownTheme() {
953
- return {
954
- heading: (text) => theme.fg("mdHeading", text),
955
- link: (text) => theme.fg("mdLink", text),
956
- linkUrl: (text) => theme.fg("mdLinkUrl", text),
957
- code: (text) => theme.fg("mdCode", text),
958
- codeBlock: (text) => theme.fg("mdCodeBlock", text),
959
- codeBlockBorder: (text) => theme.fg("mdCodeBlockBorder", text),
960
- quote: (text) => theme.fg("mdQuote", text),
961
- quoteBorder: (text) => theme.fg("mdQuoteBorder", text),
962
- hr: (text) => theme.fg("mdHr", text),
963
- listBullet: (text) => theme.fg("mdListBullet", text),
964
- bold: (text) => theme.bold(text),
965
- italic: (text) => theme.italic(text),
966
- underline: (text) => theme.underline(text),
967
- strikethrough: (text) => chalk.strikethrough(text),
968
- highlightCode: (code, lang) => {
969
- // Validate language before highlighting to avoid stderr spam from cli-highlight
970
- const validLang = lang && supportsLanguage(lang) ? lang : undefined;
971
- // Skip highlighting when no valid language is specified. cli-highlight's
972
- // auto-detection is unreliable and can misidentify prose as AppleScript,
973
- // LiveCodeServer, etc., coloring random English words as keywords.
974
- if (!validLang) {
975
- return code.split("\n").map((line) => theme.fg("mdCodeBlock", line));
976
- }
977
- const opts = {
978
- language: validLang,
979
- ignoreIllegals: true,
980
- theme: getCliHighlightTheme(theme),
981
- };
982
- try {
983
- return highlight(code, opts).split("\n");
984
- }
985
- catch {
986
- return code.split("\n").map((line) => theme.fg("mdCodeBlock", line));
987
- }
988
- },
989
- };
58
+ export function getThemeByName(_name) {
59
+ return theme;
990
60
  }
991
- export function getSelectListTheme() {
992
- return {
993
- selectedPrefix: (text) => theme.fg("accent", text),
994
- selectedText: (text) => theme.fg("accent", text),
995
- description: (text) => theme.fg("muted", text),
996
- scrollInfo: (text) => theme.fg("muted", text),
997
- noMatch: (text) => theme.fg("muted", text),
998
- };
61
+ export function getThemeForRgbColor(_r, _g, _b) {
62
+ return "dark";
999
63
  }
1000
- export function getEditorTheme() {
1001
- return {
1002
- borderColor: (text) => theme.fg("borderMuted", text),
1003
- selectList: getSelectListTheme(),
1004
- };
1005
- }
1006
- export function getSettingsListTheme() {
1007
- return {
1008
- label: (text, selected) => (selected ? theme.fg("accent", text) : text),
1009
- value: (text, selected) => (selected ? theme.fg("accent", text) : theme.fg("muted", text)),
1010
- description: (text) => theme.fg("dim", text),
1011
- cursor: theme.fg("accent", "→ "),
1012
- hint: (text) => theme.fg("dim", text),
1013
- };
64
+ export function parseOsc11BackgroundColor(_value) {
65
+ return undefined;
1014
66
  }
1015
67
  //# sourceMappingURL=theme.js.map