@oh-my-pi/pi-coding-agent 3.14.0 → 3.15.1

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 (213) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/docs/theme.md +38 -5
  3. package/examples/sdk/11-sessions.ts +2 -2
  4. package/package.json +7 -4
  5. package/src/cli/file-processor.ts +51 -2
  6. package/src/cli/plugin-cli.ts +25 -19
  7. package/src/cli/update-cli.ts +4 -3
  8. package/src/core/agent-session.ts +31 -4
  9. package/src/core/compaction/branch-summarization.ts +4 -32
  10. package/src/core/compaction/compaction.ts +6 -84
  11. package/src/core/compaction/utils.ts +2 -3
  12. package/src/core/custom-tools/types.ts +2 -0
  13. package/src/core/export-html/index.ts +1 -1
  14. package/src/core/hooks/tool-wrapper.ts +0 -1
  15. package/src/core/hooks/types.ts +2 -2
  16. package/src/core/plugins/doctor.ts +9 -1
  17. package/src/core/sdk.ts +2 -1
  18. package/src/core/session-manager.ts +518 -40
  19. package/src/core/settings-manager.ts +174 -0
  20. package/src/core/system-prompt.ts +9 -14
  21. package/src/core/title-generator.ts +2 -8
  22. package/src/core/tools/ask.ts +19 -37
  23. package/src/core/tools/bash.ts +2 -37
  24. package/src/core/tools/edit.ts +2 -9
  25. package/src/core/tools/exa/render.ts +52 -48
  26. package/src/core/tools/find.ts +10 -8
  27. package/src/core/tools/grep.ts +45 -17
  28. package/src/core/tools/ls.ts +22 -2
  29. package/src/core/tools/lsp/clients/biome-client.ts +207 -0
  30. package/src/core/tools/lsp/clients/index.ts +49 -0
  31. package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
  32. package/src/core/tools/lsp/config.ts +3 -0
  33. package/src/core/tools/lsp/index.ts +107 -55
  34. package/src/core/tools/lsp/render.ts +192 -79
  35. package/src/core/tools/lsp/types.ts +27 -0
  36. package/src/core/tools/lsp/utils.ts +62 -22
  37. package/src/core/tools/notebook.ts +9 -1
  38. package/src/core/tools/output.ts +37 -14
  39. package/src/core/tools/read.ts +349 -34
  40. package/src/core/tools/renderers.ts +290 -89
  41. package/src/core/tools/review.ts +12 -5
  42. package/src/core/tools/task/agents.ts +5 -5
  43. package/src/core/tools/task/commands.ts +3 -3
  44. package/src/core/tools/task/executor.ts +33 -1
  45. package/src/core/tools/task/index.ts +93 -6
  46. package/src/core/tools/task/render.ts +147 -66
  47. package/src/core/tools/task/types.ts +14 -9
  48. package/src/core/tools/web-fetch.ts +242 -103
  49. package/src/core/tools/web-search/index.ts +64 -20
  50. package/src/core/tools/web-search/providers/exa.ts +68 -172
  51. package/src/core/tools/web-search/render.ts +264 -74
  52. package/src/core/tools/write.ts +2 -8
  53. package/src/main.ts +10 -6
  54. package/src/modes/cleanup.ts +23 -0
  55. package/src/modes/index.ts +9 -4
  56. package/src/modes/interactive/components/bash-execution.ts +6 -3
  57. package/src/modes/interactive/components/branch-summary-message.ts +1 -1
  58. package/src/modes/interactive/components/compaction-summary-message.ts +1 -1
  59. package/src/modes/interactive/components/dynamic-border.ts +1 -1
  60. package/src/modes/interactive/components/extensions/extension-dashboard.ts +4 -5
  61. package/src/modes/interactive/components/extensions/extension-list.ts +18 -16
  62. package/src/modes/interactive/components/extensions/inspector-panel.ts +8 -8
  63. package/src/modes/interactive/components/hook-editor.ts +1 -0
  64. package/src/modes/interactive/components/hook-message.ts +2 -2
  65. package/src/modes/interactive/components/hook-selector.ts +1 -1
  66. package/src/modes/interactive/components/model-selector.ts +22 -9
  67. package/src/modes/interactive/components/oauth-selector.ts +20 -4
  68. package/src/modes/interactive/components/plugin-settings.ts +4 -2
  69. package/src/modes/interactive/components/session-selector.ts +9 -6
  70. package/src/modes/interactive/components/settings-defs.ts +285 -1
  71. package/src/modes/interactive/components/settings-selector.ts +176 -3
  72. package/src/modes/interactive/components/status-line/index.ts +4 -0
  73. package/src/modes/interactive/components/status-line/presets.ts +94 -0
  74. package/src/modes/interactive/components/status-line/segments.ts +350 -0
  75. package/src/modes/interactive/components/status-line/separators.ts +55 -0
  76. package/src/modes/interactive/components/status-line/types.ts +81 -0
  77. package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
  78. package/src/modes/interactive/components/status-line.ts +172 -223
  79. package/src/modes/interactive/components/tool-execution.ts +446 -211
  80. package/src/modes/interactive/components/tree-selector.ts +17 -6
  81. package/src/modes/interactive/components/ttsr-notification.ts +4 -4
  82. package/src/modes/interactive/components/welcome.ts +27 -19
  83. package/src/modes/interactive/interactive-mode.ts +99 -13
  84. package/src/modes/interactive/theme/dark.json +3 -2
  85. package/src/modes/interactive/theme/defaults/alabaster.json +99 -0
  86. package/src/modes/interactive/theme/defaults/amethyst.json +103 -0
  87. package/src/modes/interactive/theme/defaults/anthracite.json +100 -0
  88. package/src/modes/interactive/theme/defaults/basalt.json +90 -0
  89. package/src/modes/interactive/theme/defaults/birch.json +101 -0
  90. package/src/modes/interactive/theme/defaults/dark-abyss.json +97 -0
  91. package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
  92. package/src/modes/interactive/theme/defaults/dark-aurora.json +94 -0
  93. package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
  94. package/src/modes/interactive/theme/defaults/dark-cavern.json +97 -0
  95. package/src/modes/interactive/theme/defaults/dark-copper.json +94 -0
  96. package/src/modes/interactive/theme/defaults/dark-cosmos.json +96 -0
  97. package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
  98. package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
  99. package/src/modes/interactive/theme/defaults/dark-eclipse.json +97 -0
  100. package/src/modes/interactive/theme/defaults/dark-ember.json +94 -0
  101. package/src/modes/interactive/theme/defaults/dark-equinox.json +96 -0
  102. package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
  103. package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
  104. package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
  105. package/src/modes/interactive/theme/defaults/dark-lavender.json +94 -0
  106. package/src/modes/interactive/theme/defaults/dark-lunar.json +95 -0
  107. package/src/modes/interactive/theme/defaults/dark-midnight.json +94 -0
  108. package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
  109. package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
  110. package/src/modes/interactive/theme/defaults/dark-nebula.json +96 -0
  111. package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
  112. package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
  113. package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
  114. package/src/modes/interactive/theme/defaults/dark-rainforest.json +97 -0
  115. package/src/modes/interactive/theme/defaults/dark-reef.json +97 -0
  116. package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
  117. package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
  118. package/src/modes/interactive/theme/defaults/dark-sakura.json +94 -0
  119. package/src/modes/interactive/theme/defaults/dark-slate.json +94 -0
  120. package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
  121. package/src/modes/interactive/theme/defaults/dark-solstice.json +96 -0
  122. package/src/modes/interactive/theme/defaults/dark-starfall.json +97 -0
  123. package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
  124. package/src/modes/interactive/theme/defaults/dark-swamp.json +96 -0
  125. package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
  126. package/src/modes/interactive/theme/defaults/dark-taiga.json +97 -0
  127. package/src/modes/interactive/theme/defaults/dark-terminal.json +94 -0
  128. package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
  129. package/src/modes/interactive/theme/defaults/dark-tundra.json +97 -0
  130. package/src/modes/interactive/theme/defaults/dark-twilight.json +97 -0
  131. package/src/modes/interactive/theme/defaults/dark-volcanic.json +97 -0
  132. package/src/modes/interactive/theme/defaults/graphite.json +99 -0
  133. package/src/modes/interactive/theme/defaults/index.ts +195 -0
  134. package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
  135. package/src/modes/interactive/theme/defaults/light-aurora-day.json +97 -0
  136. package/src/modes/interactive/theme/defaults/light-canyon.json +97 -0
  137. package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
  138. package/src/modes/interactive/theme/defaults/light-cirrus.json +96 -0
  139. package/src/modes/interactive/theme/defaults/light-coral.json +94 -0
  140. package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
  141. package/src/modes/interactive/theme/defaults/light-dawn.json +96 -0
  142. package/src/modes/interactive/theme/defaults/light-dunes.json +97 -0
  143. package/src/modes/interactive/theme/defaults/light-eucalyptus.json +94 -0
  144. package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
  145. package/src/modes/interactive/theme/defaults/light-frost.json +94 -0
  146. package/src/modes/interactive/theme/defaults/light-github.json +114 -0
  147. package/src/modes/interactive/theme/defaults/light-glacier.json +97 -0
  148. package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
  149. package/src/modes/interactive/theme/defaults/light-haze.json +96 -0
  150. package/src/modes/interactive/theme/defaults/light-honeycomb.json +94 -0
  151. package/src/modes/interactive/theme/defaults/light-lagoon.json +97 -0
  152. package/src/modes/interactive/theme/defaults/light-lavender.json +94 -0
  153. package/src/modes/interactive/theme/defaults/light-meadow.json +97 -0
  154. package/src/modes/interactive/theme/defaults/light-mint.json +94 -0
  155. package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
  156. package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
  157. package/src/modes/interactive/theme/defaults/light-one.json +105 -0
  158. package/src/modes/interactive/theme/defaults/light-opal.json +97 -0
  159. package/src/modes/interactive/theme/defaults/light-orchard.json +97 -0
  160. package/src/modes/interactive/theme/defaults/light-paper.json +94 -0
  161. package/src/modes/interactive/theme/defaults/light-prism.json +96 -0
  162. package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
  163. package/src/modes/interactive/theme/defaults/light-sand.json +94 -0
  164. package/src/modes/interactive/theme/defaults/light-savanna.json +97 -0
  165. package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
  166. package/src/modes/interactive/theme/defaults/light-soleil.json +96 -0
  167. package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
  168. package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
  169. package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
  170. package/src/modes/interactive/theme/defaults/light-wetland.json +97 -0
  171. package/src/modes/interactive/theme/defaults/light-zenith.json +95 -0
  172. package/src/modes/interactive/theme/defaults/limestone.json +100 -0
  173. package/src/modes/interactive/theme/defaults/mahogany.json +104 -0
  174. package/src/modes/interactive/theme/defaults/marble.json +99 -0
  175. package/src/modes/interactive/theme/defaults/obsidian.json +90 -0
  176. package/src/modes/interactive/theme/defaults/onyx.json +90 -0
  177. package/src/modes/interactive/theme/defaults/pearl.json +99 -0
  178. package/src/modes/interactive/theme/defaults/porcelain.json +90 -0
  179. package/src/modes/interactive/theme/defaults/quartz.json +102 -0
  180. package/src/modes/interactive/theme/defaults/sandstone.json +101 -0
  181. package/src/modes/interactive/theme/defaults/titanium.json +89 -0
  182. package/src/modes/interactive/theme/light.json +3 -2
  183. package/src/modes/interactive/theme/theme-schema.json +120 -4
  184. package/src/modes/interactive/theme/theme.ts +1228 -14
  185. package/src/prompts/branch-summary-preamble.md +3 -0
  186. package/src/prompts/branch-summary.md +28 -0
  187. package/src/prompts/compaction-summary.md +34 -0
  188. package/src/prompts/compaction-turn-prefix.md +16 -0
  189. package/src/prompts/compaction-update-summary.md +41 -0
  190. package/src/prompts/init.md +30 -0
  191. package/src/{core/tools/task/bundled-agents → prompts}/reviewer.md +6 -0
  192. package/src/prompts/summarization-system.md +3 -0
  193. package/src/prompts/system-prompt.md +27 -0
  194. package/src/{core/tools/task/bundled-agents → prompts}/task.md +2 -0
  195. package/src/prompts/title-system.md +8 -0
  196. package/src/prompts/tools/ask.md +24 -0
  197. package/src/prompts/tools/bash.md +23 -0
  198. package/src/prompts/tools/edit.md +9 -0
  199. package/src/prompts/tools/find.md +6 -0
  200. package/src/prompts/tools/grep.md +12 -0
  201. package/src/prompts/tools/lsp.md +14 -0
  202. package/src/prompts/tools/output.md +23 -0
  203. package/src/prompts/tools/read.md +25 -0
  204. package/src/prompts/tools/web-fetch.md +8 -0
  205. package/src/prompts/tools/web-search.md +10 -0
  206. package/src/prompts/tools/write.md +10 -0
  207. package/src/commands/init.md +0 -20
  208. /package/src/{core/tools/task/bundled-commands → prompts}/architect-plan.md +0 -0
  209. /package/src/{core/tools/task/bundled-agents → prompts}/browser.md +0 -0
  210. /package/src/{core/tools/task/bundled-agents → prompts}/explore.md +0 -0
  211. /package/src/{core/tools/task/bundled-commands → prompts}/implement-with-critic.md +0 -0
  212. /package/src/{core/tools/task/bundled-commands → prompts}/implement.md +0 -0
  213. /package/src/{core/tools/task/bundled-agents → prompts}/plan.md +0 -0
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { EditorTheme, MarkdownTheme, SelectListTheme } from "@oh-my-pi/pi-tui";
3
+ import type { EditorTheme, MarkdownTheme, SelectListTheme, SymbolTheme } from "@oh-my-pi/pi-tui";
4
4
  import { type Static, Type } from "@sinclair/typebox";
