@agentprojectcontext/apx 1.15.6 → 1.16.0

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