@brimveyn/aimux 1.9.5 → 1.9.6

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 (41) hide show
  1. package/package.json +1 -1
  2. package/src/app.tsx +4 -5
  3. package/src/pty/terminal-snapshot.ts +7 -7
  4. package/src/ui/components/git/diff-renderer/fold-strip.tsx +7 -7
  5. package/src/ui/components/git/diff-renderer/pierre-diff.tsx +3 -3
  6. package/src/ui/components/git/diff-renderer/split-view.tsx +18 -17
  7. package/src/ui/components/git/diff-renderer/stacked-view.tsx +13 -13
  8. package/src/ui/components/git/git-panel.tsx +42 -39
  9. package/src/ui/components/git/git-view.tsx +20 -20
  10. package/src/ui/components/layout/session-bar.tsx +13 -13
  11. package/src/ui/components/layout/sidebar/sidebar.tsx +26 -28
  12. package/src/ui/components/layout/sidebar/tab-item.tsx +21 -21
  13. package/src/ui/components/layout/split-layout.tsx +2 -2
  14. package/src/ui/components/layout/status-bar.tsx +12 -12
  15. package/src/ui/components/layout/terminal-pane.tsx +19 -31
  16. package/src/ui/components/modals/app/ai-usage-modal.tsx +21 -21
  17. package/src/ui/components/modals/app/help-modal.tsx +5 -6
  18. package/src/ui/components/modals/app/update-available-modal.tsx +3 -3
  19. package/src/ui/components/modals/git/git-commit-modal.tsx +17 -17
  20. package/src/ui/components/modals/sessions/create-session-modal.tsx +8 -8
  21. package/src/ui/components/modals/sessions/session-picker-modal.tsx +6 -6
  22. package/src/ui/components/modals/shared/form.tsx +4 -4
  23. package/src/ui/components/modals/shared/modal-keybinds-overlay.tsx +4 -4
  24. package/src/ui/components/modals/shared/modal-shell.tsx +6 -6
  25. package/src/ui/components/modals/shared/picker.tsx +6 -6
  26. package/src/ui/components/modals/snippets/snippet-picker-modal.tsx +5 -5
  27. package/src/ui/components/modals/tabs/new-tab-modal.tsx +6 -6
  28. package/src/ui/components/modals/themes/theme-picker-modal.tsx +17 -20
  29. package/src/ui/components/overlays/ai-usage/ai-usage-indicator.tsx +12 -12
  30. package/src/ui/components/overlays/context-menu/context-menu-overlay.tsx +6 -6
  31. package/src/ui/components/overlays/pending-chord-overlay.tsx +5 -5
  32. package/src/ui/components/primitives/bare-input.tsx +5 -5
  33. package/src/ui/components/primitives/input-field.tsx +5 -5
  34. package/src/ui/components/primitives/list-item.tsx +27 -22
  35. package/src/ui/components/primitives/surface.tsx +12 -9
  36. package/src/ui/filter-themes.ts +9 -8
  37. package/src/ui/root.tsx +5 -4
  38. package/src/ui/shiki.ts +8 -11
  39. package/src/ui/theme-store.ts +48 -118
  40. package/src/ui/theme.ts +8 -8
  41. package/src/ui/themes.ts +9 -20
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brimveyn/aimux",
3
- "version": "1.9.5",
3
+ "version": "1.9.6",
4
4
  "description": "A terminal multiplexer for AI CLIs. Run Claude, Codex, OpenCode side-by-side with tabbed navigation, split panes, and persistent sessions.",