5
5
  import { TypeCompiler } from "@sinclair/typebox/compiler";
6
6
  import chalk from "chalk";
@@ -9,8 +9,810 @@ import { getCustomThemesDir } from "../../../config";
9
9
  import { logger } from "../../../core/logger";
10
10
  // Embed theme JSON files at build time
11
11
  import darkThemeJson from "./dark.json" with { type: "json" };
12
+ import { defaultThemes } from "./defaults";
12
13
  import lightThemeJson from "./light.json" with { type: "json" };
13
14
 
15
+ // ============================================================================
16
+ // Symbol Presets
17
+ // ============================================================================
18
+
19
+ export type SymbolPreset = "unicode" | "nerd" | "ascii";
20
+
21
+ /**
22
+ * All available symbol keys organized by category.
23
+ */
24
+ export type SymbolKey =
25
+ // Status Indicators
26
+ | "status.success"
27
+ | "status.error"
28
+ | "status.warning"
29
+ | "status.info"
30
+ | "status.pending"
31
+ | "status.disabled"
32
+ | "status.enabled"
33
+ | "status.running"
34
+ | "status.shadowed"
35
+ | "status.aborted"
36
+ // Navigation
37
+ | "nav.cursor"
38
+ | "nav.selected"
39
+ | "nav.expand"
40
+ | "nav.collapse"
41
+ | "nav.back"
42
+ // Tree Connectors
43
+ | "tree.branch"
44
+ | "tree.last"
45
+ | "tree.vertical"
46
+ | "tree.horizontal"
47
+ | "tree.hook"
48
+ // Box Drawing - Rounded
49
+ | "boxRound.topLeft"
50
+ | "boxRound.topRight"
51
+ | "boxRound.bottomLeft"
52
+ | "boxRound.bottomRight"
53
+ | "boxRound.horizontal"
54
+ | "boxRound.vertical"
55
+ // Box Drawing - Sharp
56
+ | "boxSharp.topLeft"
57
+ | "boxSharp.topRight"
58
+ | "boxSharp.bottomLeft"
59
+ | "boxSharp.bottomRight"
60
+ | "boxSharp.horizontal"
61
+ | "boxSharp.vertical"
62
+ | "boxSharp.cross"
63
+ | "boxSharp.teeDown"
64
+ | "boxSharp.teeUp"
65
+ | "boxSharp.teeRight"
66
+ | "boxSharp.teeLeft"
67
+ // Separators
68
+ | "sep.powerline"
69
+ | "sep.powerlineThin"
70
+ | "sep.powerlineLeft"
71
+ | "sep.powerlineRight"
72
+ | "sep.powerlineThinLeft"
73
+ | "sep.powerlineThinRight"
74
+ | "sep.block"
75
+ | "sep.space"
76
+ | "sep.asciiLeft"
77
+ | "sep.asciiRight"
78
+ | "sep.dot"
79
+ | "sep.slash"
80
+ | "sep.pipe"
81
+ // Icons
82
+ | "icon.model"
83
+ | "icon.folder"
84
+ | "icon.file"
85
+ | "icon.git"
86
+ | "icon.branch"
87
+ | "icon.tokens"
88
+ | "icon.context"
89
+ | "icon.cost"
90
+ | "icon.time"
91
+ | "icon.pi"
92
+ | "icon.agents"
93
+ | "icon.cache"
94
+ | "icon.input"
95
+ | "icon.output"
96
+ | "icon.host"
97
+ | "icon.session"
98
+ | "icon.package"
99
+ | "icon.warning"
100
+ | "icon.rewind"
101
+ | "icon.auto"
102
+ | "icon.extensionSkill"
103
+ | "icon.extensionTool"
104
+ | "icon.extensionSlashCommand"
105
+ | "icon.extensionMcp"
106
+ | "icon.extensionRule"
107
+ | "icon.extensionHook"
108
+ | "icon.extensionPrompt"
109
+ | "icon.extensionContextFile"
110
+ | "icon.extensionInstruction"
111
+ // Thinking Levels
112
+ | "thinking.minimal"
113
+ | "thinking.low"
114
+ | "thinking.medium"
115
+ | "thinking.high"
116
+ | "thinking.xhigh"
117
+ // Checkboxes
118
+ | "checkbox.checked"
119
+ | "checkbox.unchecked"
120
+ // Text Formatting
121
+ | "format.ellipsis"
122
+ | "format.bullet"
123
+ | "format.dash"
124
+ | "format.bracketLeft"
125
+ | "format.bracketRight"
126
+ // Markdown-specific
127
+ | "md.quoteBorder"
128
+ | "md.hrChar"
129
+ | "md.bullet"
130
+ // Language/file type icons
131
+ | "lang.default"
132
+ | "lang.typescript"
133
+ | "lang.javascript"
134
+ | "lang.python"
135
+ | "lang.rust"
136
+ | "lang.go"
137
+ | "lang.java"
138
+ | "lang.c"
139
+ | "lang.cpp"
140
+ | "lang.csharp"
141
+ | "lang.ruby"
142
+ | "lang.php"
143
+ | "lang.swift"
144
+ | "lang.kotlin"
145
+ | "lang.shell"
146
+ | "lang.html"
147
+ | "lang.css"
148
+ | "lang.json"
149
+ | "lang.yaml"
150
+ | "lang.markdown"
151
+ | "lang.sql"
152
+ | "lang.docker"
153
+ | "lang.lua"
154
+ | "lang.text"
155
+ | "lang.env"
156
+ | "lang.toml"
157
+ | "lang.xml"
158
+ | "lang.ini"
159
+ | "lang.conf"
160
+ | "lang.log"
161
+ | "lang.csv"
162
+ | "lang.tsv"
163
+ | "lang.image"
164
+ | "lang.pdf"
165
+ | "lang.archive"
166
+ | "lang.binary";
167
+
168
+ type SymbolMap = Record<SymbolKey, string>;
169
+
170
+ const UNICODE_SYMBOLS: SymbolMap = {
171
+ // Status Indicators
172
+ // pick: ✓ | alt: ✔ ✅ ☑ ✚
173
+ "status.success": "✓",
174
+ // pick: ✗ | alt: ✘ ✖ ❌ ⨯
175
+ "status.error": "✗",
176
+ // pick: ⚠ | alt: ‼ ⁉ ▲ △
177
+ "status.warning": "⚠",
178
+ // pick: ℹ | alt: ⓘ 🛈 ⅈ
179
+ "status.info": "ℹ",
180
+ // pick: ◔ | alt: ● ◐ ◑ ◒ ◓ ⏳ …
181
+ "status.pending": "◔",
182
+ // pick: ○ | alt: ◌ ◯ ⃠
183
+ "status.disabled": "○",
184
+ // pick: ● | alt: ◉ ◎ ⬤
185
+ "status.enabled": "●",
186
+ // pick: ↻ | alt: ↺ ⟳ ⟲ ◐ ▶
187
+ "status.running": "↻",
188
+ // pick: ◐ | alt: ◑ ◒ ◓ ◔
189
+ "status.shadowed": "◐",
190
+ // pick: ⊗ | alt: ⊘ ⛔ ⏹ ⨂
191
+ "status.aborted": "⊗",
192
+ // Navigation
193
+ // pick: ❯ | alt: › ▸ ▹
194
+ "nav.cursor": "❯",
195
+ // pick: ➜ | alt: → ➤ ➔ ⇒
196
+ "nav.selected": "➜",
197
+ // pick: ▸ | alt: ▶ ▹ ⯈
198
+ "nav.expand": "▸",
199
+ // pick: ▾ | alt: ▼ ▽ ⯆
200
+ "nav.collapse": "▾",
201
+ // pick: ← | alt: ↩ ↫ ⇦
202
+ "nav.back": "←",
203
+ // Tree Connectors
204
+ // pick: ├─ | alt: ├╴ ├╌ ├┄ ╠═
205
+ "tree.branch": "├─",
206
+ // pick: └─ | alt: └╴ └╌ └┄ ╚═
207
+ "tree.last": "└─",
208
+ // pick: │ | alt: ┃ ║ ▏ ▕
209
+ "tree.vertical": "│",
210
+ // pick: ─ | alt: ━ ═ ╌ ┄
211
+ "tree.horizontal": "─",
212
+ // pick: ⎿ | alt: └ ╰ ↳
213
+ "tree.hook": "⎿",
214
+ // Box Drawing - Rounded
215
+ // pick: ╭ | alt: ┌ ┏ ╔
216
+ "boxRound.topLeft": "╭",
217
+ // pick: ╮ | alt: ┐ ┓ ╗
218
+ "boxRound.topRight": "╮",
219
+ // pick: ╰ | alt: └ ┗ ╚
220
+ "boxRound.bottomLeft": "╰",
221
+ // pick: ╯ | alt: ┘ ┛ ╝
222
+ "boxRound.bottomRight": "╯",
223
+ // pick: ─ | alt: ━ ═ ╌
224
+ "boxRound.horizontal": "─",
225
+ // pick: │ | alt: ┃ ║ ▏
226
+ "boxRound.vertical": "│",
227
+ // Box Drawing - Sharp
228
+ // pick: ┌ | alt: ┏ ╭ ╔
229
+ "boxSharp.topLeft": "┌",
230
+ // pick: ┐ | alt: ┓ ╮ ╗
231
+ "boxSharp.topRight": "┐",
232
+ // pick: └ | alt: ┗ ╰ ╚
233
+ "boxSharp.bottomLeft": "└",
234
+ // pick: ┘ | alt: ┛ ╯ ╝
235
+ "boxSharp.bottomRight": "┘",
236
+ // pick: ─ | alt: ━ ═ ╌
237
+ "boxSharp.horizontal": "─",
238
+ // pick: │ | alt: ┃ ║ ▏
239
+ "boxSharp.vertical": "│",
240
+ // pick: ┼ | alt: ╋ ╬ ┿
241
+ "boxSharp.cross": "┼",
242
+ // pick: ┬ | alt: ╦ ┯ ┳
243
+ "boxSharp.teeDown": "┬",
244
+ // pick: ┴ | alt: ╩ ┷ ┻
245
+ "boxSharp.teeUp": "┴",
246
+ // pick: ├ | alt: ╠ ┝ ┣
247
+ "boxSharp.teeRight": "├",
248
+ // pick: ┤ | alt: ╣ ┥ ┫
249
+ "boxSharp.teeLeft": "┤",
250
+ // Separators
251
+ // pick: │ | alt: ┃ ║ ▏
252
+ "sep.powerline": "│",
253
+ // pick: │ | alt: ┆ ┊
254
+ "sep.powerlineThin": "│",
255
+ // pick: > | alt: › ▸ »
256
+ "sep.powerlineLeft": ">",
257
+ // pick: < | alt: ‹ ◂ «
258
+ "sep.powerlineRight": "<",
259
+ // pick: > | alt: › ▸
260
+ "sep.powerlineThinLeft": ">",
261
+ // pick: < | alt: ‹ ◂
262
+ "sep.powerlineThinRight": "<",
263
+ // pick: █ | alt: ▓ ▒ ░ ▉ ▌
264
+ "sep.block": "█",
265
+ // pick: space | alt: ␠ ·
266
+ "sep.space": " ",
267
+ // pick: > | alt: › » ▸
268
+ "sep.asciiLeft": ">",
269
+ // pick: < | alt: ‹ « ◂
270
+ "sep.asciiRight": "<",
271
+ // pick: · | alt: • ⋅
272
+ "sep.dot": " · ",
273
+ // pick: / | alt: / ∕ ⁄
274
+ "sep.slash": " / ",
275
+ // pick: | | alt: │ ┃ ║
276
+ "sep.pipe": " | ",
277
+ // Icons
278
+ // pick: ◈ | alt: ◆ ⬢ ◇
279
+ "icon.model": "◈",
280
+ // pick: 📁 | alt: 📂 🗂 🗃
281
+ "icon.folder": "📁",
282
+ // pick: 📄 | alt: 📃 📝
283
+ "icon.file": "📄",
284
+ // pick: ⎇ | alt: 🔀 ⑂
285
+ "icon.git": "⎇",
286
+ // pick: ⎇ | alt: 🌿 ⑂
287
+ "icon.branch": "⎇",
288
+ // pick: ⊛ | alt: ◎ ◍ ⊙
289
+ "icon.tokens": "⊛",
290
+ // pick: ◫ | alt: ◧ ▣ ▦
291
+ "icon.context": "◫",
292
+ // pick: $ | alt: 💲 💰
293
+ "icon.cost": "$",
294
+ // pick: ◷ | alt: ⏱ ⏲ ⌛
295
+ "icon.time": "◷",
296
+ // pick: π | alt: ∏ ∑
297
+ "icon.pi": "π",
298
+ // pick: AG | alt: 👥 👤
299
+ "icon.agents": "AG",
300
+ // pick: cache | alt: 💾 🗄
301
+ "icon.cache": "cache",
302
+ // pick: in: | alt: ⤵ ↲
303
+ "icon.input": "in:",
304
+ // pick: out: | alt: ⤴ ↱
305
+ "icon.output": "out:",
306
+ // pick: host | alt: 🖥 💻
307
+ "icon.host": "host",
308
+ // pick: id | alt: 🧭 🧩
309
+ "icon.session": "id",
310
+ // pick: 📦 | alt: 🧰
311
+ "icon.package": "📦",
312
+ // pick: ⚠ | alt: ❗
313
+ "icon.warning": "⚠",
314
+ // pick: ↩ | alt: ↺ ⟲
315
+ "icon.rewind": "↩",
316
+ // pick: ⚡ | alt: ✨ ✦
317
+ "icon.auto": "⚡",
318
+ // pick: SK | alt: 🧠 🎓
319
+ "icon.extensionSkill": "SK",
320
+ // pick: TL | alt: 🛠 ⚙
321
+ "icon.extensionTool": "TL",
322
+ // pick: / | alt: ⌘ ⌥
323
+ "icon.extensionSlashCommand": "/",
324
+ // pick: MCP | alt: 🔌 🧩
325
+ "icon.extensionMcp": "MCP",
326
+ // pick: RL | alt: ⚖ 📏
327
+ "icon.extensionRule": "RL",
328
+ // pick: HK | alt: 🪝 ⚓
329
+ "icon.extensionHook": "HK",
330
+ // pick: PR | alt: 💬 ✎
331
+ "icon.extensionPrompt": "PR",
332
+ // pick: CF | alt: 📄 📎
333
+ "icon.extensionContextFile": "CF",
334
+ // pick: IN | alt: 📘 ℹ
335
+ "icon.extensionInstruction": "IN",
336
+ // Thinking Levels
337
+ // pick: [min] | alt: · ◔ min
338
+ "thinking.minimal": "[min]",
339
+ // pick: [low] | alt: ◑ low ▪ low
340
+ "thinking.low": "[low]",
341
+ // pick: [med] | alt: ◒ med ▪ med
342
+ "thinking.medium": "[med]",
343
+ // pick: [high] | alt: ◕ high ▪ high
344
+ "thinking.high": "[high]",
345
+ // pick: [xhi] | alt: ◉ xhi ▪ xhi
346
+ "thinking.xhigh": "[xhi]",
347
+ // Checkboxes
348
+ // pick: ☑ | alt: ✓ ✔ ✅
349
+ "checkbox.checked": "☑",
350
+ // pick: ☐ | alt: □ ▢
351
+ "checkbox.unchecked": "☐",
352
+ // Text Formatting
353
+ // pick: … | alt: ⋯ ...
354
+ "format.ellipsis": "…",
355
+ // pick: • | alt: · ▪ ◦
356
+ "format.bullet": "•",
357
+ // pick: – | alt: — ― -
358
+ "format.dash": "–",
359
+ // pick: [ | alt: ⟦ ⟨
360
+ "format.bracketLeft": "[",
361
+ // pick: ] | alt: ⟧ ⟩
362
+ "format.bracketRight": "]",
363
+ // Markdown-specific
364
+ // pick: │ | alt: ┃ ║
365
+ "md.quoteBorder": "│",
366
+ // pick: ─ | alt: ━ ═
367
+ "md.hrChar": "─",
368
+ // pick: • | alt: · ▪ ◦
369
+ "md.bullet": "•",
370
+ // Language icons (unicode uses code symbol prefix)
371
+ "lang.default": "❖",
372
+ "lang.typescript": "❖ ts",
373
+ "lang.javascript": "❖ js",
374
+ "lang.python": "❖ py",
375
+ "lang.rust": "❖ rs",
376
+ "lang.go": "❖ go",
377
+ "lang.java": "❖ java",
378
+ "lang.c": "❖ c",
379
+ "lang.cpp": "❖ c++",
380
+ "lang.csharp": "❖ c#",
381
+ "lang.ruby": "❖ rb",
382
+ "lang.php": "❖ php",
383
+ "lang.swift": "❖ swift",
384
+ "lang.kotlin": "❖ kt",
385
+ "lang.shell": "❖ sh",
386
+ "lang.html": "❖ html",
387
+ "lang.css": "❖ css",
388
+ "lang.json": "❖ json",
389
+ "lang.yaml": "❖ yaml",
390
+ "lang.markdown": "❖ md",
391
+ "lang.sql": "❖ sql",
392
+ "lang.docker": "❖ docker",
393
+ "lang.lua": "❖ lua",
394
+ "lang.text": "❖ txt",
395
+ "lang.env": "❖ env",
396
+ "lang.toml": "❖ toml",
397
+ "lang.xml": "❖ xml",
398
+ "lang.ini": "❖ ini",
399
+ "lang.conf": "❖ conf",
400
+ "lang.log": "❖ log",
401
+ "lang.csv": "❖ csv",
402
+ "lang.tsv": "❖ tsv",
403
+ "lang.image": "❖ img",
404
+ "lang.pdf": "❖ pdf",
405
+ "lang.archive": "❖ zip",
406
+ "lang.binary": "❖ bin",
407
+ };
408
+
409
+ const NERD_SYMBOLS: SymbolMap = {
410
+ // Status Indicators
411
+ // pick:  | alt:   
412
+ "status.success": "\uf00c",
413
+ // pick:  | alt:   
414
+ "status.error": "\uf00d",
415
+ // pick:  | alt:  
416
+ "status.warning": "\uf12a",
417
+ // pick:  | alt: 
418
+ "status.info": "\uf129",
419
+ // pick:  | alt:   
420
+ "status.pending": "\uf254",
421
+ // pick:  | alt:  
422
+ "status.disabled": "\uf05e",
423
+ // pick:  | alt:  
424
+ "status.enabled": "\uf111",
425
+ // pick:  | alt:   
426
+ "status.running": "\uf110",
427
+ // pick: ◐ | alt: ◑ ◒ ◓ ◔
428
+ "status.shadowed": "◐",
429
+ // pick:  | alt:  
430
+ "status.aborted": "\uf04d",
431
+ // Navigation
432
+ // pick:  | alt:  
433
+ "nav.cursor": "\uf054",
434
+ // pick:  | alt:  
435
+ "nav.selected": "\uf178",
436
+ // pick:  | alt:  
437
+ "nav.expand": "\uf0da",
438
+ // pick:  | alt:  
439
+ "nav.collapse": "\uf0d7",
440
+ // pick:  | alt:  
441
+ "nav.back": "\uf060",
442
+ // Tree Connectors (same as unicode)
443
+ // pick: ├─ | alt: ├╴ ├╌ ╠═ ┣━
444
+ "tree.branch": "\u251c\u2500",
445
+ // pick: └─ | alt: └╴ └╌ ╚═ ┗━
446
+ "tree.last": "\u2514\u2500",
447
+ // pick: │ | alt: ┃ ║ ▏ ▕
448
+ "tree.vertical": "\u2502",
449
+ // pick: ─ | alt: ━ ═ ╌ ┄
450
+ "tree.horizontal": "\u2500",
451
+ // pick: └ | alt: ╰ ⎿ ↳
452
+ "tree.hook": "\u2514",
453
+ // Box Drawing - Rounded (same as unicode)
454
+ // pick: ╭ | alt: ┌ ┏ ╔
455
+ "boxRound.topLeft": "\u256d",
456
+ // pick: ╮ | alt: ┐ ┓ ╗
457
+ "boxRound.topRight": "\u256e",
458
+ // pick: ╰ | alt: └ ┗ ╚
459
+ "boxRound.bottomLeft": "\u2570",
460
+ // pick: ╯ | alt: ┘ ┛ ╝
461
+ "boxRound.bottomRight": "\u256f",
462
+ // pick: ─ | alt: ━ ═ ╌
463
+ "boxRound.horizontal": "\u2500",
464
+ // pick: │ | alt: ┃ ║ ▏
465
+ "boxRound.vertical": "\u2502",
466
+ // Box Drawing - Sharp (same as unicode)
467
+ // pick: ┌ | alt: ┏ ╭ ╔
468
+ "boxSharp.topLeft": "\u250c",
469
+ // pick: ┐ | alt: ┓ ╮ ╗
470
+ "boxSharp.topRight": "\u2510",
471
+ // pick: └ | alt: ┗ ╰ ╚
472
+ "boxSharp.bottomLeft": "\u2514",
473
+ // pick: ┘ | alt: ┛ ╯ ╝
474
+ "boxSharp.bottomRight": "\u2518",
475
+ // pick: ─ | alt: ━ ═ ╌
476
+ "boxSharp.horizontal": "\u2500",
477
+ // pick: │ | alt: ┃ ║ ▏
478
+ "boxSharp.vertical": "\u2502",
479
+ // pick: ┼ | alt: ╋ ╬ ┿
480
+ "boxSharp.cross": "\u253c",
481
+ // pick: ┬ | alt: ╦ ┯ ┳
482
+ "boxSharp.teeDown": "\u252c",
483
+ // pick: ┴ | alt: ╩ ┷ ┻
484
+ "boxSharp.teeUp": "\u2534",
485
+ // pick: ├ | alt: ╠ ┝ ┣
486
+ "boxSharp.teeRight": "\u251c",
487
+ // pick: ┤ | alt: ╣ ┥ ┫
488
+ "boxSharp.teeLeft": "\u2524",
489
+ // Separators - Nerd Font specific
490
+ // pick:  | alt:   
491
+ "sep.powerline": "\ue0b0",
492
+ // pick:  | alt:  
493
+ "sep.powerlineThin": "\ue0b1",
494
+ // pick:  | alt:  
495
+ "sep.powerlineLeft": "\ue0b0",
496
+ // pick:  | alt:  
497
+ "sep.powerlineRight": "\ue0b2",
498
+ // pick:  | alt: 
499
+ "sep.powerlineThinLeft": "\ue0b1",
500
+ // pick:  | alt: 
501
+ "sep.powerlineThinRight": "\ue0b3",
502
+ // pick: █ | alt: ▓ ▒ ░ ▉ ▌
503
+ "sep.block": "\u2588",
504
+ // pick: space | alt: ␠ ·
505
+ "sep.space": " ",
506
+ // pick: > | alt: › » ▸
507
+ "sep.asciiLeft": ">",
508
+ // pick: < | alt: ‹ « ◂
509
+ "sep.asciiRight": "<",
510
+ // pick: · | alt: • ⋅
511
+ "sep.dot": " \u00b7 ",
512
+ // pick:  | alt: / ∕ ⁄
513
+ "sep.slash": "\ue0bb",
514
+ // pick:  | alt: │ ┃ |
515
+ "sep.pipe": "\ue0b3",
516
+ // Icons - Nerd Font specific
517
+ // pick:  | alt:   ◆
518
+ "icon.model": "\uec19",
519
+ // pick:  | alt:  
520
+ "icon.folder": "\uf115",
521
+ // pick:  | alt:  
522
+ "icon.file": "\uf15b",
523
+ // pick:  | alt:  ⎇
524
+ "icon.git": "\uf1d3",
525
+ // pick:  | alt:  ⎇
526
+ "icon.branch": "\uf126",
527
+ // pick:  | alt: ⊛ ◍ 
528
+ "icon.tokens": "\ue26b",
529
+ // pick:  | alt: ◫ ▦
530
+ "icon.context": "\ue70f",
531
+ // pick:  | alt: $ ¢
532
+ "icon.cost": "\uf155",
533
+ // pick:  | alt: ◷ ◴
534
+ "icon.time": "\uf017",
535
+ // pick:  | alt: π ∏ ∑
536
+ "icon.pi": "\ue22c",
537
+ // pick:  | alt: 
538
+ "icon.agents": "\uf0c0",
539
+ // pick:  | alt:  
540
+ "icon.cache": "\uf1c0",
541
+ // pick:  | alt:  →
542
+ "icon.input": "\uf090",
543
+ // pick:  | alt:  →
544
+ "icon.output": "\uf08b",
545
+ // pick:  | alt:  
546
+ "icon.host": "\uf109",
547
+ // pick:  | alt:  
548
+ "icon.session": "\uf550",
549
+ // pick:  | alt: 
550
+ "icon.package": "\uf487",
551
+ // pick:  | alt:  
552
+ "icon.warning": "\uf071",
553
+ // pick:  | alt:  ↺
554
+ "icon.rewind": "\uf0e2",
555
+ // pick: 󰁨 | alt:   
556
+ "icon.auto": "\u{f0068}",
557
+ // pick:  | alt:  
558
+ "icon.extensionSkill": "\uf0eb",
559
+ // pick:  | alt:  
560
+ "icon.extensionTool": "\uf0ad",
561
+ // pick:  | alt: 
562
+ "icon.extensionSlashCommand": "\uf120",
563
+ // pick:  | alt:  
564
+ "icon.extensionMcp": "\uf1e6",
565
+ // pick:  | alt:  
566
+ "icon.extensionRule": "\uf0e3",
567
+ // pick:  | alt: 
568
+ "icon.extensionHook": "\uf0c1",
569
+ // pick:  | alt:  
570
+ "icon.extensionPrompt": "\uf075",
571
+ // pick:  | alt:  
572
+ "icon.extensionContextFile": "\uf0f6",
573
+ // pick:  | alt:  
574
+ "icon.extensionInstruction": "\uf02d",
575
+ // Thinking Levels - emoji labels
576
+ // pick: 🤨 min | alt:  min  min
577
+ "thinking.minimal": "🤨 min",
578
+ // pick: 🤔 low | alt:  low  low
579
+ "thinking.low": "🤔 low",
580
+ // pick: 🤓 med | alt:  med  med
581
+ "thinking.medium": "🤓 med",
582
+ // pick: 🤯 high | alt:  high  high
583
+ "thinking.high": "🤯 high",
584
+ // pick: 🧠 xhi | alt:  xhi  xhi
585
+ "thinking.xhigh": "🧠 xhi",
586
+ // Checkboxes
587
+ // pick:  | alt:  
588
+ "checkbox.checked": "\uf14a",
589
+ // pick:  | alt: 
590
+ "checkbox.unchecked": "\uf096",
591
+ // Text Formatting
592
+ // pick: … | alt: ⋯ ...
593
+ "format.ellipsis": "\u2026",
594
+ // pick:  | alt:   •
595
+ "format.bullet": "\uf111",
596
+ // pick: – | alt: — ― -
597
+ "format.dash": "\u2013",
598
+ // pick: [ | alt: ⟦ ⟨
599
+ "format.bracketLeft": "[",
600
+ // pick: ] | alt: ⟧ ⟩
601
+ "format.bracketRight": "]",
602
+ // Markdown-specific
603
+ // pick: │ | alt: ┃ ║
604
+ "md.quoteBorder": "\u2502",
605
+ // pick: ─ | alt: ━ ═
606
+ "md.hrChar": "\u2500",
607
+ // pick:  | alt:  •
608
+ "md.bullet": "\uf111",
609
+ // Language icons (nerd font devicons)
610
+ "lang.default": "",
611
+ "lang.typescript": "",
612
+ "lang.javascript": "",
613
+ "lang.python": "",
614
+ "lang.rust": "",
615
+ "lang.go": "",
616
+ "lang.java": "",
617
+ "lang.c": "",
618
+ "lang.cpp": "",
619
+ "lang.csharp": "",
620
+ "lang.ruby": "",
621
+ "lang.php": "",
622
+ "lang.swift": "",
623
+ "lang.kotlin": "",
624
+ "lang.shell": "",
625
+ "lang.html": "",
626
+ "lang.css": "",
627
+ "lang.json": "",
628
+ "lang.yaml": "",
629
+ "lang.markdown": "",
630
+ "lang.sql": "",
631
+ "lang.docker": "",
632
+ "lang.lua": "",
633
+ "lang.text": "",
634
+ "lang.env": "",
635
+ "lang.toml": "",
636
+ "lang.xml": "󰗀",
637
+ "lang.ini": "",
638
+ "lang.conf": "",
639
+ "lang.log": "󰌱",
640
+ "lang.csv": "󰈛",
641
+ "lang.tsv": "󰈛",
642
+ "lang.image": "󰈟",
643
+ "lang.pdf": "󰈦",
644
+ "lang.archive": "",
645
+ "lang.binary": "󰆚",
646
+ };
647
+
648
+ const ASCII_SYMBOLS: SymbolMap = {
649
+ // Status Indicators
650
+ "status.success": "[ok]",
651
+ "status.error": "[!!]",
652
+ "status.warning": "[!]",
653
+ "status.info": "[i]",
654
+ "status.pending": "[*]",
655
+ "status.disabled": "[ ]",
656
+ "status.enabled": "[x]",
657
+ "status.running": "[~]",
658
+ "status.shadowed": "[/]",
659
+ "status.aborted": "[-]",
660
+ // Navigation
661
+ "nav.cursor": ">",
662
+ "nav.selected": "->",
663
+ "nav.expand": "+",
664
+ "nav.collapse": "-",
665
+ "nav.back": "<-",
666
+ // Tree Connectors
667
+ "tree.branch": "|--",
668
+ "tree.last": "'--",
669
+ "tree.vertical": "|",
670
+ "tree.horizontal": "-",
671
+ "tree.hook": "`-",
672
+ // Box Drawing - Rounded (ASCII fallback)
673
+ "boxRound.topLeft": "+",
674
+ "boxRound.topRight": "+",
675
+ "boxRound.bottomLeft": "+",
676
+ "boxRound.bottomRight": "+",
677
+ "boxRound.horizontal": "-",
678
+ "boxRound.vertical": "|",
679
+ // Box Drawing - Sharp (ASCII fallback)
680
+ "boxSharp.topLeft": "+",
681
+ "boxSharp.topRight": "+",
682
+ "boxSharp.bottomLeft": "+",
683
+ "boxSharp.bottomRight": "+",
684
+ "boxSharp.horizontal": "-",
685
+ "boxSharp.vertical": "|",
686
+ "boxSharp.cross": "+",
687
+ "boxSharp.teeDown": "+",
688
+ "boxSharp.teeUp": "+",
689
+ "boxSharp.teeRight": "+",
690
+ "boxSharp.teeLeft": "+",
691
+ // Separators
692
+ "sep.powerline": ">",
693
+ "sep.powerlineThin": ">",
694
+ "sep.powerlineLeft": ">",
695
+ "sep.powerlineRight": "<",
696
+ "sep.powerlineThinLeft": ">",
697
+ "sep.powerlineThinRight": "<",
698
+ "sep.block": "#",
699
+ "sep.space": " ",
700
+ "sep.asciiLeft": ">",
701
+ "sep.asciiRight": "<",
702
+ "sep.dot": " - ",
703
+ "sep.slash": " / ",
704
+ "sep.pipe": " | ",
705
+ // Icons
706
+ "icon.model": "[M]",
707
+ "icon.folder": "[D]",
708
+ "icon.file": "[F]",
709
+ "icon.git": "git:",
710
+ "icon.branch": "@",
711
+ "icon.tokens": "tok:",
712
+ "icon.context": "ctx:",
713
+ "icon.cost": "$",
714
+ "icon.time": "t:",
715
+ "icon.pi": "pi",
716
+ "icon.agents": "AG",
717
+ "icon.cache": "cache",
718
+ "icon.input": "in:",
719
+ "icon.output": "out:",
720
+ "icon.host": "host",
721
+ "icon.session": "id",
722
+ "icon.package": "[P]",
723
+ "icon.warning": "[!]",
724
+ "icon.rewind": "<-",
725
+ "icon.auto": "[A]",
726
+ "icon.extensionSkill": "SK",
727
+ "icon.extensionTool": "TL",
728
+ "icon.extensionSlashCommand": "/",
729
+ "icon.extensionMcp": "MCP",
730
+ "icon.extensionRule": "RL",
731
+ "icon.extensionHook": "HK",
732
+ "icon.extensionPrompt": "PR",
733
+ "icon.extensionContextFile": "CF",
734
+ "icon.extensionInstruction": "IN",
735
+ // Thinking Levels
736
+ "thinking.minimal": "[min]",
737
+ "thinking.low": "[low]",
738
+ "thinking.medium": "[med]",
739
+ "thinking.high": "[high]",
740
+ "thinking.xhigh": "[xhi]",
741
+ // Checkboxes
742
+ "checkbox.checked": "[x]",
743
+ "checkbox.unchecked": "[ ]",
744
+ // Text Formatting
745
+ "format.ellipsis": "...",
746
+ "format.bullet": "*",
747
+ "format.dash": "-",
748
+ "format.bracketLeft": "[",
749
+ "format.bracketRight": "]",
750
+ // Markdown-specific
751
+ "md.quoteBorder": "|",
752
+ "md.hrChar": "-",
753
+ "md.bullet": "*",
754
+ // Language icons (ASCII uses abbreviations)
755
+ "lang.default": "code",
756
+ "lang.typescript": "ts",
757
+ "lang.javascript": "js",
758
+ "lang.python": "py",
759
+ "lang.rust": "rs",
760
+ "lang.go": "go",
761
+ "lang.java": "java",
762
+ "lang.c": "c",
763
+ "lang.cpp": "cpp",
764
+ "lang.csharp": "cs",
765
+ "lang.ruby": "rb",
766
+ "lang.php": "php",
767
+ "lang.swift": "swift",
768
+ "lang.kotlin": "kt",
769
+ "lang.shell": "sh",
770
+ "lang.html": "html",
771
+ "lang.css": "css",
772
+ "lang.json": "json",
773
+ "lang.yaml": "yaml",
774
+ "lang.markdown": "md",
775
+ "lang.sql": "sql",
776
+ "lang.docker": "docker",
777
+ "lang.lua": "lua",
778
+ "lang.text": "txt",
779
+ "lang.env": "env",
780
+ "lang.toml": "toml",
781
+ "lang.xml": "xml",
782
+ "lang.ini": "ini",
783
+ "lang.conf": "conf",
784
+ "lang.log": "log",
785
+ "lang.csv": "csv",
786
+ "lang.tsv": "tsv",
787
+ "lang.image": "img",
788
+ "lang.pdf": "pdf",
789
+ "lang.archive": "zip",
790
+ "lang.binary": "bin",
791
+ };
792
+
793
+ const SYMBOL_PRESETS: Record<SymbolPreset, SymbolMap> = {
794
+ unicode: UNICODE_SYMBOLS,
795
+ nerd: NERD_SYMBOLS,
796
+ ascii: ASCII_SYMBOLS,
797
+ };
798
+
799
+ export type SpinnerType = "status" | "activity";
800
+
801
+ const SPINNER_FRAMES: Record<SymbolPreset, Record<SpinnerType, string[]>> = {
802
+ unicode: {
803
+ status: ["·", "•", "●", "•"],
804
+ activity: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
805
+ },
806
+ nerd: {
807
+ status: ["󰪥", "󰪤", "󰪣", "󰪢", "󰪡", "󰪠", "󰪟", "󰪞", "󰪥"],
808
+ activity: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
809
+ },
810
+ ascii: {
811
+ status: ["|", "/", "-", "\\"],
812
+ activity: ["-", "\\", "|", "/"],
813
+ },
814
+ };
815
+
14
816
  // ============================================================================
