@brimveyn/aimux 1.20.4 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -25
- package/package.json +2 -2
- package/src/app-runtime/auto-commit-driver.ts +22 -27
- package/src/app-runtime/backend-attach-runtime.ts +43 -36
- package/src/app-runtime/backend-runtime-events.ts +107 -88
- package/src/app-runtime/editor-actions.ts +201 -0
- package/src/app-runtime/git-actions.ts +197 -0
- package/src/app-runtime/measure-ref.ts +19 -0
- package/src/app-runtime/navigation-actions.ts +213 -0
- package/src/app-runtime/project-actions.ts +188 -0
- package/src/app-runtime/prompt-injection.ts +123 -0
- package/src/app-runtime/selection.ts +29 -0
- package/src/app-runtime/settings-actions.ts +156 -0
- package/src/app-runtime/setup-actions.ts +237 -0
- package/src/app-runtime/side-effect-context.ts +33 -0
- package/src/app-runtime/side-effects.ts +305 -1589
- package/src/app-runtime/snippet-actions.ts +3 -2
- package/src/app-runtime/split-drag-controller.ts +4 -8
- package/src/app-runtime/tab-actions.ts +218 -0
- package/src/app-runtime/tab-runtime-timeouts.ts +1 -1
- package/src/app-runtime/use-auto-commit-driver.ts +14 -13
- package/src/app-runtime/use-backend-runtime.ts +12 -11
- package/src/app-runtime/use-directory-search.ts +5 -4
- package/src/app-runtime/use-mouse-handlers.ts +28 -42
- package/src/app-runtime/{use-workspace-autosave.ts → use-project-autosave.ts} +4 -4
- package/src/app-runtime/use-renderer-bindings.ts +2 -1
- package/src/app-runtime/use-setup-runner.ts +96 -0
- package/src/app-runtime/use-terminal-resize.ts +28 -33
- package/src/app-runtime/workspace-actions.ts +366 -0
- package/src/app-runtime/workspace-activity.ts +138 -0
- package/src/app-runtime/workspace-naming.ts +90 -0
- package/src/app.tsx +102 -85
- package/src/assets/sounds/bell.wav +0 -0
- package/src/assets/sounds/ding.wav +0 -0
- package/src/assets/sounds/plane.wav +0 -0
- package/src/cli/client/daemon-client.ts +2 -2
- package/src/cli/client/project-resolver.ts +112 -0
- package/src/cli/commands/project/close.ts +32 -0
- package/src/cli/commands/project/create.ts +93 -0
- package/src/cli/commands/project/list.ts +25 -0
- package/src/cli/commands/project/show.ts +32 -0
- package/src/cli/commands/{workspace → project}/switch.ts +19 -19
- package/src/cli/commands/tab/await.ts +2 -2
- package/src/cli/commands/tab/close.ts +3 -3
- package/src/cli/commands/tab/create.ts +74 -73
- package/src/cli/commands/tab/focus.ts +3 -3
- package/src/cli/commands/tab/list.ts +6 -6
- package/src/cli/commands/tab/run.ts +2 -2
- package/src/cli/commands/tab/send.ts +2 -2
- package/src/cli/commands/tab/snapshot.ts +2 -2
- package/src/cli/commands/tab/tail.ts +2 -2
- package/src/cli/commands/tab/wait.ts +2 -2
- package/src/cli/commands/worker/await.ts +3 -3
- package/src/cli/commands/worker/doctor.ts +30 -30
- package/src/cli/commands/worker/list.ts +15 -15
- package/src/cli/commands/worker/prompt.ts +3 -3
- package/src/cli/commands/worker/run.ts +23 -23
- package/src/cli/commands/worker/shared.ts +53 -59
- package/src/cli/commands/worker/stop.ts +29 -29
- package/src/cli/commands/worker/submit.ts +3 -3
- package/src/cli/commands/{worktree → workspace}/create-core.ts +26 -26
- package/src/cli/commands/workspace/create.ts +30 -68
- package/src/cli/commands/workspace/list.ts +52 -9
- package/src/cli/commands/workspace/remove.ts +81 -0
- package/src/cli/completion/plan.ts +2 -2
- package/src/cli/completion/sources.ts +18 -18
- package/src/cli/context.ts +13 -13
- package/src/cli/flags.ts +30 -10
- package/src/cli/index.ts +26 -16
- package/src/cli/output.ts +1 -1
- package/src/cli/registry.ts +12 -12
- package/src/config/loader.ts +16 -5
- package/src/config.ts +149 -101
- package/src/daemon/catalog-writer.ts +67 -67
- package/src/daemon/daemon.ts +205 -201
- package/src/daemon/session-manager.ts +39 -39
- package/src/daemon/session-registry.ts +14 -14
- package/src/git/divergence.ts +33 -2
- package/src/git/git-poller.ts +1 -1
- package/src/git/git-status.ts +2 -2
- package/src/git/{move-worktree.ts → move-workspace.ts} +5 -5
- package/src/git/pr-merge.ts +64 -0
- package/src/git/pr-status-poller.ts +57 -0
- package/src/git/pr-status.ts +227 -0
- package/src/git/repo-discovery.ts +1 -1
- package/src/git/use-repo-discovery.ts +1 -1
- package/src/git/workspace-branch-poller.ts +54 -0
- package/src/git/workspace-divergence-poller.ts +65 -0
- package/src/git/worktree.ts +29 -0
- package/src/index.tsx +11 -6
- package/src/input/keymap/help-entries.ts +7 -5
- package/src/input/modes/bridge.ts +13 -12
- package/src/input/modes/handlers/shared.ts +1 -1
- package/src/input/modes/transitions.ts +34 -21
- package/src/input/modes/types.ts +48 -31
- package/src/ipc/manager-protocol.ts +44 -44
- package/src/ipc/protocol.ts +192 -166
- package/src/platform/open-url.ts +39 -0
- package/src/platform/play-sound.ts +184 -0
- package/src/platform/project-search.ts +8 -8
- package/src/platform/worktree-paths.ts +8 -6
- package/src/pty/assistant-question-extractor.ts +1 -1
- package/src/pty/assistant-status-detection-loop.ts +79 -55
- package/src/pty/assistant-status-detector.ts +40 -15
- package/src/pty/command-registry.ts +81 -1
- package/src/restart-terminal-manager.ts +1 -1
- package/src/services/ai-usage/provider.ts +9 -2
- package/src/services/ai-usage/spawn.ts +3 -1
- package/src/session-backend/bootstrap.ts +4 -4
- package/src/session-backend/local-session-backend.ts +78 -75
- package/src/session-backend/remote-session-backend.ts +64 -52
- package/src/session-backend/types.ts +43 -32
- package/src/settings/live.ts +90 -0
- package/src/settings/search.ts +43 -0
- package/src/settings/sections/about.ts +49 -0
- package/src/settings/sections/appearance.ts +54 -0
- package/src/settings/sections/automation.ts +105 -0
- package/src/settings/sections/commands.ts +72 -0
- package/src/settings/sections/editor.ts +56 -0
- package/src/settings/sections/experimental.ts +57 -0
- package/src/settings/sections/git.ts +112 -0
- package/src/settings/sections/index.ts +95 -0
- package/src/settings/sections/integrations.ts +23 -0
- package/src/settings/sections/layout.ts +72 -0
- package/src/settings/sections/notifications.ts +99 -0
- package/src/settings/sections/setup.ts +54 -0
- package/src/settings/sections/status-bar.ts +63 -0
- package/src/settings/settings-store.ts +207 -0
- package/src/settings/types.ts +108 -0
- package/src/snippets/run-shell-var.ts +48 -22
- package/src/state/actions.ts +342 -0
- package/src/state/app-store.ts +2 -1
- package/src/state/bars.ts +75 -0
- package/src/state/dispatch-ref.ts +1 -1
- package/src/state/git-pane-sizing.ts +0 -9
- package/src/state/layout-resize.ts +2 -2
- package/src/state/pr-status-store.ts +39 -0
- package/src/state/project-catalog.ts +275 -0
- package/src/state/project-data.ts +143 -0
- package/src/state/{session-persistence.ts → project-persistence.ts} +77 -73
- package/src/state/project-save.ts +46 -0
- package/src/state/project-workspaces.ts +392 -0
- package/src/state/reducers/auto-commit-state.ts +11 -10
- package/src/state/reducers/git-commit-modal-state.ts +122 -0
- package/src/state/reducers/git-mode-state.ts +1 -1
- package/src/state/reducers/git-panel-state.ts +10 -53
- package/src/state/reducers/modal-state.ts +290 -681
- package/src/state/reducers/multi-repo-state.ts +3 -6
- package/src/state/reducers/project-state.ts +312 -0
- package/src/state/reducers/settings-state.ts +80 -0
- package/src/state/reducers/tab-state.ts +107 -60
- package/src/state/reducers/ui-state.ts +86 -15
- package/src/state/selectors.ts +49 -37
- package/src/state/store.ts +83 -57
- package/src/state/types.ts +261 -417
- package/src/state/validation.ts +14 -12
- package/src/terminal-manager/manager-client.ts +32 -32
- package/src/terminal-manager/terminal-manager.ts +24 -24
- package/src/ui/components/git/diff-renderer/pierre-diff.tsx +2 -1
- package/src/ui/components/git/git-panel.tsx +2 -2
- package/src/ui/components/git/git-view.tsx +12 -12
- package/src/ui/components/git/image-diff/image-diff-view.tsx +1 -1
- package/src/ui/components/git/pane/git-pane-header.tsx +105 -39
- package/src/ui/components/git/pane/git-pane-widget.tsx +37 -23
- package/src/ui/components/git/pane/pr-checks-panel.tsx +197 -0
- package/src/ui/components/git/pane/pr-state-row.tsx +103 -0
- package/src/ui/components/layout/bar.tsx +191 -0
- package/src/ui/components/layout/sidebar/project-list.tsx +444 -0
- package/src/ui/components/layout/sidebar/tab-item.tsx +27 -12
- package/src/ui/components/layout/sidebar/use-sidebar-auto-scroll.ts +1 -1
- package/src/ui/components/layout/sidebar/workspace-row.tsx +253 -0
- package/src/ui/components/layout/status-bar.tsx +6 -2
- package/src/ui/components/layout/terminal-pane.tsx +34 -29
- package/src/ui/components/layout/top-tab-bar.tsx +48 -37
- package/src/ui/components/modals/git/git-commit-modal.tsx +8 -8
- package/src/ui/components/modals/{sessions/create-session-modal.tsx → projects/create-project-modal.tsx} +12 -12
- package/src/ui/components/modals/{sessions/session-name-modal.tsx → projects/project-name-modal.tsx} +2 -2
- package/src/ui/components/modals/{sessions/session-picker-modal.tsx → projects/project-picker-modal.tsx} +33 -35
- package/src/ui/components/modals/settings/settings-search-modal.tsx +70 -0
- package/src/ui/components/modals/shared/form.tsx +34 -22
- package/src/ui/components/modals/shared/picker.tsx +10 -10
- package/src/ui/components/modals/shared/{worktree-delete-confirm.tsx → workspace-delete-confirm.tsx} +9 -9
- package/src/ui/components/modals/tabs/new-tab-modal.tsx +30 -324
- package/src/ui/components/modals/workspace/create-workspace-modal.tsx +101 -0
- package/src/ui/components/modals/{worktree/worktree-move-confirm-modal.tsx → workspace/workspace-move-confirm-modal.tsx} +5 -5
- package/src/ui/components/modals/{worktree/worktree-move-modal.tsx → workspace/workspace-move-modal.tsx} +29 -27
- package/src/ui/components/primitives/input-field.tsx +7 -3
- package/src/ui/components/primitives/surface.tsx +3 -0
- package/src/ui/components/settings/row-value.tsx +74 -0
- package/src/ui/components/settings/settings-row.tsx +78 -0
- package/src/ui/components/settings/settings-view.tsx +169 -0
- package/src/ui/components/setup/setup-widget.tsx +152 -0
- package/src/ui/flash/build-labels.ts +27 -27
- package/src/ui/hooks/use-activity-sprite.ts +73 -0
- package/src/ui/hooks/use-scroll-active-into-view.ts +26 -0
- package/src/ui/{session-ordering.ts → project-ordering.ts} +4 -4
- package/src/ui/root.tsx +164 -230
- package/src/ui/status-bar-model.ts +48 -34
- package/src/ui/terminal-graphics/kitty.ts +28 -2
- package/src/ui/terminal-graphics/sprites/done@330/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/done@330/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/idle@1000/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/idle@1000/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/waiting@170/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/waiting@170/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/2.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/3.png +0 -0
- package/src/ui/terminal-graphics/sprites.ts +246 -0
- package/src/ui/truncate.ts +7 -0
- package/src/ui/widgets/registry.tsx +21 -0
- package/src/ui/widgets/widget-context-menu.ts +85 -0
- package/src/update.ts +2 -2
- package/src/app-runtime/session-actions.ts +0 -187
- package/src/cli/client/workspace-resolver.ts +0 -110
- package/src/cli/commands/workspace/close.ts +0 -32
- package/src/cli/commands/workspace/show.ts +0 -32
- package/src/cli/commands/worktree/create.ts +0 -55
- package/src/cli/commands/worktree/list.ts +0 -68
- package/src/cli/commands/worktree/remove.ts +0 -81
- package/src/git/worktree-branch-poller.ts +0 -54
- package/src/git/worktree-divergence-poller.ts +0 -59
- package/src/state/reducers/session-state.ts +0 -262
- package/src/state/session-catalog.ts +0 -143
- package/src/state/session-worktrees.ts +0 -260
- package/src/state/workspace-save.ts +0 -45
- package/src/ui/components/git/pane/git-pane-context-menu.ts +0 -26
- package/src/ui/components/layout/sidebar/sidebar.tsx +0 -163
- package/src/ui/components/layout/sidebar/use-top-tab-bar-auto-scroll.ts +0 -30
- package/src/ui/components/layout/sidebar/workspace-list.tsx +0 -418
- package/src/ui/components/layout/sidebar/worktree-row.tsx +0 -149
- /package/src/ui/{components/git/image-diff → terminal-graphics}/dimensions.ts +0 -0
|
@@ -8,20 +8,26 @@ import { memo, type ReactNode, useCallback, useMemo, useRef, useState } from 're
|
|
|
8
8
|
|
|
9
9
|
import type { FocusMode, TabSession } from '../../../state/types'
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { useWorkspaceDivergencePolling } from '../../../git/workspace-divergence-poller'
|
|
12
12
|
import { useAppStore } from '../../../state/app-store'
|
|
13
|
+
import { getBarWidth } from '../../../state/bars'
|
|
13
14
|
import { dispatchGlobal, runSideEffectGlobal } from '../../../state/dispatch-ref'
|
|
14
|
-
import {
|
|
15
|
+
import { filterTabsForActiveWorkspace } from '../../../state/project-workspaces'
|
|
15
16
|
import { buildTabEntries, type GroupEntry, type TabEntry } from '../../../state/tab-entries'
|
|
16
|
-
import {
|
|
17
|
+
import { useScrollActiveIntoView } from '../../hooks/use-scroll-active-into-view'
|
|
18
|
+
import { moveIdToIdPosition } from '../../project-ordering'
|
|
17
19
|
import { useTheme } from '../../theme'
|
|
18
20
|
import { FlashLabelBadge } from '../flash/flash-label-badge'
|
|
19
21
|
import { ContextMenuBox } from '../overlays/context-menu/context-menu-box'
|
|
20
22
|
import { TabItem } from './sidebar/tab-item'
|
|
21
|
-
import { useTopTabBarAutoScroll } from './sidebar/use-top-tab-bar-auto-scroll'
|
|
22
23
|
|
|
23
24
|
interface TopTabBarProps {
|
|
24
|
-
|
|
25
|
+
/**
|
|
26
|
+
* A full-screen view has replaced the pane tree (git mode, settings). The strip
|
|
27
|
+
* shows regardless of the user's own preference — it is the only orientation
|
|
28
|
+
* left — but it shows as a strip to read, not to act on.
|
|
29
|
+
*/
|
|
30
|
+
panesReplaced?: boolean
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
const ROW_CONTENT_OPTIONS = {
|
|
@@ -191,34 +197,34 @@ function entryTabIds(entry: TabEntry): string[] {
|
|
|
191
197
|
return entry.kind === 'single' ? [entry.tab.id] : entry.tabs.map((tab) => tab.id)
|
|
192
198
|
}
|
|
193
199
|
|
|
194
|
-
export function TopTabBar({
|
|
200
|
+
export function TopTabBar({ panesReplaced = false }: TopTabBarProps) {
|
|
195
201
|
const t = useTheme()
|
|
196
202
|
const headerBg = t.backgroundPanel
|
|
197
203
|
const tabs = useAppStore((s) => s.tabs)
|
|
198
204
|
const activeTabId = useAppStore((s) => s.activeTabId)
|
|
199
|
-
const bar = useAppStore((s) => s.
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
const
|
|
205
|
+
const bar = useAppStore((s) => s.projectBar)
|
|
206
|
+
const leftBarVisible = useAppStore((s) => getBarWidth(s.bars.left) > 0)
|
|
207
|
+
const currentProjectId = useAppStore((s) => s.currentProjectId)
|
|
208
|
+
const projects = useAppStore((s) => s.projects)
|
|
203
209
|
const focusMode: FocusMode = useAppStore((s) => s.focusMode)
|
|
204
210
|
const layoutTrees = useAppStore((s) => s.layoutTrees)
|
|
205
211
|
const tabGroupMap = useAppStore((s) => s.tabGroupMap)
|
|
206
212
|
|
|
207
|
-
// Sidebar now also shows
|
|
213
|
+
// Sidebar now also shows workspace chips with divergence — poll whenever
|
|
208
214
|
// either surface is visible.
|
|
209
|
-
|
|
215
|
+
useWorkspaceDivergencePolling(bar.visible || leftBarVisible || panesReplaced)
|
|
210
216
|
|
|
211
|
-
const
|
|
217
|
+
const currentProject = useMemo(
|
|
212
218
|
() =>
|
|
213
|
-
|
|
214
|
-
?
|
|
219
|
+
currentProjectId != null && currentProjectId !== ''
|
|
220
|
+
? projects.find((s) => s.id === currentProjectId)
|
|
215
221
|
: undefined,
|
|
216
|
-
[
|
|
222
|
+
[currentProjectId, projects]
|
|
217
223
|
)
|
|
218
224
|
|
|
219
225
|
const visibleTabs = useMemo(
|
|
220
|
-
() =>
|
|
221
|
-
[tabs,
|
|
226
|
+
() => filterTabsForActiveWorkspace(tabs, currentProject),
|
|
227
|
+
[tabs, currentProject]
|
|
222
228
|
)
|
|
223
229
|
|
|
224
230
|
const entries = useMemo(
|
|
@@ -238,11 +244,11 @@ export function TopTabBar({ forceVisible = false }: TopTabBarProps) {
|
|
|
238
244
|
}, [entries, activeTabId])
|
|
239
245
|
|
|
240
246
|
const scrollRef = useRef<ScrollBoxRenderable | null>(null)
|
|
241
|
-
|
|
242
|
-
|
|
247
|
+
useScrollActiveIntoView({
|
|
248
|
+
activeId: activeEntryId,
|
|
243
249
|
idPrefix: 'top-tab-',
|
|
244
250
|
scrollRef,
|
|
245
|
-
visible: bar.visible ||
|
|
251
|
+
visible: bar.visible || panesReplaced,
|
|
246
252
|
})
|
|
247
253
|
|
|
248
254
|
const handleEntryActivate = useCallback(
|
|
@@ -259,7 +265,7 @@ export function TopTabBar({ forceVisible = false }: TopTabBarProps) {
|
|
|
259
265
|
)
|
|
260
266
|
|
|
261
267
|
// --- Drag-and-drop reorder of the tab strip ---------------------------------
|
|
262
|
-
// Mirrors the
|
|
268
|
+
// Mirrors the project-list drag, but on the horizontal axis: entries are
|
|
263
269
|
// laid out left-to-right inside a scrollX box, so hit-testing is on x/width.
|
|
264
270
|
const [draggingId, setDraggingId] = useState<string | null>(null)
|
|
265
271
|
const [dragOrder, setDragOrder] = useState<string[] | null>(null)
|
|
@@ -343,13 +349,13 @@ export function TopTabBar({ forceVisible = false }: TopTabBarProps) {
|
|
|
343
349
|
|
|
344
350
|
const handleNewTab = useCallback((e: OtuiMouseEvent) => {
|
|
345
351
|
e.stopPropagation()
|
|
346
|
-
|
|
352
|
+
runSideEffectGlobal({ type: 'open-new-tab' })
|
|
347
353
|
}, [])
|
|
348
354
|
|
|
349
|
-
if (!bar.visible && !
|
|
350
|
-
// Keep the bar visible even with zero entries — when the active
|
|
355
|
+
if (!bar.visible && !panesReplaced) return null
|
|
356
|
+
// Keep the bar visible even with zero entries — when the active workspace
|
|
351
357
|
// has no tabs, the lone "+" affordance is what tells the user "you're in
|
|
352
|
-
// an empty
|
|
358
|
+
// an empty workspace, click here to start one".
|
|
353
359
|
|
|
354
360
|
const isFocused = focusMode === 'terminal-input' || focusMode === 'navigation'
|
|
355
361
|
|
|
@@ -425,17 +431,22 @@ export function TopTabBar({ forceVisible = false }: TopTabBarProps) {
|
|
|
425
431
|
</TopTabCell>
|
|
426
432
|
)
|
|
427
433
|
})}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
434
|
+
{/* Not while a full-screen view is up: a `+` that quietly starts a tab
|
|
435
|
+
and drops you out of the screen you were reading is noise, and on the
|
|
436
|
+
settings screen it is not even about anything on it. */}
|
|
437
|
+
{panesReplaced ? null : (
|
|
438
|
+
<box
|
|
439
|
+
flexDirection="row"
|
|
440
|
+
flexShrink={0}
|
|
441
|
+
paddingLeft={1}
|
|
442
|
+
paddingRight={1}
|
|
443
|
+
onMouseDown={handleNewTab}
|
|
444
|
+
>
|
|
445
|
+
<text fg={t.textMuted} selectable={false}>
|
|
446
|
+
+
|
|
447
|
+
</text>
|
|
448
|
+
</box>
|
|
449
|
+
)}
|
|
439
450
|
</scrollbox>
|
|
440
451
|
</box>
|
|
441
452
|
)
|
|
@@ -89,10 +89,10 @@ export function GitCommitModal({
|
|
|
89
89
|
const bodyActive = activeField === 'body'
|
|
90
90
|
const isConfirm = stage === 'confirm'
|
|
91
91
|
const isGenerating = stage === 'generating'
|
|
92
|
-
const
|
|
92
|
+
const currentProjectId = useAppStore((s) => s.currentProjectId)
|
|
93
93
|
const bgKind = useAppStore((s) => {
|
|
94
|
-
const id = s.
|
|
95
|
-
return id != null && id !== '' ? s.autoCommit.
|
|
94
|
+
const id = s.currentProjectId
|
|
95
|
+
return id != null && id !== '' ? s.autoCommit.byProject[id]?.kind : undefined
|
|
96
96
|
})
|
|
97
97
|
const isBgGenerating = bgKind === 'generating'
|
|
98
98
|
const showBgSpinner = isBgGenerating && !isConfirm && !isGenerating
|
|
@@ -103,19 +103,19 @@ export function GitCommitModal({
|
|
|
103
103
|
|
|
104
104
|
const onAutoCommitClick = useCallback((): void => {
|
|
105
105
|
const hasTitle = title.trim().length > 0
|
|
106
|
-
if (hasTitle || !(
|
|
106
|
+
if (hasTitle || !(currentProjectId != null && currentProjectId !== '')) {
|
|
107
107
|
dispatchGlobal({ type: 'git-commit-enter-confirm' })
|
|
108
108
|
return
|
|
109
109
|
}
|
|
110
110
|
if (bgKind === 'ready') {
|
|
111
|
-
dispatchGlobal({
|
|
111
|
+
dispatchGlobal({ projectId: currentProjectId, type: 'git-commit-use-background-suggestion' })
|
|
112
112
|
return
|
|
113
113
|
}
|
|
114
|
-
dispatchGlobal({
|
|
114
|
+
dispatchGlobal({ projectId: currentProjectId, type: 'git-commit-enter-generating' })
|
|
115
115
|
if (bgKind !== 'generating') {
|
|
116
|
-
runSideEffectGlobal({
|
|
116
|
+
runSideEffectGlobal({ projectId: currentProjectId, type: 'generate-auto-commit-now' })
|
|
117
117
|
}
|
|
118
|
-
}, [bgKind,
|
|
118
|
+
}, [bgKind, currentProjectId, title])
|
|
119
119
|
|
|
120
120
|
const handleAutoCommitMouseDown = useCallback(
|
|
121
121
|
(event: OtuiMouseEvent) => {
|
|
@@ -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>
|