@neurocode-ai/tui 1.18.8

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 (241) hide show
  1. package/bunfig.toml +4 -0
  2. package/package.json +72 -0
  3. package/src/app.tsx +1134 -0
  4. package/src/attention.ts +260 -0
  5. package/src/audio.d.ts +9 -0
  6. package/src/audio.ts +53 -0
  7. package/src/clipboard.ts +124 -0
  8. package/src/component/bg-pulse-render.ts +436 -0
  9. package/src/component/bg-pulse.tsx +99 -0
  10. package/src/component/command-palette.tsx +79 -0
  11. package/src/component/dialog-agent.tsx +31 -0
  12. package/src/component/dialog-console-org.tsx +135 -0
  13. package/src/component/dialog-debug.tsx +90 -0
  14. package/src/component/dialog-mcp.tsx +85 -0
  15. package/src/component/dialog-model.tsx +197 -0
  16. package/src/component/dialog-move-session.tsx +353 -0
  17. package/src/component/dialog-provider.tsx +469 -0
  18. package/src/component/dialog-retry-action.tsx +160 -0
  19. package/src/component/dialog-session-delete-failed.tsx +99 -0
  20. package/src/component/dialog-session-list.tsx +364 -0
  21. package/src/component/dialog-session-rename.tsx +31 -0
  22. package/src/component/dialog-skill.tsx +70 -0
  23. package/src/component/dialog-stash.tsx +87 -0
  24. package/src/component/dialog-status.tsx +168 -0
  25. package/src/component/dialog-tag.tsx +47 -0
  26. package/src/component/dialog-theme-list.tsx +50 -0
  27. package/src/component/dialog-variant.tsx +39 -0
  28. package/src/component/dialog-workspace-create.tsx +308 -0
  29. package/src/component/dialog-workspace-file-changes.tsx +144 -0
  30. package/src/component/dialog-workspace-list.tsx +112 -0
  31. package/src/component/dialog-workspace-unavailable.tsx +69 -0
  32. package/src/component/error-component.tsx +240 -0
  33. package/src/component/logo.tsx +61 -0
  34. package/src/component/plugin-route-missing.tsx +14 -0
  35. package/src/component/prompt/autocomplete.tsx +781 -0
  36. package/src/component/prompt/cwd.ts +0 -0
  37. package/src/component/prompt/frecency.tsx +1 -0
  38. package/src/component/prompt/history.tsx +1 -0
  39. package/src/component/prompt/index.tsx +1713 -0
  40. package/src/component/prompt/local-attachment.ts +48 -0
  41. package/src/component/prompt/move.tsx +205 -0
  42. package/src/component/prompt/stash.tsx +1 -0
  43. package/src/component/prompt/workspace.tsx +137 -0
  44. package/src/component/register-spinner.ts +6 -0
  45. package/src/component/spinner.tsx +26 -0
  46. package/src/component/startup-loading.tsx +63 -0
  47. package/src/component/todo-item.tsx +32 -0
  48. package/src/component/use-connected.tsx +12 -0
  49. package/src/component/workspace-label.tsx +19 -0
  50. package/src/config/index.tsx +129 -0
  51. package/src/config/keybind.ts +471 -0
  52. package/src/context/args.tsx +16 -0
  53. package/src/context/clipboard.tsx +18 -0
  54. package/src/context/data.tsx +569 -0
  55. package/src/context/directory.ts +17 -0
  56. package/src/context/editor.ts +408 -0
  57. package/src/context/epilogue.tsx +6 -0
  58. package/src/context/event.ts +36 -0
  59. package/src/context/exit.tsx +8 -0
  60. package/src/context/helper.tsx +26 -0
  61. package/src/context/kv.tsx +66 -0
  62. package/src/context/local.tsx +542 -0
  63. package/src/context/location.tsx +14 -0
  64. package/src/context/path-format.tsx +24 -0
  65. package/src/context/permission.tsx +26 -0
  66. package/src/context/project.tsx +115 -0
  67. package/src/context/prompt.tsx +18 -0
  68. package/src/context/route.tsx +60 -0
  69. package/src/context/runtime.tsx +62 -0
  70. package/src/context/sdk.tsx +151 -0
  71. package/src/context/sync.tsx +666 -0
  72. package/src/context/theme.tsx +332 -0
  73. package/src/context/thinking.ts +67 -0
  74. package/src/editor-zed.ts +286 -0
  75. package/src/editor.ts +101 -0
  76. package/src/feature-plugins/builtins.ts +36 -0
  77. package/src/feature-plugins/home/footer.tsx +100 -0
  78. package/src/feature-plugins/home/tips-view.tsx +287 -0
  79. package/src/feature-plugins/home/tips.tsx +59 -0
  80. package/src/feature-plugins/sidebar/context.tsx +65 -0
  81. package/src/feature-plugins/sidebar/files.tsx +70 -0
  82. package/src/feature-plugins/sidebar/footer.tsx +98 -0
  83. package/src/feature-plugins/sidebar/lsp.tsx +65 -0
  84. package/src/feature-plugins/sidebar/mcp.tsx +97 -0
  85. package/src/feature-plugins/sidebar/todo.tsx +49 -0
  86. package/src/feature-plugins/system/diff-viewer-file-tree-utils.ts +232 -0
  87. package/src/feature-plugins/system/diff-viewer-file-tree.tsx +162 -0
  88. package/src/feature-plugins/system/diff-viewer-ui.tsx +103 -0
  89. package/src/feature-plugins/system/diff-viewer.tsx +1077 -0
  90. package/src/feature-plugins/system/notifications.ts +94 -0
  91. package/src/feature-plugins/system/plugins.tsx +269 -0
  92. package/src/feature-plugins/system/which-key.tsx +608 -0
  93. package/src/index.tsx +1 -0
  94. package/src/keymap.tsx +290 -0
  95. package/src/logo.ts +11 -0
  96. package/src/parsers-config.ts +386 -0
  97. package/src/plugin/adapters.tsx +355 -0
  98. package/src/plugin/api.ts +52 -0
  99. package/src/plugin/command-shim.ts +109 -0
  100. package/src/plugin/runtime.tsx +81 -0
  101. package/src/plugin/slots.tsx +65 -0
  102. package/src/prompt/display.ts +48 -0
  103. package/src/prompt/frecency.tsx +80 -0
  104. package/src/prompt/history.tsx +111 -0
  105. package/src/prompt/part.ts +29 -0
  106. package/src/prompt/stash.tsx +89 -0
  107. package/src/prompt/traits.ts +29 -0
  108. package/src/routes/home/session-destination.tsx +41 -0
  109. package/src/routes/home.tsx +95 -0
  110. package/src/routes/session/dialog-fork-from-timeline.tsx +76 -0
  111. package/src/routes/session/dialog-message.tsx +109 -0
  112. package/src/routes/session/dialog-subagent.tsx +26 -0
  113. package/src/routes/session/dialog-timeline.tsx +47 -0
  114. package/src/routes/session/footer.tsx +91 -0
  115. package/src/routes/session/index.tsx +2710 -0
  116. package/src/routes/session/permission.tsx +718 -0
  117. package/src/routes/session/question.tsx +514 -0
  118. package/src/routes/session/sidebar.tsx +103 -0
  119. package/src/routes/session/subagent-footer.tsx +132 -0
  120. package/src/runtime.tsx +9 -0
  121. package/src/terminal-win32.ts +130 -0
  122. package/src/theme/assets/aura.json +69 -0
  123. package/src/theme/assets/ayu.json +80 -0
  124. package/src/theme/assets/carbonfox.json +248 -0
  125. package/src/theme/assets/catppuccin-frappe.json +230 -0
  126. package/src/theme/assets/catppuccin-macchiato.json +230 -0
  127. package/src/theme/assets/catppuccin.json +112 -0
  128. package/src/theme/assets/cobalt2.json +225 -0
  129. package/src/theme/assets/cursor.json +249 -0
  130. package/src/theme/assets/dracula.json +219 -0
  131. package/src/theme/assets/everforest.json +241 -0
  132. package/src/theme/assets/flexoki.json +237 -0
  133. package/src/theme/assets/github.json +233 -0
  134. package/src/theme/assets/gruvbox.json +242 -0
  135. package/src/theme/assets/kanagawa.json +77 -0
  136. package/src/theme/assets/lucent-orng.json +234 -0
  137. package/src/theme/assets/material.json +235 -0
  138. package/src/theme/assets/matrix.json +77 -0
  139. package/src/theme/assets/mercury.json +252 -0
  140. package/src/theme/assets/monokai.json +221 -0
  141. package/src/theme/assets/nightowl.json +221 -0
  142. package/src/theme/assets/nord.json +223 -0
  143. package/src/theme/assets/one-dark.json +84 -0
  144. package/src/theme/assets/opencode.json +245 -0
  145. package/src/theme/assets/orng.json +249 -0
  146. package/src/theme/assets/osaka-jade.json +93 -0
  147. package/src/theme/assets/palenight.json +222 -0
  148. package/src/theme/assets/rosepine.json +234 -0
  149. package/src/theme/assets/solarized.json +223 -0
  150. package/src/theme/assets/synthwave84.json +226 -0
  151. package/src/theme/assets/tokyonight.json +243 -0
  152. package/src/theme/assets/vercel.json +245 -0
  153. package/src/theme/assets/vesper.json +218 -0
  154. package/src/theme/assets/zenburn.json +223 -0
  155. package/src/theme/index.ts +1089 -0
  156. package/src/ui/border.ts +21 -0
  157. package/src/ui/dialog-alert.tsx +66 -0
  158. package/src/ui/dialog-confirm.tsx +108 -0
  159. package/src/ui/dialog-export-options.tsx +217 -0
  160. package/src/ui/dialog-help.tsx +40 -0
  161. package/src/ui/dialog-prompt.tsx +126 -0
  162. package/src/ui/dialog-select.tsx +790 -0
  163. package/src/ui/dialog.tsx +231 -0
  164. package/src/ui/link.tsx +34 -0
  165. package/src/ui/spinner.ts +368 -0
  166. package/src/ui/toast.tsx +102 -0
  167. package/src/util/collapse-tool-output.ts +19 -0
  168. package/src/util/error.ts +182 -0
  169. package/src/util/filetype.ts +130 -0
  170. package/src/util/format.ts +20 -0
  171. package/src/util/layout.ts +25 -0
  172. package/src/util/locale.ts +86 -0
  173. package/src/util/model.ts +28 -0
  174. package/src/util/path.ts +12 -0
  175. package/src/util/persistence.ts +33 -0
  176. package/src/util/presentation.ts +38 -0
  177. package/src/util/provider-origin.ts +7 -0
  178. package/src/util/record.ts +3 -0
  179. package/src/util/renderer.ts +7 -0
  180. package/src/util/revert-diff.ts +18 -0
  181. package/src/util/scroll.ts +27 -0
  182. package/src/util/selection.ts +79 -0
  183. package/src/util/session.ts +3 -0
  184. package/src/util/signal.ts +51 -0
  185. package/src/util/system.ts +20 -0
  186. package/src/util/tool-display.ts +13 -0
  187. package/src/util/transcript.ts +112 -0
  188. package/sst-env.d.ts +10 -0
  189. package/test/app-lifecycle.test.tsx +128 -0
  190. package/test/cli/cmd/tui/dialog-workspace-create.test.ts +28 -0
  191. package/test/cli/cmd/tui/model-options.test.ts +32 -0
  192. package/test/cli/cmd/tui/notifications.test.ts +267 -0
  193. package/test/cli/cmd/tui/provider-options.test.ts +41 -0
  194. package/test/cli/cmd/tui/sync-fixture.tsx +70 -0
  195. package/test/cli/cmd/tui/sync-live-hydration.test.tsx +262 -0
  196. package/test/cli/cmd/tui/sync-undefined-messages.test.tsx +43 -0
  197. package/test/cli/cmd/tui/sync.test.tsx +65 -0
  198. package/test/cli/tui/__snapshots__/inline-tool-wrap-snapshot.test.tsx.snap +92 -0
  199. package/test/cli/tui/data.test.tsx +486 -0
  200. package/test/cli/tui/dialog-prompt.test.tsx +147 -0
  201. package/test/cli/tui/diff-viewer-file-tree.test.tsx +200 -0
  202. package/test/cli/tui/diff-viewer.test.tsx +268 -0
  203. package/test/cli/tui/inline-tool-wrap-snapshot.test.tsx +351 -0
  204. package/test/cli/tui/prompt-submit-race.test.ts +98 -0
  205. package/test/cli/tui/thinking.test.ts +36 -0
  206. package/test/cli/tui/use-event.test.tsx +148 -0
  207. package/test/clipboard.test.ts +19 -0
  208. package/test/component/dialog-session-list.test.ts +46 -0
  209. package/test/config.test.tsx +127 -0
  210. package/test/context/local.test.ts +22 -0
  211. package/test/editor.test.ts +32 -0
  212. package/test/feature-plugins/diff-viewer-file-tree-utils.test.ts +323 -0
  213. package/test/fixture/fixture.ts +13 -0
  214. package/test/fixture/tui-environment.tsx +32 -0
  215. package/test/fixture/tui-plugin.ts +36 -0
  216. package/test/fixture/tui-runtime.ts +12 -0
  217. package/test/fixture/tui-sdk.ts +109 -0
  218. package/test/index.test.tsx +6 -0
  219. package/test/keymap.test.tsx +141 -0
  220. package/test/plugin/runtime.test.ts +50 -0
  221. package/test/plugin/slots.test.tsx +38 -0
  222. package/test/prompt/display.test.ts +33 -0
  223. package/test/prompt/history.test.ts +39 -0
  224. package/test/prompt/jsonl.test.ts +24 -0
  225. package/test/prompt/local-attachment.test.ts +43 -0
  226. package/test/prompt/part.test.ts +53 -0
  227. package/test/prompt/persistence.test.ts +23 -0
  228. package/test/prompt/traits.test.ts +25 -0
  229. package/test/runtime.test.tsx +37 -0
  230. package/test/theme.test.ts +81 -0
  231. package/test/util/error.test.ts +49 -0
  232. package/test/util/filetype.test.ts +16 -0
  233. package/test/util/format.test.ts +59 -0
  234. package/test/util/model.test.ts +9 -0
  235. package/test/util/presentation.test.ts +8 -0
  236. package/test/util/renderer.test.ts +30 -0
  237. package/test/util/revert-diff.test.ts +35 -0
  238. package/test/util/session.test.ts +10 -0
  239. package/test/util/tool-display.test.ts +40 -0
  240. package/test/util/transcript.test.ts +421 -0
  241. package/tsconfig.json +10 -0
