@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
|
@@ -10,35 +10,35 @@ import { AutoComplete, Form, type FormOptionItem, TextField } from '../shared/fo
|
|
|
10
10
|
const VISIBLE_ROWS = 8
|
|
11
11
|
|
|
12
12
|
function getDirectoryResultIcon(result: DirectoryResult): string {
|
|
13
|
-
if (result.type === '
|
|
14
|
-
if (result.type === '
|
|
13
|
+
if (result.type === 'workspace') return '\u{e728}'
|
|
14
|
+
if (result.type === 'project') return '\u{f07c}'
|
|
15
15
|
return '\u{e702}'
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function getDirectoryResultColor(result: DirectoryResult): string {
|
|
19
19
|
const t = getCurrentTheme()
|
|
20
|
-
if (result.type === '
|
|
21
|
-
if (result.type === '
|
|
20
|
+
if (result.type === 'workspace') return t.warning
|
|
21
|
+
if (result.type === 'project') return t.info
|
|
22
22
|
return t.text
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
interface
|
|
25
|
+
interface CreateProjectModalProps {
|
|
26
26
|
activeField: 'directory' | 'name'
|
|
27
27
|
directoryQuery: string
|
|
28
|
-
|
|
28
|
+
projectName: string
|
|
29
29
|
results: DirectoryResult[]
|
|
30
30
|
selectedIndex: number
|
|
31
31
|
pendingProjectPath: string | null
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export function
|
|
34
|
+
export function CreateProjectModal({
|
|
35
35
|
activeField,
|
|
36
36
|
directoryQuery,
|
|
37
37
|
pendingProjectPath,
|
|
38
|
+
projectName,
|
|
38
39
|
results,
|
|
39
40
|
selectedIndex,
|
|
40
|
-
|
|
41
|
-
}: CreateSessionModalProps) {
|
|
41
|
+
}: CreateProjectModalProps) {
|
|
42
42
|
const t = useTheme()
|
|
43
43
|
const dirActive = activeField === 'directory'
|
|
44
44
|
const nameActive = activeField === 'name'
|
|
@@ -57,8 +57,8 @@ export function CreateSessionModal({
|
|
|
57
57
|
|
|
58
58
|
return (
|
|
59
59
|
<Form
|
|
60
|
-
title="Create
|
|
61
|
-
keybindsModeId="modal.create-
|
|
60
|
+
title="Create project"
|
|
61
|
+
keybindsModeId="modal.create-project"
|
|
62
62
|
width={uiTokens.modalWidth.xl}
|
|
63
63
|
>
|
|
64
64
|
<AutoComplete
|
|
@@ -81,7 +81,7 @@ export function CreateSessionModal({
|
|
|
81
81
|
}
|
|
82
82
|
/>
|
|
83
83
|
|
|
84
|
-
<TextField active={nameActive} label="
|
|
84
|
+
<TextField active={nameActive} label="Project name" value={projectName} />
|
|
85
85
|
</Form>
|
|
86
86
|
)
|
|
87
87
|
}
|
package/src/ui/components/modals/{sessions/session-name-modal.tsx → projects/project-name-modal.tsx}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { uiTokens } from '../../../ui-tokens'
|
|
2
2
|
import { Form, TextField } from '../shared/form'
|
|
3
3
|
|
|
4
|
-
export function
|
|
4
|
+
export function ProjectNameModal({ title, value }: { title: string; value: string }) {
|
|
5
5
|
return (
|
|
6
|
-
<Form title={title} keybindsModeId="modal.
|
|
6
|
+
<Form title={title} keybindsModeId="modal.project-name" width={uiTokens.modalWidth.md}>
|
|
7
7
|
<TextField active value={value} />
|
|
8
8
|
</Form>
|
|
9
9
|
)
|
|
@@ -1,86 +1,84 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react'
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { ProjectRecord } from '../../../../state/types'
|
|
4
4
|
|
|
5
5
|
import { dispatchGlobal, runSideEffectGlobal } from '../../../../state/dispatch-ref'
|
|
6
|
-
import {
|
|
6
|
+
import { filterProjects } from '../../../../state/selectors'
|
|
7
7
|
import { abbreviatePath } from '../../../path-format'
|
|
8
|
-
import {
|
|
8
|
+
import { orderProjectsForDisplay } from '../../../project-ordering'
|
|
9
9
|
import { useTheme } from '../../../theme'
|
|
10
10
|
import { uiTokens } from '../../../ui-tokens'
|
|
11
11
|
import { Picker, type PickerItem } from '../shared/picker'
|
|
12
12
|
|
|
13
|
-
interface
|
|
14
|
-
|
|
13
|
+
interface ProjectPickerModalProps {
|
|
14
|
+
projects: ProjectRecord[]
|
|
15
15
|
selectedIndex: number
|
|
16
|
-
|
|
16
|
+
currentProjectId: string | null
|
|
17
17
|
currentTabCount: number
|
|
18
18
|
filter: string | null
|
|
19
19
|
cursorPos?: number
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
function formatProjectLine(
|
|
23
|
+
project: ProjectRecord,
|
|
24
|
+
currentProjectId: string | null,
|
|
25
25
|
currentTabCount: number,
|
|
26
26
|
displayIndex: number
|
|
27
27
|
): string {
|
|
28
28
|
const tabCount =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
: (session.workspaceSnapshot?.tabs.length ?? 0)
|
|
32
|
-
return `[${displayIndex}] ${session.name} (${tabCount} tab${tabCount === 1 ? '' : 's'})`
|
|
29
|
+
project.id === currentProjectId ? currentTabCount : (project.projectSnapshot?.tabs.length ?? 0)
|
|
30
|
+
return `[${displayIndex}] ${project.name} (${tabCount} tab${tabCount === 1 ? '' : 's'})`
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
function getEmptyStateMessage(hasFilter: boolean): string {
|
|
36
|
-
if (hasFilter) return 'No matching
|
|
37
|
-
return 'No
|
|
34
|
+
if (hasFilter) return 'No matching projects.'
|
|
35
|
+
return 'No projects yet. Press Enter or n to create your first project.'
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
export function
|
|
41
|
-
|
|
38
|
+
export function ProjectPickerModal({
|
|
39
|
+
currentProjectId,
|
|
42
40
|
currentTabCount,
|
|
43
41
|
cursorPos,
|
|
44
42
|
filter,
|
|
43
|
+
projects,
|
|
45
44
|
selectedIndex,
|
|
46
|
-
|
|
47
|
-
}: SessionPickerModalProps) {
|
|
45
|
+
}: ProjectPickerModalProps) {
|
|
48
46
|
const t = useTheme()
|
|
49
|
-
const ordered = useMemo(() =>
|
|
50
|
-
const filtered = useMemo(() =>
|
|
47
|
+
const ordered = useMemo(() => orderProjectsForDisplay(projects), [projects])
|
|
48
|
+
const filtered = useMemo(() => filterProjects(ordered, filter), [filter, ordered])
|
|
51
49
|
const hasFilter = !!(filter != null && filter !== '')
|
|
52
50
|
|
|
53
51
|
const items = useMemo<PickerItem[]>(() => {
|
|
54
52
|
const baselineOrder = ordered.map((s) => s.id)
|
|
55
|
-
const
|
|
53
|
+
const projectItems: PickerItem[] = filtered.map((project, index) => {
|
|
56
54
|
const active = index === selectedIndex
|
|
57
|
-
const displayIndex = baselineOrder.indexOf(
|
|
55
|
+
const displayIndex = baselineOrder.indexOf(project.id) + 1
|
|
58
56
|
return {
|
|
59
|
-
key:
|
|
60
|
-
onClick: () => runSideEffectGlobal({ type: 'confirm-selected-
|
|
61
|
-
onDelete: () => runSideEffectGlobal({ type: 'delete-selected-
|
|
57
|
+
key: project.id,
|
|
58
|
+
onClick: () => runSideEffectGlobal({ type: 'confirm-selected-project' }),
|
|
59
|
+
onDelete: () => runSideEffectGlobal({ type: 'delete-selected-project' }),
|
|
62
60
|
subtitle:
|
|
63
|
-
|
|
64
|
-
<text fg={t.textMuted}>{abbreviatePath(
|
|
61
|
+
project.projectPath != null && project.projectPath !== '' ? (
|
|
62
|
+
<text fg={t.textMuted}>{abbreviatePath(project.projectPath)}</text>
|
|
65
63
|
) : undefined,
|
|
66
64
|
title: (
|
|
67
65
|
<text fg={active ? t.text : t.textMuted}>
|
|
68
|
-
{
|
|
66
|
+
{formatProjectLine(project, currentProjectId, currentTabCount, displayIndex)}
|
|
69
67
|
</text>
|
|
70
68
|
),
|
|
71
69
|
}
|
|
72
70
|
})
|
|
73
71
|
const createNewItem: PickerItem = {
|
|
74
72
|
key: '__create-new__',
|
|
75
|
-
onClick: () => runSideEffectGlobal({ type: 'confirm-selected-
|
|
73
|
+
onClick: () => runSideEffectGlobal({ type: 'confirm-selected-project' }),
|
|
76
74
|
title: (
|
|
77
75
|
<text fg={selectedIndex === filtered.length ? t.text : t.textMuted}>
|
|
78
|
-
Create new
|
|
76
|
+
Create new project
|
|
79
77
|
</text>
|
|
80
78
|
),
|
|
81
79
|
}
|
|
82
|
-
return [...
|
|
83
|
-
}, [
|
|
80
|
+
return [...projectItems, createNewItem]
|
|
81
|
+
}, [currentProjectId, currentTabCount, filtered, ordered, selectedIndex, t])
|
|
84
82
|
|
|
85
83
|
const handleHover = useCallback(
|
|
86
84
|
(index: number) => dispatchGlobal({ index, type: 'set-modal-selection-index' }),
|
|
@@ -89,8 +87,8 @@ export function SessionPickerModal({
|
|
|
89
87
|
|
|
90
88
|
return (
|
|
91
89
|
<Picker
|
|
92
|
-
title="
|
|
93
|
-
keybindsModeId="modal.
|
|
90
|
+
title="Projects"
|
|
91
|
+
keybindsModeId="modal.project-picker.filtering"
|
|
94
92
|
width={uiTokens.modalWidth.lg}
|
|
95
93
|
gap={1}
|
|
96
94
|
filter={filter}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useCallback, useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
import { filterSettingRows } from '../../../../settings/search'
|
|
4
|
+
import { useAppStore } from '../../../../state/app-store'
|
|
5
|
+
import { dispatchGlobal, runSideEffectGlobal } from '../../../../state/dispatch-ref'
|
|
6
|
+
import { useTheme } from '../../../theme'
|
|
7
|
+
import { uiTokens } from '../../../ui-tokens'
|
|
8
|
+
import { RowValue } from '../../settings/row-value'
|
|
9
|
+
import { Picker, type PickerItem } from '../shared/picker'
|
|
10
|
+
|
|
11
|
+
interface SettingsSearchModalProps {
|
|
12
|
+
filter: string | null
|
|
13
|
+
selectedIndex: number
|
|
14
|
+
cursorPos?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Every setting in one list, grouped by section and laid out the way the screen
|
|
19
|
+
* lays them out — label and value on one line, what it does underneath. A result
|
|
20
|
+
* you cannot read the current value of is a result you have to go and check.
|
|
21
|
+
*
|
|
22
|
+
* The filter is the one `getModalOptionCount` counts with, so the list and the
|
|
23
|
+
* cursor moving through it never disagree.
|
|
24
|
+
*/
|
|
25
|
+
export function SettingsSearchModal({
|
|
26
|
+
cursorPos,
|
|
27
|
+
filter,
|
|
28
|
+
selectedIndex,
|
|
29
|
+
}: SettingsSearchModalProps) {
|
|
30
|
+
const t = useTheme()
|
|
31
|
+
const projects = useAppStore((s) => s.projects)
|
|
32
|
+
const hits = useMemo(() => filterSettingRows(projects, filter), [projects, filter])
|
|
33
|
+
|
|
34
|
+
const items = useMemo<PickerItem[]>(
|
|
35
|
+
() =>
|
|
36
|
+
hits.map((hit) => ({
|
|
37
|
+
group: hit.sectionLabel,
|
|
38
|
+
key: hit.row.id,
|
|
39
|
+
// Acts on the selection, which the hover just set — the same two-step
|
|
40
|
+
// every other picker's rows use.
|
|
41
|
+
onClick: () => runSideEffectGlobal({ type: 'confirm-settings-search' }),
|
|
42
|
+
subtitle:
|
|
43
|
+
hit.row.description != null && hit.row.description !== '' ? (
|
|
44
|
+
<text fg={t.textMuted}>{hit.row.description}</text>
|
|
45
|
+
) : undefined,
|
|
46
|
+
title: <text fg={t.text}>{hit.row.label}</text>,
|
|
47
|
+
trailing: <RowValue row={hit.row} />,
|
|
48
|
+
})),
|
|
49
|
+
[hits, t]
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const handleHover = useCallback(
|
|
53
|
+
(index: number) => dispatchGlobal({ index, type: 'set-modal-selection-index' }),
|
|
54
|
+
[]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<Picker
|
|
59
|
+
title="Search settings"
|
|
60
|
+
keybindsModeId="modal.settings-search.filtering"
|
|
61
|
+
width={uiTokens.modalWidth.xl}
|
|
62
|
+
filter={filter}
|
|
63
|
+
cursorPos={cursorPos}
|
|
64
|
+
items={items}
|
|
65
|
+
selectedIndex={selectedIndex}
|
|
66
|
+
emptyState={<text fg={t.textMuted}>No setting matches.</text>}
|
|
67
|
+
onHover={handleHover}
|
|
68
|
+
/>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -27,6 +27,7 @@ interface TextFieldProps {
|
|
|
27
27
|
cursorPos?: number
|
|
28
28
|
description?: ReactNode
|
|
29
29
|
label?: ReactNode
|
|
30
|
+
minLines?: number
|
|
30
31
|
placeholder?: string
|
|
31
32
|
value: string
|
|
32
33
|
}
|
|
@@ -94,6 +95,7 @@ export function TextField({
|
|
|
94
95
|
cursorPos,
|
|
95
96
|
description,
|
|
96
97
|
label,
|
|
98
|
+
minLines,
|
|
97
99
|
placeholder,
|
|
98
100
|
value,
|
|
99
101
|
}: TextFieldProps) {
|
|
@@ -104,7 +106,13 @@ export function TextField({
|
|
|
104
106
|
{label}
|
|
105
107
|
</FieldLabel>
|
|
106
108
|
) : null}
|
|
107
|
-
<InputField
|
|
109
|
+
<InputField
|
|
110
|
+
active={active}
|
|
111
|
+
value={value}
|
|
112
|
+
cursorPos={cursorPos}
|
|
113
|
+
placeholder={placeholder}
|
|
114
|
+
minLines={minLines}
|
|
115
|
+
/>
|
|
108
116
|
</box>
|
|
109
117
|
)
|
|
110
118
|
}
|
|
@@ -138,27 +146,31 @@ export function AutoComplete({
|
|
|
138
146
|
cursorPos={cursorPos}
|
|
139
147
|
placeholder={placeholder}
|
|
140
148
|
/>
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
149
|
+
{/* Collapsed unless focused: a dropdown nobody is driving is noise, and
|
|
150
|
+
it pushes the rest of the form off the modal. */}
|
|
151
|
+
{active ? (
|
|
152
|
+
<box flexDirection="column" height={maxVisibleRows}>
|
|
153
|
+
{items.length === 0
|
|
154
|
+
? (emptyState ?? null)
|
|
155
|
+
: visibleItems.map((item, index) => {
|
|
156
|
+
const optionIndex = scrollOffset + index
|
|
157
|
+
const optionActive = active && optionIndex === selectedIndex
|
|
158
|
+
return (
|
|
159
|
+
<ListItem
|
|
160
|
+
key={item.key}
|
|
161
|
+
index={optionIndex}
|
|
162
|
+
active={optionActive}
|
|
163
|
+
leading={resolveItemContent(item.leading, optionActive)}
|
|
164
|
+
title={resolveItemContent(item.title, optionActive)}
|
|
165
|
+
subtitle={resolveItemContent(item.subtitle, optionActive)}
|
|
166
|
+
trailing={resolveItemContent(item.trailing, optionActive)}
|
|
167
|
+
onHoverIndex={onHover}
|
|
168
|
+
onClick={item.onClick}
|
|
169
|
+
/>
|
|
170
|
+
)
|
|
171
|
+
})}
|
|
172
|
+
</box>
|
|
173
|
+
) : null}
|
|
162
174
|
</box>
|
|
163
175
|
)
|
|
164
176
|
}
|
|
@@ -18,7 +18,7 @@ export interface PickerItem {
|
|
|
18
18
|
onClick?: () => void
|
|
19
19
|
onEdit?: () => void
|
|
20
20
|
onDelete?: () => void
|
|
21
|
-
|
|
21
|
+
onWorkspace?: () => void
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface PickerProps {
|
|
@@ -42,11 +42,11 @@ const MODAL_CHROME_ROWS = 6
|
|
|
42
42
|
function PickerItemCtas({
|
|
43
43
|
onDelete,
|
|
44
44
|
onEdit,
|
|
45
|
-
|
|
45
|
+
onWorkspace,
|
|
46
46
|
}: {
|
|
47
47
|
onEdit?: () => void
|
|
48
48
|
onDelete?: () => void
|
|
49
|
-
|
|
49
|
+
onWorkspace?: () => void
|
|
50
50
|
}) {
|
|
51
51
|
const t = useTheme()
|
|
52
52
|
const handleEdit = useCallback(
|
|
@@ -56,12 +56,12 @@ function PickerItemCtas({
|
|
|
56
56
|
},
|
|
57
57
|
[onEdit]
|
|
58
58
|
)
|
|
59
|
-
const
|
|
59
|
+
const handleWorkspace = useCallback(
|
|
60
60
|
(event: OtuiMouseEvent) => {
|
|
61
61
|
event.stopPropagation()
|
|
62
|
-
|
|
62
|
+
onWorkspace?.()
|
|
63
63
|
},
|
|
64
|
-
[
|
|
64
|
+
[onWorkspace]
|
|
65
65
|
)
|
|
66
66
|
const handleDelete = useCallback(
|
|
67
67
|
(event: OtuiMouseEvent) => {
|
|
@@ -77,8 +77,8 @@ function PickerItemCtas({
|
|
|
77
77
|
<text fg={t.primary}>[edit]</text>
|
|
78
78
|
</box>
|
|
79
79
|
) : null}
|
|
80
|
-
{
|
|
81
|
-
<box onMouseDown={
|
|
80
|
+
{onWorkspace ? (
|
|
81
|
+
<box onMouseDown={handleWorkspace}>
|
|
82
82
|
<text fg={t.warning}>[WT]</text>
|
|
83
83
|
</box>
|
|
84
84
|
) : null}
|
|
@@ -189,11 +189,11 @@ export function Picker({
|
|
|
189
189
|
const capturedIndex = index
|
|
190
190
|
const trailing =
|
|
191
191
|
item.trailing ??
|
|
192
|
-
(active && (item.onEdit || item.onDelete || item.
|
|
192
|
+
(active && (item.onEdit || item.onDelete || item.onWorkspace) ? (
|
|
193
193
|
<PickerItemCtas
|
|
194
194
|
onEdit={item.onEdit}
|
|
195
195
|
onDelete={item.onDelete}
|
|
196
|
-
|
|
196
|
+
onWorkspace={item.onWorkspace}
|
|
197
197
|
/>
|
|
198
198
|
) : null)
|
|
199
199
|
nodes.push(
|
package/src/ui/components/modals/shared/{worktree-delete-confirm.tsx → workspace-delete-confirm.tsx}
RENAMED
|
@@ -4,33 +4,33 @@ import { useTheme } from '../../../theme'
|
|
|
4
4
|
import { uiTokens } from '../../../ui-tokens'
|
|
5
5
|
import { Form } from './form'
|
|
6
6
|
|
|
7
|
-
interface
|
|
7
|
+
interface WorkspaceDeleteConfirmProps {
|
|
8
8
|
keybindsModeId: ModeId
|
|
9
9
|
reason: string
|
|
10
|
-
|
|
10
|
+
workspaceLabel: string
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Shared confirmation dialog for a recoverable
|
|
15
|
-
* the new-tab picker and as a standalone modal (sidebar "Remove
|
|
14
|
+
* Shared confirmation dialog for a recoverable workspace delete. Used both inside
|
|
15
|
+
* the new-tab picker and as a standalone modal (sidebar "Remove workspace"); the
|
|
16
16
|
* keybinds mode wires Enter/y to confirm and Esc/n to cancel for each context.
|
|
17
17
|
*/
|
|
18
|
-
export function
|
|
18
|
+
export function WorkspaceDeleteConfirm({
|
|
19
19
|
keybindsModeId,
|
|
20
20
|
reason,
|
|
21
|
-
|
|
22
|
-
}:
|
|
21
|
+
workspaceLabel,
|
|
22
|
+
}: WorkspaceDeleteConfirmProps) {
|
|
23
23
|
const t = useTheme()
|
|
24
24
|
return (
|
|
25
25
|
<Form
|
|
26
|
-
title="Delete
|
|
26
|
+
title="Delete workspace?"
|
|
27
27
|
keybindsModeId={keybindsModeId}
|
|
28
28
|
width={uiTokens.modalWidth.md}
|
|
29
29
|
footer={<text fg={t.textMuted}>Enter / y to delete · Esc / n to cancel</text>}
|
|
30
30
|
>
|
|
31
31
|
<box flexDirection="column" gap={1}>
|
|
32
32
|
<text fg={t.text}>
|
|
33
|
-
Delete <strong>{
|
|
33
|
+
Delete <strong>{workspaceLabel}</strong>?
|
|
34
34
|
</text>
|
|
35
35
|
<text fg={t.warning}>{reason}</text>
|
|
36
36
|
</box>
|