@brimveyn/aimux 1.9.3 → 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 (46) hide show
  1. package/package.json +2 -2
  2. package/src/app-runtime/side-effects.ts +29 -0
  3. package/src/app.tsx +4 -5
  4. package/src/input/modes/types.ts +2 -0
  5. package/src/pty/terminal-snapshot.ts +7 -7
  6. package/src/state/reducers/git-mode-state.ts +33 -1
  7. package/src/state/types.ts +1 -0
  8. package/src/ui/components/git/diff-renderer/build-rows.ts +21 -0
  9. package/src/ui/components/git/diff-renderer/fold-strip.tsx +7 -7
  10. package/src/ui/components/git/diff-renderer/pierre-diff.tsx +3 -3
  11. package/src/ui/components/git/diff-renderer/split-view.tsx +18 -17
  12. package/src/ui/components/git/diff-renderer/stacked-view.tsx +13 -13
  13. package/src/ui/components/git/git-panel.tsx +42 -39
  14. package/src/ui/components/git/git-view.tsx +20 -20
  15. package/src/ui/components/layout/session-bar.tsx +13 -13
  16. package/src/ui/components/layout/sidebar/sidebar.tsx +26 -28
  17. package/src/ui/components/layout/sidebar/tab-item.tsx +21 -21
  18. package/src/ui/components/layout/split-layout.tsx +2 -2
  19. package/src/ui/components/layout/status-bar.tsx +12 -12
  20. package/src/ui/components/layout/terminal-pane.tsx +19 -31
  21. package/src/ui/components/modals/app/ai-usage-modal.tsx +21 -21
  22. package/src/ui/components/modals/app/help-modal.tsx +5 -6
  23. package/src/ui/components/modals/app/update-available-modal.tsx +3 -3
  24. package/src/ui/components/modals/git/git-commit-modal.tsx +17 -17
  25. package/src/ui/components/modals/sessions/create-session-modal.tsx +8 -8
  26. package/src/ui/components/modals/sessions/session-picker-modal.tsx +6 -6
  27. package/src/ui/components/modals/shared/form.tsx +4 -4
  28. package/src/ui/components/modals/shared/modal-keybinds-overlay.tsx +4 -4
  29. package/src/ui/components/modals/shared/modal-shell.tsx +6 -6
  30. package/src/ui/components/modals/shared/picker.tsx +6 -6
  31. package/src/ui/components/modals/snippets/snippet-picker-modal.tsx +5 -5
  32. package/src/ui/components/modals/tabs/new-tab-modal.tsx +6 -6
  33. package/src/ui/components/modals/themes/theme-picker-modal.tsx +17 -20
  34. package/src/ui/components/overlays/ai-usage/ai-usage-indicator.tsx +12 -12
  35. package/src/ui/components/overlays/context-menu/context-menu-overlay.tsx +6 -6
  36. package/src/ui/components/overlays/pending-chord-overlay.tsx +5 -5
  37. package/src/ui/components/primitives/bare-input.tsx +5 -5
  38. package/src/ui/components/primitives/input-field.tsx +5 -5
  39. package/src/ui/components/primitives/list-item.tsx +27 -22
  40. package/src/ui/components/primitives/surface.tsx +12 -9
  41. package/src/ui/filter-themes.ts +9 -8
  42. package/src/ui/root.tsx +5 -4
  43. package/src/ui/shiki.ts +8 -11
  44. package/src/ui/theme-store.ts +48 -118
  45. package/src/ui/theme.ts +8 -8
  46. package/src/ui/themes.ts +9 -20
@@ -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>
@@ -10,7 +10,7 @@ import { dispatchGlobal, runSideEffectGlobal } from '../../../state/dispatch-ref
10
10
  import { IDLE_SESSION_STATUS } from '../../../state/types'
11
11
  import { useBusySpinner } from '../../hooks/use-busy-spinner'
12
12
  import { moveIdToIdPosition, orderSessionsForDisplay } from '../../session-ordering'
