@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brimveyn/aimux",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.6",
|
|
4
4
|
"description": "A terminal multiplexer for AI CLIs. Run Claude, Codex, OpenCode side-by-side with tabbed navigation, split panes, and persistent sessions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"bump": "bun run scripts/bump.ts"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@brimveyn/aimux-config": "0.5.
|
|
63
|
+
"@brimveyn/aimux-config": "0.5.5",
|
|
64
64
|
"@opentui/core": "^0.1.90",
|
|
65
65
|
"@opentui/react": "^0.1.90",
|
|
66
66
|
"@xterm/headless": "^6.0.0",
|
|
@@ -548,6 +548,16 @@ export function executeSideEffect(effect: SideEffect, ctx: SideEffectContext): v
|
|
|
548
548
|
)
|
|
549
549
|
return
|
|
550
550
|
}
|
|
551
|
+
case 'git-stage-all': {
|
|
552
|
+
const paths = ctx.state.gitPanel.files.map((f) => f.path)
|
|
553
|
+
void enqueueGitOp(() => runGitActionAll(ctx, ['add', '-A'], paths))
|
|
554
|
+
return
|
|
555
|
+
}
|
|
556
|
+
case 'git-unstage-all': {
|
|
557
|
+
const paths = ctx.state.gitPanel.files.map((f) => f.path)
|
|
558
|
+
void enqueueGitOp(() => runGitActionAll(ctx, ['reset'], paths))
|
|
559
|
+
return
|
|
560
|
+
}
|
|
551
561
|
case 'git-restore': {
|
|
552
562
|
void enqueueGitOp(() => runGitAction(ctx, ['restore', '--', effect.path], effect.path))
|
|
553
563
|
return
|
|
@@ -670,6 +680,25 @@ async function runGitAction(
|
|
|
670
680
|
}
|
|
671
681
|
}
|
|
672
682
|
|
|
683
|
+
async function runGitActionAll(
|
|
684
|
+
ctx: SideEffectContext,
|
|
685
|
+
args: string[],
|
|
686
|
+
pathsToInvalidate: string[]
|
|
687
|
+
): Promise<void> {
|
|
688
|
+
const cwd = ctx.getCurrentSessionProjectPath()
|
|
689
|
+
if (!cwd) return
|
|
690
|
+
const result = await $`git -C ${cwd} ${args}`.quiet().nothrow()
|
|
691
|
+
if (result.exitCode !== 0) {
|
|
692
|
+
const stderr = result.stderr.toString().trim()
|
|
693
|
+
ctx.dispatch({ message: stderr || 'git action failed', type: 'git-mode-set-message' })
|
|
694
|
+
return
|
|
695
|
+
}
|
|
696
|
+
ctx.dispatch({ message: null, type: 'git-mode-set-message' })
|
|
697
|
+
if (pathsToInvalidate.length > 0) {
|
|
698
|
+
ctx.dispatch({ paths: pathsToInvalidate, type: 'git-mode-invalidate-diffs' })
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
673
702
|
async function runGitRm(ctx: SideEffectContext, path: string): Promise<void> {
|
|
674
703
|
const repoPath = ctx.state.gitPanel.files.find((f) => f.path === path)?.repoPath
|
|
675
704
|
const cwd = repoPath ?? ctx.getCurrentSessionProjectPath()
|
package/src/app.tsx
CHANGED
|
@@ -35,7 +35,7 @@ import { loadSnippetCatalog } from './state/snippet-catalog'
|
|
|
35
35
|
import { createInitialState } from './state/store'
|
|
36
36
|
import { KeymapContext } from './ui/keymap-context'
|
|
37
37
|
import { RootView } from './ui/root'
|
|
38
|
-
import { applyTheme, setTransparent } from './ui/theme'
|
|
38
|
+
import { applyTheme, setMode, setTransparent } from './ui/theme'
|
|
39
39
|
import { isKnownThemeId, type ThemeId } from './ui/themes'
|
|
40
40
|
import {
|
|
41
41
|
fetchLatestNpmVersion,
|
|
@@ -71,10 +71,9 @@ export function App({
|
|
|
71
71
|
const [themeId, setThemeId] = useState<ThemeId>(() => {
|
|
72
72
|
const config = loadConfig()
|
|
73
73
|
const persisted = config.themeId && isKnownThemeId(config.themeId) ? config.themeId : undefined
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
applyTheme(initial, resolvedConfig.theme?.paletteOverrides)
|
|
74
|
+
const initial: ThemeId = persisted ?? resolvedConfig.theme?.initialId ?? 'aimux'
|
|
75
|
+
applyTheme(initial)
|
|
76
|
+
if (resolvedConfig.theme?.initialMode) setMode(resolvedConfig.theme.initialMode)
|
|
78
77
|
setTransparent(config.themeTransparent ?? false)
|
|
79
78
|
return initial
|
|
80
79
|
})
|
package/src/input/modes/types.ts
CHANGED
|
@@ -55,6 +55,8 @@ export type SideEffect =
|
|
|
55
55
|
| { type: 'persist-git-tree-compaction'; enabled: boolean }
|
|
56
56
|
| { type: 'git-stage'; path: string }
|
|
57
57
|
| { type: 'git-unstage'; path: string }
|
|
58
|
+
| { type: 'git-stage-all' }
|
|
59
|
+
| { type: 'git-unstage-all' }
|
|
58
60
|
| { type: 'git-restore'; path: string }
|
|
59
61
|
| { type: 'git-rm'; path: string }
|
|
60
62
|
| { type: 'git-commit'; title: string; body: string }
|
|
@@ -2,7 +2,7 @@ import type { Terminal } from '@xterm/headless'
|
|
|
2
2
|
|
|
3
3
|
import type { TerminalLine, TerminalSnapshot, TerminalSpan } from '../state/types'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { getCurrentTheme } from '../ui/theme'
|
|
6
6
|
|
|
7
7
|
const SNAPSHOT_TAIL_LINE_COUNT = 10
|
|
8
8
|
|
|
@@ -115,17 +115,17 @@ function buildLine(
|
|
|
115
115
|
let bg = getColorHex(current.getBgColor(), bgMode)
|
|
116
116
|
|
|
117
117
|
if (current.isInverse()) {
|
|
118
|
-
const tokens =
|
|
119
|
-
const resolvedFg = fg ?? tokens.
|
|
120
|
-
const resolvedBg = bg ?? tokens.
|
|
118
|
+
const tokens = getCurrentTheme()
|
|
119
|
+
const resolvedFg = fg ?? tokens.text
|
|
120
|
+
const resolvedBg = bg ?? tokens.background
|
|
121
121
|
;[fg, bg] = [resolvedBg, resolvedFg]
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
const isCursorCell = cursorVisible && cursorColumn === column
|
|
125
125
|
if (isCursorCell) {
|
|
126
|
-
const tokens =
|
|
127
|
-
const resolvedFg = fg ?? tokens.
|
|
128
|
-
const resolvedBg = bg ?? tokens.
|
|
126
|
+
const tokens = getCurrentTheme()
|
|
127
|
+
const resolvedFg = fg ?? tokens.text
|
|
128
|
+
const resolvedBg = bg ?? tokens.background
|
|
129
129
|
;[fg, bg] = [resolvedBg, resolvedFg]
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PreparedParsedDiff } from '../../ui/components/git/diff-renderer/prepare-diff'
|
|
2
2
|
|
|
3
|
+
import { collectFoldables } from '../../ui/components/git/diff-renderer/build-rows'
|
|
3
4
|
import {
|
|
4
5
|
getSelectedGitRow,
|
|
5
6
|
gitFileKey,
|
|
@@ -7,6 +8,14 @@ import {
|
|
|
7
8
|
moveGitSelection,
|
|
8
9
|
reconcileSelectedGitEntryKey,
|
|
9
10
|
} from '../git-tree'
|
|
11
|
+
import {
|
|
12
|
+
type AppAction,
|
|
13
|
+
type AppState,
|
|
14
|
+
type FoldState,
|
|
15
|
+
getParsedPayload,
|
|
16
|
+
type GitFileEntry,
|
|
17
|
+
type GitModeState,
|
|
18
|
+
} from '../types'
|
|
10
19
|
import { clearDiffCacheForPath, clearDiffCacheForPaths } from './diff-cache'
|
|
11
20
|
import { sortFilesBySection } from './git-panel-state'
|
|
12
21
|
|
|
@@ -439,6 +448,29 @@ export function reduceGitModeState(state: AppState, action: AppAction): AppState
|
|
|
439
448
|
const bottom = Math.max(0, action.bottom)
|
|
440
449
|
return applyFold(state, action.key, action.foldId, { bottom, top })
|
|
441
450
|
}
|
|
451
|
+
case 'git-mode-fold-toggle-all': {
|
|
452
|
+
const parsed = getParsedPayload<PreparedParsedDiff>(state.gitMode.parsedFiles[action.key])
|
|
453
|
+
const file = parsed?.file
|
|
454
|
+
if (!file || !Array.isArray(file.hunks)) return state
|
|
455
|
+
const foldables = collectFoldables(file)
|
|
456
|
+
if (foldables.length === 0) return state
|
|
457
|
+
const current = state.gitMode.folds[action.key] ?? {}
|
|
458
|
+
const allExpanded = foldables.every(({ foldId, middle }) => {
|
|
459
|
+
const fs = current[foldId] ?? { bottom: 0, top: 0 }
|
|
460
|
+
return fs.top + fs.bottom >= middle
|
|
461
|
+
})
|
|
462
|
+
const nextFolds = { ...state.gitMode.folds }
|
|
463
|
+
if (allExpanded) {
|
|
464
|
+
delete nextFolds[action.key]
|
|
465
|
+
} else {
|
|
466
|
+
const perPath: Record<string, FoldState> = {}
|
|
467
|
+
for (const { foldId, middle } of foldables) {
|
|
468
|
+
perPath[foldId] = { bottom: 0, top: middle }
|
|
469
|
+
}
|
|
470
|
+
nextFolds[action.key] = perPath
|
|
471
|
+
}
|
|
472
|
+
return { ...state, gitMode: { ...state.gitMode, folds: nextFolds } }
|
|
473
|
+
}
|
|
442
474
|
case 'git-mode-optimistic-move': {
|
|
443
475
|
const currentKey = state.gitMode.selectedEntryKey
|
|
444
476
|
const files = state.gitPanel.files
|
package/src/state/types.ts
CHANGED
|
@@ -650,6 +650,7 @@ export type GitModeAction =
|
|
|
650
650
|
delta: number
|
|
651
651
|
}
|
|
652
652
|
| { type: 'git-mode-fold-set'; key: string; foldId: string; top: number; bottom: number }
|
|
653
|
+
| { type: 'git-mode-fold-toggle-all'; key: string }
|
|
653
654
|
| {
|
|
654
655
|
type: 'git-mode-optimistic-move'
|
|
655
656
|
path: string
|
|
@@ -181,6 +181,27 @@ function contextNeighbors(
|
|
|
181
181
|
return { hasChangeAfter, hasChangeBefore }
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
export interface Foldable {
|
|
185
|
+
foldId: string
|
|
186
|
+
middle: number
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function collectFoldables(file: FileDiffMetadata): Foldable[] {
|
|
190
|
+
const result: Foldable[] = []
|
|
191
|
+
for (const [hIdx, hunk] of file.hunks.entries()) {
|
|
192
|
+
for (const [cIdx, content] of hunk.hunkContent.entries()) {
|
|
193
|
+
if (content.type !== 'context') continue
|
|
194
|
+
const { hasChangeAfter, hasChangeBefore } = contextNeighbors(hunk.hunkContent, cIdx)
|
|
195
|
+
const preKeep = hasChangeBefore ? Math.min(KEEP_CONTEXT, content.lines) : 0
|
|
196
|
+
const postKeep = hasChangeAfter ? Math.min(KEEP_CONTEXT, content.lines - preKeep) : 0
|
|
197
|
+
const middle = content.lines - preKeep - postKeep
|
|
198
|
+
if (middle < MIN_FOLD_SIZE) continue
|
|
199
|
+
result.push({ foldId: `${hIdx}:${cIdx}`, middle })
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return result
|
|
203
|
+
}
|
|
204
|
+
|
|
184
205
|
export function buildDiffSegments(file: FileDiffMetadata, folds: FoldMap = {}): DiffSegmentBuild {
|
|
185
206
|
const segments: DiffSegment[] = []
|
|
186
207
|
let offset = 0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useTheme } from '../../../theme'
|
|
2
2
|
import { FOLD_STEP, type FoldInfo } from './build-rows'
|
|
3
3
|
import { type FoldDispatch } from './pierre-diff'
|
|
4
4
|
|
|
@@ -8,11 +8,11 @@ interface Props {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
function Button({ label, onPress }: { label: string; onPress: () => void }) {
|
|
11
|
-
const t =
|
|
12
|
-
const bg =
|
|
11
|
+
const t = useTheme()
|
|
12
|
+
const bg = t.diffContextBg
|
|
13
13
|
return (
|
|
14
14
|
<box paddingLeft={1} paddingRight={1} backgroundColor={bg} onMouseDown={onPress}>
|
|
15
|
-
<text fg={t.
|
|
15
|
+
<text fg={t.primary}>{label}</text>
|
|
16
16
|
</box>
|
|
17
17
|
)
|
|
18
18
|
}
|
|
@@ -22,8 +22,8 @@ function Spacer() {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export function FoldStrip({ dispatch, fold }: Props) {
|
|
25
|
-
const t =
|
|
26
|
-
const headerBg =
|
|
25
|
+
const t = useTheme()
|
|
26
|
+
const headerBg = t.diffContextBg
|
|
27
27
|
const { bottomExpanded, foldId, hidden, topExpanded, total } = fold
|
|
28
28
|
const stepUp = Math.min(FOLD_STEP, hidden)
|
|
29
29
|
const stepDown = Math.min(FOLD_STEP, hidden)
|
|
@@ -71,7 +71,7 @@ export function FoldStrip({ dispatch, fold }: Props) {
|
|
|
71
71
|
|
|
72
72
|
return (
|
|
73
73
|
<box flexDirection="row" backgroundColor={headerBg} paddingLeft={1} paddingRight={1}>
|
|
74
|
-
<text fg={t.
|
|
74
|
+
<text fg={t.textMuted}>{`⋯ ${hidden} hidden `}</text>
|
|
75
75
|
{controls}
|
|
76
76
|
</box>
|
|
77
77
|
)
|
|
@@ -8,7 +8,7 @@ import type { ThemeId } from '../../../themes'
|
|
|
8
8
|
import { useAppStore } from '../../../../state/app-store'
|
|
9
9
|
import { dispatchGlobal } from '../../../../state/dispatch-ref'
|
|
10
10
|
import { type FoldState } from '../../../../state/types'
|
|
11
|
-
import {
|
|
11
|
+
import { useTheme } from '../../../theme'
|
|
12
12
|
import { buildDiffSegments, firstChangeSegmentOffset, gutterWidth } from './build-rows'
|
|
13
13
|
import { SplitView, type SplitViewHandle } from './split-view'
|
|
14
14
|
import { StackedView, type StackedViewHandle } from './stacked-view'
|
|
@@ -45,7 +45,7 @@ export const PierreDiff = forwardRef<PierreDiffHandle, Props>(function PierreDif
|
|
|
45
45
|
{ cacheKey, diff, path, themeId, view },
|
|
46
46
|
ref
|
|
47
47
|
) {
|
|
48
|
-
const t =
|
|
48
|
+
const t = useTheme()
|
|
49
49
|
const preparation = useDiffPreparation(cacheKey, diff, path, themeId)
|
|
50
50
|
const file = preparation.file ?? undefined
|
|
51
51
|
const highlights: DiffHighlights = preparation.highlights
|
|
@@ -123,7 +123,7 @@ export const PierreDiff = forwardRef<PierreDiffHandle, Props>(function PierreDif
|
|
|
123
123
|
if (!file) {
|
|
124
124
|
return (
|
|
125
125
|
<box flexGrow={1} padding={1}>
|
|
126
|
-
<text fg={t.
|
|
126
|
+
<text fg={t.textMuted}>
|
|
127
127
|
{preparation.preparing ? 'Preparing diff…' : '(could not parse diff)'}
|
|
128
128
|
</text>
|
|
129
129
|
</box>
|
|
@@ -20,7 +20,7 @@ import type { DiffHighlights, FoldDispatch } from './pierre-diff'
|
|
|
20
20
|
|
|
21
21
|
import { getScrollViewportDelta } from '../../../../app-runtime/terminal-mouse-adapter'
|
|
22
22
|
import { scrollGitDiff } from '../../../git-view-controls'
|
|
23
|
-
import {
|
|
23
|
+
import { useTheme, useTransparent } from '../../../theme'
|
|
24
24
|
import {
|
|
25
25
|
type DiffSegment,
|
|
26
26
|
estimatedSegmentHeight,
|
|
@@ -67,7 +67,8 @@ export const SplitView = forwardRef<SplitViewHandle, Props>(function SplitView(
|
|
|
67
67
|
{ contentWidth, file, foldDispatch, highlights, requestSegmentHighlights, segments },
|
|
68
68
|
ref
|
|
69
69
|
) {
|
|
70
|
-
const
|
|
70
|
+
const t = useTheme()
|
|
71
|
+
const separatorBg = t.background
|
|
71
72
|
const leftRef = useRef<ScrollBoxRenderable | null>(null)
|
|
72
73
|
const rightRef = useRef<ScrollBoxRenderable | null>(null)
|
|
73
74
|
const measuredHeightsRef = useRef<Record<string, number>>({})
|
|
@@ -224,12 +225,12 @@ function SideRow({
|
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
function HunkHeaderRow({ row }: { row: Extract<SplitRowOrHeader, { type: 'hunk-header' }> }) {
|
|
227
|
-
const t =
|
|
228
|
-
const headerBg =
|
|
228
|
+
const t = useTheme()
|
|
229
|
+
const headerBg = t.diffContextBg
|
|
229
230
|
return (
|
|
230
231
|
<box flexDirection="row" backgroundColor={headerBg} paddingLeft={1} paddingRight={1}>
|
|
231
|
-
<text fg={t.
|
|
232
|
-
{row.context ? <text fg={t.
|
|
232
|
+
<text fg={t.text}>{row.spec}</text>
|
|
233
|
+
{row.context ? <text fg={t.textMuted}> {row.context}</text> : null}
|
|
233
234
|
</box>
|
|
234
235
|
)
|
|
235
236
|
}
|
|
@@ -245,29 +246,29 @@ function HalfRow({
|
|
|
245
246
|
height: number
|
|
246
247
|
tokens: ThemedToken[][]
|
|
247
248
|
}) {
|
|
248
|
-
const t =
|
|
249
|
-
const headerBg =
|
|
249
|
+
const t = useTheme()
|
|
250
|
+
const headerBg = t.diffContextBg
|
|
250
251
|
const transparent = useTransparent()
|
|
251
252
|
if (cell.type === 'filler') {
|
|
252
253
|
return <box backgroundColor={headerBg} height={height} />
|
|
253
254
|
}
|
|
254
255
|
let bg: string | undefined
|
|
255
256
|
let sign = ' '
|
|
256
|
-
let signColor = t.
|
|
257
|
+
let signColor = t.textMuted
|
|
257
258
|
if (cell.type === 'addition') {
|
|
258
|
-
bg = t.
|
|
259
|
+
bg = t.diffAddedBg
|
|
259
260
|
sign = '+'
|
|
260
|
-
signColor = t.
|
|
261
|
+
signColor = t.diffAdded
|
|
261
262
|
} else if (cell.type === 'deletion') {
|
|
262
|
-
bg = t.
|
|
263
|
+
bg = t.diffRemovedBg
|
|
263
264
|
sign = '-'
|
|
264
|
-
signColor = t.
|
|
265
|
+
signColor = t.diffRemoved
|
|
265
266
|
}
|
|
266
267
|
const num = String(cell.lineNumber).padStart(gw, ' ')
|
|
267
268
|
const lineTokens = tokens[cell.lineIdx]
|
|
268
269
|
return (
|
|
269
270
|
<box flexDirection="row" backgroundColor={transparent ? undefined : bg} height={height}>
|
|
270
|
-
<text fg={t.
|
|
271
|
+
<text fg={t.textMuted}>{` ${num} `}</text>
|
|
271
272
|
<text fg={signColor}>{`${sign} `}</text>
|
|
272
273
|
<LineContent content={cell.content} tokens={lineTokens} />
|
|
273
274
|
</box>
|
|
@@ -275,9 +276,9 @@ function HalfRow({
|
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
function LineContent({ content, tokens }: { content: string; tokens: ThemedToken[] | undefined }) {
|
|
278
|
-
const t =
|
|
279
|
+
const t = useTheme()
|
|
279
280
|
if (!tokens || tokens.length === 0) {
|
|
280
|
-
return <text fg={t.
|
|
281
|
+
return <text fg={t.text}>{content}</text>
|
|
281
282
|
}
|
|
282
283
|
return (
|
|
283
284
|
<text>
|
|
@@ -288,7 +289,7 @@ function LineContent({ content, tokens }: { content: string; tokens: ThemedToken
|
|
|
288
289
|
if (s.italic) attributes |= TextAttributes.ITALIC
|
|
289
290
|
if (s.underline) attributes |= TextAttributes.UNDERLINE
|
|
290
291
|
return (
|
|
291
|
-
<span key={i} fg={s.fg ?? t.
|
|
292
|
+
<span key={i} fg={s.fg ?? t.text} attributes={attributes}>
|
|
292
293
|
{s.text}
|
|
293
294
|
</span>
|
|
294
295
|
)
|
|
@@ -20,7 +20,7 @@ import type { DiffHighlights, FoldDispatch } from './pierre-diff'
|
|
|
20
20
|
|
|
21
21
|
import { getScrollViewportDelta } from '../../../../app-runtime/terminal-mouse-adapter'
|
|
22
22
|
import { scrollGitDiff } from '../../../git-view-controls'
|
|
23
|
-
import {
|
|
23
|
+
import { useTheme, useTransparent } from '../../../theme'
|
|
24
24
|
import {
|
|
25
25
|
type DiffSegment,
|
|
26
26
|
estimatedSegmentHeight,
|
|
@@ -179,14 +179,14 @@ function UnifiedRowRender({
|
|
|
179
179
|
highlights: DiffHighlights
|
|
180
180
|
row: UnifiedRowOrHeader
|
|
181
181
|
}) {
|
|
182
|
-
const t =
|
|
183
|
-
const headerBg =
|
|
182
|
+
const t = useTheme()
|
|
183
|
+
const headerBg = t.diffContextBg
|
|
184
184
|
const transparent = useTransparent()
|
|
185
185
|
if (row.type === 'hunk-header') {
|
|
186
186
|
return (
|
|
187
187
|
<box flexDirection="row" backgroundColor={headerBg} paddingLeft={1} paddingRight={1}>
|
|
188
|
-
<text fg={t.
|
|
189
|
-
{row.context ? <text fg={t.
|
|
188
|
+
<text fg={t.text}>{row.spec}</text>
|
|
189
|
+
{row.context ? <text fg={t.textMuted}> {row.context}</text> : null}
|
|
190
190
|
</box>
|
|
191
191
|
)
|
|
192
192
|
}
|
|
@@ -199,21 +199,21 @@ function UnifiedRowRender({
|
|
|
199
199
|
const tokens = highlights.add[row.lineIdx]
|
|
200
200
|
return (
|
|
201
201
|
<box flexDirection="row" height={row.height}>
|
|
202
|
-
<text fg={t.
|
|
203
|
-
<text fg={t.
|
|
202
|
+
<text fg={t.textMuted}>{` ${pad(row.delLineNumber)} ${pad(row.addLineNumber)} `}</text>
|
|
203
|
+
<text fg={t.text}> </text>
|
|
204
204
|
<LineContent content={row.content} tokens={tokens} />
|
|
205
205
|
</box>
|
|
206
206
|
)
|
|
207
207
|
}
|
|
208
|
-
const bg = row.type === 'addition' ? t.
|
|
208
|
+
const bg = row.type === 'addition' ? t.diffAddedBg : t.diffRemovedBg
|
|
209
209
|
const sign = row.type === 'addition' ? '+' : '-'
|
|
210
|
-
const signColor = row.type === 'addition' ? t.
|
|
210
|
+
const signColor = row.type === 'addition' ? t.diffAdded : t.diffRemoved
|
|
211
211
|
const delNum = row.type === 'deletion' ? row.lineNumber : undefined
|
|
212
212
|
const addNum = row.type === 'addition' ? row.lineNumber : undefined
|
|
213
213
|
const tokens = row.type === 'addition' ? highlights.add[row.lineIdx] : highlights.del[row.lineIdx]
|
|
214
214
|
return (
|
|
215
215
|
<box flexDirection="row" backgroundColor={transparent ? undefined : bg} height={row.height}>
|
|
216
|
-
<text fg={t.
|
|
216
|
+
<text fg={t.textMuted}>{` ${pad(delNum)} ${pad(addNum)} `}</text>
|
|
217
217
|
<text fg={signColor}>{`${sign} `}</text>
|
|
218
218
|
<LineContent content={row.content} tokens={tokens} />
|
|
219
219
|
</box>
|
|
@@ -221,9 +221,9 @@ function UnifiedRowRender({
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
function LineContent({ content, tokens }: { content: string; tokens: ThemedToken[] | undefined }) {
|
|
224
|
-
const t =
|
|
224
|
+
const t = useTheme()
|
|
225
225
|
if (!tokens || tokens.length === 0) {
|
|
226
|
-
return <text fg={t.
|
|
226
|
+
return <text fg={t.text}>{content}</text>
|
|
227
227
|
}
|
|
228
228
|
return (
|
|
229
229
|
<text>
|
|
@@ -234,7 +234,7 @@ function LineContent({ content, tokens }: { content: string; tokens: ThemedToken
|
|
|
234
234
|
if (s.italic) attributes |= TextAttributes.ITALIC
|
|
235
235
|
if (s.underline) attributes |= TextAttributes.UNDERLINE
|
|
236
236
|
return (
|
|
237
|
-
<span key={i} fg={s.fg ?? t.
|
|
237
|
+
<span key={i} fg={s.fg ?? t.text} attributes={attributes}>
|
|
238
238
|
{s.text}
|
|
239
239
|
</span>
|
|
240
240
|
)
|