@brimveyn/aimux-config 0.3.5 → 0.4.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 (76) hide show
  1. package/README.md +45 -28
  2. package/package.json +4 -1
  3. package/src/actions.ts +110 -10
  4. package/src/defaults.ts +37 -8
  5. package/src/house-themes.ts +313 -0
  6. package/src/index.ts +18 -3
  7. package/src/normalize-theme.ts +260 -0
  8. package/src/resolver.ts +1 -0
  9. package/src/themes/generated/andromeeda.ts +346 -0
  10. package/src/themes/generated/aurora-x.ts +603 -0
  11. package/src/themes/generated/ayu-dark.ts +817 -0
  12. package/src/themes/generated/ayu-light.ts +817 -0
  13. package/src/themes/generated/ayu-mirage.ts +817 -0
  14. package/src/themes/generated/catppuccin-frappe.ts +2041 -0
  15. package/src/themes/generated/catppuccin-latte.ts +2041 -0
  16. package/src/themes/generated/catppuccin-macchiato.ts +2041 -0
  17. package/src/themes/generated/catppuccin-mocha.ts +2041 -0
  18. package/src/themes/generated/dark-plus.ts +614 -0
  19. package/src/themes/generated/dracula-soft.ts +947 -0
  20. package/src/themes/generated/dracula.ts +947 -0
  21. package/src/themes/generated/everforest-dark.ts +2246 -0
  22. package/src/themes/generated/everforest-light.ts +2246 -0
  23. package/src/themes/generated/github-dark-default.ts +605 -0
  24. package/src/themes/generated/github-dark-dimmed.ts +605 -0
  25. package/src/themes/generated/github-dark-high-contrast.ts +608 -0
  26. package/src/themes/generated/github-dark.ts +519 -0
  27. package/src/themes/generated/github-light-default.ts +602 -0
  28. package/src/themes/generated/github-light-high-contrast.ts +604 -0
  29. package/src/themes/generated/github-light.ts +515 -0
  30. package/src/themes/generated/gruvbox-dark-hard.ts +1124 -0
  31. package/src/themes/generated/gruvbox-dark-medium.ts +1124 -0
  32. package/src/themes/generated/gruvbox-dark-soft.ts +1124 -0
  33. package/src/themes/generated/gruvbox-light-hard.ts +1124 -0
  34. package/src/themes/generated/gruvbox-light-medium.ts +1124 -0
  35. package/src/themes/generated/gruvbox-light-soft.ts +1124 -0
  36. package/src/themes/generated/horizon-bright.ts +420 -0
  37. package/src/themes/generated/horizon.ts +418 -0
  38. package/src/themes/generated/houston.ts +1819 -0
  39. package/src/themes/generated/index.ts +140 -0
  40. package/src/themes/generated/kanagawa-dragon.ts +753 -0
  41. package/src/themes/generated/kanagawa-lotus.ts +753 -0
  42. package/src/themes/generated/kanagawa-wave.ts +753 -0
  43. package/src/themes/generated/laserwave.ts +489 -0
  44. package/src/themes/generated/light-plus.ts +639 -0
  45. package/src/themes/generated/material-theme-darker.ts +759 -0
  46. package/src/themes/generated/material-theme-lighter.ts +759 -0
  47. package/src/themes/generated/material-theme-ocean.ts +759 -0
  48. package/src/themes/generated/material-theme-palenight.ts +759 -0
  49. package/src/themes/generated/material-theme.ts +759 -0
  50. package/src/themes/generated/min-dark.ts +326 -0
  51. package/src/themes/generated/min-light.ts +331 -0
  52. package/src/themes/generated/monokai.ts +463 -0
  53. package/src/themes/generated/night-owl-light.ts +1526 -0
  54. package/src/themes/generated/night-owl.ts +1628 -0
  55. package/src/themes/generated/nord.ts +1277 -0
  56. package/src/themes/generated/one-dark-pro.ts +1920 -0
  57. package/src/themes/generated/one-light.ts +1599 -0
  58. package/src/themes/generated/plastic.ts +315 -0
  59. package/src/themes/generated/poimandres.ts +1233 -0
  60. package/src/themes/generated/red.ts +395 -0
  61. package/src/themes/generated/rose-pine-dawn.ts +696 -0
  62. package/src/themes/generated/rose-pine-moon.ts +696 -0
  63. package/src/themes/generated/rose-pine.ts +696 -0
  64. package/src/themes/generated/slack-dark.ts +594 -0
  65. package/src/themes/generated/slack-ochin.ts +461 -0
  66. package/src/themes/generated/snazzy-light.ts +1254 -0
  67. package/src/themes/generated/solarized-dark.ts +387 -0
  68. package/src/themes/generated/solarized-light.ts +379 -0
  69. package/src/themes/generated/synthwave-84.ts +751 -0
  70. package/src/themes/generated/tokyo-night.ts +1420 -0
  71. package/src/themes/generated/vesper.ts +578 -0
  72. package/src/themes/generated/vitesse-black.ts +629 -0
  73. package/src/themes/generated/vitesse-dark.ts +629 -0
  74. package/src/themes/generated/vitesse-light.ts +627 -0
  75. package/src/themes.ts +86 -238
  76. package/src/types.ts +181 -40
