@brimveyn/aimux 1.12.2 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/package.json +4 -3
- package/src/app-runtime/auto-commit-driver.ts +8 -4
- package/src/app-runtime/auto-commit-ref.ts +1 -1
- package/src/app-runtime/backend-attach-runtime.ts +18 -2
- package/src/app-runtime/backend-runtime-events.ts +1 -1
- package/src/app-runtime/pty-write.ts +52 -0
- package/src/app-runtime/selection-scroll.ts +2 -2
- package/src/app-runtime/session-actions.ts +16 -4
- package/src/app-runtime/side-effects.ts +598 -102
- package/src/app-runtime/snippet-actions.ts +25 -11
- package/src/app-runtime/use-auto-commit-driver.ts +7 -4
- package/src/app-runtime/use-backend-runtime.ts +3 -3
- package/src/app-runtime/use-directory-search.ts +7 -5
- package/src/app-runtime/use-mouse-handlers.ts +11 -6
- package/src/app-runtime/use-pane-size-report.ts +1 -1
- package/src/app-runtime/use-renderer-bindings.ts +143 -8
- package/src/app.tsx +25 -9
- package/src/auto-commit/headless-commands.ts +4 -6
- package/src/daemon/daemon.ts +3 -3
- package/src/daemon/runtime-paths.ts +1 -1
- package/src/daemon/session-manager.ts +1 -1
- package/src/daemon/session-registry.ts +10 -3
- package/src/diff-parser/parse-patch-files.ts +18 -18
- package/src/git/command-queue.ts +1 -1
- package/src/git/divergence.ts +46 -0
- package/src/git/git-diff.ts +12 -5
- package/src/git/git-poller.ts +33 -11
- package/src/git/git-status.ts +14 -3
- package/src/git/move-worktree.ts +96 -0
- package/src/git/use-repo-discovery.ts +2 -1
- package/src/git/worktree-divergence-poller.ts +59 -0
- package/src/git/worktree.ts +99 -0
- package/src/index.tsx +1 -1
- package/src/input/keymap/describe-bindings.ts +27 -9
- package/src/input/keymap/key-chord.ts +4 -4
- package/src/input/keymap/key-format.ts +2 -2
- package/src/input/keymap/sequence-resolver.ts +1 -1
- package/src/input/keymap/trie.ts +1 -1
- package/src/input/modes/bridge.ts +7 -0
- package/src/input/modes/transitions.ts +2 -1
- package/src/input/modes/types.ts +13 -1
- package/src/input/raw-input-handler.ts +34 -1
- package/src/input/terminal-text-extraction.ts +9 -5
- package/src/integrations/claude-syntax-overlay.ts +8 -8
- package/src/integrations/claude-theme-sync.ts +12 -12
- package/src/ipc/protocol.ts +3 -3
- package/src/platform/clipboard.ts +18 -2
- package/src/platform/worktree-deps.ts +22 -0
- package/src/platform/worktree-paths.ts +67 -0
- package/src/profile-paths.ts +3 -3
- package/src/pty/assistant-status-detection-loop.ts +2 -2
- package/src/pty/assistant-status-detector.ts +15 -6
- package/src/pty/command-registry.ts +8 -1
- package/src/pty/pty-manager.ts +1 -1
- package/src/services/ai-usage/adapters/claude.ts +5 -5
- package/src/services/ai-usage/adapters/codex.ts +8 -8
- package/src/services/ai-usage/cache.ts +2 -2
- package/src/services/ai-usage/pace.ts +3 -6
- package/src/services/ai-usage/provider.ts +1 -1
- package/src/session-backend/bootstrap.ts +2 -2
- package/src/session-backend/local-session-backend.ts +11 -11
- package/src/session-backend/remote-session-backend.ts +3 -3
- package/src/session-backend/types.ts +2 -2
- package/src/snippets/expand-variables.ts +112 -0
- package/src/snippets/run-shell-var.ts +93 -0
- package/src/snippets/trigger-detector.ts +105 -0
- package/src/state/ai-usage-store.ts +1 -1
- package/src/state/git-tree.ts +8 -4
- package/src/state/layout-tree.ts +8 -5
- package/src/state/reducers/git-mode-state.ts +44 -14
- package/src/state/reducers/git-panel-state.ts +38 -2
- package/src/state/reducers/modal-state.ts +385 -24
- package/src/state/reducers/session-state.ts +60 -1
- package/src/state/reducers/tab-state.ts +151 -85
- package/src/state/selectors.ts +6 -4
- package/src/state/session-catalog.ts +20 -5
- package/src/state/session-persistence.ts +8 -4
- package/src/state/session-worktrees.ts +227 -0
- package/src/state/snippet-catalog.ts +65 -4
- package/src/state/store.ts +1 -0
- package/src/state/toast-store.ts +95 -0
- package/src/state/types.ts +108 -16
- package/src/state/validation.ts +47 -3
- package/src/terminal-manager/manager-client.ts +15 -13
- package/src/ui/components/git/diff-renderer/fold-strip.tsx +2 -1
- package/src/ui/components/git/diff-renderer/pierre-diff.tsx +1 -1
- package/src/ui/components/git/diff-renderer/prepare-diff.ts +2 -2
- package/src/ui/components/git/diff-renderer/split-view.tsx +6 -4
- package/src/ui/components/git/diff-renderer/stacked-view.tsx +6 -4
- package/src/ui/components/git/diff-renderer/use-diff-prefetch.ts +19 -6
- package/src/ui/components/git/diff-renderer/use-diff-preparation.ts +2 -2
- package/src/ui/components/git/git-panel.tsx +17 -10
- package/src/ui/components/git/git-view.tsx +79 -19
- package/src/ui/components/git/image-diff/image-diff-view.tsx +2 -2
- package/src/ui/components/git/image-diff/terminal-image-pane.tsx +5 -4
- package/src/ui/components/git/pane/git-pane-context-menu.ts +2 -2
- package/src/ui/components/git/pane/git-pane-widget.tsx +6 -4
- package/src/ui/components/layout/session-bar.tsx +3 -3
- package/src/ui/components/layout/sidebar/sidebar.tsx +162 -36
- package/src/ui/components/layout/sidebar/tab-item.tsx +17 -1
- package/src/ui/components/layout/sidebar/use-sidebar-auto-scroll.ts +2 -2
- package/src/ui/components/layout/sidebar/use-sidebar-branch.ts +1 -1
- package/src/ui/components/layout/split-layout.tsx +1 -1
- package/src/ui/components/layout/terminal-pane.tsx +43 -40
- package/src/ui/components/modals/app/ai-usage-modal.tsx +8 -5
- package/src/ui/components/modals/app/help-modal.tsx +4 -2
- package/src/ui/components/modals/git/git-commit-modal.tsx +2 -2
- package/src/ui/components/modals/sessions/create-session-modal.tsx +5 -1
- package/src/ui/components/modals/sessions/session-picker-modal.tsx +5 -4
- package/src/ui/components/modals/shared/form.tsx +4 -5
- package/src/ui/components/modals/shared/modal-shell.tsx +3 -3
- package/src/ui/components/modals/shared/picker.tsx +28 -5
- package/src/ui/components/modals/snippets/snippet-editor-modal.tsx +10 -6
- package/src/ui/components/modals/snippets/snippet-picker-modal.tsx +25 -6
- package/src/ui/components/modals/tabs/new-tab-modal.tsx +173 -5
- package/src/ui/components/modals/worktree/worktree-move-modal.tsx +94 -0
- package/src/ui/components/overlays/ai-usage/ai-usage-indicator.tsx +3 -3
- package/src/ui/components/overlays/toast/toast-item.tsx +88 -0
- package/src/ui/components/overlays/toast/toast-position.ts +45 -0
- package/src/ui/components/overlays/toast/toast-stack.tsx +30 -0
- package/src/ui/components/overlays/toast/toast-viewport.tsx +24 -0
- package/src/ui/components/overlays/toast/use-toast-animation.ts +64 -0
- package/src/ui/components/primitives/input-field.tsx +1 -1
- package/src/ui/components/primitives/list-item.tsx +3 -3
- package/src/ui/filter-themes.ts +1 -1
- package/src/ui/root.tsx +65 -14
- package/src/ui/session-ordering.ts +2 -2
- package/src/ui/status-bar-model.ts +14 -7
- package/src/ui/terminal-graphics/capabilities.ts +1 -1
- package/src/ui/terminal-graphics/format-fallback.ts +7 -9
- package/src/ui/terminal-graphics/kitty.ts +2 -7
- package/src/update/version-check.ts +1 -1
|
@@ -29,13 +29,16 @@ interface GitPanelProps {
|
|
|
29
29
|
pathConfig?: GitPanePathConfig
|
|
30
30
|
diffCountConfig?: GitPaneDiffCountConfig
|
|
31
31
|
headOffset?: number
|
|
32
|
+
// When set, the single changed-files section is a worktree fork-point review
|
|
33
|
+
// ("vs <base>") rather than a HEAD~N history walk.
|
|
34
|
+
baseLabel?: string
|
|
32
35
|
compact?: boolean
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
function sectionTitle(section: GitFileSection, headOffset: number): string {
|
|
38
|
+
function sectionTitle(section: GitFileSection, headOffset: number, baseLabel?: string): string {
|
|
36
39
|
switch (section) {
|
|
37
40
|
case 'historical':
|
|
38
|
-
return `HEAD~${headOffset}`
|
|
41
|
+
return baseLabel != null && baseLabel !== '' ? baseLabel : `HEAD~${headOffset}`
|
|
39
42
|
case 'staged':
|
|
40
43
|
return 'Staged Changes'
|
|
41
44
|
case 'unstaged':
|
|
@@ -105,8 +108,8 @@ function renderFileLabel(
|
|
|
105
108
|
const displayPath = transform ? transform(file.path) : file.path
|
|
106
109
|
const { basename, prefix } = splitPath(displayPath)
|
|
107
110
|
const dir = stripTrailingSlash(prefix)
|
|
108
|
-
const showDir = fileListMode === 'flat' && pathConfig.enabled && dir
|
|
109
|
-
if (!file.renamedFrom) {
|
|
111
|
+
const showDir = fileListMode === 'flat' && pathConfig.enabled && dir !== ''
|
|
112
|
+
if (!(file.renamedFrom != null && file.renamedFrom !== '')) {
|
|
110
113
|
return (
|
|
111
114
|
<text selectable={false} wrapMode="none">
|
|
112
115
|
<span fg={t.text}>{basename}</span>
|
|
@@ -211,7 +214,9 @@ function renderFileRow(
|
|
|
211
214
|
// Repo disambiguation prefix: only in flat mode, only when the file came
|
|
212
215
|
// from a sub-repo (root repo files get an empty prefix).
|
|
213
216
|
const repoTag =
|
|
214
|
-
fileListMode === 'flat' && file.repoPath
|
|
217
|
+
fileListMode === 'flat' && file.repoPath != null && file.repoPath !== ''
|
|
218
|
+
? (repoPrefixes[file.repoPath] ?? '')
|
|
219
|
+
: ''
|
|
215
220
|
return (
|
|
216
221
|
<box key={row.key} flexDirection="row" gap={1} backgroundColor={bg} onMouseDown={onSelect}>
|
|
217
222
|
<box width={2} flexShrink={0} justifyContent="center">
|
|
@@ -238,7 +243,7 @@ function renderTreeSection(
|
|
|
238
243
|
section: GitFileSection,
|
|
239
244
|
title: string,
|
|
240
245
|
files: GitFileEntry[],
|
|
241
|
-
rows:
|
|
246
|
+
rows: (GitTreeFolderRow | GitTreeFileRow)[],
|
|
242
247
|
addedW: number,
|
|
243
248
|
removedW: number,
|
|
244
249
|
fileListMode: GitFileListMode,
|
|
@@ -337,6 +342,7 @@ const DEFAULT_PATH_CONFIG: GitPanePathConfig = { enabled: true }
|
|
|
337
342
|
const DEFAULT_DIFF_COUNT_CONFIG: GitPaneDiffCountConfig = { enabled: true }
|
|
338
343
|
|
|
339
344
|
export const GitPanel = memo(function GitPanel({
|
|
345
|
+
baseLabel,
|
|
340
346
|
collapsedFolders = {},
|
|
341
347
|
compact = false,
|
|
342
348
|
diffCountConfig = DEFAULT_DIFF_COUNT_CONFIG,
|
|
@@ -350,7 +356,8 @@ export const GitPanel = memo(function GitPanel({
|
|
|
350
356
|
const t = useTheme()
|
|
351
357
|
useTransparent()
|
|
352
358
|
const repoPrefixes = useAppStore((s) => s.multiRepo.prefixes)
|
|
353
|
-
const
|
|
359
|
+
const isSingleSection = headOffset > 0 || (baseLabel != null && baseLabel !== '')
|
|
360
|
+
const sectionOrder = isSingleSection ? HISTORICAL_SECTION_ORDER : BASE_SECTION_ORDER
|
|
354
361
|
const scrollRef = useRef<ScrollBoxRenderable | null>(null)
|
|
355
362
|
const tree = useMemo(
|
|
356
363
|
() => buildGitTreeRows(gitPanel.files, collapsedFolders, fileListMode, compact),
|
|
@@ -361,7 +368,7 @@ export const GitPanel = memo(function GitPanel({
|
|
|
361
368
|
[gitPanel.files]
|
|
362
369
|
)
|
|
363
370
|
|
|
364
|
-
const statusNode = renderStatus(gitPanel, !!projectPath)
|
|
371
|
+
const statusNode = renderStatus(gitPanel, !!(projectPath != null && projectPath !== ''))
|
|
365
372
|
|
|
366
373
|
const hasRemoteTracking = gitPanel.ahead > 0 || gitPanel.behind > 0
|
|
367
374
|
const toggleSection =
|
|
@@ -372,7 +379,7 @@ export const GitPanel = memo(function GitPanel({
|
|
|
372
379
|
|
|
373
380
|
useEffect(() => {
|
|
374
381
|
const scrollbox = scrollRef.current
|
|
375
|
-
if (!scrollbox || !selectedEntryKey) return
|
|
382
|
+
if (!scrollbox || !(selectedEntryKey != null && selectedEntryKey !== '')) return
|
|
376
383
|
const selectedIndex = tree.visibleRows.findIndex((row) => row.key === selectedEntryKey)
|
|
377
384
|
if (selectedIndex < 0) return
|
|
378
385
|
const viewportHeight = Math.max(1, scrollbox.viewport.height)
|
|
@@ -403,7 +410,7 @@ export const GitPanel = memo(function GitPanel({
|
|
|
403
410
|
.some((k) => (tree.sections.find((e) => e.section === k)?.files.length ?? 0) > 0)
|
|
404
411
|
return renderTreeSection(
|
|
405
412
|
key,
|
|
406
|
-
sectionTitle(key, headOffset),
|
|
413
|
+
sectionTitle(key, headOffset, baseLabel),
|
|
407
414
|
section?.files ?? [],
|
|
408
415
|
section?.rows ?? [],
|
|
409
416
|
addedW,
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { useTerminalDimensions } from '@opentui/react'
|
|
2
|
-
import { memo, useEffect, useRef } from 'react'
|
|
2
|
+
import { memo, useEffect, useRef, useState } from 'react'
|
|
3
3
|
|
|
4
4
|
import type { DiffData, GitDiffView } from '../../../state/types'
|
|
5
5
|
import type { ThemeId } from '../../themes'
|
|
6
6
|
|
|
7
7
|
import { diffHash } from '../../../git/diff-hash'
|
|
8
|
+
import { getMergeBase } from '../../../git/divergence'
|
|
8
9
|
import { fetchDiff } from '../../../git/git-diff'
|
|
9
10
|
import { useGitPanelPolling } from '../../../git/git-poller'
|
|
10
11
|
import { useRepoDiscovery } from '../../../git/use-repo-discovery'
|
|
11
12
|
import { useAppStore } from '../../../state/app-store'
|
|
12
13
|
import { dispatchGlobal } from '../../../state/dispatch-ref'
|
|
13
14
|
import { getSelectedGitFile, gitFileKey } from '../../../state/git-tree'
|
|
15
|
+
import { getActiveWorktree, getSessionProjectPath } from '../../../state/session-worktrees'
|
|
14
16
|
import { setGitDiffScroller } from '../../git-view-controls'
|
|
15
17
|
import { useTheme } from '../../theme'
|
|
16
18
|
import { PierreDiff, type PierreDiffHandle } from './diff-renderer'
|
|
@@ -64,7 +66,7 @@ const DiffStage = memo(function DiffStage({
|
|
|
64
66
|
</box>
|
|
65
67
|
)
|
|
66
68
|
}
|
|
67
|
-
if (diff.errorMessage) {
|
|
69
|
+
if (diff.errorMessage != null && diff.errorMessage !== '') {
|
|
68
70
|
return (
|
|
69
71
|
<box flexGrow={1} padding={1}>
|
|
70
72
|
<text fg={t.error}>{diff.errorMessage}</text>
|
|
@@ -77,7 +79,7 @@ const DiffStage = memo(function DiffStage({
|
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
const placeholder = placeholderText(diff)
|
|
80
|
-
if (placeholder) {
|
|
82
|
+
if (placeholder != null && placeholder !== '') {
|
|
81
83
|
return (
|
|
82
84
|
<box flexGrow={1} padding={1}>
|
|
83
85
|
<text fg={t.textMuted}>{placeholder}</text>
|
|
@@ -87,7 +89,7 @@ const DiffStage = memo(function DiffStage({
|
|
|
87
89
|
|
|
88
90
|
return (
|
|
89
91
|
<box flexDirection="column" flexGrow={1} overflow="hidden">
|
|
90
|
-
{diff.oldPath ? (
|
|
92
|
+
{diff.oldPath != null && diff.oldPath !== '' ? (
|
|
91
93
|
<box paddingLeft={1} paddingRight={1}>
|
|
92
94
|
<text fg={t.textMuted}>
|
|
93
95
|
renamed: {diff.oldPath} → {diff.path}
|
|
@@ -123,13 +125,41 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
123
125
|
const focusMode = useAppStore((s) => s.focusMode)
|
|
124
126
|
const diffRef = useRef<PierreDiffHandle | null>(null)
|
|
125
127
|
|
|
126
|
-
const currentSession =
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
const currentSession =
|
|
129
|
+
currentSessionId != null && currentSessionId !== ''
|
|
130
|
+
? sessions.find((s) => s.id === currentSessionId)
|
|
131
|
+
: undefined
|
|
132
|
+
const projectPath = getSessionProjectPath(currentSession)
|
|
133
|
+
|
|
134
|
+
// Review-vs-base: when toggled on for a worktree that records a baseRef,
|
|
135
|
+
// resolve the fork point and diff the working tree against it.
|
|
136
|
+
const activeWorktree = getActiveWorktree(currentSession)
|
|
137
|
+
const reviewBaseRef =
|
|
138
|
+
gitMode.reviewBase && activeWorktree?.baseRef != null && activeWorktree.baseRef !== ''
|
|
139
|
+
? activeWorktree.baseRef
|
|
140
|
+
: null
|
|
141
|
+
const [compareRef, setCompareRef] = useState<string | undefined>(undefined)
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (reviewBaseRef == null || projectPath == null || projectPath === '') {
|
|
144
|
+
setCompareRef(undefined)
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
let cancelled = false
|
|
148
|
+
void getMergeBase(projectPath, reviewBaseRef).then((mergeBase) => {
|
|
149
|
+
if (!cancelled) setCompareRef(mergeBase)
|
|
150
|
+
})
|
|
151
|
+
return () => {
|
|
152
|
+
cancelled = true
|
|
153
|
+
}
|
|
154
|
+
}, [reviewBaseRef, projectPath])
|
|
130
155
|
|
|
131
156
|
useRepoDiscovery(projectPath)
|
|
132
|
-
useGitPanelPolling({
|
|
157
|
+
useGitPanelPolling({
|
|
158
|
+
compareRef,
|
|
159
|
+
enabled: focusMode === 'git',
|
|
160
|
+
headOffset: gitMode.headOffset,
|
|
161
|
+
projectPath,
|
|
162
|
+
})
|
|
133
163
|
|
|
134
164
|
const fileBarWidth = Math.max(20, Math.floor(dimensions.width * gitPane.diffModeRatio))
|
|
135
165
|
|
|
@@ -140,8 +170,12 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
140
170
|
selectedEntryKey: gitMode.selectedEntryKey,
|
|
141
171
|
})
|
|
142
172
|
const selectedDiffKey = selectedFile ? gitFileKey(selectedFile) : null
|
|
143
|
-
const diff =
|
|
144
|
-
|
|
173
|
+
const diff =
|
|
174
|
+
selectedDiffKey != null && selectedDiffKey !== '' ? gitMode.diffs[selectedDiffKey] : undefined
|
|
175
|
+
const loading =
|
|
176
|
+
selectedDiffKey != null && selectedDiffKey !== ''
|
|
177
|
+
? !!(gitMode.loading[selectedDiffKey] === true)
|
|
178
|
+
: false
|
|
145
179
|
|
|
146
180
|
useEffect(() => {
|
|
147
181
|
setGitDiffScroller((delta: number) => {
|
|
@@ -160,6 +194,7 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
160
194
|
}, [])
|
|
161
195
|
|
|
162
196
|
useDiffPrefetch(gitMode.selectedEntryKey, gitPane.prefetchRadius, {
|
|
197
|
+
compareRef,
|
|
163
198
|
enabled: focusMode === 'git',
|
|
164
199
|
headOffset: gitMode.headOffset,
|
|
165
200
|
projectPath,
|
|
@@ -168,11 +203,16 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
168
203
|
|
|
169
204
|
useEffect(() => {
|
|
170
205
|
if (focusMode !== 'git') return
|
|
171
|
-
if (!selectedFile || !projectPath) return
|
|
172
|
-
if (!selectedDiffKey) return
|
|
206
|
+
if (!selectedFile || !(projectPath != null && projectPath !== '')) return
|
|
207
|
+
if (!(selectedDiffKey != null && selectedDiffKey !== '')) return
|
|
173
208
|
if (diff || loading) return
|
|
174
209
|
dispatchGlobal({ key: selectedDiffKey, loading: true, type: 'git-mode-set-loading' })
|
|
175
|
-
void fetchDiff(
|
|
210
|
+
void fetchDiff(
|
|
211
|
+
selectedFile.repoPath ?? projectPath,
|
|
212
|
+
selectedFile,
|
|
213
|
+
gitMode.headOffset,
|
|
214
|
+
compareRef
|
|
215
|
+
)
|
|
176
216
|
.then((d) =>
|
|
177
217
|
dispatchGlobal({
|
|
178
218
|
diff: d,
|
|
@@ -184,7 +224,16 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
184
224
|
.catch(() =>
|
|
185
225
|
dispatchGlobal({ key: selectedDiffKey, loading: false, type: 'git-mode-set-loading' })
|
|
186
226
|
)
|
|
187
|
-
}, [
|
|
227
|
+
}, [
|
|
228
|
+
focusMode,
|
|
229
|
+
projectPath,
|
|
230
|
+
selectedFile,
|
|
231
|
+
selectedDiffKey,
|
|
232
|
+
diff,
|
|
233
|
+
loading,
|
|
234
|
+
gitMode.headOffset,
|
|
235
|
+
compareRef,
|
|
236
|
+
])
|
|
188
237
|
|
|
189
238
|
const pendingPath = gitMode.pendingDeletePath
|
|
190
239
|
const pendingIsUntracked =
|
|
@@ -199,13 +248,13 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
199
248
|
}
|
|
200
249
|
const actionMessage = gitMode.actionMessage
|
|
201
250
|
let footerNode: React.ReactNode = null
|
|
202
|
-
if (pendingHint) {
|
|
251
|
+
if (pendingHint != null && pendingHint !== '') {
|
|
203
252
|
footerNode = (
|
|
204
253
|
<text fg={t.warning}>
|
|
205
254
|
<strong>{pendingHint}</strong>
|
|
206
255
|
</text>
|
|
207
256
|
)
|
|
208
|
-
} else if (actionMessage) {
|
|
257
|
+
} else if (actionMessage != null && actionMessage !== '') {
|
|
209
258
|
footerNode = actionMessage.split('\n').map((line, idx) => (
|
|
210
259
|
<text key={idx} fg={t.primary}>
|
|
211
260
|
{line}
|
|
@@ -213,6 +262,8 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
213
262
|
))
|
|
214
263
|
}
|
|
215
264
|
|
|
265
|
+
const baseLabel = compareRef != null && reviewBaseRef != null ? `vs ${reviewBaseRef}` : undefined
|
|
266
|
+
|
|
216
267
|
return (
|
|
217
268
|
<box flexDirection="column" flexGrow={1} overflow="hidden">
|
|
218
269
|
<box flexDirection="row" flexGrow={1}>
|
|
@@ -228,7 +279,7 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
228
279
|
<text fg={t.text}>
|
|
229
280
|
<strong>aimux · git</strong>
|
|
230
281
|
</text>
|
|
231
|
-
{gitPanel.branch ? (
|
|
282
|
+
{gitPanel.branch != null && gitPanel.branch !== '' ? (
|
|
232
283
|
<box flexDirection="row">
|
|
233
284
|
<text fg={t.text}>{'\u{e702}'} </text>
|
|
234
285
|
<text fg={t.text}>{gitPanel.branch}</text>
|
|
@@ -242,9 +293,18 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
242
293
|
<text fg={t.textMuted}>[ newer · ] older</text>
|
|
243
294
|
</box>
|
|
244
295
|
) : null}
|
|
296
|
+
{baseLabel != null ? (
|
|
297
|
+
<box flexDirection="row" gap={1}>
|
|
298
|
+
<text fg={t.primary}>
|
|
299
|
+
<strong>{baseLabel}</strong>
|
|
300
|
+
</text>
|
|
301
|
+
<text fg={t.textMuted}>b: back</text>
|
|
302
|
+
</box>
|
|
303
|
+
) : null}
|
|
245
304
|
<text fg={t.textMuted}>{'·'.repeat(Math.max(0, fileBarWidth - 2))}</text>
|
|
246
305
|
</box>
|
|
247
306
|
<GitPanel
|
|
307
|
+
baseLabel={baseLabel}
|
|
248
308
|
collapsedFolders={gitMode.collapsedFolders}
|
|
249
309
|
compact={gitPane.treeCompaction}
|
|
250
310
|
fileListMode={gitPane.fileListMode}
|
|
@@ -292,7 +352,7 @@ export const GitView = memo(function GitView({ themeId }: GitViewProps) {
|
|
|
292
352
|
/>
|
|
293
353
|
</box>
|
|
294
354
|
</box>
|
|
295
|
-
{footerNode ? (
|
|
355
|
+
{footerNode != null ? (
|
|
296
356
|
<box paddingLeft={1} paddingRight={1} backgroundColor={sidebarBg} flexDirection="column">
|
|
297
357
|
{footerNode}
|
|
298
358
|
</box>
|
|
@@ -76,14 +76,14 @@ export const ImageDiffView = memo(function ImageDiffView({ diff }: ImageDiffView
|
|
|
76
76
|
const showBoth = before && after
|
|
77
77
|
return (
|
|
78
78
|
<box flexDirection="column" flexGrow={1} overflow="hidden" backgroundColor={t.background}>
|
|
79
|
-
{diff.oldPath ? (
|
|
79
|
+
{diff.oldPath != null && diff.oldPath !== '' ? (
|
|
80
80
|
<box paddingLeft={1} paddingRight={1}>
|
|
81
81
|
<text fg={t.textMuted}>
|
|
82
82
|
renamed: {diff.oldPath} → {diff.path}
|
|
83
83
|
</text>
|
|
84
84
|
</box>
|
|
85
85
|
) : null}
|
|
86
|
-
{banner ? (
|
|
86
|
+
{banner != null && banner !== '' ? (
|
|
87
87
|
<box paddingLeft={1} paddingRight={1}>
|
|
88
88
|
<text fg={protocol === 'kitty' ? t.textMuted : t.warning}>{banner}</text>
|
|
89
89
|
</box>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { BoxRenderable } from '@opentui/core'
|
|
2
|
+
|
|
2
3
|
import { memo, useEffect, useRef, useState } from 'react'
|
|
3
4
|
|
|
4
5
|
import { convertToPng, isPng } from '../../../terminal-graphics/format-fallback'
|
|
@@ -47,7 +48,7 @@ export const TerminalImagePane = memo(function TerminalImagePane({
|
|
|
47
48
|
let cancelled = false
|
|
48
49
|
stateRef.current = { imageId: null, lastKey: null, uploaded: false }
|
|
49
50
|
setError(null)
|
|
50
|
-
|
|
51
|
+
void (async () => {
|
|
51
52
|
let pngBytes: Uint8Array | null = null
|
|
52
53
|
if (mime === 'image/png' || isPng(bytes)) {
|
|
53
54
|
pngBytes = bytes
|
|
@@ -60,7 +61,7 @@ export const TerminalImagePane = memo(function TerminalImagePane({
|
|
|
60
61
|
}
|
|
61
62
|
pngBytes = result.png
|
|
62
63
|
}
|
|
63
|
-
if (cancelled
|
|
64
|
+
if (cancelled) return
|
|
64
65
|
const id = nextImageId()
|
|
65
66
|
writeRaw(uploadPngEscape(pngBytes, id))
|
|
66
67
|
stateRef.current.imageId = id
|
|
@@ -76,7 +77,7 @@ export const TerminalImagePane = memo(function TerminalImagePane({
|
|
|
76
77
|
}
|
|
77
78
|
}, [bytes, mime])
|
|
78
79
|
|
|
79
|
-
if (error) {
|
|
80
|
+
if (error != null && error !== '') {
|
|
80
81
|
return (
|
|
81
82
|
<box flexGrow={1} alignItems="center" justifyContent="center" padding={1}>
|
|
82
83
|
<text fg={t.warning}>({error})</text>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { GitPaneMode, GitPanePosition, GitPaneState } from '../../../../state/types'
|
|
2
2
|
import type { ContextMenuItem } from '../../../context-menu/controller'
|
|
3
3
|
|
|
4
|
-
const GIT_PANE_MENU_OPTIONS:
|
|
4
|
+
const GIT_PANE_MENU_OPTIONS: {
|
|
5
5
|
label: string
|
|
6
6
|
mode: GitPaneMode
|
|
7
7
|
position: GitPanePosition
|
|
8
|
-
}
|
|
8
|
+
}[] = [
|
|
9
9
|
{ label: 'Move to top', mode: 'embedded', position: 'top' },
|
|
10
10
|
{ label: 'Move to bottom', mode: 'embedded', position: 'bottom' },
|
|
11
11
|
{ label: 'Move to left', mode: 'pane', position: 'left' },
|
|
@@ -5,6 +5,7 @@ import type { GitPanelState } from '../../../../state/types'
|
|
|
5
5
|
import { useGitPanelPolling } from '../../../../git/git-poller'
|
|
6
6
|
import { useRepoDiscovery } from '../../../../git/use-repo-discovery'
|
|
7
7
|
import { useAppStore } from '../../../../state/app-store'
|
|
8
|
+
import { getSessionProjectPath } from '../../../../state/session-worktrees'
|
|
8
9
|
import { GitPanel } from '../git-panel'
|
|
9
10
|
|
|
10
11
|
interface GitPaneWidgetProps {
|
|
@@ -20,10 +21,11 @@ export const GitPaneWidget = memo(function GitPaneWidget({ pollingEnabled }: Git
|
|
|
20
21
|
const diffCountConfig = useAppStore((s) => s.gitPane.diffCount)
|
|
21
22
|
const currentSessionId = useAppStore((s) => s.currentSessionId)
|
|
22
23
|
const sessions = useAppStore((s) => s.sessions)
|
|
23
|
-
const currentSession =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const currentSession =
|
|
25
|
+
currentSessionId != null && currentSessionId !== ''
|
|
26
|
+
? sessions.find((s) => s.id === currentSessionId)
|
|
27
|
+
: undefined
|
|
28
|
+
const projectPath = getSessionProjectPath(currentSession)
|
|
27
29
|
|
|
28
30
|
useRepoDiscovery(projectPath)
|
|
29
31
|
useGitPanelPolling({ enabled: pollingEnabled, headOffset: 0, projectPath })
|
|
@@ -61,7 +61,7 @@ export function SessionBar({ forceVisible = false }: SessionBarProps) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const handleMouseDrag = (event: OtuiMouseEvent) => {
|
|
64
|
-
if (!draggingId) return
|
|
64
|
+
if (!(draggingId != null && draggingId !== '')) return
|
|
65
65
|
const hit = findChipAtX(event.x)
|
|
66
66
|
if (hit === null) {
|
|
67
67
|
lastSwapWithRef.current = null
|
|
@@ -83,7 +83,7 @@ export function SessionBar({ forceVisible = false }: SessionBarProps) {
|
|
|
83
83
|
setDragOrder(null)
|
|
84
84
|
lastSwapWithRef.current = null
|
|
85
85
|
|
|
86
|
-
if (
|
|
86
|
+
if (source == null || source === '' || !finalOrder) return
|
|
87
87
|
|
|
88
88
|
const changed = !arraysEqual(finalOrder, baselineOrder)
|
|
89
89
|
if (changed) {
|
|
@@ -184,7 +184,7 @@ interface SessionChipProps {
|
|
|
184
184
|
onMouseDrag: (event: OtuiMouseEvent) => void
|
|
185
185
|
onMouseUp: (event: OtuiMouseEvent) => void
|
|
186
186
|
onMouseDragEnd: (event: OtuiMouseEvent) => void
|
|
187
|
-
rightClickMenu:
|
|
187
|
+
rightClickMenu: [string, () => void][]
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
function SessionChip({
|