@brimveyn/aimux-config 0.4.0 → 0.4.2

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 (74) hide show
  1. package/package.json +1 -1
  2. package/src/actions.ts +47 -0
  3. package/src/defaults.ts +3 -0
  4. package/src/index.ts +9 -1
  5. package/src/normalize-theme.ts +260 -0
  6. package/src/themes/generated/andromeeda.ts +346 -0
  7. package/src/themes/generated/aurora-x.ts +603 -0
  8. package/src/themes/generated/ayu-dark.ts +817 -0
  9. package/src/themes/generated/ayu-light.ts +817 -0
  10. package/src/themes/generated/ayu-mirage.ts +817 -0
  11. package/src/themes/generated/catppuccin-frappe.ts +2041 -0
  12. package/src/themes/generated/catppuccin-latte.ts +2041 -0
  13. package/src/themes/generated/catppuccin-macchiato.ts +2041 -0
  14. package/src/themes/generated/catppuccin-mocha.ts +2041 -0
  15. package/src/themes/generated/dark-plus.ts +614 -0
  16. package/src/themes/generated/dracula-soft.ts +947 -0
  17. package/src/themes/generated/dracula.ts +947 -0
  18. package/src/themes/generated/everforest-dark.ts +2246 -0
  19. package/src/themes/generated/everforest-light.ts +2246 -0
  20. package/src/themes/generated/github-dark-default.ts +605 -0
  21. package/src/themes/generated/github-dark-dimmed.ts +605 -0
  22. package/src/themes/generated/github-dark-high-contrast.ts +608 -0
  23. package/src/themes/generated/github-dark.ts +519 -0
  24. package/src/themes/generated/github-light-default.ts +602 -0
  25. package/src/themes/generated/github-light-high-contrast.ts +604 -0
  26. package/src/themes/generated/github-light.ts +515 -0
  27. package/src/themes/generated/gruvbox-dark-hard.ts +1124 -0
  28. package/src/themes/generated/gruvbox-dark-medium.ts +1124 -0
  29. package/src/themes/generated/gruvbox-dark-soft.ts +1124 -0
  30. package/src/themes/generated/gruvbox-light-hard.ts +1124 -0
  31. package/src/themes/generated/gruvbox-light-medium.ts +1124 -0
  32. package/src/themes/generated/gruvbox-light-soft.ts +1124 -0
  33. package/src/themes/generated/horizon-bright.ts +420 -0
  34. package/src/themes/generated/horizon.ts +418 -0
  35. package/src/themes/generated/houston.ts +1819 -0
  36. package/src/themes/generated/index.ts +140 -0
  37. package/src/themes/generated/kanagawa-dragon.ts +753 -0
  38. package/src/themes/generated/kanagawa-lotus.ts +753 -0
  39. package/src/themes/generated/kanagawa-wave.ts +753 -0
  40. package/src/themes/generated/laserwave.ts +489 -0
  41. package/src/themes/generated/light-plus.ts +639 -0
  42. package/src/themes/generated/material-theme-darker.ts +759 -0
  43. package/src/themes/generated/material-theme-lighter.ts +759 -0
  44. package/src/themes/generated/material-theme-ocean.ts +759 -0
  45. package/src/themes/generated/material-theme-palenight.ts +759 -0
  46. package/src/themes/generated/material-theme.ts +759 -0
  47. package/src/themes/generated/min-dark.ts +326 -0
  48. package/src/themes/generated/min-light.ts +331 -0
  49. package/src/themes/generated/monokai.ts +463 -0
  50. package/src/themes/generated/night-owl-light.ts +1526 -0
  51. package/src/themes/generated/night-owl.ts +1628 -0
  52. package/src/themes/generated/nord.ts +1277 -0
  53. package/src/themes/generated/one-dark-pro.ts +1920 -0
  54. package/src/themes/generated/one-light.ts +1599 -0
  55. package/src/themes/generated/plastic.ts +315 -0
  56. package/src/themes/generated/poimandres.ts +1233 -0
  57. package/src/themes/generated/red.ts +395 -0
  58. package/src/themes/generated/rose-pine-dawn.ts +696 -0
  59. package/src/themes/generated/rose-pine-moon.ts +696 -0
  60. package/src/themes/generated/rose-pine.ts +696 -0
  61. package/src/themes/generated/slack-dark.ts +594 -0
  62. package/src/themes/generated/slack-ochin.ts +461 -0
  63. package/src/themes/generated/snazzy-light.ts +1254 -0
  64. package/src/themes/generated/solarized-dark.ts +387 -0
  65. package/src/themes/generated/solarized-light.ts +379 -0
  66. package/src/themes/generated/synthwave-84.ts +751 -0
  67. package/src/themes/generated/tokyo-night.ts +1420 -0
  68. package/src/themes/generated/vesper.ts +578 -0
  69. package/src/themes/generated/vitesse-black.ts +629 -0
  70. package/src/themes/generated/vitesse-dark.ts +629 -0
  71. package/src/themes/generated/vitesse-light.ts +627 -0
  72. package/src/themes.ts +57 -10
  73. package/src/types.ts +29 -4
  74. package/src/themes.generated.ts +0 -59794
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brimveyn/aimux-config",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "TypeScript configuration API for aimux — keymaps, themes, and backends with a fluent builder.",
5
5
  "keywords": [
6
6
  "aimux",
package/src/actions.ts CHANGED
@@ -457,13 +457,36 @@ export const toggleGitFileListMode: ActionFn = (ctx: ModeContext) => {
457
457
  )
458
458
  }
