@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
@@ -4,7 +4,7 @@ import { dispatchGlobal, runSideEffectGlobal } from '../../../../state/dispatch-
4
4
  import { filterSessions } from '../../../../state/selectors'
5
5
  import { abbreviatePath } from '../../../path-format'
6
6
  import { orderSessionsForDisplay } from '../../../session-ordering'
7
- import { useTokens } from '../../../theme'
7
+ import { useTheme } from '../../../theme'
8
8
  import { uiTokens } from '../../../ui-tokens'
9
9
  import { Picker, type PickerItem } from '../shared/picker'
10
10
 
@@ -43,7 +43,7 @@ export function SessionPickerModal({
43
43
  selectedIndex,
44
44
  sessions,
45
45
  }: SessionPickerModalProps) {
46
- const t = useTokens()
46
+ const t = useTheme()
47
47
  const ordered = orderSessionsForDisplay(sessions)
48
48
  const baselineOrder = ordered.map((s) => s.id)
49
49
  const filtered = filterSessions(ordered, filter)
@@ -57,10 +57,10 @@ export function SessionPickerModal({
57
57
  onClick: () => runSideEffectGlobal({ type: 'confirm-selected-session' }),
58
58
  onDelete: () => runSideEffectGlobal({ type: 'delete-selected-session' }),
59
59
  subtitle: session.projectPath ? (
60
- <text fg={t.muted}>{abbreviatePath(session.projectPath)}</text>
60
+ <text fg={t.textMuted}>{abbreviatePath(session.projectPath)}</text>
61
61
  ) : undefined,
62
62
  title: (
63
- <text fg={active ? t.palette.ink : t.muted}>
63
+ <text fg={active ? t.text : t.textMuted}>
64
64
  {formatSessionLine(session, currentSessionId, currentTabCount, displayIndex)}
65
65
  </text>
66
66
  ),
@@ -71,7 +71,7 @@ export function SessionPickerModal({
71
71
  key: '__create-new__',
72
72
  onClick: () => runSideEffectGlobal({ type: 'confirm-selected-session' }),
73
73
  title: (
74
- <text fg={selectedIndex === filtered.length ? t.palette.ink : t.muted}>
74
+ <text fg={selectedIndex === filtered.length ? t.text : t.textMuted}>
75
75
  Create new workspace
76
76
  </text>
77
77
  ),
@@ -91,7 +91,7 @@ export function SessionPickerModal({
91
91
  selectedIndex={selectedIndex}
92
92
  emptyState={
93
93
  filtered.length === 0 ? (
94
- <text fg={t.muted}>{getEmptyStateMessage(hasFilter)}</text>
94
+ <text fg={t.textMuted}>{getEmptyStateMessage(hasFilter)}</text>
95
95
  ) : undefined
96
96
  }
97
97
  onHover={(index) => dispatchGlobal({ index, type: 'set-modal-selection-index' })}
@@ -2,7 +2,7 @@ import type { ModeId } from '@brimveyn/aimux-config'
2
2
 
3
3
  import { type ReactNode } from 'react'
4
4
 
5
- import { useTokens } from '../../../theme'
5
+ import { useTheme } from '../../../theme'
6
6
  import { InputField } from '../../primitives/input-field'
7
7
  import { ListItem } from '../../primitives/list-item'
8
8
  import { ModalShell } from './modal-shell'
@@ -81,11 +81,11 @@ export function Form({
81
81
  }
82
82
 
83
83
  export function FieldLabel({ active = false, children, description }: FieldLabelProps) {
84
- const t = useTokens()
84
+ const t = useTheme()
85
85
  return (
86
86
  <box flexDirection="column">
87
- {children ? <text fg={active ? t.palette.ink : t.muted}>{children}</text> : null}
88
- {description ? <text fg={t.muted}>{description}</text> : null}
87
+ {children ? <text fg={active ? t.text : t.textMuted}>{children}</text> : null}
88
+ {description ? <text fg={t.textMuted}>{description}</text> : null}
89
89
  </box>
90
90
  )
91
91
  }
@@ -2,7 +2,7 @@ import type { ModeId } from '@brimveyn/aimux-config'
2
2
 
3
3
  import { describeBindings } from '../../../../input/keymap/describe-bindings'
4
4
  import { useKeymap } from '../../../keymap-context'
5
- import { useTokens } from '../../../theme'
5
+ import { useTheme } from '../../../theme'
6
6
  import { Surface } from '../../primitives/surface'
7
7
 
8
8
  const KEYS_COLUMN_WIDTH = 12
@@ -13,7 +13,7 @@ interface ModalKeybindsOverlayProps {
13
13
  }
14
14
 
15
15
  export function ModalKeybindsOverlay({ limit, modeId }: ModalKeybindsOverlayProps) {
16
- const t = useTokens()
16
+ const t = useTheme()
17
17
  const config = useKeymap()
18
18
  const bindings = describeBindings(config, modeId, {
19
19
  mergeAlternativesByDescription: true,
@@ -29,9 +29,9 @@ export function ModalKeybindsOverlay({ limit, modeId }: ModalKeybindsOverlayProp
29
29
  {entries.map((binding) => (
30
30
  <box key={binding.description ?? binding.keys} flexDirection="row">
31
31
  <box width={KEYS_COLUMN_WIDTH}>
32
- <text fg={t.accent}>{binding.keysDisplay}</text>
32
+ <text fg={t.textMuted}>{binding.keysDisplay}</text>
33
33
  </box>
34
- <text fg={t.muted}>{binding.description ?? ''}</text>
34
+ <text fg={t.textMuted}>{binding.description ?? ''}</text>
35
35
  </box>
36
36
  ))}
37
37
  </Surface>
@@ -3,7 +3,7 @@ import type { ModeId } from '@brimveyn/aimux-config'
3
3
  import { type BoxRenderable, type OptimizedBuffer, RGBA } from '@opentui/core'
4
4
  import { type ReactNode } from 'react'
5
5
 
6
- import { useTokens, useTransparent } from '../../../theme'
6
+ import { useTheme, useTransparent } from '../../../theme'
7
7
  import { ModalKeybindsOverlay } from './modal-keybinds-overlay'
8
8
 
9
9
  interface ModalShellProps {
@@ -57,9 +57,9 @@ export function ModalShell({
57
57
  title,
58
58
  width,
59
59
  }: ModalShellProps) {
60
- const t = useTokens()
60
+ const t = useTheme()
61
61
  const transparent = useTransparent()
62
- const bg = transparent ? 'transparent' : t.elevated
62
+ const bg = transparent ? 'transparent' : t.backgroundPanel
63
63
  return (
64
64
  <box
65
65
  position="absolute"
@@ -72,7 +72,7 @@ export function ModalShell({
72
72
  >
73
73
  <box
74
74
  border
75
- borderColor={t.palette.primary}
75
+ borderColor={t.border}
76
76
  backgroundColor={bg}
77
77
  padding={1}
78
78
  width={width}
@@ -80,8 +80,8 @@ export function ModalShell({
80
80
  >
81
81
  <box width="100%" flexDirection="column" gap={listGap}>
82
82
  <box flexDirection="column">
83
- <text fg={t.accent}>{title}</text>
84
- {subtitle ? <text fg={t.muted}>{subtitle}</text> : null}
83
+ <text fg={t.text}>{title}</text>
84
+ {subtitle ? <text fg={t.textMuted}>{subtitle}</text> : null}
85
85
  </box>
86
86
  {children}
87
87
  {footer ? <box>{footer}</box> : null}
@@ -4,7 +4,7 @@ import type { ScrollBoxRenderable } from '@opentui/core'
4
4
  import { useTerminalDimensions } from '@opentui/react'
5
5
  import { type ReactNode, useLayoutEffect, useRef } from 'react'
6
6
 
7
- import { useTokens } from '../../../theme'
7
+ import { useTheme } from '../../../theme'
8
8
  import { BareInput } from '../../primitives/bare-input'
9
9
  import { ListItem } from '../../primitives/list-item'
10
10
  import { ModalShell } from './modal-shell'
@@ -39,7 +39,7 @@ const VIEWPORT_HEIGHT_RATIO = 0.6
39
39
  const MODAL_CHROME_ROWS = 6
40
40
 
41
41
  function PickerItemCtas({ onDelete, onEdit }: { onEdit?: () => void; onDelete?: () => void }) {
42
- const t = useTokens()
42
+ const t = useTheme()
43
43
  return (
44
44
  <box flexDirection="row" gap={1}>
45
45
  {onEdit ? (
@@ -49,7 +49,7 @@ function PickerItemCtas({ onDelete, onEdit }: { onEdit?: () => void; onDelete?:
49
49
  onEdit()
50
50
  }}
51
51
  >
52
- <text fg={t.palette.primary}>[edit]</text>
52
+ <text fg={t.primary}>[edit]</text>
53
53
  </box>
54
54
  ) : null}
55
55
  {onDelete ? (
@@ -59,7 +59,7 @@ function PickerItemCtas({ onDelete, onEdit }: { onEdit?: () => void; onDelete?:
59
59
  onDelete()
60
60
  }}
61
61
  >
62
- <text fg={t.palette.error}>[del]</text>
62
+ <text fg={t.error}>[del]</text>
63
63
  </box>
64
64
  ) : null}
65
65
  </box>
@@ -80,7 +80,7 @@ export function Picker({
80
80
  title,
81
81
  width,
82
82
  }: PickerProps) {
83
- const t = useTokens()
83
+ const t = useTheme()
84
84
  const dimensions = useTerminalDimensions()
85
85
  const maxHeight = Math.max(6, Math.floor(dimensions.height * VIEWPORT_HEIGHT_RATIO))
86
86
  const listHeight = Math.max(1, maxHeight - MODAL_CHROME_ROWS)
@@ -140,7 +140,7 @@ export function Picker({
140
140
  if (item.group && item.group !== prevGroup) {
141
141
  nodes.push(
142
142
  <box key={`group::${item.group}`} paddingLeft={1} paddingTop={index === 0 ? 0 : 1}>
143
- <text fg={t.palette.warning} wrapMode="none">
143
+ <text fg={t.textMuted} wrapMode="none">
144
144
  {item.group}
145
145
  </text>
146
146
  </box>
@@ -2,7 +2,7 @@ import type { SnippetRecord } from '../../../../state/types'
2
2
 
3
3
  import { dispatchGlobal, runSideEffectGlobal } from '../../../../state/dispatch-ref'
4
4
  import { filterSnippets } from '../../../../state/selectors'
5
- import { useTokens } from '../../../theme'
5
+ import { useTheme } from '../../../theme'
6
6
  import { uiTokens } from '../../../ui-tokens'
7
7
  import { Picker, type PickerItem } from '../shared/picker'
8
8
 
@@ -27,7 +27,7 @@ export function SnippetPickerModal({
27
27
  selectedIndex,
28
28
  snippets,
29
29
  }: SnippetPickerModalProps) {
30
- const t = useTokens()
30
+ const t = useTheme()
31
31
  const filtered = filterSnippets(snippets, filter)
32
32
 
33
33
  const items: PickerItem[] = filtered.map((snippet, index) => {
@@ -40,9 +40,9 @@ export function SnippetPickerModal({
40
40
  },
41
41
  onDelete: () => runSideEffectGlobal({ type: 'delete-selected-snippet' }),
42
42
  onEdit: () => runSideEffectGlobal({ type: 'edit-selected-snippet' }),
43
- subtitle: <text fg={t.muted}>{truncateContent(snippet.content)}</text>,
43
+ subtitle: <text fg={t.textMuted}>{truncateContent(snippet.content)}</text>,
44
44
  title: (
45
- <text fg={active ? t.palette.ink : t.muted}>
45
+ <text fg={active ? t.text : t.textMuted}>
46
46
  <strong>{snippet.name}</strong>
47
47
  </text>
48
48
  ),
@@ -60,7 +60,7 @@ export function SnippetPickerModal({
60
60
  items={items}
61
61
  selectedIndex={selectedIndex}
62
62
  emptyState={
63
- <text fg={t.muted}>
63
+ <text fg={t.textMuted}>
64
64
  {filter ? 'No matching snippets.' : 'No snippets yet. Press n to create one.'}
65
65
  </text>
66
66
  }
@@ -3,7 +3,7 @@ import type { AssistantId } from '../../../../state/types'
3
3
  import { getAllAssistantOptions, getAssistantOption } from '../../../../pty/command-registry'
4
4
  import { dispatchGlobal, runSideEffectGlobal } from '../../../../state/dispatch-ref'
5
5
  import { filterAssistants } from '../../../../state/selectors'
6
- import { useTokens } from '../../../theme'
6
+ import { useTheme } from '../../../theme'
7
7
  import { uiTokens } from '../../../ui-tokens'
8
8
  import { Form, TextField } from '../shared/form'
9
9
  import { Picker, type PickerItem } from '../shared/picker'
@@ -25,7 +25,7 @@ export function NewTabModal({
25
25
  filter,
26
26
  selectedIndex,
27
27
  }: NewTabModalProps) {
28
- const t = useTokens()
28
+ const t = useTheme()
29
29
 
30
30
  if (editingCommand !== null) {
31
31
  const option =
@@ -60,11 +60,11 @@ export function NewTabModal({
60
60
  onEdit: () => dispatchGlobal({ assistantId: option.id, type: 'open-edit-custom-command' }),
61
61
  subtitle: (
62
62
  <box flexDirection="column">
63
- <text fg={t.muted}>{option.description}</text>
64
- {customCmd ? <text fg={t.palette.primary}>{customCmd}</text> : null}
63
+ <text fg={t.textMuted}>{option.description}</text>
64
+ {customCmd ? <text fg={t.primary}>{customCmd}</text> : null}
65
65
  </box>
66
66
  ),
67
- title: <text fg={active ? t.palette.ink : t.muted}>{option.label}</text>,
67
+ title: <text fg={active ? t.text : t.textMuted}>{option.label}</text>,
68
68
  }
69
69
  })
70
70
 
@@ -78,7 +78,7 @@ export function NewTabModal({
78
78
  cursorPos={cursorPos}
79
79
  items={items}
80
80
  selectedIndex={selectedIndex}
81
- emptyState={<text fg={t.muted}>No matching assistants.</text>}
81
+ emptyState={<text fg={t.textMuted}>No matching assistants.</text>}
82
82
  onHover={(index) => dispatchGlobal({ index, type: 'set-modal-selection-index' })}
83
83
  />
84
84
  )
@@ -1,9 +1,10 @@
1
1
  import { useLayoutEffect, useMemo } from 'react'
2
2
 
3
+ import type { ThemeId } from '../../../themes'
4
+
3
5
  import { dispatchGlobal, runSideEffectGlobal } from '../../../../state/dispatch-ref'
4
- import { filterThemeIds } from '../../../filter-themes'
5
- import { useTokens, useTransparent } from '../../../theme'
6
- import { type ThemeId, THEMES } from '../../../themes'
6
+ import { filterThemeIds, themeDisplayName } from '../../../filter-themes'
7
+ import { useTheme, useTransparent } from '../../../theme'
7
8
  import { uiTokens } from '../../../ui-tokens'
8
9
  import { Picker, type PickerItem } from '../shared/picker'
9
10
 
@@ -25,7 +26,7 @@ export function ThemePickerModal({
25
26
  filter,
26
27
  selectedIndex,
27
28
  }: ThemePickerModalProps) {
28
- const t = useTokens()
29
+ const t = useTheme()
29
30
  const transparent = useTransparent()
30
31
  const filtered = useMemo(() => filterThemeIds(filter), [filter])
31
32
 
@@ -35,22 +36,18 @@ export function ThemePickerModal({
35
36
 
36
37
  const effectiveIndex = clampSelection(selectedIndex, filtered.length)
37
38
 
38
- const items: PickerItem[] = filtered.flatMap((id, rowIndex) => {
39
- const entry = THEMES[id]
40
- if (!entry) return []
39
+ const items: PickerItem[] = filtered.map((id, rowIndex) => {
41
40
  const active = rowIndex === effectiveIndex
42
41
  const isCurrent = id === currentThemeId
43
- return [
44
- {
45
- key: id,
46
- onClick: () => {
47
- dispatchGlobal({ type: 'close-modal' })
48
- runSideEffectGlobal({ action: 'confirm', type: 'apply-theme' })
49
- },
50
- title: <text fg={active ? t.palette.ink : t.muted}>{entry.displayName}</text>,
51
- trailing: isCurrent ? <text fg={t.palette.primary}>current</text> : undefined,
42
+ return {
43
+ key: id,
44
+ onClick: () => {
45
+ dispatchGlobal({ type: 'close-modal' })
46
+ runSideEffectGlobal({ action: 'confirm', type: 'apply-theme' })
52
47
  },
53
- ]
48
+ title: <text fg={active ? t.text : t.textMuted}>{themeDisplayName(id)}</text>,
49
+ trailing: isCurrent ? <text fg={t.primary}>current</text> : undefined,
50
+ }
54
51
  })
55
52
 
56
53
  return (
@@ -62,15 +59,15 @@ export function ThemePickerModal({
62
59
  cursorPos={cursorPos}
63
60
  footer={
64
61
  <box flexDirection="column" gap={0}>
65
- <text fg={t.hover}>
62
+ <text fg={t.textMuted}>
66
63
  {filtered.length === 0 ? '' : ` ${effectiveIndex + 1} / ${filtered.length}`}
67
64
  </text>
68
- <text fg={t.muted}>{` transparent: ${transparent ? 'on' : 'off'} (ctrl-t)`}</text>
65
+ <text fg={t.textMuted}>{` transparent: ${transparent ? 'on' : 'off'} (ctrl-t)`}</text>
69
66
  </box>
70
67
  }
71
68
  items={items}
72
69
  selectedIndex={effectiveIndex}
73
- emptyState={<text fg={t.muted}>No themes available.</text>}
70
+ emptyState={<text fg={t.textMuted}>No themes available.</text>}
74
71
  onHover={(index) => dispatchGlobal({ index, type: 'set-modal-selection-index' })}
75
72
  />
76
73
  )
@@ -2,7 +2,7 @@ import type { AIUsageTool } from '@brimveyn/aimux-config'
2
2
 
3
3
  import { useAIUsageStore } from '../../../../state/ai-usage-store'
4
4
  import { dispatchGlobal } from '../../../../state/dispatch-ref'
5
- import { useBg, useTokens } from '../../../theme'
5
+ import { useTheme } from '../../../theme'
6
6
 
7
7
  const TOOL_ICON: Record<AIUsageTool, string> = {
8
8
  claude: 'CC',
@@ -47,8 +47,8 @@ function formatResetIn(snap: {
47
47
  }
48
48
 
49
49
  export function AIUsageIndicator() {
50
- const t = useTokens()
51
- const bg = useBg('elevated')
50
+ const t = useTheme()
51
+ const bg = t.backgroundPanel
52
52
  const enabled = useAIUsageStore((s) => s.enabled)
53
53
  const snapshots = useAIUsageStore((s) => s.snapshots)
54
54
 
@@ -74,7 +74,7 @@ export function AIUsageIndicator() {
74
74
  backgroundColor={bg}
75
75
  onMouseDown={openModal}
76
76
  >
77
- <text fg={t.muted}>…</text>
77
+ <text fg={t.textMuted}>…</text>
78
78
  </box>
79
79
  )
80
80
  }
@@ -95,7 +95,7 @@ export function AIUsageIndicator() {
95
95
  backgroundColor={bg}
96
96
  onMouseDown={openModal}
97
97
  >
98
- <text fg={t.palette.error} selectable={false}>
98
+ <text fg={t.error} selectable={false}>
99
99
  {`${icon} —`}
100
100
  </text>
101
101
  </box>
@@ -104,11 +104,11 @@ export function AIUsageIndicator() {
104
104
 
105
105
  if (snap.percent !== null) {
106
106
  const p = Math.round(snap.percent)
107
- let color = t.palette.success
107
+ let color = t.success
108
108
  if (p >= 85) {
109
- color = t.palette.error
109
+ color = t.error
110
110
  } else if (p >= 60) {
111
- color = t.palette.warning
111
+ color = t.warning
112
112
  }
113
113
  const { empty, filled } = buildBar(snap.percent)
114
114
  const reset = formatResetIn(snap)
@@ -128,14 +128,14 @@ export function AIUsageIndicator() {
128
128
  <text fg={color} selectable={false}>
129
129
  {filled}
130
130
  </text>
131
- <text fg={t.muted} selectable={false}>
131
+ <text fg={t.textMuted} selectable={false}>
132
132
  {empty}
133
133
  </text>
134
- <text fg={t.palette.ink} selectable={false}>
134
+ <text fg={t.text} selectable={false}>
135
135
  {` ${pctText}`}
136
136
  </text>
137
137
  {reset ? (
138
- <text fg={t.muted} selectable={false}>
138
+ <text fg={t.textMuted} selectable={false}>
139
139
  {` · ${reset}`}
140
140
  </text>
141
141
  ) : null}
@@ -152,7 +152,7 @@ export function AIUsageIndicator() {
152
152
  backgroundColor={bg}
153
153
  onMouseDown={openModal}
154
154
  >
155
- <text fg={t.muted} selectable={false}>
155
+ <text fg={t.textMuted} selectable={false}>
156
156
  {`${icon} ${formatTokens(snap.tokens.total)}`}
157
157
  </text>
158
158
  </box>
@@ -7,12 +7,12 @@ import {
7
7
  type ContextMenuState,
8
8
  subscribeContextMenu,
9
9
  } from '../../../context-menu/controller'
10
- import { useTokens } from '../../../theme'
10
+ import { useTheme } from '../../../theme'
11
11
 
12
12
  export function ContextMenuOverlay() {
13
13
  const [menu, setMenu] = useState<ContextMenuState | null>(null)
14
14
  const [selected, setSelected] = useState(0)
15
- const t = useTokens()
15
+ const t = useTheme()
16
16
  const terminalCols = useAppStore((s) => s.layout.terminalCols)
17
17
  const terminalRows = useAppStore((s) => s.layout.terminalRows)
18
18
 
@@ -79,8 +79,8 @@ export function ContextMenuOverlay() {
79
79
  width={width}
80
80
  flexDirection="column"
81
81
  border
82
- borderColor={t.palette.primary}
83
- backgroundColor={t.elevated}
82
+ borderColor={t.border}
83
+ backgroundColor={t.backgroundPanel}
84
84
  onMouseDown={(e) => {
85
85
  e.stopPropagation()
86
86
  }}
@@ -94,7 +94,7 @@ export function ContextMenuOverlay() {
94
94
  flexShrink={0}
95
95
  paddingLeft={1}
96
96
  paddingRight={1}
97
- backgroundColor={active ? t.selected : undefined}
97
+ backgroundColor={active ? t.backgroundElement : undefined}
98
98
  onMouseOver={() => setSelected(index)}
99
99
  onMouseDown={(e) => {
100
100
  e.preventDefault()
@@ -104,7 +104,7 @@ export function ContextMenuOverlay() {
104
104
  onSelect()
105
105
  }}
106
106
  >
107
- <text fg={active ? t.palette.ink : t.muted} selectable={false}>
107
+ <text fg={active ? t.text : t.textMuted} selectable={false}>
108
108
  {label}
109
109
  </text>
110
110
  </box>
@@ -2,11 +2,11 @@ import { parseKeyNotation } from '../../../input/keymap/key-chord'
2
2
  import { formatChord } from '../../../input/keymap/key-format'
3
3
  import { useAppStore } from '../../../state/app-store'
4
4
  import { useKeymap } from '../../keymap-context'
5
- import { useTokens } from '../../theme'
5
+ import { useTheme } from '../../theme'
6
6
  import { Surface } from '../primitives/surface'
7
7
 
8
8
  export function PendingChordOverlay() {
9
- const t = useTokens()
9
+ const t = useTheme()
10
10
  const pendingChords = useAppStore((s) => s.pendingChords)
11
11
  const modalOpen = useAppStore((s) => s.modal.type !== null)
12
12
  const config = useKeymap()
@@ -20,9 +20,9 @@ export function PendingChordOverlay() {
20
20
  <box position="absolute" bottom={modalOpen ? 10 : 2} right={1}>
21
21
  <Surface tone="elevated" paddingLeft={1} paddingRight={1}>
22
22
  <box flexDirection="row">
23
- <text fg={t.muted}>pending: </text>
24
- <text fg={t.palette.primary}>{display}</text>
25
- <text fg={t.muted}> …</text>
23
+ <text fg={t.textMuted}>pending: </text>
24
+ <text fg={t.primary}>{display}</text>
25
+ <text fg={t.textMuted}> …</text>
26
26
  </box>
27
27
  </Surface>
28
28
  </box>
@@ -1,4 +1,4 @@
1
- import { useTokens } from '../../theme'
1
+ import { useTheme } from '../../theme'
2
2
 
3
3
  interface BareInputProps {
4
4
  value: string
@@ -7,10 +7,10 @@ interface BareInputProps {
7
7
  }
8
8
 
9
9
  export function BareInput({ cursorPos, placeholder = '', value }: BareInputProps) {
10
- const t = useTokens()
11
- const fg = t.palette.ink
12
- const bg = t.bg
13
- const placeholderFg = t.faint
10
+ const t = useTheme()
11
+ const fg = t.text
12
+ const bg = t.background
13
+ const placeholderFg = t.textMuted
14
14
 
15
15
  if (!value) {
16
16
  const firstChar = placeholder.charAt(0) || ' '
@@ -1,4 +1,4 @@
1
- import { useTokens } from '../../theme'
1
+ import { useTheme } from '../../theme'
2
2
  import { Surface } from './surface'
3
3
 
4
4
  interface InputFieldProps {
@@ -9,13 +9,13 @@ interface InputFieldProps {
9
9
  }
10
10
 
11
11
  export function InputField({ active, cursorPos, placeholder, value }: InputFieldProps) {
12
- const t = useTokens()
13
- const fg = active ? t.palette.ink : t.muted
12
+ const t = useTheme()
13
+ const fg = active ? t.text : t.textMuted
14
14
  if (!active) {
15
15
  const showPlaceholder = !value && !!placeholder
16
16
  return (
17
17
  <Surface tone="input" padding={1}>
18
- <text fg={showPlaceholder ? t.faint : fg}>{showPlaceholder ? placeholder : value}</text>
18
+ <text fg={showPlaceholder ? t.textMuted : fg}>{showPlaceholder ? placeholder : value}</text>
19
19
  </Surface>
20
20
  )
21
21
  }
@@ -32,7 +32,7 @@ export function InputField({ active, cursorPos, placeholder, value }: InputField
32
32
  <Surface tone="inputActive" padding={1}>
33
33
  <text fg={fg}>
34
34
  {before}
35
- <span bg={t.palette.ink} fg={t.bg}>
35
+ <span bg={t.text} fg={t.background}>
36
36
  {cursorDisplay}
37
37
  </span>
38
38
  {trailing}
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from 'react'
2
2
 
3
- import { useTokens } from '../../theme'
3
+ import { useTheme } from '../../theme'
4
4
  import { Surface } from './surface'
5
5
 
6
6
  export type ListItemDirection = 'row' | 'column'
@@ -28,31 +28,36 @@ export function ListItem({
28
28
  title,
29
29
  trailing,
30
30
  }: ListItemProps) {
31
- const t = useTokens()
31
+ const t = useTheme()
32
32
  const isRow = direction === 'row'
33
+ const inner = (
34
+ <box flexDirection="column">
35
+ <box flexDirection="row">
36
+ {isRow ? null : (
37
+ <>
38
+ <text fg={active ? t.primary : t.textMuted}>{active ? '›' : '·'}</text>
39
+ <text> </text>
40
+ </>
41
+ )}
42
+ {leading}
43
+ {leading ? <text> </text> : null}
44
+ <box flexGrow={isRow ? 0 : 1}>{title}</box>
45
+ {trailing ? <box>{trailing}</box> : null}
46
+ </box>
47
+ {subtitle ? <box paddingLeft={2}>{subtitle}</box> : null}
48
+ </box>
49
+ )
33
50
  return (
34
51
  <box id={id} onMouseOver={onHover} onMouseDown={onClick}>
35
- <Surface
36
- tone={active ? 'selected' : 'elevated'}
37
- paddingLeft={isRow ? 2 : 1}
38
- paddingRight={isRow ? 2 : 1}
39
- >
40
- <box flexDirection="column">
41
- <box flexDirection="row">
42
- {isRow ? null : (
43
- <>
44
- <text fg={active ? t.palette.primary : t.hover}>{active ? '›' : '·'}</text>
45
- <text> </text>
46
- </>
47
- )}
48
- {leading}
49
- {leading ? <text> </text> : null}
50
- <box flexGrow={isRow ? 0 : 1}>{title}</box>
51
- {trailing ? <box>{trailing}</box> : null}
52
- </box>
53
- {subtitle ? <box paddingLeft={2}>{subtitle}</box> : null}
52
+ {active ? (
53
+ <Surface tone="selected" paddingLeft={isRow ? 2 : 1} paddingRight={isRow ? 2 : 1}>
54
+ {inner}
55
+ </Surface>
56
+ ) : (
57
+ <box paddingLeft={isRow ? 2 : 1} paddingRight={isRow ? 2 : 1}>
58
+ {inner}
54
59
  </box>
55
- </Surface>
60
+ )}
56
61
  </box>
57
62
  )
58
63
  }