@brimveyn/aimux 1.9.5 → 1.9.7

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 +2 -2
  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
@@ -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()
@@ -4,7 +4,7 @@ import type { TabSession } from '../../../../state/types'
4
4
 
5
5
  import { dispatchGlobal, runSideEffectGlobal } from '../../../../state/dispatch-ref'
6
6
  import { useBusySpinner } from '../../../hooks/use-busy-spinner'
7
- import { getCurrentTokens, useTokens } from '../../../theme'
7
+ import { getCurrentTheme, useTheme } from '../../../theme'
8
8
  import { ContextMenuBox } from '../../overlays/context-menu/context-menu-box'
9
9
 
10
10
  interface TabItemProps {
@@ -16,16 +16,16 @@ interface TabItemProps {
16
16
  }
17
17
 
18
18
  function getStatusColor(status: TabSession['status']): string {
19
- const t = getCurrentTokens()
19
+ const t = getCurrentTheme()
20
20
  switch (status) {
21
21
  case 'running':
22
- return t.palette.success
22
+ return t.success
23
23
  case 'disconnected':
24
- return t.palette.warning
24
+ return t.warning
25
25
  case 'error':
26
- return t.palette.error
26
+ return t.error
27
27
  default:
28
- return t.muted
28
+ return t.textMuted
29
29
  }
30
30
  }
31
31
 
@@ -38,33 +38,33 @@ function getIndicator(active: boolean, focused: boolean, inLayout: boolean): str
38
38
  }
39
39
 
40
40
  function getIndicatorColor(active: boolean, focused: boolean, inLayout: boolean): string {
41
- const t = getCurrentTokens()
41
+ const t = getCurrentTheme()
42
42
  if (active) {
43
- return focused ? t.palette.primary : t.accent
43
+ return focused ? t.primary : t.text
44
44
  }
45
45
 
46
- return inLayout ? t.muted : t.hover
46
+ return inLayout ? t.textMuted : t.textMuted
47
47
  }
48
48
 
49
49
  function BusyIndicator() {
50
- const t = useTokens()
50
+ const t = useTheme()
51
51
  const frame = useBusySpinner()
52
- return <text fg={t.palette.primary}>{frame} working</text>
52
+ return <text fg={t.primary}>{frame} working</text>
53
53
  }
54
54
 
55
55
  function WaitingIndicator() {
56
- const t = useTokens()
57
- return <text fg={t.palette.warning}>? waiting</text>
56
+ const t = useTheme()
57
+ return <text fg={t.warning}>? waiting</text>
58
58
  }
59
59
 