@@ -0,0 +1,1089 @@
1
+ import { SyntaxStyle, RGBA, type TerminalColors } from "@opentui/core"
2
+ import aura from "./assets/aura.json" with { type: "json" }
3
+ import ayu from "./assets/ayu.json" with { type: "json" }
4
+ import carbonfox from "./assets/carbonfox.json" with { type: "json" }
5
+ import catppuccinFrappe from "./assets/catppuccin-frappe.json" with { type: "json" }
6
+ import catppuccinMacchiato from "./assets/catppuccin-macchiato.json" with { type: "json" }
7
+ import catppuccin from "./assets/catppuccin.json" with { type: "json" }
8
+ import cobalt2 from "./assets/cobalt2.json" with { type: "json" }
9
+ import cursor from "./assets/cursor.json" with { type: "json" }
10
+ import dracula from "./assets/dracula.json" with { type: "json" }
11
+ import everforest from "./assets/everforest.json" with { type: "json" }
12
+ import flexoki from "./assets/flexoki.json" with { type: "json" }
13
+ import github from "./assets/github.json" with { type: "json" }
14
+ import gruvbox from "./assets/gruvbox.json" with { type: "json" }
15
+ import kanagawa from "./assets/kanagawa.json" with { type: "json" }
16
+ import lucentOrng from "./assets/lucent-orng.json" with { type: "json" }
17
+ import material from "./assets/material.json" with { type: "json" }
18
+ import matrix from "./assets/matrix.json" with { type: "json" }
19
+ import mercury from "./assets/mercury.json" with { type: "json" }
20
+ import monokai from "./assets/monokai.json" with { type: "json" }
21
+ import nightowl from "./assets/nightowl.json" with { type: "json" }
22
+ import nord from "./assets/nord.json" with { type: "json" }
23
+ import onedark from "./assets/one-dark.json" with { type: "json" }
24
+ import opencode from "./assets/opencode.json" with { type: "json" }
25
+ import orng from "./assets/orng.json" with { type: "json" }
26
+ import osakaJade from "./assets/osaka-jade.json" with { type: "json" }
27
+ import palenight from "./assets/palenight.json" with { type: "json" }
28
+ import rosepine from "./assets/rosepine.json" with { type: "json" }
29
+ import solarized from "./assets/solarized.json" with { type: "json" }
30
+ import synthwave84 from "./assets/synthwave84.json" with { type: "json" }
31
+ import tokyonight from "./assets/tokyonight.json" with { type: "json" }
32
+ import vercel from "./assets/vercel.json" with { type: "json" }
33
+ import vesper from "./assets/vesper.json" with { type: "json" }
34
+ import zenburn from "./assets/zenburn.json" with { type: "json" }
35
+
36
+ export type Theme = {
37
+ readonly primary: RGBA
38
+ readonly secondary: RGBA
39
+ readonly accent: RGBA
40
+ readonly error: RGBA
41
+ readonly warning: RGBA
42
+ readonly success: RGBA
43
+ readonly info: RGBA
44
+ readonly text: RGBA
45
+ readonly textMuted: RGBA
46
+ readonly selectedListItemText: RGBA
47
+ readonly background: RGBA
48
+ readonly backgroundPanel: RGBA
49
+ readonly backgroundElement: RGBA
50
+ readonly backgroundMenu: RGBA
51
+ readonly border: RGBA
52
+ readonly borderActive: RGBA
53
+ readonly borderSubtle: RGBA
54
+ readonly diffAdded: RGBA
55
+ readonly diffRemoved: RGBA
56
+ readonly diffContext: RGBA
57
+ readonly diffHunkHeader: RGBA
58
+ readonly diffHighlightAdded: RGBA
59
+ readonly diffHighlightRemoved: RGBA
60
+ readonly diffAddedBg: RGBA
61
+ readonly diffRemovedBg: RGBA
62
+ readonly diffContextBg: RGBA
63
+ readonly diffLineNumber: RGBA
64
+ readonly diffAddedLineNumberBg: RGBA
65
+ readonly diffRemovedLineNumberBg: RGBA
66
+ readonly markdownText: RGBA
67
+ readonly markdownHeading: RGBA
68
+ readonly markdownLink: RGBA
69
+ readonly markdownLinkText: RGBA
70
+ readonly markdownCode: RGBA
71
+ readonly markdownBlockQuote: RGBA
72
+ readonly markdownEmph: RGBA
73
+ readonly markdownStrong: RGBA
74
+ readonly markdownHorizontalRule: RGBA
75
+ readonly markdownListItem: RGBA
76
+ readonly markdownListEnumeration: RGBA
77
+ readonly markdownImage: RGBA
78
+ readonly markdownImageText: RGBA
79
+ readonly markdownCodeBlock: RGBA
80
+ readonly syntaxComment: RGBA
81
+ readonly syntaxKeyword: RGBA
82
+ readonly syntaxFunction: RGBA
83
+ readonly syntaxVariable: RGBA
84
+ readonly syntaxString: RGBA
85
+ readonly syntaxNumber: RGBA
86
+ readonly syntaxType: RGBA
87
+ readonly syntaxOperator: RGBA
88
+ readonly syntaxPunctuation: RGBA
89
+ readonly thinkingOpacity: number
90
+ _hasSelectedListItemText: boolean
91
+ }
92
+ type ThemeColor = Exclude<keyof Theme, "thinkingOpacity" | "_hasSelectedListItemText">
93
+ export type SyntaxStyleOverrides = Record<string, { italic?: boolean }>
94
+
95
+ export function selectedForeground(theme: Theme, bg?: RGBA): RGBA {
96
+ // If theme explicitly defines selectedListItemText, use it
97
+ if (theme._hasSelectedListItemText) {
98
+ return theme.selectedListItemText
99
+ }
100
+
101
+ // For transparent backgrounds, calculate contrast based on the actual bg (or fallback to primary)
102
+ if (theme.background.a === 0) {
103
+ const targetColor = bg ?? theme.primary
104
+ const { r, g, b } = targetColor
105
+ const luminance = 0.299 * r + 0.587 * g + 0.114 * b
106
+ return luminance > 0.5 ? RGBA.fromInts(0, 0, 0) : RGBA.fromInts(255, 255, 255)
107
+ }
108
+
109
+ // Fall back to background color
110
+ return theme.background
111
+ }
112
+
113
+ type HexColor = `#${string}`
114
+ type RefName = string
115
+ type Variant = {
116
+ dark: HexColor | RefName
117
+ light: HexColor | RefName
118
+ }
119
+ type ColorValue = HexColor | RefName | Variant | RGBA
120
+ export type ThemeJson = {
121
+ $schema?: string
122
+ defs?: Record<string, HexColor | RefName>
123
+ theme: Omit<Record<ThemeColor, ColorValue>, "selectedListItemText" | "backgroundMenu"> & {
124
+ selectedListItemText?: ColorValue
125
+ backgroundMenu?: ColorValue
126
+ thinkingOpacity?: number
127
+ }
128
+ }
129
+
130
+ export const DEFAULT_THEMES: Record<string, ThemeJson> = {
131
+ aura,
132
+ ayu,
133
+ catppuccin,
134
+ ["catppuccin-frappe"]: catppuccinFrappe,
135
+ ["catppuccin-macchiato"]: catppuccinMacchiato,
136
+ cobalt2,
137
+ cursor,
138
+ dracula,
139
+ everforest,
140
+ flexoki,
141
+ github,
142
+ gruvbox,
143
+ kanagawa,
144
+ material,
145
+ matrix,
146
+ mercury,
147
+ monokai,
148
+ nightowl,
149
+ nord,
150
+ ["one-dark"]: onedark,
151
+ ["osaka-jade"]: osakaJade,
152
+ opencode,
153
+ orng,
154
+ ["lucent-orng"]: lucentOrng,
155
+ palenight,
156
+ rosepine,
157
+ solarized,
158
+ synthwave84,
159
+ tokyonight,
160
+ vesper,
161
+ vercel,
162
+ zenburn,
163
+ carbonfox,
164
+ }
165
+
166
+ const pluginThemes: Record<string, ThemeJson> = {}
167
+ let customThemes: Record<string, ThemeJson> = {}
168
+ let systemTheme: ThemeJson | undefined
169
+ const listeners = new Set<(themes: Record<string, ThemeJson>) => void>()
170
+
171
+ function listThemes() {
172
+ // Priority: defaults < plugin installs < custom files < generated system.
173
+ const themes = {
174
+ ...DEFAULT_THEMES,
175
+ ...pluginThemes,
176
+ ...customThemes,
177
+ }
178
+ if (!systemTheme) return themes
179
+ return {
180
+ ...themes,
181
+ system: systemTheme,
182
+ }
183
+ }
184
+
185
+ function syncThemes() {
186
+ const themes = listThemes()
187
+ for (const listener of listeners) listener(themes)
188
+ }
189
+
190
+ export function allThemes() {
191
+ return listThemes()
192
+ }
193
+
194
+ export function isTheme(theme: unknown): theme is ThemeJson {
195
+ if (typeof theme !== "object" || theme === null || Array.isArray(theme)) return false
196
+ const value = Reflect.get(theme, "theme")
197
+ return typeof value === "object" && value !== null && !Array.isArray(value)
198
+ }
199
+
200
+ export function subscribeThemes(listener: (themes: Record<string, ThemeJson>) => void) {
201
+ listeners.add(listener)
202
+ return () => listeners.delete(listener)
203
+ }
204
+
205
+ export function setCustomThemes(themes: Record<string, ThemeJson>) {
206
+ customThemes = themes
207
+ syncThemes()
208
+ }
209
+
210
+ export function setSystemTheme(theme: ThemeJson | undefined) {
211
+ systemTheme = theme
212
+ syncThemes()
213
+ }
214
+
215
+ export function hasTheme(name: string) {
216
+ if (!name) return false
217
+ return allThemes()[name] !== undefined
218
+ }
219
+
220
+ export function addTheme(name: string, theme: unknown) {
221
+ if (!name) return false
222
+ if (!isTheme(theme)) return false
223
+ if (hasTheme(name)) return false
224
+ pluginThemes[name] = theme
225
+ syncThemes()
226
+ return true
227
+ }
228
+
229
+ export function upsertTheme(name: string, theme: unknown) {
230
+ if (!name) return false
231
+ if (!isTheme(theme)) return false
232
+ if (customThemes[name] !== undefined) {
233
+ customThemes[name] = theme
234
+ } else {
235
+ pluginThemes[name] = theme
236
+ }
237
+ syncThemes()
238
+ return true
239
+ }
240
+
241
+ export function resolveTheme(theme: ThemeJson, mode: "dark" | "light") {
242
+ const defs = theme.defs ?? {}
243
+ function resolveColor(c: ColorValue, chain: string[] = []): RGBA {
244
+ if (c instanceof RGBA) return c
245
+ if (typeof c === "string") {
246
+ if (c === "transparent" || c === "none") return RGBA.fromInts(0, 0, 0, 0)
247
+
248
+ if (c.startsWith("#")) return RGBA.fromHex(c)
249
+
250
+ if (chain.includes(c)) {
251
+ throw new Error(`Circular color reference: ${[...chain, c].join(" -> ")}`)
252
+ }
253
+
254
+ const next = defs[c] ?? theme.theme[c as ThemeColor]
255
+ if (next === undefined) {
256
+ throw new Error(`Color reference "${c}" not found in defs or theme`)
257
+ }
258
+ return resolveColor(next, [...chain, c])
259
+ }
260
+ if (typeof c === "number") {
261
+ return ansiToRgba(c)
262
+ }
263
+ return resolveColor(c[mode], chain)
264
+ }
265
+
266
+ const resolved = Object.fromEntries(
267
+ Object.entries(theme.theme)
268
+ .filter(([key]) => key !== "selectedListItemText" && key !== "backgroundMenu" && key !== "thinkingOpacity")
269
+ .map(([key, value]) => {
270
+ return [key, resolveColor(value as ColorValue)]
271
+ }),
272
+ ) as Partial<Record<ThemeColor, RGBA>>
273
+
274
+ // Handle selectedListItemText separately since it's optional
275
+ const hasSelectedListItemText = theme.theme.selectedListItemText !== undefined
276
+ if (hasSelectedListItemText) {
277
+ resolved.selectedListItemText = resolveColor(theme.theme.selectedListItemText!)
278
+ } else {
279
+ // Backward compatibility: if selectedListItemText is not defined, use background color
280
+ // This preserves the current behavior for all existing themes
281
+ resolved.selectedListItemText = resolved.background
282
+ }
283
+
284
+ // Handle backgroundMenu - optional with fallback to backgroundElement
285
+ if (theme.theme.backgroundMenu !== undefined) {
286
+ resolved.backgroundMenu = resolveColor(theme.theme.backgroundMenu)
287
+ } else {
288
+ resolved.backgroundMenu = resolved.backgroundElement
289
+ }
290
+
291
+ // Handle thinkingOpacity - optional with default of 0.6
292
+ const thinkingOpacity = theme.theme.thinkingOpacity ?? 0.6
293
+
294
+ return {
295
+ ...resolved,
296
+ _hasSelectedListItemText: hasSelectedListItemText,
297
+ thinkingOpacity,
298
+ } as Theme
299
+ }
300
+
301
+ function ansiToRgba(code: number): RGBA {
302
+ // Standard ANSI colors (0-15)
303
+ if (code < 16) {
304
+ const ansiColors = [
305
+ "#000000", // Black
306
+ "#800000", // Red
307
+ "#008000", // Green
308
+ "#808000", // Yellow
309
+ "#000080", // Blue
310
+ "#800080", // Magenta
311
+ "#008080", // Cyan
312
+ "#c0c0c0", // White
313
+ "#808080", // Bright Black
314
+ "#ff0000", // Bright Red
315
+ "#00ff00", // Bright Green
316
+ "#ffff00", // Bright Yellow
317
+ "#0000ff", // Bright Blue
318
+ "#ff00ff", // Bright Magenta
319
+ "#00ffff", // Bright Cyan
320
+ "#ffffff", // Bright White
321
+ ]
322
+ return RGBA.fromHex(ansiColors[code] ?? "#000000")
323
+ }
324
+
325
+ // 6x6x6 Color Cube (16-231)
326
+ if (code < 232) {
327
+ const index = code - 16
328
+ const b = index % 6
329
+ const g = Math.floor(index / 6) % 6
330
+ const r = Math.floor(index / 36)
331
+
332
+ const val = (x: number) => (x === 0 ? 0 : x * 40 + 55)
333
+ return RGBA.fromInts(val(r), val(g), val(b))
334
+ }
335
+
336
+ // Grayscale Ramp (232-255)
337
+ if (code < 256) {
338
+ const gray = (code - 232) * 10 + 8
339
+ return RGBA.fromInts(gray, gray, gray)
340
+ }
341
+
342
+ // Fallback for invalid codes
343
+ return RGBA.fromInts(0, 0, 0)
344
+ }
345
+
346
+ export function tint(base: RGBA, overlay: RGBA, alpha: number): RGBA {
347
+ const r = base.r + (overlay.r - base.r) * alpha
348
+ const g = base.g + (overlay.g - base.g) * alpha
349
+ const b = base.b + (overlay.b - base.b) * alpha
350
+ return RGBA.fromInts(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255))
351
+ }
352
+
353
+ export function terminalMode(colors: TerminalColors): "dark" | "light" | undefined {
354
+ const bg = colors.defaultBackground
355
+ if (!bg) return
356
+ const { r, g, b } = RGBA.fromHex(bg)
357
+ return 0.299 * r + 0.587 * g + 0.114 * b > 0.5 ? "light" : "dark"
358
+ }
359
+
360
+ export function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJson {
361
+ const bg = RGBA.fromHex(colors.defaultBackground ?? colors.palette[0]!)
362
+ const fg = RGBA.fromHex(colors.defaultForeground ?? colors.palette[7]!)
363
+ const transparent = RGBA.fromValues(bg.r, bg.g, bg.b, 0)
364
+ const isDark = mode == "dark"
365
+
366
+ const col = (i: number) => {
367
+ const value = colors.palette[i]
368
+ if (value) return RGBA.fromHex(value)
369
+ return ansiToRgba(i)
370
+ }
371
+
372
+ // Generate gray scale based on terminal background
373
+ const grays = generateGrayScale(bg, isDark)
374
+ const textMuted = generateMutedTextColor(bg, isDark)
375
+
376
+ // ANSI color references
377
+ const ansiColors = {
378
+ black: col(0),
379
+ red: col(1),
380
+ green: col(2),
381
+ yellow: col(3),
382
+ blue: col(4),
383
+ magenta: col(5),
384
+ cyan: col(6),
385
+ white: col(7),
386
+ redBright: col(9),
387
+ greenBright: col(10),
388
+ }
389
+
390
+ const diffAlpha = isDark ? 0.22 : 0.14
391
+ const diffAddedBg = tint(bg, ansiColors.green, diffAlpha)
392
+ const diffRemovedBg = tint(bg, ansiColors.red, diffAlpha)
393
+ const diffContextBg = grays[2]
394
+ const diffAddedLineNumberBg = tint(diffContextBg, ansiColors.green, diffAlpha)
395
+ const diffRemovedLineNumberBg = tint(diffContextBg, ansiColors.red, diffAlpha)
396
+ const diffLineNumber = textMuted
397
+
398
+ return {
399
+ theme: {
400
+ // Primary colors using ANSI
401
+ primary: ansiColors.cyan,
402
+ secondary: ansiColors.magenta,
403
+ accent: ansiColors.cyan,
404
+
405
+ // Status colors using ANSI
406
+ error: ansiColors.red,
407
+ warning: ansiColors.yellow,
408
+ success: ansiColors.green,
409
+ info: ansiColors.cyan,
410
+
411
+ // Text colors
412
+ text: fg,
413
+ textMuted,
414
+ selectedListItemText: bg,
415
+
416
+ // Background colors - use transparent to respect terminal transparency
417
+ background: transparent,
418
+ backgroundPanel: grays[2],
419
+ backgroundElement: grays[3],
420
+ backgroundMenu: grays[3],
421
+
422
+ // Border colors
423
+ borderSubtle: grays[6],
424
+ border: grays[7],
425
+ borderActive: grays[8],
426
+
427
+ // Diff colors
428
+ diffAdded: ansiColors.green,
429
+ diffRemoved: ansiColors.red,
430
+ diffContext: grays[7],
431
+ diffHunkHeader: grays[7],
432
+ diffHighlightAdded: ansiColors.greenBright,
433
+ diffHighlightRemoved: ansiColors.redBright,
434
+ diffAddedBg,
435
+ diffRemovedBg,
436
+ diffContextBg,
437
+ diffLineNumber,
438
+ diffAddedLineNumberBg,
439
+ diffRemovedLineNumberBg,
440
+
441
+ // Markdown colors
442
+ markdownText: fg,
443
+ markdownHeading: fg,
444
+ markdownLink: ansiColors.blue,
445
+ markdownLinkText: ansiColors.cyan,
446
+ markdownCode: ansiColors.green,
447
+ markdownBlockQuote: ansiColors.yellow,
448
+ markdownEmph: ansiColors.yellow,
449
+ markdownStrong: fg,
450
+ markdownHorizontalRule: grays[7],
451
+ markdownListItem: ansiColors.blue,
452
+ markdownListEnumeration: ansiColors.cyan,
453
+ markdownImage: ansiColors.blue,
454
+ markdownImageText: ansiColors.cyan,
455
+ markdownCodeBlock: fg,
456
+
457
+ // Syntax colors
458
+ syntaxComment: textMuted,
459
+ syntaxKeyword: ansiColors.magenta,
460
+ syntaxFunction: ansiColors.blue,
461
+ syntaxVariable: fg,
462
+ syntaxString: ansiColors.green,
463
+ syntaxNumber: ansiColors.yellow,
464
+ syntaxType: ansiColors.cyan,
465
+ syntaxOperator: ansiColors.cyan,
466
+ syntaxPunctuation: fg,
467
+ },
468
+ }
469
+ }
470
+
471
+ function generateGrayScale(bg: RGBA, isDark: boolean): Record<number, RGBA> {
472
+ const grays: Record<number, RGBA> = {}
473
+
474
+ // RGBA stores floats in range 0-1, convert to 0-255
475
+ const bgR = bg.r * 255
476
+ const bgG = bg.g * 255
477
+ const bgB = bg.b * 255
478
+
479
+ const luminance = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB
480
+
481
+ for (let i = 1; i <= 12; i++) {
482
+ const factor = i / 12.0
483
+
484
+ let grayValue: number
485
+ let newR: number
486
+ let newG: number
487
+ let newB: number
488
+
489
+ if (isDark) {
490
+ if (luminance < 10) {
491
+ grayValue = Math.floor(factor * 0.4 * 255)
492
+ newR = grayValue
493
+ newG = grayValue
494
+ newB = grayValue
495
+ } else {
496
+ const newLum = luminance + (255 - luminance) * factor * 0.4
497
+
498
+ const ratio = newLum / luminance
499
+ newR = Math.min(bgR * ratio, 255)
500
+ newG = Math.min(bgG * ratio, 255)
501
+ newB = Math.min(bgB * ratio, 255)
502
+ }
503
+ } else {
504
+ if (luminance > 245) {
505
+ grayValue = Math.floor(255 - factor * 0.4 * 255)
506
+ newR = grayValue
507
+ newG = grayValue
508
+ newB = grayValue
509
+ } else {
510
+ const newLum = luminance * (1 - factor * 0.4)
511
+
512
+ const ratio = newLum / luminance
513
+ newR = Math.max(bgR * ratio, 0)
514
+ newG = Math.max(bgG * ratio, 0)
515
+ newB = Math.max(bgB * ratio, 0)
516
+ }
517
+ }
518
+
519
+ grays[i] = RGBA.fromInts(Math.floor(newR), Math.floor(newG), Math.floor(newB))
520
+ }
521
+
522
+ return grays
523
+ }
524
+
525
+ function generateMutedTextColor(bg: RGBA, isDark: boolean): RGBA {
526
+ // RGBA stores floats in range 0-1, convert to 0-255
527
+ const bgR = bg.r * 255
528
+ const bgG = bg.g * 255
529
+ const bgB = bg.b * 255
530
+
531
+ const bgLum = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB
532
+
533
+ let grayValue: number
534
+
535
+ if (isDark) {
536
+ if (bgLum < 10) {
537
+ // Very dark/black background
538
+ grayValue = 180 // #b4b4b4
539
+ } else {
540
+ // Scale up for lighter dark backgrounds
541
+ grayValue = Math.min(Math.floor(160 + bgLum * 0.3), 200)
542
+ }
543
+ } else {
544
+ if (bgLum > 245) {
545
+ // Very light/white background
546
+ grayValue = 75 // #4b4b4b
547
+ } else {
548
+ // Scale down for darker light backgrounds
549
+ grayValue = Math.max(Math.floor(100 - (255 - bgLum) * 0.2), 60)
550
+ }
551
+ }
552
+
553
+ return RGBA.fromInts(grayValue, grayValue, grayValue)
554
+ }
555
+
556
+ export function generateSyntax(theme: Theme) {
557
+ return SyntaxStyle.fromTheme(getSyntaxRules(theme))
558
+ }
559
+
560
+ export function generateSubtleSyntax(theme: Theme, overrides?: SyntaxStyleOverrides) {
561
+ const rules = getSyntaxRules(theme)
562
+ return SyntaxStyle.fromTheme(
563
+ rules.map((rule) => {
564
+ const override = rule.scope.reduce((acc, scope) => ({ ...acc, ...overrides?.[scope] }), {})
565
+ if (rule.style.foreground) {
566
+ const fg = rule.style.foreground
567
+ return {
568
+ ...rule,
569
+ style: {
570
+ ...rule.style,
571
+ ...override,
572
+ foreground: RGBA.fromInts(
573
+ Math.round(fg.r * 255),
574
+ Math.round(fg.g * 255),
575
+ Math.round(fg.b * 255),
576
+ Math.round(theme.thinkingOpacity * 255),
577
+ ),
578
+ },
579
+ }
580
+ }
581
+ return rule
582
+ }),
583
+ )
584
+ }
585
+
586
+ function getSyntaxRules(theme: Theme) {
587
+ return [
588
+ {
589
+ scope: ["default"],
590
+ style: {
591
+ foreground: theme.text,
592
+ },
593
+ },
594
+ {
595
+ scope: ["prompt"],
596
+ style: {
597
+ foreground: theme.accent,
598
+ },
599
+ },
600
+ {
601
+ scope: ["extmark.file"],
602
+ style: {
603
+ foreground: theme.warning,
604
+ bold: true,
605
+ },
606
+ },
607
+ {
608
+ scope: ["extmark.agent"],
609
+ style: {
610
+ foreground: theme.secondary,
611
+ bold: true,
612
+ },
613
+ },
614
+ {
615
+ scope: ["extmark.paste"],
616
+ style: {
617
+ foreground: selectedForeground(theme, theme.warning),
618
+ background: theme.warning,
619
+ bold: true,
620
+ },
621
+ },
622
+ {
623
+ scope: ["comment"],
624
+ style: {
625
+ foreground: theme.syntaxComment,
626
+ italic: true,
627
+ },
628
+ },
629
+ {
630
+ scope: ["comment.documentation"],
631
+ style: {
632
+ foreground: theme.syntaxComment,
633
+ italic: true,
634
+ },
635
+ },
636
+ {
637
+ scope: ["string", "symbol"],
638
+ style: {
639
+ foreground: theme.syntaxString,
640
+ },
641
+ },
642
+ {
643
+ scope: ["number", "boolean"],
644
+ style: {
645
+ foreground: theme.syntaxNumber,
646
+ },
647
+ },
648
+ {
649
+ scope: ["character.special"],
650
+ style: {
651
+ foreground: theme.syntaxString,
652
+ },
653
+ },
654
+ {
655
+ scope: ["keyword.return", "keyword.conditional", "keyword.repeat", "keyword.coroutine"],
656
+ style: {
657
+ foreground: theme.syntaxKeyword,
658
+ italic: true,
659
+ },
660
+ },
661
+ {
662
+ scope: ["keyword.type"],
663
+ style: {
664
+ foreground: theme.syntaxType,
665
+ bold: true,
666
+ italic: true,
667
+ },
668
+ },
669
+ {
670
+ scope: ["keyword.function", "function.method"],
671
+ style: {
672
+ foreground: theme.syntaxFunction,
673
+ },
674
+ },
675
+ {
676
+ scope: ["keyword"],
677
+ style: {
678
+ foreground: theme.syntaxKeyword,
679
+ italic: true,
680
+ },
681
+ },
682
+ {
683
+ scope: ["keyword.import"],
684
+ style: {
685
+ foreground: theme.syntaxKeyword,
686
+ },
687
+ },
688
+ {
689
+ scope: ["operator", "keyword.operator", "punctuation.delimiter"],
690
+ style: {
691
+ foreground: theme.syntaxOperator,
692
+ },
693
+ },
694
+ {
695
+ scope: ["keyword.conditional.ternary"],
696
+ style: {
697
+ foreground: theme.syntaxOperator,
698
+ },
699
+ },
700
+ {
701
+ scope: ["variable", "variable.parameter", "function.method.call", "function.call"],
702
+ style: {
703
+ foreground: theme.syntaxVariable,
704
+ },
705
+ },
706
+ {
707
+ scope: ["variable.member", "function", "constructor"],
708
+ style: {
709
+ foreground: theme.syntaxFunction,
710
+ },
711
+ },
712
+ {
713
+ scope: ["type", "module"],
714
+ style: {
715
+ foreground: theme.syntaxType,
716
+ },
717
+ },
718
+ {
719
+ scope: ["constant"],
720
+ style: {
721
+ foreground: theme.syntaxNumber,
722
+ },
723
+ },
724
+ {
725
+ scope: ["property"],
726
+ style: {
727
+ foreground: theme.syntaxVariable,
728
+ },
729
+ },
730
+ {
731
+ scope: ["class"],
732
+ style: {
733
+ foreground: theme.syntaxType,
734
+ },
735
+ },
736
+ {
737
+ scope: ["parameter"],
738
+ style: {
739
+ foreground: theme.syntaxVariable,
740
+ },
741
+ },
742
+ {
743
+ scope: ["punctuation", "punctuation.bracket"],
744
+ style: {
745
+ foreground: theme.syntaxPunctuation,
746
+ },
747
+ },
748
+ {
749
+ scope: ["variable.builtin", "type.builtin", "function.builtin", "module.builtin", "constant.builtin"],
750
+ style: {
751
+ foreground: theme.error,
752
+ },
753
+ },
754
+ {
755
+ scope: ["variable.super"],
756
+ style: {
757
+ foreground: theme.error,
758
+ },
759
+ },
760
+ {
761
+ scope: ["string.escape", "string.regexp"],
762
+ style: {
763
+ foreground: theme.syntaxKeyword,
764
+ },
765
+ },
766
+ {
767
+ scope: ["keyword.directive"],
768
+ style: {
769
+ foreground: theme.syntaxKeyword,
770
+ italic: true,
771
+ },
772
+ },
773
+ {
774
+ scope: ["punctuation.special"],
775
+ style: {
776
+ foreground: theme.syntaxOperator,
777
+ },
778
+ },
779
+ {
780
+ scope: ["keyword.modifier"],
781
+ style: {
782
+ foreground: theme.syntaxKeyword,
783
+ italic: true,
784
+ },
785
+ },
786
+ {
787
+ scope: ["keyword.exception"],
788
+ style: {
789
+ foreground: theme.syntaxKeyword,
790
+ italic: true,
791
+ },
792
+ },
793
+ // Markdown specific styles
794
+ {
795
+ scope: ["markup.heading"],
796
+ style: {
797
+ foreground: theme.markdownHeading,
798
+ bold: true,
799
+ },
800
+ },
801
+ {
802
+ scope: ["markup.heading.1"],
803
+ style: {
804
+ foreground: theme.markdownHeading,
805
+ bold: true,
806
+ underline: true,
807
+ },
808
+ },
809
+ {
810
+ scope: ["markup.heading.2"],
811
+ style: {
812
+ foreground: theme.markdownHeading,
813
+ bold: true,
814
+ },
815
+ },
816
+ {
817
+ scope: ["markup.heading.3"],
818
+ style: {
819
+ foreground: theme.markdownHeading,
820
+ bold: true,
821
+ },
822
+ },
823
+ {
824
+ scope: ["markup.heading.4"],
825
+ style: {
826
+ foreground: theme.markdownHeading,
827
+ bold: true,
828
+ },
829
+ },
830
+ {
831
+ scope: ["markup.heading.5"],
832
+ style: {
833
+ foreground: theme.markdownHeading,
834
+ bold: true,
835
+ },
836
+ },
837
+ {
838
+ scope: ["markup.heading.6"],
839
+ style: {
840
+ foreground: theme.markdownHeading,
841
+ bold: true,
842
+ },
843
+ },
844
+ {
845
+ scope: ["markup.bold", "markup.strong"],
846
+ style: {
847
+ foreground: theme.markdownStrong,
848
+ bold: true,
849
+ },
850
+ },
851
+ {
852
+ scope: ["markup.italic"],
853
+ style: {
854
+ foreground: theme.markdownEmph,
855
+ italic: true,
856
+ },
857
+ },
858
+ {
859
+ scope: ["markup.list"],
860
+ style: {
861
+ foreground: theme.markdownListItem,
862
+ },
863
+ },
864
+ {
865
+ scope: ["markup.quote"],
866
+ style: {
867
+ foreground: theme.markdownBlockQuote,
868
+ italic: true,
869
+ },
870
+ },
871
+ {
872
+ scope: ["markup.raw", "markup.raw.block"],
873
+ style: {
874
+ foreground: theme.markdownCode,
875
+ },
876
+ },
877
+ {
878
+ scope: ["markup.raw.inline"],
879
+ style: {
880
+ foreground: theme.markdownCode,
881
+ background: theme.background,
882
+ },
883
+ },
884
+ {
885
+ scope: ["markup.link"],
886
+ style: {
887
+ foreground: theme.markdownLink,
888
+ underline: true,
889
+ },
890
+ },
891
+ {
892
+ scope: ["markup.link.label"],
893
+ style: {
894
+ foreground: theme.markdownLinkText,
895
+ underline: true,
896
+ },
897
+ },
898
+ {
899
+ scope: ["markup.link.url"],
900
+ style: {
901
+ foreground: theme.markdownLink,
902
+ underline: true,
903
+ },
904
+ },
905
+ {
906
+ scope: ["label"],
907
+ style: {
908
+ foreground: theme.markdownLinkText,
909
+ },
910
+ },
911
+ {
912
+ scope: ["spell", "nospell"],
913
+ style: {
914
+ foreground: theme.text,
915
+ },
916
+ },
917
+ {
918
+ scope: ["conceal"],
919
+ style: {
920
+ foreground: theme.textMuted,
921
+ },
922
+ },
923
+ // Additional common highlight groups
924
+ {
925
+ scope: ["string.special", "string.special.url"],
926
+ style: {
927
+ foreground: theme.markdownLink,
928
+ underline: true,
929
+ },
930
+ },
931
+ {
932
+ scope: ["character"],
933
+ style: {
934
+ foreground: theme.syntaxString,
935
+ },
936
+ },
937
+ {
938
+ scope: ["float"],
939
+ style: {
940
+ foreground: theme.syntaxNumber,
941
+ },
942
+ },
943
+ {
944
+ scope: ["comment.error"],
945
+ style: {
946
+ foreground: theme.error,
947
+ italic: true,
948
+ bold: true,
949
+ },
950
+ },
951
+ {
952
+ scope: ["comment.warning"],
953
+ style: {
954
+ foreground: theme.warning,
955
+ italic: true,
956
+ bold: true,
957
+ },
958
+ },
959
+ {
960
+ scope: ["comment.todo", "comment.note"],
961
+ style: {
962
+ foreground: theme.info,
963
+ italic: true,
964
+ bold: true,
965
+ },
966
+ },
967
+ {
968
+ scope: ["namespace"],
969
+ style: {
970
+ foreground: theme.syntaxType,
971
+ },
972
+ },
973
+ {
974
+ scope: ["field"],
975
+ style: {
976
+ foreground: theme.syntaxVariable,
977
+ },
978
+ },
979
+ {
980
+ scope: ["type.definition"],
981
+ style: {
982
+ foreground: theme.syntaxType,
983
+ bold: true,
984
+ },
985
+ },
986
+ {
987
+ scope: ["keyword.export"],
988
+ style: {
989
+ foreground: theme.syntaxKeyword,
990
+ },
991
+ },
992
+ {
993
+ scope: ["attribute", "annotation"],
994
+ style: {
995
+ foreground: theme.warning,
996
+ },
997
+ },
998
+ {
999
+ scope: ["tag"],
1000
+ style: {
1001
+ foreground: theme.error,
1002
+ },
1003
+ },
1004
+ {
1005
+ scope: ["tag.attribute"],
1006
+ style: {
1007
+ foreground: theme.syntaxKeyword,
1008
+ },
1009
+ },
1010
+ {
1011
+ scope: ["tag.delimiter"],
1012
+ style: {
1013
+ foreground: theme.syntaxOperator,
1014
+ },
1015
+ },
1016
+ {
1017
+ scope: ["markup.strikethrough"],
1018
+ style: {
1019
+ foreground: theme.textMuted,
1020
+ },
1021
+ },
1022
+ {
1023
+ scope: ["markup.underline"],
1024
+ style: {
1025
+ foreground: theme.text,
1026
+ underline: true,
1027
+ },
1028
+ },
1029
+ {
1030
+ scope: ["markup.list.checked"],
1031
+ style: {
1032
+ foreground: theme.success,
1033
+ },
1034
+ },
1035
+ {
1036
+ scope: ["markup.list.unchecked"],
1037
+ style: {
1038
+ foreground: theme.textMuted,
1039
+ },
1040
+ },
1041
+ {
1042
+ scope: ["diff.plus"],
1043
+ style: {
1044
+ foreground: theme.diffAdded,
1045
+ background: theme.diffAddedBg,
1046
+ },
1047
+ },
1048
+ {
1049
+ scope: ["diff.minus"],
1050
+ style: {
1051
+ foreground: theme.diffRemoved,
1052
+ background: theme.diffRemovedBg,
1053
+ },
1054
+ },
1055
+ {
1056
+ scope: ["diff.delta"],
1057
+ style: {
1058
+ foreground: theme.diffContext,
1059
+ background: theme.diffContextBg,
1060
+ },
1061
+ },
1062
+ {
1063
+ scope: ["error"],
1064
+ style: {
1065
+ foreground: theme.error,
1066
+ bold: true,
1067
+ },
1068
+ },
1069
+ {
1070
+ scope: ["warning"],
1071
+ style: {
1072
+ foreground: theme.warning,
1073
+ bold: true,
1074
+ },
1075
+ },
1076
+ {
1077
+ scope: ["info"],
1078
+ style: {
1079
+ foreground: theme.info,
1080
+ },
1081
+ },
1082
+ {
1083
+ scope: ["debug"],
1084
+ style: {
1085
+ foreground: theme.textMuted,
1086
+ },
1087
+ },
1088
+ ]
1089
+ }