15
817
  // Types & Schema
16
818
  // ============================================================================
@@ -22,6 +824,15 @@ const ColorValueSchema = Type.Union([
22
824
 
23
825
  type ColorValue = Static<typeof ColorValueSchema>;
24
826
 
827
+ const SymbolPresetSchema = Type.Union([Type.Literal("unicode"), Type.Literal("nerd"), Type.Literal("ascii")]);
828
+
829
+ const SymbolsSchema = Type.Optional(
830
+ Type.Object({
831
+ preset: Type.Optional(SymbolPresetSchema),
832
+ overrides: Type.Optional(Type.Record(Type.String(), Type.String())),
833
+ }),
834
+ );
835
+
25
836
  const ThemeJsonSchema = Type.Object({
26
837
  $schema: Type.Optional(Type.String()),
27
838
  name: Type.String(),
@@ -99,6 +910,7 @@ const ThemeJsonSchema = Type.Object({
99
910
  statusLineUntracked: ColorValueSchema,
100
911
  statusLineOutput: ColorValueSchema,
101
912
  statusLineCost: ColorValueSchema,
913
+ statusLineSubagents: ColorValueSchema,
102
914
  }),
103
915
  export: Type.Optional(
104
916
  Type.Object({
@@ -107,6 +919,7 @@ const ThemeJsonSchema = Type.Object({
107
919
  infoBg: Type.Optional(ColorValueSchema),
108
920
  }),
109
921
  ),
922
+ symbols: SymbolsSchema,
110
923
  });
111
924
 
112
925
  type ThemeJson = Static<typeof ThemeJsonSchema>;
@@ -171,7 +984,8 @@ export type ThemeColor =
171
984
  | "statusLineDirty"
172
985
  | "statusLineUntracked"
173
986
  | "statusLineOutput"
174
- | "statusLineCost";
987
+ | "statusLineCost"
988
+ | "statusLineSubagents";
175
989
 
176
990
  export type ThemeBg =
177
991
  | "selectedBg"
@@ -359,17 +1173,112 @@ function resolveThemeColors<T extends Record<string, ColorValue>>(
359
1173
  // Theme Class
360
1174
  // ============================================================================
361
1175
 
1176
+ const langMap: Record<string, SymbolKey> = {
1177
+ typescript: "lang.typescript",
1178
+ ts: "lang.typescript",
1179
+ tsx: "lang.typescript",
1180
+ javascript: "lang.javascript",
1181
+ js: "lang.javascript",
1182
+ jsx: "lang.javascript",
1183
+ mjs: "lang.javascript",
1184
+ cjs: "lang.javascript",
1185
+ python: "lang.python",
1186
+ py: "lang.python",
1187
+ rust: "lang.rust",
1188
+ rs: "lang.rust",
1189
+ go: "lang.go",
1190
+ java: "lang.java",
1191
+ c: "lang.c",
1192
+ cpp: "lang.cpp",
1193
+ "c++": "lang.cpp",
1194
+ cc: "lang.cpp",
1195
+ cxx: "lang.cpp",
1196
+ csharp: "lang.csharp",
1197
+ cs: "lang.csharp",
1198
+ ruby: "lang.ruby",
1199
+ rb: "lang.ruby",
1200
+ php: "lang.php",
1201
+ swift: "lang.swift",
1202
+ kotlin: "lang.kotlin",
1203
+ kt: "lang.kotlin",
1204
+ bash: "lang.shell",
1205
+ sh: "lang.shell",
1206
+ zsh: "lang.shell",
1207
+ fish: "lang.shell",
1208
+ shell: "lang.shell",
1209
+ html: "lang.html",
1210
+ htm: "lang.html",
1211
+ css: "lang.css",
1212
+ scss: "lang.css",
1213
+ sass: "lang.css",
1214
+ less: "lang.css",
1215
+ json: "lang.json",
1216
+ yaml: "lang.yaml",
1217
+ yml: "lang.yaml",
1218
+ markdown: "lang.markdown",
1219
+ md: "lang.markdown",
1220
+ sql: "lang.sql",
1221
+ dockerfile: "lang.docker",
1222
+ docker: "lang.docker",
1223
+ lua: "lang.lua",
1224
+ text: "lang.text",
1225
+ txt: "lang.text",
1226
+ plain: "lang.text",
1227
+ log: "lang.log",
1228
+ env: "lang.env",
1229
+ dotenv: "lang.env",
1230
+ toml: "lang.toml",
1231
+ xml: "lang.xml",
1232
+ ini: "lang.ini",
1233
+ conf: "lang.conf",
1234
+ cfg: "lang.conf",
1235
+ config: "lang.conf",
1236
+ properties: "lang.conf",
1237
+ csv: "lang.csv",
1238
+ tsv: "lang.tsv",
1239
+ image: "lang.image",
1240
+ img: "lang.image",
1241
+ png: "lang.image",
1242
+ jpg: "lang.image",
1243
+ jpeg: "lang.image",
1244
+ gif: "lang.image",
1245
+ webp: "lang.image",
1246
+ svg: "lang.image",
1247
+ ico: "lang.image",
1248
+ bmp: "lang.image",
1249
+ tiff: "lang.image",
1250
+ pdf: "lang.pdf",
1251
+ zip: "lang.archive",
1252
+ tar: "lang.archive",
1253
+ gz: "lang.archive",
1254
+ tgz: "lang.archive",
1255
+ bz2: "lang.archive",
1256
+ xz: "lang.archive",
1257
+ "7z": "lang.archive",
1258
+ exe: "lang.binary",
1259
+ dll: "lang.binary",
1260
+ so: "lang.binary",
1261
+ dylib: "lang.binary",
1262
+ wasm: "lang.binary",
1263
+ bin: "lang.binary",
1264
+ };
1265
+
362
1266
  export class Theme {
363
1267
  private fgColors: Map<ThemeColor, string>;
364
1268
  private bgColors: Map<ThemeBg, string>;
365
1269
  private mode: ColorMode;
1270
+ private symbols: SymbolMap;
1271
+ private symbolPreset: SymbolPreset;
366
1272
 
367
1273
  constructor(
368
1274
  fgColors: Record<ThemeColor, string | number>,
369
1275
  bgColors: Record<ThemeBg, string | number>,
370
1276
  mode: ColorMode,
1277
+ symbolPreset: SymbolPreset = "unicode",
1278
+ symbolOverrides: Record<string, string> = {},
371
1279
  ) {
372
1280
  this.mode = mode;
1281
+ this.symbolPreset = symbolPreset;
373
1282
  this.fgColors = new Map();
374
1283
  for (const [key, value] of Object.entries(fgColors) as [ThemeColor, string | number][]) {
375
1284
  this.fgColors.set(key, fgAnsi(value, mode));
@@ -378,6 +1287,16 @@ export class Theme {
378
1287
  for (const [key, value] of Object.entries(bgColors) as [ThemeBg, string | number][]) {
379
1288
  this.bgColors.set(key, bgAnsi(value, mode));
380
1289
  }
1290
+ // Build symbol map from preset + overrides
1291
+ const baseSymbols = SYMBOL_PRESETS[symbolPreset];
1292
+ this.symbols = { ...baseSymbols };
1293
+ for (const [key, value] of Object.entries(symbolOverrides)) {
1294
+ if (key in this.symbols) {
1295
+ this.symbols[key as SymbolKey] = value;
1296
+ } else {
1297
+ logger.debug("Invalid symbol key in override", { key, availableKeys: Object.keys(this.symbols) });
1298
+ }
1299
+ }
381
1300
  }
382
1301
 
383
1302
  fg(color: ThemeColor, text: string): string {
@@ -447,6 +1366,209 @@ export class Theme {
447
1366
  getBashModeBorderColor(): (str: string) => string {
448
1367
  return (str: string) => this.fg("bashMode", str);
449
1368
  }
1369
+
1370
+ // -------------------------------------------------------------------------
1371
+ // Symbol Methods
1372
+ // -------------------------------------------------------------------------
1373
+
1374
+ /**
1375
+ * Get a symbol by key.
1376
+ */
1377
+ symbol(key: SymbolKey): string {
1378
+ return this.symbols[key];
1379
+ }
1380
+
1381
+ /**
1382
+ * Get a symbol styled with a color.
1383
+ */
1384
+ styledSymbol(key: SymbolKey, color: ThemeColor): string {
1385
+ return this.fg(color, this.symbols[key]);
1386
+ }
1387
+
1388
+ /**
1389
+ * Get the current symbol preset.
1390
+ */
1391
+ getSymbolPreset(): SymbolPreset {
1392
+ return this.symbolPreset;
1393
+ }
1394
+
1395
+ // -------------------------------------------------------------------------
1396
+ // Symbol Category Accessors
1397
+ // -------------------------------------------------------------------------
1398
+
1399
+ get status() {
1400
+ return {
1401
+ success: this.symbols["status.success"],
1402
+ error: this.symbols["status.error"],
1403
+ warning: this.symbols["status.warning"],
1404
+ info: this.symbols["status.info"],
1405
+ pending: this.symbols["status.pending"],
1406
+ disabled: this.symbols["status.disabled"],
1407
+ enabled: this.symbols["status.enabled"],
1408
+ running: this.symbols["status.running"],
1409
+ shadowed: this.symbols["status.shadowed"],
1410
+ aborted: this.symbols["status.aborted"],
1411
+ };
1412
+ }
1413
+
1414
+ get nav() {
1415
+ return {
1416
+ cursor: this.symbols["nav.cursor"],
1417
+ selected: this.symbols["nav.selected"],
1418
+ expand: this.symbols["nav.expand"],
1419
+ collapse: this.symbols["nav.collapse"],
1420
+ back: this.symbols["nav.back"],
1421
+ };
1422
+ }
1423
+
1424
+ get tree() {
1425
+ return {
1426
+ branch: this.symbols["tree.branch"],
1427
+ last: this.symbols["tree.last"],
1428
+ vertical: this.symbols["tree.vertical"],
1429
+ horizontal: this.symbols["tree.horizontal"],
1430
+ hook: this.symbols["tree.hook"],
1431
+ };
1432
+ }
1433
+
1434
+ get boxRound() {
1435
+ return {
1436
+ topLeft: this.symbols["boxRound.topLeft"],
1437
+ topRight: this.symbols["boxRound.topRight"],
1438
+ bottomLeft: this.symbols["boxRound.bottomLeft"],
1439
+ bottomRight: this.symbols["boxRound.bottomRight"],
1440
+ horizontal: this.symbols["boxRound.horizontal"],
1441
+ vertical: this.symbols["boxRound.vertical"],
1442
+ };
1443
+ }
1444
+
1445
+ get boxSharp() {
1446
+ return {
1447
+ topLeft: this.symbols["boxSharp.topLeft"],
1448
+ topRight: this.symbols["boxSharp.topRight"],
1449
+ bottomLeft: this.symbols["boxSharp.bottomLeft"],
1450
+ bottomRight: this.symbols["boxSharp.bottomRight"],
1451
+ horizontal: this.symbols["boxSharp.horizontal"],
1452
+ vertical: this.symbols["boxSharp.vertical"],
1453
+ cross: this.symbols["boxSharp.cross"],
1454
+ teeDown: this.symbols["boxSharp.teeDown"],
1455
+ teeUp: this.symbols["boxSharp.teeUp"],
1456
+ teeRight: this.symbols["boxSharp.teeRight"],
1457
+ teeLeft: this.symbols["boxSharp.teeLeft"],
1458
+ };
1459
+ }
1460
+
1461
+ get sep() {
1462
+ return {
1463
+ powerline: this.symbols["sep.powerline"],
1464
+ powerlineThin: this.symbols["sep.powerlineThin"],
1465
+ powerlineLeft: this.symbols["sep.powerlineLeft"],
1466
+ powerlineRight: this.symbols["sep.powerlineRight"],
1467
+ powerlineThinLeft: this.symbols["sep.powerlineThinLeft"],
1468
+ powerlineThinRight: this.symbols["sep.powerlineThinRight"],
1469
+ block: this.symbols["sep.block"],
1470
+ space: this.symbols["sep.space"],
1471
+ asciiLeft: this.symbols["sep.asciiLeft"],
1472
+ asciiRight: this.symbols["sep.asciiRight"],
1473
+ dot: this.symbols["sep.dot"],
1474
+ slash: this.symbols["sep.slash"],
1475
+ pipe: this.symbols["sep.pipe"],
1476
+ };
1477
+ }
1478
+
1479
+ get icon() {
1480
+ return {
1481
+ model: this.symbols["icon.model"],
1482
+ folder: this.symbols["icon.folder"],
1483
+ file: this.symbols["icon.file"],
1484
+ git: this.symbols["icon.git"],
1485
+ branch: this.symbols["icon.branch"],
1486
+ tokens: this.symbols["icon.tokens"],
1487
+ context: this.symbols["icon.context"],
1488
+ cost: this.symbols["icon.cost"],
1489
+ time: this.symbols["icon.time"],
1490
+ pi: this.symbols["icon.pi"],
1491
+ agents: this.symbols["icon.agents"],
1492
+ cache: this.symbols["icon.cache"],
1493
+ input: this.symbols["icon.input"],
1494
+ output: this.symbols["icon.output"],
1495
+ host: this.symbols["icon.host"],
1496
+ session: this.symbols["icon.session"],
1497
+ package: this.symbols["icon.package"],
1498
+ warning: this.symbols["icon.warning"],
1499
+ rewind: this.symbols["icon.rewind"],
1500
+ auto: this.symbols["icon.auto"],
1501
+ extensionSkill: this.symbols["icon.extensionSkill"],
1502
+ extensionTool: this.symbols["icon.extensionTool"],
1503
+ extensionSlashCommand: this.symbols["icon.extensionSlashCommand"],
1504
+ extensionMcp: this.symbols["icon.extensionMcp"],
1505
+ extensionRule: this.symbols["icon.extensionRule"],
1506
+ extensionHook: this.symbols["icon.extensionHook"],
1507
+ extensionPrompt: this.symbols["icon.extensionPrompt"],
1508
+ extensionContextFile: this.symbols["icon.extensionContextFile"],
1509
+ extensionInstruction: this.symbols["icon.extensionInstruction"],
1510
+ };
1511
+ }
1512
+
1513
+ get thinking() {
1514
+ return {
1515
+ minimal: this.symbols["thinking.minimal"],
1516
+ low: this.symbols["thinking.low"],
1517
+ medium: this.symbols["thinking.medium"],
1518
+ high: this.symbols["thinking.high"],
1519
+ xhigh: this.symbols["thinking.xhigh"],
1520
+ };
1521
+ }
1522
+
1523
+ get checkbox() {
1524
+ return {
1525
+ checked: this.symbols["checkbox.checked"],
1526
+ unchecked: this.symbols["checkbox.unchecked"],
1527
+ };
1528
+ }
1529
+
1530
+ get format() {
1531
+ return {
1532
+ ellipsis: this.symbols["format.ellipsis"],
1533
+ bullet: this.symbols["format.bullet"],
1534
+ dash: this.symbols["format.dash"],
1535
+ bracketLeft: this.symbols["format.bracketLeft"],
1536
+ bracketRight: this.symbols["format.bracketRight"],
1537
+ };
1538
+ }
1539
+
1540
+ get md() {
1541
+ return {
1542
+ quoteBorder: this.symbols["md.quoteBorder"],
1543
+ hrChar: this.symbols["md.hrChar"],
1544
+ bullet: this.symbols["md.bullet"],
1545
+ };
1546
+ }
1547
+
1548
+ /**
1549
+ * Default spinner frames (status spinner).
1550
+ */
1551
+ get spinnerFrames(): string[] {
1552
+ return this.getSpinnerFrames();
1553
+ }
1554
+
1555
+ /**
1556
+ * Get spinner frames by type.
1557
+ */
1558
+ getSpinnerFrames(type: SpinnerType = "status"): string[] {
1559
+ return SPINNER_FRAMES[this.symbolPreset][type];
1560
+ }
1561
+
1562
+ /**
1563
+ * Get language icon for a language name.
1564
+ * Maps common language names to their corresponding symbol keys.
1565
+ */
1566
+ getLangIcon(lang: string | undefined): string {
1567
+ if (!lang) return this.symbols["lang.default"];
1568
+ const normalized = lang.toLowerCase();
1569
+ const key = langMap[normalized];
1570
+ return key ? this.symbols[key] : this.symbols["lang.default"];
1571
+ }
450
1572
  }
451
1573
 
452
1574
  // ============================================================================
@@ -456,6 +1578,7 @@ export class Theme {
456
1578
  const BUILTIN_THEMES: Record<string, ThemeJson> = {
457
1579
  dark: darkThemeJson as ThemeJson,
458
1580
  light: lightThemeJson as ThemeJson,
1581
+ ...(defaultThemes as Record<string, ThemeJson>),
459
1582
  };
460
1583
 
461
1584
  function getBuiltinThemes(): Record<string, ThemeJson> {
@@ -524,7 +1647,7 @@ function loadThemeJson(name: string): ThemeJson {
524
1647
  return json as ThemeJson;
525
1648
  }
526
1649
 
527
- function createTheme(themeJson: ThemeJson, mode?: ColorMode): Theme {
1650
+ function createTheme(themeJson: ThemeJson, mode?: ColorMode, symbolPresetOverride?: SymbolPreset): Theme {
528
1651
  const colorMode = mode ?? detectColorMode();
529
1652
  const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
530
1653
  const fgColors: Record<ThemeColor, string | number> = {} as Record<ThemeColor, string | number>;
@@ -545,12 +1668,15 @@ function createTheme(themeJson: ThemeJson, mode?: ColorMode): Theme {
545
1668
  fgColors[key as ThemeColor] = value;
546
1669
  }
547
1670
  }
548
- return new Theme(fgColors, bgColors, colorMode);
1671
+ // Extract symbol configuration - settings override takes precedence over theme
1672
+ const symbolPreset: SymbolPreset = symbolPresetOverride ?? themeJson.symbols?.preset ?? "unicode";
1673
+ const symbolOverrides = themeJson.symbols?.overrides ?? {};
1674
+ return new Theme(fgColors, bgColors, colorMode, symbolPreset, symbolOverrides);
549
1675
  }
550
1676
 
551
- function loadTheme(name: string, mode?: ColorMode): Theme {
1677
+ function loadTheme(name: string, mode?: ColorMode, symbolPresetOverride?: SymbolPreset): Theme {
552
1678
  const themeJson = loadThemeJson(name);
553
- return createTheme(themeJson, mode);
1679
+ return createTheme(themeJson, mode, symbolPresetOverride);
554
1680
  }
555
1681
 
556
1682
  function detectTerminalBackground(): "dark" | "light" {
@@ -578,21 +1704,23 @@ function getDefaultTheme(): string {
578
1704
 
579
1705
  export let theme: Theme;
580
1706
  let currentThemeName: string | undefined;
1707
+ let currentSymbolPresetOverride: SymbolPreset | undefined;
581
1708
  let themeWatcher: fs.FSWatcher | undefined;
582
1709
  let onThemeChangeCallback: (() => void) | undefined;
583
1710
 
584
- export function initTheme(themeName?: string, enableWatcher: boolean = false): void {
1711
+ export function initTheme(themeName?: string, enableWatcher: boolean = false, symbolPreset?: SymbolPreset): void {
585
1712
  const name = themeName ?? getDefaultTheme();
586
1713
  currentThemeName = name;
1714
+ currentSymbolPresetOverride = symbolPreset;
587
1715
  try {
588
- theme = loadTheme(name);
1716
+ theme = loadTheme(name, undefined, symbolPreset);
589
1717
  if (enableWatcher) {
590
1718
  startThemeWatcher();
591
1719
  }
592
1720
  } catch (err) {
593
1721
  logger.debug("Theme loading failed, falling back to dark theme", { error: String(err) });
594
1722
  currentThemeName = "dark";
595
- theme = loadTheme("dark");
1723
+ theme = loadTheme("dark", undefined, symbolPreset);
596
1724
  // Don't start watcher for fallback theme
597
1725
  }
598
1726
  }
@@ -600,15 +1728,18 @@ export function initTheme(themeName?: string, enableWatcher: boolean = false): v
600
1728
  export function setTheme(name: string, enableWatcher: boolean = false): { success: boolean; error?: string } {
601
1729
  currentThemeName = name;
602
1730
  try {
603
- theme = loadTheme(name);
1731
+ theme = loadTheme(name, undefined, currentSymbolPresetOverride);
604
1732
  if (enableWatcher) {
605
1733
  startThemeWatcher();
606
1734
  }
1735
+ if (onThemeChangeCallback) {
1736
+ onThemeChangeCallback();
1737
+ }
607
1738
  return { success: true };
608
1739
  } catch (error) {
609
1740
  // Theme is invalid - fall back to dark theme
610
1741
  currentThemeName = "dark";
611
- theme = loadTheme("dark");
1742
+ theme = loadTheme("dark", undefined, currentSymbolPresetOverride);
612
1743
  // Don't start watcher for fallback theme
613
1744
  return {
614
1745
  success: false,
@@ -617,10 +1748,49 @@ export function setTheme(name: string, enableWatcher: boolean = false): { succes
617
1748
  }
618
1749
  }
619
1750
 
1751
+ /**
1752
+ * Set the symbol preset override, recreating the theme with the new preset.
1753
+ */
1754
+ export function setSymbolPreset(preset: SymbolPreset): void {
1755
+ currentSymbolPresetOverride = preset;
1756
+ if (currentThemeName) {
1757
+ try {
1758
+ theme = loadTheme(currentThemeName, undefined, preset);
1759
+ } catch {
1760
+ // Fall back to dark theme with new preset
1761
+ theme = loadTheme("dark", undefined, preset);
1762
+ }
1763
+ if (onThemeChangeCallback) {
1764
+ onThemeChangeCallback();
1765
+ }
1766
+ }
1767
+ }
1768
+
1769
+ /**
1770
+ * Get the current symbol preset override.
1771
+ */
1772
+ export function getSymbolPresetOverride(): SymbolPreset | undefined {
1773
+ return currentSymbolPresetOverride;
1774
+ }
1775
+
620
1776
  export function onThemeChange(callback: () => void): void {
621
1777
  onThemeChangeCallback = callback;
622
1778
  }
623
1779
 
1780
+ /**
1781
+ * Get available symbol presets.
1782
+ */
1783
+ export function getAvailableSymbolPresets(): SymbolPreset[] {
1784
+ return ["unicode", "nerd", "ascii"];
1785
+ }
1786
+
1787
+ /**
1788
+ * Check if a string is a valid symbol preset.
1789
+ */
1790
+ export function isValidSymbolPreset(preset: string): preset is SymbolPreset {
1791
+ return preset === "unicode" || preset === "nerd" || preset === "ascii";
1792
+ }
1793
+
624
1794
  function startThemeWatcher(): void {
625
1795
  // Stop existing watcher if any
626
1796
  if (themeWatcher) {
@@ -647,8 +1817,8 @@ function startThemeWatcher(): void {
647
1817
  // Debounce rapid changes
648
1818
  setTimeout(() => {
649
1819
  try {
650
- // Reload the theme
651
- theme = loadTheme(currentThemeName!);
1820
+ // Reload the theme with current symbol preset override
1821
+ theme = loadTheme(currentThemeName!, undefined, currentSymbolPresetOverride);
652
1822
  // Notify callback (to invalidate UI)
653
1823
  if (onThemeChangeCallback) {
654
1824
  onThemeChangeCallback();
@@ -869,6 +2039,20 @@ export function highlightCode(code: string, lang?: string): string[] {
869
2039
  * Get language identifier from file path extension.
870
2040
  */
871
2041
  export function getLanguageFromPath(filePath: string): string | undefined {
2042
+ const baseName = path.basename(filePath).toLowerCase();
2043
+ if (baseName === ".env" || baseName.startsWith(".env.")) return "env";
2044
+ if (
2045
+ baseName === ".gitignore" ||
2046
+ baseName === ".gitattributes" ||
2047
+ baseName === ".gitmodules" ||
2048
+ baseName === ".editorconfig" ||
2049
+ baseName === ".npmrc" ||
2050
+ baseName === ".prettierrc" ||
2051
+ baseName === ".eslintrc"
2052
+ ) {
2053
+ return "conf";
2054
+ }
2055
+
872
2056
  const ext = filePath.split(".").pop()?.toLowerCase();
873
2057
  if (!ext) return undefined;
874
2058
 
@@ -931,11 +2115,38 @@ export function getLanguageFromPath(filePath: string): string | undefined {
931
2115
  proto: "protobuf",
932
2116
  tf: "hcl",
933
2117
  hcl: "hcl",
2118
+ txt: "text",
2119
+ text: "text",
2120
+ log: "log",
2121
+ csv: "csv",
2122
+ tsv: "tsv",
2123
+ ini: "ini",
2124
+ cfg: "conf",
2125
+ conf: "conf",
2126
+ config: "conf",
2127
+ properties: "conf",
2128
+ env: "env",
934
2129
  };
935
2130
 
936
2131
  return extToLang[ext];
937
2132
  }
938
2133
 
2134
+ export function getSymbolTheme(): SymbolTheme {
2135
+ const preset = theme.getSymbolPreset();
2136
+
2137
+ return {
2138
+ cursor: theme.nav.cursor,
2139
+ inputCursor: preset === "ascii" ? "|" : "▏",
2140
+ ellipsis: theme.format.ellipsis,
2141
+ boxRound: theme.boxRound,
2142
+ boxSharp: theme.boxSharp,
2143
+ table: theme.boxSharp,
2144
+ quoteBorder: theme.md.quoteBorder,
2145
+ hrChar: theme.md.hrChar,
2146
+ spinnerFrames: theme.getSpinnerFrames("activity"),
2147
+ };
2148
+ }
2149
+
939
2150
  export function getMarkdownTheme(): MarkdownTheme {
940
2151
  return {
941
2152
  heading: (text: string) => theme.fg("mdHeading", text),
@@ -952,6 +2163,7 @@ export function getMarkdownTheme(): MarkdownTheme {
952
2163
  italic: (text: string) => theme.italic(text),
953
2164
  underline: (text: string) => theme.underline(text),
954
2165
  strikethrough: (text: string) => chalk.strikethrough(text),
2166
+ symbols: getSymbolTheme(),
955
2167
  highlightCode: (code: string, lang?: string): string[] => {
956
2168
  // Validate language before highlighting to avoid stderr spam from cli-highlight
957
2169
  const validLang = lang && supportsLanguage(lang) ? lang : undefined;
@@ -976,6 +2188,7 @@ export function getSelectListTheme(): SelectListTheme {
976
2188
  description: (text: string) => theme.fg("muted", text),
977
2189
  scrollInfo: (text: string) => theme.fg("muted", text),
978
2190
  noMatch: (text: string) => theme.fg("muted", text),
2191
+ symbols: getSymbolTheme(),
979
2192
  };
980
2193
  }
981
2194
 
@@ -983,6 +2196,7 @@ export function getEditorTheme(): EditorTheme {
983
2196
  return {
984
2197
  borderColor: (text: string) => theme.fg("borderMuted", text),
985
2198
  selectList: getSelectListTheme(),
2199
+ symbols: getSymbolTheme(),
986
2200
  };
987
2201
  }
988
2202
 
@@ -991,7 +2205,7 @@ export function getSettingsListTheme(): import("@oh-my-pi/pi-tui").SettingsListT
991
2205
  label: (text: string, selected: boolean) => (selected ? theme.fg("accent", text) : text),
992
2206
  value: (text: string, selected: boolean) => (selected ? theme.fg("accent", text) : theme.fg("muted", text)),
993
2207
  description: (text: string) => theme.fg("dim", text),
994
- cursor: theme.fg("accent", "→ "),
2208
+ cursor: theme.fg("accent", `${theme.nav.cursor} `),
995
2209
  hint: (text: string) => theme.fg("dim", text),
996
2210
  };
997
2211
  }