60
60
  function ActivityIndicator({ tab }: { tab: TabSession }) {
61
- const t = useTokens()
61
+ const t = useTheme()
62
62
  if (tab.status === 'error') {
63
- return <text fg={t.palette.error}>✗ error</text>
63
+ return <text fg={t.error}>✗ error</text>
64
64
  }
65
65
 
66
66
  if (tab.status === 'disconnected') {
67
- return <text fg={t.palette.warning}>⏸ restore</text>
67
+ return <text fg={t.warning}>⏸ restore</text>
68
68
  }
69
69
 
70
70
  if (tab.activity === 'working') {
@@ -76,14 +76,14 @@ function ActivityIndicator({ tab }: { tab: TabSession }) {
76
76
  }
77
77
 
78
78
  if (tab.activity === 'idle') {
79
- return <text fg={t.palette.success}>● idle</text>
79
+ return <text fg={t.success}>● idle</text>
80
80
  }
81
81
 
82
82
  return <text fg={getStatusColor(tab.status)}>{tab.status}</text>
83
83
  }
84
84
 
85
85
  export function TabItem({ active, focused, id, inLayout, tab }: TabItemProps) {
86
- const t = useTokens()
86
+ const t = useTheme()
87
87
  const label = tab.command.split(' ')[0]
88
88
  const isInLayout = inLayout ?? false
89
89
  const indicator = getIndicator(active, focused, isInLayout)
@@ -135,7 +135,7 @@ export function TabItem({ active, focused, id, inLayout, tab }: TabItemProps) {
135
135
  <box flexDirection="row" alignItems="center">
136
136
  <text fg={indicatorColor}>{indicator} </text>
137
137
  <box flexGrow={1}>
138
- <text fg={active ? t.palette.ink : t.muted}>{tab.title}</text>
138
+ <text fg={active ? t.text : t.textMuted}>{tab.title}</text>
139
139
  </box>
140
140
  {hovered ? (
141
141
  <box
@@ -145,12 +145,12 @@ export function TabItem({ active, focused, id, inLayout, tab }: TabItemProps) {
145
145
  runSideEffectGlobal({ tabId: tab.id, type: 'close-tab' })
146
146
  }}
147
147
  >
148
- <text fg={t.muted}>×</text>
148
+ <text fg={t.textMuted}>×</text>
149
149
  </box>
150
150
  ) : null}
151
151
  </box>
152
152
  <box flexDirection="row">
153
- <text fg={t.muted}> {label} </text>
153
+ <text fg={t.textMuted}> {label} </text>
154
154
  <ActivityIndicator tab={tab} />
155
155
  </box>
156
156
  </ContextMenuBox>
@@ -11,7 +11,7 @@ import {
11
11
  type PaneRect,
12
12
  type SplitDirection,
13
13
  } from '../../../state/layout-tree'
14
- import { useTokens } from '../../theme'
14
+ import { useTheme } from '../../theme'
15
15
  import { TerminalPane } from './terminal-pane'
16
16
 
17
17
  const PANE_CHROME = PANE_BORDER
@@ -58,7 +58,7 @@ export function SplitLayout({
58
58
  onTerminalScrollEvent,
59
59
  tabs,
60
60
  }: SplitLayoutProps) {
61
- const t = useTokens()
61
+ const t = useTheme()
62
62
  if (node.type === 'leaf') {
63
63
  const tab = tabs.find((t) => t.id === node.tabId)
64
64
  const isActive = node.tabId === activeTabId
@@ -4,22 +4,22 @@ import { version as APP_VERSION } from '../../../../package.json'
4
4
  import { useAppStore } from '../../../state/app-store'
5
5
  import { useKeymap } from '../../keymap-context'
6
6
  import { getStatusBarModel } from '../../status-bar-model'
7
- import { getCurrentTokens, useBg, useTokens } from '../../theme'
7
+ import { getCurrentTheme, useTheme } from '../../theme'
8
8
  import { AIUsageIndicator } from '../overlays/ai-usage/ai-usage-indicator'
9
9
 
10
10
  function getModeColor(focusMode: AppState['focusMode']): string {
11
- const t = getCurrentTokens()
11
+ const t = getCurrentTheme()
12
12
  switch (focusMode) {
13
13
  case 'terminal-input':
14
- return t.palette.primary
14
+ return t.primary
15
15
  case 'modal':
16
16
  case 'command-edit':
17
- return t.palette.warning
17
+ return t.warning
18
18
  case 'git':
19
- return t.palette.success
19
+ return t.success
20
20
  case 'navigation':
21
21
  default:
22
- return t.accent
22
+ return t.text
23
23
  }
24
24
  }
25
25
 
@@ -40,8 +40,8 @@ function getModeLabel(focusMode: AppState['focusMode']): string {
40
40
  }
41
41
 
42
42
  export function StatusBar() {
43
- const t = useTokens()
44
- const headerBg = useBg('elevated')
43
+ const t = useTheme()
44
+ const headerBg = t.backgroundPanel
45
45
  const state = useAppStore((s) => s)
46
46
  const activeTab = state.tabs.find((tab) => tab.id === state.activeTabId)
47
47
  const config = useKeymap()
@@ -60,14 +60,14 @@ export function StatusBar() {
60
60
  <box width="100%" flexDirection="row">
61
61
  <text fg={getModeColor(state.focusMode)}>[{getModeLabel(state.focusMode)}]</text>
62
62
  <text> </text>
63
- <text fg={t.palette.ink}>{model.left}</text>
63
+ <text fg={t.text}>{model.left}</text>
64
64
  </box>
65
65
  <box width="100%" flexDirection="row" justifyContent="space-between">
66
- <text fg={t.muted}>{model.right}</text>
66
+ <text fg={t.textMuted}>{model.right}</text>
67
67
  <box flexDirection="row" gap={2}>
68
- {model.help ? <text fg={t.muted}>{model.help}</text> : null}
68
+ {model.help ? <text fg={t.textMuted}>{model.help}</text> : null}
69
69
  <AIUsageIndicator />
70
- <text fg={t.hover}>v{APP_VERSION}</text>
70
+ <text fg={t.textMuted}>v{APP_VERSION}</text>
71
71
  </box>
72
72
  </box>
73
73
  </box>
@@ -8,7 +8,7 @@ import type { TabSession, TerminalSnapshot, TerminalSpan } from '../../../state/
8
8
  import { logInputDebug } from '../../../debug/input-log'
9
9
  import { dispatchGlobal, runSideEffectGlobal } from '../../../state/dispatch-ref'
10
10
  import { type ContextMenuItem, openContextMenu } from '../../context-menu/controller'
11
- import { getCurrentTokens, useBg, useTokens } from '../../theme'
11
+ import { getCurrentTheme, useTheme } from '../../theme'
12
12
  import { ContextMenuBox } from '../overlays/context-menu/context-menu-box'
13
13
 
14
14
  interface TerminalPaneProps {
@@ -47,17 +47,9 @@ function getTitle(
47
47
  return `${tab.title} · ${tab.status}`
48
48
  }
49
49
 
50
- function getBorderColor(isActive: boolean, focusMode: TerminalPaneProps['focusMode']): string {
51
- const t = getCurrentTokens()
52
- if (isActive && focusMode === 'terminal-input') {
53
- return t.palette.primary
54
- }
55
-
56
- if (isActive) {
57
- return t.accent
58
- }
59
-
60
- return t.hover
50
+ function getBorderColor(isActive: boolean, _focusMode: TerminalPaneProps['focusMode']): string {
51
+ const t = getCurrentTheme()
52
+ return isActive ? t.borderActive : t.border
61
53
  }
62
54
 
63
55
  function renderSpan(span: TerminalSpan, key: string): ReactNode {
@@ -76,7 +68,7 @@ function renderSpan(span: TerminalSpan, key: string): ReactNode {
76
68
  }
77
69
 
78
70
  return (
79
- <span key={key} fg={span.fg ?? getCurrentTokens().palette.ink} bg={span.bg}>
71
+ <span key={key} fg={span.fg ?? getCurrentTheme().text} bg={span.bg}>
80
72
  {node}
81
73
  </span>
82
74
  )
@@ -91,11 +83,11 @@ const TerminalViewport = memo(function TerminalViewport({
91
83
  buffer,
92
84
  viewport,
93
85
  }: TerminalViewportProps) {
94
- const t = useTokens()
86
+ const t = useTheme()
95
87
  if (viewport && viewport.lines.length > 0) {
96
88
  const lines = viewport.lines
97
89
  return (
98
- <text fg={t.palette.ink}>
90
+ <text fg={t.text}>
99
91
  {lines.map((line, lineIndex) => (
100
92
  <span key={`line-${lineIndex}`}>
101
93
  {line.spans.map((span, spanIndex) => renderSpan(span, `s-${spanIndex}`))}
@@ -106,9 +98,7 @@ const TerminalViewport = memo(function TerminalViewport({
106
98
  )
107
99
  }
108
100
 
109
- return (
110
- <text fg={t.palette.ink}>{buffer.length > 0 ? buffer : 'Waiting for workspace output...'}</text>
111
- )
101
+ return <text fg={t.text}>{buffer.length > 0 ? buffer : 'Waiting for workspace output...'}</text>
112
102
  })
113
103
 
114
104
  export function TerminalPane({
@@ -126,8 +116,8 @@ export function TerminalPane({
126
116
  tab,
127
117
  tabId,
128
118
  }: TerminalPaneProps) {
129
- const t = useTokens()
130
- const editorBg = useBg('base')
119
+ const t = useTheme()
120
+ const editorBg = t.background
131
121
  const paneIsActive = isActive ?? true
132
122
  const canForwardMouse = focusMode === 'terminal-input' && !!tab && mouseForwardingEnabled
133
123
  const canUseLocalScrollback = focusMode === 'terminal-input' && !!tab && localScrollbackEnabled
@@ -232,25 +222,25 @@ export function TerminalPane({
232
222
  >
233
223
  {!tab ? (
234
224
  <box flexGrow={1} justifyContent="center" alignItems="center" flexDirection="column">
235
- <text fg={t.hover}>· · ·</text>
236
- <text fg={t.muted}> </text>
225
+ <text fg={t.textMuted}>· · ·</text>
226
+ <text fg={t.textMuted}> </text>
237
227
  <box flexDirection="row">
238
- <text fg={t.muted}>Press </text>
239
- <text fg={t.palette.primary}>Ctrl+n</text>
240
- <text fg={t.muted}> to launch an assistant</text>
228
+ <text fg={t.textMuted}>Press </text>
229
+ <text fg={t.primary}>Ctrl+n</text>
230
+ <text fg={t.textMuted}> to launch an assistant</text>
241
231
  </box>
242
232
  <box
243
233
  flexDirection="row"
244
234
  justifyContent="center"
245
235
  marginTop={1}
246
236
  paddingX={2}
247
- backgroundColor={t.selected}
237
+ backgroundColor={t.backgroundPanel}
248
238
  onMouseDown={(event) => {
249
239
  event.stopPropagation()
250
240
  dispatchGlobal({ type: 'open-new-tab-modal' })
251
241
  }}
252
242
  >
253
- <text fg={t.palette.ink}>New assistant</text>
243
+ <text fg={t.text}>New assistant</text>
254
244
  </box>
255
245
  </box>
256
246
  ) : (
@@ -271,11 +261,9 @@ export function TerminalPane({
271
261
  )}
272
262
  </ContextMenuBox>
273
263
  {tab?.status === 'disconnected' ? (
274
- <text fg={t.palette.warning}>
275
- Restored snapshot. Press Ctrl+r to restart this workspace.
276
- </text>
264
+ <text fg={t.warning}>Restored snapshot. Press Ctrl+r to restart this workspace.</text>
277
265
  ) : null}
278
- {tab?.errorMessage ? <text fg={t.palette.error}>{tab.errorMessage}</text> : null}
266
+ {tab?.errorMessage ? <text fg={t.error}>{tab.errorMessage}</text> : null}
279
267
  </box>
280
268
  )
281
269
  }