@brimveyn/aimux 1.5.4 → 1.6.0

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 (65) hide show
  1. package/README.md +14 -2
  2. package/package.json +5 -3
  3. package/src/app-runtime/side-effects.ts +41 -6
  4. package/src/app.tsx +17 -5
  5. package/src/config.ts +29 -4
  6. package/src/diff-parser/clean-last-newline.ts +5 -0
  7. package/src/diff-parser/constants.ts +13 -0
  8. package/src/diff-parser/index.ts +12 -0
  9. package/src/diff-parser/parse-line-type.ts +29 -0
  10. package/src/diff-parser/parse-patch-files.ts +369 -0
  11. package/src/diff-parser/types.ts +75 -0
  12. package/src/input/modes/bridge.ts +8 -0
  13. package/src/input/modes/transitions.ts +2 -1
  14. package/src/input/modes/types.ts +4 -1
  15. package/src/pty/terminal-snapshot.ts +4 -4
  16. package/src/state/git-tree.ts +220 -0
  17. package/src/state/reducers/git-mode-state.ts +232 -35
  18. package/src/state/reducers/git-panel-state.ts +29 -3
  19. package/src/state/reducers/modal-state.ts +43 -10
  20. package/src/state/store.ts +5 -1
  21. package/src/state/types.ts +41 -5
  22. package/src/state/workspace-save.ts +2 -0
  23. package/src/ui/components/create-session-modal.tsx +23 -7
  24. package/src/ui/components/diff-renderer/build-rows.ts +349 -0
  25. package/src/ui/components/diff-renderer/filetype.ts +29 -0
  26. package/src/ui/components/diff-renderer/fold-strip.tsx +84 -0
  27. package/src/ui/components/diff-renderer/highlight.ts +48 -0
  28. package/src/ui/components/diff-renderer/index.ts +1 -0
  29. package/src/ui/components/diff-renderer/pierre-diff.tsx +170 -0
  30. package/src/ui/components/diff-renderer/split-view.tsx +207 -0
  31. package/src/ui/components/diff-renderer/stacked-view.tsx +166 -0
  32. package/src/ui/components/git-commit-modal.tsx +14 -2
  33. package/src/ui/components/git-pane-widget.tsx +5 -0
  34. package/src/ui/components/git-panel.tsx +176 -79
  35. package/src/ui/components/git-view.tsx +89 -87
  36. package/src/ui/components/help-modal.tsx +43 -11
  37. package/src/ui/components/input-field.tsx +2 -2
  38. package/src/ui/components/list-item.tsx +9 -1
  39. package/src/ui/components/modal-filter-bar.tsx +1 -1
  40. package/src/ui/components/modal-keybinds-overlay.tsx +2 -2
  41. package/src/ui/components/modal-shell.tsx +3 -3
  42. package/src/ui/components/new-tab-modal.tsx +15 -3
  43. package/src/ui/components/pending-chord-overlay.tsx +3 -3
  44. package/src/ui/components/session-bar.tsx +10 -5
  45. package/src/ui/components/session-picker-modal.tsx +26 -9
  46. package/src/ui/components/sidebar.tsx +22 -10
  47. package/src/ui/components/snippet-editor-modal.tsx +16 -2
  48. package/src/ui/components/snippet-picker-modal.tsx +11 -3
  49. package/src/ui/components/split-layout.tsx +1 -1
  50. package/src/ui/components/status-bar.tsx +12 -9
  51. package/src/ui/components/surface.tsx +5 -5
  52. package/src/ui/components/tab-item.tsx +22 -16
  53. package/src/ui/components/terminal-pane.tsx +25 -15
  54. package/src/ui/components/theme-picker-modal.tsx +111 -16
  55. package/src/ui/components/update-available-modal.tsx +11 -1
  56. package/src/ui/filter-themes.ts +10 -0
  57. package/src/ui/house-themes.ts +313 -0
  58. package/src/ui/keymap-context.ts +10 -2
  59. package/src/ui/root.tsx +26 -6
  60. package/src/ui/shiki.ts +49 -0
  61. package/src/ui/status-bar-model.ts +19 -1
  62. package/src/ui/theme.ts +22 -10
  63. package/src/ui/themes.generated.ts +59794 -0
  64. package/src/ui/themes.ts +68 -274
  65. package/src/ui/syntax.ts +0 -102
