@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 { 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 {
|
|
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 =
|
|
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.
|
|
60
|
+
<text fg={t.textMuted}>{abbreviatePath(session.projectPath)}</text>
|
|
61
61
|
) : undefined,
|
|
62
62
|
title: (
|
|
63
|
-
<text fg={active ? t.
|
|
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.
|
|
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.
|
|
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 {
|
|
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 =
|
|
84
|
+
const t = useTheme()
|
|
85
85
|
return (
|
|
86
86
|
<box flexDirection="column">
|
|
87
|
-
{children ? <text fg={active ? t.
|
|
88
|
-
{description ? <text fg={t.
|
|
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 {
|
|
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 =
|
|
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.
|
|
32
|
+
<text fg={t.textMuted}>{binding.keysDisplay}</text>
|
|
33
33
|
</box>
|
|
34
|
-
<text fg={t.
|
|
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 {
|
|
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 =
|
|
60
|
+
const t = useTheme()
|
|
61
61
|
const transparent = useTransparent()
|
|
62
|
-
const bg = transparent ? 'transparent' : t.
|
|
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.
|
|
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.
|
|
84
|
-
{subtitle ? <text fg={t.
|
|
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 {
|
|
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 =
|
|
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.
|
|
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.
|
|
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 =
|
|
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.
|
|
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 {
|
|
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 =
|
|
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.
|
|
43
|
+
subtitle: <text fg={t.textMuted}>{truncateContent(snippet.content)}</text>,
|
|
44
44
|
title: (
|
|
45
|
-
<text fg={active ? t.
|
|
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.
|
|
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 {
|
|
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 =
|
|
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.
|
|
64
|
-
{customCmd ? <text fg={t.
|
|
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.
|
|
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.
|
|
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 {
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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.
|
|
62
|
+
<text fg={t.textMuted}>
|
|
66
63
|
{filtered.length === 0 ? '' : ` ${effectiveIndex + 1} / ${filtered.length}`}
|
|
67
64
|
</text>
|
|
68
|
-
<text fg={t.
|
|
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.
|
|
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 {
|
|
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 =
|
|
51
|
-
const bg =
|
|
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.
|
|
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.
|
|
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.
|
|
107
|
+
let color = t.success
|
|
108
108
|
if (p >= 85) {
|
|
109
|
-
color = t.
|
|
109
|
+
color = t.error
|
|
110
110
|
} else if (p >= 60) {
|
|
111
|
-
color = t.
|
|
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.
|
|
131
|
+
<text fg={t.textMuted} selectable={false}>
|
|
132
132
|
{empty}
|
|
133
133
|
</text>
|
|
134
|
-
<text fg={t.
|
|
134
|
+
<text fg={t.text} selectable={false}>
|
|
135
135
|
{` ${pctText}`}
|
|
136
136
|
</text>
|
|
137
137
|
{reset ? (
|
|
138
|
-
<text fg={t.
|
|
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.
|
|
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 {
|
|
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 =
|
|
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.
|
|
83
|
-
backgroundColor={t.
|
|
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.
|
|
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.
|
|
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 {
|
|
5
|
+
import { useTheme } from '../../theme'
|
|
6
6
|
import { Surface } from '../primitives/surface'
|
|
7
7
|
|
|
8
8
|
export function PendingChordOverlay() {
|
|
9
|
-
const t =
|
|
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.
|
|
24
|
-
<text fg={t.
|
|
25
|
-
<text fg={t.
|
|
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 {
|
|
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 =
|
|
11
|
-
const fg = t.
|
|
12
|
-
const bg = t.
|
|
13
|
-
const placeholderFg = t.
|
|
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 {
|
|
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 =
|
|
13
|
-
const fg = active ? t.
|
|
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.
|
|
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.
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
36
|
-
tone={
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<box
|
|
41
|
-
|
|
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
|
-
|
|
60
|
+
)}
|
|
56
61
|
</box>
|
|
57
62
|
)
|
|
58
63
|
}
|