@brimveyn/aimux-config 0.4.0 → 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 (74) hide show
  1. package/package.json +1 -1
  2. package/src/actions.ts +39 -0
  3. package/src/defaults.ts +2 -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 +17 -3
  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.1",
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
@@ -463,7 +463,22 @@ export function scrollGitDiff(delta: number): KeyResult {
463
463
 
464
464
  export const toggleGitDiffView: KeyResult = r([{ type: 'git-mode-toggle-diff-view' }])
465
465
 
466
+ export function shiftGitHeadOffset(delta: number): KeyResult {
467
+ return r([
468
+ { delta, type: 'git-mode-shift-head-offset' },
469
+ { message: null, type: 'git-mode-set-message' },
470
+ ])
471
+ }
472
+
466
473
  export const gitStageSelected: ActionFn = (ctx: ModeContext) => {
474
+ if (ctx.state.gitMode.headOffset > 0) {
475
+ return r([
476
+ {
477
+ message: 'disabled while viewing HEAD~N (press 0 or [ to return)',
478
+ type: 'git-mode-set-message',
479
+ },
480
+ ])
481
+ }
467
482
  const file = selectedGitFile(ctx)
468
483
  if (!file) return r(clearPendingDelete(ctx))
469
484
  const actions = clearPendingDelete(ctx)
@@ -478,6 +493,14 @@ export const gitStageSelected: ActionFn = (ctx: ModeContext) => {
478
493
  }
479
494
 
480
495
  export const gitDestructiveSelected: ActionFn = (ctx: ModeContext) => {
496
+ if (ctx.state.gitMode.headOffset > 0) {
497
+ return r([
498
+ {
499
+ message: 'disabled while viewing HEAD~N (press 0 or [ to return)',
500
+ type: 'git-mode-set-message',
501
+ },
502
+ ])
503
+ }
481
504
  const file = selectedGitFile(ctx)
482
505
  if (!file) return r(clearPendingDelete(ctx))
483
506
 
@@ -514,10 +537,26 @@ export const gitDestructiveSelected: ActionFn = (ctx: ModeContext) => {
514
537
  }
515
538
 
516
539
  export const gitCommitOpen: ActionFn = (ctx: ModeContext) => {
540
+ if (ctx.state.gitMode.headOffset > 0) {
541
+ return r([
542
+ {
543
+ message: 'disabled while viewing HEAD~N (press 0 or [ to return)',
544
+ type: 'git-mode-set-message',
545
+ },
546
+ ])
547
+ }
517
548
  return r([...clearPendingDelete(ctx), { type: 'open-git-commit-modal' }], [], 'modal.git-commit')
518
549
  }
519
550
 
520
551
  export const gitPush: ActionFn = (ctx: ModeContext) => {
552
+ if (ctx.state.gitMode.headOffset > 0) {
553
+ return r([
554
+ {
555
+ message: 'disabled while viewing HEAD~N (press 0 or [ to return)',
556
+ type: 'git-mode-set-message',
557
+ },
558
+ ])
559
+ }
521
560
  return r(clearPendingDelete(ctx), [{ type: 'git-push' }])
522
561
  }
523
562
 
package/src/defaults.ts CHANGED
@@ -88,6 +88,8 @@ 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(']', actions.shiftGitHeadOffset(1), 'Older commit')
92
+ .map('[', actions.shiftGitHeadOffset(-1), 'Newer commit')
91
93
  .map('?', actions.helpModal('git-mode'), 'Help')
92
94
  .map('<Esc>', actions.exitGitMode, 'Exit git')
93
95
  .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
+ }