@@ -1,51 +1,26 @@
1
- import type { DiffRenderable } from '@opentui/core'
2
-
1
+ import { useTerminalDimensions } from '@opentui/react'
3
2
  import { memo, useEffect, useRef } from 'react'
4
3
 
5
- import type { DiffData } from '../../state/types'
4
+ import type { DiffData, GitDiffView } from '../../state/types'
5
+ import type { ThemeId } from '../themes'
6
6
 
7
7
  import { fetchDiff } from '../../git/git-diff'
8
8
  import { useGitPanelPolling } from '../../git/git-poller'
9
9
  import { useAppStore } from '../../state/app-store'
10
10
  import { dispatchGlobal } from '../../state/dispatch-ref'
11
+ import { getSelectedGitFile, gitFileKey } from '../../state/git-tree'
11
12
  import { setGitDiffScroller } from '../git-view-controls'
12
- import { getSyntaxClient, getSyntaxStyle } from '../syntax'
13
13
  import { theme } from '../theme'
14
- import { fileKey, GitPanel } from './git-panel'
15
-
16
- interface CodePaneLike {
17
- scrollY: number
18
- maxScrollY: number
19
- }
20
-
21
- interface DiffRenderableInternals {
22
- leftCodeRenderable?: CodePaneLike
23
- rightCodeRenderable?: CodePaneLike
24
- }
25
-
26
- function filetypeFromPath(path: string): string | undefined {
27
- const dot = path.lastIndexOf('.')
28
- if (dot < 0) return undefined
29
- const ext = path.slice(dot + 1).toLowerCase()
30
- const map: Record<string, string> = {
31
- cjs: 'javascript',
32
- js: 'javascript',
33
- jsx: 'javascript',
34
- markdown: 'markdown',
35
- md: 'markdown',
36
- mdx: 'markdown',
37
- mjs: 'javascript',
38
- ts: 'typescript',
39
- tsx: 'typescript',
40
- zig: 'zig',
41
- }
42
- return map[ext]
43
- }
14
+ import { PierreDiff, type PierreDiffHandle } from './diff-renderer'
15
+ import { GitPanel } from './git-panel'
44
16
 
45
17
  interface DiffStageProps {
46
18
  diff: DiffData | undefined
19
+ diffKey: string | null
47
20
  loading: boolean
48
- diffRef: React.RefObject<DiffRenderable | null>
21
+ diffRef: React.RefObject<PierreDiffHandle | null>
22
+ themeId: ThemeId
23
+ view: GitDiffView
49
24
  }
50
25
 
