@brimveyn/aimux 1.20.4 → 1.22.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 +27 -25
- package/package.json +2 -2
- package/src/app-runtime/auto-commit-driver.ts +22 -27
- package/src/app-runtime/backend-attach-runtime.ts +43 -36
- package/src/app-runtime/backend-runtime-events.ts +107 -88
- package/src/app-runtime/editor-actions.ts +201 -0
- package/src/app-runtime/git-actions.ts +197 -0
- package/src/app-runtime/measure-ref.ts +19 -0
- package/src/app-runtime/navigation-actions.ts +213 -0
- package/src/app-runtime/project-actions.ts +188 -0
- package/src/app-runtime/prompt-injection.ts +123 -0
- package/src/app-runtime/selection.ts +29 -0
- package/src/app-runtime/settings-actions.ts +156 -0
- package/src/app-runtime/setup-actions.ts +237 -0
- package/src/app-runtime/side-effect-context.ts +33 -0
- package/src/app-runtime/side-effects.ts +305 -1589
- package/src/app-runtime/snippet-actions.ts +3 -2
- package/src/app-runtime/split-drag-controller.ts +4 -8
- package/src/app-runtime/tab-actions.ts +218 -0
- package/src/app-runtime/tab-runtime-timeouts.ts +1 -1
- package/src/app-runtime/use-auto-commit-driver.ts +14 -13
- package/src/app-runtime/use-backend-runtime.ts +12 -11
- package/src/app-runtime/use-directory-search.ts +5 -4
- package/src/app-runtime/use-mouse-handlers.ts +28 -42
- package/src/app-runtime/{use-workspace-autosave.ts → use-project-autosave.ts} +4 -4
- package/src/app-runtime/use-renderer-bindings.ts +2 -1
- package/src/app-runtime/use-setup-runner.ts +96 -0
- package/src/app-runtime/use-terminal-resize.ts +28 -33
- package/src/app-runtime/workspace-actions.ts +366 -0
- package/src/app-runtime/workspace-activity.ts +138 -0
- package/src/app-runtime/workspace-naming.ts +90 -0
- package/src/app.tsx +102 -85
- package/src/assets/sounds/bell.wav +0 -0
- package/src/assets/sounds/ding.wav +0 -0
- package/src/assets/sounds/plane.wav +0 -0
- package/src/cli/client/daemon-client.ts +2 -2
- package/src/cli/client/project-resolver.ts +112 -0
- package/src/cli/commands/project/close.ts +32 -0
- package/src/cli/commands/project/create.ts +93 -0
- package/src/cli/commands/project/list.ts +25 -0
- package/src/cli/commands/project/show.ts +32 -0
- package/src/cli/commands/{workspace → project}/switch.ts +19 -19
- package/src/cli/commands/tab/await.ts +2 -2
- package/src/cli/commands/tab/close.ts +3 -3
- package/src/cli/commands/tab/create.ts +74 -73
- package/src/cli/commands/tab/focus.ts +3 -3
- package/src/cli/commands/tab/list.ts +6 -6
- package/src/cli/commands/tab/run.ts +2 -2
- package/src/cli/commands/tab/send.ts +2 -2
- package/src/cli/commands/tab/snapshot.ts +2 -2
- package/src/cli/commands/tab/tail.ts +2 -2
- package/src/cli/commands/tab/wait.ts +2 -2
- package/src/cli/commands/worker/await.ts +3 -3
- package/src/cli/commands/worker/doctor.ts +30 -30
- package/src/cli/commands/worker/list.ts +15 -15
- package/src/cli/commands/worker/prompt.ts +3 -3
- package/src/cli/commands/worker/run.ts +23 -23
- package/src/cli/commands/worker/shared.ts +53 -59
- package/src/cli/commands/worker/stop.ts +29 -29
- package/src/cli/commands/worker/submit.ts +3 -3
- package/src/cli/commands/{worktree → workspace}/create-core.ts +26 -26
- package/src/cli/commands/workspace/create.ts +30 -68
- package/src/cli/commands/workspace/list.ts +52 -9
- package/src/cli/commands/workspace/remove.ts +81 -0
- package/src/cli/completion/plan.ts +2 -2
- package/src/cli/completion/sources.ts +18 -18
- package/src/cli/context.ts +13 -13
- package/src/cli/flags.ts +30 -10
- package/src/cli/index.ts +26 -16
- package/src/cli/output.ts +1 -1
- package/src/cli/registry.ts +12 -12
- package/src/config/loader.ts +16 -5
- package/src/config.ts +149 -101
- package/src/daemon/catalog-writer.ts +67 -67
- package/src/daemon/daemon.ts +205 -201
- package/src/daemon/session-manager.ts +39 -39
- package/src/daemon/session-registry.ts +14 -14
- package/src/git/divergence.ts +33 -2
- package/src/git/git-poller.ts +1 -1
- package/src/git/git-status.ts +2 -2
- package/src/git/{move-worktree.ts → move-workspace.ts} +5 -5
- package/src/git/pr-merge.ts +64 -0
- package/src/git/pr-status-poller.ts +57 -0
- package/src/git/pr-status.ts +227 -0
- package/src/git/repo-discovery.ts +1 -1
- package/src/git/use-repo-discovery.ts +1 -1
- package/src/git/workspace-branch-poller.ts +54 -0
- package/src/git/workspace-divergence-poller.ts +65 -0
- package/src/git/worktree.ts +29 -0
- package/src/index.tsx +11 -6
- package/src/input/keymap/help-entries.ts +7 -5
- package/src/input/modes/bridge.ts +13 -12
- package/src/input/modes/handlers/shared.ts +1 -1
- package/src/input/modes/transitions.ts +34 -21
- package/src/input/modes/types.ts +48 -31
- package/src/ipc/manager-protocol.ts +44 -44
- package/src/ipc/protocol.ts +192 -166
- package/src/platform/open-url.ts +39 -0
- package/src/platform/play-sound.ts +184 -0
- package/src/platform/project-search.ts +8 -8
- package/src/platform/worktree-paths.ts +8 -6
- package/src/pty/assistant-question-extractor.ts +1 -1
- package/src/pty/assistant-status-detection-loop.ts +79 -55
- package/src/pty/assistant-status-detector.ts +40 -15
- package/src/pty/command-registry.ts +81 -1
- package/src/restart-terminal-manager.ts +1 -1
- package/src/services/ai-usage/provider.ts +9 -2
- package/src/services/ai-usage/spawn.ts +3 -1
- package/src/session-backend/bootstrap.ts +4 -4
- package/src/session-backend/local-session-backend.ts +78 -75
- package/src/session-backend/remote-session-backend.ts +64 -52
- package/src/session-backend/types.ts +43 -32
- package/src/settings/live.ts +90 -0
- package/src/settings/search.ts +43 -0
- package/src/settings/sections/about.ts +49 -0
- package/src/settings/sections/appearance.ts +54 -0
- package/src/settings/sections/automation.ts +105 -0
- package/src/settings/sections/commands.ts +72 -0
- package/src/settings/sections/editor.ts +56 -0
- package/src/settings/sections/experimental.ts +57 -0
- package/src/settings/sections/git.ts +112 -0
- package/src/settings/sections/index.ts +95 -0
- package/src/settings/sections/integrations.ts +23 -0
- package/src/settings/sections/layout.ts +72 -0
- package/src/settings/sections/notifications.ts +99 -0
- package/src/settings/sections/setup.ts +54 -0
- package/src/settings/sections/status-bar.ts +63 -0
- package/src/settings/settings-store.ts +207 -0
- package/src/settings/types.ts +108 -0
- package/src/snippets/run-shell-var.ts +48 -22
- package/src/state/actions.ts +342 -0
- package/src/state/app-store.ts +2 -1
- package/src/state/bars.ts +75 -0
- package/src/state/dispatch-ref.ts +1 -1
- package/src/state/git-pane-sizing.ts +0 -9
- package/src/state/layout-resize.ts +2 -2
- package/src/state/pr-status-store.ts +39 -0
- package/src/state/project-catalog.ts +275 -0
- package/src/state/project-data.ts +143 -0
- package/src/state/{session-persistence.ts → project-persistence.ts} +77 -73
- package/src/state/project-save.ts +46 -0
- package/src/state/project-workspaces.ts +392 -0
- package/src/state/reducers/auto-commit-state.ts +11 -10
- package/src/state/reducers/git-commit-modal-state.ts +122 -0
- package/src/state/reducers/git-mode-state.ts +1 -1
- package/src/state/reducers/git-panel-state.ts +10 -53
- package/src/state/reducers/modal-state.ts +290 -681
- package/src/state/reducers/multi-repo-state.ts +3 -6
- package/src/state/reducers/project-state.ts +312 -0
- package/src/state/reducers/settings-state.ts +80 -0
- package/src/state/reducers/tab-state.ts +107 -60
- package/src/state/reducers/ui-state.ts +86 -15
- package/src/state/selectors.ts +49 -37
- package/src/state/store.ts +83 -57
- package/src/state/types.ts +261 -417
- package/src/state/validation.ts +14 -12
- package/src/terminal-manager/manager-client.ts +32 -32
- package/src/terminal-manager/terminal-manager.ts +24 -24
- package/src/ui/components/git/diff-renderer/pierre-diff.tsx +2 -1
- package/src/ui/components/git/git-panel.tsx +2 -2
- package/src/ui/components/git/git-view.tsx +12 -12
- package/src/ui/components/git/image-diff/image-diff-view.tsx +1 -1
- package/src/ui/components/git/pane/git-pane-header.tsx +105 -39
- package/src/ui/components/git/pane/git-pane-widget.tsx +37 -23
- package/src/ui/components/git/pane/pr-checks-panel.tsx +197 -0
- package/src/ui/components/git/pane/pr-state-row.tsx +103 -0
- package/src/ui/components/layout/bar.tsx +191 -0
- package/src/ui/components/layout/sidebar/project-list.tsx +444 -0
- package/src/ui/components/layout/sidebar/tab-item.tsx +27 -12
- package/src/ui/components/layout/sidebar/use-sidebar-auto-scroll.ts +1 -1
- package/src/ui/components/layout/sidebar/workspace-row.tsx +253 -0
- package/src/ui/components/layout/status-bar.tsx +6 -2
- package/src/ui/components/layout/terminal-pane.tsx +34 -29
- package/src/ui/components/layout/top-tab-bar.tsx +48 -37
- package/src/ui/components/modals/git/git-commit-modal.tsx +8 -8
- package/src/ui/components/modals/{sessions/create-session-modal.tsx → projects/create-project-modal.tsx} +12 -12
- package/src/ui/components/modals/{sessions/session-name-modal.tsx → projects/project-name-modal.tsx} +2 -2
- package/src/ui/components/modals/{sessions/session-picker-modal.tsx → projects/project-picker-modal.tsx} +33 -35
- package/src/ui/components/modals/settings/settings-search-modal.tsx +70 -0
- package/src/ui/components/modals/shared/form.tsx +34 -22
- package/src/ui/components/modals/shared/picker.tsx +10 -10
- package/src/ui/components/modals/shared/{worktree-delete-confirm.tsx → workspace-delete-confirm.tsx} +9 -9
- package/src/ui/components/modals/tabs/new-tab-modal.tsx +30 -324
- package/src/ui/components/modals/workspace/create-workspace-modal.tsx +101 -0
- package/src/ui/components/modals/{worktree/worktree-move-confirm-modal.tsx → workspace/workspace-move-confirm-modal.tsx} +5 -5
- package/src/ui/components/modals/{worktree/worktree-move-modal.tsx → workspace/workspace-move-modal.tsx} +29 -27
- package/src/ui/components/primitives/input-field.tsx +7 -3
- package/src/ui/components/primitives/surface.tsx +3 -0
- package/src/ui/components/settings/row-value.tsx +74 -0
- package/src/ui/components/settings/settings-row.tsx +78 -0
- package/src/ui/components/settings/settings-view.tsx +169 -0
- package/src/ui/components/setup/setup-widget.tsx +152 -0
- package/src/ui/flash/build-labels.ts +27 -27
- package/src/ui/hooks/use-activity-sprite.ts +73 -0
- package/src/ui/hooks/use-scroll-active-into-view.ts +26 -0
- package/src/ui/{session-ordering.ts → project-ordering.ts} +4 -4
- package/src/ui/root.tsx +164 -230
- package/src/ui/status-bar-model.ts +48 -34
- package/src/ui/terminal-graphics/kitty.ts +28 -2
- package/src/ui/terminal-graphics/sprites/done@330/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/done@330/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/idle@1000/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/idle@1000/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/waiting@170/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/waiting@170/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/2.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/3.png +0 -0
- package/src/ui/terminal-graphics/sprites.ts +246 -0
- package/src/ui/truncate.ts +7 -0
- package/src/ui/widgets/registry.tsx +21 -0
- package/src/ui/widgets/widget-context-menu.ts +85 -0
- package/src/update.ts +2 -2
- package/src/app-runtime/session-actions.ts +0 -187
- package/src/cli/client/workspace-resolver.ts +0 -110
- package/src/cli/commands/workspace/close.ts +0 -32
- package/src/cli/commands/workspace/show.ts +0 -32
- package/src/cli/commands/worktree/create.ts +0 -55
- package/src/cli/commands/worktree/list.ts +0 -68
- package/src/cli/commands/worktree/remove.ts +0 -81
- package/src/git/worktree-branch-poller.ts +0 -54
- package/src/git/worktree-divergence-poller.ts +0 -59
- package/src/state/reducers/session-state.ts +0 -262
- package/src/state/session-catalog.ts +0 -143
- package/src/state/session-worktrees.ts +0 -260
- package/src/state/workspace-save.ts +0 -45
- package/src/ui/components/git/pane/git-pane-context-menu.ts +0 -26
- package/src/ui/components/layout/sidebar/sidebar.tsx +0 -163
- package/src/ui/components/layout/sidebar/use-top-tab-bar-auto-scroll.ts +0 -30
- package/src/ui/components/layout/sidebar/workspace-list.tsx +0 -418
- package/src/ui/components/layout/sidebar/worktree-row.tsx +0 -149
- /package/src/ui/{components/git/image-diff → terminal-graphics}/dimensions.ts +0 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
// Every action the reducer accepts, grouped by the slice of state it touches.
|
|
2
|
+
//
|
|
3
|
+
// Split from `types.ts` so the state shapes and the messages that change them
|
|
4
|
+
// are separate files: the shapes are read by the whole app, this union only by
|
|
5
|
+
// the reducer and the things that dispatch into it.
|
|
6
|
+
|
|
7
|
+
import type { ModeId } from '@brimveyn/aimux-config'
|
|
8
|
+
|
|
9
|
+
import type { LayoutNode, SplitDirection } from './layout-tree'
|
|
10
|
+
import type {
|
|
11
|
+
AssistantId,
|
|
12
|
+
BarSide,
|
|
13
|
+
BranchDivergence,
|
|
14
|
+
DiffData,
|
|
15
|
+
DirectoryResult,
|
|
16
|
+
DiscoveredRepo,
|
|
17
|
+
FocusMode,
|
|
18
|
+
GitFileSection,
|
|
19
|
+
GitPanelError,
|
|
20
|
+
GitPaneState,
|
|
21
|
+
GitRefreshPayload,
|
|
22
|
+
PendingWorkspaceLaunch,
|
|
23
|
+
ProjectRecord,
|
|
24
|
+
ProjectSnapshotV1,
|
|
25
|
+
ProjectStatus,
|
|
26
|
+
SnippetRecord,
|
|
27
|
+
TabActivity,
|
|
28
|
+
TabSession,
|
|
29
|
+
TerminalModeState,
|
|
30
|
+
TerminalSnapshot,
|
|
31
|
+
WorkspaceRecord,
|
|
32
|
+
} from './types'
|
|
33
|
+
|
|
34
|
+
export type ModalAction =
|
|
35
|
+
| { type: 'move-modal-cursor'; delta?: number; to?: 'home' | 'end' }
|
|
36
|
+
| {
|
|
37
|
+
type: 'open-new-tab-modal'
|
|
38
|
+
pendingWorkspace?: PendingWorkspaceLaunch
|
|
39
|
+
pendingPrompt?: string
|
|
40
|
+
}
|
|
41
|
+
| { type: 'open-edit-custom-command'; assistantId: AssistantId }
|
|
42
|
+
| { type: 'open-help-modal'; scope?: ModeId }
|
|
43
|
+
| { type: 'open-split-picker'; direction: SplitDirection }
|
|
44
|
+
| { type: 'open-project-picker' }
|
|
45
|
+
| {
|
|
46
|
+
type: 'open-project-name-modal'
|
|
47
|
+
projectTargetId?: string
|
|
48
|
+
initialName?: string
|
|
49
|
+
returnToProjectPicker?: boolean
|
|
50
|
+
}
|
|
51
|
+
| { type: 'close-modal' }
|
|
52
|
+
| { type: 'move-modal-selection'; delta: number }
|
|
53
|
+
| { type: 'update-command-edit'; char: string }
|
|
54
|
+
| { type: 'cancel-command-edit' }
|
|
55
|
+
| { type: 'open-create-project-modal'; returnToProjectPicker: boolean }
|
|
56
|
+
| { type: 'open-create-workspace-modal' }
|
|
57
|
+
| { type: 'set-create-workspace-base-branches'; branches: string[]; defaultBranch?: string }
|
|
58
|
+
| { type: 'set-create-workspace-branch-error'; message: string | null }
|
|
59
|
+
| { type: 'switch-create-workspace-field' }
|
|
60
|
+
| { type: 'set-directory-results'; results: DirectoryResult[] }
|
|
61
|
+
| { type: 'switch-create-project-field' }
|
|
62
|
+
| { type: 'select-directory' }
|
|
63
|
+
| { type: 'open-rename-tab-modal' }
|
|
64
|
+
| {
|
|
65
|
+
type: 'open-rename-workspace-modal'
|
|
66
|
+
projectId: string
|
|
67
|
+
workspaceId: string
|
|
68
|
+
initialName: string
|
|
69
|
+
}
|
|
70
|
+
| { type: 'open-snippet-picker'; returnTo?: FocusMode }
|
|
71
|
+
| { type: 'open-snippet-editor'; snippetId?: string }
|
|
72
|
+
| { type: 'set-help-entry-count'; count: number }
|
|
73
|
+
| { type: 'set-theme-entry-count'; count: number }
|
|
74
|
+
| { type: 'open-theme-picker'; returnTo?: FocusMode }
|
|
75
|
+
| { type: 'open-update-available-modal'; currentVersion: string; latestVersion: string }
|
|
76
|
+
| { type: 'set-modal-selection-index'; index: number }
|
|
77
|
+
| { type: 'open-ai-usage-modal' }
|
|
78
|
+
| { type: 'open-workspace-move-modal'; sourceWorkspaceId: string }
|
|
79
|
+
| { type: 'toggle-workspace-move-delete' }
|
|
80
|
+
| { type: 'set-workspace-move-stats'; dirtyFiles: Record<string, number> }
|
|
81
|
+
| {
|
|
82
|
+
type: 'open-workspace-move-confirm'
|
|
83
|
+
variant: 'stash-target' | 'keep-conflicts'
|
|
84
|
+
files: string[]
|
|
85
|
+
projectId: string
|
|
86
|
+
sourceWorkspaceId: string
|
|
87
|
+
targetWorkspaceId: string
|
|
88
|
+
deleteSource: boolean
|
|
89
|
+
sourceLabel: string
|
|
90
|
+
targetLabel: string
|
|
91
|
+
}
|
|
92
|
+
| {
|
|
93
|
+
type: 'open-workspace-delete-confirm'
|
|
94
|
+
projectId: string
|
|
95
|
+
workspaceId: string
|
|
96
|
+
workspaceLabel: string
|
|
97
|
+
reason: string
|
|
98
|
+
closeTabs: boolean
|
|
99
|
+
force: boolean
|
|
100
|
+
}
|
|
101
|
+
| { type: 'open-flash-jump-modal' }
|
|
102
|
+
| { type: 'clear-flash-jump-pending' }
|
|
103
|
+
|
|
104
|
+
export type ProjectAction =
|
|
105
|
+
| {
|
|
106
|
+
type: 'load-project'
|
|
107
|
+
projectId: string
|
|
108
|
+
projectSnapshot?: ProjectSnapshotV1
|
|
109
|
+
forceDisconnected?: boolean
|
|
110
|
+
}
|
|
111
|
+
| { type: 'set-projects'; projects: ProjectRecord[] }
|
|
112
|
+
| { type: 'create-project-record'; project: ProjectRecord }
|
|
113
|
+
| { type: 'rename-project-record'; projectId: string; name: string }
|
|
114
|
+
| { type: 'delete-project-record'; projectId: string; openProjectPicker?: boolean }
|
|
115
|
+
| { type: 'reorder-projects'; orderedIds: string[] }
|
|
116
|
+
| { type: 'reorder-active-project'; delta: number }
|
|
117
|
+
| { type: 'set-project-status'; projectId: string; status: ProjectStatus }
|
|
118
|
+
/** What a workspace's assistants are doing. `done` is owned by the reducer,
|
|
119
|
+
* so this carries only the two flags derived from tab statuses. */
|
|
120
|
+
| { type: 'set-workspace-activity'; workspaceId: string; working: boolean; waiting: boolean }
|
|
121
|
+
/** An assistant in this workspace finished a turn and nobody has looked yet. */
|
|
122
|
+
| { type: 'mark-workspace-done'; workspaceId: string }
|
|
123
|
+
/** The tab that rang: it finished a turn while the user was elsewhere. */
|
|
124
|
+
| { type: 'mark-tab-unseen'; tabId: string }
|
|
125
|
+
| {
|
|
126
|
+
type: 'add-workspace-record'
|
|
127
|
+
projectId: string
|
|
128
|
+
workspace: WorkspaceRecord
|
|
129
|
+
activate?: boolean
|
|
130
|
+
}
|
|
131
|
+
| { type: 'set-active-workspace'; projectId: string; workspaceId: string }
|
|
132
|
+
| {
|
|
133
|
+
type: 'update-workspace-record'
|
|
134
|
+
projectId: string
|
|
135
|
+
workspaceId: string
|
|
136
|
+
patch: Partial<WorkspaceRecord>
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type TabAction =
|
|
140
|
+
| { type: 'add-tab'; tab: TabSession }
|
|
141
|
+
| {
|
|
142
|
+
type: 'hydrate-project'
|
|
143
|
+
tabs: TabSession[]
|
|
144
|
+
activeTabId: string | null
|
|
145
|
+
layoutTree?: LayoutNode | null
|
|
146
|
+
layoutTrees?: Record<string, LayoutNode>
|
|
147
|
+
tabGroupMap?: Record<string, string>
|
|
148
|
+
}
|
|
149
|
+
| { type: 'close-tab'; tabId: string }
|
|
150
|
+
| { type: 'close-active-tab' }
|
|
151
|
+
| { type: 'set-active-tab'; tabId: string }
|
|
152
|
+
| { type: 'move-active-tab'; delta: number }
|
|
153
|
+
| { type: 'reorder-active-tab'; delta: number }
|
|
154
|
+
| { type: 'reorder-tabs'; orderedTabIds: string[] }
|
|
155
|
+
| { type: 'reset-tab-project'; tabId: string }
|
|
156
|
+
| {
|
|
157
|
+
type: 'rename-tab'
|
|
158
|
+
tabId: string
|
|
159
|
+
title: string
|
|
160
|
+
autoRenameStatus?: 'eligible' | 'attempted'
|
|
161
|
+
}
|
|
162
|
+
| {
|
|
163
|
+
type: 'update-tab-metadata'
|
|
164
|
+
tabId: string
|
|
165
|
+
title?: string
|
|
166
|
+
autoRenameStatus?: 'eligible' | 'attempted'
|
|
167
|
+
/** Set to false to promote a hidden tab into the normal tab strip. */
|
|
168
|
+
hidden?: boolean
|
|
169
|
+
}
|
|
170
|
+
| { type: 'append-tab-buffer'; tabId: string; chunk: string }
|
|
171
|
+
| {
|
|
172
|
+
type: 'replace-tab-viewport'
|
|
173
|
+
tabId: string
|
|
174
|
+
viewport: TerminalSnapshot
|
|
175
|
+
terminalModes: TerminalModeState
|
|
176
|
+
source?: 'resize' | 'scroll' | 'data' | 'switch'
|
|
177
|
+
}
|
|
178
|
+
| { type: 'set-tab-activity'; tabId: string; activity?: TabActivity }
|
|
179
|
+
| { type: 'set-tab-error'; tabId: string; message: string }
|
|
180
|
+
|
|
181
|
+
export type LayoutAction =
|
|
182
|
+
| {
|
|
183
|
+
type: 'split-pane'
|
|
184
|
+
direction: SplitDirection
|
|
185
|
+
newTab: TabSession
|
|
186
|
+
}
|
|
187
|
+
| { type: 'close-pane'; tabId: string }
|
|
188
|
+
| {
|
|
189
|
+
type: 'focus-pane-direction'
|
|
190
|
+
direction: 'left' | 'right' | 'up' | 'down'
|
|
191
|
+
}
|
|
192
|
+
| {
|
|
193
|
+
type: 'resize-pane'
|
|
194
|
+
tabId: string
|
|
195
|
+
delta: number
|
|
196
|
+
axis?: SplitDirection
|
|
197
|
+
}
|
|
198
|
+
| {
|
|
199
|
+
type: 'set-split-ratio'
|
|
200
|
+
tabId: string
|
|
201
|
+
ratio: number
|
|
202
|
+
axis?: SplitDirection
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export type UIAction =
|
|
206
|
+
| { type: 'toggle-bar'; side: BarSide }
|
|
207
|
+
| { type: 'resize-bar'; side: BarSide; delta: number }
|
|
208
|
+
| { type: 'set-bar-width'; side: BarSide; width: number }
|
|
209
|
+
| { type: 'toggle-widget'; widgetId: string }
|
|
210
|
+
| { type: 'move-widget'; widgetId: string; side: BarSide; index: number }
|
|
211
|
+
| { type: 'set-bar-boundary'; side: BarSide; index: number; ratio: number }
|
|
212
|
+
| { type: 'resize-widget'; widgetId: string; delta: number }
|
|
213
|
+
| { type: 'set-focus-mode'; focusMode: FocusMode }
|
|
214
|
+
| { type: 'set-terminal-size'; cols: number; rows: number }
|
|
215
|
+
| { type: 'resize-git-diff-pane'; delta: number }
|
|
216
|
+
| { type: 'set-pending-chords'; chords: string[] | null }
|
|
217
|
+
| { type: 'toggle-project-bar' }
|
|
218
|
+
|
|
219
|
+
export type SettingsAction =
|
|
220
|
+
| { type: 'enter-settings' }
|
|
221
|
+
| { type: 'exit-settings' }
|
|
222
|
+
| { type: 'settings-focus-pane'; pane: 'nav' | 'rows' }
|
|
223
|
+
| { type: 'settings-move-selection'; delta: -1 | 1 }
|
|
224
|
+
| { type: 'settings-select-section'; sectionId: string }
|
|
225
|
+
| { type: 'settings-select-row'; rowIndex: number }
|
|
226
|
+
| { type: 'open-settings-search' }
|
|
227
|
+
| { type: 'open-setting-text-modal'; settingId: string; label: string; value: string }
|
|
228
|
+
|
|
229
|
+
export type GitPanelAction =
|
|
230
|
+
| { type: 'git-refresh-success'; payload: GitRefreshPayload }
|
|
231
|
+
| { type: 'git-refresh-error'; kind: GitPanelError }
|
|
232
|
+
| { type: 'git-panel-reset' }
|
|
233
|
+
| { type: 'set-workspace-divergence'; divergence: Record<string, BranchDivergence> }
|
|
234
|
+
| { type: 'set-git-pane'; patch: Partial<GitPaneState> }
|
|
235
|
+
|
|
236
|
+
export type AutoCommitAction =
|
|
237
|
+
| {
|
|
238
|
+
type: 'auto-commit-generation-started'
|
|
239
|
+
projectId: string
|
|
240
|
+
tabId: string
|
|
241
|
+
workingTreeHash: string
|
|
242
|
+
abortController: AbortController
|
|
243
|
+
startedAt: number
|
|
244
|
+
}
|
|
245
|
+
| {
|
|
246
|
+
type: 'auto-commit-generation-ready'
|
|
247
|
+
projectId: string
|
|
248
|
+
workingTreeHash: string
|
|
249
|
+
title: string
|
|
250
|
+
body: string
|
|
251
|
+
generatedAt: number
|
|
252
|
+
}
|
|
253
|
+
| { type: 'auto-commit-clear'; projectId: string }
|
|
254
|
+
|
|
255
|
+
export type GitModeAction =
|
|
256
|
+
| { type: 'enter-git-mode' }
|
|
257
|
+
| { type: 'exit-git-mode' }
|
|
258
|
+
| { type: 'git-mode-move-selection'; delta: -1 | 1 }
|
|
259
|
+
| { type: 'git-mode-move-file-selection'; delta: -1 | 1 }
|
|
260
|
+
| { type: 'git-mode-select-entry-by-key'; key: string }
|
|
261
|
+
| { type: 'git-mode-toggle-folder'; key: string }
|
|
262
|
+
| { type: 'git-mode-toggle-selected-folder' }
|
|
263
|
+
| { type: 'git-mode-collapse-selection' }
|
|
264
|
+
| { type: 'git-mode-expand-selection' }
|
|
265
|
+
| { type: 'git-mode-toggle-file-list-mode' }
|
|
266
|
+
| { type: 'git-mode-toggle-tree-compaction' }
|
|
267
|
+
| { type: 'git-mode-set-diff'; key: string; diff: DiffData; hash: string }
|
|
268
|
+
| {
|
|
269
|
+
type: 'git-mode-set-parsed'
|
|
270
|
+
key: string
|
|
271
|
+
hash: string
|
|
272
|
+
file: unknown
|
|
273
|
+
}
|
|
274
|
+
| {
|
|
275
|
+
type: 'git-mode-set-highlights'
|
|
276
|
+
key: string
|
|
277
|
+
hash: string
|
|
278
|
+
themeId: string
|
|
279
|
+
add: unknown
|
|
280
|
+
del: unknown
|
|
281
|
+
}
|
|
282
|
+
| {
|
|
283
|
+
type: 'git-mode-merge-highlights'
|
|
284
|
+
key: string
|
|
285
|
+
hash: string
|
|
286
|
+
themeId: string
|
|
287
|
+
add: { start: number; tokens: unknown }[]
|
|
288
|
+
del: { start: number; tokens: unknown }[]
|
|
289
|
+
}
|
|
290
|
+
| { type: 'git-mode-invalidate-diffs'; paths: string[] }
|
|
291
|
+
| { type: 'git-mode-set-loading'; key: string; loading: boolean }
|
|
292
|
+
| { type: 'git-mode-set-pending-delete'; path: string | null }
|
|
293
|
+
| { type: 'git-mode-clear-diff-cache'; path: string }
|
|
294
|
+
| { type: 'git-mode-set-message'; message: string | null }
|
|
295
|
+
| { type: 'snippet-picker-set-message'; message: string | null }
|
|
296
|
+
| { type: 'git-mode-toggle-diff-view' }
|
|
297
|
+
| { type: 'git-mode-toggle-review-base' }
|
|
298
|
+
| { type: 'git-mode-shift-head-offset'; delta: number }
|
|
299
|
+
| { type: 'git-mode-set-head-offset'; offset: number }
|
|
300
|
+
| {
|
|
301
|
+
type: 'git-mode-fold-adjust'
|
|
302
|
+
key: string
|
|
303
|
+
foldId: string
|
|
304
|
+
side: 'top' | 'bottom'
|
|
305
|
+
delta: number
|
|
306
|
+
}
|
|
307
|
+
| { type: 'git-mode-fold-set'; key: string; foldId: string; top: number; bottom: number }
|
|
308
|
+
| { type: 'git-mode-fold-toggle-all'; key: string }
|
|
309
|
+
| {
|
|
310
|
+
type: 'git-mode-optimistic-move'
|
|
311
|
+
path: string
|
|
312
|
+
fromSection: GitFileSection
|
|
313
|
+
toSection: GitFileSection | null
|
|
314
|
+
}
|
|
315
|
+
| { type: 'open-git-commit-modal'; projectId?: string }
|
|
316
|
+
| { type: 'git-commit-enter-confirm' }
|
|
317
|
+
| { type: 'git-commit-leave-confirm' }
|
|
318
|
+
| { type: 'git-commit-enter-generating'; projectId: string }
|
|
319
|
+
| { type: 'git-commit-leave-generating' }
|
|
320
|
+
| { type: 'git-commit-use-background-suggestion'; projectId: string }
|
|
321
|
+
|
|
322
|
+
export type DataAction =
|
|
323
|
+
| { type: 'set-snippets'; snippets: SnippetRecord[] }
|
|
324
|
+
| { type: 'delete-snippet'; snippetId: string }
|
|
325
|
+
| { type: 'set-custom-commands'; customCommands: Record<AssistantId, string> }
|
|
326
|
+
|
|
327
|
+
export type MultiRepoAction =
|
|
328
|
+
| { type: 'multi-repo-set-repos'; repos: DiscoveredRepo[] }
|
|
329
|
+
| { type: 'multi-repo-clear' }
|
|
330
|
+
|
|
331
|
+
export type AppAction =
|
|
332
|
+
| ModalAction
|
|
333
|
+
| ProjectAction
|
|
334
|
+
| TabAction
|
|
335
|
+
| LayoutAction
|
|
336
|
+
| UIAction
|
|
337
|
+
| SettingsAction
|
|
338
|
+
| DataAction
|
|
339
|
+
| GitPanelAction
|
|
340
|
+
| GitModeAction
|
|
341
|
+
| AutoCommitAction
|
|
342
|
+
| MultiRepoAction
|
package/src/state/app-store.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useStore } from 'zustand'
|
|
2
2
|
import { createStore } from 'zustand/vanilla'
|
|
3
3
|
|
|
4
|
-
import type { AppAction
|
|
4
|
+
import type { AppAction } from './actions'
|
|
5
|
+
import type { AppState } from './types'
|
|
5
6
|
|
|
6
7
|
import { appReducer, createInitialState } from './store'
|
|
7
8
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { BarSide, BarsState, BarState, BarWidget } from './types'
|
|
2
|
+
|
|
3
|
+
export const BAR_MIN_WIDTH = 18
|
|
4
|
+
export const BAR_MAX_WIDTH = 80
|
|
5
|
+
|
|
6
|
+
/** Widget ids the app knows how to render. Unknown ids are pruned on load. */
|
|
7
|
+
export const KNOWN_WIDGET_IDS = ['projects', 'git', 'setup'] as const
|
|
8
|
+
|
|
9
|
+
/** Smallest share of a bar a single widget may shrink to, as a fraction. */
|
|
10
|
+
const MIN_WIDGET_SHARE = 0.1
|
|
11
|
+
|
|
12
|
+
export function clampBarWidth(width: number): number {
|
|
13
|
+
return Math.min(BAR_MAX_WIDTH, Math.max(BAR_MIN_WIDTH, Math.round(width)))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function visibleWidgets(bar: BarState): BarWidget[] {
|
|
17
|
+
return bar.widgets.filter((widget) => widget.visible)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The single authority on how many columns a bar occupies. Both the `Bar`
|
|
22
|
+
* component and the terminal-size computation must call this — a mismatch
|
|
23
|
+
* silently corrupts PTY columns and mouse hit-testing.
|
|
24
|
+
*/
|
|
25
|
+
export function getBarWidth(bar: BarState): number {
|
|
26
|
+
if (!bar.visible || visibleWidgets(bar).length === 0) return 0
|
|
27
|
+
return clampBarWidth(bar.width)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function findWidgetBar(bars: BarsState, widgetId: string): BarSide | null {
|
|
31
|
+
if (bars.left.widgets.some((widget) => widget.id === widgetId)) return 'left'
|
|
32
|
+
if (bars.right.widgets.some((widget) => widget.id === widgetId)) return 'right'
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function totalGrow(widgets: BarWidget[]): number {
|
|
37
|
+
return widgets.reduce((sum, widget) => sum + widget.grow, 0)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Move the boundary between visible widgets `index` and `index + 1` by
|
|
42
|
+
* `deltaGrow`. Only the pair changes, so the bar's total grow is preserved and
|
|
43
|
+
* no renormalisation is ever needed — including when a widget is added.
|
|
44
|
+
*/
|
|
45
|
+
export function shiftBoundary(bar: BarState, index: number, deltaGrow: number): BarWidget[] {
|
|
46
|
+
const visible = visibleWidgets(bar)
|
|
47
|
+
const above = visible[index]
|
|
48
|
+
const below = visible[index + 1]
|
|
49
|
+
if (!above || !below) return bar.widgets
|
|
50
|
+
|
|
51
|
+
const min = Math.max(1, Math.round(totalGrow(visible) * MIN_WIDGET_SHARE))
|
|
52
|
+
const pair = above.grow + below.grow
|
|
53
|
+
if (pair < min * 2) return bar.widgets
|
|
54
|
+
|
|
55
|
+
const nextAbove = Math.min(pair - min, Math.max(min, Math.round(above.grow + deltaGrow)))
|
|
56
|
+
if (nextAbove === above.grow) return bar.widgets
|
|
57
|
+
|
|
58
|
+
return bar.widgets.map((widget) => {
|
|
59
|
+
if (widget.id === above.id) return { ...widget, grow: nextAbove }
|
|
60
|
+
if (widget.id === below.id) return { ...widget, grow: pair - nextAbove }
|
|
61
|
+
return widget
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Convert an absolute drag position (a 0..1 fraction of the bar's body) into
|
|
67
|
+
* the grow delta `shiftBoundary` expects for that boundary.
|
|
68
|
+
*/
|
|
69
|
+
export function boundaryDeltaFromRatio(bar: BarState, index: number, ratio: number): number {
|
|
70
|
+
const visible = visibleWidgets(bar)
|
|
71
|
+
const target = visible[index]
|
|
72
|
+
if (!target) return 0
|
|
73
|
+
const above = visible.slice(0, index).reduce((sum, widget) => sum + widget.grow, 0)
|
|
74
|
+
return ratio * totalGrow(visible) - above - target.grow
|
|
75
|
+
}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
export const GIT_PANE_MIN_RATIO = 0.2
|
|
2
2
|
export const GIT_PANE_MAX_RATIO = 0.8
|
|
3
|
-
export const GIT_PANE_MIN_WIDTH = 20
|
|
4
|
-
export const GIT_PANE_MAX_WIDTH = 80
|
|
5
3
|
|
|
6
4
|
export function clampGitPaneRatio(value: number): number {
|
|
7
5
|
return Math.max(GIT_PANE_MIN_RATIO, Math.min(GIT_PANE_MAX_RATIO, value))
|
|
8
6
|
}
|
|
9
|
-
|
|
10
|
-
export function getGitPaneWidthFromRatio(ratio: number): number {
|
|
11
|
-
return Math.max(
|
|
12
|
-
GIT_PANE_MIN_WIDTH,
|
|
13
|
-
Math.min(GIT_PANE_MAX_WIDTH, Math.round(clampGitPaneRatio(ratio) * GIT_PANE_MAX_WIDTH))
|
|
14
|
-
)
|
|
15
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ProjectSnapshotV1 } from './types'
|
|
2
2
|
|
|
3
3
|
import { computePaneRects, type LayoutNode, PANE_BORDER, type PaneRect } from './layout-tree'
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ export interface TerminalBounds {
|
|
|
9
9
|
rows: number
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function getSnapshotTrees(snapshot:
|
|
12
|
+
export function getSnapshotTrees(snapshot: ProjectSnapshotV1 | undefined): LayoutNode[] {
|
|
13
13
|
if (snapshot?.layoutTrees) {
|
|
14
14
|
return Object.values(snapshot.layoutTrees)
|
|
15
15
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useStore } from 'zustand'
|
|
2
|
+
import { createStore } from 'zustand/vanilla'
|
|
3
|
+
|
|
4
|
+
import type { PrStatusResult } from '../git/pr-status'
|
|
5
|
+
|
|
6
|
+
export interface PrStatusState {
|
|
7
|
+
result: PrStatusResult | null
|
|
8
|
+
/** True once a fetch failed but we are still showing the previous good result. */
|
|
9
|
+
stale: boolean
|
|
10
|
+
setResult: (result: PrStatusResult) => void
|
|
11
|
+
reset: () => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const prStatusStore = createStore<PrStatusState>((set) => ({
|
|
15
|
+
reset: () => set({ result: null, stale: false }),
|
|
16
|
+
result: null,
|
|
17
|
+
setResult: (result: PrStatusResult) =>
|
|
18
|
+
set((state) => {
|
|
19
|
+
// A transient `gh` failure shouldn't blank a PR we already resolved — keep
|
|
20
|
+
// the last good snapshot and mark it stale instead (same contract as
|
|
21
|
+
// ai-usage-store's setSnapshot).
|
|
22
|
+
if (result.kind === 'error' && state.result?.kind === 'ok') return { stale: true }
|
|
23
|
+
return { result, stale: false }
|
|
24
|
+
}),
|
|
25
|
+
stale: false,
|
|
26
|
+
}))
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The PR state row occupies its band both while the first fetch is in flight
|
|
30
|
+
* and once it resolved to a PR — anything else (no PR, no gh, error) gives the
|
|
31
|
+
* row back. Shared so the header and the row itself can never disagree and
|
|
32
|
+
* shift the layout under the user.
|
|
33
|
+
*/
|
|
34
|
+
export const selectPrRowVisible = (state: PrStatusState): boolean =>
|
|
35
|
+
state.result === null || state.result.kind === 'ok'
|
|
36
|
+
|
|
37
|
+
export function usePrStatusStore<T>(selector: (state: PrStatusState) => T): T {
|
|
38
|
+
return useStore(prStatusStore, selector)
|
|
39
|
+
}
|