13
- import { useBg, useTokens } from '../../theme'
13
+ import { useTheme } from '../../theme'
14
14
  import { ContextMenuBox } from '../overlays/context-menu/context-menu-box'
15
15
 
16
16
  interface SessionBarProps {
@@ -18,8 +18,8 @@ interface SessionBarProps {
18
18
  }
19
19
 
20
20
  export function SessionBar({ forceVisible = false }: SessionBarProps) {
21
- const t = useTokens()
22
- const headerBg = useBg('elevated')
21
+ const t = useTheme()
22
+ const headerBg = t.backgroundPanel
23
23
  const sessions = useAppStore((s) => s.sessions)
24
24
  const currentId = useAppStore((s) => s.currentSessionId)
25
25
  const bar = useAppStore((s) => s.sessionBar)
@@ -151,13 +151,13 @@ export function SessionBar({ forceVisible = false }: SessionBarProps) {
151
151
  flexDirection="row"
152
152
  paddingLeft={1}
153
153
  paddingRight={1}
154
- backgroundColor={t.selected}
154
+ backgroundColor={t.backgroundElement}
155
155
  onMouseDown={(e) => {
156
156
  e.stopPropagation()
157
157
  dispatchGlobal({ returnToSessionPicker: false, type: 'open-create-session-modal' })
158
158
  }}
159
159
  >
160
- <text fg={t.palette.ink} selectable={false}>
160
+ <text fg={t.text} selectable={false}>
161
161
  + New
162
162
  </text>
163
163
  </box>
@@ -200,16 +200,16 @@ function SessionChip({
200
200
  session,
201
201
  status,
202
202
  }: SessionChipProps) {
203
- const t = useTokens()
204
- const selectionBg = useBg('selected')
203
+ const t = useTheme()
204
+ const selectionBg = t.backgroundElement
205
205
  const showSpinner = status.working
206
206
  const showWaiting = status.waiting
207
207
  const spinner = useBusySpinner(showSpinner)
208
- const labelColor = active ? t.palette.ink : t.muted
208
+ const labelColor = active ? t.text : t.textMuted
209
209
  const bgColor = dragging || active ? selectionBg : undefined
210
- const idleColor = t.palette.success
211
- const workingColor = t.palette.primary
212
- const waitingColor = t.palette.warning
210
+ const idleColor = t.success
211
+ const workingColor = t.primary
212
+ const waitingColor = t.warning
213
213
  const [hovered, setHovered] = useState(false)
214
214
 
215
215
  return (
@@ -264,12 +264,12 @@ function SessionChip({
264
264
  runSideEffectGlobal({ sessionId: session.id, type: 'delete-session' })
265
265
  }}
266
266
  >
267
- <text fg={t.muted} selectable={false}>
267
+ <text fg={t.textMuted} selectable={false}>
268
268
  ×
269
269
  </text>
270
270
  </box>
271
271
  ) : (
272
- <text fg={t.hover} selectable={false}>
272
+ <text fg={t.textMuted} selectable={false}>
273
273
  {' '}
274
274
  </text>
275
275
  )}
@@ -7,7 +7,7 @@ import { memo, useMemo, useRef } from 'react'
7
7
 
8
8
  import { useAppStore } from '../../../../state/app-store'
9
9
  import { dispatchGlobal } from '../../../../state/dispatch-ref'
10
- import { getCurrentTokens, type ThemeTokens, useBg, useTokens } from '../../../theme'
10
+ import { getCurrentTheme, type ResolvedTuiTheme, useTheme } from '../../../theme'
11
11
  import { buildGitPaneContextMenu } from '../../git/pane/git-pane-context-menu'
12
12
  import { GitPaneWidget } from '../../git/pane/git-pane-widget'
13
13
  import { ContextMenuBox } from '../../overlays/context-menu/context-menu-box'
@@ -37,19 +37,19 @@ const RESIZE_HANDLE = '│'
37
37
  function getRowBackground({
38
38
  alternate,
39
39
  isActive,
40
- tokens,
40
+ t,
41
41
  }: {
42
42
  isActive: boolean
43
43
  alternate: boolean
44
- tokens: ThemeTokens
44
+ t: ResolvedTuiTheme
45
45
  }): string | undefined {
46
- if (isActive) return tokens.selected
47
- if (alternate) return tokens.hover
48
- return undefined
46
+ if (isActive) return t.backgroundElement
47
+ if (alternate) return t.backgroundPanel
48
+ return t.backgroundPanel
49
49
  }
50
50
 
51
51
  const SidebarTop = memo(function SidebarTop({ contentWidth }: { contentWidth: number }) {
52
- const tokens = useTokens()
52
+ const t = useTheme()
53
53
  const currentSessionId = useAppStore((s) => s.currentSessionId)
54
54
  const sessions = useAppStore((s) => s.sessions)
55
55
  const currentSession = currentSessionId
@@ -60,22 +60,20 @@ const SidebarTop = memo(function SidebarTop({ contentWidth }: { contentWidth: nu
60
60
 
61
61
  return (
62
62
  <box flexDirection="column" flexShrink={0} gap={0}>
63
- <text fg={tokens.palette.primary}>
63
+ <text fg={t.text}>
64
64
  <strong>aimux</strong>
65
65
  </text>
66
- <text fg={tokens.accent}>
67
- {currentSession ? currentSession.name : 'No workspace selected'}
68
- </text>
66
+ <text fg={t.text}>{currentSession ? currentSession.name : 'No workspace selected'}</text>
69
67
  {branch ? (
70
68
  <box flexDirection="row">
71
- <text fg={tokens.palette.primary}>{'\u{e702}'} </text>
72
- <text fg={tokens.muted}>{branch}</text>
69
+ <text fg={t.text}>{'\u{e702}'} </text>
70
+ <text fg={t.text}>{branch}</text>
73
71
  </box>
74
72
  ) : null}
75
73
  <box
76
74
  flexDirection="row"
77
75
  paddingY={1}
78
- backgroundColor={tokens.selected}
76
+ backgroundColor={t.backgroundPanel}
79
77
  justifyContent="center"
80
78
  marginTop={1}
81
79
  onMouseDown={(e) => {
@@ -83,22 +81,22 @@ const SidebarTop = memo(function SidebarTop({ contentWidth }: { contentWidth: nu
83
81
  dispatchGlobal({ type: 'open-new-tab-modal' })
84
82
  }}
85
83
  >
86
- <text fg={tokens.palette.ink}>+ New assistant</text>
84
+ <text fg={t.text}>+ New assistant</text>
87
85
  </box>
88
- <text fg={tokens.hover}>{'·'.repeat(Math.max(0, contentWidth - 2))}</text>
86
+ <text fg={t.textMuted}>{'·'.repeat(Math.max(0, contentWidth - 2))}</text>
89
87
  </box>
90
88
  )
91
89
  })
92
90
 
93
91
  function renderGroupGutter(isGroupStart: boolean, isGroupMiddle: boolean, isGroupEnd: boolean) {
94
- const t = getCurrentTokens()
92
+ const t = getCurrentTheme()
95
93
  return (
96
94
  <box flexDirection="column" width={1} overflow="hidden">
97
- <text fg={t.accent}>
95
+ <text fg={t.border}>
98
96
  {/* oxlint-disable-next-line no-nested-ternary */}
99
97
  {isGroupStart ? GUTTER_START : isGroupMiddle ? GUTTER_MIDDLE : GUTTER_PAD}
100
98
  </text>
101
- <text fg={t.accent}>{isGroupEnd ? GUTTER_END : GUTTER_PAD}</text>
99
+ <text fg={t.border}>{isGroupEnd ? GUTTER_END : GUTTER_PAD}</text>
102
100
  </box>
103
101
  )
104
102
  }
@@ -108,7 +106,7 @@ interface TabsBodyProps {
108
106
  }
109
107
 
110
108
  const TabsBody = memo(function TabsBody({ onTabActivate }: TabsBodyProps) {
111
- const tokens = useTokens()
109
+ const t = useTheme()
112
110
  const tabs = useAppStore((s) => s.tabs)
113
111
  const activeTabId = useAppStore((s) => s.activeTabId)
114
112
  const focusMode = useAppStore((s) => s.focusMode)
@@ -138,7 +136,7 @@ const TabsBody = memo(function TabsBody({ onTabActivate }: TabsBodyProps) {
138
136
  >
139
137
  {tabs.length === 0 ? (
140
138
  <box paddingTop={1}>
141
- <text fg={tokens.muted}>No tabs yet. Press Ctrl+n.</text>
139
+ <text fg={t.textMuted}>No tabs yet. Press Ctrl+n.</text>
142
140
  </box>
143
141
  ) : (
144
142
  tabs.map((tab, index) => {
@@ -154,7 +152,7 @@ const TabsBody = memo(function TabsBody({ onTabActivate }: TabsBodyProps) {
154
152
  return (
155
153
  <box
156
154
  key={tab.id}
157
- backgroundColor={getRowBackground({ alternate, isActive, tokens })}
155
+ backgroundColor={getRowBackground({ alternate, isActive, t })}
158
156
  flexDirection="row"
159
157
  onMouseDown={(event) => {
160
158
  event.stopPropagation()
@@ -186,8 +184,8 @@ export function Sidebar({
186
184
  onSidebarResizeStart,
187
185
  onTabActivate,
188
186
  }: SidebarProps) {
189
- const tokens = useTokens()
190
- const sidebarBg = useBg('elevated')
187
+ const t = useTheme()
188
+ const sidebarBg = t.background
191
189
  const sidebarVisible = useAppStore((s) => s.sidebar.visible)
192
190
  const sidebarWidth = useAppStore((s) => s.sidebar.width)
193
191
  const gitPane = useAppStore((s) => s.gitPane)
@@ -215,7 +213,7 @@ export function Sidebar({
215
213
  const tabsGrow = gitEmbedded ? Math.max(1, Math.round((1 - gitPane.embeddedRatio) * 100)) : 1
216
214
  const gitGrow = gitEmbedded ? Math.max(1, Math.round(gitPane.embeddedRatio * 100)) : 0
217
215
 
218
- const separator = <text fg={tokens.hover}>{'·'.repeat(Math.max(0, contentWidth - 2))}</text>
216
+ const separator = <text fg={t.textMuted}>{'·'.repeat(Math.max(0, contentWidth - 2))}</text>
219
217
  const gitBody = gitEmbedded ? (
220
218
  <ContextMenuBox
221
219
  flexDirection="column"
@@ -232,7 +230,7 @@ export function Sidebar({
232
230
  <box
233
231
  minHeight={1}
234
232
  flexShrink={0}
235
- backgroundColor={tokens.border}
233
+ backgroundColor={t.border}
236
234
  onMouseDown={(event) => {
237
235
  const body = bodyRef.current
238
236
  if (!body) return
@@ -245,7 +243,7 @@ export function Sidebar({
245
243
  })
246
244
  }}
247
245
  >
248
- <text fg={tokens.border}>{RESIZE_HANDLE.repeat(Math.max(1, contentWidth))}</text>
246
+ <text fg={t.border}>{RESIZE_HANDLE.repeat(Math.max(1, contentWidth))}</text>
249
247
  </box>
250
248
  ) : null
251
249
 
@@ -315,7 +313,7 @@ export function Sidebar({
315
313
  height="100%"
316
314
  width={1}
317
315
  flexShrink={0}
318
- backgroundColor={tokens.border}
316
+ backgroundColor={t.border}
319
317
  onMouseDown={(event) => {
320
318
  event.preventDefault()
321
319
  event.stopPropagation()