@cerbur/clutch-dsh-worktree 0.1.2 → 0.1.4
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 +247 -92
- package/README.zh.md +297 -0
- package/lib/client/WorktreeContext.d.ts +15 -0
- package/lib/client/WorktreeContext.d.ts.map +1 -0
- package/lib/client/WorktreeContext.js +19 -0
- package/lib/client/WorktreeContext.js.map +1 -0
- package/lib/client/WorktreeHeroContext.d.ts +10 -0
- package/lib/client/WorktreeHeroContext.d.ts.map +1 -0
- package/lib/client/WorktreeHeroContext.js +128 -0
- package/lib/client/WorktreeHeroContext.js.map +1 -0
- package/lib/client/WorktreeOverlay.d.ts +6 -0
- package/lib/client/WorktreeOverlay.d.ts.map +1 -0
- package/lib/client/WorktreeOverlay.js +8 -0
- package/lib/client/WorktreeOverlay.js.map +1 -0
- package/lib/client/WorktreeSurface.d.ts +3 -29
- package/lib/client/WorktreeSurface.d.ts.map +1 -1
- package/lib/client/WorktreeSurface.js +170 -314
- package/lib/client/WorktreeSurface.js.map +1 -1
- package/lib/client/entry.d.ts +1 -1
- package/lib/client/entry.d.ts.map +1 -1
- package/lib/client/entry.js +41 -14
- package/lib/client/entry.js.map +1 -1
- package/lib/client/locales.d.ts +30 -0
- package/lib/client/locales.d.ts.map +1 -1
- package/lib/client/locales.js +32 -2
- package/lib/client/locales.js.map +1 -1
- package/lib/client/session-view.d.ts +21 -0
- package/lib/client/session-view.d.ts.map +1 -0
- package/lib/client/session-view.js +26 -0
- package/lib/client/session-view.js.map +1 -0
- package/lib/client/sidebar-overlay-geometry.d.ts +1 -0
- package/lib/client/sidebar-overlay-geometry.d.ts.map +1 -1
- package/lib/client/sidebar-overlay-geometry.js +28 -17
- package/lib/client/sidebar-overlay-geometry.js.map +1 -1
- package/lib/client/virtual-workspace-membership.d.ts.map +1 -1
- package/lib/client/virtual-workspace-membership.js +17 -6
- package/lib/client/virtual-workspace-membership.js.map +1 -1
- package/lib/client/worktree-connection.d.ts +1 -0
- package/lib/client/worktree-connection.d.ts.map +1 -1
- package/lib/client/worktree-connection.js +4 -0
- package/lib/client/worktree-connection.js.map +1 -1
- package/lib/client/worktree-context-store.d.ts +45 -0
- package/lib/client/worktree-context-store.d.ts.map +1 -0
- package/lib/client/worktree-context-store.js +199 -0
- package/lib/client/worktree-context-store.js.map +1 -0
- package/lib/client/worktree-context.d.ts +32 -0
- package/lib/client/worktree-context.d.ts.map +1 -0
- package/lib/client/worktree-context.js +51 -0
- package/lib/client/worktree-context.js.map +1 -0
- package/lib/client/worktree-error-copy.d.ts.map +1 -1
- package/lib/client/worktree-error-copy.js +14 -0
- package/lib/client/worktree-error-copy.js.map +1 -1
- package/lib/client/worktree-session.d.ts +83 -0
- package/lib/client/worktree-session.d.ts.map +1 -0
- package/lib/client/worktree-session.js +185 -0
- package/lib/client/worktree-session.js.map +1 -0
- package/lib/client/worktree-surface-dialogs.d.ts +7 -0
- package/lib/client/worktree-surface-dialogs.d.ts.map +1 -0
- package/lib/client/worktree-surface-dialogs.js +86 -0
- package/lib/client/worktree-surface-dialogs.js.map +1 -0
- package/lib/client/worktree-surface-rows.d.ts +9 -0
- package/lib/client/worktree-surface-rows.d.ts.map +1 -0
- package/lib/client/worktree-surface-rows.js +223 -0
- package/lib/client/worktree-surface-rows.js.map +1 -0
- package/lib/client/worktree-surface-selectors.d.ts +9 -0
- package/lib/client/worktree-surface-selectors.d.ts.map +1 -0
- package/lib/client/worktree-surface-selectors.js +28 -0
- package/lib/client/worktree-surface-selectors.js.map +1 -0
- package/lib/client/worktree-surface-types.d.ts +233 -0
- package/lib/client/worktree-surface-types.d.ts.map +1 -0
- package/lib/client/worktree-surface-types.js +2 -0
- package/lib/client/worktree-surface-types.js.map +1 -0
- package/lib/client/worktree-view-actions.d.ts +34 -0
- package/lib/client/worktree-view-actions.d.ts.map +1 -0
- package/lib/client/worktree-view-actions.js +69 -0
- package/lib/client/worktree-view-actions.js.map +1 -0
- package/lib/client/worktree-view-errors.d.ts +20 -0
- package/lib/client/worktree-view-errors.d.ts.map +1 -0
- package/lib/client/worktree-view-errors.js +65 -0
- package/lib/client/worktree-view-errors.js.map +1 -0
- package/lib/client/worktree-view-read.d.ts +48 -0
- package/lib/client/worktree-view-read.d.ts.map +1 -0
- package/lib/client/worktree-view-read.js +134 -0
- package/lib/client/worktree-view-read.js.map +1 -0
- package/lib/client/worktree-view.d.ts +4 -63
- package/lib/client/worktree-view.d.ts.map +1 -1
- package/lib/client/worktree-view.js +4 -125
- package/lib/client/worktree-view.js.map +1 -1
- package/lib/client.js +1313 -363
- package/lib/client.js.map +1 -1
- package/lib/contract/index.contract.js +2 -0
- package/lib/contract/index.contract.js.map +1 -1
- package/lib/contract/index.d.ts +17 -2
- package/lib/contract/index.d.ts.map +1 -1
- package/lib/contract/index.js +2 -0
- package/lib/contract/index.js.map +1 -1
- package/lib/host/remote.d.ts.map +1 -1
- package/lib/host/remote.js +1 -0
- package/lib/host/remote.js.map +1 -1
- package/lib/host/service.d.ts +6 -1
- package/lib/host/service.d.ts.map +1 -1
- package/lib/host/service.js +6 -0
- package/lib/host/service.js.map +1 -1
- package/lib/manage/manager-context.d.ts +11 -0
- package/lib/manage/manager-context.d.ts.map +1 -0
- package/lib/manage/manager-context.js +2 -0
- package/lib/manage/manager-context.js.map +1 -0
- package/lib/manage/manager-sessions.d.ts +18 -0
- package/lib/manage/manager-sessions.d.ts.map +1 -0
- package/lib/manage/manager-sessions.js +91 -0
- package/lib/manage/manager-sessions.js.map +1 -0
- package/lib/manage/manager-support.d.ts +18 -0
- package/lib/manage/manager-support.d.ts.map +1 -0
- package/lib/manage/manager-support.js +174 -0
- package/lib/manage/manager-support.js.map +1 -0
- package/lib/manage/manager-worktrees.d.ts +27 -0
- package/lib/manage/manager-worktrees.d.ts.map +1 -0
- package/lib/manage/manager-worktrees.js +259 -0
- package/lib/manage/manager-worktrees.js.map +1 -0
- package/lib/manage/manager.d.ts +25 -62
- package/lib/manage/manager.d.ts.map +1 -1
- package/lib/manage/manager.js +32 -554
- package/lib/manage/manager.js.map +1 -1
- package/lib/provider/sidecar-persistence.d.ts +18 -0
- package/lib/provider/sidecar-persistence.d.ts.map +1 -0
- package/lib/provider/sidecar-persistence.js +162 -0
- package/lib/provider/sidecar-persistence.js.map +1 -0
- package/lib/provider/sidecar-schema.d.ts +6 -0
- package/lib/provider/sidecar-schema.d.ts.map +1 -0
- package/lib/provider/sidecar-schema.js +123 -0
- package/lib/provider/sidecar-schema.js.map +1 -0
- package/lib/provider/sidecar.d.ts +12 -68
- package/lib/provider/sidecar.d.ts.map +1 -1
- package/lib/provider/sidecar.js +61 -346
- package/lib/provider/sidecar.js.map +1 -1
- package/lib/provider/types.d.ts +2 -1
- package/lib/provider/types.d.ts.map +1 -1
- package/lib/provider/types.js.map +1 -1
- package/lib/typert.host.js +60 -18
- package/lib/typert.remote-client.d.ts +3 -1
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +60 -18
- package/package.json +6 -2
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { IconBranchOutline16, IconCloseOutline16, IconPlusOutline16, IconSearchOutline16, } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { openWorktreeSession } from './navigation.js';
|
|
5
5
|
import { useSidebarOverlayGeometry } from './sidebar-overlay-geometry.js';
|
|
6
6
|
import { effectiveViewMode, unboundSessionIds, workspaceSessionIds } from './view-mode.js';
|
|
7
7
|
import { formatWorktreeViewError } from './worktree-error-copy.js';
|
|
8
|
-
import {
|
|
8
|
+
import { filterVisibleSessionIds, sessionMatchesQuery, } from './session-view.js';
|
|
9
|
+
import { retryWorktreeSessionBinding } from './worktree-session.js';
|
|
10
|
+
import { WorktreeCreateDialog, WorktreeRemovalDialog, WorktreeSessionRenameDialog, WorktreeWorkspaceDeleteDialog, WorktreeWorkspaceRenameDialog, } from './worktree-surface-dialogs.js';
|
|
11
|
+
import { bindingIdsFor, includesText, workspaceMatches, } from './worktree-surface-selectors.js';
|
|
12
|
+
import { WorktreeGroupRow, WorktreeSessionGroup, WorktreeWorkspaceRow, } from './worktree-surface-rows.js';
|
|
13
|
+
import { executeWorktreeAction, createDefaultWorktreeName, createWorktreeRefreshGuard, filterArchivedSessionIds, loadWorktreeViews, reconcileBaseBranchSelection, resolveWorktreeMove, selectDefaultBaseBranch, stableWorkspaceIds, toRetryableWorktreeOrderError, toWorktreeViewError, WorktreeSessionBindingError, } from './worktree-view.js';
|
|
9
14
|
import styles from './worktree.css';
|
|
10
15
|
function toNativeWorktreeViewError(error) {
|
|
11
16
|
const viewError = toWorktreeViewError(error);
|
|
@@ -22,230 +27,19 @@ function useStableWorkspaceIds(workspaces) {
|
|
|
22
27
|
previousRef.current = stable;
|
|
23
28
|
return previousRef.current;
|
|
24
29
|
}
|
|
25
|
-
function sessionLabel(sessionId, sessions) {
|
|
26
|
-
return sessions.byId[sessionId]?.displayTitle ?? sessionId;
|
|
27
|
-
}
|
|
28
|
-
function rowHalf(event) {
|
|
29
|
-
const rect = event.currentTarget.getBoundingClientRect();
|
|
30
|
-
return event.clientY < rect.top + rect.height / 2 ? 'before' : 'after';
|
|
31
|
-
}
|
|
32
|
-
function bindingIdsFor(bindings, worktreeId) {
|
|
33
|
-
return bindings
|
|
34
|
-
.filter((binding) => binding.worktreeId === worktreeId)
|
|
35
|
-
.map((binding) => binding.sessionId);
|
|
36
|
-
}
|
|
37
|
-
function includesText(value, query) {
|
|
38
|
-
return value.toLocaleLowerCase().includes(query);
|
|
39
|
-
}
|
|
40
|
-
function workspaceMatches(workspace, view, sessions, query) {
|
|
41
|
-
if (query.length === 0)
|
|
42
|
-
return true;
|
|
43
|
-
if (includesText(workspace.title, query))
|
|
44
|
-
return true;
|
|
45
|
-
if (workspace.sessionIds.some((sessionId) => includesText(sessionLabel(sessionId, sessions), query))) {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
if (view === undefined)
|
|
49
|
-
return false;
|
|
50
|
-
if (view.worktrees.some((record) => includesText(record.branch, query) || includesText(record.absolutePath, query))) {
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
return view.bindings.some((binding) => includesText(sessionLabel(binding.sessionId, sessions), query));
|
|
54
|
-
}
|
|
55
|
-
/** Workspace row using the native DSH menu, fixed action column, and drag contract. */
|
|
56
|
-
function WorktreeWorkspaceRow({ t, workspace, expanded, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange, }) {
|
|
57
|
-
const workspaceMenuItems = [
|
|
58
|
-
{
|
|
59
|
-
id: 'rename',
|
|
60
|
-
label: t('workspace.rename'),
|
|
61
|
-
icon: _jsx(IconEditOutline16, {}),
|
|
62
|
-
disabled: actionPending,
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
id: 'delete',
|
|
66
|
-
label: t('workspace.delete'),
|
|
67
|
-
icon: _jsx(IconTrashOutline16, {}),
|
|
68
|
-
danger: true,
|
|
69
|
-
disabled: actionPending,
|
|
70
|
-
},
|
|
71
|
-
];
|
|
72
|
-
const markerClass = drag.marker === 'before'
|
|
73
|
-
? styles.dropBefore
|
|
74
|
-
: drag.marker === 'after'
|
|
75
|
-
? styles.dropAfter
|
|
76
|
-
: '';
|
|
77
|
-
return (_jsxs("div", { className: `${styles.workspaceRow} ${markerClass}`, "data-workspace-drag": drag.active ? 'active' : undefined, "data-menu-open": menuOpen || undefined, draggable: true, onClick: () => {
|
|
78
|
-
onToggle();
|
|
79
|
-
}, onDragStart: (event) => {
|
|
80
|
-
event.dataTransfer.effectAllowed = 'move';
|
|
81
|
-
event.dataTransfer.setData('text/plain', workspace.workspaceId);
|
|
82
|
-
drag.start();
|
|
83
|
-
}, onDragEnd: drag.end, onDragOver: (event) => {
|
|
84
|
-
if (!drag.active)
|
|
85
|
-
return;
|
|
86
|
-
event.preventDefault();
|
|
87
|
-
event.dataTransfer.dropEffect = 'move';
|
|
88
|
-
drag.hover(rowHalf(event));
|
|
89
|
-
}, onDrop: (event) => {
|
|
90
|
-
if (!drag.active)
|
|
91
|
-
return;
|
|
92
|
-
event.preventDefault();
|
|
93
|
-
drag.drop(rowHalf(event));
|
|
94
|
-
}, children: [_jsx("button", { type: "button", className: `${styles.disclosureButton} ${styles.workspaceDisclosure}`, "aria-label": t(expanded ? 'workspace.collapse' : 'workspace.expand', {
|
|
95
|
-
name: workspace.title,
|
|
96
|
-
}), "aria-expanded": expanded, onClick: (event) => {
|
|
97
|
-
event.stopPropagation();
|
|
98
|
-
onToggle();
|
|
99
|
-
}, children: expanded ? (_jsx(IconChevronDownOutline14, { size: 18 })) : (_jsx(IconChevronRightOutline14, { size: 18 })) }), _jsx("span", { className: styles.workspaceIcon, "aria-hidden": "true", children: expanded ? _jsx(IconFolderOpen16, {}) : _jsx(IconFolderClose16, {}) }), _jsx("span", { className: styles.workspaceTitle, children: workspace.title }), _jsxs("span", { className: `${styles.treeActionSlot} ${styles.workspaceActions}`, children: [_jsx("span", { className: styles.menuAction, children: _jsx(Menu, { open: menuOpen, onClose: () => {
|
|
100
|
-
onMenuOpenChange(false);
|
|
101
|
-
}, items: workspaceMenuItems, onSelect: (id) => {
|
|
102
|
-
onMenuOpenChange(false);
|
|
103
|
-
if (id === 'rename')
|
|
104
|
-
onRename();
|
|
105
|
-
if (id === 'delete')
|
|
106
|
-
onDelete();
|
|
107
|
-
}, portal: true, closeOnPointerLeave: true, anchor: (_jsx("button", { type: "button", className: styles.iconButton, "data-workspace-menu": true, "aria-label": t('workspace.options', { name: workspace.title }), onClick: (event) => {
|
|
108
|
-
event.stopPropagation();
|
|
109
|
-
onMenuOpenChange(!menuOpen);
|
|
110
|
-
}, children: _jsx(IconEllipsisOutline16, {}) })) }) }), _jsx("button", { type: "button", className: styles.iconButton, "data-add-worktree": true, "aria-label": t('workspace.addWorktree', { name: workspace.title }), onClick: (event) => {
|
|
111
|
-
event.stopPropagation();
|
|
112
|
-
onCreateWorktree();
|
|
113
|
-
}, children: _jsx(IconPlusOutline16, {}) })] })] }));
|
|
114
|
-
}
|
|
115
|
-
/** Shared Main/Worktree group row with parameterized icon, state, and actions. */
|
|
116
|
-
function WorktreeGroupRow({ t, kind, label, expanded, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession, menu, }) {
|
|
117
|
-
const main = kind === 'main';
|
|
118
|
-
const row = (_jsxs("div", { className: styles.worktreeRow, "data-main-group": main ? 'true' : undefined, "data-main-expanded": main ? String(expanded) : undefined, "data-menu-open": menu?.open ? 'true' : undefined, onClick: onToggle, children: [_jsx("button", { type: "button", className: `${styles.disclosureButton} ${styles.worktreeDisclosure}`, "aria-label": t(expanded ? 'worktree.collapse' : 'worktree.expand', { name: label }), "aria-expanded": expanded, onClick: (event) => {
|
|
119
|
-
event.stopPropagation();
|
|
120
|
-
onToggle();
|
|
121
|
-
}, children: expanded ? (_jsx(IconChevronDownOutline14, { size: 18 })) : (_jsx(IconChevronRightOutline14, { size: 18 })) }), _jsx("span", { className: styles.worktreeIcon, "aria-hidden": "true", children: icon }), state !== undefined && stateLabel !== undefined && (_jsx("span", { className: styles.worktreeState, role: "img", "aria-label": stateLabel, title: stateLabel, children: _jsx(StateDot, { state: state }) })), _jsx("span", { className: styles.worktreeLabel, children: label }), _jsxs("span", { className: styles.treeActionSlot, children: [menu !== undefined && (_jsx("span", { className: styles.menuAction, children: _jsx(Menu, { open: menu.open, onClose: () => {
|
|
122
|
-
menu.onOpenChange(false);
|
|
123
|
-
}, items: [
|
|
124
|
-
{
|
|
125
|
-
id: 'remove',
|
|
126
|
-
label: t('worktree.remove'),
|
|
127
|
-
icon: _jsx(IconTrashOutline16, {}),
|
|
128
|
-
danger: true,
|
|
129
|
-
disabled: menu.disabled,
|
|
130
|
-
},
|
|
131
|
-
], onSelect: (id) => {
|
|
132
|
-
menu.onOpenChange(false);
|
|
133
|
-
if (id === 'remove')
|
|
134
|
-
menu.onRemove();
|
|
135
|
-
}, portal: true, closeOnPointerLeave: true, anchor: _jsx("button", { type: "button", className: styles.iconButton, "data-worktree-menu": true, "aria-label": t('worktree.options', { name: menu.label }), onClick: (event) => {
|
|
136
|
-
event.stopPropagation();
|
|
137
|
-
menu.onOpenChange(!menu.open);
|
|
138
|
-
}, children: _jsx(IconEllipsisOutline16, {}) }) }) })), onCreateSession !== undefined && (_jsx("button", { type: "button", className: styles.iconButton, "data-add-main-session": main ? 'true' : undefined, "data-add-session": main ? undefined : 'true', "aria-label": t('worktree.addSession', { name: workspaceTitle }), onClick: (event) => {
|
|
139
|
-
event.stopPropagation();
|
|
140
|
-
onCreateSession();
|
|
141
|
-
}, children: _jsx(IconPlusOutline16, {}) }))] })] }));
|
|
142
|
-
if (main)
|
|
143
|
-
return row;
|
|
144
|
-
return (_jsx(HoverCard, { anchor: row, content: _jsx("div", { className: styles.worktreeHoverTitle, children: label }), openDelayMs: 500, disabled: menu?.open === true }));
|
|
145
|
-
}
|
|
146
|
-
/** Worktree-mode Session row with the same trailing options menu as native DSH rows. */
|
|
147
|
-
function WorktreeSessionRow({ t, sessionId, label, drag, actionPending, onOpen, onRename, onFork, onArchive, }) {
|
|
148
|
-
const [menuOpen, setMenuOpen] = useState(false);
|
|
149
|
-
const markerClass = drag.marker === 'before'
|
|
150
|
-
? styles.dropBefore
|
|
151
|
-
: drag.marker === 'after'
|
|
152
|
-
? styles.dropAfter
|
|
153
|
-
: '';
|
|
154
|
-
const sessionMenuItems = [
|
|
155
|
-
{
|
|
156
|
-
id: 'rename',
|
|
157
|
-
label: t('session.rename'),
|
|
158
|
-
icon: _jsx(IconEditOutline16, {}),
|
|
159
|
-
disabled: onRename === undefined || actionPending,
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
id: 'fork',
|
|
163
|
-
label: t('session.fork'),
|
|
164
|
-
icon: _jsx(IconBranchOutline16, {}),
|
|
165
|
-
disabled: onFork === undefined || actionPending,
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
id: 'archive',
|
|
169
|
-
label: t('session.archive'),
|
|
170
|
-
icon: _jsx(IconArchiveOutline20, { size: 16 }),
|
|
171
|
-
disabled: onArchive === undefined || actionPending,
|
|
172
|
-
},
|
|
173
|
-
];
|
|
174
|
-
return (_jsxs("div", { className: `${styles.treeSessionRow} ${markerClass}`, "data-session-id": sessionId, "data-session-drag": drag.active ? 'active' : undefined, "data-menu-open": menuOpen || undefined, role: "treeitem", draggable: true, onDragStart: (event) => {
|
|
175
|
-
event.dataTransfer.effectAllowed = 'move';
|
|
176
|
-
event.dataTransfer.setData('text/plain', sessionId);
|
|
177
|
-
drag.start();
|
|
178
|
-
}, onDragEnd: drag.end, onDragOver: (event) => {
|
|
179
|
-
if (!drag.active)
|
|
180
|
-
return;
|
|
181
|
-
event.preventDefault();
|
|
182
|
-
event.dataTransfer.dropEffect = 'move';
|
|
183
|
-
drag.hover(rowHalf(event));
|
|
184
|
-
}, onDrop: (event) => {
|
|
185
|
-
if (!drag.active)
|
|
186
|
-
return;
|
|
187
|
-
event.preventDefault();
|
|
188
|
-
drag.drop(rowHalf(event));
|
|
189
|
-
}, children: [_jsxs("button", { type: "button", className: styles.treeSessionContent, onClick: onOpen, children: [_jsx("span", { className: styles.treeGuide, "aria-hidden": "true", children: "\u2514" }), _jsx("span", { className: styles.sessionLabel, children: label })] }), _jsx("span", { className: styles.rowActions, children: _jsx(Menu, { open: menuOpen, onClose: () => {
|
|
190
|
-
setMenuOpen(false);
|
|
191
|
-
}, items: sessionMenuItems, onSelect: (id) => {
|
|
192
|
-
setMenuOpen(false);
|
|
193
|
-
if (id === 'rename')
|
|
194
|
-
onRename?.(sessionId, label);
|
|
195
|
-
if (id === 'fork')
|
|
196
|
-
onFork?.(sessionId);
|
|
197
|
-
if (id === 'archive')
|
|
198
|
-
onArchive?.(sessionId);
|
|
199
|
-
}, portal: true, closeOnPointerLeave: true, anchor: (_jsx("button", { type: "button", className: styles.iconButton, "data-session-menu": true, "aria-label": t('session.options', { name: label }), onClick: (event) => {
|
|
200
|
-
event.stopPropagation();
|
|
201
|
-
setMenuOpen((current) => !current);
|
|
202
|
-
}, children: _jsx(IconEllipsisOutline16, {}) })) }) })] }));
|
|
203
|
-
}
|
|
204
|
-
function WorktreeSessionGroup({ t, groupKey, sessionIds, workspaceId, expanded, actionPending, sessions, dragState, onToggleExpanded, onStartDrag, onHoverDrag, onClearDrag, onFinishDrag, onCommitDrag, onOpen, onRename, onFork, onArchive, }) {
|
|
205
|
-
const visibleSessionIds = expanded ? sessionIds : sessionIds.slice(0, 5);
|
|
206
|
-
const sameGroupDrag = dragState?.groupKey === groupKey;
|
|
207
|
-
return (_jsxs(_Fragment, { children: [visibleSessionIds.map((sessionId) => (_jsx(WorktreeSessionRow, { t: t, sessionId: sessionId, label: sessionLabel(sessionId, sessions), drag: {
|
|
208
|
-
active: sameGroupDrag,
|
|
209
|
-
marker: sameGroupDrag && dragState.over?.sessionId === sessionId
|
|
210
|
-
? dragState.over.half
|
|
211
|
-
: null,
|
|
212
|
-
start: () => {
|
|
213
|
-
onStartDrag(groupKey, sessionId);
|
|
214
|
-
},
|
|
215
|
-
hover: (half) => {
|
|
216
|
-
onHoverDrag(sessionId, half);
|
|
217
|
-
},
|
|
218
|
-
drop: (half) => {
|
|
219
|
-
if (dragState === undefined)
|
|
220
|
-
return;
|
|
221
|
-
onCommitDrag(dragState, { sessionId, half }, sessionIds, workspaceId);
|
|
222
|
-
},
|
|
223
|
-
end: () => {
|
|
224
|
-
if (dragState?.over !== null && dragState?.over !== undefined) {
|
|
225
|
-
onCommitDrag(dragState, dragState.over, sessionIds, workspaceId);
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
onClearDrag();
|
|
229
|
-
}
|
|
230
|
-
onFinishDrag();
|
|
231
|
-
},
|
|
232
|
-
}, actionPending: actionPending, onOpen: () => {
|
|
233
|
-
onOpen(sessionId);
|
|
234
|
-
}, onRename: onRename, onFork: onFork, onArchive: onArchive }, `${groupKey}:${sessionId}`))), sessionIds.length > 5 && (_jsx("button", { type: "button", className: styles.sessionOverflowButton, "aria-expanded": expanded, onClick: onToggleExpanded, children: expanded
|
|
235
|
-
? t('session.collapse')
|
|
236
|
-
: t('session.expandMore', { count: sessionIds.length - 5 }) }))] }));
|
|
237
|
-
}
|
|
238
30
|
/**
|
|
239
31
|
* Peer Worktree navigation surface. It keeps the original DSH Workspace mode
|
|
240
32
|
* intact and renders a browser-local Workspace → Worktree → Session projection.
|
|
241
33
|
*/
|
|
242
|
-
export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, manager, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession, }) {
|
|
34
|
+
export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, manager, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession, }) {
|
|
243
35
|
const preferredMode = useStore((state) => state.viewMode);
|
|
244
36
|
const mode = effectiveViewMode(preferredMode, available && manager !== undefined);
|
|
245
37
|
const sessions = useSessions((state) => state);
|
|
246
38
|
const workspaces = useWorkspaces((state) => state);
|
|
247
39
|
const workspaceIds = useStableWorkspaceIds(workspaces.items);
|
|
248
40
|
const [readState, setReadState] = useState(EMPTY_READ_STATE);
|
|
41
|
+
const readStateRef = useRef(readState);
|
|
42
|
+
readStateRef.current = readState;
|
|
249
43
|
const [searchQuery, setSearchQuery] = useState('');
|
|
250
44
|
const [expandedWorkspaces, setExpandedWorkspaces] = useState({});
|
|
251
45
|
const [expandedMains, setExpandedMains] = useState({});
|
|
@@ -275,33 +69,46 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
275
69
|
const [sessionDrag, setSessionDrag] = useState();
|
|
276
70
|
const [expandedSessionGroups, setExpandedSessionGroups] = useState({});
|
|
277
71
|
const sessionDropCommitted = useRef(false);
|
|
72
|
+
const [worktreeDrag, setWorktreeDrag] = useState();
|
|
73
|
+
const worktreeDropCommitted = useRef(false);
|
|
74
|
+
const refreshGuard = useRef(createWorktreeRefreshGuard());
|
|
278
75
|
const { ref, width, bounds } = useSidebarOverlayGeometry(mode === 'worktree');
|
|
279
76
|
const collapsed = width <= 64;
|
|
280
77
|
const query = searchQuery.trim().toLocaleLowerCase();
|
|
281
78
|
const archivedSessionIds = workspaces.archivedSessionIds ?? [];
|
|
282
|
-
const
|
|
79
|
+
const pendingSessionArchived = pendingSessionBinding !== undefined &&
|
|
80
|
+
archivedSessionIds.includes(pendingSessionBinding.sessionId);
|
|
81
|
+
const refresh = useCallback(async (options = {}) => {
|
|
283
82
|
if (manager === undefined) {
|
|
83
|
+
refreshGuard.current.invalidate();
|
|
284
84
|
setReadState(EMPTY_READ_STATE);
|
|
285
85
|
return;
|
|
286
86
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
setReadState({ status: 'ready', views });
|
|
87
|
+
const preserveCurrent = options.preserveCurrent === true;
|
|
88
|
+
if (!preserveCurrent) {
|
|
89
|
+
setReadState({ status: 'loading', views: [] });
|
|
291
90
|
}
|
|
292
|
-
|
|
91
|
+
await refreshGuard.current.run(() => loadWorktreeViews(manager, workspaceIds, {
|
|
92
|
+
invalidateContext: options.invalidateContext !== false,
|
|
93
|
+
invalidateWorktreeContext,
|
|
94
|
+
}), (views) => {
|
|
95
|
+
setReadState({ status: 'ready', views });
|
|
96
|
+
}, (error) => {
|
|
97
|
+
if (preserveCurrent)
|
|
98
|
+
throw error;
|
|
293
99
|
setReadState({
|
|
294
100
|
status: 'error',
|
|
295
101
|
views: [],
|
|
296
102
|
error: toWorktreeViewError(error),
|
|
297
103
|
});
|
|
298
|
-
}
|
|
299
|
-
}, [manager, workspaceIds]);
|
|
104
|
+
});
|
|
105
|
+
}, [invalidateWorktreeContext, manager, workspaceIds]);
|
|
300
106
|
useEffect(() => {
|
|
301
107
|
if (mode === 'worktree') {
|
|
302
|
-
void refresh();
|
|
108
|
+
void refresh({ preserveCurrent: readStateRef.current.status === 'ready' });
|
|
303
109
|
}
|
|
304
110
|
else {
|
|
111
|
+
refreshGuard.current.invalidate();
|
|
305
112
|
setReadState(EMPTY_READ_STATE);
|
|
306
113
|
}
|
|
307
114
|
}, [mode, refresh]);
|
|
@@ -318,12 +125,28 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
318
125
|
const modalView = worktreeModalWorkspaceId === undefined
|
|
319
126
|
? undefined
|
|
320
127
|
: viewByWorkspace.get(worktreeModalWorkspaceId);
|
|
128
|
+
const modalReadiness = modalView?.readiness;
|
|
129
|
+
const modalSetupStatus = modalView === undefined
|
|
130
|
+
? undefined
|
|
131
|
+
: modalReadiness?.status === 'ready'
|
|
132
|
+
? modalView.branches.length === 0 ? 'noLocalBranch' : undefined
|
|
133
|
+
: modalReadiness?.status;
|
|
134
|
+
const modalCanCreate = modalSetupStatus === undefined && modalReadiness?.status === 'ready';
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
if (worktreeModalWorkspaceId === undefined || modalView === undefined)
|
|
137
|
+
return;
|
|
138
|
+
if (modalView.readiness.status !== 'ready') {
|
|
139
|
+
setSelectedBranch('');
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
setSelectedBranch((current) => reconcileBaseBranchSelection(current, modalView.branches));
|
|
143
|
+
}, [modalView, worktreeModalWorkspaceId]);
|
|
321
144
|
const runMutation = async (operation) => {
|
|
322
145
|
setActionPending(true);
|
|
323
146
|
setActionError(undefined);
|
|
324
147
|
try {
|
|
325
148
|
await operation();
|
|
326
|
-
await refresh();
|
|
149
|
+
await refresh({ preserveCurrent: true });
|
|
327
150
|
}
|
|
328
151
|
catch (error) {
|
|
329
152
|
setActionError(toWorktreeViewError(error));
|
|
@@ -483,6 +306,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
483
306
|
setActionError(toWorktreeViewError(error));
|
|
484
307
|
});
|
|
485
308
|
};
|
|
309
|
+
const commitWorktreeDrag = (activeDrag, over, worktreeIds, workspaceId) => {
|
|
310
|
+
if (worktreeDropCommitted.current)
|
|
311
|
+
return;
|
|
312
|
+
worktreeDropCommitted.current = true;
|
|
313
|
+
setWorktreeDrag(undefined);
|
|
314
|
+
if (activeDrag.workspaceId !== workspaceId)
|
|
315
|
+
return;
|
|
316
|
+
const move = resolveWorktreeMove(worktreeIds, activeDrag.worktreeId, over.worktreeId, over.half);
|
|
317
|
+
if (move === undefined)
|
|
318
|
+
return;
|
|
319
|
+
if (insertWorktreeBefore === undefined) {
|
|
320
|
+
setActionError({
|
|
321
|
+
code: 'WORKTREE_ORDER_UNAVAILABLE',
|
|
322
|
+
message: '',
|
|
323
|
+
retryable: true,
|
|
324
|
+
});
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
setActionError(undefined);
|
|
328
|
+
void insertWorktreeBefore(workspaceId, activeDrag.worktreeId, move.beforeWorktreeId)
|
|
329
|
+
.then(() => refresh({ preserveCurrent: true, invalidateContext: false }))
|
|
330
|
+
.catch((error) => {
|
|
331
|
+
setActionError(toRetryableWorktreeOrderError(error));
|
|
332
|
+
});
|
|
333
|
+
};
|
|
486
334
|
const openSessionRename = (sessionId, currentTitle) => {
|
|
487
335
|
setSessionRenameTarget({ sessionId, currentTitle });
|
|
488
336
|
setSessionRenameDraft(currentTitle);
|
|
@@ -559,6 +407,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
559
407
|
const worktreeName = newBranch.trim();
|
|
560
408
|
if (manager === undefined ||
|
|
561
409
|
modalWorkspace === undefined ||
|
|
410
|
+
!modalCanCreate ||
|
|
562
411
|
selectedBranch.length === 0 ||
|
|
563
412
|
worktreeName.length === 0) {
|
|
564
413
|
return;
|
|
@@ -585,7 +434,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
585
434
|
}
|
|
586
435
|
setWorktreeModalWorkspaceId(undefined);
|
|
587
436
|
if (createSessionCallback === undefined) {
|
|
588
|
-
await refresh();
|
|
437
|
+
await refresh({ preserveCurrent: true });
|
|
589
438
|
setActionError({
|
|
590
439
|
code: 'WORKTREE_CREATED_SESSION_UNAVAILABLE',
|
|
591
440
|
message: '',
|
|
@@ -600,14 +449,15 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
600
449
|
};
|
|
601
450
|
try {
|
|
602
451
|
await createSessionCallback(sessionInput);
|
|
452
|
+
await invalidateWorktreeContext?.(createdWorktree.workspaceId);
|
|
603
453
|
}
|
|
604
454
|
catch (error) {
|
|
605
|
-
if (error instanceof WorktreeSessionBindingError) {
|
|
455
|
+
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
606
456
|
setPendingSessionBinding({ ...sessionInput, sessionId: error.sessionId });
|
|
607
457
|
}
|
|
608
458
|
throw error;
|
|
609
459
|
}
|
|
610
|
-
await refresh();
|
|
460
|
+
await refresh({ preserveCurrent: true });
|
|
611
461
|
}
|
|
612
462
|
catch (error) {
|
|
613
463
|
setActionError(toWorktreeViewError(error));
|
|
@@ -630,10 +480,11 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
630
480
|
setPendingSessionBinding(undefined);
|
|
631
481
|
try {
|
|
632
482
|
await createSessionCallback(input);
|
|
633
|
-
await
|
|
483
|
+
await invalidateWorktreeContext?.(input.workspaceId);
|
|
484
|
+
await refresh({ preserveCurrent: true });
|
|
634
485
|
}
|
|
635
486
|
catch (error) {
|
|
636
|
-
if (error instanceof WorktreeSessionBindingError) {
|
|
487
|
+
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
637
488
|
setPendingSessionBinding({ ...input, sessionId: error.sessionId });
|
|
638
489
|
}
|
|
639
490
|
setActionError(toWorktreeViewError(error));
|
|
@@ -643,23 +494,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
643
494
|
}
|
|
644
495
|
};
|
|
645
496
|
const retrySessionBinding = async () => {
|
|
646
|
-
if (manager === undefined ||
|
|
497
|
+
if (manager === undefined ||
|
|
498
|
+
pendingSessionBinding === undefined ||
|
|
499
|
+
pendingSessionArchived)
|
|
647
500
|
return;
|
|
501
|
+
const pending = pendingSessionBinding;
|
|
648
502
|
setActionPending(true);
|
|
649
503
|
setActionError(undefined);
|
|
650
504
|
try {
|
|
651
|
-
await
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
505
|
+
await retryWorktreeSessionBinding({
|
|
506
|
+
manager,
|
|
507
|
+
pending,
|
|
508
|
+
archived: false,
|
|
509
|
+
ensureSessionWorkspace: (workspaceId, sessionId) => {
|
|
510
|
+
ensureSessionWorkspace?.(workspaceId, sessionId);
|
|
511
|
+
},
|
|
512
|
+
openSession,
|
|
655
513
|
});
|
|
656
|
-
const sessionId = pendingSessionBinding.sessionId;
|
|
657
514
|
setPendingSessionBinding(undefined);
|
|
658
|
-
|
|
659
|
-
await
|
|
660
|
-
openSession(sessionId);
|
|
515
|
+
await refresh({ preserveCurrent: true });
|
|
516
|
+
await invalidateWorktreeContext?.(pending.workspaceId);
|
|
661
517
|
}
|
|
662
518
|
catch (error) {
|
|
519
|
+
if (error instanceof WorktreeSessionBindingError && !error.retryable) {
|
|
520
|
+
setPendingSessionBinding(undefined);
|
|
521
|
+
}
|
|
663
522
|
setActionError(toWorktreeViewError(error));
|
|
664
523
|
}
|
|
665
524
|
finally {
|
|
@@ -685,7 +544,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
685
544
|
} }), _jsx("button", { type: "button", className: styles.iconButton, "aria-label": t('workspace.add'), onClick: () => {
|
|
686
545
|
if (createWorkspace !== undefined)
|
|
687
546
|
void runMutation(createWorkspace);
|
|
688
|
-
}, children: _jsx(IconPlusOutline16, {}) })] }), _jsxs("div", { className: styles.content, tabIndex: 0, children: [actionError !== undefined && (_jsxs("div", { className: styles.error, role: "alert", "data-worktree-error": true, children: [_jsx("p", { className: styles.message, "data-error": "true", children: formatWorktreeViewError(actionError, t) }), pendingSessionBinding !== undefined && (_jsxs("div", { className: styles.recoveryActions, children: [_jsx("button", { type: "button", className: styles.actionButton, disabled: actionPending, onClick: () => {
|
|
547
|
+
}, children: _jsx(IconPlusOutline16, {}) })] }), _jsxs("div", { className: styles.content, tabIndex: 0, children: [actionError !== undefined && (_jsxs("div", { className: styles.error, role: "alert", "data-worktree-error": true, children: [_jsx("p", { className: styles.message, "data-error": "true", children: formatWorktreeViewError(actionError, t) }), pendingSessionBinding !== undefined && (_jsxs("div", { className: styles.recoveryActions, children: [_jsx("button", { type: "button", className: styles.actionButton, disabled: actionPending || pendingSessionArchived, onClick: () => {
|
|
689
548
|
void retrySessionBinding();
|
|
690
549
|
}, children: t('action.retryBinding') }), _jsx("button", { type: "button", className: styles.actionButton, disabled: actionPending, onClick: () => {
|
|
691
550
|
const sessionId = pendingSessionBinding.sessionId;
|
|
@@ -699,19 +558,21 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
699
558
|
void refresh();
|
|
700
559
|
}, children: t('action.retry') })] })) : readState.status === 'ready' ? (_jsx("div", { className: styles.workspaceList, children: visibleWorkspaces.length === 0 ? (_jsx("p", { className: styles.empty, children: t('workspace.noMatches') })) : (visibleWorkspaces.map((workspace) => {
|
|
701
560
|
const view = viewByWorkspace.get(workspace.workspaceId);
|
|
561
|
+
const currentBranch = view?.branches.find((branch) => branch.isCurrent)?.name;
|
|
562
|
+
const mainLabel = currentBranch === undefined
|
|
563
|
+
? t('worktree.main')
|
|
564
|
+
: t('worktree.mainWithBranch', { branch: currentBranch });
|
|
702
565
|
const expanded = expandedWorkspaces[workspace.workspaceId] !== false;
|
|
703
566
|
const workspaceMatchesQuery = includesText(workspace.title, query);
|
|
704
567
|
const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
|
|
705
568
|
const bindings = view?.bindings ?? [];
|
|
706
569
|
const boundSessionIds = new Set(bindings.map((binding) => binding.sessionId));
|
|
707
|
-
const mainSessionIds = unboundSessionIds(allWorkspaceSessionIds, [
|
|
708
|
-
|
|
709
|
-
]);
|
|
710
|
-
const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery ||
|
|
711
|
-
includesText(sessionLabel(sessionId, sessions), query));
|
|
570
|
+
const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...boundSessionIds]), sessions);
|
|
571
|
+
const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
712
572
|
const mainExpanded = expandedMains[workspace.workspaceId] !== false;
|
|
713
573
|
const mainGroupKey = `main:${workspace.workspaceId}`;
|
|
714
574
|
const worktrees = view?.worktrees ?? [];
|
|
575
|
+
const sameWorkspaceWorktreeDrag = worktreeDrag?.workspaceId === workspace.workspaceId;
|
|
715
576
|
return (_jsxs("section", { className: styles.workspaceGroup, "data-workspace-id": workspace.workspaceId, children: [_jsx(WorktreeWorkspaceRow, { t: t, workspace: workspace, expanded: expanded, actionPending: actionPending, menuOpen: openWorkspaceMenuId === workspace.workspaceId, drag: {
|
|
716
577
|
active: workspaceDrag !== undefined,
|
|
717
578
|
marker: workspaceDrag?.over?.workspaceId === workspace.workspaceId
|
|
@@ -756,7 +617,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
756
617
|
openWorkspaceDelete(workspace);
|
|
757
618
|
}, onMenuOpenChange: (open) => {
|
|
758
619
|
setOpenWorkspaceMenuId(open ? workspace.workspaceId : undefined);
|
|
759
|
-
} }), expanded && (_jsxs("div", { className: styles.treeChildren, children: [_jsx(WorktreeGroupRow, { t: t, kind: "main", label:
|
|
620
|
+
} }), expanded && (_jsxs("div", { className: styles.treeChildren, children: [_jsx(WorktreeGroupRow, { t: t, kind: "main", label: mainLabel, expanded: mainExpanded, icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, onToggle: () => {
|
|
760
621
|
toggleMain(workspace.workspaceId);
|
|
761
622
|
}, onCreateSession: createMainSession === undefined
|
|
762
623
|
? undefined
|
|
@@ -781,17 +642,16 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
781
642
|
}, onCommitDrag: commitSessionDrag, onOpen: (sessionId) => {
|
|
782
643
|
openWorktreeSession({ open: openSession }, sessionId);
|
|
783
644
|
}, onRename: renameSession === undefined ? undefined : openSessionRename, onFork: forkSession, onArchive: archiveSession === undefined ? undefined : archiveWorktreeSession })), worktrees.length === 0 && (_jsx("p", { className: styles.emptyNested, children: t('worktree.noWorktrees') })), worktrees.map((record) => {
|
|
784
|
-
const worktreeSessionIds = filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds);
|
|
645
|
+
const worktreeSessionIds = filterVisibleSessionIds(filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds), sessions);
|
|
785
646
|
const worktreeMatchesQuery = workspaceMatchesQuery ||
|
|
786
647
|
includesText(record.branch, query) ||
|
|
787
648
|
includesText(record.absolutePath, query);
|
|
788
649
|
if (query.length > 0 &&
|
|
789
650
|
!worktreeMatchesQuery &&
|
|
790
|
-
!worktreeSessionIds.some((sessionId) =>
|
|
651
|
+
!worktreeSessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) {
|
|
791
652
|
return null;
|
|
792
653
|
}
|
|
793
|
-
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery ||
|
|
794
|
-
includesText(sessionLabel(sessionId, sessions), query));
|
|
654
|
+
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
795
655
|
const worktreeGroupKey = `worktree:${record.worktreeId}`;
|
|
796
656
|
const state = record.status === 'removed'
|
|
797
657
|
? 'warning'
|
|
@@ -804,9 +664,9 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
804
664
|
? t('worktree.repair')
|
|
805
665
|
: t('worktree.ready');
|
|
806
666
|
const worktreeExpanded = expandedWorktrees[record.worktreeId] !== false;
|
|
807
|
-
return (_jsxs("div", { className: styles.worktreeGroup, "data-worktree-id": record.worktreeId, children: [_jsx(WorktreeGroupRow, { t: t, kind: "worktree", label: record.branch, expanded: worktreeExpanded, icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, state: state, stateLabel: stateLabel, onToggle: () => {
|
|
667
|
+
return (_jsxs("div", { className: styles.worktreeGroup, "data-worktree-id": record.worktreeId, children: [_jsx(WorktreeGroupRow, { t: t, kind: "worktree", label: record.branch, worktreeId: record.worktreeId, expanded: worktreeExpanded, icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, state: state, stateLabel: stateLabel, onToggle: () => {
|
|
808
668
|
toggleWorktree(record.worktreeId);
|
|
809
|
-
}, onCreateSession: record.status === 'active'
|
|
669
|
+
}, onCreateSession: record.status === 'active' && record.health !== 'repair'
|
|
810
670
|
? () => {
|
|
811
671
|
void createSession({
|
|
812
672
|
workspaceId: record.workspaceId,
|
|
@@ -827,7 +687,38 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
827
687
|
setActionError(undefined);
|
|
828
688
|
},
|
|
829
689
|
}
|
|
830
|
-
: undefined
|
|
690
|
+
: undefined, drag: {
|
|
691
|
+
active: sameWorkspaceWorktreeDrag,
|
|
692
|
+
marker: worktreeDrag?.over?.worktreeId === record.worktreeId
|
|
693
|
+
? worktreeDrag.over.half
|
|
694
|
+
: null,
|
|
695
|
+
start: () => {
|
|
696
|
+
worktreeDropCommitted.current = false;
|
|
697
|
+
setWorktreeDrag({
|
|
698
|
+
workspaceId: workspace.workspaceId,
|
|
699
|
+
worktreeId: record.worktreeId,
|
|
700
|
+
over: null,
|
|
701
|
+
});
|
|
702
|
+
},
|
|
703
|
+
hover: (half) => {
|
|
704
|
+
setWorktreeDrag((current) => current === undefined ||
|
|
705
|
+
current.workspaceId !== workspace.workspaceId
|
|
706
|
+
? current
|
|
707
|
+
: {
|
|
708
|
+
...current,
|
|
709
|
+
over: { worktreeId: record.worktreeId, half },
|
|
710
|
+
});
|
|
711
|
+
},
|
|
712
|
+
drop: (half) => {
|
|
713
|
+
if (worktreeDrag === undefined)
|
|
714
|
+
return;
|
|
715
|
+
commitWorktreeDrag(worktreeDrag, { worktreeId: record.worktreeId, half }, worktrees.map((candidate) => candidate.worktreeId), workspace.workspaceId);
|
|
716
|
+
},
|
|
717
|
+
end: () => {
|
|
718
|
+
setWorktreeDrag(undefined);
|
|
719
|
+
worktreeDropCommitted.current = false;
|
|
720
|
+
},
|
|
721
|
+
} }), worktreeExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: worktreeGroupKey, sessionIds: visibleWorktreeSessionIds, workspaceId: workspace.workspaceId, expanded: expandedSessionGroups[worktreeGroupKey] === true, actionPending: actionPending, sessions: sessions, dragState: sessionDrag, onToggleExpanded: () => {
|
|
831
722
|
setExpandedSessionGroups((current) => ({
|
|
832
723
|
...current,
|
|
833
724
|
[worktreeGroupKey]: current[worktreeGroupKey] !== true,
|
|
@@ -851,66 +742,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
851
742
|
})] }))] }, workspace.workspaceId));
|
|
852
743
|
})) })) : null] })] }), _jsx("div", { className: styles.railContent, children: _jsx("button", { type: "button", className: styles.railButton, "aria-label": t('mode.exit'), onClick: () => {
|
|
853
744
|
actions.setViewMode('workspace-session');
|
|
854
|
-
}, children: "WT" }) }),
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
}, onKeyDown: (event) => {
|
|
862
|
-
if (event.key === 'Enter' && !event.nativeEvent.isComposing) {
|
|
863
|
-
event.preventDefault();
|
|
864
|
-
void confirmSessionRename();
|
|
865
|
-
}
|
|
866
|
-
} }), sessionRenameError !== undefined && (_jsx("p", { className: styles.renameError, role: "alert", children: formatWorktreeViewError(sessionRenameError, t) }))] }), workspaceRenameTarget !== undefined && (_jsxs(Modal, { open: workspaceRenameTarget !== undefined, onClose: closeWorkspaceRename, closeLabel: t('dialog.closeWorkspaceRename'), title: t('workspace.renameTitle'), footer: (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: workspaceRenamePending, onClick: closeWorkspaceRename, children: t('dialog.cancel') }), _jsx(Button, { variant: "primary", disabled: workspaceRenameBlocked, onClick: () => {
|
|
867
|
-
void confirmWorkspaceRename();
|
|
868
|
-
}, children: t('dialog.rename') })] })), children: [_jsx(Input, { className: styles.renameInput, value: workspaceRenameDraft, "aria-label": t('workspace.name'), autoFocus: true, disabled: workspaceRenamePending, onFocus: (event) => {
|
|
869
|
-
event.currentTarget.select();
|
|
870
|
-
}, onChange: (event) => {
|
|
871
|
-
setWorkspaceRenameDraft(event.currentTarget.value);
|
|
872
|
-
setWorkspaceRenameError(undefined);
|
|
873
|
-
}, onKeyDown: (event) => {
|
|
874
|
-
if (event.key === 'Enter' && !event.nativeEvent.isComposing) {
|
|
875
|
-
event.preventDefault();
|
|
876
|
-
void confirmWorkspaceRename();
|
|
877
|
-
}
|
|
878
|
-
} }), workspaceRenameDuplicate && (_jsx("p", { className: styles.renameError, role: "alert", children: t('workspace.duplicate') })), workspaceRenameError !== undefined && (_jsx("p", { className: styles.renameError, role: "alert", children: formatWorktreeViewError(workspaceRenameError, t) }))] })), workspaceDeleteTarget !== undefined && (_jsx(Modal, { open: workspaceDeleteTarget !== undefined, onClose: closeWorkspaceDelete, closeLabel: t('dialog.closeWorkspaceDelete'), title: t('workspace.deleteTitle'), description: t('workspace.deleteDescription', { name: workspaceDeleteTarget.title }), footer: (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: workspaceDeletePending, onClick: closeWorkspaceDelete, children: t('dialog.cancel') }), _jsx(Button, { variant: "outline", disabled: workspaceDeletePending, onClick: () => {
|
|
879
|
-
void confirmWorkspaceDelete();
|
|
880
|
-
}, children: t('dialog.delete') })] })), children: workspaceDeleteError !== undefined && (_jsx("p", { className: styles.renameError, role: "alert", children: formatWorktreeViewError(workspaceDeleteError, t) })) })), worktreeModalWorkspaceId !== undefined && modalWorkspace !== undefined && (_jsxs(Modal, { open: worktreeModalWorkspaceId !== undefined, onClose: () => {
|
|
881
|
-
if (actionPending)
|
|
882
|
-
return;
|
|
745
|
+
}, children: "WT" }) }), _jsx(WorktreeSessionRenameDialog, { t: t, target: sessionRenameTarget, draft: sessionRenameDraft, pending: sessionRenamePending, error: sessionRenameError, onClose: closeSessionRename, onDraftChange: (draft) => {
|
|
746
|
+
setSessionRenameDraft(draft);
|
|
747
|
+
setSessionRenameError(undefined);
|
|
748
|
+
}, onSubmit: confirmSessionRename }), _jsx(WorktreeWorkspaceRenameDialog, { t: t, target: workspaceRenameTarget, draft: workspaceRenameDraft, pending: workspaceRenamePending, duplicate: workspaceRenameDuplicate, error: workspaceRenameError, onClose: closeWorkspaceRename, onDraftChange: (draft) => {
|
|
749
|
+
setWorkspaceRenameDraft(draft);
|
|
750
|
+
setWorkspaceRenameError(undefined);
|
|
751
|
+
}, onSubmit: confirmWorkspaceRename }), _jsx(WorktreeWorkspaceDeleteDialog, { t: t, target: workspaceDeleteTarget, pending: workspaceDeletePending, error: workspaceDeleteError, onClose: closeWorkspaceDelete, onSubmit: confirmWorkspaceDelete }), _jsx(WorktreeCreateDialog, { t: t, workspace: modalWorkspace, view: modalView, setupStatus: modalSetupStatus, canCreate: modalCanCreate, selectedBranch: selectedBranch, newBranch: newBranch, actionPending: actionPending, onClose: () => {
|
|
883
752
|
setWorktreeModalWorkspaceId(undefined);
|
|
884
|
-
},
|
|
885
|
-
setWorktreeModalWorkspaceId(undefined);
|
|
886
|
-
}, children: t('dialog.cancel') }), _jsx(Button, { variant: "primary", disabled: actionPending ||
|
|
887
|
-
selectedBranch.length === 0 ||
|
|
888
|
-
newBranch.trim().length === 0, onClick: () => {
|
|
889
|
-
void submitWorktree();
|
|
890
|
-
}, children: t('worktree.create') })] })), children: [_jsxs("label", { className: styles.modalField, children: [t('worktree.baseBranch'), _jsxs("select", { className: styles.actionSelect, "aria-label": t('worktree.baseBranch'), value: selectedBranch, disabled: actionPending, onChange: (event) => {
|
|
891
|
-
setSelectedBranch(event.currentTarget.value);
|
|
892
|
-
}, children: [_jsx("option", { value: "", children: t('worktree.noLocalBranch') }), (modalView?.branches ?? []).map((branch) => (_jsxs("option", { value: branch.name, children: [branch.name, branch.isCurrent ? t('branch.current') : '', branch.checkedOut ? t('branch.checkedOut') : ''] }, branch.name)))] })] }), _jsxs("label", { className: styles.modalField, children: [t('worktree.name'), _jsx(Input, { className: styles.renameInput, "aria-label": t('worktree.name'), value: newBranch, placeholder: "dsh/12345678", disabled: actionPending, onChange: (event) => {
|
|
893
|
-
setNewBranch(event.currentTarget.value);
|
|
894
|
-
} })] }), modalView?.branches.length === 0 && (_jsx("p", { className: styles.empty, children: t('worktree.noBranches') }))] })), worktreeRemoval !== undefined && (_jsx(Modal, { open: worktreeRemoval !== undefined, onClose: () => {
|
|
895
|
-
if (actionPending)
|
|
896
|
-
return;
|
|
753
|
+
}, onSelectedBranchChange: setSelectedBranch, onNewBranchChange: setNewBranch, onSubmit: submitWorktree }), _jsx(WorktreeRemovalDialog, { t: t, worktree: worktreeRemoval, actionPending: actionPending, onClose: () => {
|
|
897
754
|
setWorktreeRemoval(undefined);
|
|
898
|
-
},
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
}, children: t('worktree.remove') })] })) }))] }));
|
|
755
|
+
}, onSubmit: () => {
|
|
756
|
+
if (manager === undefined || worktreeRemoval === undefined)
|
|
757
|
+
return;
|
|
758
|
+
const target = worktreeRemoval;
|
|
759
|
+
void runMutation(async () => {
|
|
760
|
+
await executeWorktreeAction(manager, {
|
|
761
|
+
type: 'removeWorktree',
|
|
762
|
+
input: {
|
|
763
|
+
workspaceId: target.workspaceId,
|
|
764
|
+
worktreeId: target.worktreeId,
|
|
765
|
+
},
|
|
766
|
+
});
|
|
767
|
+
await invalidateWorktreeContext?.(target.workspaceId);
|
|
768
|
+
setWorktreeRemoval(undefined);
|
|
769
|
+
});
|
|
770
|
+
} })] }));
|
|
915
771
|
}
|
|
916
772
|
//# sourceMappingURL=WorktreeSurface.js.map
|