@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.
- package/package.json +2 -2
- package/src/app-runtime/side-effects.ts +29 -0
- package/src/app.tsx +4 -5
- package/src/input/modes/types.ts +2 -0
- package/src/pty/terminal-snapshot.ts +7 -7
- package/src/state/reducers/git-mode-state.ts +33 -1
- package/src/state/types.ts +1 -0
- package/src/ui/components/git/diff-renderer/build-rows.ts +21 -0
- package/src/ui/components/git/diff-renderer/fold-strip.tsx +7 -7
- package/src/ui/components/git/diff-renderer/pierre-diff.tsx +3 -3
- package/src/ui/components/git/diff-renderer/split-view.tsx +18 -17
- package/src/ui/components/git/diff-renderer/stacked-view.tsx +13 -13
- package/src/ui/components/git/git-panel.tsx +42 -39
- package/src/ui/components/git/git-view.tsx +20 -20
- package/src/ui/components/layout/session-bar.tsx +13 -13
- package/src/ui/components/layout/sidebar/sidebar.tsx +26 -28
- package/src/ui/components/layout/sidebar/tab-item.tsx +21 -21
- package/src/ui/components/layout/split-layout.tsx +2 -2
- package/src/ui/components/layout/status-bar.tsx +12 -12
- package/src/ui/components/layout/terminal-pane.tsx +19 -31
- package/src/ui/components/modals/app/ai-usage-modal.tsx +21 -21
- package/src/ui/components/modals/app/help-modal.tsx +5 -6
- package/src/ui/components/modals/app/update-available-modal.tsx +3 -3
- package/src/ui/components/modals/git/git-commit-modal.tsx +17 -17
- package/src/ui/components/modals/sessions/create-session-modal.tsx +8 -8
- package/src/ui/components/modals/sessions/session-picker-modal.tsx +6 -6
- package/src/ui/components/modals/shared/form.tsx +4 -4
- package/src/ui/components/modals/shared/modal-keybinds-overlay.tsx +4 -4
- package/src/ui/components/modals/shared/modal-shell.tsx +6 -6
- package/src/ui/components/modals/shared/picker.tsx +6 -6
- package/src/ui/components/modals/snippets/snippet-picker-modal.tsx +5 -5
- package/src/ui/components/modals/tabs/new-tab-modal.tsx +6 -6
- package/src/ui/components/modals/themes/theme-picker-modal.tsx +17 -20
- package/src/ui/components/overlays/ai-usage/ai-usage-indicator.tsx +12 -12
- package/src/ui/components/overlays/context-menu/context-menu-overlay.tsx +6 -6
- package/src/ui/components/overlays/pending-chord-overlay.tsx +5 -5
- package/src/ui/components/primitives/bare-input.tsx +5 -5
- package/src/ui/components/primitives/input-field.tsx +5 -5
- package/src/ui/components/primitives/list-item.tsx +27 -22
- package/src/ui/components/primitives/surface.tsx +12 -9
- package/src/ui/filter-themes.ts +9 -8
- package/src/ui/root.tsx +5 -4
- package/src/ui/shiki.ts +8 -11
- package/src/ui/theme-store.ts +48 -118
- package/src/ui/theme.ts +8 -8
- package/src/ui/themes.ts +9 -20
|
@@ -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 {
|
|
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 =
|
|
19
|
+
const t = getCurrentTheme()
|
|
20
20
|
switch (status) {
|
|
21
21
|
case 'running':
|
|
22
|
-
return t.
|
|
22
|
+
return t.success
|
|
23
23
|
case 'disconnected':
|
|
24
|
-
return t.
|
|
24
|
+
return t.warning
|
|
25
25
|
case 'error':
|
|
26
|
-
return t.
|
|
26
|
+
return t.error
|
|
27
27
|
default:
|
|
28
|
-
return t.
|
|
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 =
|
|
41
|
+
const t = getCurrentTheme()
|
|
42
42
|
if (active) {
|
|
43
|
-
return focused ? t.
|
|
43
|
+
return focused ? t.primary : t.text
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
return inLayout ? t.
|
|
46
|
+
return inLayout ? t.textMuted : t.textMuted
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function BusyIndicator() {
|
|
50
|
-
const t =
|
|
50
|
+
const t = useTheme()
|
|
51
51
|
const frame = useBusySpinner()
|
|
52
|
-
return <text fg={t.
|
|
52
|
+
return <text fg={t.primary}>{frame} working</text>
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function WaitingIndicator() {
|
|
56
|
-
const t =
|
|
57
|
-
return <text fg={t.
|
|
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 =
|
|
61
|
+
const t = useTheme()
|
|
62
62
|
if (tab.status === 'error') {
|
|
63
|
-
return <text fg={t.
|
|
63
|
+
return <text fg={t.error}>✗ error</text>
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
if (tab.status === 'disconnected') {
|
|
67
|
-
return <text fg={t.
|
|
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.
|
|
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 =
|
|
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.
|
|
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.
|
|
148
|
+
<text fg={t.textMuted}>×</text>
|
|
149
149
|
</box>
|
|
150
150
|
) : null}
|
|
151
151
|
</box>
|
|
152
152
|
<box flexDirection="row">
|
|
153
|
-
<text fg={t.
|
|
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 {
|
|
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 =
|
|
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 {
|
|
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 =
|
|
11
|
+
const t = getCurrentTheme()
|
|
12
12
|
switch (focusMode) {
|
|
13
13
|
case 'terminal-input':
|
|
14
|
-
return t.
|
|
14
|
+
return t.primary
|
|
15
15
|
case 'modal':
|
|
16
16
|
case 'command-edit':
|
|
17
|
-
return t.
|
|
17
|
+
return t.warning
|
|
18
18
|
case 'git':
|
|
19
|
-
return t.
|
|
19
|
+
return t.success
|
|
20
20
|
case 'navigation':
|
|
21
21
|
default:
|
|
22
|
-
return t.
|
|
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 =
|
|
44
|
-
const headerBg =
|
|
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.
|
|
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.
|
|
66
|
+
<text fg={t.textMuted}>{model.right}</text>
|
|
67
67
|
<box flexDirection="row" gap={2}>
|
|
68
|
-
{model.help ? <text fg={t.
|
|
68
|
+
{model.help ? <text fg={t.textMuted}>{model.help}</text> : null}
|
|
69
69
|
<AIUsageIndicator />
|
|
70
|
-
<text fg={t.
|
|
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 {
|
|
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,
|
|
51
|
-
const t =
|
|
52
|
-
|
|
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 ??
|
|
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 =
|
|
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.
|
|
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 =
|
|
130
|
-
const editorBg =
|
|
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.
|
|
236
|
-
<text fg={t.
|
|
225
|
+
<text fg={t.textMuted}>· · ·</text>
|
|
226
|
+
<text fg={t.textMuted}> </text>
|
|
237
227
|
<box flexDirection="row">
|
|
238
|
-
<text fg={t.
|
|
239
|
-
<text fg={t.
|
|
240
|
-
<text fg={t.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
266
|
+
{tab?.errorMessage ? <text fg={t.error}>{tab.errorMessage}</text> : null}
|
|
279
267
|
</box>
|
|
280
268
|
)
|
|
281
269
|
}
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
} from '../../../../services/ai-usage/types'
|
|
9
9
|
|
|
10
10
|
import { useAIUsageStore } from '../../../../state/ai-usage-store'
|
|
11
|
-
import {
|
|
11
|
+
import { useTheme } from '../../../theme'
|
|
12
12
|
import { uiTokens } from '../../../ui-tokens'
|
|
13
13
|
import { ModalShell } from '../shared/modal-shell'
|
|
14
14
|
|
|
@@ -56,7 +56,7 @@ function paceStageIsBehind(stage: UsagePaceStage): boolean {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function AIUsageModal() {
|
|
59
|
-
const t =
|
|
59
|
+
const t = useTheme()
|
|
60
60
|
const snapshots = useAIUsageStore((s) => s.snapshots)
|
|
61
61
|
|
|
62
62
|
const tools: AIUsageTool[] = ['claude', 'codex']
|
|
@@ -72,7 +72,7 @@ export function AIUsageModal() {
|
|
|
72
72
|
listGap={1}
|
|
73
73
|
>
|
|
74
74
|
{sections.length === 0 ? (
|
|
75
|
-
<text fg={t.
|
|
75
|
+
<text fg={t.textMuted} selectable={false}>
|
|
76
76
|
no data yet — collecting…
|
|
77
77
|
</text>
|
|
78
78
|
) : (
|
|
@@ -92,20 +92,20 @@ interface ToolSectionProps {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
function ToolSection({ snap, tool }: ToolSectionProps) {
|
|
95
|
-
const t =
|
|
95
|
+
const t = useTheme()
|
|
96
96
|
const isHardError = Boolean(snap.error) && !snap.stale
|
|
97
97
|
const relative = formatRelative(snap.lastUpdated)
|
|
98
98
|
|
|
99
99
|
let body: ReactNode
|
|
100
100
|
if (isHardError) {
|
|
101
101
|
body = (
|
|
102
|
-
<text fg={t.
|
|
102
|
+
<text fg={t.error} selectable={false}>
|
|
103
103
|
{`error: ${snap.error ?? ''}`}
|
|
104
104
|
</text>
|
|
105
105
|
)
|
|
106
106
|
} else if (snap.windows.length === 0) {
|
|
107
107
|
body = (
|
|
108
|
-
<text fg={t.
|
|
108
|
+
<text fg={t.textMuted} selectable={false}>
|
|
109
109
|
no window data
|
|
110
110
|
</text>
|
|
111
111
|
)
|
|
@@ -116,16 +116,16 @@ function ToolSection({ snap, tool }: ToolSectionProps) {
|
|
|
116
116
|
return (
|
|
117
117
|
<box flexDirection="column">
|
|
118
118
|
<box flexDirection="row" justifyContent="space-between">
|
|
119
|
-
<text fg={t.
|
|
119
|
+
<text fg={t.text} selectable={false}>
|
|
120
120
|
{TOOL_TITLE[tool]}
|
|
121
121
|
</text>
|
|
122
122
|
{snap.planTier ? (
|
|
123
|
-
<text fg={t.
|
|
123
|
+
<text fg={t.textMuted} selectable={false}>
|
|
124
124
|
{snap.planTier}
|
|
125
125
|
</text>
|
|
126
126
|
) : null}
|
|
127
127
|
</box>
|
|
128
|
-
<text fg={t.
|
|
128
|
+
<text fg={t.textMuted} selectable={false}>
|
|
129
129
|
{`Updated ${relative}`}
|
|
130
130
|
</text>
|
|
131
131
|
{body}
|
|
@@ -134,14 +134,14 @@ function ToolSection({ snap, tool }: ToolSectionProps) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
function WindowRow({ window }: { window: UsageWindow }) {
|
|
137
|
-
const t =
|
|
137
|
+
const t = useTheme()
|
|
138
138
|
const percent = window.percent
|
|
139
139
|
const { empty, filled } = buildBar(percent)
|
|
140
140
|
|
|
141
|
-
let barColor = t.
|
|
141
|
+
let barColor = t.success
|
|
142
142
|
if (percent !== null) {
|
|
143
|
-
if (percent >= 85) barColor = t.
|
|
144
|
-
else if (percent >= 60) barColor = t.
|
|
143
|
+
if (percent >= 85) barColor = t.error
|
|
144
|
+
else if (percent >= 60) barColor = t.warning
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
const pctText = percent === null ? '—' : `${Math.round(percent)}% used`
|
|
@@ -149,23 +149,23 @@ function WindowRow({ window }: { window: UsageWindow }) {
|
|
|
149
149
|
|
|
150
150
|
return (
|
|
151
151
|
<box flexDirection="column" paddingTop={1}>
|
|
152
|
-
<text fg={t.
|
|
152
|
+
<text fg={t.text} selectable={false}>
|
|
153
153
|
{window.label}
|
|
154
154
|
</text>
|
|
155
155
|
<box flexDirection="row">
|
|
156
156
|
<text fg={barColor} selectable={false}>
|
|
157
157
|
{filled}
|
|
158
158
|
</text>
|
|
159
|
-
<text fg={t.
|
|
159
|
+
<text fg={t.textMuted} selectable={false}>
|
|
160
160
|
{empty}
|
|
161
161
|
</text>
|
|
162
162
|
</box>
|
|
163
163
|
<box flexDirection="row" justifyContent="space-between">
|
|
164
|
-
<text fg={t.
|
|
164
|
+
<text fg={t.textMuted} selectable={false}>
|
|
165
165
|
{pctText}
|
|
166
166
|
</text>
|
|
167
167
|
{resetText ? (
|
|
168
|
-
<text fg={t.
|
|
168
|
+
<text fg={t.textMuted} selectable={false}>
|
|
169
169
|
{resetText}
|
|
170
170
|
</text>
|
|
171
171
|
) : null}
|
|
@@ -176,10 +176,10 @@ function WindowRow({ window }: { window: UsageWindow }) {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
function PaceLine({ pace }: { pace: NonNullable<UsageWindow['pace']> }) {
|
|
179
|
-
const t =
|
|
180
|
-
let color = t.
|
|
181
|
-
if (paceStageIsBehind(pace.stage)) color = t.
|
|
182
|
-
else if (paceStageIsAhead(pace.stage)) color = t.
|
|
179
|
+
const t = useTheme()
|
|
180
|
+
let color = t.textMuted
|
|
181
|
+
if (paceStageIsBehind(pace.stage)) color = t.warning
|
|
182
|
+
else if (paceStageIsAhead(pace.stage)) color = t.success
|
|
183
183
|
|
|
184
184
|
const suffix = pace.rightText ? ` · ${pace.rightText}` : ''
|
|
185
185
|
return (
|
|
@@ -5,7 +5,7 @@ import { useLayoutEffect, useMemo } from 'react'
|
|
|
5
5
|
import { collectHelpEntries, HELP_MODE_LABELS } from '../../../../input/keymap/help-entries'
|
|
6
6
|
import { dispatchGlobal } from '../../../../state/dispatch-ref'
|
|
7
7
|
import { useKeymap } from '../../../keymap-context'
|
|
8
|
-
import {
|
|
8
|
+
import { useTheme } from '../../../theme'
|
|
9
9
|
import { uiTokens } from '../../../ui-tokens'
|
|
10
10
|
import { Picker, type PickerItem } from '../shared/picker'
|
|
11
11
|
|
|
@@ -23,7 +23,7 @@ function matchesFilter(needle: string, ...fields: (string | undefined)[]): boole
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export function HelpModal({ cursorPos, filter, scope, selectedIndex }: HelpModalProps) {
|
|
26
|
-
const t =
|
|
26
|
+
const t = useTheme()
|
|
27
27
|
const config = useKeymap()
|
|
28
28
|
const allEntries = useMemo(() => collectHelpEntries(config), [config])
|
|
29
29
|
const scoped = useMemo(
|
|
@@ -49,17 +49,16 @@ export function HelpModal({ cursorPos, filter, scope, selectedIndex }: HelpModal
|
|
|
49
49
|
}, [filtered.length])
|
|
50
50
|
|
|
51
51
|
const items: PickerItem[] = filtered.map((entry, index) => {
|
|
52
|
-
const active = index === selectedIndex
|
|
53
52
|
return {
|
|
54
53
|
group: entry.modeLabel,
|
|
55
54
|
key: `${entry.mode}::${entry.keysDisplay}::${entry.description ?? ''}::${index}`,
|
|
56
55
|
title: (
|
|
57
|
-
<text fg={
|
|
56
|
+
<text fg={t.textMuted} wrapMode="none">
|
|
58
57
|
{entry.description ?? ''}
|
|
59
58
|
</text>
|
|
60
59
|
),
|
|
61
60
|
trailing: (
|
|
62
|
-
<text fg={
|
|
61
|
+
<text fg={t.textMuted} wrapMode="none">
|
|
63
62
|
{entry.keysDisplay}
|
|
64
63
|
</text>
|
|
65
64
|
),
|
|
@@ -76,7 +75,7 @@ export function HelpModal({ cursorPos, filter, scope, selectedIndex }: HelpModal
|
|
|
76
75
|
items={items}
|
|
77
76
|
selectedIndex={selectedIndex}
|
|
78
77
|
emptyState={
|
|
79
|
-
<text fg={t.
|
|
78
|
+
<text fg={t.textMuted}>{filter ? 'No matching bindings.' : 'No bindings registered.'}</text>
|
|
80
79
|
}
|
|
81
80
|
onHover={(index) => dispatchGlobal({ index, type: 'set-modal-selection-index' })}
|
|
82
81
|
/>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useTheme } from '../../../theme'
|
|
2
2
|
import { uiTokens } from '../../../ui-tokens'
|
|
3
3
|
import { ListItem } from '../../primitives/list-item'
|
|
4
4
|
import { ModalShell } from '../shared/modal-shell'
|
|
@@ -19,7 +19,7 @@ export function UpdateAvailableModal({
|
|
|
19
19
|
latestVersion,
|
|
20
20
|
selectedIndex,
|
|
21
21
|
}: UpdateAvailableModalProps) {
|
|
22
|
-
const t =
|
|
22
|
+
const t = useTheme()
|
|
23
23
|
return (
|
|
24
24
|
<ModalShell
|
|
25
25
|
title="Update available"
|
|
@@ -36,7 +36,7 @@ export function UpdateAvailableModal({
|
|
|
36
36
|
key={option.label}
|
|
37
37
|
active={active}
|
|
38
38
|
direction="row"
|
|
39
|
-
title={<text fg={active ? t.
|
|
39
|
+
title={<text fg={active ? t.text : t.textMuted}>{option.label}</text>}
|
|
40
40
|
/>
|
|
41
41
|
)
|
|
42
42
|
})}
|
|
@@ -5,7 +5,7 @@ import type { ModeId } from '../../../../input/modes/types'
|
|
|
5
5
|
|
|
6
6
|
import { useAppStore } from '../../../../state/app-store'
|
|
7
7
|
import { dispatchGlobal, runSideEffectGlobal } from '../../../../state/dispatch-ref'
|
|
8
|
-
import {
|
|
8
|
+
import { useTheme } from '../../../theme'
|
|
9
9
|
import { uiTokens } from '../../../ui-tokens'
|
|
10
10
|
import { InputField } from '../../primitives/input-field'
|
|
11
11
|
import { ModalShell } from '../shared/modal-shell'
|
|
@@ -49,7 +49,7 @@ function pickShellTitle(stage: 'edit' | 'generating' | 'confirm'): string {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function GeneratingOverlay({ assistant, model }: { assistant?: string; model?: string }) {
|
|
52
|
-
const t =
|
|
52
|
+
const t = useTheme()
|
|
53
53
|
const frame = useSpinnerFrame(true)
|
|
54
54
|
const providerLabel = [assistant, model].filter(Boolean).join(' · ') || 'configured provider'
|
|
55
55
|
return (
|
|
@@ -60,14 +60,14 @@ function GeneratingOverlay({ assistant, model }: { assistant?: string; model?: s
|
|
|
60
60
|
paddingTop={2}
|
|
61
61
|
paddingBottom={2}
|
|
62
62
|
>
|
|
63
|
-
<text fg={t.
|
|
63
|
+
<text fg={t.primary}>🪄</text>
|
|
64
64
|
<box marginTop={1} flexDirection="row" gap={1}>
|
|
65
|
-
<text fg={t.
|
|
66
|
-
<text fg={t.
|
|
65
|
+
<text fg={t.primary}>{frame}</text>
|
|
66
|
+
<text fg={t.text}>Generating commit message</text>
|
|
67
67
|
</box>
|
|
68
|
-
<text fg={t.
|
|
68
|
+
<text fg={t.textMuted}>via {providerLabel}</text>
|
|
69
69
|
<box marginTop={1}>
|
|
70
|
-
<text fg={t.
|
|
70
|
+
<text fg={t.textMuted}>Esc to cancel</text>
|
|
71
71
|
</box>
|
|
72
72
|
</box>
|
|
73
73
|
)
|
|
@@ -82,7 +82,7 @@ export function GitCommitModal({
|
|
|
82
82
|
stage,
|
|
83
83
|
title,
|
|
84
84
|
}: GitCommitModalProps) {
|
|
85
|
-
const t =
|
|
85
|
+
const t = useTheme()
|
|
86
86
|
const titleActive = activeField === 'title'
|
|
87
87
|
const bodyActive = activeField === 'body'
|
|
88
88
|
const isConfirm = stage === 'confirm'
|
|
@@ -125,22 +125,22 @@ export function GitCommitModal({
|
|
|
125
125
|
{isConfirm ? (
|
|
126
126
|
<box flexDirection="column">
|
|
127
127
|
{stagedCount > 0 ? (
|
|
128
|
-
<text fg={t.
|
|
128
|
+
<text fg={t.warning}>
|
|
129
129
|
Commit will include the <strong>{stagedCount} staged file(s)</strong> only.
|
|
130
130
|
</text>
|
|
131
131
|
) : (
|
|
132
|
-
<text fg={t.
|
|
132
|
+
<text fg={t.warning}>
|
|
133
133
|
<strong>git add -A</strong> will stage every change before committing.
|
|
134
134
|
</text>
|
|
135
135
|
)}
|
|
136
|
-
<text fg={t.
|
|
136
|
+
<text fg={t.textMuted}>Enter to confirm · Esc to cancel · edits below still apply.</text>
|
|
137
137
|
</box>
|
|
138
138
|
) : null}
|
|
139
139
|
|
|
140
140
|
{isGenerating ? null : (
|
|
141
141
|
<>
|
|
142
142
|
<box flexDirection="column">
|
|
143
|
-
<text fg={titleActive ? t.
|
|
143
|
+
<text fg={titleActive ? t.text : t.textMuted}>Title</text>
|
|
144
144
|
<InputField
|
|
145
145
|
active={titleActive}
|
|
146
146
|
cursorPos={titleActive ? cursorPos : undefined}
|
|
@@ -149,7 +149,7 @@ export function GitCommitModal({
|
|
|
149
149
|
</box>
|
|
150
150
|
|
|
151
151
|
<box flexDirection="column">
|
|
152
|
-
<text fg={bodyActive ? t.
|
|
152
|
+
<text fg={bodyActive ? t.text : t.textMuted}>Body (optional)</text>
|
|
153
153
|
<InputField
|
|
154
154
|
active={bodyActive}
|
|
155
155
|
cursorPos={bodyActive ? cursorPos : undefined}
|
|
@@ -163,7 +163,7 @@ export function GitCommitModal({
|
|
|
163
163
|
<box flexDirection="row" gap={1} marginTop={1} alignItems="center">
|
|
164
164
|
<box
|
|
165
165
|
border
|
|
166
|
-
borderColor={t.
|
|
166
|
+
borderColor={t.border}
|
|
167
167
|
paddingLeft={1}
|
|
168
168
|
paddingRight={1}
|
|
169
169
|
flexDirection="row"
|
|
@@ -175,12 +175,12 @@ export function GitCommitModal({
|
|
|
175
175
|
onAutoCommitClick()
|
|
176
176
|
}}
|
|
177
177
|
>
|
|
178
|
-
{showBgSpinner ? <text fg={t.
|
|
179
|
-
<text fg={t.
|
|
178
|
+
{showBgSpinner ? <text fg={t.primary}>{bgSpinnerFrame}</text> : null}
|
|
179
|
+
<text fg={t.text}>
|
|
180
180
|
<strong>Auto-commit</strong>
|
|
181
181
|
</text>
|
|
182
182
|
</box>
|
|
183
|
-
<text fg={t.
|
|
183
|
+
<text fg={t.textMuted}>C-a · stages all changes (AI-suggests message if empty)</text>
|
|
184
184
|
</box>
|
|
185
185
|
)}
|
|
186
186
|
</ModalShell>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DirectoryResult } from '../../../../state/types'
|
|
2
2
|
|
|
3
3
|
import { abbreviatePath } from '../../../path-format'
|
|
4
|
-
import {
|
|
4
|
+
import { getCurrentTheme, useTheme } from '../../../theme'
|
|
5
5
|
import { uiTokens } from '../../../ui-tokens'
|
|
6
6
|
import { AutoComplete, Form, type FormOptionItem, TextField } from '../shared/form'
|
|
7
7
|
|
|
@@ -14,10 +14,10 @@ function getDirectoryResultIcon(result: DirectoryResult): string {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function getDirectoryResultColor(result: DirectoryResult): string {
|
|
17
|
-
const t =
|
|
18
|
-
if (result.type === 'worktree') return t.
|
|
19
|
-
if (result.type === 'workspace') return t.
|
|
20
|
-
return t.
|
|
17
|
+
const t = getCurrentTheme()
|
|
18
|
+
if (result.type === 'worktree') return t.warning
|
|
19
|
+
if (result.type === 'workspace') return t.info
|
|
20
|
+
return t.text
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
interface CreateSessionModalProps {
|
|
@@ -37,7 +37,7 @@ export function CreateSessionModal({
|
|
|
37
37
|
selectedIndex,
|
|
38
38
|
sessionName,
|
|
39
39
|
}: CreateSessionModalProps) {
|
|
40
|
-
const t =
|
|
40
|
+
const t = useTheme()
|
|
41
41
|
const dirActive = activeField === 'directory'
|
|
42
42
|
const nameActive = activeField === 'name'
|
|
43
43
|
|
|
@@ -46,7 +46,7 @@ export function CreateSessionModal({
|
|
|
46
46
|
key: result.path,
|
|
47
47
|
leading: <text fg={getDirectoryResultColor(result)}>{getDirectoryResultIcon(result)}</text>,
|
|
48
48
|
title: (active) => (
|
|
49
|
-
<text fg={active ? t.
|
|
49
|
+
<text fg={active ? t.text : t.textMuted}>{abbreviatePath(result.path)}</text>
|
|
50
50
|
),
|
|
51
51
|
}
|
|
52
52
|
})
|
|
@@ -67,7 +67,7 @@ export function CreateSessionModal({
|
|
|
67
67
|
selectedIndex={selectedIndex}
|
|
68
68
|
maxVisibleRows={VISIBLE_ROWS}
|
|
69
69
|
emptyState={
|
|
70
|
-
<text fg={t.
|
|
70
|
+
<text fg={t.textMuted}>
|
|
71
71
|
{directoryQuery.length > 0 ? 'No matches' : 'Type a project name to search...'}
|
|
72
72
|
</text>
|
|
73
73
|
}
|