@brimveyn/aimux 1.6.0 → 1.6.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.
- package/package.json +2 -2
- package/src/git/git-diff.ts +24 -12
- package/src/git/git-poller.ts +11 -3
- package/src/git/git-status.ts +98 -17
- package/src/pty/terminal-snapshot.ts +5 -5
- package/src/state/git-tree.ts +1 -1
- package/src/state/reducers/git-mode-state.ts +44 -1
- package/src/state/reducers/git-panel-state.ts +6 -1
- package/src/state/types.ts +5 -1
- package/src/ui/components/create-session-modal.tsx +5 -4
- package/src/ui/components/diff-renderer/fold-strip.tsx +3 -1
- package/src/ui/components/diff-renderer/pierre-diff.tsx +2 -1
- package/src/ui/components/diff-renderer/split-view.tsx +5 -1
- package/src/ui/components/diff-renderer/stacked-view.tsx +3 -1
- package/src/ui/components/git-commit-modal.tsx +2 -1
- package/src/ui/components/git-pane-widget.tsx +1 -1
- package/src/ui/components/git-panel.tsx +77 -45
- package/src/ui/components/git-view.tsx +15 -4
- package/src/ui/components/help-modal.tsx +2 -1
- package/src/ui/components/input-field.tsx +2 -1
- package/src/ui/components/list-item.tsx +2 -1
- package/src/ui/components/modal-filter-bar.tsx +2 -1
- package/src/ui/components/modal-keybinds-overlay.tsx +2 -1
- package/src/ui/components/modal-shell.tsx +2 -1
- package/src/ui/components/new-tab-modal.tsx +2 -1
- package/src/ui/components/pending-chord-overlay.tsx +2 -1
- package/src/ui/components/session-bar.tsx +3 -1
- package/src/ui/components/session-picker-modal.tsx +2 -1
- package/src/ui/components/sidebar.tsx +8 -5
- package/src/ui/components/snippet-editor-modal.tsx +2 -1
- package/src/ui/components/snippet-picker-modal.tsx +2 -1
- package/src/ui/components/split-layout.tsx +2 -1
- package/src/ui/components/status-bar.tsx +8 -6
- package/src/ui/components/surface.tsx +6 -6
- package/src/ui/components/tab-item.tsx +14 -9
- package/src/ui/components/terminal-pane.tsx +7 -5
- package/src/ui/components/theme-picker-modal.tsx +2 -1
- package/src/ui/components/update-available-modal.tsx +2 -1
- package/src/ui/filter-themes.ts +7 -2
- package/src/ui/root.tsx +3 -1
- package/src/ui/status-bar-model.ts +13 -3
- package/src/ui/theme-store.ts +36 -0
- package/src/ui/theme.ts +4 -29
- package/src/ui/themes.ts +15 -63
- package/src/ui/house-themes.ts +0 -313
- package/src/ui/themes.generated.ts +0 -59794
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { getCurrentTheme } from '../theme'
|
|
4
4
|
|
|
5
5
|
type SurfaceTone = 'muted' | 'elevated' | 'selected' | 'input' | 'inputActive'
|
|
6
6
|
|
|
7
7
|
function getSurfaceColor(tone: SurfaceTone): string {
|
|
8
8
|
switch (tone) {
|
|
9
9
|
case 'elevated':
|
|
10
|
-
return
|
|
10
|
+
return getCurrentTheme().colors['sideBar.background']
|
|
11
11
|
case 'selected':
|
|
12
|
-
return
|
|
12
|
+
return getCurrentTheme().colors['list.activeSelectionBackground']
|
|
13
13
|
case 'input':
|
|
14
|
-
return
|
|
14
|
+
return getCurrentTheme().colors['editor.background']
|
|
15
15
|
case 'inputActive':
|
|
16
|
-
return
|
|
16
|
+
return getCurrentTheme().colors['list.activeSelectionBackground']
|
|
17
17
|
case 'muted':
|
|
18
18
|
default:
|
|
19
|
-
return
|
|
19
|
+
return getCurrentTheme().colors['sideBarSectionHeader.background']
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TabSession } from '../../state/types'
|
|
2
2
|
|
|
3
3
|
import { useBusySpinner } from '../hooks/use-busy-spinner'
|
|
4
|
-
import {
|
|
4
|
+
import { getCurrentTheme, useTheme } from '../theme'
|
|
5
5
|
|
|
6
6
|
interface TabItemProps {
|
|
7
7
|
id?: string
|
|
@@ -15,15 +15,15 @@ interface TabItemProps {
|
|
|
15
15
|
function getStatusColor(status: TabSession['status']): string {
|
|
16
16
|
switch (status) {
|
|
17
17
|
case 'running':
|
|
18
|
-
return
|
|
18
|
+
return getCurrentTheme().colors['gitDecoration.addedResourceForeground']
|
|
19
19
|
case 'disconnected':
|
|
20
|
-
return
|
|
20
|
+
return getCurrentTheme().colors['editorWarning.foreground']
|
|
21
21
|
case 'error':
|
|
22
|
-
return
|
|
22
|
+
return getCurrentTheme().colors['editorError.foreground']
|
|
23
23
|
case 'exited':
|
|
24
|
-
return
|
|
24
|
+
return getCurrentTheme().colors['editorWarning.foreground']
|
|
25
25
|
default:
|
|
26
|
-
return
|
|
26
|
+
return getCurrentTheme().colors['descriptionForeground']
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -37,20 +37,24 @@ function getIndicator(active: boolean, focused: boolean, inLayout: boolean): str
|
|
|
37
37
|
|
|
38
38
|
function getIndicatorColor(active: boolean, focused: boolean, inLayout: boolean): string {
|
|
39
39
|
if (active) {
|
|
40
|
-
return focused
|
|
40
|
+
return focused
|
|
41
|
+
? getCurrentTheme().colors['textLink.foreground']
|
|
42
|
+
: getCurrentTheme().colors['terminal.ansiMagenta']
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
return inLayout
|
|
44
|
-
?
|
|
45
|
-
:
|
|
46
|
+
? getCurrentTheme().colors['descriptionForeground']
|
|
47
|
+
: getCurrentTheme().colors['editor.lineHighlightBackground']
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
function BusyIndicator() {
|
|
51
|
+
const theme = useTheme()
|
|
49
52
|
const frame = useBusySpinner()
|
|
50
53
|
return <text fg={theme.colors['textLink.foreground']}>{frame} busy</text>
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
function ActivityIndicator({ isFocusedInput, tab }: { tab: TabSession; isFocusedInput: boolean }) {
|
|
57
|
+
const theme = useTheme()
|
|
54
58
|
if (tab.status === 'error') {
|
|
55
59
|
return <text fg={theme.colors['editorError.foreground']}>✗ error</text>
|
|
56
60
|
}
|
|
@@ -79,6 +83,7 @@ function ActivityIndicator({ isFocusedInput, tab }: { tab: TabSession; isFocused
|
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
export function TabItem({ active, focused, id, inLayout, isFocusedInput, tab }: TabItemProps) {
|
|
86
|
+
const theme = useTheme()
|
|
82
87
|
const label = tab.command.split(' ')[0]
|
|
83
88
|
const isInLayout = inLayout ?? false
|
|
84
89
|
const indicator = getIndicator(active, focused, isInLayout)
|
|
@@ -6,7 +6,7 @@ import type { TerminalContentOrigin } from '../../input/raw-input-handler'
|
|
|
6
6
|
import type { TabSession, TerminalSnapshot, TerminalSpan } from '../../state/types'
|
|
7
7
|
|
|
8
8
|
import { logInputDebug } from '../../debug/input-log'
|
|
9
|
-
import {
|
|
9
|
+
import { getCurrentTheme, useTheme } from '../theme'
|
|
10
10
|
|
|
11
11
|
interface TerminalPaneProps {
|
|
12
12
|
tab?: TabSession
|
|
@@ -46,14 +46,14 @@ function getTitle(
|
|
|
46
46
|
|
|
47
47
|
function getBorderColor(isActive: boolean, focusMode: TerminalPaneProps['focusMode']): string {
|
|
48
48
|
if (isActive && focusMode === 'terminal-input') {
|
|
49
|
-
return
|
|
49
|
+
return getCurrentTheme().colors['focusBorder']
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
if (isActive) {
|
|
53
|
-
return
|
|
53
|
+
return getCurrentTheme().colors['terminal.ansiMagenta']
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
return
|
|
56
|
+
return getCurrentTheme().colors['editor.lineHighlightBackground']
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
function renderSpan(span: TerminalSpan, key: string): ReactNode {
|
|
@@ -72,7 +72,7 @@ function renderSpan(span: TerminalSpan, key: string): ReactNode {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
return (
|
|
75
|
-
<span key={key} fg={span.fg ??
|
|
75
|
+
<span key={key} fg={span.fg ?? getCurrentTheme().colors['editor.foreground']} bg={span.bg}>
|
|
76
76
|
{node}
|
|
77
77
|
</span>
|
|
78
78
|
)
|
|
@@ -87,6 +87,7 @@ const TerminalViewport = memo(function TerminalViewport({
|
|
|
87
87
|
buffer,
|
|
88
88
|
viewport,
|
|
89
89
|
}: TerminalViewportProps) {
|
|
90
|
+
const theme = useTheme()
|
|
90
91
|
if (viewport && viewport.lines.length > 0) {
|
|
91
92
|
const lines = viewport.lines
|
|
92
93
|
return (
|
|
@@ -123,6 +124,7 @@ export function TerminalPane({
|
|
|
123
124
|
tab,
|
|
124
125
|
tabId,
|
|
125
126
|
}: TerminalPaneProps) {
|
|
127
|
+
const theme = useTheme()
|
|
126
128
|
const paneIsActive = isActive ?? true
|
|
127
129
|
const canForwardMouse = focusMode === 'terminal-input' && !!tab && mouseForwardingEnabled
|
|
128
130
|
const canUseLocalScrollback = focusMode === 'terminal-input' && !!tab && localScrollbackEnabled
|
|
@@ -3,7 +3,7 @@ import { useLayoutEffect, useMemo, useRef } from 'react'
|
|
|
3
3
|
|
|
4
4
|
import { dispatchGlobal } from '../../state/dispatch-ref'
|
|
5
5
|
import { filterThemeIds } from '../filter-themes'
|
|
6
|
-
import {
|
|
6
|
+
import { useTheme } from '../theme'
|
|
7
7
|
import { type ThemeId, THEMES } from '../themes'
|
|
8
8
|
import { uiTokens } from '../ui-tokens'
|
|
9
9
|
import { ListItem } from './list-item'
|
|
@@ -45,6 +45,7 @@ function computeWindowStart(
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export function ThemePickerModal({ currentThemeId, filter, selectedIndex }: ThemePickerModalProps) {
|
|
48
|
+
const theme = useTheme()
|
|
48
49
|
const dimensions = useTerminalDimensions()
|
|
49
50
|
const filtered = useMemo(() => filterThemeIds(filter), [filter])
|
|
50
51
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useTheme } from '../theme'
|
|
2
2
|
import { uiTokens } from '../ui-tokens'
|
|
3
3
|
import { ListItem } from './list-item'
|
|
4
4
|
import { ModalShell } from './modal-shell'
|
|
@@ -19,6 +19,7 @@ export function UpdateAvailableModal({
|
|
|
19
19
|
latestVersion,
|
|
20
20
|
selectedIndex,
|
|
21
21
|
}: UpdateAvailableModalProps) {
|
|
22
|
+
const theme = useTheme()
|
|
22
23
|
return (
|
|
23
24
|
<ModalShell
|
|
24
25
|
title="Update available"
|
package/src/ui/filter-themes.ts
CHANGED
|
@@ -4,7 +4,12 @@ export function filterThemeIds(filter: string | null): ThemeId[] {
|
|
|
4
4
|
if (!filter) return THEME_IDS
|
|
5
5
|
const needle = filter.toLowerCase()
|
|
6
6
|
return THEME_IDS.filter((id) => {
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const entry = THEMES[id]
|
|
8
|
+
if (!entry) return false
|
|
9
|
+
return (
|
|
10
|
+
id.toLowerCase().includes(needle) ||
|
|
11
|
+
entry.displayName.toLowerCase().includes(needle) ||
|
|
12
|
+
entry.name.toLowerCase().includes(needle)
|
|
13
|
+
)
|
|
9
14
|
})
|
|
10
15
|
}
|
package/src/ui/root.tsx
CHANGED
|
@@ -24,7 +24,7 @@ import { StatusBar } from './components/status-bar'
|
|
|
24
24
|
import { TerminalPane } from './components/terminal-pane'
|
|
25
25
|
import { ThemePickerModal } from './components/theme-picker-modal'
|
|
26
26
|
import { UpdateAvailableModal } from './components/update-available-modal'
|
|
27
|
-
import {
|
|
27
|
+
import { useTheme } from './theme'
|
|
28
28
|
|
|
29
29
|
function getCreateSessionFields(modal: ModalState) {
|
|
30
30
|
if (modal.type !== 'create-session') {
|
|
@@ -215,6 +215,7 @@ export function RootView({
|
|
|
215
215
|
terminalRows,
|
|
216
216
|
themeId,
|
|
217
217
|
}: RootViewProps) {
|
|
218
|
+
const theme = useTheme()
|
|
218
219
|
const tabs = useAppStore((s) => s.tabs)
|
|
219
220
|
const activeTabId = useAppStore((s) => s.activeTabId)
|
|
220
221
|
const layoutTrees = useAppStore((s) => s.layoutTrees)
|
|
@@ -342,6 +343,7 @@ export function RootView({
|
|
|
342
343
|
}
|
|
343
344
|
|
|
344
345
|
function GitPaneInPaneMode({ ratio }: { ratio: number }) {
|
|
346
|
+
const theme = useTheme()
|
|
345
347
|
// Ratio maps to a fixed column count (20..80), mirroring the reservation in
|
|
346
348
|
// use-terminal-resize so the terminal-content area stays in sync.
|
|
347
349
|
const width = Math.max(20, Math.min(80, Math.round(ratio * 80)))
|
|
@@ -32,10 +32,17 @@ function getActiveTabLabel(tab?: TabSession): string {
|
|
|
32
32
|
return `${truncateLabel(tab.title)} (${tab.status})`
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const STAGING_DESCRIPTIONS = ['Stage', 'Unstage/delete', 'Commit', 'Push']
|
|
36
|
+
|
|
35
37
|
function hintForMode(config: ResolvedKeymapConfig, modeId: ModeId): string {
|
|
36
38
|
return buildHintText(config, modeId, HINT_LIMIT, { excludeDescriptions: [HELP_DESCRIPTION] })
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
function hintForGitMode(config: ResolvedKeymapConfig, headOffset: number): string {
|
|
42
|
+
const exclude = headOffset > 0 ? [HELP_DESCRIPTION, ...STAGING_DESCRIPTIONS] : [HELP_DESCRIPTION]
|
|
43
|
+
return buildHintText(config, 'git-mode', HINT_LIMIT, { excludeDescriptions: exclude })
|
|
44
|
+
}
|
|
45
|
+
|
|
39
46
|
function helpHintForMode(config: ResolvedKeymapConfig, modeId: ModeId): string {
|
|
40
47
|
const bindings = describeBindings(config, modeId, {
|
|
41
48
|
dedupeByDescription: true,
|
|
@@ -77,12 +84,15 @@ export function getStatusBarModel(
|
|
|
77
84
|
right: modalMode ? hintForMode(config, modalMode) : '',
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
|
-
case 'git':
|
|
87
|
+
case 'git': {
|
|
88
|
+
const headOffset = state.gitMode.headOffset
|
|
89
|
+
const offsetTag = headOffset > 0 ? ` HEAD~${headOffset}` : ''
|
|
81
90
|
return {
|
|
82
91
|
help: helpHintForMode(config, 'git-mode'),
|
|
83
|
-
left: `${sessionIcon} ${sessionLabel}`,
|
|
84
|
-
right:
|
|
92
|
+
left: `${sessionIcon} ${sessionLabel}${offsetTag}`,
|
|
93
|
+
right: hintForGitMode(config, headOffset),
|
|
85
94
|
}
|
|
95
|
+
}
|
|
86
96
|
case 'command-edit': {
|
|
87
97
|
const commandEditMode = deriveCommandEditModeId(state.modal.type)
|
|
88
98
|
return {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useStore } from 'zustand'
|
|
2
|
+
import { createStore } from 'zustand/vanilla'
|
|
3
|
+
|
|
4
|
+
import { type Theme, type ThemeId, THEMES } from './themes'
|
|
5
|
+
|
|
6
|
+
interface ThemeStore {
|
|
7
|
+
theme: Theme
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const DEFAULT = THEMES['aimux']
|
|
11
|
+
if (!DEFAULT) throw new Error('default theme missing')
|
|
12
|
+
|
|
13
|
+
const themeStore = createStore<ThemeStore>(() => ({ theme: DEFAULT }))
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Subscribe to the active theme. Pass a selector to subscribe to a narrower
|
|
17
|
+
* slice (`useTheme(t => t.colors['editor.foreground'])`) or call with no
|
|
18
|
+
* arguments to get the whole theme.
|
|
19
|
+
*/
|
|
20
|
+
export function useTheme(): Theme
|
|
21
|
+
export function useTheme<T>(selector: (theme: Theme) => T): T
|
|
22
|
+
export function useTheme<T>(selector?: (theme: Theme) => T): T | Theme {
|
|
23
|
+
return useStore(themeStore, (s) => (selector ? selector(s.theme) : s.theme))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Synchronous snapshot for non-React callers (reducers, side effects). */
|
|
27
|
+
export function getCurrentTheme(): Theme {
|
|
28
|
+
return themeStore.getState().theme
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Swap the active theme. Triggers re-renders on every `useTheme` subscriber. */
|
|
32
|
+
export function applyTheme(id: ThemeId): void {
|
|
33
|
+
const entry = THEMES[id]
|
|
34
|
+
if (!entry) return
|
|
35
|
+
themeStore.setState({ theme: entry })
|
|
36
|
+
}
|
package/src/ui/theme.ts
CHANGED
|
@@ -1,30 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// Back-compat shim: the theme singleton has been replaced with a Zustand store.
|
|
2
|
+
// React components should import `useTheme` from `./theme-store`; non-React
|
|
3
|
+
// callers use `getCurrentTheme()`. `applyTheme` moved to the store module.
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
if (!DEFAULT) throw new Error('default theme missing')
|
|
5
|
-
|
|
6
|
-
// Mutable theme singleton. Components access `theme.colors['editor.foreground']`
|
|
7
|
-
// directly. On `applyTheme(id)` the colors map is replaced in place so live
|
|
8
|
-
// imports keep pointing at the current palette.
|
|
9
|
-
export const theme: Theme = {
|
|
10
|
-
bg: DEFAULT.bg,
|
|
11
|
-
colors: { ...DEFAULT.colors },
|
|
12
|
-
displayName: DEFAULT.displayName,
|
|
13
|
-
fg: DEFAULT.fg,
|
|
14
|
-
name: DEFAULT.name,
|
|
15
|
-
settings: DEFAULT.settings,
|
|
16
|
-
type: DEFAULT.type,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function applyTheme(id: ThemeId): void {
|
|
20
|
-
const entry = THEMES[id]
|
|
21
|
-
if (!entry) return
|
|
22
|
-
for (const key of Object.keys(theme.colors)) delete theme.colors[key]
|
|
23
|
-
Object.assign(theme.colors, entry.colors)
|
|
24
|
-
theme.name = entry.name
|
|
25
|
-
theme.displayName = entry.displayName
|
|
26
|
-
theme.type = entry.type
|
|
27
|
-
theme.fg = entry.fg
|
|
28
|
-
theme.bg = entry.bg
|
|
29
|
-
theme.settings = entry.settings
|
|
30
|
-
}
|
|
5
|
+
export { applyTheme, getCurrentTheme, useTheme } from './theme-store'
|
package/src/ui/themes.ts
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type AimuxColorKey,
|
|
6
|
-
type NamedTheme,
|
|
7
|
-
type NamedThemeDefinition,
|
|
8
|
-
type Theme,
|
|
9
|
-
type ThemeColorMap,
|
|
10
|
-
type ThemeSettings,
|
|
11
|
-
type ThemeTokenRule,
|
|
12
|
-
} from '@brimveyn/aimux-config'
|
|
13
|
-
|
|
14
|
-
import { HOUSE_THEME_IDS, HOUSE_THEMES } from './house-themes'
|
|
15
|
-
import { GENERATED_THEME_IDS, GENERATED_THEMES } from './themes.generated'
|
|
1
|
+
// Thin runtime re-export. Theme data, types, normalization, and registration
|
|
2
|
+
// all live in `@brimveyn/aimux-config` so the dependency-free config package is
|
|
3
|
+
// the single source of truth. The mutable theme singleton + `applyTheme` stay
|
|
4
|
+
// in `./theme.ts` since they're UI-layer concerns.
|
|
16
5
|
|
|
17
6
|
export type {
|
|
18
7
|
AimuxColorKey,
|
|
@@ -20,54 +9,17 @@ export type {
|
|
|
20
9
|
NamedThemeDefinition,
|
|
21
10
|
Theme,
|
|
22
11
|
ThemeColorMap,
|
|
12
|
+
ThemeId,
|
|
23
13
|
ThemeSettings,
|
|
24
14
|
ThemeTokenRule,
|
|
25
|
-
}
|
|
26
|
-
export { AIMUX_COLOR_KEYS }
|
|
27
|
-
|
|
28
|
-
export type ThemeId = BundledTheme | (string & {})
|
|
29
|
-
|
|
30
|
-
export const THEMES: Record<string, Theme> = { ...GENERATED_THEMES, ...HOUSE_THEMES }
|
|
31
|
-
export const THEME_IDS: ThemeId[] = [...GENERATED_THEME_IDS, ...HOUSE_THEME_IDS]
|
|
32
|
-
|
|
33
|
-
const DEFAULT_THEME_ID: ThemeId = 'aimux'
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Merge user-declared themes into the registry. Themes declared via
|
|
37
|
-
* `themes.full(...)` arrive as complete `NamedTheme` objects; themes declared
|
|
38
|
-
* via `themes.define(...)` arrive as `NamedThemeDefinition` (partial overrides
|
|
39
|
-
* over a base) and are expanded here.
|
|
40
|
-
*/
|
|
41
|
-
export function registerUserThemes(
|
|
42
|
-
defs: Record<string, NamedTheme | NamedThemeDefinition> | undefined
|
|
43
|
-
): void {
|
|
44
|
-
if (!defs) return
|
|
45
|
-
const fallback = THEMES[DEFAULT_THEME_ID]
|
|
46
|
-
if (!fallback) return
|
|
47
|
-
for (const [id, def] of Object.entries(defs)) {
|
|
48
|
-
THEMES[id] = isFullTheme(def) ? def : expandDefinition(id, def, fallback)
|
|
49
|
-
if (!THEME_IDS.includes(id)) THEME_IDS.push(id)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function isFullTheme(def: NamedTheme | NamedThemeDefinition): def is NamedTheme {
|
|
54
|
-
return 'settings' in def && Array.isArray((def as Theme).settings)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function expandDefinition(id: string, def: NamedThemeDefinition, fallback: Theme): Theme {
|
|
58
|
-
const base = def.base ? (THEMES[def.base] ?? fallback) : fallback
|
|
59
|
-
const colors: ThemeColorMap = { ...base.colors }
|
|
60
|
-
for (const [k, v] of Object.entries(def.colors ?? {})) {
|
|
61
|
-
if (typeof v === 'string') colors[k] = v
|
|
62
|
-
}
|
|
63
|
-
return {
|
|
64
|
-
...base,
|
|
65
|
-
colors,
|
|
66
|
-
displayName: def.name,
|
|
67
|
-
name: id,
|
|
68
|
-
}
|
|
69
|
-
}
|
|
15
|
+
} from '@brimveyn/aimux-config'
|
|
70
16
|
|
|
71
|
-
export
|
|
72
|
-
|
|
73
|
-
|
|
17
|
+
export {
|
|
18
|
+
AIMUX_COLOR_KEYS,
|
|
19
|
+
isKnownThemeId,
|
|
20
|
+
migrateThemeId,
|
|
21
|
+
normalizeTheme,
|
|
22
|
+
registerUserThemes,
|
|
23
|
+
THEME_IDS,
|
|
24
|
+
THEMES,
|
|
25
|
+
} from '@brimveyn/aimux-config'
|