@@ -0,0 +1,313 @@
1
+ // Hand-curated aimux house themes, full Shiki theme shape. Colors follow the
2
+ // VSCode workbench color key namespace; `settings` is a TextMate token rule
3
+ // array shared by the in-repo diff highlighter and `shiki.loadTheme`.
4
+ /* eslint-disable perfectionist/sort-objects */
5
+
6
+ import type { Theme, ThemeColorMap } from './types'
7
+
8
+ // --- aimux --------------------------------------------------------------
9
+ //
10
+ // Teal accents on a deep blue-black background. Designed to look at home
11
+ // inside the aimux chrome.
12
+ const AIMUX_BG = '#11151b'
13
+ const AIMUX_FG = '#edf4ff'
14
+
15
+ const AIMUX_COLORS: ThemeColorMap = {
16
+ 'contrastBorder': '#3a4c60',
17
+ 'descriptionForeground': '#6b829a',
18
+ 'diffEditor.insertedLineBackground': '#1e3d2b',
19
+ 'diffEditor.removedLineBackground': '#3b1e27',
20
+ 'editor.background': AIMUX_BG,
21
+ 'editor.foreground': AIMUX_FG,
22
+ 'editor.lineHighlightBackground': '#1a2330',
23
+ 'editor.selectionBackground': '#1f3344',
24
+ 'editorCursor.foreground': '#7cd1b8',
25
+ 'editorError.foreground': '#f38ba8',
26
+ 'editorGroup.border': '#2d3f52',
27
+ 'editorGroupHeader.tabsBackground': '#16202b',
28
+ 'editorLineNumber.foreground': '#3e506a',
29
+ 'editorWarning.foreground': '#f6c177',
30
+ 'editorWidget.background': '#0b1016',
31
+ 'errorForeground': '#f38ba8',
32
+ 'focusBorder': '#7cd1b8',
33
+ 'foreground': AIMUX_FG,
34
+ 'gitDecoration.addedResourceForeground': '#8bd5ca',
35
+ 'gitDecoration.deletedResourceForeground': '#f38ba8',
36
+ 'gitDecoration.modifiedResourceForeground': '#f6c177',
37
+ 'gitDecoration.untrackedResourceForeground': '#8bd5ca',
38
+ 'input.background': '#1c2734',
39
+ 'input.border': '#2d3f52',
40
+ 'input.foreground': AIMUX_FG,
41
+ 'list.activeSelectionBackground': '#1f3344',
42
+ 'list.activeSelectionForeground': AIMUX_FG,
43
+ 'list.hoverBackground': '#1a2330',
44
+ 'panel.background': '#16202b',
45
+ 'panel.border': '#2d3f52',
46
+ 'sideBar.background': '#16202b',
47
+ 'sideBar.foreground': AIMUX_FG,
48
+ 'sideBarSectionHeader.background': '#1c2734',
49
+ 'sideBarSectionHeader.foreground': '#b8c5d6',
50
+ 'terminal.ansiBlue': '#7cb3ff',
51
+ 'terminal.ansiCyan': '#7cd1b8',
52
+ 'terminal.ansiGreen': '#8bd5ca',
53
+ 'terminal.ansiMagenta': '#c4a7e7',
54
+ 'terminal.ansiRed': '#f38ba8',
55
+ 'terminal.ansiYellow': '#f6c177',
56
+ 'textLink.activeForeground': '#7cd1b8',
57
+ 'textLink.foreground': '#7cd1b8',
58
+ }
59
+
60
+ // --- dracula-at-night --------------------------------------------------
61
+ const DAN_BG = '#0e1419'
62
+ const DAN_FG = '#f8f8f2'
63
+
64
+ const DAN_COLORS: ThemeColorMap = {
65
+ 'contrastBorder': '#44475a',
66
+ 'descriptionForeground': '#6272a4',
67
+ 'diffEditor.insertedLineBackground': '#162b1d',
68
+ 'diffEditor.removedLineBackground': '#2a1820',
69
+ 'editor.background': DAN_BG,
70
+ 'editor.foreground': DAN_FG,
71
+ 'editor.lineHighlightBackground': '#1a2129',
72
+ 'editor.selectionBackground': '#222a33',
73
+ 'editorCursor.foreground': '#f8f8f0',
74
+ 'editorError.foreground': '#ff5555',
75
+ 'editorGroup.border': '#2a3440',
76
+ 'editorGroupHeader.tabsBackground': '#131920',
77
+ 'editorLineNumber.foreground': '#495466',
78
+ 'editorWarning.foreground': '#f1fa8c',
79
+ 'editorWidget.background': '#090d11',
80
+ 'errorForeground': '#ff5555',
81
+ 'focusBorder': '#bd93f9',
82
+ 'foreground': DAN_FG,
83
+ 'gitDecoration.addedResourceForeground': '#50fa7b',
84
+ 'gitDecoration.deletedResourceForeground': '#ff5555',
85
+ 'gitDecoration.modifiedResourceForeground': '#f1fa8c',
86
+ 'gitDecoration.untrackedResourceForeground': '#50fa7b',
87
+ 'input.background': '#1a2129',
88
+ 'input.border': '#2a3440',
89
+ 'input.foreground': DAN_FG,
90
+ 'list.activeSelectionBackground': '#222a33',
91
+ 'list.activeSelectionForeground': DAN_FG,
92
+ 'list.hoverBackground': '#1a2129',
93
+ 'panel.background': '#131920',
94
+ 'panel.border': '#2a3440',
95
+ 'sideBar.background': '#131920',
96
+ 'sideBar.foreground': DAN_FG,
97
+ 'sideBarSectionHeader.background': '#1a2129',
98
+ 'sideBarSectionHeader.foreground': '#bdc3cf',
99
+ 'terminal.ansiBlue': '#6272a4',
100
+ 'terminal.ansiCyan': '#8be9fd',
101
+ 'terminal.ansiGreen': '#50fa7b',
102
+ 'terminal.ansiMagenta': '#ff79c6',
103
+ 'terminal.ansiRed': '#ff5555',
104
+ 'terminal.ansiYellow': '#f1fa8c',
105
+ 'textLink.activeForeground': '#ff79c6',
106
+ 'textLink.foreground': '#bd93f9',
107
+ }
108
+
109
+ // --- Shared TextMate rule helpers --------------------------------------
110
+
111
+ const AIMUX_SETTINGS = [
112
+ { settings: { background: AIMUX_BG, foreground: AIMUX_FG } },
113
+ {
114
+ scope: ['comment', 'punctuation.definition.comment'],
115
+ settings: { fontStyle: 'italic', foreground: '#6b829a' },
116
+ },
117
+ { scope: ['comment.documentation'], settings: { foreground: '#8fa6bf', fontStyle: 'italic' } },
118
+ { scope: ['string', 'string.quoted'], settings: { foreground: '#f6c177' } },
119
+ { scope: ['string.template'], settings: { foreground: '#f6c177' } },
120
+ {
121
+ scope: ['string.regexp', 'string.escape', 'constant.character.escape'],
122
+ settings: { foreground: '#c4a7e7' },
123
+ },
124
+ {
125
+ scope: ['constant.numeric', 'constant.language', 'constant.language.boolean'],
126
+ settings: { foreground: '#f38ba8' },
127
+ },
128
+ { scope: ['constant.character', 'constant.other'], settings: { foreground: '#f38ba8' } },
129
+ {
130
+ scope: ['keyword', 'keyword.control', 'keyword.operator.new', 'keyword.other'],
131
+ settings: { fontStyle: 'bold', foreground: '#c4a7e7' },
132
+ },
133
+ {
134
+ scope: ['storage', 'storage.type', 'storage.modifier'],
135
+ settings: { fontStyle: 'bold', foreground: '#c4a7e7' },
136
+ },
137
+ {
138
+ scope: ['keyword.operator', 'punctuation', 'meta.brace', 'punctuation.separator'],
139
+ settings: { foreground: '#6b829a' },
140
+ },
141
+ {
142
+ scope: [
143
+ 'entity.name.function',
144
+ 'support.function',
145
+ 'meta.function-call',
146
+ 'entity.name.function.member',
147
+ ],
148
+ settings: { foreground: '#8bd5ca' },
149
+ },
150
+ {
151
+ scope: [
152
+ 'entity.name.type',
153
+ 'support.type',
154
+ 'support.class',
155
+ 'entity.name.class',
156
+ 'entity.name.interface',
157
+ 'entity.name.namespace',
158
+ ],
159
+ settings: { fontStyle: 'italic', foreground: '#7cd1b8' },
160
+ },
161
+ {
162
+ scope: ['entity.name.type.enum', 'entity.name.type.alias'],
163
+ settings: { foreground: '#7cd1b8' },
164
+ },
165
+ { scope: ['variable', 'meta.variable'], settings: { foreground: AIMUX_FG } },
166
+ {
167
+ scope: ['variable.parameter', 'meta.function.parameters'],
168
+ settings: { fontStyle: 'italic', foreground: AIMUX_FG },
169
+ },
170
+ {
171
+ scope: ['variable.other.property', 'variable.other.member', 'meta.object-literal.key'],
172
+ settings: { foreground: '#7cd1b8' },
173
+ },
174
+ {
175
+ scope: ['variable.language', 'variable.language.this'],
176
+ settings: { foreground: '#f38ba8', fontStyle: 'italic' },
177
+ },
178
+ { scope: ['entity.name.tag', 'meta.tag'], settings: { foreground: '#f38ba8' } },
179
+ {
180
+ scope: ['entity.other.attribute-name'],
181
+ settings: { fontStyle: 'italic', foreground: '#f6c177' },
182
+ },
183
+ { scope: ['support.constant'], settings: { foreground: '#f38ba8' } },
184
+ { scope: ['support.variable'], settings: { foreground: AIMUX_FG } },
185
+ // Markdown
186
+ { scope: ['markup.heading'], settings: { fontStyle: 'bold', foreground: '#7cd1b8' } },
187
+ { scope: ['markup.bold'], settings: { fontStyle: 'bold', foreground: AIMUX_FG } },
188
+ { scope: ['markup.italic'], settings: { fontStyle: 'italic', foreground: AIMUX_FG } },
189
+ { scope: ['markup.underline.link', 'markup.inline.raw'], settings: { foreground: '#8bd5ca' } },
190
+ { scope: ['markup.list'], settings: { foreground: '#c4a7e7' } },
191
+ { scope: ['markup.quote'], settings: { foreground: '#6b829a', fontStyle: 'italic' } },
192
+ { scope: ['markup.raw'], settings: { foreground: '#f6c177' } },
193
+ // JSX / TSX
194
+ {
195
+ scope: ['entity.name.tag.jsx', 'support.class.component'],
196
+ settings: { foreground: '#7cd1b8' },
197
+ },
198
+ { scope: ['meta.jsx.children'], settings: { foreground: AIMUX_FG } },
199
+ // Diff
200
+ { scope: ['markup.inserted', 'meta.diff.header.to-file'], settings: { foreground: '#8bd5ca' } },
201
+ { scope: ['markup.deleted', 'meta.diff.header.from-file'], settings: { foreground: '#f38ba8' } },
202
+ { scope: ['markup.changed'], settings: { foreground: '#f6c177' } },
203
+ { scope: ['meta.diff.range'], settings: { foreground: '#c4a7e7' } },
204
+ ]
205
+
206
+ const DAN_SETTINGS = [
207
+ { settings: { background: DAN_BG, foreground: DAN_FG } },
208
+ {
209
+ scope: ['comment', 'punctuation.definition.comment'],
210
+ settings: { fontStyle: 'italic', foreground: '#6272a4' },
211
+ },
212
+ { scope: ['comment.documentation'], settings: { fontStyle: 'italic', foreground: '#8a96b4' } },
213
+ { scope: ['string', 'string.quoted'], settings: { foreground: '#f1fa8c' } },
214
+ { scope: ['string.template'], settings: { foreground: '#f1fa8c' } },
215
+ {
216
+ scope: ['string.regexp', 'string.escape', 'constant.character.escape'],
217
+ settings: { foreground: '#ff79c6' },
218
+ },
219
+ {
220
+ scope: ['constant.numeric', 'constant.language', 'constant.language.boolean'],
221
+ settings: { foreground: '#bd93f9' },
222
+ },
223
+ { scope: ['constant.character', 'constant.other'], settings: { foreground: '#bd93f9' } },
224
+ {
225
+ scope: ['keyword', 'keyword.control', 'keyword.other'],
226
+ settings: { fontStyle: 'bold', foreground: '#ff79c6' },
227
+ },
228
+ {
229
+ scope: ['storage', 'storage.type', 'storage.modifier'],
230
+ settings: { fontStyle: 'bold italic', foreground: '#ff79c6' },
231
+ },
232
+ {
233
+ scope: ['keyword.operator', 'punctuation', 'meta.brace', 'punctuation.separator'],
234
+ settings: { foreground: '#f8f8f2' },
235
+ },
236
+ {
237
+ scope: ['entity.name.function', 'support.function', 'meta.function-call'],
238
+ settings: { foreground: '#50fa7b' },
239
+ },
240
+ {
241
+ scope: [
242
+ 'entity.name.type',
243
+ 'support.type',
244
+ 'support.class',
245
+ 'entity.name.class',
246
+ 'entity.name.interface',
247
+ ],
248
+ settings: { fontStyle: 'italic', foreground: '#8be9fd' },
249
+ },
250
+ {
251
+ scope: ['entity.name.type.enum', 'entity.name.type.alias'],
252
+ settings: { foreground: '#8be9fd' },
253
+ },
254
+ { scope: ['variable', 'meta.variable'], settings: { foreground: DAN_FG } },
255
+ {
256
+ scope: ['variable.parameter', 'meta.function.parameters'],
257
+ settings: { fontStyle: 'italic', foreground: '#ffb86c' },
258
+ },
259
+ {
260
+ scope: ['variable.other.property', 'variable.other.member', 'meta.object-literal.key'],
261
+ settings: { foreground: '#8be9fd' },
262
+ },
263
+ {
264
+ scope: ['variable.language', 'variable.language.this'],
265
+ settings: { foreground: '#bd93f9', fontStyle: 'italic' },
266
+ },
267
+ { scope: ['entity.name.tag', 'meta.tag'], settings: { foreground: '#ff79c6' } },
268
+ {
269
+ scope: ['entity.other.attribute-name'],
270
+ settings: { fontStyle: 'italic', foreground: '#50fa7b' },
271
+ },
272
+ { scope: ['support.constant'], settings: { foreground: '#bd93f9' } },
273
+ // Markdown
274
+ { scope: ['markup.heading'], settings: { fontStyle: 'bold', foreground: '#bd93f9' } },
275
+ { scope: ['markup.bold'], settings: { fontStyle: 'bold', foreground: '#ffb86c' } },
276
+ { scope: ['markup.italic'], settings: { fontStyle: 'italic', foreground: '#f1fa8c' } },
277
+ { scope: ['markup.underline.link', 'markup.inline.raw'], settings: { foreground: '#8be9fd' } },
278
+ { scope: ['markup.list'], settings: { foreground: '#ff79c6' } },
279
+ { scope: ['markup.quote'], settings: { foreground: '#6272a4', fontStyle: 'italic' } },
280
+ { scope: ['markup.raw'], settings: { foreground: '#f1fa8c' } },
281
+ // JSX / TSX
282
+ {
283
+ scope: ['entity.name.tag.jsx', 'support.class.component'],
284
+ settings: { foreground: '#8be9fd' },
285
+ },
286
+ // Diff
287
+ { scope: ['markup.inserted'], settings: { foreground: '#50fa7b' } },
288
+ { scope: ['markup.deleted'], settings: { foreground: '#ff5555' } },
289
+ { scope: ['markup.changed'], settings: { foreground: '#f1fa8c' } },
290
+ ]
291
+
292
+ export const HOUSE_THEMES: Record<string, Theme> = {
293
+ 'aimux': {
294
+ bg: AIMUX_BG,
295
+ colors: AIMUX_COLORS,
296
+ displayName: 'Aimux',
297
+ fg: AIMUX_FG,
298
+ name: 'aimux',
299
+ settings: AIMUX_SETTINGS,
300
+ type: 'dark',
301
+ },
302
+ 'dracula-at-night': {
303
+ bg: DAN_BG,
304
+ colors: DAN_COLORS,
305
+ displayName: 'Dracula At Night',
306
+ fg: DAN_FG,
307
+ name: 'dracula-at-night',
308
+ settings: DAN_SETTINGS,
309
+ type: 'dark',
310
+ },
311
+ }
312
+
313
+ export const HOUSE_THEME_IDS: string[] = Object.keys(HOUSE_THEMES)
package/src/index.ts CHANGED
@@ -2,7 +2,15 @@
2
2
 
