@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
package/src/themes.ts CHANGED
@@ -1,252 +1,100 @@
1
- import type { ThemeColors, ThemeDefinition, ThemeId } from './types'
1
+ import type {
2
+ NamedTheme,
3
+ NamedThemeDefinition,
4
+ Theme,
5
+ ThemeColorMap,
6
+ ThemeDefinition,
7
+ ThemeId,
8
+ UserThemeInput,
9
+ } from './types'
2
10
 
3
- export const THEMES: Record<ThemeId, { name: string; colors: ThemeColors }> = {
4
- 'aimux': {
5
- colors: {
6
- accent: '#7cd1b8',
7
- accentAlt: '#c4a7e7',
8
- background: '#11151b',
9
- border: '#2d3f52',
10
- borderActive: '#7cd1b8',
11
- danger: '#f38ba8',
12
- dim: '#243242',
13
- overlay: '#0b1016',
14
- panel: '#16202b',
15
- panelHighlight: '#1f3344',
16
- panelMuted: '#1c2734',
17
- success: '#8bd5ca',
18
- text: '#edf4ff',
19
- textMuted: '#6b829a',
20
- warning: '#f6c177',
21
- },
22
- name: 'Aimux',
23
- },
24
- 'catppuccin-mocha': {
25
- colors: {
26
- accent: '#cba6f7',
27
- accentAlt: '#f5c2e7',
28
- background: '#1e1e2e',
29
- border: '#45475a',
30
- borderActive: '#cba6f7',
31
- danger: '#f38ba8',
32
- dim: '#313244',
33
- overlay: '#181825',
34
- panel: '#232334',
35
- panelHighlight: '#313244',
36
- panelMuted: '#2a2a3c',
37
- success: '#a6e3a1',
38
- text: '#cdd6f4',
39
- textMuted: '#6c7086',
40
- warning: '#f9e2af',
41
- },
42
- name: 'Catppuccin Mocha',
43
- },
44
- 'dracula': {
45
- colors: {
46
- accent: '#bd93f9',
47
- accentAlt: '#ff79c6',
48
- background: '#282a36',
49
- border: '#44475a',
50
- borderActive: '#bd93f9',
51
- danger: '#ff5555',
52
- dim: '#383a4a',
53
- overlay: '#1f2029',
54
- panel: '#2d2f3d',
55
- panelHighlight: '#3d4056',
56
- panelMuted: '#343746',
57
- success: '#50fa7b',
58
- text: '#f8f8f2',
59
- textMuted: '#6272a4',
60
- warning: '#f1fa8c',
61
- },
62
- name: 'Dracula',
63
- },
64
- 'dracula-at-night': {
65
- colors: {
66
- accent: '#bd93f9',
67
- accentAlt: '#ff79c6',
68
- background: '#0e1419',
69
- border: '#2a3440',
70
- borderActive: '#bd93f9',
71
- danger: '#ff5555',
72
- dim: '#1e2630',
73
- overlay: '#090d11',
74
- panel: '#131920',
75
- panelHighlight: '#222a33',
76
- panelMuted: '#1a2129',
77
- success: '#50fa7b',
78
- text: '#f8f8f2',
79
- textMuted: '#6272a4',
80
- warning: '#f1fa8c',
81
- },
82
- name: 'Dracula At Night',
83
- },
84
- 'everforest': {
85
- colors: {
86
- accent: '#a7c080',
87
- accentAlt: '#d699b6',
88
- background: '#2d353b',
89
- border: '#4f585e',
90
- borderActive: '#a7c080',
91
- danger: '#e67e80',
92
- dim: '#3d484d',
93
- overlay: '#232a2e',
94
- panel: '#343f44',
95
- panelHighlight: '#475258',
96
- panelMuted: '#3d484d',
97
- success: '#a7c080',
98
- text: '#d3c6aa',
99
- textMuted: '#859289',
100
- warning: '#dbbc7f',
101
- },
102
- name: 'Everforest',
103
- },
104
- 'gruvbox-dark': {
105
- colors: {
106
- accent: '#b8bb26',
107
- accentAlt: '#d3869b',
108
- background: '#282828',
109
- border: '#504945',
110
- borderActive: '#b8bb26',
111
- danger: '#fb4934',
112
- dim: '#3c3836',
113
- overlay: '#1f1d1b',
114
- panel: '#2e2e2e',
115
- panelHighlight: '#504945',
116
- panelMuted: '#3c3836',
117
- success: '#b8bb26',
118
- text: '#ebdbb2',
119
- textMuted: '#928374',
120
- warning: '#fabd2f',
121
- },
122
- name: 'Gruvbox Dark',
123
- },
124
- 'kanagawa': {
125
- colors: {
126
- accent: '#7e9cd8',
127
- accentAlt: '#957fb8',
128
- background: '#1f1f28',
129
- border: '#363646',
130
- borderActive: '#7e9cd8',
131
- danger: '#e82424',
132
- dim: '#2a2a37',
133
- overlay: '#171720',
134
- panel: '#24242e',
135
- panelHighlight: '#363646',
136
- panelMuted: '#2a2a37',
137
- success: '#76946a',
138
- text: '#dcd7ba',
139
- textMuted: '#727169',
140
- warning: '#e6c384',
141
- },
142
- name: 'Kanagawa',
143
- },
144
- 'nord': {
145
- colors: {
146
- accent: '#88c0d0',
147
- accentAlt: '#b48ead',
148
- background: '#2e3440',
149
- border: '#434c5e',
150
- borderActive: '#88c0d0',
151
- danger: '#bf616a',
152
- dim: '#3b4252',
153
- overlay: '#252a33',
154
- panel: '#333a47',
155
- panelHighlight: '#434c5e',
156
- panelMuted: '#3b4252',
157
- success: '#a3be8c',
158
- text: '#eceff4',
159
- textMuted: '#7b88a1',
160
- warning: '#ebcb8b',
161
- },
162
- name: 'Nord',
163
- },
164
- 'one-dark': {
165
- colors: {
166
- accent: '#61afef',
167
- accentAlt: '#c678dd',
168
- background: '#282c34',
169
- border: '#3e4452',
170
- borderActive: '#61afef',
171
- danger: '#e06c75',
172
- dim: '#3b4048',
173
- overlay: '#1f2329',
174
- panel: '#2c313a',
175
- panelHighlight: '#3b4048',
176
- panelMuted: '#333842',
177
- success: '#98c379',
178
- text: '#abb2bf',
179
- textMuted: '#636d83',
180
- warning: '#e5c07b',
181
- },
182
- name: 'One Dark',
183
- },
184
- 'solarized-dark': {
185
- colors: {
186
- accent: '#268bd2',
187
- accentAlt: '#d33682',
188
- background: '#002b36',
189
- border: '#2e5560',
190
- borderActive: '#268bd2',
191
- danger: '#dc322f',
192
- dim: '#073642',
193
- overlay: '#00242c',
194
- panel: '#003340',
195
- panelHighlight: '#0a4050',
196
- panelMuted: '#073642',
197
- success: '#859900',
198
- text: '#fdf6e3',
199
- textMuted: '#657b83',
200
- warning: '#b58900',
201
- },
202
- name: 'Solarized Dark',
203
- },
204
- 'tokyo-night': {
205
- colors: {
206
- accent: '#7aa2f7',
207
- accentAlt: '#bb9af7',
208
- background: '#1a1b26',
209
- border: '#3b4261',
210
- borderActive: '#7aa2f7',
211
- danger: '#f7768e',
212
- dim: '#292e42',
213
- overlay: '#141722',
214
- panel: '#1f2335',
215
- panelHighlight: '#292e42',
216
- panelMuted: '#24283b',
217
- success: '#9ece6a',
218
- text: '#c0caf5',
219
- textMuted: '#565f89',
220
- warning: '#e0af68',
221
- },
222
- name: 'Tokyo Night',
223
- },
11
+ import { HOUSE_THEME_IDS, HOUSE_THEMES } from './house-themes'
12
+ import { normalizeTheme } from './normalize-theme'
13
+ import { GENERATED_THEME_IDS, GENERATED_THEMES } from './themes/generated'
14
+
15
+ export const THEMES: Record<string, Theme> = { ...GENERATED_THEMES, ...HOUSE_THEMES }
16
+ export const THEME_IDS: ThemeId[] = [...GENERATED_THEME_IDS, ...HOUSE_THEME_IDS]
17
+
18
+ const DEFAULT_THEME_ID: ThemeId = 'aimux'
19
+
20
+ const LEGACY_THEME_ALIASES: Record<string, ThemeId> = {
21
+ 'everforest': 'everforest-dark',
22
+ 'gruvbox-dark': 'gruvbox-dark-hard',
23
+ 'kanagawa': 'kanagawa-wave',
24
+ 'one-dark': 'one-dark-pro',
25
+ }
26
+
27
+ export function migrateThemeId(id: string | undefined): ThemeId {
28
+ if (id !== undefined && id in THEMES) return id as ThemeId
29
+ if (id !== undefined) {
30
+ const alias = LEGACY_THEME_ALIASES[id]
31
+ if (alias !== undefined) return alias
32
+ }
33
+ return DEFAULT_THEME_ID
224
34
  }
225
35
 
226
- export const THEME_IDS = Object.keys(THEMES) as ThemeId[]
36
+ export function isKnownThemeId(id: string): boolean {
37
+ return id in THEMES
38
+ }
39
+
40
+ function isFullTheme(
41
+ def: NamedTheme | NamedThemeDefinition | UserThemeInput
42
+ ): def is NamedTheme | UserThemeInput {
43
+ return 'settings' in def || 'fg' in def || 'bg' in def
44
+ }
227
45
 
228
46
  /**
229
- * Theme helpers for user configs.
47
+ * Merge user-declared themes into the registry. Both the full and palette
48
+ * shortcut paths run through `normalizeTheme` so every registered theme has
49
+ * all AIMUX_COLOR_KEYS populated — same guarantees as shipped themes.
230
50
  */
51
+ export function registerUserThemes(
52
+ defs: Record<string, NamedTheme | NamedThemeDefinition | UserThemeInput> | undefined
53
+ ): void {
54
+ if (!defs) return
55
+ const fallback = THEMES[DEFAULT_THEME_ID]
56
+ if (!fallback) return
57
+ for (const [id, def] of Object.entries(defs)) {
58
+ if (isFullTheme(def)) {
59
+ THEMES[id] = normalizeTheme({ ...def, name: def.name ?? id })
60
+ } else {
61
+ const base = def.base ? (THEMES[def.base] ?? fallback) : fallback
62
+ const colors: Record<string, string> = { ...base.colors }
63
+ for (const [k, v] of Object.entries(def.colors ?? {})) {
64
+ if (typeof v === 'string') colors[k] = v
65
+ }
66
+ THEMES[id] = normalizeTheme({
67
+ bg: base.bg,
68
+ colors,
69
+ displayName: def.name,
70
+ fg: base.fg,
71
+ name: id,
72
+ settings: base.settings,
73
+ type: base.type,
74
+ })
75
+ }
76
+ if (!THEME_IDS.includes(id)) THEME_IDS.push(id)
77
+ }
78
+ }
79
+
231
80
  export const themes = {
232
81
  /**
233
- * Create a fully custom theme from scratch.
82
+ * Palette shortcut. Patches VSCode color keys on top of an existing theme;
83
+ * token settings inherit from the base unchanged.
234
84
  */
235
- create(colors: ThemeColors): ThemeDefinition {
236
- return { colors }
85
+ define(name: string, base: ThemeId, overrides: Partial<ThemeColorMap>): NamedThemeDefinition {
86
+ return { base, colors: overrides, name }
87
+ },
88
+ /** Unnamed variant of `define`. Prefer `define` in config. */
89
+ extend(base: ThemeId, overrides: Partial<ThemeColorMap>): ThemeDefinition {
90
+ return { base, colors: overrides }
237
91
  },
238
-
239
92
  /**
240
- * Create a new theme by extending an existing built-in theme with overrides.
93
+ * Passthrough for a raw Shiki / VSCode-shape theme. Missing
94
+ * `AIMUX_COLOR_KEYS` are filled at registration via the same fallback chain
95
+ * the shipped themes go through.
241
96
  */
242
- extend(base: ThemeId, overrides: Partial<ThemeColors>): ThemeDefinition {
243
- const baseTheme = THEMES[base]
244
- if (!baseTheme) {
245
- throw new Error(`themes.extend: unknown base theme "${base}"`)
246
- }
247
- return {
248
- base,
249
- colors: { ...baseTheme.colors, ...overrides },
250
- }
97
+ full(theme: UserThemeInput): UserThemeInput {
98
+ return theme
251
99
  },
252
100
  }
package/src/types.ts CHANGED
@@ -23,6 +23,7 @@ export type ModeId =
23
23
  | 'modal.snippet-picker.filtering'
24
24
  | 'modal.snippet-editor'
25
25
  | 'modal.theme-picker'
26
+ | 'modal.theme-picker.filtering'
26
27
  | 'modal.help'
27
28
  | 'modal.help.filtering'
28
29
  | 'modal.split-picker'
@@ -162,12 +163,14 @@ export interface GitPaneState {
162
163
  mode: 'embedded' | 'pane'
163
164
  position: 'top' | 'bottom' | 'left' | 'right'
164
165
  ratio: number
166
+ diffModeRatio: number
167
+ fileListMode: GitFileListMode
165
168
  path: GitPanePathConfig
166
169
  diffCount: GitPaneDiffCountConfig
167
170
  }
168
171
 
169
172
  export type GitFileStatus = 'M' | 'A' | 'D' | 'R' | 'C' | 'U' | '?'
170
- export type GitFileSection = 'staged' | 'unstaged' | 'untracked'
173
+ export type GitFileSection = 'staged' | 'unstaged' | 'untracked' | 'historical'
171
174
 
172
175
  export interface GitFileEntry {
173
176
  path: string
@@ -200,12 +203,24 @@ export interface DiffData {
200
203
  errorMessage?: string
201
204
  }
202
205
 
206
+ export type GitDiffView = 'split' | 'stacked'
207
+ export type GitFileListMode = 'tree' | 'flat'
208
+
209
+ export interface FoldState {
210
+ top: number
211
+ bottom: number
212
+ }
213
+
203
214
  export interface GitModeState {
204
- selectedFileIndex: number
215
+ selectedEntryKey: string | null
216
+ collapsedFolders: Record<string, true>
205
217
  diffs: Record<string, DiffData>
206
218
  loading: Record<string, boolean>
207
219
  pendingDeletePath: string | null
208
220
  actionMessage: string | null
221
+ diffView: GitDiffView
222
+ folds: Record<string, Record<string, FoldState>>
223
+ headOffset: number
209
224
  }
210
225
 
211
226
  interface ModalBase {
@@ -238,10 +253,12 @@ export interface ModalSnippetPicker extends ModalBase {
238
253
  }
239
254
  export interface ModalThemePicker extends ModalBase {
240
255
  type: 'theme-picker'
256
+ entryCount: number
241
257
  }
242
258
  export interface ModalHelp extends ModalBase {
243
259
  type: 'help'
244
260
  entryCount: number
261
+ scope: ModeId | null
245
262
  }
246
263
  export interface ModalSplitPicker extends ModalBase {
247
264
  type: 'split-picker'
@@ -323,7 +340,7 @@ export interface AppState {
323
340
 
324
341
  export type ModalAction =
325
342
  | { type: 'open-new-tab-modal' }
326
- | { type: 'open-help-modal' }
343
+ | { type: 'open-help-modal'; scope?: ModeId }
327
344
  | { type: 'open-split-picker'; direction: SplitDirection }
328
345
  | { type: 'open-session-picker' }
329
346
  | { type: 'open-session-name-modal'; sessionTargetId?: string; initialName?: string }
@@ -343,7 +360,9 @@ export type ModalAction =
343
360
  | { type: 'open-snippet-editor'; snippetId?: string }
344
361
  | { type: 'begin-snippet-filter' }
345
362
  | { type: 'begin-help-filter' }
363
+ | { type: 'begin-theme-filter' }
346
364
  | { type: 'set-help-entry-count'; count: number }
365
+ | { type: 'set-theme-entry-count'; count: number }
347
366
  | { type: 'open-theme-picker' }
348
367
  | { type: 'open-update-available-modal'; currentVersion: string; latestVersion: string }
349
368
 
@@ -415,6 +434,7 @@ export type UIAction =
415
434
  | { type: 'set-terminal-size'; cols: number; rows: number }
416
435
  | { type: 'toggle-git-pane' }
417
436
  | { type: 'resize-git-pane'; delta: number }
437
+ | { type: 'resize-git-diff-pane'; delta: number }
418
438
  | { type: 'set-git-pane-mode'; mode: 'embedded' | 'pane' }
419
439
  | { type: 'set-git-pane-position'; position: 'top' | 'bottom' | 'left' | 'right' }
420
440
  | { type: 'set-pending-chords'; chords: string[] | null }
@@ -436,12 +456,30 @@ export type GitPanelAction =
436
456
  export type GitModeAction =
437
457
  | { type: 'enter-git-mode' }
438
458
  | { type: 'exit-git-mode' }
439
- | { type: 'git-mode-select-file'; delta: -1 | 1 }
440
- | { type: 'git-mode-set-diff'; path: string; diff: DiffData }
441
- | { type: 'git-mode-set-loading'; path: string; loading: boolean }
459
+ | { type: 'git-mode-move-selection'; delta: -1 | 1 }
460
+ | { type: 'git-mode-move-file-selection'; delta: -1 | 1 }
461
+ | { type: 'git-mode-select-entry-by-key'; key: string }
462
+ | { type: 'git-mode-toggle-folder'; key: string }
463
+ | { type: 'git-mode-toggle-selected-folder' }
464
+ | { type: 'git-mode-collapse-selection' }
465
+ | { type: 'git-mode-expand-selection' }
466
+ | { type: 'git-mode-toggle-file-list-mode' }
467
+ | { type: 'git-mode-set-diff'; key: string; diff: DiffData }
468
+ | { type: 'git-mode-set-loading'; key: string; loading: boolean }
442
469
  | { type: 'git-mode-set-pending-delete'; path: string | null }
443
470
  | { type: 'git-mode-clear-diff-cache'; path: string }
444
471
  | { type: 'git-mode-set-message'; message: string | null }
472
+ | { type: 'git-mode-toggle-diff-view' }
473
+ | { type: 'git-mode-shift-head-offset'; delta: number }
474
+ | { type: 'git-mode-set-head-offset'; offset: number }
475
+ | {
476
+ type: 'git-mode-fold-adjust'
477
+ key: string
478
+ foldId: string
479
+ side: 'top' | 'bottom'
480
+ delta: number
481
+ }
482
+ | { type: 'git-mode-fold-set'; key: string; foldId: string; top: number; bottom: number }
445
483
  | {
446
484
  type: 'git-mode-optimistic-move'
447
485
  path: string
@@ -489,6 +527,8 @@ export type SideEffect =
489
527
  | { type: 'split-pane'; direction: SplitDirection }
490
528
  | { type: 'confirm-split' }
491
529
  | { type: 'scroll-git-diff'; delta: number }
530
+ | { type: 'persist-git-diff-mode-ratio'; ratio: number }
531
+ | { type: 'persist-git-file-list-mode'; mode: GitFileListMode }
492
532
  | { type: 'git-stage'; path: string }
493
533
  | { type: 'git-unstage'; path: string }
494
534
  | { type: 'git-restore'; path: string }
@@ -521,40 +561,137 @@ export interface ModeContext {
521
561
 
522
562
  // ─── Themes ───────────────────────────────────────────────────────────────────
523
563
 
524
- export interface ThemeColors {
525
- background: string
526
- panel: string
527
- panelMuted: string
528
- panelHighlight: string
529
- overlay: string
530
- border: string
531
- borderActive: string
532
- text: string
533
- textMuted: string
534
- accent: string
535
- accentAlt: string
536
- warning: string
537
- danger: string
538
- success: string
539
- dim: string
540
- }
541
-
542
- export type ThemeId =
543
- | 'aimux'
544
- | 'dracula'
545
- | 'dracula-at-night'
546
- | 'everforest'
547
- | 'tokyo-night'
548
- | 'gruvbox-dark'
549
- | 'catppuccin-mocha'
550
- | 'nord'
551
- | 'solarized-dark'
552
- | 'one-dark'
553
- | 'kanagawa'
554
-
564
+ import type { BundledTheme } from 'shiki'
565
+
566
+ export type ThemeId = BundledTheme | (string & {})
567
+
568
+ /**
569
+ * VSCode workbench color keys aimux's UI depends on. The build-time normalizer
570
+ * guarantees every `Theme` has all of these populated.
571
+ */
572
+ export const AIMUX_COLOR_KEYS = [
573
+ // editor
574
+ 'editor.background',
575
+ 'editor.foreground',
576
+ 'editor.lineHighlightBackground',
577
+ 'editor.selectionBackground',
578
+ 'editorLineNumber.foreground',
579
+ 'editorCursor.foreground',
580
+ // sidebar / panels
581
+ 'sideBar.background',
582
+ 'sideBar.foreground',
583
+ 'sideBarSectionHeader.background',
584
+ 'sideBarSectionHeader.foreground',
585
+ 'editorGroupHeader.tabsBackground',
586
+ 'editorWidget.background',
587
+ 'panel.background',
588
+ 'panel.border',
589
+ // borders & focus
590
+ 'focusBorder',
591
+ 'editorGroup.border',
592
+ 'contrastBorder',
593
+ // lists
594
+ 'list.activeSelectionBackground',
595
+ 'list.activeSelectionForeground',
596
+ 'list.hoverBackground',
597
+ // text & links
598
+ 'foreground',
599
+ 'descriptionForeground',
600
+ 'textLink.foreground',
601
+ 'textLink.activeForeground',
602
+ // status
603
+ 'errorForeground',
604
+ 'editorError.foreground',
605
+ 'editorWarning.foreground',
606
+ // terminal ANSI
607
+ 'terminal.ansiRed',
608
+ 'terminal.ansiGreen',
609
+ 'terminal.ansiYellow',
610
+ 'terminal.ansiBlue',
611
+ 'terminal.ansiMagenta',
612
+ 'terminal.ansiCyan',
613
+ // git decorations
614
+ 'gitDecoration.addedResourceForeground',
615
+ 'gitDecoration.modifiedResourceForeground',
616
+ 'gitDecoration.deletedResourceForeground',
617
+ 'gitDecoration.untrackedResourceForeground',
618
+ // diff editor
619
+ 'diffEditor.insertedLineBackground',
620
+ 'diffEditor.removedLineBackground',
621
+ // input
622
+ 'input.background',
623
+ 'input.foreground',
624
+ 'input.border',
625
+ ] as const
626
+
627
+ export type AimuxColorKey = (typeof AIMUX_COLOR_KEYS)[number]
628
+
629
+ export type ThemeColorMap = Record<AimuxColorKey, string> & Record<string, string>
630
+
631
+ export interface ThemeSettings {
632
+ fontStyle?: string
633
+ foreground?: string
634
+ background?: string
635
+ // Some bundled shiki themes include non-standard keys (e.g. `content`); allow passthrough.
636
+ [key: string]: string | undefined
637
+ }
638
+
639
+ export interface ThemeTokenRule {
640
+ name?: string
641
+ scope?: string | string[]
642
+ // `foreground` at the top level is used by a handful of shiki themes as a
643
+ // shorthand for `settings.foreground` — accept both forms.
644
+ foreground?: string
645
+ settings?: ThemeSettings
646
+ }
647
+
648
+ export interface Theme {
649
+ name: string
650
+ displayName: string
651
+ type: 'dark' | 'light'
652
+ fg: string
653
+ bg: string
654
+ colors: ThemeColorMap
655
+ settings: ThemeTokenRule[]
656
+ tokenColors?: ThemeTokenRule[]
657
+ colorReplacements?: Record<string, string>
658
+ semanticHighlighting?: boolean
659
+ semanticTokenColors?: Record<
660
+ string,
661
+ | string
662
+ | {
663
+ foreground?: string
664
+ fontStyle?: string
665
+ bold?: boolean
666
+ italic?: boolean
667
+ underline?: boolean
668
+ }
669
+ >
670
+ }
671
+
672
+ /** Partial-override shape for `themes.define(name, base, overrides)`. */
555
673
  export interface ThemeDefinition {
556
674
  base?: ThemeId
557
- colors: ThemeColors
675
+ colors: Partial<ThemeColorMap>
676
+ }
677
+
678
+ export interface NamedThemeDefinition extends ThemeDefinition {
679
+ name: string
680
+ }
681
+
682
+ export interface NamedTheme extends Theme {
683
+ displayName: string
684
+ }
685
+
686
+ /**
687
+ * Input shape accepted by `themes.full(...)` — lets users paste a shiki /
688
+ * VSCode theme JSON without typing every AIMUX_COLOR_KEY. `registerUserThemes`
689
+ * runs it through `normalizeTheme` to produce a complete `Theme`.
690
+ */
691
+ export interface UserThemeInput extends Partial<Omit<Theme, 'colors' | 'name' | 'displayName'>> {
692
+ name: string
693
+ displayName: string
694
+ colors?: Partial<ThemeColorMap> & Record<string, string | undefined>
558
695
  }
559
696
 
560
697
  // ─── Backend config (stub) ────────────────────────────────────────────────────
@@ -648,6 +785,8 @@ export type GitPaneDiffCountConfig = { enabled: boolean }
648
785
  interface GitPaneBaseConfig {
649
786
  visible?: boolean
650
787
  ratio?: number
788
+ diffModeRatio?: number
789
+ fileListMode?: GitFileListMode
651
790
  path?: GitPanePathConfig
652
791
  diffCount?: GitPaneDiffCountConfig
653
792
  }
@@ -665,7 +804,8 @@ export interface GitPanePaneConfig extends GitPaneBaseConfig {
665
804
  export type GitPaneConfig = GitPaneEmbeddedConfig | GitPanePaneConfig
666
805
 
667
806
  export interface AimuxUserConfig {
668
- theme?: ThemeId | ThemeDefinition
807
+ theme?: ThemeId
808
+ themes?: Record<string, NamedThemeDefinition | UserThemeInput>
669
809
  keymaps?: (k: KeymapBuilderApi) => KeymapBuilderApi
670
810
  backends?: Record<string, BackendConfig>
671
811
  sidebar?: SidebarConfig
@@ -698,7 +838,8 @@ export interface ResolvedKeymapConfig {
698
838
  }
699
839
 
700
840
  export interface ResolvedConfig {
701
- theme: ThemeId | ThemeDefinition | undefined
841
+ theme: ThemeId | undefined
842
+ themes: Record<string, NamedThemeDefinition | UserThemeInput>
702
843
  keymaps: ResolvedKeymapConfig
703
844
  backends: Record<string, BackendConfig>
704
845
  sidebar: SidebarConfig