459
459
 
460
+ export const toggleTreeCompaction: ActionFn = (ctx: ModeContext) => {
461
+ const enabled = !ctx.state.gitPane.treeCompaction
462
+ return r(
463
+ [{ type: 'git-mode-toggle-tree-compaction' }],
464
+ [{ enabled, type: 'persist-git-tree-compaction' }]
465
+ )
466
+ }
467
+
460
468
  export function scrollGitDiff(delta: number): KeyResult {
461
469
  return r([], [{ delta, type: 'scroll-git-diff' }])
462
470
  }
463
471
 
464
472
  export const toggleGitDiffView: KeyResult = r([{ type: 'git-mode-toggle-diff-view' }])
465
473
 
474
+ export function shiftGitHeadOffset(delta: number): KeyResult {
475
+ return r([
476
+ { delta, type: 'git-mode-shift-head-offset' },
477
+ { message: null, type: 'git-mode-set-message' },
478
+ ])
479
+ }
480
+
466
481
  export const gitStageSelected: ActionFn = (ctx: ModeContext) => {
482
+ if (ctx.state.gitMode.headOffset > 0) {
483
+ return r([
484
+ {
485
+ message: 'disabled while viewing HEAD~N (press 0 or [ to return)',
486
+ type: 'git-mode-set-message',
487
+ },
488
+ ])
489
+ }
467
490
  const file = selectedGitFile(ctx)
468
491
  if (!file) return r(clearPendingDelete(ctx))
469
492
  const actions = clearPendingDelete(ctx)
@@ -478,6 +501,14 @@ export const gitStageSelected: ActionFn = (ctx: ModeContext) => {
478
501
  }
479
502
 
480
503
  export const gitDestructiveSelected: ActionFn = (ctx: ModeContext) => {
504
+ if (ctx.state.gitMode.headOffset > 0) {
505
+ return r([
506
+ {
507
+ message: 'disabled while viewing HEAD~N (press 0 or [ to return)',
508
+ type: 'git-mode-set-message',
509
+ },
510
+ ])
511
+ }
481
512
  const file = selectedGitFile(ctx)
482
513
  if (!file) return r(clearPendingDelete(ctx))
483
514
 
@@ -514,10 +545,26 @@ export const gitDestructiveSelected: ActionFn = (ctx: ModeContext) => {
514
545
  }
515
546
 
516
547
  export const gitCommitOpen: ActionFn = (ctx: ModeContext) => {
548
+ if (ctx.state.gitMode.headOffset > 0) {
549
+ return r([
550
+ {
551
+ message: 'disabled while viewing HEAD~N (press 0 or [ to return)',
552
+ type: 'git-mode-set-message',
553
+ },
554
+ ])
555
+ }
517
556
  return r([...clearPendingDelete(ctx), { type: 'open-git-commit-modal' }], [], 'modal.git-commit')
518
557
  }
519
558
 
520
559
  export const gitPush: ActionFn = (ctx: ModeContext) => {
560
+ if (ctx.state.gitMode.headOffset > 0) {
561
+ return r([
562
+ {
563
+ message: 'disabled while viewing HEAD~N (press 0 or [ to return)',
564
+ type: 'git-mode-set-message',
565
+ },
566
+ ])
567
+ }
521
568
  return r(clearPendingDelete(ctx), [{ type: 'git-push' }])
522
569
  }
523
570
 
package/src/defaults.ts CHANGED
@@ -88,6 +88,9 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
88
88
  .map('p', actions.gitPush, 'Push')
89
89
  .map('v', actions.toggleGitDiffView, 'Toggle split/stacked')
90
90
  .map('t', actions.toggleGitFileListMode, 'Toggle flat/tree')
91
+ .map('T', actions.toggleTreeCompaction, 'Toggle tree compaction')
92
+ .map(']', actions.shiftGitHeadOffset(1), 'Older commit')
93
+ .map('[', actions.shiftGitHeadOffset(-1), 'Newer commit')
91
94
  .map('?', actions.helpModal('git-mode'), 'Help')
92
95
  .map('<Esc>', actions.exitGitMode, 'Exit git')
93
96
  .map('j', actions.selectGitFile(1), 'Next entry')
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 { migrateThemeId, 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'
@@ -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
+ }