51
26
  function placeholderText(diff: DiffData): string | null {
@@ -62,25 +37,32 @@ function placeholderText(diff: DiffData): string | null {
62
37
  return null
63
38
  }
64
39
 
65
- const DiffStage = memo(function DiffStage({ diff, diffRef, loading }: DiffStageProps) {
40
+ const DiffStage = memo(function DiffStage({
41
+ diff,
42
+ diffKey,
43
+ diffRef,
44
+ loading,
45
+ themeId,
46
+ view,
47
+ }: DiffStageProps) {
66
48
  if (loading && !diff) {
67
49
  return (
68
50
  <box flexGrow={1} padding={1}>
69
- <text fg={theme.textMuted}>Loading diff…</text>
51
+ <text fg={theme.colors['descriptionForeground']}>Loading diff…</text>
70
52
  </box>
71
53
  )
72
54
  }
73
55
  if (!diff) {
74
56
  return (
75
57
  <box flexGrow={1} padding={1}>
76
- <text fg={theme.textMuted}>Select a file.</text>
58
+ <text fg={theme.colors['descriptionForeground']}>Select a file.</text>
77
59
  </box>
78
60
  )
79
61
  }
80
62
  if (diff.errorMessage) {
81
63
  return (
82
64
  <box flexGrow={1} padding={1}>
83
- <text fg={theme.danger}>{diff.errorMessage}</text>
65
+ <text fg={theme.colors['editorError.foreground']}>{diff.errorMessage}</text>
84
66
  </box>
85
67
  )
86
68
  }
@@ -89,50 +71,45 @@ const DiffStage = memo(function DiffStage({ diff, diffRef, loading }: DiffStageP
89
71
  if (placeholder) {
90
72
  return (
91
73
  <box flexGrow={1} padding={1}>
92
- <text fg={theme.textMuted}>{placeholder}</text>
74
+ <text fg={theme.colors['descriptionForeground']}>{placeholder}</text>
93
75
  </box>
94
76
  )
95
77
  }
96
78
 
97
- const filetype = filetypeFromPath(diff.path)
98
-
99
79
  return (
100
80
  <box flexDirection="column" flexGrow={1} overflow="hidden">
101
81
  {diff.oldPath ? (
102
82
  <box paddingLeft={1} paddingRight={1}>
103
- <text fg={theme.textMuted}>
83
+ <text fg={theme.colors['descriptionForeground']}>
104
84
  renamed: {diff.oldPath} → {diff.path}
105
85
  </text>
106
86
  </box>
107
87
  ) : null}
108
- <diff
88
+ <PierreDiff
89
+ cacheKey={diffKey ?? diff.path}
109
90
  ref={diffRef}
110
91
  diff={diff.rawDiff}
111
- view="split"
112
- syncScroll
113
- showLineNumbers
114
- wrapMode="none"
115
- filetype={filetype}
116
- treeSitterClient={filetype ? getSyntaxClient() : undefined}
117
- syntaxStyle={filetype ? getSyntaxStyle() : undefined}
118
- addedBg={theme.diffAddBg}
119
- removedBg={theme.diffRemoveBg}
120
- addedSignColor={theme.success}
121
- removedSignColor={theme.danger}
122
- flexGrow={1}
92
+ path={diff.path}
93
+ themeId={themeId}
94
+ view={view}
123
95
  />
124
96
  </box>
125
97
  )
126
98
  })
127
99
 
128
- export const GitView = memo(function GitView() {
129
- const sidebarWidth = useAppStore((s) => s.sidebar.width)
100
+ interface GitViewProps {
101
+ themeId: ThemeId
102
+ }
103
+
104
+ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
105
+ const dimensions = useTerminalDimensions()
106
+ const gitPane = useAppStore((s) => s.gitPane)
130
107
  const gitPanel = useAppStore((s) => s.gitPanel)
131
108
  const gitMode = useAppStore((s) => s.gitMode)
132
109
  const currentSessionId = useAppStore((s) => s.currentSessionId)
133
110
  const sessions = useAppStore((s) => s.sessions)
134
111
  const focusMode = useAppStore((s) => s.focusMode)
135
- const diffRef = useRef<DiffRenderable | null>(null)
112
+ const diffRef = useRef<PierreDiffHandle | null>(null)
136
113
 
137
114
  const currentSession = currentSessionId
138
115
  ? sessions.find((s) => s.id === currentSessionId)
@@ -141,22 +118,29 @@ export const GitView = memo(function GitView() {
141
118
 
142
119
  useGitPanelPolling({ enabled: focusMode === 'git', projectPath })
143
120
 
144
- const selectedFile = gitPanel.files[gitMode.selectedFileIndex]
145
- const diff = selectedFile ? gitMode.diffs[selectedFile.path] : undefined
146
- const loading = selectedFile ? !!gitMode.loading[selectedFile.path] : false
121
+ const fileBarWidth = Math.max(20, Math.floor(dimensions.width * gitPane.diffModeRatio))
122
+
123
+ const selectedFile = getSelectedGitFile(gitPanel.files, {
124
+ collapsedFolders: gitMode.collapsedFolders,
125
+ fileListMode: gitPane.fileListMode,
126
+ selectedEntryKey: gitMode.selectedEntryKey,
127
+ })
128
+ const selectedDiffKey = selectedFile ? gitFileKey(selectedFile) : null
129
+ const diff = selectedDiffKey ? gitMode.diffs[selectedDiffKey] : undefined
130
+ const loading = selectedDiffKey ? !!gitMode.loading[selectedDiffKey] : false
147
131
 
148
132
  useEffect(() => {
149
133
  setGitDiffScroller((delta: number) => {
150
- const node = diffRef.current as unknown as DiffRenderableInternals | null
134
+ const node = diffRef.current
151
135
  if (!node) return
152
- const left = node.leftCodeRenderable
153
- const right = node.rightCodeRenderable
154
- if (!left || !right) return
155
- const cap = Math.max(left.maxScrollY, right.maxScrollY)
156
- const base = left.scrollY
136
+ const left = node.leftScroll
137
+ const right = node.rightScroll
138
+ if (!left) return
139
+ const cap = Math.max(left.scrollHeight - left.viewport.height, 0)
140
+ const base = left.scrollTop
157
141
  const nextScroll = Math.max(0, Math.min(cap, base + delta))
158
- left.scrollY = nextScroll
159
- right.scrollY = nextScroll
142
+ left.scrollTop = nextScroll
143
+ if (right && right !== left) right.scrollTop = nextScroll
160
144
  })
161
145
  return () => setGitDiffScroller(null)
162
146
  }, [])
@@ -164,13 +148,15 @@ export const GitView = memo(function GitView() {
164
148
  useEffect(() => {
165
149
  if (focusMode !== 'git') return
166
150
  if (!selectedFile || !projectPath) return
167
- const path = selectedFile.path
151
+ if (!selectedDiffKey) return
168
152
  if (diff || loading) return
169
- dispatchGlobal({ loading: true, path, type: 'git-mode-set-loading' })
153
+ dispatchGlobal({ key: selectedDiffKey, loading: true, type: 'git-mode-set-loading' })
170
154
  void fetchDiff(projectPath, selectedFile)
171
- .then((d) => dispatchGlobal({ diff: d, path, type: 'git-mode-set-diff' }))
172
- .catch(() => dispatchGlobal({ loading: false, path, type: 'git-mode-set-loading' }))
173
- }, [focusMode, projectPath, selectedFile, diff, loading])
155
+ .then((d) => dispatchGlobal({ diff: d, key: selectedDiffKey, type: 'git-mode-set-diff' }))
156
+ .catch(() =>
157
+ dispatchGlobal({ key: selectedDiffKey, loading: false, type: 'git-mode-set-loading' })
158
+ )
159
+ }, [focusMode, projectPath, selectedFile, selectedDiffKey, diff, loading])
174
160
 
175
161
  const pendingPath = gitMode.pendingDeletePath
176
162
  const pendingIsUntracked =
@@ -178,7 +164,7 @@ export const GitView = memo(function GitView() {
178
164
  selectedFile?.path === pendingPath &&
179
165
  selectedFile.section === 'untracked'
180
166
  let pendingHint: string | null = null
181
- if (pendingPath !== null) {
167
+ if (pendingPath !== null && selectedFile) {
182
168
  pendingHint = pendingIsUntracked
183
169
  ? 'press d again to delete file'
184
170
  : 'press d again to discard changes'
@@ -187,13 +173,13 @@ export const GitView = memo(function GitView() {
187
173
  let footerNode: React.ReactNode = null
188
174
  if (pendingHint) {
189
175
  footerNode = (
190
- <text fg={theme.warning}>
176
+ <text fg={theme.colors['editorWarning.foreground']}>
191
177
  <strong>{pendingHint}</strong>
192
178
  </text>
193
179
  )
194
180
  } else if (actionMessage) {
195
181
  footerNode = actionMessage.split('\n').map((line, idx) => (
196
- <text key={idx} fg={theme.accent}>
182
+ <text key={idx} fg={theme.colors['textLink.foreground']}>
197
183
  {line}
198
184
  </text>
199
185
  ))
@@ -203,32 +189,48 @@ export const GitView = memo(function GitView() {
203
189
  <box flexDirection="column" flexGrow={1}>
204
190
  <box flexDirection="row" flexGrow={1}>
205
191
  <box
206
- width={sidebarWidth}
192
+ width={fileBarWidth}
207
193
  flexDirection="column"
208
- backgroundColor={theme.panel}
194
+ backgroundColor={theme.colors['sideBar.background']}
209
195
  padding={0}
210
196
  gap={0}
211
197
  >
212
- <text fg={theme.accent}>
198
+ <text fg={theme.colors['textLink.foreground']}>
213
199
  <strong>aimux · git</strong>
214
200
  </text>
215
201
  {gitPanel.branch ? (
216
202
  <box flexDirection="row">
217
- <text fg={theme.accent}>{'\u{e702}'} </text>
218
- <text fg={theme.textMuted}>{gitPanel.branch}</text>
203
+ <text fg={theme.colors['textLink.foreground']}>{'\u{e702}'} </text>
204
+ <text fg={theme.colors['descriptionForeground']}>{gitPanel.branch}</text>
219
205
  </box>
220
206
  ) : null}
221
- <text fg={theme.dim}>{'·'.repeat(Math.max(0, sidebarWidth - 2))}</text>
207
+ <text fg={theme.colors['editor.lineHighlightBackground']}>
208
+ {'·'.repeat(Math.max(0, fileBarWidth - 2))}
209
+ </text>
222
210
  <GitPanel
211
+ collapsedFolders={gitMode.collapsedFolders}
212
+ fileListMode={gitPane.fileListMode}
223
213
  gitPanel={gitPanel}
224
214
  projectPath={projectPath}
225
- selectedFileKey={selectedFile ? fileKey(selectedFile) : null}
215
+ selectedEntryKey={gitMode.selectedEntryKey}
226
216
  />
227
217
  </box>
228
- <DiffStage diff={diff} diffRef={diffRef} loading={loading} />
218
+ <DiffStage
219
+ diff={diff}
220
+ diffKey={selectedDiffKey}
221
+ diffRef={diffRef}
222
+ loading={loading}
223
+ themeId={themeId}
224
+ view={gitMode.diffView}
225
+ />
229
226
  </box>
230
227
  {footerNode ? (
231
- <box paddingLeft={1} paddingRight={1} backgroundColor={theme.panel} flexDirection="column">
228
+ <box
229
+ paddingLeft={1}
230
+ paddingRight={1}
231
+ backgroundColor={theme.colors['sideBar.background']}
232
+ flexDirection="column"
233
+ >
232
234
  {footerNode}
233
235
  </box>
234
236
  ) : null}
@@ -1,7 +1,13 @@
1
+ import type { ModeId } from '@brimveyn/aimux-config'
2
+
1
3
  import { useTerminalDimensions } from '@opentui/react'
2
4
  import { useLayoutEffect, useMemo, useRef } from 'react'
3
5
 
4
- import { collectHelpEntries, type HelpEntry } from '../../input/keymap/help-entries'
6
+ import {
7
+ collectHelpEntries,
8
+ HELP_MODE_LABELS,
9
+ type HelpEntry,
10
+ } from '../../input/keymap/help-entries'
5
11
  import { dispatchGlobal } from '../../state/dispatch-ref'
6
12
  import { useKeymap } from '../keymap-context'
7
13
  import { theme } from '../theme'
@@ -12,6 +18,7 @@ import { ModalShell } from './modal-shell'
12
18
  interface HelpModalProps {
13
19
  filter: string | null
14
20
  selectedIndex: number
21
+ scope: ModeId | null
15
22
  }
16
23
 
17
24
  function matchesFilter(entry: HelpEntry, needle: string): boolean {
@@ -78,14 +85,23 @@ function computeWindowStart(
78
85
  return start
79
86
  }
80
87
 
81
- export function HelpModal({ filter, selectedIndex }: HelpModalProps) {
88
+ export function HelpModal({ filter, scope, selectedIndex }: HelpModalProps) {
82
89
  const config = useKeymap()
83
90
  const dimensions = useTerminalDimensions()
84
91
  const allEntries = useMemo(() => collectHelpEntries(config), [config])
92
+ const scoped = useMemo(
93
+ () => (scope ? allEntries.filter((e) => e.mode === scope) : allEntries),
94
+ [allEntries, scope]
95
+ )
85
96
  const filtered = useMemo(
86
- () => allEntries.filter((e) => matchesFilter(e, filter ?? '')),
87
- [allEntries, filter]
97
+ () => scoped.filter((e) => matchesFilter(e, filter ?? '')),
98
+ [scoped, filter]
88
99
  )
100
+ const title = useMemo(() => {
101
+ if (!scope) return 'Keybindings'
102
+ const label = HELP_MODE_LABELS.find((m) => m.modeId === scope)?.label ?? scope
103
+ return `${label} — keybindings`
104
+ }, [scope])
89
105
  const rows = useMemo(() => buildRows(filtered), [filtered])
90
106
 
91
107
  useLayoutEffect(() => {
@@ -113,12 +129,12 @@ export function HelpModal({ filter, selectedIndex }: HelpModalProps) {
113
129
 
114
130
  return (
115
131
  <ModalShell
116
- title="Keybindings"
132
+ title={title}
117
133
  keybindsModeId="modal.help"
118
134
  width={uiTokens.modalWidth.lg}
119
135
  footer={
120
136
  <box flexDirection="column" gap={0}>
121
- <text fg={theme.dim}>
137
+ <text fg={theme.colors['editor.lineHighlightBackground']}>
122
138
  {filtered.length === 0
123
139
  ? ''
124
140
  : ` ${effectiveIndex + 1} / ${filtered.length}${filter ? '' : ' — type / to filter'}`}
@@ -128,7 +144,7 @@ export function HelpModal({ filter, selectedIndex }: HelpModalProps) {
128
144
  }
129
145
  >
130
146
  {filtered.length === 0 ? (
131
- <text fg={theme.textMuted}>
147
+ <text fg={theme.colors['descriptionForeground']}>
132
148
  {filter ? 'No matching bindings.' : 'No bindings registered.'}
133
149
  </text>
134
150
  ) : (
@@ -138,14 +154,14 @@ export function HelpModal({ filter, selectedIndex }: HelpModalProps) {
138
154
  if (row.kind === 'header') {
139
155
  return (
140
156
  <box key={`h-${rowIndex}`} paddingLeft={1} paddingTop={i === 0 ? 0 : 1}>
141
- <text fg={theme.warning} wrapMode="none">
157
+ <text fg={theme.colors['editorWarning.foreground']} wrapMode="none">
142
158
  <strong>{row.label}</strong>
143
159
  </text>
144
160
  </box>
145
161
  )
146
162
  }
147
163
  const active = row.entryIndex === effectiveIndex
148
- const bg = active ? theme.panelHighlight : undefined
164
+ const bg = active ? theme.colors['list.activeSelectionBackground'] : undefined
149
165
  return (
150
166
  <box
151
167
  key={`e-${rowIndex}`}
@@ -155,12 +171,28 @@ export function HelpModal({ filter, selectedIndex }: HelpModalProps) {
155
171
  backgroundColor={bg}
156
172
  >
157
173
  <box width={KEYS_COLUMN_WIDTH} flexShrink={0}>
158
- <text fg={active ? theme.accent : theme.accentAlt} bg={bg} wrapMode="none">
174
+ <text
175
+ fg={
176
+ active
177
+ ? theme.colors['textLink.foreground']
178
+ : theme.colors['terminal.ansiMagenta']
179
+ }
180
+ bg={bg}
181
+ wrapMode="none"
182
+ >
159
183
  {row.entry.keysDisplay}
160
184
  </text>
161
185
  </box>
162
186
  <box flexGrow={1} overflow="hidden">
163
- <text fg={active ? theme.text : theme.textMuted} bg={bg} wrapMode="none">
187
+ <text
188
+ fg={
189
+ active
190
+ ? theme.colors['editor.foreground']
191
+ : theme.colors['descriptionForeground']
192
+ }
193
+ bg={bg}
194
+ wrapMode="none"
195
+ >
164
196
  {row.entry.description ?? ''}
165
197
  </text>
166
198
  </box>
@@ -8,7 +8,7 @@ interface InputFieldProps {
8
8
  }
9
9
 
10
10
  export function InputField({ active, cursorPos, value }: InputFieldProps) {
11
- const fg = active ? theme.text : theme.textMuted
11
+ const fg = active ? theme.colors['editor.foreground'] : theme.colors['descriptionForeground']
12
12
  if (!active) {
13
13
  return (
14
14
  <Surface tone="input" padding={1}>
@@ -29,7 +29,7 @@ export function InputField({ active, cursorPos, value }: InputFieldProps) {
29
29
  <Surface tone="inputActive" padding={1}>
30
30
  <text fg={fg}>
31
31
  {before}
32
- <span bg={theme.text} fg={theme.background}>
32
+ <span bg={theme.colors['editor.foreground']} fg={theme.colors['editor.background']}>
33
33
  {cursorDisplay}
34
34
  </span>
35
35
  {trailing}
@@ -33,7 +33,15 @@ export function ListItem({
33
33
  <box flexDirection="row">
34
34
  {isRow ? null : (
35
35
  <>
36
- <text fg={active ? theme.accent : theme.dim}>{active ? '›' : '·'}</text>
36
+ <text
37
+ fg={
38
+ active
39
+ ? theme.colors['textLink.foreground']
40
+ : theme.colors['editor.lineHighlightBackground']
41
+ }
42
+ >
43
+ {active ? '›' : '·'}
44
+ </text>
37
45
  <text> </text>
38
46
  </>
39
47
  )}
@@ -12,7 +12,7 @@ export function ModalFilterBar({ filter }: ModalFilterBarProps) {
12
12
 
13
13
  return (
14
14
  <Surface tone="input" paddingLeft={1} paddingRight={1} paddingTop={1} paddingBottom={1}>
15
- <text fg={theme.text}>/{filter}_</text>
15
+ <text fg={theme.colors['editor.foreground']}>/{filter}_</text>
16
16
  </Surface>
17
17
  )
18
18
  }
@@ -28,9 +28,9 @@ export function ModalKeybindsOverlay({ limit, modeId }: ModalKeybindsOverlayProp
28
28
  {entries.map((binding) => (
29
29
  <box key={`${binding.keys}-${binding.description}`} flexDirection="row">
30
30
  <box width={KEYS_COLUMN_WIDTH}>
31
- <text fg={theme.accentAlt}>{binding.keysDisplay}</text>
31
+ <text fg={theme.colors['terminal.ansiMagenta']}>{binding.keysDisplay}</text>
32
32
  </box>
33
- <text fg={theme.textMuted}>{binding.description ?? ''}</text>
33
+ <text fg={theme.colors['descriptionForeground']}>{binding.description ?? ''}</text>
34
34
  </box>
35
35
  ))}
36
36
  </Surface>
@@ -40,14 +40,14 @@ export function ModalShell({
40
40
  left={0}
41
41
  width="100%"
42
42
  height="100%"
43
- backgroundColor={theme.overlay}
43
+ backgroundColor={theme.colors['editorWidget.background']}
44
44
  opacity={0.7}
45
45
  />
46
46
  <Surface tone="elevated" padding={1} gap={1} width={width}>
47
47
  <box width="100%" flexDirection="column" gap={listGap}>
48
48
  <box flexDirection="column">
49
- <text fg={theme.accentAlt}>{title}</text>
50
- {subtitle ? <text fg={theme.textMuted}>{subtitle}</text> : null}
49
+ <text fg={theme.colors['terminal.ansiMagenta']}>{title}</text>
50
+ {subtitle ? <text fg={theme.colors['descriptionForeground']}>{subtitle}</text> : null}
51
51
  </box>
52
52
  {children}
53
53
  {footer ? <box>{footer}</box> : null}
@@ -34,11 +34,23 @@ export function NewTabModal({ customCommands, editBuffer, selectedIndex }: NewTa
34
34
  <ListItem
35
35
  key={option.id}
36
36
  active={active}
37
- title={<text fg={active ? theme.text : theme.textMuted}>{option.label}</text>}
37
+ title={
38
+ <text
39
+ fg={
40
+ active
41
+ ? theme.colors['editor.foreground']
42
+ : theme.colors['descriptionForeground']
43
+ }
44
+ >
45
+ {option.label}
46
+ </text>
47
+ }
38
48
  subtitle={
39
49
  <box flexDirection="column">
40
- <text fg={theme.textMuted}>{option.description}</text>
41
- {customCmd ? <text fg={theme.accent}>{customCmd}</text> : null}
50
+ <text fg={theme.colors['descriptionForeground']}>{option.description}</text>
51
+ {customCmd ? (
52
+ <text fg={theme.colors['textLink.foreground']}>{customCmd}</text>
53
+ ) : null}
42
54
  </box>
43
55
  }
44
56
  />
@@ -19,9 +19,9 @@ export function PendingChordOverlay() {
19
19
  <box position="absolute" bottom={modalOpen ? 10 : 2} right={1}>
20
20
  <Surface tone="elevated" paddingLeft={1} paddingRight={1}>
21
21
  <box flexDirection="row">
22
- <text fg={theme.textMuted}>pending: </text>
23
- <text fg={theme.accent}>{display}</text>
24
- <text fg={theme.textMuted}> …</text>
22
+ <text fg={theme.colors['descriptionForeground']}>pending: </text>
23
+ <text fg={theme.colors['textLink.foreground']}>{display}</text>
24
+ <text fg={theme.colors['descriptionForeground']}> …</text>
25
25
  </box>
26
26
  </Surface>
27
27
  </box>
@@ -108,7 +108,7 @@ export function SessionBar() {
108
108
  flexDirection="row"
109
109
  paddingLeft={1}
110
110
  paddingRight={1}
111
- backgroundColor={theme.panelMuted}
111
+ backgroundColor={theme.colors['sideBarSectionHeader.background']}
112
112
  >
113
113
  {visibleSessions.map((session) => {
114
114
  const displayIndex = baselineOrder.indexOf(session.id) + 1
@@ -168,9 +168,14 @@ function SessionChip({
168
168
  const showSpinner = busy && !active
169
169
  const spinner = useBusySpinner(showSpinner)
170
170
  const indicator = showSpinner ? spinner : '●'
171
- const indicatorColor = active || showSpinner ? theme.accent : theme.success
172
- const labelColor = active ? theme.text : theme.textMuted
173
- const bgColor = dragging || active ? theme.panelHighlight : undefined
171
+ const indicatorColor =
172
+ active || showSpinner
173
+ ? theme.colors['textLink.foreground']
174
+ : theme.colors['gitDecoration.addedResourceForeground']
175
+ const labelColor = active
176
+ ? theme.colors['editor.foreground']
177
+ : theme.colors['descriptionForeground']
178
+ const bgColor = dragging || active ? theme.colors['list.activeSelectionBackground'] : undefined
174
179
 
175
180
  return (
176
181
  <box
@@ -200,7 +205,7 @@ function SessionChip({
200
205
  <text fg={labelColor} selectable={false}>
201
206
  [{index}] {session.name}
202
207
  </text>
203
- <text fg={theme.dim} selectable={false}>
208
+ <text fg={theme.colors['editor.lineHighlightBackground']} selectable={false}>
204
209
  {' '}
205
210
  </text>
206
211
  </box>