3
3
  export { defineConfig } from './define-config'
4
4
  export * as actions from './actions'
5
- export { THEME_IDS, themes, THEMES } from './themes'
5
+ export {
6
+ isKnownThemeId,
7
+ migrateThemeId,
8
+ registerUserThemes,
9
+ THEME_IDS,
10
+ themes,
11
+ THEMES,
12
+ } from './themes'
13
+ export { normalizeTheme } from './normalize-theme'
6
14
  export { GroupBuilder, KeymapBuilder, ModeBindingBuilder } from './keymap-builder'
7
15
  export { getDefaultKeymapConfig } from './defaults'
8
16
  export { resolveConfig } from './resolver'
@@ -12,6 +20,7 @@ export type {
12
20
  // Action types
13
21
  Action,
14
22
  ActionFn,
23
+ AimuxColorKey,
15
24
  // User-facing config
16
25
  AimuxUserConfig,
17
26
  AppAction,
@@ -19,6 +28,7 @@ export type {
19
28
  BackendConfig,
20
29
  BindingDef,
21
30
  FocusMode,
31
+ GitFileListMode,
22
32
  GitPaneConfig,
23
33
  GitPaneDiffCountConfig,
24
34
  GitPaneEmbeddedConfig,
@@ -39,6 +49,8 @@ export type {
39
49
  // Mode / state
40
50
  ModeId,
41
51
  ModeKeymapDef,
52
+ NamedTheme,
53
+ NamedThemeDefinition,
42
54
  // Resolved config (internal, but exported for tooling)
43
55
  ResolvedConfig,
44
56
  ResolvedKeymapConfig,
@@ -49,8 +61,11 @@ export type {
49
61
  SnippetRecord,
50
62
  SplitDirection,
51
63
  TabSession,
52
- // Themes
53
- ThemeColors,
64
+ Theme,
65
+ ThemeColorMap,
54
66
  ThemeDefinition,
55
67
  ThemeId,
68
+ ThemeSettings,
69
+ ThemeTokenRule,
56
70
  } from './types'
71
+ export { AIMUX_COLOR_KEYS } from './types'
@@ -0,0 +1,260 @@
1
+ // Normalize a partial theme input into a full aimux `Theme` where every
2
+ // AIMUX_COLOR_KEY is guaranteed to have a value. Used at build time by the
3
+ // generator script and at runtime by registerUserThemes — same code path, same
4
+ // guarantees.
5
+
6
+ import {
7
+ AIMUX_COLOR_KEYS,
8
+ type AimuxColorKey,
9
+ type Theme,
10
+ type ThemeColorMap,
11
+ type ThemeTokenRule,
12
+ } from './types'
13
+
14
+ export interface NormalizeInput {
15
+ name: string
16
+ displayName?: string
17
+ type?: 'dark' | 'light'
18
+ fg?: string
19
+ bg?: string
20
+ colors?: Record<string, string | null | undefined>
21
+ settings?: ThemeTokenRule[]
22
+ tokenColors?: ThemeTokenRule[]
23
+ colorReplacements?: Record<string, string>
24
+ semanticHighlighting?: boolean
25
+ semanticTokenColors?: Theme['semanticTokenColors']
26
+ }
27
+
28
+ function stripAlpha(hex: string | null | undefined): string {
29
+ if (!hex || typeof hex !== 'string') return '#000000'
30
+ if (hex.length === 9) return hex.slice(0, 7)
31
+ if (hex.length === 5) return hex.slice(0, 4)
32
+ return hex
33
+ }
34
+
35
+ function expand3(hex: string): string {
36
+ if (hex.length === 4) {
37
+ return `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`
38
+ }
39
+ return hex
40
+ }
41
+
42
+ function parseHex(hex: string): { r: number; g: number; b: number } {
43
+ const clean = expand3(stripAlpha(hex))
44
+ return {
45
+ b: parseInt(clean.slice(5, 7), 16),
46
+ g: parseInt(clean.slice(3, 5), 16),
47
+ r: parseInt(clean.slice(1, 3), 16),
48
+ }
49
+ }
50
+
51
+ function toHex({ b, g, r }: { b: number; g: number; r: number }): string {
52
+ const clamp = (n: number): number => Math.max(0, Math.min(255, Math.round(n)))
53
+ const h = (n: number): string => clamp(n).toString(16).padStart(2, '0')
54
+ return `#${h(r)}${h(g)}${h(b)}`
55
+ }
56
+
57
+ export function shade(hex: string, amount: number): string {
58
+ const { b, g, r } = parseHex(hex)
59
+ const d = amount * 255
60
+ return toHex({ b: b + d, g: g + d, r: r + d })
61
+ }
62
+
63
+ export function mix(a: string, b: string, t: number): string {
64
+ const ca = parseHex(a)
65
+ const cb = parseHex(b)
66
+ return toHex({
67
+ b: ca.b + (cb.b - ca.b) * t,
68
+ g: ca.g + (cb.g - ca.g) * t,
69
+ r: ca.r + (cb.r - ca.r) * t,
70
+ })
71
+ }
72
+
73
+ function first(map: Record<string, string | null | undefined>, keys: string[]): string | undefined {
74
+ for (const k of keys) {
75
+ const v = map[k]
76
+ if (v && typeof v === 'string') return stripAlpha(v)
77
+ }
78
+ return undefined
79
+ }
80
+
81
+ const FALLBACKS: Record<AimuxColorKey, string[]> = {
82
+ 'contrastBorder': ['editorGroup.border'],
83
+ 'descriptionForeground': ['editorLineNumber.foreground', 'foreground'],
84
+ 'diffEditor.insertedLineBackground': [],
85
+ 'diffEditor.removedLineBackground': [],
86
+ 'editor.background': [],
87
+ 'editor.foreground': [],
88
+ 'editor.lineHighlightBackground': [],
89
+ 'editor.selectionBackground': ['selection.background'],
90
+ 'editorCursor.foreground': ['editor.foreground'],
91
+ 'editorError.foreground': ['errorForeground', 'terminal.ansiRed'],
92
+ 'editorGroup.border': ['contrastBorder', 'panel.border'],
93
+ 'editorGroupHeader.tabsBackground': ['sideBar.background'],
94
+ 'editorLineNumber.foreground': ['descriptionForeground'],
95
+ 'editorWarning.foreground': ['terminal.ansiYellow'],
96
+ 'editorWidget.background': ['sideBar.background', 'editor.background'],
97
+ 'errorForeground': ['editorError.foreground', 'terminal.ansiRed'],
98
+ 'focusBorder': ['textLink.foreground'],
99
+ 'foreground': ['editor.foreground'],
100
+ 'gitDecoration.addedResourceForeground': ['terminal.ansiGreen'],
101
+ 'gitDecoration.deletedResourceForeground': ['terminal.ansiRed', 'editorError.foreground'],
102
+ 'gitDecoration.modifiedResourceForeground': ['terminal.ansiYellow'],
103
+ 'gitDecoration.untrackedResourceForeground': ['gitDecoration.addedResourceForeground'],
104
+ 'input.background': ['editorWidget.background'],
105
+ 'input.border': ['focusBorder', 'editorGroup.border'],
106
+ 'input.foreground': ['editor.foreground'],
107
+ 'list.activeSelectionBackground': ['editor.selectionBackground'],
108
+ 'list.activeSelectionForeground': ['editor.foreground'],
109
+ 'list.hoverBackground': ['list.activeSelectionBackground'],
110
+ 'panel.background': ['editor.background'],
111
+ 'panel.border': ['editorGroup.border', 'contrastBorder'],
112
+ 'sideBar.background': ['editorGroupHeader.tabsBackground', 'panel.background'],
113
+ 'sideBar.foreground': ['editor.foreground', 'foreground'],
114
+ 'sideBarSectionHeader.background': ['sideBar.background'],
115
+ 'sideBarSectionHeader.foreground': ['sideBar.foreground'],
116
+ 'terminal.ansiBlue': ['textLink.foreground'],
117
+ 'terminal.ansiCyan': [],
118
+ 'terminal.ansiGreen': ['gitDecoration.addedResourceForeground'],
119
+ 'terminal.ansiMagenta': [],
120
+ 'terminal.ansiRed': ['editorError.foreground'],
121
+ 'terminal.ansiYellow': ['editorWarning.foreground'],
122
+ 'textLink.activeForeground': ['textLink.foreground'],
123
+ 'textLink.foreground': ['editorLink.activeForeground', 'terminal.ansiBlue'],
124
+ }
125
+
126
+ function computeFallback(
127
+ key: AimuxColorKey,
128
+ bg: string,
129
+ fg: string,
130
+ resolved: ThemeColorMap
131
+ ): string {
132
+ switch (key) {
133
+ case 'editor.background':
134
+ return bg
135
+ case 'editor.foreground':
136
+ return fg
137
+ case 'editor.lineHighlightBackground':
138
+ return shade(bg, 0.03)
139
+ case 'editor.selectionBackground':
140
+ return shade(bg, 0.1)
141
+ case 'editorCursor.foreground':
142
+ return fg
143
+ case 'editorLineNumber.foreground':
144
+ return mix(fg, bg, 0.55)
145
+ case 'sideBar.background':
146
+ return shade(bg, -0.04)
147
+ case 'sideBar.foreground':
148
+ return fg
149
+ case 'sideBarSectionHeader.background':
150
+ return shade(bg, -0.02)
151
+ case 'sideBarSectionHeader.foreground':
152
+ return mix(fg, bg, 0.35)
153
+ case 'editorGroupHeader.tabsBackground':
154
+ return shade(bg, -0.04)
155
+ case 'editorWidget.background':
156
+ return shade(bg, -0.08)
157
+ case 'panel.background':
158
+ return bg
159
+ case 'panel.border':
160
+ return shade(bg, 0.15)
161
+ case 'focusBorder':
162
+ return resolved['textLink.foreground'] ?? fg
163
+ case 'editorGroup.border':
164
+ return shade(bg, 0.15)
165
+ case 'contrastBorder':
166
+ return shade(bg, 0.2)
167
+ case 'list.activeSelectionBackground':
168
+ return shade(bg, 0.1)
169
+ case 'list.activeSelectionForeground':
170
+ return fg
171
+ case 'list.hoverBackground':
172
+ return shade(bg, 0.05)
173
+ case 'foreground':
174
+ return fg
175
+ case 'descriptionForeground':
176
+ return mix(fg, bg, 0.55)
177
+ case 'textLink.foreground':
178
+ case 'textLink.activeForeground':
179
+ return resolved['terminal.ansiBlue'] ?? fg
180
+ case 'errorForeground':
181
+ case 'editorError.foreground':
182
+ return '#f38ba8'
183
+ case 'editorWarning.foreground':
184
+ return '#f6c177'
185
+ case 'terminal.ansiRed':
186
+ return '#f38ba8'
187
+ case 'terminal.ansiGreen':
188
+ return '#8bd5ca'
189
+ case 'terminal.ansiYellow':
190
+ return '#f6c177'
191
+ case 'terminal.ansiBlue':
192
+ return '#7cd1b8'
193
+ case 'terminal.ansiMagenta':
194
+ return resolved['textLink.activeForeground'] ?? '#c4a7e7'
195
+ case 'terminal.ansiCyan':
196
+ return resolved['terminal.ansiBlue'] ?? '#89dceb'
197
+ case 'gitDecoration.addedResourceForeground':
198
+ return resolved['terminal.ansiGreen'] ?? '#8bd5ca'
199
+ case 'gitDecoration.modifiedResourceForeground':
200
+ return resolved['terminal.ansiYellow'] ?? '#f6c177'
201
+ case 'gitDecoration.deletedResourceForeground':
202
+ return resolved['terminal.ansiRed'] ?? '#f38ba8'
203
+ case 'gitDecoration.untrackedResourceForeground':
204
+ return resolved['gitDecoration.addedResourceForeground'] ?? '#8bd5ca'
205
+ case 'diffEditor.insertedLineBackground':
206
+ return mix(bg, resolved['gitDecoration.addedResourceForeground'] ?? '#8bd5ca', 0.18)
207
+ case 'diffEditor.removedLineBackground':
208
+ return mix(bg, resolved['gitDecoration.deletedResourceForeground'] ?? '#f38ba8', 0.18)
209
+ case 'input.background':
210
+ return shade(bg, -0.04)
211
+ case 'input.foreground':
212
+ return fg
213
+ case 'input.border':
214
+ return shade(bg, 0.15)
215
+ default:
216
+ return fg
217
+ }
218
+ }
219
+
220
+ export function normalizeTheme(raw: NormalizeInput): Theme {
221
+ const srcColors = raw.colors ?? {}
222
+ const bg = stripAlpha(srcColors['editor.background'] ?? raw.bg ?? '#000000')
223
+ const fg = stripAlpha(srcColors['editor.foreground'] ?? raw.fg ?? '#ffffff')
224
+
225
+ const resolved: ThemeColorMap = {} as ThemeColorMap
226
+ for (const key of AIMUX_COLOR_KEYS) {
227
+ const direct = srcColors[key]
228
+ if (direct && typeof direct === 'string') {
229
+ resolved[key] = stripAlpha(direct)
230
+ continue
231
+ }
232
+ const alt = first(srcColors, FALLBACKS[key])
233
+ if (alt) resolved[key] = alt
234
+ }
235
+ for (const key of AIMUX_COLOR_KEYS) {
236
+ if (!resolved[key]) resolved[key] = computeFallback(key, bg, fg, resolved)
237
+ }
238
+ for (const [k, v] of Object.entries(srcColors)) {
239
+ if (!resolved[k] && v && typeof v === 'string') resolved[k] = stripAlpha(v)
240
+ }
241
+
242
+ const out: Theme = {
243
+ bg,
244
+ colors: resolved,
245
+ displayName: raw.displayName ?? raw.name,
246
+ fg,
247
+ name: raw.name,
248
+ settings: raw.settings ?? [],
249
+ type: raw.type ?? 'dark',
250
+ }
251
+ if (raw.colorReplacements && Object.keys(raw.colorReplacements).length > 0) {
252
+ out.colorReplacements = raw.colorReplacements
253
+ }
254
+ if (raw.semanticHighlighting) out.semanticHighlighting = raw.semanticHighlighting
255
+ if (raw.semanticTokenColors && Object.keys(raw.semanticTokenColors).length > 0) {
256
+ out.semanticTokenColors = raw.semanticTokenColors
257
+ }
258
+ if (raw.tokenColors && raw.tokenColors !== raw.settings) out.tokenColors = raw.tokenColors
259
+ return out
260
+ }
package/src/resolver.ts CHANGED
@@ -25,6 +25,7 @@ export function resolveConfig(userConfig: AimuxUserConfig): ResolvedConfig {
25
25
  sidebar: userConfig.sidebar ?? {},
26
26
  snippets: userConfig.snippets ?? [],
27
27
  theme: userConfig.theme,
28
+ themes: userConfig.themes ?? {},
28
29
  }
29
30
  }
30
31