@cerbur/clutch-dsh-worktree 0.1.2 → 0.1.3
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 +20 -5
- 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 -1
- package/lib/client/WorktreeSurface.d.ts.map +1 -1
- package/lib/client/WorktreeSurface.js +200 -57
- 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-view.d.ts +37 -3
- package/lib/client/worktree-view.d.ts.map +1 -1
- package/lib/client/worktree-view.js +145 -7
- package/lib/client/worktree-view.js.map +1 -1
- package/lib/client.js +1003 -133
- 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.d.ts +6 -1
- package/lib/manage/manager.d.ts.map +1 -1
- package/lib/manage/manager.js +12 -1
- package/lib/manage/manager.js.map +1 -1
- package/lib/provider/sidecar.d.ts +2 -1
- package/lib/provider/sidecar.d.ts.map +1 -1
- package/lib/provider/sidecar.js +40 -0
- 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
|
@@ -5,8 +5,20 @@ 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, isBlankSession, sessionDisplayLabel, sessionMatchesQuery, } from './session-view.js';
|
|
9
|
+
import { retryWorktreeSessionBinding } from './worktree-session.js';
|
|
10
|
+
import { executeWorktreeAction, createDefaultWorktreeName, createWorktreeRefreshGuard, filterArchivedSessionIds, loadWorktreeViews, reconcileBaseBranchSelection, resolveWorktreeMove, selectDefaultBaseBranch, stableWorkspaceIds, toRetryableWorktreeOrderError, toWorktreeViewError, WorktreeSessionBindingError, worktreeSetupCommands, } from './worktree-view.js';
|
|
9
11
|
import styles from './worktree.css';
|
|
12
|
+
function worktreeSetupMessage(status, t) {
|
|
13
|
+
switch (status) {
|
|
14
|
+
case 'noRepository':
|
|
15
|
+
return t('worktree.setup.noRepository');
|
|
16
|
+
case 'noInitialCommit':
|
|
17
|
+
return t('worktree.setup.noInitialCommit');
|
|
18
|
+
case 'noLocalBranch':
|
|
19
|
+
return t('worktree.setup.noLocalBranch');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
10
22
|
function toNativeWorktreeViewError(error) {
|
|
11
23
|
const viewError = toWorktreeViewError(error);
|
|
12
24
|
if (typeof error === 'object' && error !== null)
|
|
@@ -22,8 +34,8 @@ function useStableWorkspaceIds(workspaces) {
|
|
|
22
34
|
previousRef.current = stable;
|
|
23
35
|
return previousRef.current;
|
|
24
36
|
}
|
|
25
|
-
function sessionLabel(sessionId, sessions) {
|
|
26
|
-
return
|
|
37
|
+
function sessionLabel(sessionId, sessions, t) {
|
|
38
|
+
return sessionDisplayLabel(sessionId, sessions, t('session.new'));
|
|
27
39
|
}
|
|
28
40
|
function rowHalf(event) {
|
|
29
41
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
@@ -42,7 +54,7 @@ function workspaceMatches(workspace, view, sessions, query) {
|
|
|
42
54
|
return true;
|
|
43
55
|
if (includesText(workspace.title, query))
|
|
44
56
|
return true;
|
|
45
|
-
if (workspace.sessionIds.some((sessionId) =>
|
|
57
|
+
if (workspace.sessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) {
|
|
46
58
|
return true;
|
|
47
59
|
}
|
|
48
60
|
if (view === undefined)
|
|
@@ -50,7 +62,7 @@ function workspaceMatches(workspace, view, sessions, query) {
|
|
|
50
62
|
if (view.worktrees.some((record) => includesText(record.branch, query) || includesText(record.absolutePath, query))) {
|
|
51
63
|
return true;
|
|
52
64
|
}
|
|
53
|
-
return view.bindings.some((binding) =>
|
|
65
|
+
return view.bindings.some((binding) => sessionMatchesQuery(binding.sessionId, sessions, query));
|
|
54
66
|
}
|
|
55
67
|
/** Workspace row using the native DSH menu, fixed action column, and drag contract. */
|
|
56
68
|
function WorktreeWorkspaceRow({ t, workspace, expanded, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange, }) {
|
|
@@ -113,9 +125,38 @@ function WorktreeWorkspaceRow({ t, workspace, expanded, actionPending, menuOpen,
|
|
|
113
125
|
}, children: _jsx(IconPlusOutline16, {}) })] })] }));
|
|
114
126
|
}
|
|
115
127
|
/** 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, }) {
|
|
128
|
+
function WorktreeGroupRow({ t, kind, label, worktreeId, expanded, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession, menu, drag, }) {
|
|
117
129
|
const main = kind === 'main';
|
|
118
|
-
const
|
|
130
|
+
const markerClass = drag?.marker === 'before'
|
|
131
|
+
? styles.dropBefore
|
|
132
|
+
: drag?.marker === 'after'
|
|
133
|
+
? styles.dropAfter
|
|
134
|
+
: '';
|
|
135
|
+
const dragProps = drag === undefined
|
|
136
|
+
? {}
|
|
137
|
+
: {
|
|
138
|
+
draggable: true,
|
|
139
|
+
onDragStart: (event) => {
|
|
140
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
141
|
+
event.dataTransfer.setData('text/plain', worktreeId ?? label);
|
|
142
|
+
drag.start();
|
|
143
|
+
},
|
|
144
|
+
onDragEnd: drag.end,
|
|
145
|
+
onDragOver: (event) => {
|
|
146
|
+
if (!drag.active)
|
|
147
|
+
return;
|
|
148
|
+
event.preventDefault();
|
|
149
|
+
event.dataTransfer.dropEffect = 'move';
|
|
150
|
+
drag.hover(rowHalf(event));
|
|
151
|
+
},
|
|
152
|
+
onDrop: (event) => {
|
|
153
|
+
if (!drag.active)
|
|
154
|
+
return;
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
drag.drop(rowHalf(event));
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
const row = (_jsxs("div", { className: `${styles.worktreeRow} ${markerClass}`, "data-main-group": main ? 'true' : undefined, "data-main-expanded": main ? String(expanded) : undefined, "data-menu-open": menu?.open ? 'true' : undefined, "data-worktree-drag": drag?.active ? 'active' : undefined, ...dragProps, 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
160
|
event.stopPropagation();
|
|
120
161
|
onToggle();
|
|
121
162
|
}, 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: () => {
|
|
@@ -144,7 +185,7 @@ function WorktreeGroupRow({ t, kind, label, expanded, icon, workspaceTitle, stat
|
|
|
144
185
|
return (_jsx(HoverCard, { anchor: row, content: _jsx("div", { className: styles.worktreeHoverTitle, children: label }), openDelayMs: 500, disabled: menu?.open === true }));
|
|
145
186
|
}
|
|
146
187
|
/** 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, }) {
|
|
188
|
+
function WorktreeSessionRow({ t, sessionId, blank, label, drag, actionPending, onOpen, onRename, onFork, onArchive, }) {
|
|
148
189
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
149
190
|
const markerClass = drag.marker === 'before'
|
|
150
191
|
? styles.dropBefore
|
|
@@ -171,7 +212,7 @@ function WorktreeSessionRow({ t, sessionId, label, drag, actionPending, onOpen,
|
|
|
171
212
|
disabled: onArchive === undefined || actionPending,
|
|
172
213
|
},
|
|
173
214
|
];
|
|
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) => {
|
|
215
|
+
return (_jsxs("div", { className: `${styles.treeSessionRow} ${markerClass}`, "data-session-id": sessionId, "data-session-blank": blank ? 'true' : undefined, "data-session-drag": drag.active ? 'active' : undefined, "data-menu-open": menuOpen || undefined, role: "treeitem", draggable: true, onDragStart: (event) => {
|
|
175
216
|
event.dataTransfer.effectAllowed = 'move';
|
|
176
217
|
event.dataTransfer.setData('text/plain', sessionId);
|
|
177
218
|
drag.start();
|
|
@@ -186,7 +227,7 @@ function WorktreeSessionRow({ t, sessionId, label, drag, actionPending, onOpen,
|
|
|
186
227
|
return;
|
|
187
228
|
event.preventDefault();
|
|
188
229
|
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: () => {
|
|
230
|
+
}, 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 })] }), !blank && (_jsx("span", { className: styles.rowActions, children: _jsx(Menu, { open: menuOpen, onClose: () => {
|
|
190
231
|
setMenuOpen(false);
|
|
191
232
|
}, items: sessionMenuItems, onSelect: (id) => {
|
|
192
233
|
setMenuOpen(false);
|
|
@@ -199,12 +240,12 @@ function WorktreeSessionRow({ t, sessionId, label, drag, actionPending, onOpen,
|
|
|
199
240
|
}, 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
241
|
event.stopPropagation();
|
|
201
242
|
setMenuOpen((current) => !current);
|
|
202
|
-
}, children: _jsx(IconEllipsisOutline16, {}) })) }) })] }));
|
|
243
|
+
}, children: _jsx(IconEllipsisOutline16, {}) })) }) }))] }));
|
|
203
244
|
}
|
|
204
245
|
function WorktreeSessionGroup({ t, groupKey, sessionIds, workspaceId, expanded, actionPending, sessions, dragState, onToggleExpanded, onStartDrag, onHoverDrag, onClearDrag, onFinishDrag, onCommitDrag, onOpen, onRename, onFork, onArchive, }) {
|
|
205
246
|
const visibleSessionIds = expanded ? sessionIds : sessionIds.slice(0, 5);
|
|
206
247
|
const sameGroupDrag = dragState?.groupKey === groupKey;
|
|
207
|
-
return (_jsxs(_Fragment, { children: [visibleSessionIds.map((sessionId) => (_jsx(WorktreeSessionRow, { t: t, sessionId: sessionId, label: sessionLabel(sessionId, sessions), drag: {
|
|
248
|
+
return (_jsxs(_Fragment, { children: [visibleSessionIds.map((sessionId) => (_jsx(WorktreeSessionRow, { t: t, sessionId: sessionId, blank: isBlankSession(sessionId, sessions), label: sessionLabel(sessionId, sessions, t), drag: {
|
|
208
249
|
active: sameGroupDrag,
|
|
209
250
|
marker: sameGroupDrag && dragState.over?.sessionId === sessionId
|
|
210
251
|
? dragState.over.half
|
|
@@ -239,13 +280,15 @@ function WorktreeSessionGroup({ t, groupKey, sessionIds, workspaceId, expanded,
|
|
|
239
280
|
* Peer Worktree navigation surface. It keeps the original DSH Workspace mode
|
|
240
281
|
* intact and renders a browser-local Workspace → Worktree → Session projection.
|
|
241
282
|
*/
|
|
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, }) {
|
|
283
|
+
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
284
|
const preferredMode = useStore((state) => state.viewMode);
|
|
244
285
|
const mode = effectiveViewMode(preferredMode, available && manager !== undefined);
|
|
245
286
|
const sessions = useSessions((state) => state);
|
|
246
287
|
const workspaces = useWorkspaces((state) => state);
|
|
247
288
|
const workspaceIds = useStableWorkspaceIds(workspaces.items);
|
|
248
289
|
const [readState, setReadState] = useState(EMPTY_READ_STATE);
|
|
290
|
+
const readStateRef = useRef(readState);
|
|
291
|
+
readStateRef.current = readState;
|
|
249
292
|
const [searchQuery, setSearchQuery] = useState('');
|
|
250
293
|
const [expandedWorkspaces, setExpandedWorkspaces] = useState({});
|
|
251
294
|
const [expandedMains, setExpandedMains] = useState({});
|
|
@@ -275,33 +318,46 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
275
318
|
const [sessionDrag, setSessionDrag] = useState();
|
|
276
319
|
const [expandedSessionGroups, setExpandedSessionGroups] = useState({});
|
|
277
320
|
const sessionDropCommitted = useRef(false);
|
|
321
|
+
const [worktreeDrag, setWorktreeDrag] = useState();
|
|
322
|
+
const worktreeDropCommitted = useRef(false);
|
|
323
|
+
const refreshGuard = useRef(createWorktreeRefreshGuard());
|
|
278
324
|
const { ref, width, bounds } = useSidebarOverlayGeometry(mode === 'worktree');
|
|
279
325
|
const collapsed = width <= 64;
|
|
280
326
|
const query = searchQuery.trim().toLocaleLowerCase();
|
|
281
327
|
const archivedSessionIds = workspaces.archivedSessionIds ?? [];
|
|
282
|
-
const
|
|
328
|
+
const pendingSessionArchived = pendingSessionBinding !== undefined &&
|
|
329
|
+
archivedSessionIds.includes(pendingSessionBinding.sessionId);
|
|
330
|
+
const refresh = useCallback(async (options = {}) => {
|
|
283
331
|
if (manager === undefined) {
|
|
332
|
+
refreshGuard.current.invalidate();
|
|
284
333
|
setReadState(EMPTY_READ_STATE);
|
|
285
334
|
return;
|
|
286
335
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
setReadState({ status: 'ready', views });
|
|
336
|
+
const preserveCurrent = options.preserveCurrent === true;
|
|
337
|
+
if (!preserveCurrent) {
|
|
338
|
+
setReadState({ status: 'loading', views: [] });
|
|
291
339
|
}
|
|
292
|
-
|
|
340
|
+
await refreshGuard.current.run(() => loadWorktreeViews(manager, workspaceIds, {
|
|
341
|
+
invalidateContext: options.invalidateContext !== false,
|
|
342
|
+
invalidateWorktreeContext,
|
|
343
|
+
}), (views) => {
|
|
344
|
+
setReadState({ status: 'ready', views });
|
|
345
|
+
}, (error) => {
|
|
346
|
+
if (preserveCurrent)
|
|
347
|
+
throw error;
|
|
293
348
|
setReadState({
|
|
294
349
|
status: 'error',
|
|
295
350
|
views: [],
|
|
296
351
|
error: toWorktreeViewError(error),
|
|
297
352
|
});
|
|
298
|
-
}
|
|
299
|
-
}, [manager, workspaceIds]);
|
|
353
|
+
});
|
|
354
|
+
}, [invalidateWorktreeContext, manager, workspaceIds]);
|
|
300
355
|
useEffect(() => {
|
|
301
356
|
if (mode === 'worktree') {
|
|
302
|
-
void refresh();
|
|
357
|
+
void refresh({ preserveCurrent: readStateRef.current.status === 'ready' });
|
|
303
358
|
}
|
|
304
359
|
else {
|
|
360
|
+
refreshGuard.current.invalidate();
|
|
305
361
|
setReadState(EMPTY_READ_STATE);
|
|
306
362
|
}
|
|
307
363
|
}, [mode, refresh]);
|
|
@@ -318,12 +374,28 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
318
374
|
const modalView = worktreeModalWorkspaceId === undefined
|
|
319
375
|
? undefined
|
|
320
376
|
: viewByWorkspace.get(worktreeModalWorkspaceId);
|
|
377
|
+
const modalReadiness = modalView?.readiness;
|
|
378
|
+
const modalSetupStatus = modalView === undefined
|
|
379
|
+
? undefined
|
|
380
|
+
: modalReadiness?.status === 'ready'
|
|
381
|
+
? modalView.branches.length === 0 ? 'noLocalBranch' : undefined
|
|
382
|
+
: modalReadiness?.status;
|
|
383
|
+
const modalCanCreate = modalSetupStatus === undefined && modalReadiness?.status === 'ready';
|
|
384
|
+
useEffect(() => {
|
|
385
|
+
if (worktreeModalWorkspaceId === undefined || modalView === undefined)
|
|
386
|
+
return;
|
|
387
|
+
if (modalView.readiness.status !== 'ready') {
|
|
388
|
+
setSelectedBranch('');
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
setSelectedBranch((current) => reconcileBaseBranchSelection(current, modalView.branches));
|
|
392
|
+
}, [modalView, worktreeModalWorkspaceId]);
|
|
321
393
|
const runMutation = async (operation) => {
|
|
322
394
|
setActionPending(true);
|
|
323
395
|
setActionError(undefined);
|
|
324
396
|
try {
|
|
325
397
|
await operation();
|
|
326
|
-
await refresh();
|
|
398
|
+
await refresh({ preserveCurrent: true });
|
|
327
399
|
}
|
|
328
400
|
catch (error) {
|
|
329
401
|
setActionError(toWorktreeViewError(error));
|
|
@@ -483,6 +555,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
483
555
|
setActionError(toWorktreeViewError(error));
|
|
484
556
|
});
|
|
485
557
|
};
|
|
558
|
+
const commitWorktreeDrag = (activeDrag, over, worktreeIds, workspaceId) => {
|
|
559
|
+
if (worktreeDropCommitted.current)
|
|
560
|
+
return;
|
|
561
|
+
worktreeDropCommitted.current = true;
|
|
562
|
+
setWorktreeDrag(undefined);
|
|
563
|
+
if (activeDrag.workspaceId !== workspaceId)
|
|
564
|
+
return;
|
|
565
|
+
const move = resolveWorktreeMove(worktreeIds, activeDrag.worktreeId, over.worktreeId, over.half);
|
|
566
|
+
if (move === undefined)
|
|
567
|
+
return;
|
|
568
|
+
if (insertWorktreeBefore === undefined) {
|
|
569
|
+
setActionError({
|
|
570
|
+
code: 'WORKTREE_ORDER_UNAVAILABLE',
|
|
571
|
+
message: '',
|
|
572
|
+
retryable: true,
|
|
573
|
+
});
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
setActionError(undefined);
|
|
577
|
+
void insertWorktreeBefore(workspaceId, activeDrag.worktreeId, move.beforeWorktreeId)
|
|
578
|
+
.then(() => refresh({ preserveCurrent: true, invalidateContext: false }))
|
|
579
|
+
.catch((error) => {
|
|
580
|
+
setActionError(toRetryableWorktreeOrderError(error));
|
|
581
|
+
});
|
|
582
|
+
};
|
|
486
583
|
const openSessionRename = (sessionId, currentTitle) => {
|
|
487
584
|
setSessionRenameTarget({ sessionId, currentTitle });
|
|
488
585
|
setSessionRenameDraft(currentTitle);
|
|
@@ -559,6 +656,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
559
656
|
const worktreeName = newBranch.trim();
|
|
560
657
|
if (manager === undefined ||
|
|
561
658
|
modalWorkspace === undefined ||
|
|
659
|
+
!modalCanCreate ||
|
|
562
660
|
selectedBranch.length === 0 ||
|
|
563
661
|
worktreeName.length === 0) {
|
|
564
662
|
return;
|
|
@@ -585,7 +683,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
585
683
|
}
|
|
586
684
|
setWorktreeModalWorkspaceId(undefined);
|
|
587
685
|
if (createSessionCallback === undefined) {
|
|
588
|
-
await refresh();
|
|
686
|
+
await refresh({ preserveCurrent: true });
|
|
589
687
|
setActionError({
|
|
590
688
|
code: 'WORKTREE_CREATED_SESSION_UNAVAILABLE',
|
|
591
689
|
message: '',
|
|
@@ -600,14 +698,15 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
600
698
|
};
|
|
601
699
|
try {
|
|
602
700
|
await createSessionCallback(sessionInput);
|
|
701
|
+
await invalidateWorktreeContext?.(createdWorktree.workspaceId);
|
|
603
702
|
}
|
|
604
703
|
catch (error) {
|
|
605
|
-
if (error instanceof WorktreeSessionBindingError) {
|
|
704
|
+
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
606
705
|
setPendingSessionBinding({ ...sessionInput, sessionId: error.sessionId });
|
|
607
706
|
}
|
|
608
707
|
throw error;
|
|
609
708
|
}
|
|
610
|
-
await refresh();
|
|
709
|
+
await refresh({ preserveCurrent: true });
|
|
611
710
|
}
|
|
612
711
|
catch (error) {
|
|
613
712
|
setActionError(toWorktreeViewError(error));
|
|
@@ -630,10 +729,11 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
630
729
|
setPendingSessionBinding(undefined);
|
|
631
730
|
try {
|
|
632
731
|
await createSessionCallback(input);
|
|
633
|
-
await
|
|
732
|
+
await invalidateWorktreeContext?.(input.workspaceId);
|
|
733
|
+
await refresh({ preserveCurrent: true });
|
|
634
734
|
}
|
|
635
735
|
catch (error) {
|
|
636
|
-
if (error instanceof WorktreeSessionBindingError) {
|
|
736
|
+
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
637
737
|
setPendingSessionBinding({ ...input, sessionId: error.sessionId });
|
|
638
738
|
}
|
|
639
739
|
setActionError(toWorktreeViewError(error));
|
|
@@ -643,23 +743,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
643
743
|
}
|
|
644
744
|
};
|
|
645
745
|
const retrySessionBinding = async () => {
|
|
646
|
-
if (manager === undefined ||
|
|
746
|
+
if (manager === undefined ||
|
|
747
|
+
pendingSessionBinding === undefined ||
|
|
748
|
+
pendingSessionArchived)
|
|
647
749
|
return;
|
|
750
|
+
const pending = pendingSessionBinding;
|
|
648
751
|
setActionPending(true);
|
|
649
752
|
setActionError(undefined);
|
|
650
753
|
try {
|
|
651
|
-
await
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
754
|
+
await retryWorktreeSessionBinding({
|
|
755
|
+
manager,
|
|
756
|
+
pending,
|
|
757
|
+
archived: false,
|
|
758
|
+
ensureSessionWorkspace: (workspaceId, sessionId) => {
|
|
759
|
+
ensureSessionWorkspace?.(workspaceId, sessionId);
|
|
760
|
+
},
|
|
761
|
+
openSession,
|
|
655
762
|
});
|
|
656
|
-
const sessionId = pendingSessionBinding.sessionId;
|
|
657
763
|
setPendingSessionBinding(undefined);
|
|
658
|
-
|
|
659
|
-
await
|
|
660
|
-
openSession(sessionId);
|
|
764
|
+
await refresh({ preserveCurrent: true });
|
|
765
|
+
await invalidateWorktreeContext?.(pending.workspaceId);
|
|
661
766
|
}
|
|
662
767
|
catch (error) {
|
|
768
|
+
if (error instanceof WorktreeSessionBindingError && !error.retryable) {
|
|
769
|
+
setPendingSessionBinding(undefined);
|
|
770
|
+
}
|
|
663
771
|
setActionError(toWorktreeViewError(error));
|
|
664
772
|
}
|
|
665
773
|
finally {
|
|
@@ -685,7 +793,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
685
793
|
} }), _jsx("button", { type: "button", className: styles.iconButton, "aria-label": t('workspace.add'), onClick: () => {
|
|
686
794
|
if (createWorkspace !== undefined)
|
|
687
795
|
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: () => {
|
|
796
|
+
}, 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
797
|
void retrySessionBinding();
|
|
690
798
|
}, children: t('action.retryBinding') }), _jsx("button", { type: "button", className: styles.actionButton, disabled: actionPending, onClick: () => {
|
|
691
799
|
const sessionId = pendingSessionBinding.sessionId;
|
|
@@ -699,19 +807,21 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
699
807
|
void refresh();
|
|
700
808
|
}, 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
809
|
const view = viewByWorkspace.get(workspace.workspaceId);
|
|
810
|
+
const currentBranch = view?.branches.find((branch) => branch.isCurrent)?.name;
|
|
811
|
+
const mainLabel = currentBranch === undefined
|
|
812
|
+
? t('worktree.main')
|
|
813
|
+
: t('worktree.mainWithBranch', { branch: currentBranch });
|
|
702
814
|
const expanded = expandedWorkspaces[workspace.workspaceId] !== false;
|
|
703
815
|
const workspaceMatchesQuery = includesText(workspace.title, query);
|
|
704
816
|
const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
|
|
705
817
|
const bindings = view?.bindings ?? [];
|
|
706
818
|
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));
|
|
819
|
+
const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...boundSessionIds]), sessions);
|
|
820
|
+
const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
712
821
|
const mainExpanded = expandedMains[workspace.workspaceId] !== false;
|
|
713
822
|
const mainGroupKey = `main:${workspace.workspaceId}`;
|
|
714
823
|
const worktrees = view?.worktrees ?? [];
|
|
824
|
+
const sameWorkspaceWorktreeDrag = worktreeDrag?.workspaceId === workspace.workspaceId;
|
|
715
825
|
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
826
|
active: workspaceDrag !== undefined,
|
|
717
827
|
marker: workspaceDrag?.over?.workspaceId === workspace.workspaceId
|
|
@@ -756,7 +866,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
756
866
|
openWorkspaceDelete(workspace);
|
|
757
867
|
}, onMenuOpenChange: (open) => {
|
|
758
868
|
setOpenWorkspaceMenuId(open ? workspace.workspaceId : undefined);
|
|
759
|
-
} }), expanded && (_jsxs("div", { className: styles.treeChildren, children: [_jsx(WorktreeGroupRow, { t: t, kind: "main", label:
|
|
869
|
+
} }), 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
870
|
toggleMain(workspace.workspaceId);
|
|
761
871
|
}, onCreateSession: createMainSession === undefined
|
|
762
872
|
? undefined
|
|
@@ -781,17 +891,16 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
781
891
|
}, onCommitDrag: commitSessionDrag, onOpen: (sessionId) => {
|
|
782
892
|
openWorktreeSession({ open: openSession }, sessionId);
|
|
783
893
|
}, 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);
|
|
894
|
+
const worktreeSessionIds = filterVisibleSessionIds(filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds), sessions);
|
|
785
895
|
const worktreeMatchesQuery = workspaceMatchesQuery ||
|
|
786
896
|
includesText(record.branch, query) ||
|
|
787
897
|
includesText(record.absolutePath, query);
|
|
788
898
|
if (query.length > 0 &&
|
|
789
899
|
!worktreeMatchesQuery &&
|
|
790
|
-
!worktreeSessionIds.some((sessionId) =>
|
|
900
|
+
!worktreeSessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) {
|
|
791
901
|
return null;
|
|
792
902
|
}
|
|
793
|
-
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery ||
|
|
794
|
-
includesText(sessionLabel(sessionId, sessions), query));
|
|
903
|
+
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
795
904
|
const worktreeGroupKey = `worktree:${record.worktreeId}`;
|
|
796
905
|
const state = record.status === 'removed'
|
|
797
906
|
? 'warning'
|
|
@@ -804,9 +913,9 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
804
913
|
? t('worktree.repair')
|
|
805
914
|
: t('worktree.ready');
|
|
806
915
|
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: () => {
|
|
916
|
+
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
917
|
toggleWorktree(record.worktreeId);
|
|
809
|
-
}, onCreateSession: record.status === 'active'
|
|
918
|
+
}, onCreateSession: record.status === 'active' && record.health !== 'repair'
|
|
810
919
|
? () => {
|
|
811
920
|
void createSession({
|
|
812
921
|
workspaceId: record.workspaceId,
|
|
@@ -827,7 +936,38 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
827
936
|
setActionError(undefined);
|
|
828
937
|
},
|
|
829
938
|
}
|
|
830
|
-
: undefined
|
|
939
|
+
: undefined, drag: {
|
|
940
|
+
active: sameWorkspaceWorktreeDrag,
|
|
941
|
+
marker: worktreeDrag?.over?.worktreeId === record.worktreeId
|
|
942
|
+
? worktreeDrag.over.half
|
|
943
|
+
: null,
|
|
944
|
+
start: () => {
|
|
945
|
+
worktreeDropCommitted.current = false;
|
|
946
|
+
setWorktreeDrag({
|
|
947
|
+
workspaceId: workspace.workspaceId,
|
|
948
|
+
worktreeId: record.worktreeId,
|
|
949
|
+
over: null,
|
|
950
|
+
});
|
|
951
|
+
},
|
|
952
|
+
hover: (half) => {
|
|
953
|
+
setWorktreeDrag((current) => current === undefined ||
|
|
954
|
+
current.workspaceId !== workspace.workspaceId
|
|
955
|
+
? current
|
|
956
|
+
: {
|
|
957
|
+
...current,
|
|
958
|
+
over: { worktreeId: record.worktreeId, half },
|
|
959
|
+
});
|
|
960
|
+
},
|
|
961
|
+
drop: (half) => {
|
|
962
|
+
if (worktreeDrag === undefined)
|
|
963
|
+
return;
|
|
964
|
+
commitWorktreeDrag(worktreeDrag, { worktreeId: record.worktreeId, half }, worktrees.map((candidate) => candidate.worktreeId), workspace.workspaceId);
|
|
965
|
+
},
|
|
966
|
+
end: () => {
|
|
967
|
+
setWorktreeDrag(undefined);
|
|
968
|
+
worktreeDropCommitted.current = false;
|
|
969
|
+
},
|
|
970
|
+
} }), worktreeExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: worktreeGroupKey, sessionIds: visibleWorktreeSessionIds, workspaceId: workspace.workspaceId, expanded: expandedSessionGroups[worktreeGroupKey] === true, actionPending: actionPending, sessions: sessions, dragState: sessionDrag, onToggleExpanded: () => {
|
|
831
971
|
setExpandedSessionGroups((current) => ({
|
|
832
972
|
...current,
|
|
833
973
|
[worktreeGroupKey]: current[worktreeGroupKey] !== true,
|
|
@@ -877,7 +1017,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
877
1017
|
}
|
|
878
1018
|
} }), 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
1019
|
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 && (
|
|
1020
|
+
}, children: t('dialog.delete') })] })), children: workspaceDeleteError !== undefined && (_jsx("p", { className: styles.renameError, role: "alert", children: formatWorktreeViewError(workspaceDeleteError, t) })) })), worktreeModalWorkspaceId !== undefined && modalWorkspace !== undefined && (_jsx(Modal, { open: worktreeModalWorkspaceId !== undefined, onClose: () => {
|
|
881
1021
|
if (actionPending)
|
|
882
1022
|
return;
|
|
883
1023
|
setWorktreeModalWorkspaceId(undefined);
|
|
@@ -887,11 +1027,13 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
887
1027
|
selectedBranch.length === 0 ||
|
|
888
1028
|
newBranch.trim().length === 0, onClick: () => {
|
|
889
1029
|
void submitWorktree();
|
|
890
|
-
}, children: t('worktree.create') })] })), children: [_jsxs("label", { className: styles.modalField, children: [t('worktree.baseBranch'),
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1030
|
+
}, children: t('worktree.create') })] })), children: modalCanCreate ? (_jsxs(_Fragment, { children: [_jsxs("label", { className: styles.modalField, children: [t('worktree.baseBranch'), _jsx("select", { className: styles.actionSelect, "aria-label": t('worktree.baseBranch'), value: selectedBranch, disabled: actionPending, onChange: (event) => {
|
|
1031
|
+
setSelectedBranch(event.currentTarget.value);
|
|
1032
|
+
}, children: 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) => {
|
|
1033
|
+
setNewBranch(event.currentTarget.value);
|
|
1034
|
+
} })] })] })) : (_jsxs("div", { className: styles.gitReadiness, "data-worktree-readiness": modalSetupStatus ?? 'loading', role: "alert", children: [_jsx("p", { className: styles.message, children: modalSetupStatus === undefined
|
|
1035
|
+
? t('status.loading')
|
|
1036
|
+
: worktreeSetupMessage(modalSetupStatus, t) }), modalSetupStatus !== undefined && (_jsx("pre", { className: styles.commandBlock, "aria-label": t('worktree.setup.commands'), children: worktreeSetupCommands(modalSetupStatus).join('\n') }))] })) })), worktreeRemoval !== undefined && (_jsx(Modal, { open: worktreeRemoval !== undefined, onClose: () => {
|
|
895
1037
|
if (actionPending)
|
|
896
1038
|
return;
|
|
897
1039
|
setWorktreeRemoval(undefined);
|
|
@@ -909,6 +1051,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
909
1051
|
worktreeId: target.worktreeId,
|
|
910
1052
|
},
|
|
911
1053
|
});
|
|
1054
|
+
await invalidateWorktreeContext?.(target.workspaceId);
|
|
912
1055
|
setWorktreeRemoval(undefined);
|
|
913
1056
|
});
|
|
914
1057
|
}, children: t('worktree.remove') })] })) }))] }));
|