5
5
  "keywords": [
6
6
  "ai",
package/src/app.tsx CHANGED
@@ -35,7 +35,7 @@ import { loadSnippetCatalog } from './state/snippet-catalog'
35
35
  import { createInitialState } from './state/store'
36
36
  import { KeymapContext } from './ui/keymap-context'
37
37
  import { RootView } from './ui/root'
38
- import { applyTheme, setTransparent } from './ui/theme'
38
+ import { applyTheme, setMode, setTransparent } from './ui/theme'
39
39
  import { isKnownThemeId, type ThemeId } from './ui/themes'
40
40
  import {
41
41
  fetchLatestNpmVersion,
@@ -71,10 +71,9 @@ export function App({
71
71
  const [themeId, setThemeId] = useState<ThemeId>(() => {
72
72
  const config = loadConfig()
73
73
  const persisted = config.themeId && isKnownThemeId(config.themeId) ? config.themeId : undefined
74
- const fromConfig: ThemeId =
75
- resolvedConfig.theme?.initialMode === 'light' ? 'aimux-light' : 'aimux-dark'
76
- const initial: ThemeId = persisted ?? fromConfig
77
- applyTheme(initial, resolvedConfig.theme?.paletteOverrides)
74
+ const initial: ThemeId = persisted ?? resolvedConfig.theme?.initialId ?? 'aimux'
75
+ applyTheme(initial)
76
+ if (resolvedConfig.theme?.initialMode) setMode(resolvedConfig.theme.initialMode)
78
77
  setTransparent(config.themeTransparent ?? false)
79
78
  return initial
80
79
  })
@@ -2,7 +2,7 @@ import type { Terminal } from '@xterm/headless'
2
2
 
3
3
  import type { TerminalLine, TerminalSnapshot, TerminalSpan } from '../state/types'
4
4
 
5
- import { getCurrentTokens } from '../ui/theme'
5
+ import { getCurrentTheme } from '../ui/theme'
6
6
 
7
7
  const SNAPSHOT_TAIL_LINE_COUNT = 10
8
8
 
@@ -115,17 +115,17 @@ function buildLine(
115
115
  let bg = getColorHex(current.getBgColor(), bgMode)
116
116
 
117
117
  if (current.isInverse()) {
118
- const tokens = getCurrentTokens()
119
- const resolvedFg = fg ?? tokens.palette.ink
120
- const resolvedBg = bg ?? tokens.bg
118
+ const tokens = getCurrentTheme()
119
+ const resolvedFg = fg ?? tokens.text
120
+ const resolvedBg = bg ?? tokens.background
121
121
  ;[fg, bg] = [resolvedBg, resolvedFg]
122
122
  }
123
123
 
124
124
  const isCursorCell = cursorVisible && cursorColumn === column
125
125
  if (isCursorCell) {
126
- const tokens = getCurrentTokens()
127
- const resolvedFg = fg ?? tokens.palette.ink
128
- const resolvedBg = bg ?? tokens.bg
126
+ const tokens = getCurrentTheme()
127
+ const resolvedFg = fg ?? tokens.text
128
+ const resolvedBg = bg ?? tokens.background
129
129
  ;[fg, bg] = [resolvedBg, resolvedFg]
130
130
  }
131
131
 
@@ -1,4 +1,4 @@
1
- import { useBg, useTokens } from '../../../theme'
1
+ import { useTheme } from '../../../theme'
2
2
  import { FOLD_STEP, type FoldInfo } from './build-rows'
3
3
  import { type FoldDispatch } from './pierre-diff'
4
4
 
@@ -8,11 +8,11 @@ interface Props {
8
8
  }
9
9
 
10
10
  function Button({ label, onPress }: { label: string; onPress: () => void }) {
11
- const t = useTokens()
12
- const bg = useBg('elevated')
11
+ const t = useTheme()
12
+ const bg = t.diffContextBg
13
13
  return (
14
14
  <box paddingLeft={1} paddingRight={1} backgroundColor={bg} onMouseDown={onPress}>
15
- <text fg={t.palette.primary}>{label}</text>
15
+ <text fg={t.primary}>{label}</text>
16
16
  </box>
17
17
  )
18
18
  }
@@ -22,8 +22,8 @@ function Spacer() {
22
22
  }
23
23
 
24
24
  export function FoldStrip({ dispatch, fold }: Props) {
25
- const t = useTokens()
26
- const headerBg = useBg('elevated')
25
+ const t = useTheme()
26
+ const headerBg = t.diffContextBg
27
27
  const { bottomExpanded, foldId, hidden, topExpanded, total } = fold
28
28
  const stepUp = Math.min(FOLD_STEP, hidden)
29
29
  const stepDown = Math.min(FOLD_STEP, hidden)
@@ -71,7 +71,7 @@ export function FoldStrip({ dispatch, fold }: Props) {
71
71
 
72
72
  return (
73
73
  <box flexDirection="row" backgroundColor={headerBg} paddingLeft={1} paddingRight={1}>
74
- <text fg={t.muted}>{`⋯ ${hidden} hidden `}</text>
74
+ <text fg={t.textMuted}>{`⋯ ${hidden} hidden `}</text>
75
75
  {controls}
76
76
  </box>
77
77
  )
@@ -8,7 +8,7 @@ import type { ThemeId } from '../../../themes'
8
8
  import { useAppStore } from '../../../../state/app-store'
9
9
  import { dispatchGlobal } from '../../../../state/dispatch-ref'
10
10
  import { type FoldState } from '../../../../state/types'
11
- import { useTokens } from '../../../theme'
11
+ import { useTheme } from '../../../theme'
12
12
  import { buildDiffSegments, firstChangeSegmentOffset, gutterWidth } from './build-rows'
13
13
  import { SplitView, type SplitViewHandle } from './split-view'
14
14
  import { StackedView, type StackedViewHandle } from './stacked-view'
@@ -45,7 +45,7 @@ export const PierreDiff = forwardRef<PierreDiffHandle, Props>(function PierreDif
45
45
  { cacheKey, diff, path, themeId, view },
46
46
  ref
47
47
  ) {
48
- const t = useTokens()
48
+ const t = useTheme()
49
49
  const preparation = useDiffPreparation(cacheKey, diff, path, themeId)
50
50
  const file = preparation.file ?? undefined
51
51
  const highlights: DiffHighlights = preparation.highlights
@@ -123,7 +123,7 @@ export const PierreDiff = forwardRef<PierreDiffHandle, Props>(function PierreDif
123
123
  if (!file) {
124
124
  return (
125
125
  <box flexGrow={1} padding={1}>
126
- <text fg={t.muted}>
126
+ <text fg={t.textMuted}>
127
127
  {preparation.preparing ? 'Preparing diff…' : '(could not parse diff)'}
128
128
  </text>
129
129
  </box>
@@ -20,7 +20,7 @@ import type { DiffHighlights, FoldDispatch } from './pierre-diff'
20
20
 
21
21
  import { getScrollViewportDelta } from '../../../../app-runtime/terminal-mouse-adapter'
22
22
  import { scrollGitDiff } from '../../../git-view-controls'
23
- import { useBg, useTokens, useTransparent } from '../../../theme'
23
+ import { useTheme, useTransparent } from '../../../theme'
24
24
  import {
25
25
  type DiffSegment,
26
26
  estimatedSegmentHeight,
@@ -67,7 +67,8 @@ export const SplitView = forwardRef<SplitViewHandle, Props>(function SplitView(
67
67
  { contentWidth, file, foldDispatch, highlights, requestSegmentHighlights, segments },
68
68
  ref
69
69
  ) {
70
- const separatorBg = useBg('base')
70
+ const t = useTheme()
71
+ const separatorBg = t.background
71
72
  const leftRef = useRef<ScrollBoxRenderable | null>(null)
72
73
  const rightRef = useRef<ScrollBoxRenderable | null>(null)
73
74
  const measuredHeightsRef = useRef<Record<string, number>>({})
@@ -224,12 +225,12 @@ function SideRow({
224
225
  }
225
226
 
226
227
  function HunkHeaderRow({ row }: { row: Extract<SplitRowOrHeader, { type: 'hunk-header' }> }) {
227
- const t = useTokens()
228
- const headerBg = useBg('elevated')
228
+ const t = useTheme()
229
+ const headerBg = t.diffContextBg
229
230
  return (
230
231
  <box flexDirection="row" backgroundColor={headerBg} paddingLeft={1} paddingRight={1}>
231
- <text fg={t.muted}>{row.spec}</text>
232
- {row.context ? <text fg={t.hover}> {row.context}</text> : null}
232
+ <text fg={t.text}>{row.spec}</text>
233
+ {row.context ? <text fg={t.textMuted}> {row.context}</text> : null}
233
234
  </box>
234
235
  )
235
236
  }
@@ -245,29 +246,29 @@ function HalfRow({
245
246
  height: number
246
247
  tokens: ThemedToken[][]
247
248
  }) {
248
- const t = useTokens()
249
- const headerBg = useBg('elevated')
249
+ const t = useTheme()
250
+ const headerBg = t.diffContextBg
250
251
  const transparent = useTransparent()
251
252
  if (cell.type === 'filler') {
252
253
  return <box backgroundColor={headerBg} height={height} />
253
254
  }
254
255
  let bg: string | undefined
255
256
  let sign = ' '
256
- let signColor = t.muted
257
+ let signColor = t.textMuted
257
258
  if (cell.type === 'addition') {
258
- bg = t.diffAddBg
259
+ bg = t.diffAddedBg
259
260
  sign = '+'
260
- signColor = t.palette.success
261
+ signColor = t.diffAdded
261
262
  } else if (cell.type === 'deletion') {
262
- bg = t.diffDeleteBg
263
+ bg = t.diffRemovedBg
263
264
  sign = '-'
264
- signColor = t.palette.error
265
+ signColor = t.diffRemoved
265
266
  }
266
267
  const num = String(cell.lineNumber).padStart(gw, ' ')
267
268
  const lineTokens = tokens[cell.lineIdx]
268
269
  return (
269
270
  <box flexDirection="row" backgroundColor={transparent ? undefined : bg} height={height}>
270
- <text fg={t.muted}>{` ${num} `}</text>
271
+ <text fg={t.textMuted}>{` ${num} `}</text>
271
272
  <text fg={signColor}>{`${sign} `}</text>
272
273
  <LineContent content={cell.content} tokens={lineTokens} />
273
274
  </box>
@@ -275,9 +276,9 @@ function HalfRow({
275
276
  }
276
277
 
277
278
  function LineContent({ content, tokens }: { content: string; tokens: ThemedToken[] | undefined }) {
278
- const t = useTokens()
279
+ const t = useTheme()
279
280
  if (!tokens || tokens.length === 0) {
280
- return <text fg={t.palette.ink}>{content}</text>
281
+ return <text fg={t.text}>{content}</text>
281
282
  }
282
283
  return (
283
284
  <text>
@@ -288,7 +289,7 @@ function LineContent({ content, tokens }: { content: string; tokens: ThemedToken
288
289
  if (s.italic) attributes |= TextAttributes.ITALIC
289
290
  if (s.underline) attributes |= TextAttributes.UNDERLINE
290
291
  return (
291
- <span key={i} fg={s.fg ?? t.palette.ink} attributes={attributes}>
292
+ <span key={i} fg={s.fg ?? t.text} attributes={attributes}>
292
293
  {s.text}
293
294
  </span>
294
295
  )
@@ -20,7 +20,7 @@ import type { DiffHighlights, FoldDispatch } from './pierre-diff'
20
20
 
21
21
  import { getScrollViewportDelta } from '../../../../app-runtime/terminal-mouse-adapter'
22
22
  import { scrollGitDiff } from '../../../git-view-controls'
23
- import { useBg, useTokens, useTransparent } from '../../../theme'
23
+ import { useTheme, useTransparent } from '../../../theme'
24
24
  import {
25
25
  type DiffSegment,
26
26
  estimatedSegmentHeight,
@@ -179,14 +179,14 @@ function UnifiedRowRender({
179
179
  highlights: DiffHighlights
180
180
  row: UnifiedRowOrHeader
181
181
  }) {
182
- const t = useTokens()
183
- const headerBg = useBg('elevated')
182
+ const t = useTheme()
183
+ const headerBg = t.diffContextBg
184
184
  const transparent = useTransparent()
185
185
  if (row.type === 'hunk-header') {
186
186
  return (
187
187
  <box flexDirection="row" backgroundColor={headerBg} paddingLeft={1} paddingRight={1}>
188
- <text fg={t.muted}>{row.spec}</text>
189
- {row.context ? <text fg={t.hover}> {row.context}</text> : null}
188
+ <text fg={t.text}>{row.spec}</text>
189
+ {row.context ? <text fg={t.textMuted}> {row.context}</text> : null}
190
190
  </box>
191
191
  )
192
192
  }
@@ -199,21 +199,21 @@ function UnifiedRowRender({
199
199
  const tokens = highlights.add[row.lineIdx]
200
200
  return (
201
201
  <box flexDirection="row" height={row.height}>
202
- <text fg={t.muted}>{` ${pad(row.delLineNumber)} ${pad(row.addLineNumber)} `}</text>
203
- <text fg={t.palette.ink}> </text>
202
+ <text fg={t.textMuted}>{` ${pad(row.delLineNumber)} ${pad(row.addLineNumber)} `}</text>
203
+ <text fg={t.text}> </text>
204
204
  <LineContent content={row.content} tokens={tokens} />
205
205
  </box>
206
206
  )
207
207
  }
208
- const bg = row.type === 'addition' ? t.diffAddBg : t.diffDeleteBg
208
+ const bg = row.type === 'addition' ? t.diffAddedBg : t.diffRemovedBg
209
209
  const sign = row.type === 'addition' ? '+' : '-'
210
- const signColor = row.type === 'addition' ? t.palette.success : t.palette.error
210
+ const signColor = row.type === 'addition' ? t.diffAdded : t.diffRemoved
211
211
  const delNum = row.type === 'deletion' ? row.lineNumber : undefined
212
212
  const addNum = row.type === 'addition' ? row.lineNumber : undefined
213
213
  const tokens = row.type === 'addition' ? highlights.add[row.lineIdx] : highlights.del[row.lineIdx]
214
214
  return (
215
215
  <box flexDirection="row" backgroundColor={transparent ? undefined : bg} height={row.height}>
216
- <text fg={t.muted}>{` ${pad(delNum)} ${pad(addNum)} `}</text>
216
+ <text fg={t.textMuted}>{` ${pad(delNum)} ${pad(addNum)} `}</text>
217
217
  <text fg={signColor}>{`${sign} `}</text>
218
218
  <LineContent content={row.content} tokens={tokens} />
219
219
  </box>
@@ -221,9 +221,9 @@ function UnifiedRowRender({
221
221
  }
222
222
 
223
223
  function LineContent({ content, tokens }: { content: string; tokens: ThemedToken[] | undefined }) {
224
- const t = useTokens()
224
+ const t = useTheme()
225
225
  if (!tokens || tokens.length === 0) {
226
- return <text fg={t.palette.ink}>{content}</text>
226
+ return <text fg={t.text}>{content}</text>
227
227
  }
228
228
  return (
229
229
  <text>
@@ -234,7 +234,7 @@ function LineContent({ content, tokens }: { content: string; tokens: ThemedToken
234
234
  if (s.italic) attributes |= TextAttributes.ITALIC
235
235
  if (s.underline) attributes |= TextAttributes.UNDERLINE
236
236
  return (
237
- <span key={i} fg={s.fg ?? t.palette.ink} attributes={attributes}>
237
+ <span key={i} fg={s.fg ?? t.text} attributes={attributes}>
238
238
  {s.text}
239
239
  </span>
240
240
  )
@@ -18,7 +18,7 @@ import {
18
18
  type GitTreeFileRow,
19
19
  type GitTreeFolderRow,
20
20
  } from '../../../state/git-tree'
21
- import { getCurrentTokens, getTransparent, useTokens, useTransparent } from '../../theme'
21
+ import { getCurrentTheme, getTransparent, useTheme, useTransparent } from '../../theme'
22
22
 
23
23
  interface GitPanelProps {
24
24
  collapsedFolders?: Record<string, true>
@@ -49,19 +49,20 @@ const BASE_SECTION_ORDER: GitFileSection[] = ['staged', 'unstaged', 'untracked']
49
49
  const HISTORICAL_SECTION_ORDER: GitFileSection[] = ['historical', 'untracked']
50
50
 
51
51
  function statusColor(status: GitFileEntry['status']): string {
52
- const t = getCurrentTokens()
52
+ const t = getCurrentTheme()
53
53
  switch (status) {
54
54
  case '?':
55
+ return t.text
55
56
  case 'A':
56
- return t.palette.success
57
+ return t.diffAdded
57
58
  case 'C':
58
59
  case 'R':
59
- return t.palette.primary
60
+ return t.text
60
61
  case 'D':
61
62
  case 'U':
62
- return t.palette.error
63
+ return t.diffRemoved
63
64
  case 'M':
64
- return t.palette.warning
65
+ return t.warning
65
66
  }
66
67
  }
67
68
 
@@ -99,7 +100,7 @@ function renderFileLabel(
99
100
  pathConfig: GitPanePathConfig,
100
101
  fileListMode: GitFileListMode
101
102
  ): ReactNode {
102
- const t = getCurrentTokens()
103
+ const t = getCurrentTheme()
103
104
  const transform = pathConfig.enabled ? pathConfig.pathFn : undefined
104
105
  const displayPath = transform ? transform(file.path) : file.path
105
106
  const { basename, prefix } = splitPath(displayPath)
@@ -108,8 +109,8 @@ function renderFileLabel(
108
109
  if (!file.renamedFrom) {
109
110
  return (
110
111
  <text wrapMode="none">
111
- <span fg={t.palette.ink}>{basename}</span>
112
- {showDir ? <span fg={t.muted}> {dir}</span> : null}
112
+ <span fg={t.text}>{basename}</span>
113
+ {showDir ? <span fg={t.textMuted}> {dir}</span> : null}
113
114
  </text>
114
115
  )
115
116
  }
@@ -118,11 +119,11 @@ function renderFileLabel(
118
119
  const renamedDir = stripTrailingSlash(renamed.prefix)
119
120
  return (
120
121
  <text wrapMode="none">
121
- <span fg={t.muted}>{renamed.basename}</span>
122
- {showDir && renamedDir ? <span fg={t.muted}> {renamedDir}</span> : null}
123
- <span fg={t.muted}> → </span>
124
- <span fg={t.palette.ink}>{basename}</span>
125
- {showDir ? <span fg={t.muted}> {dir}</span> : null}
122
+ <span fg={t.textMuted}>{renamed.basename}</span>
123
+ {showDir && renamedDir ? <span fg={t.textMuted}> {renamedDir}</span> : null}
124
+ <span fg={t.textMuted}> → </span>
125
+ <span fg={t.text}>{basename}</span>
126
+ {showDir ? <span fg={t.textMuted}> {dir}</span> : null}
126
127
  </text>
127
128
  )
128
129
  }
@@ -136,28 +137,31 @@ function renderDiffCount(
136
137
  hasNumstat: boolean
137
138
  ): ReactNode {
138
139
  if (!diffCountConfig.enabled) return null
139
- const t = getCurrentTokens()
140
+ const t = getCurrentTheme()
140
141
  if (!hasNumstat) {
141
142
  return (
142
- <text fg={t.muted} bg={bg} flexShrink={0}>
143
+ <text fg={t.textMuted} bg={bg} flexShrink={0}>
143
144
 
144
145
  </text>
145
146
  )
146
147
  }
147
148
  return (
148
149
  <box flexDirection="row" flexShrink={0}>
149
- <text fg={t.palette.success} bg={bg}>{`+${padRight(file.added, addedW)}`}</text>
150
- <text fg={t.hover} bg={bg}>
150
+ <text fg={getCurrentTheme().diffAdded} bg={bg}>{`+${padRight(file.added, addedW)}`}</text>
151
+ <text fg={t.textMuted} bg={bg}>
151
152
  {' '}
152
153
  </text>
153
- <text fg={t.palette.error} bg={bg}>{`−${padRight(file.removed, removedW)}`}</text>
154
+ <text
155
+ fg={getCurrentTheme().diffRemoved}
156
+ bg={bg}
157
+ >{`−${padRight(file.removed, removedW)}`}</text>
154
158
  </box>
155
159
  )
156
160
  }
157
161
 
158
162
  function renderFolderRow(row: GitTreeFolderRow, isSelected: boolean): ReactNode {
159
- const t = getCurrentTokens()
160
- const bg = isSelected && !getTransparent() ? t.selected : undefined
163
+ const t = getCurrentTheme()
164
+ const bg = isSelected && !getTransparent() ? t.backgroundElement : undefined
161
165
  const onSelect = (): void => {
162
166
  dispatchGlobal({ key: row.key, type: 'git-mode-select-entry-by-key' })
163
167
  }
@@ -168,13 +172,13 @@ function renderFolderRow(row: GitTreeFolderRow, isSelected: boolean): ReactNode
168
172
  <box key={row.key} flexDirection="row" gap={1} backgroundColor={bg} onMouseDown={onSelect}>
169
173
  <box flexGrow={1} overflow="hidden" paddingLeft={row.depth * 2}>
170
174
  <box flexDirection="row" gap={1} onMouseDown={onToggle}>
171
- <text fg={t.muted} bg={bg}>
175
+ <text fg={t.textMuted} bg={bg}>
172
176
  {row.isCollapsed ? '▸' : '▾'}
173
177
  </text>
174
- <text fg={t.palette.primary} bg={bg}>
178
+ <text fg={getCurrentTheme().textMuted} bg={bg}>
175
179
  {row.isCollapsed ? '\uf07b' : '\uf07c'}
176
180
  </text>
177
- <text fg={t.palette.primary} bg={bg} wrapMode="none">
181
+ <text fg={t.textMuted} bg={bg} wrapMode="none">
178
182
  {row.name}
179
183
  </text>
180
184
  </box>
@@ -193,10 +197,9 @@ function renderFileRow(
193
197
  diffCountConfig: GitPaneDiffCountConfig,
194
198
  repoPrefixes: Record<string, string>
195
199
  ): ReactNode {
196
- const t = getCurrentTokens()
197
200
  const file = row.file
198
201
  const hasNumstat = file.added !== null || file.removed !== null
199
- const bg = isSelected && !getTransparent() ? t.selected : undefined
202
+ const bg = isSelected && !getTransparent() ? getCurrentTheme().backgroundElement : undefined
200
203
  const onSelect = (): void => {
201
204
  dispatchGlobal({ key: row.key, type: 'git-mode-select-entry-by-key' })
202
205
  }
@@ -213,7 +216,7 @@ function renderFileRow(
213
216
  </box>
214
217
  {repoTag ? (
215
218
  <box flexShrink={0}>
216
- <text fg={t.palette.primary} bg={bg}>
219
+ <text fg={getCurrentTheme().primary} bg={bg}>
217
220
  <strong>{repoTag}</strong>
218
221
  </text>
219
222
  </box>
@@ -242,7 +245,7 @@ function renderTreeSection(
242
245
  repoPrefixes: Record<string, string>
243
246
  ): ReactNode {
244
247
  if (files.length === 0) return null
245
- const t = getCurrentTokens()
248
+ const t2 = getCurrentTheme()
246
249
  const nextFileListMode = fileListMode === 'tree' ? 'flat' : 'tree'
247
250
  const toggleListMode = (): void => {
248
251
  dispatchGlobal({ type: 'git-mode-toggle-file-list-mode' })
@@ -251,16 +254,16 @@ function renderTreeSection(
251
254
  return (
252
255
  <box key={section} flexDirection="column" marginTop={marginTop}>
253
256
  <box flexDirection="row" justifyContent="space-between">
254
- <text fg={t.accent}>
257
+ <text fg={t2.text}>
255
258
  <strong>
256
259
  {title} ({files.length})
257
260
  </strong>
258
261
  </text>
259
262
  {showListModeToggle ? (
260
263
  <box flexDirection="row" gap={1} onMouseDown={toggleListMode}>
261
- <text fg={fileListMode === 'tree' ? t.palette.primary : t.muted}>tree</text>
262
- <text fg={t.muted}>|</text>
263
- <text fg={fileListMode === 'flat' ? t.palette.primary : t.muted}>flat</text>
264
+ <text fg={fileListMode === 'tree' ? t2.primary : t2.textMuted}>tree</text>
265
+ <text fg={t2.textMuted}>|</text>
266
+ <text fg={fileListMode === 'flat' ? t2.primary : t2.textMuted}>flat</text>
264
267
  </box>
265
268
  ) : null}
266
269
  </box>
@@ -301,18 +304,18 @@ function computeStatusPlaceholder(
301
304
  gitPanel: GitPanelState,
302
305
  hasProjectPath: boolean
303
306
  ): StatusPlaceholder | null {
304
- const t = getCurrentTokens()
307
+ const t = getCurrentTheme()
305
308
  if (!hasProjectPath) {
306
- return { label: 'No active session', labelColor: t.muted }
309
+ return { label: 'No active session', labelColor: t.textMuted }
307
310
  }
308
311
  if (gitPanel.error === 'not-a-repo') {
309
- return { label: 'Not a git repository', labelColor: t.muted }
312
+ return { label: 'Not a git repository', labelColor: t.textMuted }
310
313
  }
311
314
  if (gitPanel.error === 'unknown') {
312
- return { label: 'Git error', labelColor: t.palette.error }
315
+ return { label: 'Git error', labelColor: t.error }
313
316
  }
314
317
  if (gitPanel.files.length === 0) {
315
- return { label: 'Working tree clean', labelColor: t.muted }
318
+ return { label: 'Working tree clean', labelColor: t.textMuted }
316
319
  }
317
320
  return null
318
321
  }
@@ -331,7 +334,7 @@ export const GitPanel = memo(function GitPanel({
331
334
  projectPath,
332
335
  selectedEntryKey,
333
336
  }: GitPanelProps) {
334
- const t = useTokens()
337
+ const t = useTheme()
335
338
  useTransparent()
336
339
  const repoPrefixes = useAppStore((s) => s.multiRepo.prefixes)
337
340
  const sectionOrder = headOffset > 0 ? HISTORICAL_SECTION_ORDER : BASE_SECTION_ORDER
@@ -367,7 +370,7 @@ export const GitPanel = memo(function GitPanel({
367
370
  return (
368
371
  <box flexDirection="column" flexGrow={1} flexShrink={1} flexBasis={0} overflow="hidden" gap={0}>
369
372
  {hasRemoteTracking ? (
370
- <text fg={t.muted}>
373
+ <text fg={t.textMuted}>
371
374
  ↑{gitPanel.ahead} ↓{gitPanel.behind}
372
375
  </text>
373
376
  ) : null}
@@ -12,7 +12,7 @@ import { useAppStore } from '../../../state/app-store'
12
12
  import { dispatchGlobal } from '../../../state/dispatch-ref'
13
13
  import { getSelectedGitFile, gitFileKey } from '../../../state/git-tree'
14
14
  import { setGitDiffScroller } from '../../git-view-controls'
15
- import { useBg, useTokens } from '../../theme'
15
+ import { useTheme } from '../../theme'
16
16
  import { PierreDiff, type PierreDiffHandle } from './diff-renderer'
17
17
  import { useDiffPrefetch } from './diff-renderer/use-diff-prefetch'
18
18
  import { GitPanel } from './git-panel'
@@ -48,25 +48,25 @@ const DiffStage = memo(function DiffStage({
48
48
  themeId,
49
49
  view,
50
50
  }: DiffStageProps) {
51
- const t = useTokens()
51
+ const t = useTheme()
52
52
  if (loading && !diff) {
53
53
  return (
54
54
  <box flexGrow={1} padding={1}>
55
- <text fg={t.muted}>Loading diff…</text>
55
+ <text fg={t.textMuted}>Loading diff…</text>
56
56
  </box>
57
57
  )
58
58
  }
59
59
  if (!diff) {
60
60
  return (
61
61
  <box flexGrow={1} padding={1}>
62
- <text fg={t.muted}>Select a file.</text>
62
+ <text fg={t.textMuted}>Select a file.</text>
63
63
  </box>
64
64
  )
65
65
  }
66
66
  if (diff.errorMessage) {
67
67
  return (
68
68
  <box flexGrow={1} padding={1}>
69
- <text fg={t.palette.error}>{diff.errorMessage}</text>
69
+ <text fg={t.error}>{diff.errorMessage}</text>
70
70
  </box>
71
71
  )
72
72
  }
@@ -75,7 +75,7 @@ const DiffStage = memo(function DiffStage({
75
75
  if (placeholder) {
76
76
  return (
77
77
  <box flexGrow={1} padding={1}>
78
- <text fg={t.muted}>{placeholder}</text>
78
+ <text fg={t.textMuted}>{placeholder}</text>
79
79
  </box>
80
80
  )
81
81
  }
@@ -84,7 +84,7 @@ const DiffStage = memo(function DiffStage({
84
84
  <box flexDirection="column" flexGrow={1} overflow="hidden">
85
85
  {diff.oldPath ? (
86
86
  <box paddingLeft={1} paddingRight={1}>
87
- <text fg={t.muted}>
87
+ <text fg={t.textMuted}>
88
88
  renamed: {diff.oldPath} → {diff.path}
89
89
  </text>
90
90
  </box>
@@ -106,9 +106,9 @@ interface GitViewProps {
106
106
  }
107
107
 
108
108
  export const GitView = memo(function GitView({ themeId }: GitViewProps) {
109
- const t = useTokens()
110
- const sidebarBg = useBg('elevated')
111
- const actionBg = useBg('selected')
109
+ const t = useTheme()
110
+ const sidebarBg = t.background
111
+ const actionBg = t.backgroundElement
112
112
  const dimensions = useTerminalDimensions()
113
113
  const gitPane = useAppStore((s) => s.gitPane)
114
114
  const gitPanel = useAppStore((s) => s.gitPanel)
@@ -196,13 +196,13 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
196
196
  let footerNode: React.ReactNode = null
197
197
  if (pendingHint) {
198
198
  footerNode = (
199
- <text fg={t.palette.warning}>
199
+ <text fg={t.warning}>
200
200
  <strong>{pendingHint}</strong>
201
201
  </text>
202
202
  )
203
203
  } else if (actionMessage) {
204
204
  footerNode = actionMessage.split('\n').map((line, idx) => (
205
- <text key={idx} fg={t.palette.primary}>
205
+ <text key={idx} fg={t.primary}>
206
206
  {line}
207
207
  </text>
208
208
  ))
@@ -220,24 +220,24 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
220
220
  overflow="hidden"
221
221
  >
222
222
  <box flexDirection="column" flexShrink={0}>
223
- <text fg={t.palette.primary}>
223
+ <text fg={t.text}>
224
224
  <strong>aimux · git</strong>
225
225
  </text>
226
226
  {gitPanel.branch ? (
227
227
  <box flexDirection="row">
228
- <text fg={t.palette.primary}>{'\u{e702}'} </text>
229
- <text fg={t.muted}>{gitPanel.branch}</text>
228
+ <text fg={t.text}>{'\u{e702}'} </text>
229
+ <text fg={t.text}>{gitPanel.branch}</text>
230
230
  </box>
231
231
  ) : null}
232
232
  {gitMode.headOffset > 0 ? (
233
233
  <box flexDirection="row" gap={1}>
234
- <text fg={t.palette.warning}>
234
+ <text fg={t.warning}>
235
235
  <strong>HEAD~{gitMode.headOffset}</strong>
236
236
  </text>
237
- <text fg={t.muted}>[ newer · ] older</text>
237
+ <text fg={t.textMuted}>[ newer · ] older</text>
238
238
  </box>
239
239
  ) : null}
240
- <text fg={t.hover}>{'·'.repeat(Math.max(0, fileBarWidth - 2))}</text>
240
+ <text fg={t.textMuted}>{'·'.repeat(Math.max(0, fileBarWidth - 2))}</text>
241
241
  </box>
242
242
  <GitPanel
243
243
  collapsedFolders={gitMode.collapsedFolders}
@@ -258,7 +258,7 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
258
258
  backgroundColor={sidebarBg}
259
259
  >
260
260
  <box flexGrow={1}>
261
- <text fg={selectedFile ? t.palette.ink : t.muted}>
261
+ <text fg={selectedFile ? t.text : t.textMuted}>
262
262
  {selectedFile ? selectedFile.path : 'Diff'}
263
263
  </text>
264
264
  </box>
@@ -272,7 +272,7 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
272
272
  dispatchGlobal({ type: 'exit-git-mode' })
273
273
  }}
274
274
  >
275
- <text fg={t.palette.ink}>
275
+ <text fg={t.text}>
276
276
  <strong>Exit diff</strong>
277
277
  </text>
278
278
  </box>