@cerbur/clutch-dsh-worktree 0.1.8 → 0.1.9
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 +18 -23
- package/README.zh.md +17 -21
- package/lib/client/WorktreeContext.d.ts +1 -1
- package/lib/client/WorktreeContext.d.ts.map +1 -1
- package/lib/client/WorktreeContext.js +1 -1
- package/lib/client/WorktreeContext.js.map +1 -1
- package/lib/client/WorktreeHeroContext.d.ts.map +1 -1
- package/lib/client/WorktreeHeroContext.js +1 -1
- package/lib/client/WorktreeHeroContext.js.map +1 -1
- package/lib/client/WorktreeModeAction.d.ts.map +1 -1
- package/lib/client/WorktreeModeAction.js.map +1 -1
- package/lib/client/WorktreeSurface.d.ts +1 -1
- package/lib/client/WorktreeSurface.d.ts.map +1 -1
- package/lib/client/WorktreeSurface.js +221 -45
- package/lib/client/WorktreeSurface.js.map +1 -1
- package/lib/client/dsh-rc1-slot-contract.d.ts +43 -0
- package/lib/client/dsh-rc1-slot-contract.d.ts.map +1 -0
- package/lib/client/dsh-rc1-slot-contract.js +2 -0
- package/lib/client/dsh-rc1-slot-contract.js.map +1 -0
- package/lib/client/dsh-slot-contract.d.ts +43 -0
- package/lib/client/dsh-slot-contract.d.ts.map +1 -0
- package/lib/client/dsh-slot-contract.js +2 -0
- package/lib/client/dsh-slot-contract.js.map +1 -0
- package/lib/client/entry.d.ts +2 -2
- package/lib/client/entry.d.ts.map +1 -1
- package/lib/client/entry.js +144 -26
- package/lib/client/entry.js.map +1 -1
- package/lib/client/view-mode-store.d.ts +1 -1
- package/lib/client/view-mode-store.d.ts.map +1 -1
- package/lib/client/view-mode-store.js +1 -1
- package/lib/client/view-mode-store.js.map +1 -1
- package/lib/client/view-mode.d.ts +20 -6
- package/lib/client/view-mode.d.ts.map +1 -1
- package/lib/client/view-mode.js +35 -10
- package/lib/client/view-mode.js.map +1 -1
- package/lib/client/virtual-workspace-membership.d.ts +12 -10
- package/lib/client/virtual-workspace-membership.d.ts.map +1 -1
- package/lib/client/virtual-workspace-membership.js +82 -57
- package/lib/client/virtual-workspace-membership.js.map +1 -1
- package/lib/client/worktree-context-store.d.ts +7 -15
- package/lib/client/worktree-context-store.d.ts.map +1 -1
- package/lib/client/worktree-context-store.js +20 -13
- package/lib/client/worktree-context-store.js.map +1 -1
- package/lib/client/worktree-expand-state.d.ts +5 -4
- package/lib/client/worktree-expand-state.d.ts.map +1 -1
- package/lib/client/worktree-expand-state.js.map +1 -1
- package/lib/client/worktree-session-fork.d.ts +18 -3
- package/lib/client/worktree-session-fork.d.ts.map +1 -1
- package/lib/client/worktree-session-fork.js +125 -36
- package/lib/client/worktree-session-fork.js.map +1 -1
- package/lib/client/worktree-session-order.d.ts +2 -2
- package/lib/client/worktree-session-order.d.ts.map +1 -1
- package/lib/client/worktree-surface-rows.d.ts.map +1 -1
- package/lib/client/worktree-surface-rows.js +1 -1
- package/lib/client/worktree-surface-rows.js.map +1 -1
- package/lib/client/worktree-surface-types.d.ts +19 -1
- package/lib/client/worktree-surface-types.d.ts.map +1 -1
- package/lib/client/worktree-view-read.d.ts +18 -8
- package/lib/client/worktree-view-read.d.ts.map +1 -1
- package/lib/client/worktree-view-read.js +75 -10
- package/lib/client/worktree-view-read.js.map +1 -1
- package/lib/client.js +1359 -169
- package/lib/client.js.map +1 -1
- package/lib/host/worktree-permission.d.ts +3 -2
- package/lib/host/worktree-permission.d.ts.map +1 -1
- package/lib/host/worktree-permission.js +29 -5
- package/lib/host/worktree-permission.js.map +1 -1
- package/lib/manage/manager-worktrees.d.ts.map +1 -1
- package/lib/manage/manager-worktrees.js +5 -2
- package/lib/manage/manager-worktrees.js.map +1 -1
- package/lib/manage/manager.d.ts.map +1 -1
- package/lib/manage/manager.js +2 -0
- package/lib/manage/manager.js.map +1 -1
- package/lib/provider/git.d.ts.map +1 -1
- package/lib/provider/git.js +4 -0
- package/lib/provider/git.js.map +1 -1
- package/lib/provider/sidecar.js +1 -1
- package/lib/provider/sidecar.js.map +1 -1
- package/lib/provider/subprocess.d.ts.map +1 -1
- package/lib/provider/subprocess.js +8 -0
- package/lib/provider/subprocess.js.map +1 -1
- package/lib/provider/transaction.js +2 -2
- package/lib/provider/transaction.js.map +1 -1
- package/package.json +48 -34
|
@@ -13,7 +13,7 @@ import { WorktreeCreateDialog, WorktreeRemovalDialog, WorktreeSessionRenameDialo
|
|
|
13
13
|
import { bindingIdsFor, clearSessionGroupExpansion, includesText, isCompleteWorktreeWorkspaceSnapshot, currentSessionRevealKeys, isSessionGroupAutoExpanded, resolveCurrentSessionLocation, workspaceMatches, } from './worktree-surface-selectors.js';
|
|
14
14
|
import { scrollCurrentSessionIntoView } from './worktree-session-position.js';
|
|
15
15
|
import { WorktreeGroupRow, WorktreeSessionGroup, WorktreeWorkspaceRow, } from './worktree-surface-rows.js';
|
|
16
|
-
import { executeWorktreeAction, createDefaultWorktreeName, createNumberedWorktreeName, createWorktreeModalViewLoader, createWorktreeRefreshGuard, filterArchivedSessionIds,
|
|
16
|
+
import { executeWorktreeAction, createDefaultWorktreeName, createNumberedWorktreeName, createWorktreeModalViewLoader, createWorktreeRefreshGuard, filterArchivedSessionIds, mergeWorktreeView, mergeWorktreeViews, reconcileBaseBranchSelection, resolveWorktreeMove, selectDefaultBaseBranch, stableWorkspaceIds, toRetryableWorktreeOrderError, toWorktreeViewError, WorktreeSessionBindingError, WorktreeSessionPermissionError, } from './worktree-view.js';
|
|
17
17
|
import styles from './worktree.css';
|
|
18
18
|
function toNativeWorktreeViewError(error) {
|
|
19
19
|
const viewError = toWorktreeViewError(error);
|
|
@@ -27,7 +27,11 @@ const EMPTY_PERMISSION_NOTICE_SUBSCRIBE = () => () => { };
|
|
|
27
27
|
const EMPTY_PERMISSION_NOTICE_SNAPSHOT = () => EMPTY_PERMISSION_NOTICE;
|
|
28
28
|
const EMPTY_FULL_ACCESS_CONFIRMATION_SUBSCRIBE = () => () => { };
|
|
29
29
|
const EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT = () => undefined;
|
|
30
|
-
const EMPTY_FORK_RECOVERY_SNAPSHOT = {
|
|
30
|
+
const EMPTY_FORK_RECOVERY_SNAPSHOT = {
|
|
31
|
+
revision: 0,
|
|
32
|
+
pending: [],
|
|
33
|
+
affectedWorkspaceIds: [],
|
|
34
|
+
};
|
|
31
35
|
const EMPTY_FORK_RECOVERY_STORE = {
|
|
32
36
|
getSnapshot: () => EMPTY_FORK_RECOVERY_SNAPSHOT,
|
|
33
37
|
subscribe: () => () => { },
|
|
@@ -58,7 +62,7 @@ function useStableWorkspaceIds(workspaces) {
|
|
|
58
62
|
* Peer Worktree navigation surface. It keeps the original DSH Workspace mode
|
|
59
63
|
* intact and renders a browser-local Workspace → Worktree → Session projection.
|
|
60
64
|
*/
|
|
61
|
-
export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, expandState, sessionOrder, manager, permission, confirmFullAccess, fullAccessConfirmation, onPermissionResult, onPermissionNotice, permissionNotice, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, forkRecovery, retryForkSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession, }) {
|
|
65
|
+
export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, expandState, sessionOrder, manager, viewReader, permission, confirmFullAccess, fullAccessConfirmation, onPermissionResult, onPermissionNotice, permissionNotice, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, forkRecovery, retryForkSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession, }) {
|
|
62
66
|
const preferredMode = useStore((state) => state.viewMode);
|
|
63
67
|
const mode = effectiveViewMode(preferredMode, available && manager !== undefined);
|
|
64
68
|
const sessions = useSessions((state) => state);
|
|
@@ -67,6 +71,8 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
67
71
|
const forkRecoverySnapshot = useSyncExternalStore(forkRecoveryStore.subscribe, forkRecoveryStore.getSnapshot, forkRecoveryStore.getSnapshot);
|
|
68
72
|
const currentSessionId = sessions.current;
|
|
69
73
|
const workspaceIds = useStableWorkspaceIds(workspaces.items);
|
|
74
|
+
const workspaceIdsRef = useRef(workspaceIds);
|
|
75
|
+
workspaceIdsRef.current = workspaceIds;
|
|
70
76
|
const [readState, setReadState] = useState(EMPTY_READ_STATE);
|
|
71
77
|
const expandSnapshot = useSyncExternalStore(expandState.subscribe, expandState.getSnapshot, expandState.getSnapshot);
|
|
72
78
|
const permissionNoticeSnapshot = useSyncExternalStore(permissionNotice?.subscribe ?? EMPTY_PERMISSION_NOTICE_SUBSCRIBE, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT);
|
|
@@ -131,8 +137,9 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
131
137
|
const [worktreeDrag, setWorktreeDrag] = useState();
|
|
132
138
|
const worktreeDropCommitted = useRef(false);
|
|
133
139
|
const refreshGuard = useRef(createWorktreeRefreshGuard());
|
|
140
|
+
const targetRefreshGuards = useRef(new Map());
|
|
134
141
|
const forkRecoveryRevisionRef = useRef(forkRecoverySnapshot.revision);
|
|
135
|
-
const modalReadLoader = useRef(createWorktreeModalViewLoader());
|
|
142
|
+
const modalReadLoader = useRef(createWorktreeModalViewLoader(viewReader));
|
|
136
143
|
const modalReadViewRef = useRef();
|
|
137
144
|
const importCandidatesGuard = useRef(createWorktreeRefreshGuard());
|
|
138
145
|
const modalWorkspaceIdRef = useRef(worktreeModalWorkspaceId);
|
|
@@ -143,36 +150,131 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
143
150
|
const archivedSessionIds = workspaces.archivedSessionIds ?? [];
|
|
144
151
|
const pendingSessionArchived = pendingSessionBinding !== undefined &&
|
|
145
152
|
archivedSessionIds.includes(pendingSessionBinding.sessionId);
|
|
153
|
+
const guardFor = (workspaceId) => {
|
|
154
|
+
const current = targetRefreshGuards.current.get(workspaceId);
|
|
155
|
+
if (current !== undefined)
|
|
156
|
+
return current;
|
|
157
|
+
const created = createWorktreeRefreshGuard();
|
|
158
|
+
targetRefreshGuards.current.set(workspaceId, created);
|
|
159
|
+
return created;
|
|
160
|
+
};
|
|
146
161
|
const refresh = useCallback(async (options = {}) => {
|
|
147
162
|
if (manager === undefined) {
|
|
148
163
|
refreshGuard.current.invalidate();
|
|
164
|
+
for (const guard of targetRefreshGuards.current.values())
|
|
165
|
+
guard.invalidate();
|
|
149
166
|
setReadState(EMPTY_READ_STATE);
|
|
150
167
|
return;
|
|
151
168
|
}
|
|
169
|
+
const scope = options.scope ?? { kind: 'global' };
|
|
152
170
|
const preserveCurrent = options.preserveCurrent === true;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
const currentWorkspaceIds = workspaceIdsRef.current;
|
|
172
|
+
if (scope.kind === 'global') {
|
|
173
|
+
if (!preserveCurrent) {
|
|
174
|
+
setReadState({ status: 'loading', views: [] });
|
|
175
|
+
}
|
|
176
|
+
const selectedWorkspaceIds = [...currentWorkspaceIds];
|
|
177
|
+
for (const workspaceId of selectedWorkspaceIds) {
|
|
178
|
+
viewReader.invalidate(workspaceId);
|
|
179
|
+
}
|
|
180
|
+
const contextRefresh = options.invalidateContext === false
|
|
181
|
+
? undefined
|
|
182
|
+
: invalidateWorktreeContext?.();
|
|
183
|
+
await refreshGuard.current.run(async () => {
|
|
184
|
+
const [views] = await Promise.all([
|
|
185
|
+
viewReader.readMany(selectedWorkspaceIds),
|
|
186
|
+
contextRefresh ?? Promise.resolve(),
|
|
187
|
+
]);
|
|
188
|
+
return views;
|
|
189
|
+
}, (views) => {
|
|
190
|
+
setReadState((current) => {
|
|
191
|
+
const merged = mergeWorktreeViews(current.views, workspaceIdsRef.current, views);
|
|
192
|
+
return {
|
|
193
|
+
status: 'ready',
|
|
194
|
+
views: modalReadViewRef.current === undefined
|
|
195
|
+
? merged
|
|
196
|
+
: mergeWorktreeView(merged, modalReadViewRef.current),
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}, (error) => {
|
|
200
|
+
if (preserveCurrent)
|
|
201
|
+
throw error;
|
|
202
|
+
setReadState({
|
|
203
|
+
status: 'error',
|
|
204
|
+
views: modalReadViewRef.current === undefined ? [] : [modalReadViewRef.current],
|
|
205
|
+
error: toWorktreeViewError(error),
|
|
206
|
+
});
|
|
173
207
|
});
|
|
174
|
-
|
|
175
|
-
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const requestedWorkspaceIds = scope.kind === 'workspace'
|
|
211
|
+
? [scope.workspaceId]
|
|
212
|
+
: scope.workspaceIds;
|
|
213
|
+
const selectedWorkspaceIds = [...new Set(requestedWorkspaceIds)].filter((workspaceId) => currentWorkspaceIds.includes(workspaceId));
|
|
214
|
+
if (selectedWorkspaceIds.length === 0)
|
|
215
|
+
return;
|
|
216
|
+
await Promise.allSettled(selectedWorkspaceIds.map(async (workspaceId) => {
|
|
217
|
+
const guard = guardFor(workspaceId);
|
|
218
|
+
viewReader.invalidate(workspaceId);
|
|
219
|
+
const contextRefresh = options.invalidateContext === false
|
|
220
|
+
? undefined
|
|
221
|
+
: invalidateWorktreeContext?.(workspaceId);
|
|
222
|
+
const generation = guard.begin();
|
|
223
|
+
try {
|
|
224
|
+
const view = await viewReader.read(workspaceId);
|
|
225
|
+
if (!guard.isCurrent(generation))
|
|
226
|
+
return;
|
|
227
|
+
if (!workspaceIdsRef.current.includes(workspaceId))
|
|
228
|
+
return;
|
|
229
|
+
setReadState((current) => {
|
|
230
|
+
const remainingTargetError = current.targetError?.workspaceIds.filter((candidate) => candidate !== workspaceId) ?? [];
|
|
231
|
+
const next = {
|
|
232
|
+
...current,
|
|
233
|
+
status: 'ready',
|
|
234
|
+
views: mergeWorktreeViews(current.views, workspaceIdsRef.current, [view]),
|
|
235
|
+
error: undefined,
|
|
236
|
+
};
|
|
237
|
+
if (remainingTargetError.length === 0) {
|
|
238
|
+
const withoutTargetError = { ...next };
|
|
239
|
+
delete withoutTargetError.targetError;
|
|
240
|
+
return withoutTargetError;
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
...next,
|
|
244
|
+
targetError: {
|
|
245
|
+
workspaceIds: remainingTargetError,
|
|
246
|
+
error: current.targetError.error,
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
if (!guard.isCurrent(generation))
|
|
253
|
+
return;
|
|
254
|
+
if (!workspaceIdsRef.current.includes(workspaceId))
|
|
255
|
+
return;
|
|
256
|
+
setReadState((current) => {
|
|
257
|
+
const previousIds = current.targetError?.workspaceIds ?? [];
|
|
258
|
+
return {
|
|
259
|
+
...current,
|
|
260
|
+
status: 'ready',
|
|
261
|
+
views: mergeWorktreeViews(current.views, workspaceIdsRef.current, []),
|
|
262
|
+
error: undefined,
|
|
263
|
+
targetError: {
|
|
264
|
+
workspaceIds: [...new Set([...previousIds, workspaceId])],
|
|
265
|
+
error: {
|
|
266
|
+
...toWorktreeViewError(error),
|
|
267
|
+
retryable: true,
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
await contextRefresh?.catch(() => undefined);
|
|
275
|
+
}
|
|
276
|
+
}));
|
|
277
|
+
}, [invalidateWorktreeContext, manager, viewReader]);
|
|
176
278
|
const loadModalWorktreeView = useCallback((workspaceId) => {
|
|
177
279
|
setModalReadError(undefined);
|
|
178
280
|
setModalReadLoading(true);
|
|
@@ -185,7 +287,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
185
287
|
});
|
|
186
288
|
return;
|
|
187
289
|
}
|
|
188
|
-
void modalReadLoader.current.load(
|
|
290
|
+
void modalReadLoader.current.load(workspaceId, (view) => {
|
|
189
291
|
modalReadViewRef.current = view;
|
|
190
292
|
setModalReadLoading(false);
|
|
191
293
|
setReadState((current) => ({
|
|
@@ -196,13 +298,15 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
196
298
|
setModalReadLoading(false);
|
|
197
299
|
setModalReadError(toWorktreeViewError(error));
|
|
198
300
|
});
|
|
199
|
-
}, [manager]);
|
|
301
|
+
}, [manager, viewReader]);
|
|
200
302
|
useEffect(() => {
|
|
201
303
|
if (mode === 'worktree') {
|
|
202
304
|
void refresh({ preserveCurrent: readStateRef.current.status === 'ready' });
|
|
203
305
|
}
|
|
204
306
|
else {
|
|
205
307
|
refreshGuard.current.invalidate();
|
|
308
|
+
for (const guard of targetRefreshGuards.current.values())
|
|
309
|
+
guard.invalidate();
|
|
206
310
|
modalReadLoader.current.invalidate();
|
|
207
311
|
modalReadViewRef.current = undefined;
|
|
208
312
|
setWorktreeModalWorkspaceId(undefined);
|
|
@@ -211,19 +315,67 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
211
315
|
setReadState(EMPTY_READ_STATE);
|
|
212
316
|
}
|
|
213
317
|
}, [mode, refresh]);
|
|
318
|
+
const previousWorkspaceIdsRef = useRef(workspaceIds);
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
const previousWorkspaceIds = previousWorkspaceIdsRef.current;
|
|
321
|
+
previousWorkspaceIdsRef.current = workspaceIds;
|
|
322
|
+
if (mode !== 'worktree')
|
|
323
|
+
return;
|
|
324
|
+
const addedWorkspaceIds = workspaceIds.filter((workspaceId) => !previousWorkspaceIds.includes(workspaceId));
|
|
325
|
+
const removedWorkspaceIds = previousWorkspaceIds.filter((workspaceId) => !workspaceIds.includes(workspaceId));
|
|
326
|
+
for (const workspaceId of removedWorkspaceIds) {
|
|
327
|
+
targetRefreshGuards.current.delete(workspaceId);
|
|
328
|
+
}
|
|
329
|
+
setReadState((current) => {
|
|
330
|
+
const views = mergeWorktreeViews(current.views, workspaceIds, []);
|
|
331
|
+
const remainingTargetError = current.targetError?.workspaceIds.filter((workspaceId) => workspaceIds.includes(workspaceId)) ?? [];
|
|
332
|
+
const next = { ...current, views };
|
|
333
|
+
if (remainingTargetError.length === 0) {
|
|
334
|
+
const withoutTargetError = { ...next };
|
|
335
|
+
delete withoutTargetError.targetError;
|
|
336
|
+
return withoutTargetError;
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
...next,
|
|
340
|
+
targetError: {
|
|
341
|
+
workspaceIds: remainingTargetError,
|
|
342
|
+
error: current.targetError.error,
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
});
|
|
346
|
+
for (const workspaceId of addedWorkspaceIds) {
|
|
347
|
+
void refresh({
|
|
348
|
+
scope: { kind: 'workspace', workspaceId },
|
|
349
|
+
preserveCurrent: true,
|
|
350
|
+
}).catch(() => {
|
|
351
|
+
// The target error remains visible and can be retried explicitly.
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}, [mode, refresh, workspaceIds]);
|
|
214
355
|
useEffect(() => {
|
|
215
356
|
if (forkRecoveryRevisionRef.current === forkRecoverySnapshot.revision)
|
|
216
357
|
return;
|
|
217
358
|
forkRecoveryRevisionRef.current = forkRecoverySnapshot.revision;
|
|
218
359
|
if (mode !== 'worktree' || manager === undefined)
|
|
219
360
|
return;
|
|
361
|
+
if (forkRecoverySnapshot.affectedWorkspaceIds.length === 0)
|
|
362
|
+
return;
|
|
220
363
|
void refresh({
|
|
364
|
+
scope: {
|
|
365
|
+
kind: 'workspaces',
|
|
366
|
+
workspaceIds: forkRecoverySnapshot.affectedWorkspaceIds,
|
|
367
|
+
},
|
|
221
368
|
preserveCurrent: readStateRef.current.status === 'ready',
|
|
222
|
-
invalidateContext: false,
|
|
223
369
|
}).catch(() => {
|
|
224
370
|
// Recovery state remains visible; the next explicit retry can refresh it again.
|
|
225
371
|
});
|
|
226
|
-
}, [
|
|
372
|
+
}, [
|
|
373
|
+
forkRecoverySnapshot.affectedWorkspaceIds,
|
|
374
|
+
forkRecoverySnapshot.revision,
|
|
375
|
+
manager,
|
|
376
|
+
mode,
|
|
377
|
+
refresh,
|
|
378
|
+
]);
|
|
227
379
|
useEffect(() => {
|
|
228
380
|
if (readState.status !== 'ready' || syncSessionWorkspaces === undefined)
|
|
229
381
|
return;
|
|
@@ -376,12 +528,13 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
376
528
|
return createDefaultWorktreeName(existingNames);
|
|
377
529
|
});
|
|
378
530
|
}, [modalView, worktreeModalWorkspaceId]);
|
|
379
|
-
const runMutation = async (operation) => {
|
|
531
|
+
const runMutation = async (operation, refreshOptions) => {
|
|
380
532
|
setActionPending(true);
|
|
381
533
|
setActionError(undefined);
|
|
382
534
|
try {
|
|
383
535
|
await operation();
|
|
384
|
-
|
|
536
|
+
if (refreshOptions !== undefined)
|
|
537
|
+
await refresh(refreshOptions);
|
|
385
538
|
}
|
|
386
539
|
catch (error) {
|
|
387
540
|
setActionError(toWorktreeViewError(error));
|
|
@@ -578,7 +731,11 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
578
731
|
}
|
|
579
732
|
setActionError(undefined);
|
|
580
733
|
void insertWorktreeBefore(workspaceId, activeDrag.worktreeId, move.beforeWorktreeId)
|
|
581
|
-
.then(() => refresh({
|
|
734
|
+
.then(() => refresh({
|
|
735
|
+
scope: { kind: 'workspace', workspaceId },
|
|
736
|
+
preserveCurrent: true,
|
|
737
|
+
invalidateContext: false,
|
|
738
|
+
}))
|
|
582
739
|
.catch((error) => {
|
|
583
740
|
setActionError(toRetryableWorktreeOrderError(error));
|
|
584
741
|
});
|
|
@@ -756,7 +913,10 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
756
913
|
const continueWorktreeRegistration = async (registeredWorktree) => {
|
|
757
914
|
closeWorktreeCreator(true);
|
|
758
915
|
if (createSessionCallback === undefined) {
|
|
759
|
-
await refresh({
|
|
916
|
+
await refresh({
|
|
917
|
+
scope: { kind: 'workspace', workspaceId: registeredWorktree.workspaceId },
|
|
918
|
+
preserveCurrent: true,
|
|
919
|
+
});
|
|
760
920
|
setActionError({
|
|
761
921
|
code: 'WORKTREE_REGISTRATION_SESSION_UNAVAILABLE',
|
|
762
922
|
message: '',
|
|
@@ -771,7 +931,6 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
771
931
|
};
|
|
772
932
|
try {
|
|
773
933
|
await createSessionCallback(sessionInput);
|
|
774
|
-
await invalidateWorktreeContext?.(registeredWorktree.workspaceId);
|
|
775
934
|
}
|
|
776
935
|
catch (error) {
|
|
777
936
|
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
@@ -786,7 +945,10 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
786
945
|
}
|
|
787
946
|
throw error;
|
|
788
947
|
}
|
|
789
|
-
await refresh({
|
|
948
|
+
await refresh({
|
|
949
|
+
scope: { kind: 'workspace', workspaceId: registeredWorktree.workspaceId },
|
|
950
|
+
preserveCurrent: true,
|
|
951
|
+
});
|
|
790
952
|
};
|
|
791
953
|
const submitWorktree = async () => {
|
|
792
954
|
const worktreeName = newBranch.trim();
|
|
@@ -848,8 +1010,10 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
848
1010
|
setPendingSessionBinding(undefined);
|
|
849
1011
|
try {
|
|
850
1012
|
await createSessionCallback(input);
|
|
851
|
-
await
|
|
852
|
-
|
|
1013
|
+
await refresh({
|
|
1014
|
+
scope: { kind: 'workspace', workspaceId: input.workspaceId },
|
|
1015
|
+
preserveCurrent: true,
|
|
1016
|
+
});
|
|
853
1017
|
}
|
|
854
1018
|
catch (error) {
|
|
855
1019
|
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
@@ -888,8 +1052,10 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
888
1052
|
openSession,
|
|
889
1053
|
});
|
|
890
1054
|
setPendingSessionBinding(undefined);
|
|
891
|
-
await refresh({
|
|
892
|
-
|
|
1055
|
+
await refresh({
|
|
1056
|
+
scope: { kind: 'workspace', workspaceId: pending.workspaceId },
|
|
1057
|
+
preserveCurrent: true,
|
|
1058
|
+
});
|
|
893
1059
|
}
|
|
894
1060
|
catch (error) {
|
|
895
1061
|
if (error instanceof WorktreeSessionBindingError && !error.retryable) {
|
|
@@ -950,9 +1116,17 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
950
1116
|
openSession(sessionId);
|
|
951
1117
|
}, children: t('action.openCreatedSession') }))] })), actionError.retryable && (_jsx("button", { type: "button", className: styles.retryButton, onClick: () => {
|
|
952
1118
|
setActionError(undefined);
|
|
953
|
-
void refresh();
|
|
954
|
-
}, children: t('action.retry') }))] })), readState.
|
|
955
|
-
void refresh(
|
|
1119
|
+
void refresh({ scope: { kind: 'global' } });
|
|
1120
|
+
}, children: t('action.retry') }))] })), readState.targetError !== undefined && (_jsxs("div", { className: styles.error, role: "alert", "data-worktree-target-error": true, children: [_jsx("p", { className: styles.message, "data-error": "true", children: formatWorktreeViewError(readState.targetError.error, t) }), _jsx("button", { type: "button", className: styles.retryButton, onClick: () => {
|
|
1121
|
+
void refresh({
|
|
1122
|
+
scope: {
|
|
1123
|
+
kind: 'workspaces',
|
|
1124
|
+
workspaceIds: readState.targetError.workspaceIds,
|
|
1125
|
+
},
|
|
1126
|
+
preserveCurrent: true,
|
|
1127
|
+
});
|
|
1128
|
+
}, children: t('action.retry') })] })), readState.status === 'loading' && (_jsx("p", { className: styles.message, children: t('status.loading') })), readState.status === 'error' && readState.error !== undefined ? (_jsxs("div", { className: styles.error, role: "alert", "data-worktree-error": true, children: [_jsx("p", { className: styles.message, "data-error": "true", children: formatWorktreeViewError(readState.error, t) }), _jsx("button", { type: "button", className: styles.retryButton, onClick: () => {
|
|
1129
|
+
void refresh({ scope: { kind: 'global' } });
|
|
956
1130
|
}, 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) => {
|
|
957
1131
|
const view = viewByWorkspace.get(workspace.workspaceId);
|
|
958
1132
|
const currentBranch = view?.branches.find((branch) => branch.isCurrent)?.name;
|
|
@@ -1236,14 +1410,16 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
1236
1410
|
mutationToken,
|
|
1237
1411
|
},
|
|
1238
1412
|
}, permission, onPermissionNotice);
|
|
1239
|
-
await invalidateWorktreeContext?.(target.workspaceId);
|
|
1240
1413
|
setWorktreeRemoval(undefined);
|
|
1414
|
+
}, {
|
|
1415
|
+
scope: { kind: 'workspace', workspaceId: target.workspaceId },
|
|
1416
|
+
preserveCurrent: true,
|
|
1241
1417
|
});
|
|
1242
1418
|
} }), _jsx(RiskConfirmation, { open: fullAccessConfirmationSnapshot !== undefined, title: t('permission.fullAccessTitle'), description: fullAccessConfirmationSnapshot === undefined
|
|
1243
1419
|
? ''
|
|
1244
1420
|
: t('permission.fullAccessDescription', {
|
|
1245
1421
|
cwd: fullAccessConfirmationSnapshot.cwd,
|
|
1246
|
-
}), acknowledgeLabel: t('permission.fullAccessAcknowledge'), cancelLabel: t('dialog.cancel'), confirmLabel: t('permission.fullAccessEnable'), acknowledged: fullAccessAcknowledged, onAcknowledgedChange: setFullAccessAcknowledged, onCancel: () => {
|
|
1422
|
+
}), acknowledgeLabel: t('permission.fullAccessAcknowledge'), cancelLabel: t('dialog.cancel'), closeLabel: t('dialog.close'), confirmLabel: t('permission.fullAccessEnable'), acknowledged: fullAccessAcknowledged, onAcknowledgedChange: setFullAccessAcknowledged, onCancel: () => {
|
|
1247
1423
|
setFullAccessAcknowledged(false);
|
|
1248
1424
|
fullAccessConfirmation?.resolve(false);
|
|
1249
1425
|
}, onConfirm: () => {
|