@brimveyn/aimux 1.21.0 → 1.22.2
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 +302 -1541
- package/src/app-runtime/snippet-actions.ts +3 -2
- 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 +2 -1
- 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 +18 -14
- 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 +93 -48
- 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 +70 -112
- 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-status.ts +1 -1
- 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 +4 -4
- 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/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 +19 -6
- 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 +1 -1
- package/src/state/dispatch-ref.ts +1 -1
- package/src/state/layout-resize.ts +2 -2
- 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} +74 -68
- 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 -6
- 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 +4 -3
- package/src/state/selectors.ts +49 -37
- package/src/state/store.ts +57 -24
- package/src/state/types.ts +244 -405
- 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/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-widget.tsx +7 -7
- 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 +46 -36
- 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 +139 -121
- 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 +6 -3
- package/src/ui/widgets/widget-context-menu.ts +26 -10
- 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 -43
- 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
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
import { basename } from 'node:path'
|
|
2
2
|
|
|
3
|
-
import type { AppAction
|
|
3
|
+
import type { AppAction } from '../actions'
|
|
4
|
+
import type { AppState } from '../types'
|
|
4
5
|
|
|
5
6
|
import { collectHelpEntries } from '../../input/keymap/help-entries'
|
|
6
7
|
import { getActiveKeymap } from '../../input/keymap/keymap-ref'
|
|
7
8
|
import { getAllAssistantOptions } from '../../pty/command-registry'
|
|
9
|
+
import { filterSettingRows } from '../../settings/search'
|
|
8
10
|
import { filterThemeIds } from '../../ui/filter-themes'
|
|
9
11
|
import { buildFlashJumpLabels } from '../../ui/flash/build-labels'
|
|
10
12
|
import {
|
|
11
13
|
type BaseRefOption,
|
|
12
14
|
buildBaseRefOptions,
|
|
13
|
-
|
|
14
|
-
filterSessions,
|
|
15
|
+
filterProjects,
|
|
15
16
|
filterSnippets,
|
|
16
|
-
|
|
17
|
+
getNewTabAssistantOptions,
|
|
17
18
|
} from '../selectors'
|
|
18
|
-
import {
|
|
19
|
+
import { reduceGitCommitModalState } from './git-commit-modal-state'
|
|
19
20
|
|
|
20
21
|
function emptyModal() {
|
|
21
22
|
return {
|
|
22
23
|
cursorPos: 0,
|
|
23
24
|
editBuffer: null,
|
|
25
|
+
projectTargetId: null,
|
|
24
26
|
selectedIndex: 0,
|
|
25
|
-
sessionTargetId: null,
|
|
26
27
|
type: null,
|
|
27
28
|
} as const
|
|
28
29
|
}
|
|
@@ -33,236 +34,97 @@ function clampCursor(value: number, max: number): number {
|
|
|
33
34
|
return Math.max(0, Math.min(max, value))
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
function
|
|
37
|
-
if (!(state.
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
function getCurrentWorktreeIndex(state: AppState): number {
|
|
42
|
-
if (!(state.currentSessionId != null && state.currentSessionId !== '')) return 0
|
|
43
|
-
const session = state.sessions.find((entry) => entry.id === state.currentSessionId)
|
|
44
|
-
return Math.max(
|
|
45
|
-
0,
|
|
46
|
-
(session?.worktrees ?? []).findIndex((worktree) => worktree.id === session?.activeWorktreeId)
|
|
37
|
+
function getCurrentWorkspaceCount(state: AppState): number {
|
|
38
|
+
if (!(state.currentProjectId != null && state.currentProjectId !== '')) return 0
|
|
39
|
+
return (
|
|
40
|
+
state.projects.find((entry) => entry.id === state.currentProjectId)?.workspaces?.length ?? 0
|
|
47
41
|
)
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
function
|
|
51
|
-
if (state.modal.type !== '
|
|
52
|
-
const
|
|
53
|
-
state.
|
|
44
|
+
function getCreateWorkspaceBaseOptions(state: AppState, queryOverride?: string): BaseRefOption[] {
|
|
45
|
+
if (state.modal.type !== 'create-workspace') return []
|
|
46
|
+
const workspaces =
|
|
47
|
+
state.projects.find((entry) => entry.id === state.currentProjectId)?.workspaces ?? []
|
|
54
48
|
return buildBaseRefOptions(
|
|
55
|
-
|
|
49
|
+
workspaces,
|
|
56
50
|
state.modal.baseBranches,
|
|
57
51
|
queryOverride ?? state.modal.baseQuery
|
|
58
52
|
)
|
|
59
53
|
}
|
|
60
54
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
/**
|
|
56
|
+
* How many rows the open modal's list has, for the two cases that move the
|
|
57
|
+
* selection through it. They read the same list, so they read it from here:
|
|
58
|
+
* two copies of this chain drift apart the moment one modal's list changes.
|
|
59
|
+
* Modals whose list is not indexed this way (create-workspace's own fields,
|
|
60
|
+
* flash-jump) return 0 and are handled by their case before it gets here.
|
|
61
|
+
*/
|
|
62
|
+
function getModalOptionCount(state: AppState): number {
|
|
63
|
+
const { modal } = state
|
|
64
|
+
switch (modal.type) {
|
|
65
|
+
case 'create-project':
|
|
66
|
+
return modal.directoryResults.length
|
|
67
|
+
case 'help':
|
|
68
|
+
return modal.entryCount
|
|
69
|
+
case 'new-tab':
|
|
70
|
+
return getNewTabAssistantOptions(
|
|
71
|
+
state.customCommands,
|
|
72
|
+
modal.editBuffer,
|
|
73
|
+
modal.pendingWorkspace != null
|
|
74
|
+
).length
|
|
75
|
+
case 'project-picker':
|
|
76
|
+
// `+1` for the "create a project" row the picker appends, and at least
|
|
77
|
+
// that row exists even when nothing matches the filter.
|
|
78
|
+
return Math.max(1, filterProjects(state.projects, modal.editBuffer).length + 1)
|
|
79
|
+
case 'settings-search':
|
|
80
|
+
return filterSettingRows(state.projects, modal.editBuffer).length
|
|
81
|
+
case 'snippet-picker':
|
|
82
|
+
return filterSnippets(state.snippets, modal.editBuffer).length
|
|
83
|
+
case 'split-picker':
|
|
84
|
+
return getAllAssistantOptions(state.customCommands).length
|
|
85
|
+
case 'theme-picker':
|
|
86
|
+
return filterThemeIds(modal.editBuffer).length
|
|
87
|
+
case 'update-available':
|
|
88
|
+
return 2
|
|
89
|
+
case 'workspace-move':
|
|
90
|
+
// Every workspace but the one being moved.
|
|
91
|
+
return Math.max(0, getCurrentWorkspaceCount(state) - 1)
|
|
92
|
+
default:
|
|
93
|
+
return 0
|
|
64
94
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const CREATE_WORKSPACE_FIELDS = ['prompt', 'base'] as const
|
|
98
|
+
|
|
99
|
+
type CreateWorkspaceField = (typeof CREATE_WORKSPACE_FIELDS)[number]
|
|
100
|
+
|
|
101
|
+
/** The buffer a create-workspace field edits, so cursor math has one source. */
|
|
102
|
+
function getCreateWorkspaceFieldValue(
|
|
103
|
+
modal: { prompt: string; baseQuery: string },
|
|
104
|
+
field: CreateWorkspaceField
|
|
105
|
+
): string {
|
|
106
|
+
return field === 'prompt' ? modal.prompt : modal.baseQuery
|
|
69
107
|
}
|
|
70
108
|
|
|
71
109
|
export function reduceModalState(state: AppState, action: AppAction): AppState | null {
|
|
110
|
+
// The one modal with a stage machine of its own, in its own file.
|
|
111
|
+
const gitCommit = reduceGitCommitModalState(state, action)
|
|
112
|
+
if (gitCommit) return gitCommit
|
|
113
|
+
|
|
72
114
|
switch (action.type) {
|
|
73
115
|
case 'open-new-tab-modal': {
|
|
74
|
-
const targetWorktreeIndex = getCurrentWorktreeIndex(state)
|
|
75
116
|
return {
|
|
76
117
|
...state,
|
|
77
118
|
focusMode: 'command-edit',
|
|
78
119
|
modal: {
|
|
79
|
-
activeField: 'assistant',
|
|
80
|
-
baseBranches: [],
|
|
81
|
-
baseQuery: '',
|
|
82
|
-
baseRef: '',
|
|
83
|
-
branchError: null,
|
|
84
|
-
branchName: '',
|
|
85
|
-
createWorktree: false,
|
|
86
120
|
cursorPos: 0,
|
|
87
121
|
editBuffer: '',
|
|
88
122
|
editingCommand: null,
|
|
89
|
-
|
|
123
|
+
pendingPrompt: action.pendingPrompt,
|
|
124
|
+
pendingWorkspace: action.pendingWorkspace,
|
|
125
|
+
projectTargetId: null,
|
|
90
126
|
selectedIndex: 0,
|
|
91
|
-
sessionTargetId: null,
|
|
92
|
-
step: 'assistant',
|
|
93
|
-
targetWorktreeIndex,
|
|
94
127
|
type: 'new-tab',
|
|
95
|
-
worktreeDeletePrompt: null,
|
|
96
|
-
worktreeName: '',
|
|
97
|
-
},
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
case 'enter-new-tab-worktree-create': {
|
|
101
|
-
if (state.modal.type !== 'new-tab') return state
|
|
102
|
-
// Default the base to the branch of the worktree we're forking from (the
|
|
103
|
-
// current target), preserving the previous always-fork-from-source
|
|
104
|
-
// behaviour until the user picks another base.
|
|
105
|
-
const session = state.sessions.find((entry) => entry.id === state.currentSessionId)
|
|
106
|
-
const sourceWorktree = session?.worktrees?.[state.modal.targetWorktreeIndex]
|
|
107
|
-
return {
|
|
108
|
-
...state,
|
|
109
|
-
modal: {
|
|
110
|
-
...state.modal,
|
|
111
|
-
activeField: 'worktree-name',
|
|
112
|
-
baseQuery: '',
|
|
113
|
-
baseRef: sourceWorktree?.branch ?? '',
|
|
114
|
-
createWorktree: true,
|
|
115
|
-
cursorPos: state.modal.worktreeName.length,
|
|
116
|
-
selectedIndex: 0,
|
|
117
|
-
step: 'worktree-create',
|
|
118
|
-
},
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
case 'set-new-tab-base-branches': {
|
|
122
|
-
if (state.modal.type !== 'new-tab') return state
|
|
123
|
-
const modalWithBranches = { ...state.modal, baseBranches: action.branches }
|
|
124
|
-
const withBranches: AppState = { ...state, modal: modalWithBranches }
|
|
125
|
-
// Backfill a default base if none resolved yet (e.g. detached source).
|
|
126
|
-
if (state.modal.baseRef !== '') return withBranches
|
|
127
|
-
const firstOption = getNewTabBaseOptions(withBranches)[0]
|
|
128
|
-
return {
|
|
129
|
-
...withBranches,
|
|
130
|
-
modal: { ...modalWithBranches, baseRef: firstOption?.ref ?? '' },
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
case 'enter-new-tab-template-pick': {
|
|
134
|
-
if (state.modal.type !== 'new-tab') return state
|
|
135
|
-
return {
|
|
136
|
-
...state,
|
|
137
|
-
modal: {
|
|
138
|
-
...state.modal,
|
|
139
|
-
activeField: 'target-worktree',
|
|
140
|
-
cursorPos: 0,
|
|
141
|
-
selectedIndex: 0,
|
|
142
|
-
step: 'template',
|
|
143
|
-
},
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
case 'enter-new-tab-template-shortcut': {
|
|
147
|
-
if (state.modal.type !== 'new-tab') return state
|
|
148
|
-
const defaultName = state.modal.worktreeName || 'wt-template'
|
|
149
|
-
return {
|
|
150
|
-
...state,
|
|
151
|
-
modal: {
|
|
152
|
-
...state.modal,
|
|
153
|
-
activeField: 'worktree-name',
|
|
154
|
-
createWorktree: true,
|
|
155
|
-
cursorPos: defaultName.length,
|
|
156
|
-
selectedAssistantId: null,
|
|
157
|
-
selectedIndex: 0,
|
|
158
|
-
step: 'worktree-create',
|
|
159
|
-
worktreeName: defaultName,
|
|
160
|
-
},
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
case 'set-new-tab-worktree-delete-prompt': {
|
|
164
|
-
if (state.modal.type !== 'new-tab') return state
|
|
165
|
-
return {
|
|
166
|
-
...state,
|
|
167
|
-
modal: {
|
|
168
|
-
...state.modal,
|
|
169
|
-
worktreeDeletePrompt: action.prompt,
|
|
170
|
-
},
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
case 'set-new-tab-branch-error': {
|
|
174
|
-
if (state.modal.type !== 'new-tab') return state
|
|
175
|
-
return {
|
|
176
|
-
...state,
|
|
177
|
-
modal: {
|
|
178
|
-
...state.modal,
|
|
179
|
-
activeField: 'branch-name',
|
|
180
|
-
branchError: action.message,
|
|
181
|
-
cursorPos: state.modal.branchName.length,
|
|
182
|
-
},
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
case 'select-new-tab-assistant': {
|
|
186
|
-
if (state.modal.type !== 'new-tab' || state.modal.editingCommand !== null) return state
|
|
187
|
-
if (
|
|
188
|
-
action.assistantId === undefined &&
|
|
189
|
-
state.modal.step === 'assistant' &&
|
|
190
|
-
state.worktreeTemplates.length > 0
|
|
191
|
-
) {
|
|
192
|
-
const filtered = filterAssistants(
|
|
193
|
-
getAllAssistantOptions(state.customCommands),
|
|
194
|
-
state.modal.editBuffer
|
|
195
|
-
)
|
|
196
|
-
if (state.modal.selectedIndex >= filtered.length) {
|
|
197
|
-
const defaultName = state.modal.worktreeName || 'wt-template'
|
|
198
|
-
return {
|
|
199
|
-
...state,
|
|
200
|
-
modal: {
|
|
201
|
-
...state.modal,
|
|
202
|
-
activeField: 'worktree-name',
|
|
203
|
-
createWorktree: true,
|
|
204
|
-
cursorPos: defaultName.length,
|
|
205
|
-
selectedAssistantId: null,
|
|
206
|
-
selectedIndex: 0,
|
|
207
|
-
step: 'worktree-create',
|
|
208
|
-
worktreeName: defaultName,
|
|
209
|
-
},
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
const option = getSelectedNewTabAssistant(state, action.assistantId)
|
|
214
|
-
if (!option) return state
|
|
215
|
-
const targetWorktreeIndex = getCurrentWorktreeIndex(state)
|
|
216
|
-
const worktreeCount = getCurrentWorktreeCount(state)
|
|
217
|
-
return {
|
|
218
|
-
...state,
|
|
219
|
-
modal: {
|
|
220
|
-
...state.modal,
|
|
221
|
-
activeField: 'target-worktree',
|
|
222
|
-
branchError: null,
|
|
223
|
-
createWorktree: worktreeCount === 0,
|
|
224
|
-
cursorPos: 0,
|
|
225
|
-
selectedAssistantId: option.id,
|
|
226
|
-
selectedIndex: worktreeCount === 0 ? 0 : targetWorktreeIndex,
|
|
227
|
-
step: 'worktree',
|
|
228
|
-
targetWorktreeIndex,
|
|
229
|
-
worktreeDeletePrompt: null,
|
|
230
|
-
worktreeName: state.modal.worktreeName || `wt-${option.label}`,
|
|
231
|
-
},
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
case 'toggle-new-tab-worktree': {
|
|
235
|
-
if (state.modal.type !== 'new-tab' || state.modal.editingCommand !== null) return state
|
|
236
|
-
const createWorktree = !state.modal.createWorktree
|
|
237
|
-
const option = getSelectedNewTabAssistant(state, action.assistantId)
|
|
238
|
-
const defaultName = state.modal.worktreeName || `wt-${option?.label ?? 'assistant'}`
|
|
239
|
-
const worktreeCount = getCurrentWorktreeCount(state)
|
|
240
|
-
const targetWorktreeIndex = getCurrentWorktreeIndex(state)
|
|
241
|
-
let activeField = state.modal.activeField
|
|
242
|
-
if (createWorktree) {
|
|
243
|
-
activeField = state.modal.step === 'worktree' || option ? 'target-worktree' : activeField
|
|
244
|
-
} else if (activeField === 'worktree-name') {
|
|
245
|
-
activeField = 'target-worktree'
|
|
246
|
-
}
|
|
247
|
-
const cursorPos =
|
|
248
|
-
createWorktree && worktreeCount <= 0
|
|
249
|
-
? defaultName.length
|
|
250
|
-
: (state.modal.editBuffer?.length ?? 0)
|
|
251
|
-
return {
|
|
252
|
-
...state,
|
|
253
|
-
modal: {
|
|
254
|
-
...state.modal,
|
|
255
|
-
activeField,
|
|
256
|
-
createWorktree,
|
|
257
|
-
cursorPos,
|
|
258
|
-
selectedAssistantId: option?.id ?? state.modal.selectedAssistantId,
|
|
259
|
-
selectedIndex: createWorktree
|
|
260
|
-
? worktreeCount
|
|
261
|
-
: Math.min(state.modal.selectedIndex, Math.max(0, worktreeCount - 1)),
|
|
262
|
-
step: option && createWorktree ? 'worktree' : state.modal.step,
|
|
263
|
-
targetWorktreeIndex,
|
|
264
|
-
worktreeDeletePrompt: null,
|
|
265
|
-
worktreeName: defaultName,
|
|
266
128
|
},
|
|
267
129
|
}
|
|
268
130
|
}
|
|
@@ -287,13 +149,13 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
287
149
|
modal: {
|
|
288
150
|
cursorPos: 0,
|
|
289
151
|
editBuffer: '',
|
|
152
|
+
projectTargetId: null,
|
|
290
153
|
selectedIndex: 0,
|
|
291
|
-
sessionTargetId: null,
|
|
292
154
|
type: 'ai-usage',
|
|
293
155
|
},
|
|
294
156
|
}
|
|
295
157
|
}
|
|
296
|
-
case 'open-
|
|
158
|
+
case 'open-workspace-move-modal': {
|
|
297
159
|
// Overlay: keep focusMode (git when opened via `m`, navigation when opened
|
|
298
160
|
// from a tab menu) so the view underneath stays mounted, like the help
|
|
299
161
|
// modal. deriveModeId routes input to the picker while open.
|
|
@@ -302,29 +164,29 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
302
164
|
modal: {
|
|
303
165
|
deleteSource: false,
|
|
304
166
|
editBuffer: null,
|
|
167
|
+
projectTargetId: null,
|
|
305
168
|
selectedIndex: 0,
|
|
306
|
-
|
|
307
|
-
sourceWorktreeId: action.sourceWorktreeId,
|
|
169
|
+
sourceWorkspaceId: action.sourceWorkspaceId,
|
|
308
170
|
stats: { kind: 'loading' },
|
|
309
|
-
type: '
|
|
171
|
+
type: 'workspace-move',
|
|
310
172
|
},
|
|
311
173
|
}
|
|
312
174
|
}
|
|
313
|
-
case 'toggle-
|
|
314
|
-
if (state.modal.type !== '
|
|
175
|
+
case 'toggle-workspace-move-delete': {
|
|
176
|
+
if (state.modal.type !== 'workspace-move') return state
|
|
315
177
|
return {
|
|
316
178
|
...state,
|
|
317
179
|
modal: { ...state.modal, deleteSource: !state.modal.deleteSource },
|
|
318
180
|
}
|
|
319
181
|
}
|
|
320
|
-
case 'set-
|
|
321
|
-
if (state.modal.type !== '
|
|
182
|
+
case 'set-workspace-move-stats': {
|
|
183
|
+
if (state.modal.type !== 'workspace-move') return state
|
|
322
184
|
return {
|
|
323
185
|
...state,
|
|
324
186
|
modal: { ...state.modal, stats: { dirtyFiles: action.dirtyFiles, kind: 'ready' } },
|
|
325
187
|
}
|
|
326
188
|
}
|
|
327
|
-
case 'open-
|
|
189
|
+
case 'open-workspace-move-confirm': {
|
|
328
190
|
return {
|
|
329
191
|
...state,
|
|
330
192
|
focusMode: 'modal',
|
|
@@ -332,19 +194,19 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
332
194
|
deleteSource: action.deleteSource,
|
|
333
195
|
editBuffer: null,
|
|
334
196
|
files: action.files,
|
|
197
|
+
projectId: action.projectId,
|
|
198
|
+
projectTargetId: null,
|
|
335
199
|
selectedIndex: 0,
|
|
336
|
-
sessionId: action.sessionId,
|
|
337
|
-
sessionTargetId: null,
|
|
338
200
|
sourceLabel: action.sourceLabel,
|
|
339
|
-
|
|
201
|
+
sourceWorkspaceId: action.sourceWorkspaceId,
|
|
340
202
|
targetLabel: action.targetLabel,
|
|
341
|
-
|
|
342
|
-
type: '
|
|
203
|
+
targetWorkspaceId: action.targetWorkspaceId,
|
|
204
|
+
type: 'workspace-move-confirm',
|
|
343
205
|
variant: action.variant,
|
|
344
206
|
},
|
|
345
207
|
}
|
|
346
208
|
}
|
|
347
|
-
case 'open-
|
|
209
|
+
case 'open-workspace-delete-confirm': {
|
|
348
210
|
return {
|
|
349
211
|
...state,
|
|
350
212
|
focusMode: 'modal',
|
|
@@ -352,13 +214,13 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
352
214
|
closeTabs: action.closeTabs,
|
|
353
215
|
editBuffer: null,
|
|
354
216
|
force: action.force,
|
|
217
|
+
projectId: action.projectId,
|
|
218
|
+
projectTargetId: null,
|
|
355
219
|
reason: action.reason,
|
|
356
220
|
selectedIndex: 0,
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
worktreeId: action.worktreeId,
|
|
361
|
-
worktreeLabel: action.worktreeLabel,
|
|
221
|
+
type: 'workspace-delete-confirm',
|
|
222
|
+
workspaceId: action.workspaceId,
|
|
223
|
+
workspaceLabel: action.workspaceLabel,
|
|
362
224
|
},
|
|
363
225
|
}
|
|
364
226
|
}
|
|
@@ -375,8 +237,8 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
375
237
|
editBuffer: '',
|
|
376
238
|
labels,
|
|
377
239
|
pendingJump: null,
|
|
240
|
+
projectTargetId: null,
|
|
378
241
|
selectedIndex: 0,
|
|
379
|
-
sessionTargetId: null,
|
|
380
242
|
type: 'flash-jump',
|
|
381
243
|
},
|
|
382
244
|
}
|
|
@@ -397,9 +259,9 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
397
259
|
cursorPos: 0,
|
|
398
260
|
editBuffer: '',
|
|
399
261
|
entryCount: scopedEntries.length,
|
|
262
|
+
projectTargetId: null,
|
|
400
263
|
scope,
|
|
401
264
|
selectedIndex: 0,
|
|
402
|
-
sessionTargetId: null,
|
|
403
265
|
type: 'help',
|
|
404
266
|
},
|
|
405
267
|
}
|
|
@@ -411,25 +273,25 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
411
273
|
modal: {
|
|
412
274
|
cursorPos: 0,
|
|
413
275
|
editBuffer: '',
|
|
276
|
+
projectTargetId: null,
|
|
414
277
|
selectedIndex: 0,
|
|
415
|
-
sessionTargetId: null,
|
|
416
278
|
splitDirection: action.direction,
|
|
417
279
|
type: 'split-picker',
|
|
418
280
|
},
|
|
419
281
|
}
|
|
420
|
-
case 'open-
|
|
282
|
+
case 'open-project-picker':
|
|
421
283
|
return {
|
|
422
284
|
...state,
|
|
423
285
|
focusMode: 'command-edit',
|
|
424
286
|
modal: {
|
|
425
287
|
cursorPos: 0,
|
|
426
288
|
editBuffer: '',
|
|
289
|
+
projectTargetId: null,
|
|
427
290
|
selectedIndex: 0,
|
|
428
|
-
|
|
429
|
-
type: 'session-picker',
|
|
291
|
+
type: 'project-picker',
|
|
430
292
|
},
|
|
431
293
|
}
|
|
432
|
-
case 'open-
|
|
294
|
+
case 'open-project-name-modal': {
|
|
433
295
|
const initialName = action.initialName ?? ''
|
|
434
296
|
return {
|
|
435
297
|
...state,
|
|
@@ -437,14 +299,14 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
437
299
|
modal: {
|
|
438
300
|
cursorPos: initialName.length,
|
|
439
301
|
editBuffer: initialName,
|
|
440
|
-
|
|
302
|
+
projectTargetId: action.projectTargetId ?? null,
|
|
303
|
+
returnToProjectPicker: action.returnToProjectPicker ?? true,
|
|
441
304
|
selectedIndex: 0,
|
|
442
|
-
|
|
443
|
-
type: 'session-name',
|
|
305
|
+
type: 'project-name',
|
|
444
306
|
},
|
|
445
307
|
}
|
|
446
308
|
}
|
|
447
|
-
case 'open-create-
|
|
309
|
+
case 'open-create-project-modal':
|
|
448
310
|
return {
|
|
449
311
|
...state,
|
|
450
312
|
focusMode: 'command-edit',
|
|
@@ -455,14 +317,14 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
455
317
|
editBuffer: '',
|
|
456
318
|
nameBuffer: '',
|
|
457
319
|
pendingProjectPath: null,
|
|
458
|
-
|
|
320
|
+
projectTargetId: null,
|
|
321
|
+
returnToProjectPicker: action.returnToProjectPicker,
|
|
459
322
|
selectedIndex: 0,
|
|
460
|
-
|
|
461
|
-
type: 'create-session',
|
|
323
|
+
type: 'create-project',
|
|
462
324
|
},
|
|
463
325
|
}
|
|
464
326
|
case 'set-directory-results': {
|
|
465
|
-
if (state.modal.type !== 'create-
|
|
327
|
+
if (state.modal.type !== 'create-project') {
|
|
466
328
|
return state
|
|
467
329
|
}
|
|
468
330
|
return {
|
|
@@ -474,6 +336,88 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
474
336
|
},
|
|
475
337
|
}
|
|
476
338
|
}
|
|
339
|
+
case 'open-create-workspace-modal':
|
|
340
|
+
return {
|
|
341
|
+
...state,
|
|
342
|
+
focusMode: 'command-edit',
|
|
343
|
+
modal: {
|
|
344
|
+
activeField: 'prompt',
|
|
345
|
+
baseBranches: [],
|
|
346
|
+
baseQuery: '',
|
|
347
|
+
// Left empty on purpose: `load-create-workspace-base-branches`
|
|
348
|
+
// resolves the repo's default branch and backfills it below. Seeding
|
|
349
|
+
// the active workspace's branch here would win over that.
|
|
350
|
+
baseRef: '',
|
|
351
|
+
branchError: null,
|
|
352
|
+
cursorPos: 0,
|
|
353
|
+
editBuffer: '',
|
|
354
|
+
projectTargetId: null,
|
|
355
|
+
prompt: '',
|
|
356
|
+
selectedIndex: 0,
|
|
357
|
+
type: 'create-workspace',
|
|
358
|
+
},
|
|
359
|
+
}
|
|
360
|
+
case 'switch-create-workspace-field': {
|
|
361
|
+
if (state.modal.type !== 'create-workspace') return state
|
|
362
|
+
const next =
|
|
363
|
+
CREATE_WORKSPACE_FIELDS[
|
|
364
|
+
(CREATE_WORKSPACE_FIELDS.indexOf(state.modal.activeField) + 1) %
|
|
365
|
+
CREATE_WORKSPACE_FIELDS.length
|
|
366
|
+
] ?? 'prompt'
|
|
367
|
+
const buffer = getCreateWorkspaceFieldValue(state.modal, next)
|
|
368
|
+
// Entering the base field highlights the row matching the resolved ref,
|
|
369
|
+
// so the picker opens on what the form already says it will fork from.
|
|
370
|
+
const baseIndex =
|
|
371
|
+
next === 'base'
|
|
372
|
+
? Math.max(
|
|
373
|
+
0,
|
|
374
|
+
getCreateWorkspaceBaseOptions(state).findIndex(
|
|
375
|
+
(option) => option.ref === (state.modal as { baseRef: string }).baseRef
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
: 0
|
|
379
|
+
return {
|
|
380
|
+
...state,
|
|
381
|
+
modal: {
|
|
382
|
+
...state.modal,
|
|
383
|
+
activeField: next,
|
|
384
|
+
cursorPos: buffer.length,
|
|
385
|
+
selectedIndex: baseIndex,
|
|
386
|
+
},
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
case 'set-create-workspace-base-branches': {
|
|
390
|
+
if (state.modal.type !== 'create-workspace') return state
|
|
391
|
+
const modalWithBranches = { ...state.modal, baseBranches: action.branches }
|
|
392
|
+
const withBranches: AppState = { ...state, modal: modalWithBranches }
|
|
393
|
+
// The user may already have typed a base while this was in flight.
|
|
394
|
+
if (state.modal.baseRef !== '') return withBranches
|
|
395
|
+
// Fork from the repo's default branch, falling back to the most recently
|
|
396
|
+
// committed one when the repo has neither an origin/HEAD nor a main.
|
|
397
|
+
const fallback = getCreateWorkspaceBaseOptions(withBranches)[0]?.ref ?? ''
|
|
398
|
+
const defaultBranch =
|
|
399
|
+
action.defaultBranch != null && action.branches.includes(action.defaultBranch)
|
|
400
|
+
? action.defaultBranch
|
|
401
|
+
: fallback
|
|
402
|
+
return {
|
|
403
|
+
...withBranches,
|
|
404
|
+
modal: { ...modalWithBranches, baseRef: defaultBranch },
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
case 'set-create-workspace-branch-error': {
|
|
408
|
+
if (state.modal.type !== 'create-workspace') return state
|
|
409
|
+
return {
|
|
410
|
+
...state,
|
|
411
|
+
modal: {
|
|
412
|
+
...state.modal,
|
|
413
|
+
// The branch is generated now, so there is no branch field to focus:
|
|
414
|
+
// send the user back to the prompt, which is what produced the name.
|
|
415
|
+
activeField: 'prompt',
|
|
416
|
+
branchError: action.message,
|
|
417
|
+
cursorPos: state.modal.prompt.length,
|
|
418
|
+
},
|
|
419
|
+
}
|
|
420
|
+
}
|
|
477
421
|
case 'open-snippet-picker':
|
|
478
422
|
return {
|
|
479
423
|
...state,
|
|
@@ -482,8 +426,9 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
482
426
|
actionMessage: null,
|
|
483
427
|
cursorPos: 0,
|
|
484
428
|
editBuffer: '',
|
|
429
|
+
projectTargetId: null,
|
|
430
|
+
returnTo: action.returnTo,
|
|
485
431
|
selectedIndex: 0,
|
|
486
|
-
sessionTargetId: null,
|
|
487
432
|
type: 'snippet-picker',
|
|
488
433
|
},
|
|
489
434
|
}
|
|
@@ -507,8 +452,8 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
507
452
|
cursorPos: initialName.length,
|
|
508
453
|
editBuffer: initialName,
|
|
509
454
|
nameBuffer: initialName,
|
|
455
|
+
projectTargetId: snippet?.id ?? null,
|
|
510
456
|
selectedIndex: 0,
|
|
511
|
-
sessionTargetId: snippet?.id ?? null,
|
|
512
457
|
triggerBuffer: snippet?.trigger ?? '',
|
|
513
458
|
type: 'snippet-editor',
|
|
514
459
|
},
|
|
@@ -522,8 +467,9 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
522
467
|
cursorPos: 0,
|
|
523
468
|
editBuffer: '',
|
|
524
469
|
entryCount: 0,
|
|
470
|
+
projectTargetId: null,
|
|
471
|
+
returnTo: action.returnTo,
|
|
525
472
|
selectedIndex: 0,
|
|
526
|
-
sessionTargetId: null,
|
|
527
473
|
type: 'theme-picker',
|
|
528
474
|
},
|
|
529
475
|
}
|
|
@@ -536,128 +482,11 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
536
482
|
cursorPos: 0,
|
|
537
483
|
editBuffer: null,
|
|
538
484
|
latestVersion: action.latestVersion,
|
|
485
|
+
projectTargetId: null,
|
|
539
486
|
selectedIndex: 0,
|
|
540
|
-
sessionTargetId: null,
|
|
541
487
|
type: 'update-available',
|
|
542
488
|
},
|
|
543
489
|
}
|
|
544
|
-
case 'open-git-commit-modal': {
|
|
545
|
-
const sessionId = action.sessionId
|
|
546
|
-
return {
|
|
547
|
-
...state,
|
|
548
|
-
focusMode: 'command-edit',
|
|
549
|
-
modal: {
|
|
550
|
-
activeField: 'title',
|
|
551
|
-
contentBuffer: '',
|
|
552
|
-
cursorPos: 0,
|
|
553
|
-
editBuffer: '',
|
|
554
|
-
selectedIndex: 0,
|
|
555
|
-
sessionTargetId: sessionId ?? null,
|
|
556
|
-
stage: 'edit',
|
|
557
|
-
type: 'git-commit',
|
|
558
|
-
},
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
case 'git-commit-enter-confirm': {
|
|
562
|
-
if (state.modal.type !== 'git-commit') return state
|
|
563
|
-
return {
|
|
564
|
-
...state,
|
|
565
|
-
focusMode: 'command-edit',
|
|
566
|
-
modal: { ...state.modal, stage: 'confirm' },
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
case 'git-commit-leave-confirm': {
|
|
570
|
-
if (state.modal.type !== 'git-commit') return state
|
|
571
|
-
return {
|
|
572
|
-
...state,
|
|
573
|
-
focusMode: 'command-edit',
|
|
574
|
-
modal: { ...state.modal, stage: 'edit' },
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
case 'git-commit-enter-generating': {
|
|
578
|
-
if (state.modal.type !== 'git-commit') return state
|
|
579
|
-
return {
|
|
580
|
-
...state,
|
|
581
|
-
focusMode: 'modal',
|
|
582
|
-
modal: { ...state.modal, sessionTargetId: action.sessionId, stage: 'generating' },
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
case 'git-commit-leave-generating': {
|
|
586
|
-
if (state.modal.type !== 'git-commit') return state
|
|
587
|
-
return {
|
|
588
|
-
...state,
|
|
589
|
-
focusMode: 'command-edit',
|
|
590
|
-
modal: { ...state.modal, stage: 'edit' },
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
case 'auto-commit-generation-ready': {
|
|
594
|
-
if (
|
|
595
|
-
state.modal.type !== 'git-commit' ||
|
|
596
|
-
state.modal.stage !== 'generating' ||
|
|
597
|
-
state.modal.sessionTargetId !== action.sessionId
|
|
598
|
-
) {
|
|
599
|
-
return null
|
|
600
|
-
}
|
|
601
|
-
const nextAutoCommit = reduceAutoCommitState(state.autoCommit, action)
|
|
602
|
-
if (!nextAutoCommit) {
|
|
603
|
-
// Stale result (hash mismatch or slice was cleared mid-flight): don't
|
|
604
|
-
// strand the modal in `generating` — flip back to edit so the user
|
|
605
|
-
// isn't stuck staring at a spinner that will never resolve.
|
|
606
|
-
return {
|
|
607
|
-
...state,
|
|
608
|
-
focusMode: 'command-edit',
|
|
609
|
-
modal: { ...state.modal, stage: 'edit' },
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
return {
|
|
613
|
-
...state,
|
|
614
|
-
autoCommit: nextAutoCommit,
|
|
615
|
-
focusMode: 'command-edit',
|
|
616
|
-
modal: {
|
|
617
|
-
...state.modal,
|
|
618
|
-
activeField: 'title',
|
|
619
|
-
contentBuffer: action.body,
|
|
620
|
-
cursorPos: action.title.length,
|
|
621
|
-
editBuffer: action.title,
|
|
622
|
-
stage: 'confirm',
|
|
623
|
-
},
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
case 'git-commit-use-background-suggestion': {
|
|
627
|
-
if (state.modal.type !== 'git-commit' || state.modal.sessionTargetId !== action.sessionId) {
|
|
628
|
-
return null
|
|
629
|
-
}
|
|
630
|
-
const suggestion = state.autoCommit.bySession[action.sessionId]
|
|
631
|
-
if (!suggestion || suggestion.kind !== 'ready') return null
|
|
632
|
-
return {
|
|
633
|
-
...state,
|
|
634
|
-
focusMode: 'command-edit',
|
|
635
|
-
modal: {
|
|
636
|
-
...state.modal,
|
|
637
|
-
activeField: 'title',
|
|
638
|
-
contentBuffer: suggestion.body,
|
|
639
|
-
cursorPos: suggestion.title.length,
|
|
640
|
-
editBuffer: suggestion.title,
|
|
641
|
-
stage: 'confirm',
|
|
642
|
-
},
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
case 'auto-commit-clear': {
|
|
646
|
-
if (
|
|
647
|
-
state.modal.type !== 'git-commit' ||
|
|
648
|
-
state.modal.stage !== 'generating' ||
|
|
649
|
-
state.modal.sessionTargetId !== action.sessionId
|
|
650
|
-
) {
|
|
651
|
-
return null
|
|
652
|
-
}
|
|
653
|
-
const nextAutoCommit = reduceAutoCommitState(state.autoCommit, action)
|
|
654
|
-
return {
|
|
655
|
-
...state,
|
|
656
|
-
autoCommit: nextAutoCommit ?? state.autoCommit,
|
|
657
|
-
focusMode: 'command-edit',
|
|
658
|
-
modal: { ...state.modal, stage: 'edit' },
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
490
|
case 'set-help-entry-count': {
|
|
662
491
|
if (state.modal.type !== 'help') return state
|
|
663
492
|
if (state.modal.entryCount === action.count) {
|
|
@@ -689,13 +518,14 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
689
518
|
// so leave it alone — closing returns to whatever was underneath.
|
|
690
519
|
if (
|
|
691
520
|
closingType === 'help' ||
|
|
692
|
-
closingType === '
|
|
521
|
+
closingType === 'workspace-move' ||
|
|
693
522
|
closingType === 'flash-jump'
|
|
694
523
|
) {
|
|
695
524
|
return { ...state, modal: emptyModal() }
|
|
696
525
|
}
|
|
697
|
-
|
|
698
|
-
|
|
526
|
+
// Whoever opened it said where to go back to, because that is who knows
|
|
527
|
+
// what is drawn behind it. Everything else came from the panes.
|
|
528
|
+
return { ...state, focusMode: state.modal.returnTo ?? 'navigation', modal: emptyModal() }
|
|
699
529
|
}
|
|
700
530
|
case 'move-modal-selection': {
|
|
701
531
|
if (state.modal.type === 'help') {
|
|
@@ -708,22 +538,23 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
708
538
|
}
|
|
709
539
|
if (
|
|
710
540
|
state.modal.type !== 'new-tab' &&
|
|
711
|
-
state.modal.type !== '
|
|
541
|
+
state.modal.type !== 'project-picker' &&
|
|
712
542
|
state.modal.type !== 'snippet-picker' &&
|
|
713
543
|
state.modal.type !== 'theme-picker' &&
|
|
714
|
-
state.modal.type !== 'create-
|
|
544
|
+
state.modal.type !== 'create-project' &&
|
|
545
|
+
state.modal.type !== 'create-workspace' &&
|
|
715
546
|
state.modal.type !== 'split-picker' &&
|
|
716
547
|
state.modal.type !== 'update-available' &&
|
|
717
|
-
state.modal.type !== '
|
|
548
|
+
state.modal.type !== 'workspace-move'
|
|
718
549
|
) {
|
|
719
550
|
return state
|
|
720
551
|
}
|
|
721
|
-
if (state.modal.type === 'create-
|
|
552
|
+
if (state.modal.type === 'create-project' && state.modal.activeField !== 'directory') {
|
|
722
553
|
return state
|
|
723
554
|
}
|
|
724
|
-
if (state.modal.type === '
|
|
555
|
+
if (state.modal.type === 'create-workspace') {
|
|
725
556
|
if (state.modal.activeField !== 'base') return state
|
|
726
|
-
const options =
|
|
557
|
+
const options = getCreateWorkspaceBaseOptions(state)
|
|
727
558
|
if (options.length === 0) return state
|
|
728
559
|
const next = (state.modal.selectedIndex + action.delta + options.length) % options.length
|
|
729
560
|
return {
|
|
@@ -735,36 +566,7 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
735
566
|
},
|
|
736
567
|
}
|
|
737
568
|
}
|
|
738
|
-
|
|
739
|
-
if (state.modal.type === 'new-tab') {
|
|
740
|
-
if (state.modal.step === 'worktree') {
|
|
741
|
-
if (state.modal.activeField === 'worktree-name') return state
|
|
742
|
-
optionCount = getCurrentWorktreeCount(state) + 1
|
|
743
|
-
} else if (state.modal.step === 'template') {
|
|
744
|
-
optionCount =
|
|
745
|
-
state.worktreeTemplates.length + getTemplateNoneOffset(state.modal.selectedAssistantId)
|
|
746
|
-
} else {
|
|
747
|
-
optionCount =
|
|
748
|
-
filterAssistants(getAllAssistantOptions(state.customCommands), state.modal.editBuffer)
|
|
749
|
-
.length + (state.worktreeTemplates.length > 0 ? 1 : 0)
|
|
750
|
-
}
|
|
751
|
-
} else if (state.modal.type === 'split-picker') {
|
|
752
|
-
optionCount = getAllAssistantOptions(state.customCommands).length
|
|
753
|
-
} else if (state.modal.type === 'create-session') {
|
|
754
|
-
optionCount = state.modal.directoryResults.length
|
|
755
|
-
} else if (state.modal.type === 'snippet-picker') {
|
|
756
|
-
const filtered = filterSnippets(state.snippets, state.modal.editBuffer)
|
|
757
|
-
optionCount = filtered.length
|
|
758
|
-
} else if (state.modal.type === 'theme-picker') {
|
|
759
|
-
optionCount = filterThemeIds(state.modal.editBuffer).length
|
|
760
|
-
} else if (state.modal.type === 'update-available') {
|
|
761
|
-
optionCount = 2
|
|
762
|
-
} else if (state.modal.type === 'worktree-move') {
|
|
763
|
-
optionCount = Math.max(0, getCurrentWorktreeCount(state) - 1)
|
|
764
|
-
} else {
|
|
765
|
-
const filtered = filterSessions(state.sessions, state.modal.editBuffer)
|
|
766
|
-
optionCount = Math.max(1, filtered.length + 1)
|
|
767
|
-
}
|
|
569
|
+
const optionCount = getModalOptionCount(state)
|
|
768
570
|
if (optionCount === 0) {
|
|
769
571
|
return state
|
|
770
572
|
}
|
|
@@ -773,34 +575,17 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
773
575
|
modal: {
|
|
774
576
|
...state.modal,
|
|
775
577
|
selectedIndex: (state.modal.selectedIndex + action.delta + optionCount) % optionCount,
|
|
776
|
-
...(state.modal.type === 'new-tab' && state.modal.step === 'worktree'
|
|
777
|
-
? {
|
|
778
|
-
createWorktree:
|
|
779
|
-
(state.modal.selectedIndex + action.delta + optionCount) % optionCount ===
|
|
780
|
-
optionCount - 1,
|
|
781
|
-
worktreeDeletePrompt: null,
|
|
782
|
-
}
|
|
783
|
-
: null),
|
|
784
578
|
},
|
|
785
579
|
}
|
|
786
580
|
}
|
|
787
581
|
case 'set-modal-selection-index': {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
state.modal.type !== 'create-session' &&
|
|
794
|
-
state.modal.type !== 'split-picker' &&
|
|
795
|
-
state.modal.type !== 'update-available' &&
|
|
796
|
-
state.modal.type !== 'worktree-move' &&
|
|
797
|
-
state.modal.type !== 'help'
|
|
798
|
-
) {
|
|
799
|
-
return state
|
|
800
|
-
}
|
|
801
|
-
if (state.modal.type === 'new-tab' && state.modal.step === 'worktree-create') {
|
|
582
|
+
// Which modals have an indexed list is `getModalOptionCount`'s question,
|
|
583
|
+
// and it answers 0 for the ones that don't. It used to be asked twice —
|
|
584
|
+
// here as a list of types to keep in step by hand, which the settings
|
|
585
|
+
// search promptly wasn't on, leaving its rows unhoverable.
|
|
586
|
+
if (state.modal.type === 'create-workspace') {
|
|
802
587
|
if (state.modal.activeField !== 'base') return state
|
|
803
|
-
const options =
|
|
588
|
+
const options = getCreateWorkspaceBaseOptions(state)
|
|
804
589
|
if (options.length === 0) return state
|
|
805
590
|
const clamped = Math.max(0, Math.min(options.length - 1, action.index))
|
|
806
591
|
if (clamped === state.modal.selectedIndex) return state
|
|
@@ -813,71 +598,15 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
813
598
|
},
|
|
814
599
|
}
|
|
815
600
|
}
|
|
816
|
-
|
|
817
|
-
if (state.modal.type === 'help') {
|
|
818
|
-
optionCount = state.modal.entryCount
|
|
819
|
-
} else if (state.modal.type === 'new-tab') {
|
|
820
|
-
if (state.modal.step === 'worktree') {
|
|
821
|
-
optionCount = getCurrentWorktreeCount(state) + 1
|
|
822
|
-
} else if (state.modal.step === 'template') {
|
|
823
|
-
optionCount =
|
|
824
|
-
state.worktreeTemplates.length + getTemplateNoneOffset(state.modal.selectedAssistantId)
|
|
825
|
-
} else {
|
|
826
|
-
optionCount =
|
|
827
|
-
filterAssistants(getAllAssistantOptions(state.customCommands), state.modal.editBuffer)
|
|
828
|
-
.length + (state.worktreeTemplates.length > 0 ? 1 : 0)
|
|
829
|
-
}
|
|
830
|
-
} else if (state.modal.type === 'split-picker') {
|
|
831
|
-
optionCount = getAllAssistantOptions(state.customCommands).length
|
|
832
|
-
} else if (state.modal.type === 'create-session') {
|
|
833
|
-
optionCount = state.modal.directoryResults.length
|
|
834
|
-
} else if (state.modal.type === 'snippet-picker') {
|
|
835
|
-
optionCount = filterSnippets(state.snippets, state.modal.editBuffer).length
|
|
836
|
-
} else if (state.modal.type === 'theme-picker') {
|
|
837
|
-
optionCount = filterThemeIds(state.modal.editBuffer).length
|
|
838
|
-
} else if (state.modal.type === 'update-available') {
|
|
839
|
-
optionCount = 2
|
|
840
|
-
} else if (state.modal.type === 'worktree-move') {
|
|
841
|
-
optionCount = Math.max(0, getCurrentWorktreeCount(state) - 1)
|
|
842
|
-
} else {
|
|
843
|
-
optionCount = Math.max(1, filterSessions(state.sessions, state.modal.editBuffer).length + 1)
|
|
844
|
-
}
|
|
601
|
+
const optionCount = getModalOptionCount(state)
|
|
845
602
|
if (optionCount === 0) return state
|
|
846
603
|
const clamped = Math.max(0, Math.min(optionCount - 1, action.index))
|
|
847
|
-
if (state.modal.type === 'new-tab' && state.modal.step === 'worktree') {
|
|
848
|
-
if (clamped === state.modal.selectedIndex) return state
|
|
849
|
-
return {
|
|
850
|
-
...state,
|
|
851
|
-
modal: {
|
|
852
|
-
...state.modal,
|
|
853
|
-
createWorktree: clamped === optionCount - 1,
|
|
854
|
-
selectedIndex: clamped,
|
|
855
|
-
worktreeDeletePrompt: null,
|
|
856
|
-
},
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
604
|
if (clamped === state.modal.selectedIndex) return state
|
|
860
605
|
return { ...state, modal: { ...state.modal, selectedIndex: clamped } }
|
|
861
606
|
}
|
|
862
607
|
case 'move-modal-cursor': {
|
|
863
608
|
if (state.modal.editBuffer === null) return state
|
|
864
|
-
|
|
865
|
-
if (state.modal.type === 'new-tab' && state.modal.editingCommand === null) {
|
|
866
|
-
if (state.modal.activeField === 'worktree-name') {
|
|
867
|
-
editableValue = state.modal.worktreeName
|
|
868
|
-
} else if (state.modal.activeField === 'branch-name') {
|
|
869
|
-
editableValue = state.modal.branchName
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
if (
|
|
873
|
-
state.modal.type === 'new-tab' &&
|
|
874
|
-
state.modal.editingCommand === null &&
|
|
875
|
-
(state.modal.step === 'template' ||
|
|
876
|
-
(state.modal.step === 'worktree' && state.modal.activeField === 'target-worktree'))
|
|
877
|
-
) {
|
|
878
|
-
return state
|
|
879
|
-
}
|
|
880
|
-
const len = editableValue.length
|
|
609
|
+
const len = state.modal.editBuffer.length
|
|
881
610
|
const current = state.modal.cursorPos ?? len
|
|
882
611
|
let next = current
|
|
883
612
|
if (action.to === 'home') next = 0
|
|
@@ -924,27 +653,43 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
924
653
|
}
|
|
925
654
|
return { ...state, modal: { ...state.modal, buffer: nextBuffer, pendingJump: null } }
|
|
926
655
|
}
|
|
927
|
-
if (state.modal.
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
656
|
+
if (state.modal.type === 'create-workspace') {
|
|
657
|
+
const field = state.modal.activeField
|
|
658
|
+
const current = getCreateWorkspaceFieldValue(state.modal, field)
|
|
659
|
+
const at = clampCursor(state.modal.cursorPos ?? current.length, current.length)
|
|
660
|
+
let text: string
|
|
661
|
+
let pos: number
|
|
662
|
+
if (action.char === '\b') {
|
|
663
|
+
if (at === 0) return state
|
|
664
|
+
text = current.slice(0, at - 1) + current.slice(at)
|
|
665
|
+
pos = at - 1
|
|
666
|
+
} else {
|
|
667
|
+
text = current.slice(0, at) + action.char + current.slice(at)
|
|
668
|
+
pos = at + action.char.length
|
|
669
|
+
}
|
|
670
|
+
if (field === 'prompt') {
|
|
671
|
+
return {
|
|
672
|
+
...state,
|
|
673
|
+
modal: { ...state.modal, branchError: null, cursorPos: pos, prompt: text },
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
// Re-filter and snap the base to the top match as the query changes.
|
|
677
|
+
const topOption = getCreateWorkspaceBaseOptions(state, text)[0]
|
|
678
|
+
return {
|
|
679
|
+
...state,
|
|
680
|
+
modal: {
|
|
681
|
+
...state.modal,
|
|
682
|
+
baseQuery: text,
|
|
683
|
+
baseRef: topOption?.ref ?? state.modal.baseRef,
|
|
684
|
+
cursorPos: pos,
|
|
685
|
+
selectedIndex: 0,
|
|
686
|
+
},
|
|
938
687
|
}
|
|
939
688
|
}
|
|
940
|
-
if (
|
|
941
|
-
state.modal.type === 'new-tab' &&
|
|
942
|
-
state.modal.editingCommand === null &&
|
|
943
|
-
(state.modal.step === 'template' ||
|
|
944
|
-
(state.modal.step === 'worktree' && state.modal.activeField === 'target-worktree'))
|
|
945
|
-
) {
|
|
689
|
+
if (state.modal.editBuffer === null) {
|
|
946
690
|
return state
|
|
947
691
|
}
|
|
692
|
+
const buffer = state.modal.editBuffer
|
|
948
693
|
const cursor = clampCursor(state.modal.cursorPos ?? buffer.length, buffer.length)
|
|
949
694
|
let nextBuffer: string
|
|
950
695
|
let nextCursor: number
|
|
@@ -957,56 +702,9 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
957
702
|
nextCursor = cursor + action.char.length
|
|
958
703
|
}
|
|
959
704
|
const isNewTabEditing = state.modal.type === 'new-tab' && state.modal.editingCommand !== null
|
|
960
|
-
if (
|
|
961
|
-
state.modal.type === 'new-tab' &&
|
|
962
|
-
state.modal.editingCommand === null &&
|
|
963
|
-
state.modal.activeField === 'worktree-name'
|
|
964
|
-
) {
|
|
965
|
-
return {
|
|
966
|
-
...state,
|
|
967
|
-
modal: {
|
|
968
|
-
...state.modal,
|
|
969
|
-
cursorPos: nextCursor,
|
|
970
|
-
worktreeName: nextBuffer,
|
|
971
|
-
},
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
if (
|
|
975
|
-
state.modal.type === 'new-tab' &&
|
|
976
|
-
state.modal.editingCommand === null &&
|
|
977
|
-
state.modal.activeField === 'branch-name'
|
|
978
|
-
) {
|
|
979
|
-
return {
|
|
980
|
-
...state,
|
|
981
|
-
modal: {
|
|
982
|
-
...state.modal,
|
|
983
|
-
branchError: null,
|
|
984
|
-
branchName: nextBuffer,
|
|
985
|
-
cursorPos: nextCursor,
|
|
986
|
-
},
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
if (
|
|
990
|
-
state.modal.type === 'new-tab' &&
|
|
991
|
-
state.modal.editingCommand === null &&
|
|
992
|
-
state.modal.activeField === 'base'
|
|
993
|
-
) {
|
|
994
|
-
// Re-filter and snap the selection/base to the top match as the query changes.
|
|
995
|
-
const topOption = getNewTabBaseOptions(state, nextBuffer)[0]
|
|
996
|
-
return {
|
|
997
|
-
...state,
|
|
998
|
-
modal: {
|
|
999
|
-
...state.modal,
|
|
1000
|
-
baseQuery: nextBuffer,
|
|
1001
|
-
baseRef: topOption?.ref ?? state.modal.baseRef,
|
|
1002
|
-
cursorPos: nextCursor,
|
|
1003
|
-
selectedIndex: 0,
|
|
1004
|
-
},
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
705
|
const resetIndex =
|
|
1008
706
|
!isNewTabEditing &&
|
|
1009
|
-
(state.modal.type === '
|
|
707
|
+
(state.modal.type === 'project-picker' ||
|
|
1010
708
|
state.modal.type === 'snippet-picker' ||
|
|
1011
709
|
state.modal.type === 'theme-picker' ||
|
|
1012
710
|
state.modal.type === 'new-tab' ||
|
|
@@ -1035,67 +733,12 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
1035
733
|
},
|
|
1036
734
|
}
|
|
1037
735
|
}
|
|
1038
|
-
if (state.modal.type === '
|
|
1039
|
-
if (state.modal.step === 'template') {
|
|
1040
|
-
return {
|
|
1041
|
-
...state,
|
|
1042
|
-
modal: {
|
|
1043
|
-
...state.modal,
|
|
1044
|
-
activeField: 'worktree-name',
|
|
1045
|
-
cursorPos: state.modal.worktreeName.length,
|
|
1046
|
-
selectedIndex: 0,
|
|
1047
|
-
step: 'worktree-create',
|
|
1048
|
-
},
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
if (state.modal.step === 'worktree-create') {
|
|
1052
|
-
const optionCount = getCurrentWorktreeCount(state) + 1
|
|
1053
|
-
return {
|
|
1054
|
-
...state,
|
|
1055
|
-
modal: {
|
|
1056
|
-
...state.modal,
|
|
1057
|
-
activeField: 'target-worktree',
|
|
1058
|
-
branchError: null,
|
|
1059
|
-
createWorktree: false,
|
|
1060
|
-
cursorPos: 0,
|
|
1061
|
-
selectedIndex: Math.min(
|
|
1062
|
-
state.modal.targetWorktreeIndex,
|
|
1063
|
-
Math.max(0, optionCount - 1)
|
|
1064
|
-
),
|
|
1065
|
-
step: 'worktree',
|
|
1066
|
-
},
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
if (state.modal.step === 'worktree') {
|
|
1070
|
-
const selectedAssistantId = state.modal.selectedAssistantId
|
|
1071
|
-
const assistants = filterAssistants(
|
|
1072
|
-
getAllAssistantOptions(state.customCommands),
|
|
1073
|
-
state.modal.editBuffer
|
|
1074
|
-
)
|
|
1075
|
-
const assistantIndex = assistants.findIndex(
|
|
1076
|
-
(assistant) => assistant.id === selectedAssistantId
|
|
1077
|
-
)
|
|
1078
|
-
return {
|
|
1079
|
-
...state,
|
|
1080
|
-
modal: {
|
|
1081
|
-
...state.modal,
|
|
1082
|
-
activeField: 'assistant',
|
|
1083
|
-
createWorktree: false,
|
|
1084
|
-
cursorPos: state.modal.editBuffer?.length ?? 0,
|
|
1085
|
-
selectedIndex: Math.max(0, assistantIndex),
|
|
1086
|
-
step: 'assistant',
|
|
1087
|
-
worktreeDeletePrompt: null,
|
|
1088
|
-
},
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
return { ...state, focusMode: 'navigation', modal: emptyModal() }
|
|
1092
|
-
}
|
|
1093
|
-
if (state.modal.type === 'create-session' || state.modal.type === 'snippet-editor') {
|
|
736
|
+
if (state.modal.type === 'create-project' || state.modal.type === 'snippet-editor') {
|
|
1094
737
|
return { ...state, focusMode: 'navigation', modal: emptyModal() }
|
|
1095
738
|
}
|
|
1096
739
|
// Pickers and overlays with auto-filter — Esc closes the modal entirely.
|
|
1097
740
|
if (
|
|
1098
|
-
state.modal.type === '
|
|
741
|
+
state.modal.type === 'project-picker' ||
|
|
1099
742
|
state.modal.type === 'snippet-picker' ||
|
|
1100
743
|
state.modal.type === 'theme-picker' ||
|
|
1101
744
|
state.modal.type === 'help'
|
|
@@ -1108,8 +751,8 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
1108
751
|
modal: { ...state.modal, cursorPos: 0, editBuffer: null },
|
|
1109
752
|
}
|
|
1110
753
|
}
|
|
1111
|
-
case 'switch-create-
|
|
1112
|
-
if (state.modal.type === 'create-
|
|
754
|
+
case 'switch-create-project-field': {
|
|
755
|
+
if (state.modal.type === 'create-project') {
|
|
1113
756
|
const nextField = state.modal.activeField === 'directory' ? 'name' : 'directory'
|
|
1114
757
|
const nextEdit = state.modal.nameBuffer
|
|
1115
758
|
return {
|
|
@@ -1170,72 +813,10 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
1170
813
|
},
|
|
1171
814
|
}
|
|
1172
815
|
}
|
|
1173
|
-
if (state.modal.type === 'new-tab') {
|
|
1174
|
-
if (state.modal.step === 'assistant') return state
|
|
1175
|
-
if (state.modal.step === 'worktree-create') {
|
|
1176
|
-
const cycle: Record<
|
|
1177
|
-
'worktree-name' | 'branch-name' | 'base',
|
|
1178
|
-
typeof state.modal.activeField
|
|
1179
|
-
> = {
|
|
1180
|
-
'base': 'worktree-name',
|
|
1181
|
-
'branch-name': 'base',
|
|
1182
|
-
'worktree-name': 'branch-name',
|
|
1183
|
-
}
|
|
1184
|
-
const nextField =
|
|
1185
|
-
state.modal.activeField === 'worktree-name' ||
|
|
1186
|
-
state.modal.activeField === 'branch-name' ||
|
|
1187
|
-
state.modal.activeField === 'base'
|
|
1188
|
-
? cycle[state.modal.activeField]
|
|
1189
|
-
: 'worktree-name'
|
|
1190
|
-
if (nextField === 'base') {
|
|
1191
|
-
// Highlight the row matching the resolved base ref when entering it.
|
|
1192
|
-
const currentBaseRef = state.modal.baseRef
|
|
1193
|
-
const options = getNewTabBaseOptions(state)
|
|
1194
|
-
const baseIndex = Math.max(
|
|
1195
|
-
0,
|
|
1196
|
-
options.findIndex((option) => option.ref === currentBaseRef)
|
|
1197
|
-
)
|
|
1198
|
-
return {
|
|
1199
|
-
...state,
|
|
1200
|
-
modal: {
|
|
1201
|
-
...state.modal,
|
|
1202
|
-
activeField: nextField,
|
|
1203
|
-
cursorPos: state.modal.baseQuery.length,
|
|
1204
|
-
selectedIndex: baseIndex,
|
|
1205
|
-
},
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
const nextValue =
|
|
1209
|
-
nextField === 'branch-name' ? state.modal.branchName : state.modal.worktreeName
|
|
1210
|
-
return {
|
|
1211
|
-
...state,
|
|
1212
|
-
modal: {
|
|
1213
|
-
...state.modal,
|
|
1214
|
-
activeField: nextField,
|
|
1215
|
-
cursorPos: nextValue.length,
|
|
1216
|
-
selectedIndex: 0,
|
|
1217
|
-
},
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
let nextField: typeof state.modal.activeField = 'target-worktree'
|
|
1221
|
-
if (state.modal.activeField === 'target-worktree' && state.modal.createWorktree) {
|
|
1222
|
-
nextField = 'worktree-name'
|
|
1223
|
-
}
|
|
1224
|
-
const nextValue =
|
|
1225
|
-
nextField === 'worktree-name' ? state.modal.worktreeName : (state.modal.editBuffer ?? '')
|
|
1226
|
-
return {
|
|
1227
|
-
...state,
|
|
1228
|
-
modal: {
|
|
1229
|
-
...state.modal,
|
|
1230
|
-
activeField: nextField,
|
|
1231
|
-
cursorPos: nextValue.length,
|
|
1232
|
-
},
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
816
|
return state
|
|
1236
817
|
}
|
|
1237
818
|
case 'select-directory': {
|
|
1238
|
-
if (state.modal.type !== 'create-
|
|
819
|
+
if (state.modal.type !== 'create-project') {
|
|
1239
820
|
return state
|
|
1240
821
|
}
|
|
1241
822
|
const selected = state.modal.directoryResults[state.modal.selectedIndex]
|
|
@@ -1262,6 +843,34 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
1262
843
|
},
|
|
1263
844
|
}
|
|
1264
845
|
}
|
|
846
|
+
case 'open-settings-search':
|
|
847
|
+
return {
|
|
848
|
+
...state,
|
|
849
|
+
focusMode: 'command-edit',
|
|
850
|
+
modal: {
|
|
851
|
+
cursorPos: 0,
|
|
852
|
+
editBuffer: '',
|
|
853
|
+
projectTargetId: null,
|
|
854
|
+
returnTo: 'settings',
|
|
855
|
+
selectedIndex: 0,
|
|
856
|
+
type: 'settings-search',
|
|
857
|
+
},
|
|
858
|
+
}
|
|
859
|
+
case 'open-setting-text-modal':
|
|
860
|
+
return {
|
|
861
|
+
...state,
|
|
862
|
+
focusMode: 'command-edit',
|
|
863
|
+
modal: {
|
|
864
|
+
cursorPos: action.value.length,
|
|
865
|
+
editBuffer: action.value,
|
|
866
|
+
projectTargetId: null,
|
|
867
|
+
returnTo: 'settings',
|
|
868
|
+
selectedIndex: 0,
|
|
869
|
+
settingId: action.settingId,
|
|
870
|
+
settingLabel: action.label,
|
|
871
|
+
type: 'setting-text',
|
|
872
|
+
},
|
|
873
|
+
}
|
|
1265
874
|
case 'open-rename-tab-modal': {
|
|
1266
875
|
const activeTab =
|
|
1267
876
|
state.activeTabId != null && state.activeTabId !== ''
|
|
@@ -1276,23 +885,23 @@ export function reduceModalState(state: AppState, action: AppAction): AppState |
|
|
|
1276
885
|
modal: {
|
|
1277
886
|
cursorPos: activeTab.title.length,
|
|
1278
887
|
editBuffer: activeTab.title,
|
|
888
|
+
projectTargetId: activeTab.id,
|
|
1279
889
|
selectedIndex: 0,
|
|
1280
|
-
sessionTargetId: activeTab.id,
|
|
1281
890
|
type: 'rename-tab',
|
|
1282
891
|
},
|
|
1283
892
|
}
|
|
1284
893
|
}
|
|
1285
|
-
case 'open-rename-
|
|
894
|
+
case 'open-rename-workspace-modal': {
|
|
1286
895
|
return {
|
|
1287
896
|
...state,
|
|
1288
897
|
focusMode: 'command-edit',
|
|
1289
898
|
modal: {
|
|
1290
899
|
cursorPos: action.initialName.length,
|
|
1291
900
|
editBuffer: action.initialName,
|
|
901
|
+
projectTargetId: action.workspaceId,
|
|
1292
902
|
selectedIndex: 0,
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
worktreeSessionId: action.sessionId,
|
|
903
|
+
type: 'rename-workspace',
|
|
904
|
+
workspaceProjectId: action.projectId,
|
|
1296
905
|
},
|
|
1297
906
|
}
|
|
1298
907
|
}
|