@cerbur/clutch-dsh-worktree 0.1.6 → 0.1.8
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 +135 -23
- package/README.zh.md +107 -13
- package/cordis.patch.yml +21 -0
- package/lib/client/WorktreeContext.d.ts.map +1 -1
- package/lib/client/WorktreeContext.js +2 -2
- package/lib/client/WorktreeContext.js.map +1 -1
- package/lib/client/WorktreeSurface.d.ts +2 -2
- package/lib/client/WorktreeSurface.d.ts.map +1 -1
- package/lib/client/WorktreeSurface.js +291 -70
- package/lib/client/WorktreeSurface.js.map +1 -1
- package/lib/client/entry.d.ts.map +1 -1
- package/lib/client/entry.js +84 -2
- package/lib/client/entry.js.map +1 -1
- package/lib/client/index.d.ts +3 -0
- package/lib/client/index.d.ts.map +1 -1
- package/lib/client/index.js +2 -0
- package/lib/client/index.js.map +1 -1
- package/lib/client/locales.d.ts +70 -0
- package/lib/client/locales.d.ts.map +1 -1
- package/lib/client/locales.js +70 -0
- package/lib/client/locales.js.map +1 -1
- package/lib/client/session-view.d.ts +34 -0
- package/lib/client/session-view.d.ts.map +1 -1
- package/lib/client/session-view.js +119 -1
- package/lib/client/session-view.js.map +1 -1
- package/lib/client/worktree-connection.d.ts +4 -2
- 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.js +1 -1
- package/lib/client/worktree-context.js.map +1 -1
- package/lib/client/worktree-error-copy.d.ts +2 -0
- package/lib/client/worktree-error-copy.d.ts.map +1 -1
- package/lib/client/worktree-error-copy.js +24 -0
- package/lib/client/worktree-error-copy.js.map +1 -1
- package/lib/client/worktree-permission-icon.d.ts +7 -0
- package/lib/client/worktree-permission-icon.d.ts.map +1 -0
- package/lib/client/worktree-permission-icon.js +119 -0
- package/lib/client/worktree-permission-icon.js.map +1 -0
- package/lib/client/worktree-permission.d.ts +21 -0
- package/lib/client/worktree-permission.d.ts.map +1 -0
- package/lib/client/worktree-permission.js +70 -0
- package/lib/client/worktree-permission.js.map +1 -0
- package/lib/client/worktree-session-fork.d.ts +65 -0
- package/lib/client/worktree-session-fork.d.ts.map +1 -0
- package/lib/client/worktree-session-fork.js +174 -0
- package/lib/client/worktree-session-fork.js.map +1 -0
- package/lib/client/worktree-session-order.d.ts +36 -0
- package/lib/client/worktree-session-order.d.ts.map +1 -0
- package/lib/client/worktree-session-order.js +215 -0
- package/lib/client/worktree-session-order.js.map +1 -0
- package/lib/client/worktree-session.d.ts +34 -1
- package/lib/client/worktree-session.d.ts.map +1 -1
- package/lib/client/worktree-session.js +101 -3
- package/lib/client/worktree-session.js.map +1 -1
- package/lib/client/worktree-surface-rows.d.ts +4 -4
- package/lib/client/worktree-surface-rows.d.ts.map +1 -1
- package/lib/client/worktree-surface-rows.js +139 -47
- package/lib/client/worktree-surface-rows.js.map +1 -1
- package/lib/client/worktree-surface-selectors.d.ts +2 -0
- package/lib/client/worktree-surface-selectors.d.ts.map +1 -1
- package/lib/client/worktree-surface-selectors.js +8 -0
- package/lib/client/worktree-surface-selectors.js.map +1 -1
- package/lib/client/worktree-surface-types.d.ts +46 -3
- package/lib/client/worktree-surface-types.d.ts.map +1 -1
- package/lib/client/worktree-view-actions.d.ts +8 -3
- package/lib/client/worktree-view-actions.d.ts.map +1 -1
- package/lib/client/worktree-view-actions.js +28 -2
- package/lib/client/worktree-view-actions.js.map +1 -1
- package/lib/client/worktree-view-errors.d.ts +10 -0
- package/lib/client/worktree-view-errors.d.ts.map +1 -1
- package/lib/client/worktree-view-errors.js +22 -1
- package/lib/client/worktree-view-errors.js.map +1 -1
- package/lib/client.js +1515 -207
- 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 +11 -3
- package/lib/contract/index.d.ts.map +1 -1
- package/lib/contract/index.js +8 -0
- package/lib/contract/index.js.map +1 -1
- package/lib/contract/worktree-permission.d.ts +73 -0
- package/lib/contract/worktree-permission.d.ts.map +1 -0
- package/lib/contract/worktree-permission.js +32 -0
- package/lib/contract/worktree-permission.js.map +1 -0
- package/lib/host/dsh-read-adapter.d.ts +1 -0
- package/lib/host/dsh-read-adapter.d.ts.map +1 -1
- package/lib/host/dsh-read-adapter.js +6 -0
- package/lib/host/dsh-read-adapter.js.map +1 -1
- package/lib/host/index.d.ts +4 -0
- package/lib/host/index.d.ts.map +1 -1
- package/lib/host/index.js +2 -0
- package/lib/host/index.js.map +1 -1
- package/lib/host/remote.d.ts +2 -2
- package/lib/host/remote.d.ts.map +1 -1
- package/lib/host/remote.js +7 -1
- package/lib/host/remote.js.map +1 -1
- package/lib/host/service.d.ts +5 -1
- package/lib/host/service.d.ts.map +1 -1
- package/lib/host/service.js +42 -3
- package/lib/host/service.js.map +1 -1
- package/lib/host/worktree-permission-manager.d.ts +14 -0
- package/lib/host/worktree-permission-manager.d.ts.map +1 -0
- package/lib/host/worktree-permission-manager.js +113 -0
- package/lib/host/worktree-permission-manager.js.map +1 -0
- package/lib/host/worktree-permission.d.ts +38 -0
- package/lib/host/worktree-permission.d.ts.map +1 -0
- package/lib/host/worktree-permission.js +156 -0
- package/lib/host/worktree-permission.js.map +1 -0
- package/lib/index.d.ts +6 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/manage/manager-context.d.ts +2 -0
- package/lib/manage/manager-context.d.ts.map +1 -1
- package/lib/manage/manager-worktrees.d.ts +5 -0
- package/lib/manage/manager-worktrees.d.ts.map +1 -1
- package/lib/manage/manager-worktrees.js +125 -37
- package/lib/manage/manager-worktrees.js.map +1 -1
- package/lib/manage/manager.d.ts +20 -0
- package/lib/manage/manager.d.ts.map +1 -1
- package/lib/manage/manager.js +91 -13
- package/lib/manage/manager.js.map +1 -1
- package/lib/manage/types.d.ts +9 -1
- package/lib/manage/types.d.ts.map +1 -1
- package/lib/manage/worktree-permission.d.ts +3 -0
- package/lib/manage/worktree-permission.d.ts.map +1 -0
- package/lib/manage/worktree-permission.js +3 -0
- package/lib/manage/worktree-permission.js.map +1 -0
- package/lib/provider/git.d.ts +37 -9
- package/lib/provider/git.d.ts.map +1 -1
- package/lib/provider/git.js +162 -61
- package/lib/provider/git.js.map +1 -1
- package/lib/provider/index.d.ts +5 -1
- package/lib/provider/index.d.ts.map +1 -1
- package/lib/provider/index.js +4 -0
- package/lib/provider/index.js.map +1 -1
- package/lib/provider/mutation-lock.d.ts +37 -0
- package/lib/provider/mutation-lock.d.ts.map +1 -0
- package/lib/provider/mutation-lock.js +270 -0
- package/lib/provider/mutation-lock.js.map +1 -0
- package/lib/provider/mutation-token.d.ts +8 -0
- package/lib/provider/mutation-token.d.ts.map +1 -0
- package/lib/provider/mutation-token.js +20 -0
- package/lib/provider/mutation-token.js.map +1 -0
- package/lib/provider/repository-fingerprint.d.ts +8 -0
- package/lib/provider/repository-fingerprint.d.ts.map +1 -0
- package/lib/provider/repository-fingerprint.js +13 -0
- package/lib/provider/repository-fingerprint.js.map +1 -0
- package/lib/provider/sidecar-persistence.d.ts +7 -3
- package/lib/provider/sidecar-persistence.d.ts.map +1 -1
- package/lib/provider/sidecar-persistence.js +118 -44
- package/lib/provider/sidecar-persistence.js.map +1 -1
- package/lib/provider/sidecar-schema.d.ts +1 -1
- package/lib/provider/sidecar-schema.d.ts.map +1 -1
- package/lib/provider/sidecar-schema.js +140 -8
- package/lib/provider/sidecar-schema.js.map +1 -1
- package/lib/provider/sidecar.d.ts +3 -1
- package/lib/provider/sidecar.d.ts.map +1 -1
- package/lib/provider/sidecar.js +6 -1
- package/lib/provider/sidecar.js.map +1 -1
- package/lib/provider/subprocess.d.ts +37 -0
- package/lib/provider/subprocess.d.ts.map +1 -0
- package/lib/provider/subprocess.js +319 -0
- package/lib/provider/subprocess.js.map +1 -0
- package/lib/provider/transaction.d.ts +65 -0
- package/lib/provider/transaction.d.ts.map +1 -0
- package/lib/provider/transaction.js +1011 -0
- package/lib/provider/transaction.js.map +1 -0
- package/lib/provider/types.d.ts +98 -7
- package/lib/provider/types.d.ts.map +1 -1
- package/lib/provider/types.js +1 -1
- package/lib/provider/types.js.map +1 -1
- package/lib/typert.host.js +133 -30
- package/lib/typert.remote-client.d.ts +7 -3
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +133 -30
- package/package.json +3 -1
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, } from 'react';
|
|
3
|
-
import { IconBranchOutline16, IconCloseOutline16,
|
|
3
|
+
import { IconBranchOutline16, IconCloseOutline16, IconProjectAddOutline16, IconSearchOutline16, RiskConfirmation, } 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 { isMainExpanded, isWorkspaceExpanded, isWorktreeExpanded, } from './worktree-expand-state.js';
|
|
7
7
|
import { effectiveViewMode, unboundSessionIds, workspaceSessionIds } from './view-mode.js';
|
|
8
|
-
import { formatWorktreeViewError } from './worktree-error-copy.js';
|
|
9
|
-
import { filterVisibleSessionIds, sessionMatchesQuery, } from './session-view.js';
|
|
8
|
+
import { formatWorktreePermissionNotice, formatWorktreeViewError, } from './worktree-error-copy.js';
|
|
9
|
+
import { deriveSessionPresentationIndex, filterVisibleSessionIds, hasOngoingSession, sessionMatchesQuery, } from './session-view.js';
|
|
10
|
+
import { nextSessionOrderAccount, reorderSessionIds, } from './worktree-session-order.js';
|
|
10
11
|
import { retryWorktreeSessionBinding } from './worktree-session.js';
|
|
11
12
|
import { WorktreeCreateDialog, WorktreeRemovalDialog, WorktreeSessionRenameDialog, WorktreeWorkspaceDeleteDialog, WorktreeWorkspaceRenameDialog, } from './worktree-surface-dialogs.js';
|
|
12
|
-
import { bindingIdsFor, clearSessionGroupExpansion, includesText, isCompleteWorktreeWorkspaceSnapshot, currentSessionRevealKeys, resolveCurrentSessionLocation, workspaceMatches, } from './worktree-surface-selectors.js';
|
|
13
|
+
import { bindingIdsFor, clearSessionGroupExpansion, includesText, isCompleteWorktreeWorkspaceSnapshot, currentSessionRevealKeys, isSessionGroupAutoExpanded, resolveCurrentSessionLocation, workspaceMatches, } from './worktree-surface-selectors.js';
|
|
13
14
|
import { scrollCurrentSessionIntoView } from './worktree-session-position.js';
|
|
14
15
|
import { WorktreeGroupRow, WorktreeSessionGroup, WorktreeWorkspaceRow, } from './worktree-surface-rows.js';
|
|
15
|
-
import { executeWorktreeAction, createDefaultWorktreeName, createWorktreeModalViewLoader, createWorktreeRefreshGuard, filterArchivedSessionIds, loadWorktreeViews, mergeWorktreeView, reconcileBaseBranchSelection, resolveWorktreeMove, selectDefaultBaseBranch, stableWorkspaceIds, toRetryableWorktreeOrderError, toWorktreeViewError, WorktreeSessionBindingError, } from './worktree-view.js';
|
|
16
|
+
import { executeWorktreeAction, createDefaultWorktreeName, createNumberedWorktreeName, createWorktreeModalViewLoader, createWorktreeRefreshGuard, filterArchivedSessionIds, loadWorktreeViews, mergeWorktreeView, reconcileBaseBranchSelection, resolveWorktreeMove, selectDefaultBaseBranch, stableWorkspaceIds, toRetryableWorktreeOrderError, toWorktreeViewError, WorktreeSessionBindingError, WorktreeSessionPermissionError, } from './worktree-view.js';
|
|
16
17
|
import styles from './worktree.css';
|
|
17
18
|
function toNativeWorktreeViewError(error) {
|
|
18
19
|
const viewError = toWorktreeViewError(error);
|
|
@@ -21,6 +22,30 @@ function toNativeWorktreeViewError(error) {
|
|
|
21
22
|
return { ...viewError, message: String(error) };
|
|
22
23
|
}
|
|
23
24
|
const EMPTY_READ_STATE = { status: 'idle', views: [] };
|
|
25
|
+
const EMPTY_PERMISSION_NOTICE = undefined;
|
|
26
|
+
const EMPTY_PERMISSION_NOTICE_SUBSCRIBE = () => () => { };
|
|
27
|
+
const EMPTY_PERMISSION_NOTICE_SNAPSHOT = () => EMPTY_PERMISSION_NOTICE;
|
|
28
|
+
const EMPTY_FULL_ACCESS_CONFIRMATION_SUBSCRIBE = () => () => { };
|
|
29
|
+
const EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT = () => undefined;
|
|
30
|
+
const EMPTY_FORK_RECOVERY_SNAPSHOT = { revision: 0, pending: [] };
|
|
31
|
+
const EMPTY_FORK_RECOVERY_STORE = {
|
|
32
|
+
getSnapshot: () => EMPTY_FORK_RECOVERY_SNAPSHOT,
|
|
33
|
+
subscribe: () => () => { },
|
|
34
|
+
};
|
|
35
|
+
function updatedAtById(sessionIds, sessions) {
|
|
36
|
+
return Object.fromEntries(sessionIds.map((sessionId) => [sessionId, sessions.byId[sessionId]?.updatedAt]));
|
|
37
|
+
}
|
|
38
|
+
function forkRecoveryError(recovery) {
|
|
39
|
+
const error = toNativeWorktreeViewError(recovery.error);
|
|
40
|
+
return {
|
|
41
|
+
...error,
|
|
42
|
+
code: error.code === 'WORKTREE_VIEW_FAILED' ? 'SESSION_BINDING_FAILED' : error.code,
|
|
43
|
+
details: {
|
|
44
|
+
...(error.details ?? {}),
|
|
45
|
+
sessionId: recovery.childSessionId,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
24
49
|
function useStableWorkspaceIds(workspaces) {
|
|
25
50
|
const next = workspaces.map((workspace) => workspace.workspaceId);
|
|
26
51
|
const previousRef = useRef([]);
|
|
@@ -33,15 +58,33 @@ function useStableWorkspaceIds(workspaces) {
|
|
|
33
58
|
* Peer Worktree navigation surface. It keeps the original DSH Workspace mode
|
|
34
59
|
* intact and renders a browser-local Workspace → Worktree → Session projection.
|
|
35
60
|
*/
|
|
36
|
-
export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, expandState, manager, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession, }) {
|
|
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, }) {
|
|
37
62
|
const preferredMode = useStore((state) => state.viewMode);
|
|
38
63
|
const mode = effectiveViewMode(preferredMode, available && manager !== undefined);
|
|
39
64
|
const sessions = useSessions((state) => state);
|
|
40
65
|
const workspaces = useWorkspaces((state) => state);
|
|
66
|
+
const forkRecoveryStore = forkRecovery ?? EMPTY_FORK_RECOVERY_STORE;
|
|
67
|
+
const forkRecoverySnapshot = useSyncExternalStore(forkRecoveryStore.subscribe, forkRecoveryStore.getSnapshot, forkRecoveryStore.getSnapshot);
|
|
41
68
|
const currentSessionId = sessions.current;
|
|
42
69
|
const workspaceIds = useStableWorkspaceIds(workspaces.items);
|
|
43
70
|
const [readState, setReadState] = useState(EMPTY_READ_STATE);
|
|
44
71
|
const expandSnapshot = useSyncExternalStore(expandState.subscribe, expandState.getSnapshot, expandState.getSnapshot);
|
|
72
|
+
const permissionNoticeSnapshot = useSyncExternalStore(permissionNotice?.subscribe ?? EMPTY_PERMISSION_NOTICE_SUBSCRIBE, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT);
|
|
73
|
+
const fullAccessConfirmationSnapshot = useSyncExternalStore(fullAccessConfirmation?.subscribe ?? EMPTY_FULL_ACCESS_CONFIRMATION_SUBSCRIBE, fullAccessConfirmation?.getSnapshot ?? EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT, fullAccessConfirmation?.getSnapshot ?? EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT);
|
|
74
|
+
const fullAccessConfirmationKey = fullAccessConfirmationSnapshot === undefined
|
|
75
|
+
? ''
|
|
76
|
+
: [
|
|
77
|
+
fullAccessConfirmationSnapshot.workspaceId,
|
|
78
|
+
fullAccessConfirmationSnapshot.worktreeId,
|
|
79
|
+
fullAccessConfirmationSnapshot.sessionId,
|
|
80
|
+
fullAccessConfirmationSnapshot.cwd,
|
|
81
|
+
].join('\u0000');
|
|
82
|
+
const [fullAccessAcknowledged, setFullAccessAcknowledged] = useState(false);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
setFullAccessAcknowledged(false);
|
|
85
|
+
}, [fullAccessConfirmationKey]);
|
|
86
|
+
const sessionOrderSnapshot = useSyncExternalStore(sessionOrder.subscribe, sessionOrder.getSnapshot, sessionOrder.getSnapshot);
|
|
87
|
+
const sessionPresentations = useMemo(() => deriveSessionPresentationIndex(sessions), [sessions.byId]);
|
|
45
88
|
const readStateRef = useRef(readState);
|
|
46
89
|
readStateRef.current = readState;
|
|
47
90
|
const [searchQuery, setSearchQuery] = useState('');
|
|
@@ -54,9 +97,13 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
54
97
|
const [modalReadError, setModalReadError] = useState();
|
|
55
98
|
const [modalReadLoading, setModalReadLoading] = useState(false);
|
|
56
99
|
const [worktreeModalMode, setWorktreeModalMode] = useState('create');
|
|
57
|
-
const [importCandidates, setImportCandidates] = useState({
|
|
100
|
+
const [importCandidates, setImportCandidates] = useState({
|
|
101
|
+
status: 'idle',
|
|
102
|
+
candidates: [],
|
|
103
|
+
});
|
|
58
104
|
const [selectedImportPath, setSelectedImportPath] = useState();
|
|
59
105
|
const [openWorkspaceMenuId, setOpenWorkspaceMenuId] = useState();
|
|
106
|
+
const [openMainMenuId, setOpenMainMenuId] = useState();
|
|
60
107
|
const [openWorktreeMenuId, setOpenWorktreeMenuId] = useState();
|
|
61
108
|
const [worktreeRemoval, setWorktreeRemoval] = useState();
|
|
62
109
|
const [selectedBranch, setSelectedBranch] = useState('');
|
|
@@ -64,6 +111,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
64
111
|
const [pendingSessionBinding, setPendingSessionBinding] = useState();
|
|
65
112
|
const [actionError, setActionError] = useState();
|
|
66
113
|
const [actionPending, setActionPending] = useState(false);
|
|
114
|
+
const [forkRetryKey, setForkRetryKey] = useState();
|
|
67
115
|
const [sessionRenameTarget, setSessionRenameTarget] = useState();
|
|
68
116
|
const [sessionRenameDraft, setSessionRenameDraft] = useState('');
|
|
69
117
|
const [sessionRenamePending, setSessionRenamePending] = useState(false);
|
|
@@ -83,6 +131,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
83
131
|
const [worktreeDrag, setWorktreeDrag] = useState();
|
|
84
132
|
const worktreeDropCommitted = useRef(false);
|
|
85
133
|
const refreshGuard = useRef(createWorktreeRefreshGuard());
|
|
134
|
+
const forkRecoveryRevisionRef = useRef(forkRecoverySnapshot.revision);
|
|
86
135
|
const modalReadLoader = useRef(createWorktreeModalViewLoader());
|
|
87
136
|
const modalReadViewRef = useRef();
|
|
88
137
|
const importCandidatesGuard = useRef(createWorktreeRefreshGuard());
|
|
@@ -119,9 +168,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
119
168
|
throw error;
|
|
120
169
|
setReadState({
|
|
121
170
|
status: 'error',
|
|
122
|
-
views: modalReadViewRef.current === undefined
|
|
123
|
-
? []
|
|
124
|
-
: [modalReadViewRef.current],
|
|
171
|
+
views: modalReadViewRef.current === undefined ? [] : [modalReadViewRef.current],
|
|
125
172
|
error: toWorktreeViewError(error),
|
|
126
173
|
});
|
|
127
174
|
});
|
|
@@ -164,6 +211,19 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
164
211
|
setReadState(EMPTY_READ_STATE);
|
|
165
212
|
}
|
|
166
213
|
}, [mode, refresh]);
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
if (forkRecoveryRevisionRef.current === forkRecoverySnapshot.revision)
|
|
216
|
+
return;
|
|
217
|
+
forkRecoveryRevisionRef.current = forkRecoverySnapshot.revision;
|
|
218
|
+
if (mode !== 'worktree' || manager === undefined)
|
|
219
|
+
return;
|
|
220
|
+
void refresh({
|
|
221
|
+
preserveCurrent: readStateRef.current.status === 'ready',
|
|
222
|
+
invalidateContext: false,
|
|
223
|
+
}).catch(() => {
|
|
224
|
+
// Recovery state remains visible; the next explicit retry can refresh it again.
|
|
225
|
+
});
|
|
226
|
+
}, [forkRecoverySnapshot.revision, manager, mode, refresh]);
|
|
167
227
|
useEffect(() => {
|
|
168
228
|
if (readState.status !== 'ready' || syncSessionWorkspaces === undefined)
|
|
169
229
|
return;
|
|
@@ -173,6 +233,60 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
173
233
|
}))));
|
|
174
234
|
}, [readState.status, readState.views, syncSessionWorkspaces]);
|
|
175
235
|
const viewByWorkspace = useMemo(() => new Map(readState.views.map((view) => [view.workspaceId, view])), [readState.views]);
|
|
236
|
+
const sessionOrderInputs = useMemo(() => {
|
|
237
|
+
if (readState.status !== 'ready')
|
|
238
|
+
return [];
|
|
239
|
+
const inputs = [];
|
|
240
|
+
for (const workspace of workspaces.items) {
|
|
241
|
+
const view = viewByWorkspace.get(workspace.workspaceId);
|
|
242
|
+
const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
|
|
243
|
+
const bindings = view?.bindings ?? [];
|
|
244
|
+
const boundSessionIds = new Set(bindings.map((binding) => binding.sessionId));
|
|
245
|
+
const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...boundSessionIds]), sessions);
|
|
246
|
+
inputs.push({
|
|
247
|
+
accountKey: `main:${workspace.workspaceId}`,
|
|
248
|
+
sessionIds: mainSessionIds,
|
|
249
|
+
updatedAtById: updatedAtById(mainSessionIds, sessions),
|
|
250
|
+
});
|
|
251
|
+
for (const record of view?.worktrees ?? []) {
|
|
252
|
+
const worktreeSessionIds = filterVisibleSessionIds(filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds), sessions);
|
|
253
|
+
inputs.push({
|
|
254
|
+
accountKey: `worktree:${record.worktreeId}`,
|
|
255
|
+
sessionIds: worktreeSessionIds,
|
|
256
|
+
updatedAtById: updatedAtById(worktreeSessionIds, sessions),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return inputs;
|
|
261
|
+
}, [
|
|
262
|
+
archivedSessionIds,
|
|
263
|
+
readState.status,
|
|
264
|
+
sessions.byId,
|
|
265
|
+
sessions.ids,
|
|
266
|
+
viewByWorkspace,
|
|
267
|
+
workspaces,
|
|
268
|
+
workspaces.items,
|
|
269
|
+
]);
|
|
270
|
+
const orderedSessionIdsByAccount = useMemo(() => {
|
|
271
|
+
const ordered = new Map();
|
|
272
|
+
for (const input of sessionOrderInputs) {
|
|
273
|
+
const previous = sessionOrderSnapshot.accounts[input.accountKey];
|
|
274
|
+
ordered.set(input.accountKey, nextSessionOrderAccount({
|
|
275
|
+
baseIds: input.sessionIds,
|
|
276
|
+
updatedAtById: input.updatedAtById,
|
|
277
|
+
previous,
|
|
278
|
+
}).order);
|
|
279
|
+
}
|
|
280
|
+
return ordered;
|
|
281
|
+
}, [sessionOrderInputs, sessionOrderSnapshot]);
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
if (mode !== 'worktree' || readState.status !== 'ready')
|
|
284
|
+
return;
|
|
285
|
+
for (const input of sessionOrderInputs) {
|
|
286
|
+
sessionOrder.actions.reconcile(input.accountKey, input.sessionIds, input.updatedAtById);
|
|
287
|
+
}
|
|
288
|
+
sessionOrder.actions.retain(sessionOrderInputs.map((input) => input.accountKey));
|
|
289
|
+
}, [mode, readState.status, sessionOrder, sessionOrderInputs]);
|
|
176
290
|
const currentSessionLocation = useMemo(() => readState.status === 'ready'
|
|
177
291
|
? resolveCurrentSessionLocation(currentSessionId, workspaces.items, readState.views)
|
|
178
292
|
: undefined, [currentSessionId, readState.status, readState.views, workspaces.items]);
|
|
@@ -238,11 +352,11 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
238
352
|
const modalSetupStatus = modalView === undefined
|
|
239
353
|
? undefined
|
|
240
354
|
: modalReadiness?.status === 'ready'
|
|
241
|
-
? modalView.branches.length === 0
|
|
355
|
+
? modalView.branches.length === 0
|
|
356
|
+
? 'noLocalBranch'
|
|
357
|
+
: undefined
|
|
242
358
|
: modalReadiness?.status;
|
|
243
|
-
const modalCanCreate = !modalReadLoading &&
|
|
244
|
-
modalSetupStatus === undefined &&
|
|
245
|
-
modalReadiness?.status === 'ready';
|
|
359
|
+
const modalCanCreate = !modalReadLoading && modalSetupStatus === undefined && modalReadiness?.status === 'ready';
|
|
246
360
|
useEffect(() => {
|
|
247
361
|
if (worktreeModalWorkspaceId === undefined || modalView === undefined)
|
|
248
362
|
return;
|
|
@@ -388,9 +502,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
388
502
|
const sourceIndex = rows.findIndex((workspace) => workspace.workspaceId === activeDrag.workspaceId);
|
|
389
503
|
if (targetIndex === -1 || sourceIndex === -1)
|
|
390
504
|
return;
|
|
391
|
-
const beforeWorkspaceId = over.half === 'before'
|
|
392
|
-
? over.workspaceId
|
|
393
|
-
: rows[targetIndex + 1]?.workspaceId;
|
|
505
|
+
const beforeWorkspaceId = over.half === 'before' ? over.workspaceId : rows[targetIndex + 1]?.workspaceId;
|
|
394
506
|
const anchorIndex = beforeWorkspaceId === undefined
|
|
395
507
|
? rows.length
|
|
396
508
|
: rows.findIndex((workspace) => workspace.workspaceId === beforeWorkspaceId);
|
|
@@ -421,12 +533,8 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
421
533
|
const sourceIndex = sessionIds.indexOf(activeDrag.sessionId);
|
|
422
534
|
if (targetIndex === -1 || sourceIndex === -1)
|
|
423
535
|
return;
|
|
424
|
-
const beforeSessionId = over.half === 'before'
|
|
425
|
-
|
|
426
|
-
: sessionIds[targetIndex + 1];
|
|
427
|
-
const anchorIndex = beforeSessionId === undefined
|
|
428
|
-
? sessionIds.length
|
|
429
|
-
: sessionIds.indexOf(beforeSessionId);
|
|
536
|
+
const beforeSessionId = over.half === 'before' ? over.sessionId : sessionIds[targetIndex + 1];
|
|
537
|
+
const anchorIndex = beforeSessionId === undefined ? sessionIds.length : sessionIds.indexOf(beforeSessionId);
|
|
430
538
|
if (beforeSessionId === activeDrag.sessionId ||
|
|
431
539
|
anchorIndex === sourceIndex ||
|
|
432
540
|
anchorIndex === sourceIndex + 1) {
|
|
@@ -441,7 +549,12 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
441
549
|
return;
|
|
442
550
|
}
|
|
443
551
|
setActionError(undefined);
|
|
444
|
-
|
|
552
|
+
const currentOrder = sessionOrderSnapshot.accounts[activeDrag.groupKey]?.order ?? sessionIds;
|
|
553
|
+
void insertSessionBefore(workspaceId, activeDrag.sessionId, beforeSessionId)
|
|
554
|
+
.then(() => {
|
|
555
|
+
sessionOrder.actions.setOrder(activeDrag.groupKey, reorderSessionIds(currentOrder, activeDrag.sessionId, over.sessionId, over.half));
|
|
556
|
+
})
|
|
557
|
+
.catch((error) => {
|
|
445
558
|
setActionError(toWorktreeViewError(error));
|
|
446
559
|
});
|
|
447
560
|
};
|
|
@@ -582,8 +695,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
582
695
|
if (visuallyExpanded)
|
|
583
696
|
clearSessionGroups(['worktree:' + worktreeId]);
|
|
584
697
|
};
|
|
585
|
-
const toggleSessionGroup = (groupKey) => {
|
|
586
|
-
const autoExpanded = isCurrentSessionReveal('session-group:' + groupKey);
|
|
698
|
+
const toggleSessionGroup = (groupKey, autoExpanded) => {
|
|
587
699
|
const transientExpanded = expandedSessionGroups[groupKey] === true;
|
|
588
700
|
if (autoExpanded) {
|
|
589
701
|
suppressCurrentSessionReveal('session-group:' + groupKey);
|
|
@@ -609,7 +721,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
609
721
|
setModalReadError(undefined);
|
|
610
722
|
setModalReadLoading(false);
|
|
611
723
|
};
|
|
612
|
-
const openWorktreeCreator = (workspace) => {
|
|
724
|
+
const openWorktreeCreator = (workspace, defaults = {}) => {
|
|
613
725
|
const view = viewByWorkspace.get(workspace.workspaceId);
|
|
614
726
|
modalReadLoader.current.invalidate();
|
|
615
727
|
modalReadViewRef.current = undefined;
|
|
@@ -622,13 +734,13 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
622
734
|
setModalReadError(undefined);
|
|
623
735
|
setModalReadLoading(false);
|
|
624
736
|
if (view === undefined) {
|
|
625
|
-
setSelectedBranch('');
|
|
626
|
-
setNewBranch('');
|
|
737
|
+
setSelectedBranch(defaults.baseBranch ?? '');
|
|
738
|
+
setNewBranch(defaults.newBranch ?? '');
|
|
627
739
|
loadModalWorktreeView(workspace.workspaceId);
|
|
628
740
|
return;
|
|
629
741
|
}
|
|
630
|
-
setSelectedBranch(selectDefaultBaseBranch(view.branches));
|
|
631
|
-
setNewBranch(createDefaultWorktreeName([
|
|
742
|
+
setSelectedBranch(defaults.baseBranch ?? selectDefaultBaseBranch(view.branches));
|
|
743
|
+
setNewBranch(defaults.newBranch ?? createDefaultWorktreeName([
|
|
632
744
|
...view.branches.map((branch) => branch.name),
|
|
633
745
|
...view.worktrees.map((worktree) => worktree.branch),
|
|
634
746
|
]));
|
|
@@ -665,6 +777,13 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
665
777
|
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
666
778
|
setPendingSessionBinding({ ...sessionInput, sessionId: error.sessionId });
|
|
667
779
|
}
|
|
780
|
+
if (error instanceof WorktreeSessionPermissionError && error.retryable) {
|
|
781
|
+
setPendingSessionBinding({
|
|
782
|
+
...sessionInput,
|
|
783
|
+
sessionId: error.sessionId,
|
|
784
|
+
permissionRequired: true,
|
|
785
|
+
});
|
|
786
|
+
}
|
|
668
787
|
throw error;
|
|
669
788
|
}
|
|
670
789
|
await refresh({ preserveCurrent: true });
|
|
@@ -736,6 +855,13 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
736
855
|
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
737
856
|
setPendingSessionBinding({ ...input, sessionId: error.sessionId });
|
|
738
857
|
}
|
|
858
|
+
if (error instanceof WorktreeSessionPermissionError && error.retryable) {
|
|
859
|
+
setPendingSessionBinding({
|
|
860
|
+
...input,
|
|
861
|
+
sessionId: error.sessionId,
|
|
862
|
+
permissionRequired: true,
|
|
863
|
+
});
|
|
864
|
+
}
|
|
739
865
|
setActionError(toWorktreeViewError(error));
|
|
740
866
|
}
|
|
741
867
|
finally {
|
|
@@ -743,9 +869,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
743
869
|
}
|
|
744
870
|
};
|
|
745
871
|
const retrySessionBinding = async () => {
|
|
746
|
-
if (manager === undefined ||
|
|
747
|
-
pendingSessionBinding === undefined ||
|
|
748
|
-
pendingSessionArchived)
|
|
872
|
+
if (manager === undefined || pendingSessionBinding === undefined || pendingSessionArchived)
|
|
749
873
|
return;
|
|
750
874
|
const pending = pendingSessionBinding;
|
|
751
875
|
setActionPending(true);
|
|
@@ -758,6 +882,9 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
758
882
|
ensureSessionWorkspace: (workspaceId, sessionId) => {
|
|
759
883
|
ensureSessionWorkspace?.(workspaceId, sessionId);
|
|
760
884
|
},
|
|
885
|
+
permission,
|
|
886
|
+
confirmFullAccess,
|
|
887
|
+
onPermissionResult,
|
|
761
888
|
openSession,
|
|
762
889
|
});
|
|
763
890
|
setPendingSessionBinding(undefined);
|
|
@@ -774,6 +901,20 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
774
901
|
setActionPending(false);
|
|
775
902
|
}
|
|
776
903
|
};
|
|
904
|
+
const retryForkBinding = async (key) => {
|
|
905
|
+
if (retryForkSession === undefined || forkRetryKey !== undefined)
|
|
906
|
+
return;
|
|
907
|
+
setForkRetryKey(key);
|
|
908
|
+
try {
|
|
909
|
+
await retryForkSession(key);
|
|
910
|
+
}
|
|
911
|
+
catch (error) {
|
|
912
|
+
setActionError(toNativeWorktreeViewError(error));
|
|
913
|
+
}
|
|
914
|
+
finally {
|
|
915
|
+
setForkRetryKey(undefined);
|
|
916
|
+
}
|
|
917
|
+
};
|
|
777
918
|
const openWorkspaceSession = (workspaceId, sessionId) => {
|
|
778
919
|
ensureSessionWorkspace?.(workspaceId, sessionId);
|
|
779
920
|
openWorktreeSession({ open: openSession }, sessionId);
|
|
@@ -793,14 +934,21 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
793
934
|
} }), _jsx("button", { type: "button", className: styles.iconButton, "aria-label": t('workspace.add'), onClick: () => {
|
|
794
935
|
if (createWorkspace !== undefined)
|
|
795
936
|
void runMutation(createWorkspace);
|
|
796
|
-
}, children: _jsx(
|
|
937
|
+
}, children: _jsx(IconProjectAddOutline16, {}) })] }), _jsxs("div", { className: styles.content, tabIndex: 0, children: [permissionNoticeSnapshot !== undefined && (_jsx("div", { className: styles.notice, role: "status", "data-worktree-permission-notice": true, children: _jsx("p", { className: styles.message, children: formatWorktreePermissionNotice(permissionNoticeSnapshot.result, t) }) })), forkRecoverySnapshot.pending.map((recovery) => {
|
|
938
|
+
const recoveryViewError = forkRecoveryError(recovery);
|
|
939
|
+
return (_jsxs("div", { className: styles.error, role: "alert", "data-fork-recovery": recovery.key, children: [_jsx("p", { className: styles.message, "data-error": "true", children: formatWorktreeViewError(recoveryViewError, t) }), _jsxs("div", { className: styles.recoveryActions, children: [retryForkSession !== undefined && recoveryViewError.retryable && (_jsx("button", { type: "button", className: styles.actionButton, disabled: forkRetryKey !== undefined, onClick: () => {
|
|
940
|
+
void retryForkBinding(recovery.key);
|
|
941
|
+
}, children: t('action.retryBinding') })), _jsx("button", { type: "button", className: styles.actionButton, disabled: forkRetryKey !== undefined, onClick: () => {
|
|
942
|
+
openSession(recovery.childSessionId);
|
|
943
|
+
}, children: t('action.openCreatedSession') })] })] }, recovery.key));
|
|
944
|
+
}), 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: () => {
|
|
797
945
|
void retrySessionBinding();
|
|
798
|
-
}, children: t('action.retryBinding') }), _jsx("button", { type: "button", className: styles.actionButton, disabled: actionPending, onClick: () => {
|
|
946
|
+
}, children: t('action.retryBinding') }), !pendingSessionBinding.permissionRequired && (_jsx("button", { type: "button", className: styles.actionButton, disabled: actionPending, onClick: () => {
|
|
799
947
|
const sessionId = pendingSessionBinding.sessionId;
|
|
800
948
|
setPendingSessionBinding(undefined);
|
|
801
949
|
setActionError(undefined);
|
|
802
950
|
openSession(sessionId);
|
|
803
|
-
}, children: t('action.openCreatedSession') })] })), actionError.retryable && (_jsx("button", { type: "button", className: styles.retryButton, onClick: () => {
|
|
951
|
+
}, children: t('action.openCreatedSession') }))] })), actionError.retryable && (_jsx("button", { type: "button", className: styles.retryButton, onClick: () => {
|
|
804
952
|
setActionError(undefined);
|
|
805
953
|
void refresh();
|
|
806
954
|
}, 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: () => {
|
|
@@ -815,20 +963,30 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
815
963
|
isCurrentSessionReveal('workspace:' + workspace.workspaceId);
|
|
816
964
|
const workspaceMatchesQuery = includesText(workspace.title, query);
|
|
817
965
|
const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
|
|
966
|
+
const workspaceActivitySessionIds = filterVisibleSessionIds(allWorkspaceSessionIds, sessions);
|
|
967
|
+
const workspaceHasOngoingSession = hasOngoingSession(workspaceActivitySessionIds, sessionPresentations);
|
|
818
968
|
const bindings = view?.bindings ?? [];
|
|
819
969
|
const boundSessionIds = new Set(bindings.map((binding) => binding.sessionId));
|
|
820
970
|
const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...boundSessionIds]), sessions);
|
|
971
|
+
const mainGroupKey = `main:${workspace.workspaceId}`;
|
|
972
|
+
const orderedMainSessionIds = orderedSessionIdsByAccount.get(mainGroupKey) ?? mainSessionIds;
|
|
821
973
|
const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
974
|
+
const orderedVisibleMainSessionIds = orderedMainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
822
975
|
const mainExpanded = isMainExpanded(expandSnapshot, workspace.workspaceId) ||
|
|
823
976
|
isCurrentSessionReveal('main:' + workspace.workspaceId);
|
|
824
|
-
const
|
|
825
|
-
|
|
977
|
+
const sessionIds = orderedVisibleMainSessionIds.length === visibleMainSessionIds.length
|
|
978
|
+
? orderedVisibleMainSessionIds
|
|
979
|
+
: visibleMainSessionIds;
|
|
980
|
+
const mainSessionGroupAutoExpanded = isSessionGroupAutoExpanded(sessionIds, currentSessionId, isCurrentSessionReveal('session-group:' + mainGroupKey));
|
|
826
981
|
const mainSessionGroupExpanded = expandedSessionGroups[mainGroupKey] === true ||
|
|
827
|
-
|
|
828
|
-
isCurrentSessionReveal('session-group:' + mainGroupKey));
|
|
982
|
+
mainSessionGroupAutoExpanded;
|
|
829
983
|
const worktrees = view?.worktrees ?? [];
|
|
984
|
+
const workspaceWorktreeNames = [
|
|
985
|
+
...(view?.branches.map((branch) => branch.name) ?? []),
|
|
986
|
+
...(view?.worktrees.map((worktree) => worktree.branch) ?? []),
|
|
987
|
+
];
|
|
830
988
|
const sameWorkspaceWorktreeDrag = worktreeDrag?.workspaceId === workspace.workspaceId;
|
|
831
|
-
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: {
|
|
989
|
+
return (_jsxs("section", { className: styles.workspaceGroup, "data-workspace-id": workspace.workspaceId, children: [_jsx(WorktreeWorkspaceRow, { t: t, workspace: workspace, expanded: expanded, hasOngoingSession: workspaceHasOngoingSession, actionPending: actionPending, menuOpen: openWorkspaceMenuId === workspace.workspaceId, drag: {
|
|
832
990
|
active: workspaceDrag !== undefined,
|
|
833
991
|
marker: workspaceDrag?.over?.workspaceId === workspace.workspaceId
|
|
834
992
|
? workspaceDrag.over.half
|
|
@@ -872,14 +1030,32 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
872
1030
|
openWorkspaceDelete(workspace);
|
|
873
1031
|
}, onMenuOpenChange: (open) => {
|
|
874
1032
|
setOpenWorkspaceMenuId(open ? workspace.workspaceId : undefined);
|
|
875
|
-
} }), expanded && (_jsxs("div", { className: styles.treeChildren, children: [_jsx(WorktreeGroupRow, { t: t, kind: "main", label: mainLabel, expanded: mainExpanded, icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, onToggle: () => {
|
|
1033
|
+
} }), expanded && (_jsxs("div", { className: styles.treeChildren, children: [_jsx(WorktreeGroupRow, { t: t, kind: "main", label: mainLabel, expanded: mainExpanded, hasOngoingSession: hasOngoingSession(mainSessionIds, sessionPresentations), icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, onToggle: () => {
|
|
876
1034
|
toggleMain(workspace.workspaceId);
|
|
1035
|
+
}, menu: {
|
|
1036
|
+
open: openMainMenuId === workspace.workspaceId,
|
|
1037
|
+
label: mainLabel,
|
|
1038
|
+
copyPath: workspace.path,
|
|
1039
|
+
showCreate: currentBranch !== undefined,
|
|
1040
|
+
showRemove: false,
|
|
1041
|
+
disabled: actionPending,
|
|
1042
|
+
onOpenChange: (open) => {
|
|
1043
|
+
setOpenMainMenuId(open ? workspace.workspaceId : undefined);
|
|
1044
|
+
},
|
|
1045
|
+
onCreateWorktree: currentBranch === undefined
|
|
1046
|
+
? undefined
|
|
1047
|
+
: () => {
|
|
1048
|
+
openWorktreeCreator(workspace, {
|
|
1049
|
+
baseBranch: currentBranch,
|
|
1050
|
+
newBranch: createNumberedWorktreeName(currentBranch, workspaceWorktreeNames),
|
|
1051
|
+
});
|
|
1052
|
+
},
|
|
877
1053
|
}, onCreateSession: createMainSession === undefined
|
|
878
1054
|
? undefined
|
|
879
1055
|
: () => {
|
|
880
1056
|
createMainSession(workspace.workspaceId);
|
|
881
|
-
} }), mainExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: mainGroupKey, sessionIds: sessionIds, workspaceId: workspace.workspaceId, currentSessionId: currentSessionId, expanded: mainSessionGroupExpanded, actionPending: actionPending, sessions: sessions, dragState: sessionDrag, onToggleExpanded: () => {
|
|
882
|
-
toggleSessionGroup(mainGroupKey);
|
|
1057
|
+
} }), mainExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: mainGroupKey, sessionIds: sessionIds, workspaceId: workspace.workspaceId, currentSessionId: currentSessionId, expanded: mainSessionGroupExpanded, actionPending: actionPending, sessions: sessions, sessionPresentations: sessionPresentations, dragState: sessionDrag, onToggleExpanded: () => {
|
|
1058
|
+
toggleSessionGroup(mainGroupKey, mainSessionGroupAutoExpanded);
|
|
883
1059
|
}, onStartDrag: (groupKey, sessionId) => {
|
|
884
1060
|
sessionDropCommitted.current = false;
|
|
885
1061
|
setSessionDrag({ groupKey, sessionId, over: null });
|
|
@@ -903,27 +1079,37 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
903
1079
|
!worktreeSessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) {
|
|
904
1080
|
return null;
|
|
905
1081
|
}
|
|
906
|
-
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery ||
|
|
1082
|
+
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery ||
|
|
1083
|
+
sessionMatchesQuery(sessionId, sessions, query));
|
|
907
1084
|
const worktreeGroupKey = `worktree:${record.worktreeId}`;
|
|
908
|
-
const
|
|
1085
|
+
const orderedWorktreeSessionIds = orderedSessionIdsByAccount.get(worktreeGroupKey) ??
|
|
1086
|
+
worktreeSessionIds;
|
|
1087
|
+
const orderedVisibleWorktreeSessionIds = orderedWorktreeSessionIds.filter((sessionId) => worktreeMatchesQuery ||
|
|
1088
|
+
sessionMatchesQuery(sessionId, sessions, query));
|
|
1089
|
+
const sessionIds = orderedVisibleWorktreeSessionIds.length ===
|
|
1090
|
+
visibleWorktreeSessionIds.length
|
|
1091
|
+
? orderedVisibleWorktreeSessionIds
|
|
1092
|
+
: visibleWorktreeSessionIds;
|
|
1093
|
+
const sessionGroupAutoExpanded = isSessionGroupAutoExpanded(sessionIds, currentSessionId, isCurrentSessionReveal('session-group:' + worktreeGroupKey));
|
|
909
1094
|
const sessionGroupExpanded = expandedSessionGroups[worktreeGroupKey] === true ||
|
|
910
|
-
|
|
911
|
-
isCurrentSessionReveal('session-group:' + worktreeGroupKey));
|
|
1095
|
+
sessionGroupAutoExpanded;
|
|
912
1096
|
const state = record.status === 'removed'
|
|
913
1097
|
? 'warning'
|
|
914
|
-
: record.health === 'repair'
|
|
1098
|
+
: record.health === 'repair' || record.health === 'recovery-needed'
|
|
915
1099
|
? 'error'
|
|
916
1100
|
: 'done';
|
|
917
1101
|
const stateLabel = record.status === 'removed'
|
|
918
1102
|
? t('worktree.detached')
|
|
919
|
-
: record.health === '
|
|
920
|
-
? t('worktree.
|
|
921
|
-
:
|
|
1103
|
+
: record.health === 'recovery-needed'
|
|
1104
|
+
? t('worktree.recovery')
|
|
1105
|
+
: record.health === 'repair'
|
|
1106
|
+
? t('worktree.repair')
|
|
1107
|
+
: t('worktree.ready');
|
|
922
1108
|
const worktreeExpanded = isWorktreeExpanded(expandSnapshot, record.worktreeId) ||
|
|
923
1109
|
isCurrentSessionReveal('worktree:' + record.worktreeId);
|
|
924
|
-
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: () => {
|
|
1110
|
+
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, hasOngoingSession: hasOngoingSession(worktreeSessionIds, sessionPresentations), icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, state: state, stateLabel: stateLabel, onToggle: () => {
|
|
925
1111
|
toggleWorktree(record.worktreeId);
|
|
926
|
-
}, onCreateSession: record.status === 'active' && record.health !== 'repair'
|
|
1112
|
+
}, onCreateSession: record.status === 'active' && record.health !== 'repair' && record.health !== 'recovery-needed'
|
|
927
1113
|
? () => {
|
|
928
1114
|
void createSession({
|
|
929
1115
|
workspaceId: record.workspaceId,
|
|
@@ -931,20 +1117,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
931
1117
|
cwd: record.absolutePath,
|
|
932
1118
|
});
|
|
933
1119
|
}
|
|
934
|
-
: undefined, menu:
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1120
|
+
: undefined, menu: {
|
|
1121
|
+
open: openWorktreeMenuId === record.worktreeId,
|
|
1122
|
+
label: record.branch,
|
|
1123
|
+
copyPath: record.absolutePath,
|
|
1124
|
+
showCreate: record.status === 'active' && record.health !== 'repair' && record.health !== 'recovery-needed',
|
|
1125
|
+
showRemove: record.status === 'active' && record.health !== 'repair' && record.health !== 'recovery-needed',
|
|
1126
|
+
disabled: actionPending,
|
|
1127
|
+
onOpenChange: (open) => {
|
|
1128
|
+
setOpenWorktreeMenuId(open ? record.worktreeId : undefined);
|
|
1129
|
+
},
|
|
1130
|
+
onCreateWorktree: record.status === 'active' && record.health !== 'repair' && record.health !== 'recovery-needed'
|
|
1131
|
+
? () => {
|
|
1132
|
+
openWorktreeCreator(workspace, {
|
|
1133
|
+
baseBranch: record.branch,
|
|
1134
|
+
newBranch: createNumberedWorktreeName(record.branch, workspaceWorktreeNames),
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
: undefined,
|
|
1138
|
+
onRemove: record.status === 'active'
|
|
1139
|
+
? () => {
|
|
943
1140
|
setWorktreeRemoval(record);
|
|
944
1141
|
setActionError(undefined);
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
|
|
1142
|
+
}
|
|
1143
|
+
: undefined,
|
|
1144
|
+
}, drag: {
|
|
948
1145
|
active: sameWorkspaceWorktreeDrag,
|
|
949
1146
|
marker: worktreeDrag?.over?.worktreeId === record.worktreeId
|
|
950
1147
|
? worktreeDrag.over.half
|
|
@@ -975,8 +1172,8 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
975
1172
|
setWorktreeDrag(undefined);
|
|
976
1173
|
worktreeDropCommitted.current = false;
|
|
977
1174
|
},
|
|
978
|
-
} }), worktreeExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: worktreeGroupKey, sessionIds: sessionIds, workspaceId: workspace.workspaceId, currentSessionId: currentSessionId, expanded: sessionGroupExpanded, actionPending: actionPending, sessions: sessions, dragState: sessionDrag, onToggleExpanded: () => {
|
|
979
|
-
toggleSessionGroup(worktreeGroupKey);
|
|
1175
|
+
} }), worktreeExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: worktreeGroupKey, sessionIds: sessionIds, workspaceId: workspace.workspaceId, currentSessionId: currentSessionId, expanded: sessionGroupExpanded, actionPending: actionPending, sessions: sessions, sessionPresentations: sessionPresentations, dragState: sessionDrag, onToggleExpanded: () => {
|
|
1176
|
+
toggleSessionGroup(worktreeGroupKey, sessionGroupAutoExpanded);
|
|
980
1177
|
}, onStartDrag: (groupKey, sessionId) => {
|
|
981
1178
|
sessionDropCommitted.current = false;
|
|
982
1179
|
setSessionDrag({ groupKey, sessionId, over: null });
|
|
@@ -1017,17 +1214,41 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
1017
1214
|
if (manager === undefined || worktreeRemoval === undefined)
|
|
1018
1215
|
return;
|
|
1019
1216
|
const target = worktreeRemoval;
|
|
1217
|
+
const mutationToken = target.mutationToken;
|
|
1218
|
+
if (mutationToken === undefined) {
|
|
1219
|
+
setActionError({
|
|
1220
|
+
code: 'WORKTREE_STATE_CONFLICT',
|
|
1221
|
+
message: '',
|
|
1222
|
+
retryable: true,
|
|
1223
|
+
details: {
|
|
1224
|
+
workspaceId: target.workspaceId,
|
|
1225
|
+
worktreeId: target.worktreeId,
|
|
1226
|
+
},
|
|
1227
|
+
});
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1020
1230
|
void runMutation(async () => {
|
|
1021
1231
|
await executeWorktreeAction(manager, {
|
|
1022
1232
|
type: 'removeWorktree',
|
|
1023
1233
|
input: {
|
|
1024
1234
|
workspaceId: target.workspaceId,
|
|
1025
1235
|
worktreeId: target.worktreeId,
|
|
1236
|
+
mutationToken,
|
|
1026
1237
|
},
|
|
1027
|
-
});
|
|
1238
|
+
}, permission, onPermissionNotice);
|
|
1028
1239
|
await invalidateWorktreeContext?.(target.workspaceId);
|
|
1029
1240
|
setWorktreeRemoval(undefined);
|
|
1030
1241
|
});
|
|
1242
|
+
} }), _jsx(RiskConfirmation, { open: fullAccessConfirmationSnapshot !== undefined, title: t('permission.fullAccessTitle'), description: fullAccessConfirmationSnapshot === undefined
|
|
1243
|
+
? ''
|
|
1244
|
+
: t('permission.fullAccessDescription', {
|
|
1245
|
+
cwd: fullAccessConfirmationSnapshot.cwd,
|
|
1246
|
+
}), acknowledgeLabel: t('permission.fullAccessAcknowledge'), cancelLabel: t('dialog.cancel'), confirmLabel: t('permission.fullAccessEnable'), acknowledged: fullAccessAcknowledged, onAcknowledgedChange: setFullAccessAcknowledged, onCancel: () => {
|
|
1247
|
+
setFullAccessAcknowledged(false);
|
|
1248
|
+
fullAccessConfirmation?.resolve(false);
|
|
1249
|
+
}, onConfirm: () => {
|
|
1250
|
+
setFullAccessAcknowledged(false);
|
|
1251
|
+
fullAccessConfirmation?.resolve(true);
|
|
1031
1252
|
} })] }));
|
|
1032
1253
|
}
|
|
1033
1254
|
//# sourceMappingURL=WorktreeSurface.js.map
|