@brimveyn/aimux-config 0.4.4 → 0.4.5
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.
- package/package.json +1 -1
- package/src/actions.ts +11 -8
- package/src/defaults.ts +3 -2
- package/src/house-themes.ts +44 -302
- package/src/index.ts +20 -16
- package/src/palette-to-shiki.ts +134 -0
- package/src/palette-utils.ts +92 -0
- package/src/resolver.ts +0 -1
- package/src/themes/opencode.ts +1460 -0
- package/src/themes.ts +18 -88
- package/src/types.ts +42 -130
- package/src/normalize-theme.ts +0 -260
- package/src/themes/generated/andromeeda.ts +0 -346
- package/src/themes/generated/aurora-x.ts +0 -603
- package/src/themes/generated/ayu-dark.ts +0 -817
- package/src/themes/generated/ayu-light.ts +0 -817
- package/src/themes/generated/ayu-mirage.ts +0 -817
- package/src/themes/generated/catppuccin-frappe.ts +0 -2041
- package/src/themes/generated/catppuccin-latte.ts +0 -2041
- package/src/themes/generated/catppuccin-macchiato.ts +0 -2041
- package/src/themes/generated/catppuccin-mocha.ts +0 -2041
- package/src/themes/generated/dark-plus.ts +0 -614
- package/src/themes/generated/dracula-soft.ts +0 -947
- package/src/themes/generated/dracula.ts +0 -947
- package/src/themes/generated/everforest-dark.ts +0 -2246
- package/src/themes/generated/everforest-light.ts +0 -2246
- package/src/themes/generated/github-dark-default.ts +0 -605
- package/src/themes/generated/github-dark-dimmed.ts +0 -605
- package/src/themes/generated/github-dark-high-contrast.ts +0 -608
- package/src/themes/generated/github-dark.ts +0 -519
- package/src/themes/generated/github-light-default.ts +0 -602
- package/src/themes/generated/github-light-high-contrast.ts +0 -604
- package/src/themes/generated/github-light.ts +0 -515
- package/src/themes/generated/gruvbox-dark-hard.ts +0 -1124
- package/src/themes/generated/gruvbox-dark-medium.ts +0 -1124
- package/src/themes/generated/gruvbox-dark-soft.ts +0 -1124
- package/src/themes/generated/gruvbox-light-hard.ts +0 -1124
- package/src/themes/generated/gruvbox-light-medium.ts +0 -1124
- package/src/themes/generated/gruvbox-light-soft.ts +0 -1124
- package/src/themes/generated/horizon-bright.ts +0 -420
- package/src/themes/generated/horizon.ts +0 -418
- package/src/themes/generated/houston.ts +0 -1819
- package/src/themes/generated/index.ts +0 -140
- package/src/themes/generated/kanagawa-dragon.ts +0 -753
- package/src/themes/generated/kanagawa-lotus.ts +0 -753
- package/src/themes/generated/kanagawa-wave.ts +0 -753
- package/src/themes/generated/laserwave.ts +0 -489
- package/src/themes/generated/light-plus.ts +0 -639
- package/src/themes/generated/material-theme-darker.ts +0 -759
- package/src/themes/generated/material-theme-lighter.ts +0 -759
- package/src/themes/generated/material-theme-ocean.ts +0 -759
- package/src/themes/generated/material-theme-palenight.ts +0 -759
- package/src/themes/generated/material-theme.ts +0 -759
- package/src/themes/generated/min-dark.ts +0 -326
- package/src/themes/generated/min-light.ts +0 -331
- package/src/themes/generated/monokai.ts +0 -463
- package/src/themes/generated/night-owl-light.ts +0 -1526
- package/src/themes/generated/night-owl.ts +0 -1628
- package/src/themes/generated/nord.ts +0 -1277
- package/src/themes/generated/one-dark-pro.ts +0 -1920
- package/src/themes/generated/one-light.ts +0 -1599
- package/src/themes/generated/plastic.ts +0 -315
- package/src/themes/generated/poimandres.ts +0 -1233
- package/src/themes/generated/red.ts +0 -395
- package/src/themes/generated/rose-pine-dawn.ts +0 -696
- package/src/themes/generated/rose-pine-moon.ts +0 -696
- package/src/themes/generated/rose-pine.ts +0 -696
- package/src/themes/generated/slack-dark.ts +0 -594
- package/src/themes/generated/slack-ochin.ts +0 -461
- package/src/themes/generated/snazzy-light.ts +0 -1254
- package/src/themes/generated/solarized-dark.ts +0 -387
- package/src/themes/generated/solarized-light.ts +0 -379
- package/src/themes/generated/synthwave-84.ts +0 -751
- package/src/themes/generated/tokyo-night.ts +0 -1420
- package/src/themes/generated/vesper.ts +0 -578
- package/src/themes/generated/vitesse-black.ts +0 -629
- package/src/themes/generated/vitesse-dark.ts +0 -629
- package/src/themes/generated/vitesse-light.ts +0 -627
package/src/themes.ts
CHANGED
|
@@ -1,100 +1,30 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
NamedTheme,
|
|
3
|
-
NamedThemeDefinition,
|
|
4
|
-
Theme,
|
|
5
|
-
ThemeColorMap,
|
|
6
|
-
ThemeDefinition,
|
|
7
|
-
ThemeId,
|
|
8
|
-
UserThemeInput,
|
|
9
|
-
} from './types'
|
|
1
|
+
import type { AimuxTheme, ThemeId } from './types'
|
|
10
2
|
|
|
11
3
|
import { HOUSE_THEME_IDS, HOUSE_THEMES } from './house-themes'
|
|
12
|
-
import {
|
|
13
|
-
import { GENERATED_THEME_IDS, GENERATED_THEMES } from './themes/generated'
|
|
4
|
+
import { OPENCODE_THEME_IDS, OPENCODE_THEMES } from './themes/opencode'
|
|
14
5
|
|
|
15
|
-
export const THEMES: Record<string,
|
|
16
|
-
export const THEME_IDS: ThemeId[] = [...
|
|
6
|
+
export const THEMES: Record<string, AimuxTheme> = { ...HOUSE_THEMES, ...OPENCODE_THEMES }
|
|
7
|
+
export const THEME_IDS: ThemeId[] = [...HOUSE_THEME_IDS, ...OPENCODE_THEME_IDS]
|
|
17
8
|
|
|
18
|
-
const DEFAULT_THEME_ID: ThemeId = 'aimux'
|
|
9
|
+
const DEFAULT_THEME_ID: ThemeId = 'aimux-dark'
|
|
19
10
|
|
|
20
|
-
const
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
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
|
|
34
|
-
}
|
|
35
|
-
|
|
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
|
|
11
|
+
const LEGACY_MODE_HINTS: Record<string, ThemeId> = {
|
|
12
|
+
'aimux': 'aimux-dark',
|
|
13
|
+
'aimux-dark': 'aimux-dark',
|
|
14
|
+
'aimux-light': 'aimux-light',
|
|
44
15
|
}
|
|
45
16
|
|
|
46
17
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
18
|
+
* Migrate a legacy persisted theme id (anything from the old wide registry)
|
|
19
|
+
* to the new light/dark identifier. Anything we don't recognize lands on the
|
|
20
|
+
* dark default — modes are the only knob now.
|
|
50
21
|
*/
|
|
51
|
-
export function
|
|
52
|
-
|
|
53
|
-
)
|
|
54
|
-
|
|
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
|
-
}
|
|
22
|
+
export function migrateThemeId(id: string | undefined): ThemeId {
|
|
23
|
+
if (id === undefined) return DEFAULT_THEME_ID
|
|
24
|
+
if (id in THEMES) return id as ThemeId
|
|
25
|
+
return LEGACY_MODE_HINTS[id] ?? DEFAULT_THEME_ID
|
|
78
26
|
}
|
|
79
27
|
|
|
80
|
-
export
|
|
81
|
-
|
|
82
|
-
* Palette shortcut. Patches VSCode color keys on top of an existing theme;
|
|
83
|
-
* token settings inherit from the base unchanged.
|
|
84
|
-
*/
|
|
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 }
|
|
91
|
-
},
|
|
92
|
-
/**
|
|
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.
|
|
96
|
-
*/
|
|
97
|
-
full(theme: UserThemeInput): UserThemeInput {
|
|
98
|
-
return theme
|
|
99
|
-
},
|
|
28
|
+
export function isKnownThemeId(id: string): id is ThemeId {
|
|
29
|
+
return id in THEMES
|
|
100
30
|
}
|
package/src/types.ts
CHANGED
|
@@ -257,6 +257,7 @@ export interface ModalSessionPicker extends ModalBase {
|
|
|
257
257
|
}
|
|
258
258
|
export interface ModalSessionName extends ModalBase {
|
|
259
259
|
type: 'session-name'
|
|
260
|
+
returnToSessionPicker: boolean
|
|
260
261
|
}
|
|
261
262
|
export interface ModalRenameTab extends ModalBase {
|
|
262
263
|
type: 'rename-tab'
|
|
@@ -357,7 +358,12 @@ export type ModalAction =
|
|
|
357
358
|
| { type: 'open-help-modal'; scope?: ModeId }
|
|
358
359
|
| { type: 'open-split-picker'; direction: SplitDirection }
|
|
359
360
|
| { type: 'open-session-picker' }
|
|
360
|
-
| {
|
|
361
|
+
| {
|
|
362
|
+
type: 'open-session-name-modal'
|
|
363
|
+
sessionTargetId?: string
|
|
364
|
+
initialName?: string
|
|
365
|
+
returnToSessionPicker?: boolean
|
|
366
|
+
}
|
|
361
367
|
| { type: 'close-modal' }
|
|
362
368
|
| { type: 'move-modal-selection'; delta: number }
|
|
363
369
|
| { type: 'update-command-edit'; char: string }
|
|
@@ -532,7 +538,7 @@ export type SideEffect =
|
|
|
532
538
|
| { type: 'apply-theme'; action: 'confirm' }
|
|
533
539
|
| { type: 'apply-theme'; action: 'preview'; delta: 1 | -1 }
|
|
534
540
|
| { type: 'rename-session'; sessionId: string; name: string }
|
|
535
|
-
| { type: 'split-pane'; direction: SplitDirection }
|
|
541
|
+
| { type: 'split-pane'; direction: SplitDirection; sourceTabId?: string }
|
|
536
542
|
| { type: 'confirm-split' }
|
|
537
543
|
| { type: 'scroll-git-diff'; delta: number }
|
|
538
544
|
| { type: 'persist-git-diff-mode-ratio'; ratio: number }
|
|
@@ -546,6 +552,7 @@ export type SideEffect =
|
|
|
546
552
|
| { type: 'git-push' }
|
|
547
553
|
| { type: 'confirm-update-selection' }
|
|
548
554
|
| { type: 'switch-session-by-index'; index: number }
|
|
555
|
+
| { type: 'delete-session'; sessionId: string }
|
|
549
556
|
| { type: 'toggle-transparent' }
|
|
550
557
|
|
|
551
558
|
// ─── Key input / KeyResult / ModeContext ──────────────────────────────────────
|
|
@@ -571,138 +578,40 @@ export interface ModeContext {
|
|
|
571
578
|
|
|
572
579
|
// ─── Themes ───────────────────────────────────────────────────────────────────
|
|
573
580
|
|
|
574
|
-
import type { BundledTheme } from 'shiki'
|
|
575
|
-
|
|
576
|
-
export type ThemeId = BundledTheme | (string & {})
|
|
577
|
-
|
|
578
581
|
/**
|
|
579
|
-
*
|
|
580
|
-
*
|
|
582
|
+
* Compact opencode-style semantic palette. Required tokens cover every UI role;
|
|
583
|
+
* optional tokens fall back via helpers in `palette-utils.ts`.
|
|
581
584
|
*/
|
|
582
|
-
export
|
|
583
|
-
//
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
// lists
|
|
604
|
-
'list.activeSelectionBackground',
|
|
605
|
-
'list.activeSelectionForeground',
|
|
606
|
-
'list.hoverBackground',
|
|
607
|
-
// text & links
|
|
608
|
-
'foreground',
|
|
609
|
-
'descriptionForeground',
|
|
610
|
-
'textLink.foreground',
|
|
611
|
-
'textLink.activeForeground',
|
|
612
|
-
// status
|
|
613
|
-
'errorForeground',
|
|
614
|
-
'editorError.foreground',
|
|
615
|
-
'editorWarning.foreground',
|
|
616
|
-
// terminal ANSI
|
|
617
|
-
'terminal.ansiRed',
|
|
618
|
-
'terminal.ansiGreen',
|
|
619
|
-
'terminal.ansiYellow',
|
|
620
|
-
'terminal.ansiBlue',
|
|
621
|
-
'terminal.ansiMagenta',
|
|
622
|
-
'terminal.ansiCyan',
|
|
623
|
-
// git decorations
|
|
624
|
-
'gitDecoration.addedResourceForeground',
|
|
625
|
-
'gitDecoration.modifiedResourceForeground',
|
|
626
|
-
'gitDecoration.deletedResourceForeground',
|
|
627
|
-
'gitDecoration.untrackedResourceForeground',
|
|
628
|
-
// diff editor
|
|
629
|
-
'diffEditor.insertedLineBackground',
|
|
630
|
-
'diffEditor.removedLineBackground',
|
|
631
|
-
// input
|
|
632
|
-
'input.background',
|
|
633
|
-
'input.foreground',
|
|
634
|
-
'input.border',
|
|
635
|
-
] as const
|
|
636
|
-
|
|
637
|
-
export type AimuxColorKey = (typeof AIMUX_COLOR_KEYS)[number]
|
|
638
|
-
|
|
639
|
-
export type ThemeColorMap = Record<AimuxColorKey, string> & Record<string, string>
|
|
640
|
-
|
|
641
|
-
export interface ThemeSettings {
|
|
642
|
-
fontStyle?: string
|
|
643
|
-
foreground?: string
|
|
644
|
-
background?: string
|
|
645
|
-
// Some bundled shiki themes include non-standard keys (e.g. `content`); allow passthrough.
|
|
646
|
-
[key: string]: string | undefined
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
export interface ThemeTokenRule {
|
|
650
|
-
name?: string
|
|
651
|
-
scope?: string | string[]
|
|
652
|
-
// `foreground` at the top level is used by a handful of shiki themes as a
|
|
653
|
-
// shorthand for `settings.foreground` — accept both forms.
|
|
654
|
-
foreground?: string
|
|
655
|
-
settings?: ThemeSettings
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
export interface Theme {
|
|
585
|
+
export interface AimuxPalette {
|
|
586
|
+
// required
|
|
587
|
+
neutral: string
|
|
588
|
+
ink: string
|
|
589
|
+
primary: string
|
|
590
|
+
success: string
|
|
591
|
+
warning: string
|
|
592
|
+
error: string
|
|
593
|
+
info: string
|
|
594
|
+
// optional
|
|
595
|
+
accent?: string
|
|
596
|
+
interactive?: string
|
|
597
|
+
diffAdd?: string
|
|
598
|
+
diffDelete?: string
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
export type ThemeMode = 'light' | 'dark'
|
|
602
|
+
// eslint-disable-next-line typescript/no-redundant-type-constituents
|
|
603
|
+
export type ThemeId = 'aimux-light' | 'aimux-dark' | (string & {})
|
|
604
|
+
|
|
605
|
+
export interface AimuxTheme {
|
|
659
606
|
name: string
|
|
607
|
+
mode: ThemeMode
|
|
660
608
|
displayName: string
|
|
661
|
-
|
|
609
|
+
palette: AimuxPalette
|
|
662
610
|
fg: string
|
|
663
611
|
bg: string
|
|
664
|
-
colors: ThemeColorMap
|
|
665
|
-
settings: ThemeTokenRule[]
|
|
666
|
-
tokenColors?: ThemeTokenRule[]
|
|
667
|
-
colorReplacements?: Record<string, string>
|
|
668
|
-
semanticHighlighting?: boolean
|
|
669
|
-
semanticTokenColors?: Record<
|
|
670
|
-
string,
|
|
671
|
-
| string
|
|
672
|
-
| {
|
|
673
|
-
foreground?: string
|
|
674
|
-
fontStyle?: string
|
|
675
|
-
bold?: boolean
|
|
676
|
-
italic?: boolean
|
|
677
|
-
underline?: boolean
|
|
678
|
-
}
|
|
679
|
-
>
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/** Partial-override shape for `themes.define(name, base, overrides)`. */
|
|
683
|
-
export interface ThemeDefinition {
|
|
684
|
-
base?: ThemeId
|
|
685
|
-
colors: Partial<ThemeColorMap>
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
export interface NamedThemeDefinition extends ThemeDefinition {
|
|
689
|
-
name: string
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
export interface NamedTheme extends Theme {
|
|
693
|
-
displayName: string
|
|
694
612
|
}
|
|
695
613
|
|
|
696
|
-
|
|
697
|
-
* Input shape accepted by `themes.full(...)` — lets users paste a shiki /
|
|
698
|
-
* VSCode theme JSON without typing every AIMUX_COLOR_KEY. `registerUserThemes`
|
|
699
|
-
* runs it through `normalizeTheme` to produce a complete `Theme`.
|
|
700
|
-
*/
|
|
701
|
-
export interface UserThemeInput extends Partial<Omit<Theme, 'colors' | 'name' | 'displayName'>> {
|
|
702
|
-
name: string
|
|
703
|
-
displayName: string
|
|
704
|
-
colors?: Partial<ThemeColorMap> & Record<string, string | undefined>
|
|
705
|
-
}
|
|
614
|
+
export type Theme = AimuxTheme
|
|
706
615
|
|
|
707
616
|
// ─── Backend config (stub) ────────────────────────────────────────────────────
|
|
708
617
|
|
|
@@ -816,9 +725,13 @@ export interface GitPanePaneConfig extends GitPaneBaseConfig {
|
|
|
816
725
|
|
|
817
726
|
export type GitPaneConfig = GitPaneEmbeddedConfig | GitPanePaneConfig
|
|
818
727
|
|
|
728
|
+
export interface AimuxThemeConfig {
|
|
729
|
+
mode?: ThemeMode
|
|
730
|
+
paletteOverrides?: Partial<AimuxPalette>
|
|
731
|
+
}
|
|
732
|
+
|
|
819
733
|
export interface AimuxUserConfig {
|
|
820
|
-
theme?:
|
|
821
|
-
themes?: Record<string, NamedThemeDefinition | UserThemeInput>
|
|
734
|
+
theme?: AimuxThemeConfig
|
|
822
735
|
keymaps?: (k: KeymapBuilderApi) => KeymapBuilderApi
|
|
823
736
|
backends?: Record<string, BackendConfig>
|
|
824
737
|
sidebar?: SidebarConfig
|
|
@@ -851,8 +764,7 @@ export interface ResolvedKeymapConfig {
|
|
|
851
764
|
}
|
|
852
765
|
|
|
853
766
|
export interface ResolvedConfig {
|
|
854
|
-
theme:
|
|
855
|
-
themes: Record<string, NamedThemeDefinition | UserThemeInput>
|
|
767
|
+
theme: AimuxThemeConfig | undefined
|
|
856
768
|
keymaps: ResolvedKeymapConfig
|
|
857
769
|
backends: Record<string, BackendConfig>
|
|
858
770
|
sidebar: SidebarConfig
|
package/src/normalize-theme.ts
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
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
|
-
}
|