@cerbur/clutch-dsh-worktree 0.1.5 → 0.1.7
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 +167 -24
- package/README.zh.md +141 -19
- 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/WorktreeHeroContext.d.ts +1 -1
- package/lib/client/WorktreeHeroContext.js +1 -1
- package/lib/client/WorktreeSurface.d.ts +2 -2
- package/lib/client/WorktreeSurface.d.ts.map +1 -1
- package/lib/client/WorktreeSurface.js +592 -124
- package/lib/client/WorktreeSurface.js.map +1 -1
- package/lib/client/entry.d.ts.map +1 -1
- package/lib/client/entry.js +87 -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 +82 -0
- package/lib/client/locales.d.ts.map +1 -1
- package/lib/client/locales.js +84 -2
- 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/virtual-workspace-membership.js +1 -1
- package/lib/client/virtual-workspace-membership.js.map +1 -1
- package/lib/client/worktree-connection.d.ts +6 -2
- package/lib/client/worktree-connection.d.ts.map +1 -1
- package/lib/client/worktree-connection.js +8 -0
- package/lib/client/worktree-connection.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 +22 -0
- package/lib/client/worktree-error-copy.js.map +1 -1
- package/lib/client/worktree-expand-state.d.ts +33 -0
- package/lib/client/worktree-expand-state.d.ts.map +1 -0
- package/lib/client/worktree-expand-state.js +83 -0
- package/lib/client/worktree-expand-state.js.map +1 -0
- 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-position.d.ts +2 -0
- package/lib/client/worktree-session-position.d.ts.map +1 -0
- package/lib/client/worktree-session-position.js +11 -0
- package/lib/client/worktree-session-position.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 +98 -2
- package/lib/client/worktree-session.js.map +1 -1
- package/lib/client/worktree-surface-dialogs.d.ts +1 -1
- package/lib/client/worktree-surface-dialogs.d.ts.map +1 -1
- package/lib/client/worktree-surface-dialogs.js +30 -13
- package/lib/client/worktree-surface-dialogs.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 +132 -47
- package/lib/client/worktree-surface-rows.js.map +1 -1
- package/lib/client/worktree-surface-selectors.d.ts +18 -0
- package/lib/client/worktree-surface-selectors.d.ts.map +1 -1
- package/lib/client/worktree-surface-selectors.js +62 -0
- package/lib/client/worktree-surface-selectors.js.map +1 -1
- package/lib/client/worktree-surface-types.d.ts +73 -3
- package/lib/client/worktree-surface-types.d.ts.map +1 -1
- package/lib/client/worktree-view-actions.d.ts +11 -3
- package/lib/client/worktree-view-actions.d.ts.map +1 -1
- package/lib/client/worktree-view-actions.js +31 -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/worktree-view-read.d.ts +8 -0
- package/lib/client/worktree-view-read.d.ts.map +1 -1
- package/lib/client/worktree-view-read.js +19 -0
- package/lib/client/worktree-view-read.js.map +1 -1
- package/lib/client.js +2047 -264
- package/lib/client.js.map +1 -1
- package/lib/contract/index.contract.js +16 -0
- package/lib/contract/index.contract.js.map +1 -1
- package/lib/contract/index.d.ts +26 -2
- 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/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 +9 -1
- package/lib/host/remote.js.map +1 -1
- package/lib/host/service.d.ts +10 -1
- package/lib/host/service.d.ts.map +1 -1
- package/lib/host/service.js +48 -2
- 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 +111 -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 +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/manage/manager-support.d.ts +2 -0
- package/lib/manage/manager-support.d.ts.map +1 -1
- package/lib/manage/manager-support.js +9 -0
- package/lib/manage/manager-support.js.map +1 -1
- package/lib/manage/manager-worktrees.d.ts +10 -1
- package/lib/manage/manager-worktrees.d.ts.map +1 -1
- package/lib/manage/manager-worktrees.js +121 -5
- package/lib/manage/manager-worktrees.js.map +1 -1
- package/lib/manage/manager.d.ts +8 -1
- package/lib/manage/manager.d.ts.map +1 -1
- package/lib/manage/manager.js +7 -1
- package/lib/manage/manager.js.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 +2 -0
- package/lib/provider/git.d.ts.map +1 -1
- package/lib/provider/git.js +23 -1
- package/lib/provider/git.js.map +1 -1
- package/lib/provider/index.d.ts +1 -1
- package/lib/provider/index.d.ts.map +1 -1
- package/lib/provider/index.js +1 -1
- package/lib/provider/index.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 +33 -37
- package/lib/provider/sidecar-schema.js.map +1 -1
- package/lib/provider/sidecar.d.ts.map +1 -1
- package/lib/provider/sidecar.js +3 -1
- package/lib/provider/sidecar.js.map +1 -1
- package/lib/provider/types.d.ts +9 -1
- package/lib/provider/types.d.ts.map +1 -1
- package/lib/provider/types.js +2 -1
- package/lib/provider/types.js.map +1 -1
- package/lib/typert.host.js +214 -21
- package/lib/typert.remote-client.d.ts +9 -1
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +214 -21
- package/package.json +30 -30
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { IconBranchOutline16, IconCloseOutline16,
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, } from 'react';
|
|
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
|
+
import { isMainExpanded, isWorkspaceExpanded, isWorktreeExpanded, } from './worktree-expand-state.js';
|
|
6
7
|
import { effectiveViewMode, unboundSessionIds, workspaceSessionIds } from './view-mode.js';
|
|
7
|
-
import { formatWorktreeViewError } from './worktree-error-copy.js';
|
|
8
|
-
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';
|
|
9
11
|
import { retryWorktreeSessionBinding } from './worktree-session.js';
|
|
10
12
|
import { WorktreeCreateDialog, WorktreeRemovalDialog, WorktreeSessionRenameDialog, WorktreeWorkspaceDeleteDialog, WorktreeWorkspaceRenameDialog, } from './worktree-surface-dialogs.js';
|
|
11
|
-
import { bindingIdsFor, includesText, workspaceMatches, } from './worktree-surface-selectors.js';
|
|
13
|
+
import { bindingIdsFor, clearSessionGroupExpansion, includesText, isCompleteWorktreeWorkspaceSnapshot, currentSessionRevealKeys, isSessionGroupAutoExpanded, resolveCurrentSessionLocation, workspaceMatches, } from './worktree-surface-selectors.js';
|
|
14
|
+
import { scrollCurrentSessionIntoView } from './worktree-session-position.js';
|
|
12
15
|
import { WorktreeGroupRow, WorktreeSessionGroup, WorktreeWorkspaceRow, } from './worktree-surface-rows.js';
|
|
13
|
-
import { executeWorktreeAction, createDefaultWorktreeName, createWorktreeRefreshGuard, filterArchivedSessionIds, loadWorktreeViews, reconcileBaseBranchSelection, resolveWorktreeMove, selectDefaultBaseBranch, stableWorkspaceIds, toRetryableWorktreeOrderError, toWorktreeViewError, WorktreeSessionBindingError, } from './worktree-view.js';
|
|
16
|
+
import { executeWorktreeAction, createDefaultWorktreeName, createNumberedWorktreeName, createWorktreeModalViewLoader, createWorktreeRefreshGuard, filterArchivedSessionIds, loadWorktreeViews, mergeWorktreeView, reconcileBaseBranchSelection, resolveWorktreeMove, selectDefaultBaseBranch, stableWorkspaceIds, toRetryableWorktreeOrderError, toWorktreeViewError, WorktreeSessionBindingError, WorktreeSessionPermissionError, } from './worktree-view.js';
|
|
14
17
|
import styles from './worktree.css';
|
|
15
18
|
function toNativeWorktreeViewError(error) {
|
|
16
19
|
const viewError = toWorktreeViewError(error);
|
|
@@ -19,6 +22,30 @@ function toNativeWorktreeViewError(error) {
|
|
|
19
22
|
return { ...viewError, message: String(error) };
|
|
20
23
|
}
|
|
21
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
|
+
}
|
|
22
49
|
function useStableWorkspaceIds(workspaces) {
|
|
23
50
|
const next = workspaces.map((workspace) => workspace.workspaceId);
|
|
24
51
|
const previousRef = useRef([]);
|
|
@@ -31,21 +58,52 @@ function useStableWorkspaceIds(workspaces) {
|
|
|
31
58
|
* Peer Worktree navigation surface. It keeps the original DSH Workspace mode
|
|
32
59
|
* intact and renders a browser-local Workspace → Worktree → Session projection.
|
|
33
60
|
*/
|
|
34
|
-
export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, manager, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession, }) {
|
|
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, }) {
|
|
35
62
|
const preferredMode = useStore((state) => state.viewMode);
|
|
36
63
|
const mode = effectiveViewMode(preferredMode, available && manager !== undefined);
|
|
37
64
|
const sessions = useSessions((state) => state);
|
|
38
65
|
const workspaces = useWorkspaces((state) => state);
|
|
66
|
+
const forkRecoveryStore = forkRecovery ?? EMPTY_FORK_RECOVERY_STORE;
|
|
67
|
+
const forkRecoverySnapshot = useSyncExternalStore(forkRecoveryStore.subscribe, forkRecoveryStore.getSnapshot, forkRecoveryStore.getSnapshot);
|
|
68
|
+
const currentSessionId = sessions.current;
|
|
39
69
|
const workspaceIds = useStableWorkspaceIds(workspaces.items);
|
|
40
70
|
const [readState, setReadState] = useState(EMPTY_READ_STATE);
|
|
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]);
|
|
41
88
|
const readStateRef = useRef(readState);
|
|
42
89
|
readStateRef.current = readState;
|
|
43
90
|
const [searchQuery, setSearchQuery] = useState('');
|
|
44
|
-
const [
|
|
45
|
-
const
|
|
46
|
-
|
|
91
|
+
const [currentSessionReveal, setCurrentSessionReveal] = useState();
|
|
92
|
+
const searchQueryRef = useRef(searchQuery);
|
|
93
|
+
searchQueryRef.current = searchQuery;
|
|
94
|
+
const locateGenerationRef = useRef(0);
|
|
95
|
+
const positionedLocateGenerationRef = useRef();
|
|
47
96
|
const [worktreeModalWorkspaceId, setWorktreeModalWorkspaceId] = useState();
|
|
97
|
+
const [modalReadError, setModalReadError] = useState();
|
|
98
|
+
const [modalReadLoading, setModalReadLoading] = useState(false);
|
|
99
|
+
const [worktreeModalMode, setWorktreeModalMode] = useState('create');
|
|
100
|
+
const [importCandidates, setImportCandidates] = useState({
|
|
101
|
+
status: 'idle',
|
|
102
|
+
candidates: [],
|
|
103
|
+
});
|
|
104
|
+
const [selectedImportPath, setSelectedImportPath] = useState();
|
|
48
105
|
const [openWorkspaceMenuId, setOpenWorkspaceMenuId] = useState();
|
|
106
|
+
const [openMainMenuId, setOpenMainMenuId] = useState();
|
|
49
107
|
const [openWorktreeMenuId, setOpenWorktreeMenuId] = useState();
|
|
50
108
|
const [worktreeRemoval, setWorktreeRemoval] = useState();
|
|
51
109
|
const [selectedBranch, setSelectedBranch] = useState('');
|
|
@@ -53,6 +111,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
53
111
|
const [pendingSessionBinding, setPendingSessionBinding] = useState();
|
|
54
112
|
const [actionError, setActionError] = useState();
|
|
55
113
|
const [actionPending, setActionPending] = useState(false);
|
|
114
|
+
const [forkRetryKey, setForkRetryKey] = useState();
|
|
56
115
|
const [sessionRenameTarget, setSessionRenameTarget] = useState();
|
|
57
116
|
const [sessionRenameDraft, setSessionRenameDraft] = useState('');
|
|
58
117
|
const [sessionRenamePending, setSessionRenamePending] = useState(false);
|
|
@@ -72,6 +131,12 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
72
131
|
const [worktreeDrag, setWorktreeDrag] = useState();
|
|
73
132
|
const worktreeDropCommitted = useRef(false);
|
|
74
133
|
const refreshGuard = useRef(createWorktreeRefreshGuard());
|
|
134
|
+
const forkRecoveryRevisionRef = useRef(forkRecoverySnapshot.revision);
|
|
135
|
+
const modalReadLoader = useRef(createWorktreeModalViewLoader());
|
|
136
|
+
const modalReadViewRef = useRef();
|
|
137
|
+
const importCandidatesGuard = useRef(createWorktreeRefreshGuard());
|
|
138
|
+
const modalWorkspaceIdRef = useRef(worktreeModalWorkspaceId);
|
|
139
|
+
modalWorkspaceIdRef.current = worktreeModalWorkspaceId;
|
|
75
140
|
const { ref, width, bounds } = useSidebarOverlayGeometry(mode === 'worktree');
|
|
76
141
|
const collapsed = width <= 64;
|
|
77
142
|
const query = searchQuery.trim().toLocaleLowerCase();
|
|
@@ -92,26 +157,73 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
92
157
|
invalidateContext: options.invalidateContext !== false,
|
|
93
158
|
invalidateWorktreeContext,
|
|
94
159
|
}), (views) => {
|
|
95
|
-
setReadState({
|
|
160
|
+
setReadState({
|
|
161
|
+
status: 'ready',
|
|
162
|
+
views: modalReadViewRef.current === undefined
|
|
163
|
+
? views
|
|
164
|
+
: mergeWorktreeView(views, modalReadViewRef.current),
|
|
165
|
+
});
|
|
96
166
|
}, (error) => {
|
|
97
167
|
if (preserveCurrent)
|
|
98
168
|
throw error;
|
|
99
169
|
setReadState({
|
|
100
170
|
status: 'error',
|
|
101
|
-
views: [],
|
|
171
|
+
views: modalReadViewRef.current === undefined ? [] : [modalReadViewRef.current],
|
|
102
172
|
error: toWorktreeViewError(error),
|
|
103
173
|
});
|
|
104
174
|
});
|
|
105
175
|
}, [invalidateWorktreeContext, manager, workspaceIds]);
|
|
176
|
+
const loadModalWorktreeView = useCallback((workspaceId) => {
|
|
177
|
+
setModalReadError(undefined);
|
|
178
|
+
setModalReadLoading(true);
|
|
179
|
+
if (manager === undefined) {
|
|
180
|
+
setModalReadLoading(false);
|
|
181
|
+
setModalReadError({
|
|
182
|
+
code: 'WORKTREE_VIEW_UNAVAILABLE',
|
|
183
|
+
message: '',
|
|
184
|
+
retryable: true,
|
|
185
|
+
});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
void modalReadLoader.current.load(manager, workspaceId, (view) => {
|
|
189
|
+
modalReadViewRef.current = view;
|
|
190
|
+
setModalReadLoading(false);
|
|
191
|
+
setReadState((current) => ({
|
|
192
|
+
...current,
|
|
193
|
+
views: mergeWorktreeView(current.views, view),
|
|
194
|
+
}));
|
|
195
|
+
}, (error) => {
|
|
196
|
+
setModalReadLoading(false);
|
|
197
|
+
setModalReadError(toWorktreeViewError(error));
|
|
198
|
+
});
|
|
199
|
+
}, [manager]);
|
|
106
200
|
useEffect(() => {
|
|
107
201
|
if (mode === 'worktree') {
|
|
108
202
|
void refresh({ preserveCurrent: readStateRef.current.status === 'ready' });
|
|
109
203
|
}
|
|
110
204
|
else {
|
|
111
205
|
refreshGuard.current.invalidate();
|
|
206
|
+
modalReadLoader.current.invalidate();
|
|
207
|
+
modalReadViewRef.current = undefined;
|
|
208
|
+
setWorktreeModalWorkspaceId(undefined);
|
|
209
|
+
setModalReadError(undefined);
|
|
210
|
+
setModalReadLoading(false);
|
|
112
211
|
setReadState(EMPTY_READ_STATE);
|
|
113
212
|
}
|
|
114
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]);
|
|
115
227
|
useEffect(() => {
|
|
116
228
|
if (readState.status !== 'ready' || syncSessionWorkspaces === undefined)
|
|
117
229
|
return;
|
|
@@ -121,6 +233,117 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
121
233
|
}))));
|
|
122
234
|
}, [readState.status, readState.views, syncSessionWorkspaces]);
|
|
123
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]);
|
|
290
|
+
const currentSessionLocation = useMemo(() => readState.status === 'ready'
|
|
291
|
+
? resolveCurrentSessionLocation(currentSessionId, workspaces.items, readState.views)
|
|
292
|
+
: undefined, [currentSessionId, readState.status, readState.views, workspaces.items]);
|
|
293
|
+
const currentRevealKeys = useMemo(() => new Set(currentSessionRevealKeys(currentSessionLocation)), [currentSessionLocation]);
|
|
294
|
+
const isCurrentSessionReveal = (key) => currentSessionReveal !== undefined &&
|
|
295
|
+
currentSessionReveal.sessionId === currentSessionId &&
|
|
296
|
+
currentRevealKeys.has(key) &&
|
|
297
|
+
currentSessionReveal.suppressedKeys[key] !== true;
|
|
298
|
+
useLayoutEffect(() => {
|
|
299
|
+
locateGenerationRef.current += 1;
|
|
300
|
+
positionedLocateGenerationRef.current = undefined;
|
|
301
|
+
if (mode !== 'worktree') {
|
|
302
|
+
setCurrentSessionReveal(undefined);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (searchQueryRef.current.trim().length > 0)
|
|
306
|
+
setSearchQuery('');
|
|
307
|
+
setCurrentSessionReveal(currentSessionId === undefined
|
|
308
|
+
? undefined
|
|
309
|
+
: { sessionId: currentSessionId, suppressedKeys: {} });
|
|
310
|
+
}, [currentSessionId, mode]);
|
|
311
|
+
useLayoutEffect(() => {
|
|
312
|
+
if (mode !== 'worktree' ||
|
|
313
|
+
currentSessionId === undefined ||
|
|
314
|
+
currentSessionLocation === undefined) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const generation = locateGenerationRef.current;
|
|
318
|
+
if (positionedLocateGenerationRef.current === generation)
|
|
319
|
+
return;
|
|
320
|
+
let cancelled = false;
|
|
321
|
+
const frame = requestAnimationFrame(() => {
|
|
322
|
+
if (cancelled || generation !== locateGenerationRef.current)
|
|
323
|
+
return;
|
|
324
|
+
if (!scrollCurrentSessionIntoView(ref.current, currentSessionId))
|
|
325
|
+
return;
|
|
326
|
+
positionedLocateGenerationRef.current = generation;
|
|
327
|
+
});
|
|
328
|
+
return () => {
|
|
329
|
+
cancelled = true;
|
|
330
|
+
cancelAnimationFrame(frame);
|
|
331
|
+
};
|
|
332
|
+
}, [
|
|
333
|
+
currentSessionId,
|
|
334
|
+
currentSessionLocation,
|
|
335
|
+
currentSessionReveal,
|
|
336
|
+
expandSnapshot,
|
|
337
|
+
mode,
|
|
338
|
+
query,
|
|
339
|
+
readState.status,
|
|
340
|
+
]);
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
if (readState.status !== 'ready' ||
|
|
343
|
+
!isCompleteWorktreeWorkspaceSnapshot(workspaceIds, readState.views))
|
|
344
|
+
return;
|
|
345
|
+
expandState.actions.retain(workspaceIds, readState.views.flatMap((view) => view.worktrees.map((record) => record.worktreeId)));
|
|
346
|
+
}, [expandState, readState.status, readState.views, workspaceIds]);
|
|
124
347
|
const modalWorkspace = workspaces.items.find((workspace) => workspace.workspaceId === worktreeModalWorkspaceId);
|
|
125
348
|
const modalView = worktreeModalWorkspaceId === undefined
|
|
126
349
|
? undefined
|
|
@@ -129,17 +352,29 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
129
352
|
const modalSetupStatus = modalView === undefined
|
|
130
353
|
? undefined
|
|
131
354
|
: modalReadiness?.status === 'ready'
|
|
132
|
-
? modalView.branches.length === 0
|
|
355
|
+
? modalView.branches.length === 0
|
|
356
|
+
? 'noLocalBranch'
|
|
357
|
+
: undefined
|
|
133
358
|
: modalReadiness?.status;
|
|
134
|
-
const modalCanCreate = modalSetupStatus === undefined && modalReadiness?.status === 'ready';
|
|
359
|
+
const modalCanCreate = !modalReadLoading && modalSetupStatus === undefined && modalReadiness?.status === 'ready';
|
|
135
360
|
useEffect(() => {
|
|
136
361
|
if (worktreeModalWorkspaceId === undefined || modalView === undefined)
|
|
137
362
|
return;
|
|
138
363
|
if (modalView.readiness.status !== 'ready') {
|
|
139
364
|
setSelectedBranch('');
|
|
365
|
+
setNewBranch('');
|
|
140
366
|
return;
|
|
141
367
|
}
|
|
142
368
|
setSelectedBranch((current) => reconcileBaseBranchSelection(current, modalView.branches));
|
|
369
|
+
setNewBranch((current) => {
|
|
370
|
+
if (current.length > 0)
|
|
371
|
+
return current;
|
|
372
|
+
const existingNames = [
|
|
373
|
+
...modalView.branches.map((branch) => branch.name),
|
|
374
|
+
...modalView.worktrees.map((worktree) => worktree.branch),
|
|
375
|
+
];
|
|
376
|
+
return createDefaultWorktreeName(existingNames);
|
|
377
|
+
});
|
|
143
378
|
}, [modalView, worktreeModalWorkspaceId]);
|
|
144
379
|
const runMutation = async (operation) => {
|
|
145
380
|
setActionPending(true);
|
|
@@ -155,6 +390,24 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
155
390
|
setActionPending(false);
|
|
156
391
|
}
|
|
157
392
|
};
|
|
393
|
+
const loadImportCandidates = useCallback(async (workspaceId) => {
|
|
394
|
+
if (manager === undefined)
|
|
395
|
+
return;
|
|
396
|
+
setImportCandidates((current) => ({ status: 'loading', candidates: current.candidates }));
|
|
397
|
+
await importCandidatesGuard.current.run(() => manager.listImportCandidates({ workspaceId }), (candidates) => {
|
|
398
|
+
if (modalWorkspaceIdRef.current !== workspaceId)
|
|
399
|
+
return;
|
|
400
|
+
setImportCandidates({ status: 'ready', candidates });
|
|
401
|
+
}, (error) => {
|
|
402
|
+
if (modalWorkspaceIdRef.current !== workspaceId)
|
|
403
|
+
return;
|
|
404
|
+
setImportCandidates((current) => ({
|
|
405
|
+
status: 'error',
|
|
406
|
+
candidates: current.candidates,
|
|
407
|
+
error: toWorktreeViewError(error),
|
|
408
|
+
}));
|
|
409
|
+
});
|
|
410
|
+
}, [manager]);
|
|
158
411
|
const workspaceRenameTrimmed = workspaceRenameDraft.trim();
|
|
159
412
|
const workspaceRenameDuplicate = workspaceRenameTarget !== undefined &&
|
|
160
413
|
workspaceRenameTrimmed.length > 0 &&
|
|
@@ -249,9 +502,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
249
502
|
const sourceIndex = rows.findIndex((workspace) => workspace.workspaceId === activeDrag.workspaceId);
|
|
250
503
|
if (targetIndex === -1 || sourceIndex === -1)
|
|
251
504
|
return;
|
|
252
|
-
const beforeWorkspaceId = over.half === 'before'
|
|
253
|
-
? over.workspaceId
|
|
254
|
-
: rows[targetIndex + 1]?.workspaceId;
|
|
505
|
+
const beforeWorkspaceId = over.half === 'before' ? over.workspaceId : rows[targetIndex + 1]?.workspaceId;
|
|
255
506
|
const anchorIndex = beforeWorkspaceId === undefined
|
|
256
507
|
? rows.length
|
|
257
508
|
: rows.findIndex((workspace) => workspace.workspaceId === beforeWorkspaceId);
|
|
@@ -282,12 +533,8 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
282
533
|
const sourceIndex = sessionIds.indexOf(activeDrag.sessionId);
|
|
283
534
|
if (targetIndex === -1 || sourceIndex === -1)
|
|
284
535
|
return;
|
|
285
|
-
const beforeSessionId = over.half === 'before'
|
|
286
|
-
|
|
287
|
-
: sessionIds[targetIndex + 1];
|
|
288
|
-
const anchorIndex = beforeSessionId === undefined
|
|
289
|
-
? sessionIds.length
|
|
290
|
-
: sessionIds.indexOf(beforeSessionId);
|
|
536
|
+
const beforeSessionId = over.half === 'before' ? over.sessionId : sessionIds[targetIndex + 1];
|
|
537
|
+
const anchorIndex = beforeSessionId === undefined ? sessionIds.length : sessionIds.indexOf(beforeSessionId);
|
|
291
538
|
if (beforeSessionId === activeDrag.sessionId ||
|
|
292
539
|
anchorIndex === sourceIndex ||
|
|
293
540
|
anchorIndex === sourceIndex + 1) {
|
|
@@ -302,7 +549,12 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
302
549
|
return;
|
|
303
550
|
}
|
|
304
551
|
setActionError(undefined);
|
|
305
|
-
|
|
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) => {
|
|
306
558
|
setActionError(toWorktreeViewError(error));
|
|
307
559
|
});
|
|
308
560
|
};
|
|
@@ -373,91 +625,207 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
373
625
|
return;
|
|
374
626
|
await runMutation(() => archiveSession(sessionId));
|
|
375
627
|
};
|
|
628
|
+
const clearSessionGroups = (groupKeys) => {
|
|
629
|
+
if (groupKeys.length === 0)
|
|
630
|
+
return;
|
|
631
|
+
setExpandedSessionGroups((current) => clearSessionGroupExpansion(current, groupKeys));
|
|
632
|
+
};
|
|
633
|
+
const suppressCurrentSessionReveal = (key) => {
|
|
634
|
+
if (!isCurrentSessionReveal(key))
|
|
635
|
+
return false;
|
|
636
|
+
setCurrentSessionReveal((current) => {
|
|
637
|
+
if (current === undefined || current.sessionId !== currentSessionId)
|
|
638
|
+
return current;
|
|
639
|
+
return {
|
|
640
|
+
...current,
|
|
641
|
+
suppressedKeys: { ...current.suppressedKeys, [key]: true },
|
|
642
|
+
};
|
|
643
|
+
});
|
|
644
|
+
return true;
|
|
645
|
+
};
|
|
376
646
|
const toggleWorkspace = (workspaceId) => {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
647
|
+
const persistedExpanded = isWorkspaceExpanded(expandSnapshot, workspaceId);
|
|
648
|
+
const autoExpanded = isCurrentSessionReveal('workspace:' + workspaceId);
|
|
649
|
+
const visuallyExpanded = persistedExpanded || autoExpanded;
|
|
650
|
+
if (visuallyExpanded && autoExpanded) {
|
|
651
|
+
suppressCurrentSessionReveal('workspace:' + workspaceId);
|
|
652
|
+
}
|
|
653
|
+
if (visuallyExpanded && !persistedExpanded) {
|
|
654
|
+
clearSessionGroups([
|
|
655
|
+
'main:' + workspaceId,
|
|
656
|
+
...(viewByWorkspace.get(workspaceId)?.worktrees.map((record) => record.worktreeId) ?? []).map((worktreeId) => 'worktree:' + worktreeId),
|
|
657
|
+
]);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
expandState.actions.toggleWorkspace(workspaceId);
|
|
661
|
+
if (visuallyExpanded) {
|
|
662
|
+
clearSessionGroups([
|
|
663
|
+
'main:' + workspaceId,
|
|
664
|
+
...(viewByWorkspace.get(workspaceId)?.worktrees.map((record) => record.worktreeId) ?? []).map((worktreeId) => 'worktree:' + worktreeId),
|
|
665
|
+
]);
|
|
666
|
+
}
|
|
381
667
|
};
|
|
382
668
|
const toggleMain = (workspaceId) => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
669
|
+
const persistedExpanded = isMainExpanded(expandSnapshot, workspaceId);
|
|
670
|
+
const autoExpanded = isCurrentSessionReveal('main:' + workspaceId);
|
|
671
|
+
const visuallyExpanded = persistedExpanded || autoExpanded;
|
|
672
|
+
if (visuallyExpanded && autoExpanded) {
|
|
673
|
+
suppressCurrentSessionReveal('main:' + workspaceId);
|
|
674
|
+
}
|
|
675
|
+
if (visuallyExpanded && !persistedExpanded) {
|
|
676
|
+
clearSessionGroups(['main:' + workspaceId]);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
expandState.actions.toggleMain(workspaceId);
|
|
680
|
+
if (visuallyExpanded)
|
|
681
|
+
clearSessionGroups(['main:' + workspaceId]);
|
|
387
682
|
};
|
|
388
683
|
const toggleWorktree = (worktreeId) => {
|
|
389
|
-
|
|
684
|
+
const persistedExpanded = isWorktreeExpanded(expandSnapshot, worktreeId);
|
|
685
|
+
const autoExpanded = isCurrentSessionReveal('worktree:' + worktreeId);
|
|
686
|
+
const visuallyExpanded = persistedExpanded || autoExpanded;
|
|
687
|
+
if (visuallyExpanded && autoExpanded) {
|
|
688
|
+
suppressCurrentSessionReveal('worktree:' + worktreeId);
|
|
689
|
+
}
|
|
690
|
+
if (visuallyExpanded && !persistedExpanded) {
|
|
691
|
+
clearSessionGroups(['worktree:' + worktreeId]);
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
expandState.actions.toggleWorktree(worktreeId);
|
|
695
|
+
if (visuallyExpanded)
|
|
696
|
+
clearSessionGroups(['worktree:' + worktreeId]);
|
|
697
|
+
};
|
|
698
|
+
const toggleSessionGroup = (groupKey, autoExpanded) => {
|
|
699
|
+
const transientExpanded = expandedSessionGroups[groupKey] === true;
|
|
700
|
+
if (autoExpanded) {
|
|
701
|
+
suppressCurrentSessionReveal('session-group:' + groupKey);
|
|
702
|
+
setExpandedSessionGroups((current) => {
|
|
703
|
+
const next = { ...current };
|
|
704
|
+
delete next[groupKey];
|
|
705
|
+
return next;
|
|
706
|
+
});
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
setExpandedSessionGroups((current) => ({
|
|
390
710
|
...current,
|
|
391
|
-
[
|
|
711
|
+
[groupKey]: !transientExpanded,
|
|
392
712
|
}));
|
|
393
713
|
};
|
|
394
|
-
const
|
|
714
|
+
const closeWorktreeCreator = (force = false) => {
|
|
715
|
+
if (actionPending && !force)
|
|
716
|
+
return;
|
|
717
|
+
modalReadLoader.current.invalidate();
|
|
718
|
+
modalReadViewRef.current = undefined;
|
|
719
|
+
importCandidatesGuard.current.invalidate();
|
|
720
|
+
setWorktreeModalWorkspaceId(undefined);
|
|
721
|
+
setModalReadError(undefined);
|
|
722
|
+
setModalReadLoading(false);
|
|
723
|
+
};
|
|
724
|
+
const openWorktreeCreator = (workspace, defaults = {}) => {
|
|
395
725
|
const view = viewByWorkspace.get(workspace.workspaceId);
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
...(view?.worktrees ?? []).map((worktree) => worktree.branch),
|
|
400
|
-
];
|
|
726
|
+
modalReadLoader.current.invalidate();
|
|
727
|
+
modalReadViewRef.current = undefined;
|
|
728
|
+
importCandidatesGuard.current.invalidate();
|
|
401
729
|
setWorktreeModalWorkspaceId(workspace.workspaceId);
|
|
402
|
-
|
|
403
|
-
|
|
730
|
+
setWorktreeModalMode('create');
|
|
731
|
+
setImportCandidates({ status: 'idle', candidates: [] });
|
|
732
|
+
setSelectedImportPath(undefined);
|
|
404
733
|
setActionError(undefined);
|
|
734
|
+
setModalReadError(undefined);
|
|
735
|
+
setModalReadLoading(false);
|
|
736
|
+
if (view === undefined) {
|
|
737
|
+
setSelectedBranch(defaults.baseBranch ?? '');
|
|
738
|
+
setNewBranch(defaults.newBranch ?? '');
|
|
739
|
+
loadModalWorktreeView(workspace.workspaceId);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
setSelectedBranch(defaults.baseBranch ?? selectDefaultBaseBranch(view.branches));
|
|
743
|
+
setNewBranch(defaults.newBranch ?? createDefaultWorktreeName([
|
|
744
|
+
...view.branches.map((branch) => branch.name),
|
|
745
|
+
...view.worktrees.map((worktree) => worktree.branch),
|
|
746
|
+
]));
|
|
405
747
|
};
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
if (
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
748
|
+
const changeWorktreeModalMode = (mode) => {
|
|
749
|
+
setWorktreeModalMode(mode);
|
|
750
|
+
if (mode === 'import' &&
|
|
751
|
+
worktreeModalWorkspaceId !== undefined &&
|
|
752
|
+
importCandidates.status === 'idle') {
|
|
753
|
+
void loadImportCandidates(worktreeModalWorkspaceId);
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
const continueWorktreeRegistration = async (registeredWorktree) => {
|
|
757
|
+
closeWorktreeCreator(true);
|
|
758
|
+
if (createSessionCallback === undefined) {
|
|
759
|
+
await refresh({ preserveCurrent: true });
|
|
760
|
+
setActionError({
|
|
761
|
+
code: 'WORKTREE_REGISTRATION_SESSION_UNAVAILABLE',
|
|
762
|
+
message: '',
|
|
763
|
+
retryable: true,
|
|
764
|
+
});
|
|
413
765
|
return;
|
|
414
766
|
}
|
|
415
|
-
const
|
|
416
|
-
workspaceId:
|
|
417
|
-
|
|
418
|
-
|
|
767
|
+
const sessionInput = {
|
|
768
|
+
workspaceId: registeredWorktree.workspaceId,
|
|
769
|
+
worktreeId: registeredWorktree.worktreeId,
|
|
770
|
+
cwd: registeredWorktree.absolutePath,
|
|
419
771
|
};
|
|
772
|
+
try {
|
|
773
|
+
await createSessionCallback(sessionInput);
|
|
774
|
+
await invalidateWorktreeContext?.(registeredWorktree.workspaceId);
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
778
|
+
setPendingSessionBinding({ ...sessionInput, sessionId: error.sessionId });
|
|
779
|
+
}
|
|
780
|
+
if (error instanceof WorktreeSessionPermissionError && error.retryable) {
|
|
781
|
+
setPendingSessionBinding({
|
|
782
|
+
...sessionInput,
|
|
783
|
+
sessionId: error.sessionId,
|
|
784
|
+
permissionRequired: true,
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
throw error;
|
|
788
|
+
}
|
|
789
|
+
await refresh({ preserveCurrent: true });
|
|
790
|
+
};
|
|
791
|
+
const submitWorktree = async () => {
|
|
792
|
+
const worktreeName = newBranch.trim();
|
|
793
|
+
const selectedImportCandidate = importCandidates.candidates.find((candidate) => candidate.absolutePath === selectedImportPath);
|
|
794
|
+
if (manager === undefined || modalWorkspace === undefined)
|
|
795
|
+
return;
|
|
796
|
+
if (worktreeModalMode === 'create' &&
|
|
797
|
+
(!modalCanCreate || selectedBranch.length === 0 || worktreeName.length === 0))
|
|
798
|
+
return;
|
|
799
|
+
if (worktreeModalMode === 'import' && selectedImportCandidate === undefined)
|
|
800
|
+
return;
|
|
420
801
|
setActionPending(true);
|
|
421
802
|
setActionError(undefined);
|
|
422
803
|
setPendingSessionBinding(undefined);
|
|
423
804
|
try {
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
805
|
+
const registeredWorktree = worktreeModalMode === 'create'
|
|
806
|
+
? await executeWorktreeAction(manager, {
|
|
807
|
+
type: 'createWorktree',
|
|
808
|
+
input: {
|
|
809
|
+
workspaceId: modalWorkspace.workspaceId,
|
|
810
|
+
branch: selectedBranch,
|
|
811
|
+
newBranch: worktreeName,
|
|
812
|
+
},
|
|
813
|
+
})
|
|
814
|
+
: await executeWorktreeAction(manager, {
|
|
815
|
+
type: 'importWorktree',
|
|
816
|
+
input: {
|
|
817
|
+
workspaceId: modalWorkspace.workspaceId,
|
|
818
|
+
absolutePath: selectedImportCandidate.absolutePath,
|
|
819
|
+
},
|
|
820
|
+
});
|
|
821
|
+
if (registeredWorktree === undefined) {
|
|
429
822
|
throw {
|
|
430
823
|
code: 'WORKTREE_RECORD_MISSING',
|
|
431
824
|
message: '',
|
|
432
825
|
retryable: true,
|
|
433
826
|
};
|
|
434
827
|
}
|
|
435
|
-
|
|
436
|
-
if (createSessionCallback === undefined) {
|
|
437
|
-
await refresh({ preserveCurrent: true });
|
|
438
|
-
setActionError({
|
|
439
|
-
code: 'WORKTREE_CREATED_SESSION_UNAVAILABLE',
|
|
440
|
-
message: '',
|
|
441
|
-
retryable: true,
|
|
442
|
-
});
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
const sessionInput = {
|
|
446
|
-
workspaceId: createdWorktree.workspaceId,
|
|
447
|
-
worktreeId: createdWorktree.worktreeId,
|
|
448
|
-
cwd: createdWorktree.absolutePath,
|
|
449
|
-
};
|
|
450
|
-
try {
|
|
451
|
-
await createSessionCallback(sessionInput);
|
|
452
|
-
await invalidateWorktreeContext?.(createdWorktree.workspaceId);
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
456
|
-
setPendingSessionBinding({ ...sessionInput, sessionId: error.sessionId });
|
|
457
|
-
}
|
|
458
|
-
throw error;
|
|
459
|
-
}
|
|
460
|
-
await refresh({ preserveCurrent: true });
|
|
828
|
+
await continueWorktreeRegistration(registeredWorktree);
|
|
461
829
|
}
|
|
462
830
|
catch (error) {
|
|
463
831
|
setActionError(toWorktreeViewError(error));
|
|
@@ -487,6 +855,13 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
487
855
|
if (error instanceof WorktreeSessionBindingError && error.retryable) {
|
|
488
856
|
setPendingSessionBinding({ ...input, sessionId: error.sessionId });
|
|
489
857
|
}
|
|
858
|
+
if (error instanceof WorktreeSessionPermissionError && error.retryable) {
|
|
859
|
+
setPendingSessionBinding({
|
|
860
|
+
...input,
|
|
861
|
+
sessionId: error.sessionId,
|
|
862
|
+
permissionRequired: true,
|
|
863
|
+
});
|
|
864
|
+
}
|
|
490
865
|
setActionError(toWorktreeViewError(error));
|
|
491
866
|
}
|
|
492
867
|
finally {
|
|
@@ -494,9 +869,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
494
869
|
}
|
|
495
870
|
};
|
|
496
871
|
const retrySessionBinding = async () => {
|
|
497
|
-
if (manager === undefined ||
|
|
498
|
-
pendingSessionBinding === undefined ||
|
|
499
|
-
pendingSessionArchived)
|
|
872
|
+
if (manager === undefined || pendingSessionBinding === undefined || pendingSessionArchived)
|
|
500
873
|
return;
|
|
501
874
|
const pending = pendingSessionBinding;
|
|
502
875
|
setActionPending(true);
|
|
@@ -509,6 +882,9 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
509
882
|
ensureSessionWorkspace: (workspaceId, sessionId) => {
|
|
510
883
|
ensureSessionWorkspace?.(workspaceId, sessionId);
|
|
511
884
|
},
|
|
885
|
+
permission,
|
|
886
|
+
confirmFullAccess,
|
|
887
|
+
onPermissionResult,
|
|
512
888
|
openSession,
|
|
513
889
|
});
|
|
514
890
|
setPendingSessionBinding(undefined);
|
|
@@ -525,6 +901,20 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
525
901
|
setActionPending(false);
|
|
526
902
|
}
|
|
527
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
|
+
};
|
|
528
918
|
const openWorkspaceSession = (workspaceId, sessionId) => {
|
|
529
919
|
ensureSessionWorkspace?.(workspaceId, sessionId);
|
|
530
920
|
openWorktreeSession({ open: openSession }, sessionId);
|
|
@@ -544,14 +934,21 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
544
934
|
} }), _jsx("button", { type: "button", className: styles.iconButton, "aria-label": t('workspace.add'), onClick: () => {
|
|
545
935
|
if (createWorkspace !== undefined)
|
|
546
936
|
void runMutation(createWorkspace);
|
|
547
|
-
}, 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: () => {
|
|
548
945
|
void retrySessionBinding();
|
|
549
|
-
}, 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: () => {
|
|
550
947
|
const sessionId = pendingSessionBinding.sessionId;
|
|
551
948
|
setPendingSessionBinding(undefined);
|
|
552
949
|
setActionError(undefined);
|
|
553
950
|
openSession(sessionId);
|
|
554
|
-
}, 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: () => {
|
|
555
952
|
setActionError(undefined);
|
|
556
953
|
void refresh();
|
|
557
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: () => {
|
|
@@ -562,18 +959,34 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
562
959
|
const mainLabel = currentBranch === undefined
|
|
563
960
|
? t('worktree.main')
|
|
564
961
|
: t('worktree.mainWithBranch', { branch: currentBranch });
|
|
565
|
-
const expanded =
|
|
962
|
+
const expanded = isWorkspaceExpanded(expandSnapshot, workspace.workspaceId) ||
|
|
963
|
+
isCurrentSessionReveal('workspace:' + workspace.workspaceId);
|
|
566
964
|
const workspaceMatchesQuery = includesText(workspace.title, query);
|
|
567
965
|
const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
|
|
966
|
+
const workspaceActivitySessionIds = filterVisibleSessionIds(allWorkspaceSessionIds, sessions);
|
|
967
|
+
const workspaceHasOngoingSession = hasOngoingSession(workspaceActivitySessionIds, sessionPresentations);
|
|
568
968
|
const bindings = view?.bindings ?? [];
|
|
569
969
|
const boundSessionIds = new Set(bindings.map((binding) => binding.sessionId));
|
|
570
970
|
const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...boundSessionIds]), sessions);
|
|
571
|
-
const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
572
|
-
const mainExpanded = expandedMains[workspace.workspaceId] !== false;
|
|
573
971
|
const mainGroupKey = `main:${workspace.workspaceId}`;
|
|
972
|
+
const orderedMainSessionIds = orderedSessionIdsByAccount.get(mainGroupKey) ?? mainSessionIds;
|
|
973
|
+
const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
974
|
+
const orderedVisibleMainSessionIds = orderedMainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
975
|
+
const mainExpanded = isMainExpanded(expandSnapshot, workspace.workspaceId) ||
|
|
976
|
+
isCurrentSessionReveal('main:' + workspace.workspaceId);
|
|
977
|
+
const sessionIds = orderedVisibleMainSessionIds.length === visibleMainSessionIds.length
|
|
978
|
+
? orderedVisibleMainSessionIds
|
|
979
|
+
: visibleMainSessionIds;
|
|
980
|
+
const mainSessionGroupAutoExpanded = isSessionGroupAutoExpanded(sessionIds, currentSessionId, isCurrentSessionReveal('session-group:' + mainGroupKey));
|
|
981
|
+
const mainSessionGroupExpanded = expandedSessionGroups[mainGroupKey] === true ||
|
|
982
|
+
mainSessionGroupAutoExpanded;
|
|
574
983
|
const worktrees = view?.worktrees ?? [];
|
|
984
|
+
const workspaceWorktreeNames = [
|
|
985
|
+
...(view?.branches.map((branch) => branch.name) ?? []),
|
|
986
|
+
...(view?.worktrees.map((worktree) => worktree.branch) ?? []),
|
|
987
|
+
];
|
|
575
988
|
const sameWorkspaceWorktreeDrag = worktreeDrag?.workspaceId === workspace.workspaceId;
|
|
576
|
-
return (_jsxs("section", { className: styles.workspaceGroup, "data-workspace-id": workspace.workspaceId, children: [_jsx(WorktreeWorkspaceRow, { t: t, workspace: workspace, expanded: expanded, actionPending: actionPending, menuOpen: openWorkspaceMenuId === workspace.workspaceId, drag: {
|
|
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: {
|
|
577
990
|
active: workspaceDrag !== undefined,
|
|
578
991
|
marker: workspaceDrag?.over?.workspaceId === workspace.workspaceId
|
|
579
992
|
? workspaceDrag.over.half
|
|
@@ -617,17 +1030,32 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
617
1030
|
openWorkspaceDelete(workspace);
|
|
618
1031
|
}, onMenuOpenChange: (open) => {
|
|
619
1032
|
setOpenWorkspaceMenuId(open ? workspace.workspaceId : undefined);
|
|
620
|
-
} }), expanded && (_jsxs("div", { className: styles.treeChildren, children: [_jsx(WorktreeGroupRow, { t: t, kind: "main", label: mainLabel, expanded: mainExpanded, icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, onToggle: () => {
|
|
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: () => {
|
|
621
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
|
+
},
|
|
622
1053
|
}, onCreateSession: createMainSession === undefined
|
|
623
1054
|
? undefined
|
|
624
1055
|
: () => {
|
|
625
1056
|
createMainSession(workspace.workspaceId);
|
|
626
|
-
} }), mainExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: mainGroupKey, sessionIds:
|
|
627
|
-
|
|
628
|
-
...current,
|
|
629
|
-
[mainGroupKey]: current[mainGroupKey] !== true,
|
|
630
|
-
}));
|
|
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);
|
|
631
1059
|
}, onStartDrag: (groupKey, sessionId) => {
|
|
632
1060
|
sessionDropCommitted.current = false;
|
|
633
1061
|
setSessionDrag({ groupKey, sessionId, over: null });
|
|
@@ -651,8 +1079,20 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
651
1079
|
!worktreeSessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) {
|
|
652
1080
|
return null;
|
|
653
1081
|
}
|
|
654
|
-
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery ||
|
|
1082
|
+
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery ||
|
|
1083
|
+
sessionMatchesQuery(sessionId, sessions, query));
|
|
655
1084
|
const worktreeGroupKey = `worktree:${record.worktreeId}`;
|
|
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));
|
|
1094
|
+
const sessionGroupExpanded = expandedSessionGroups[worktreeGroupKey] === true ||
|
|
1095
|
+
sessionGroupAutoExpanded;
|
|
656
1096
|
const state = record.status === 'removed'
|
|
657
1097
|
? 'warning'
|
|
658
1098
|
: record.health === 'repair'
|
|
@@ -663,8 +1103,9 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
663
1103
|
: record.health === 'repair'
|
|
664
1104
|
? t('worktree.repair')
|
|
665
1105
|
: t('worktree.ready');
|
|
666
|
-
const worktreeExpanded =
|
|
667
|
-
|
|
1106
|
+
const worktreeExpanded = isWorktreeExpanded(expandSnapshot, record.worktreeId) ||
|
|
1107
|
+
isCurrentSessionReveal('worktree:' + record.worktreeId);
|
|
1108
|
+
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: () => {
|
|
668
1109
|
toggleWorktree(record.worktreeId);
|
|
669
1110
|
}, onCreateSession: record.status === 'active' && record.health !== 'repair'
|
|
670
1111
|
? () => {
|
|
@@ -674,20 +1115,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
674
1115
|
cwd: record.absolutePath,
|
|
675
1116
|
});
|
|
676
1117
|
}
|
|
677
|
-
: undefined, menu:
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
1118
|
+
: undefined, menu: {
|
|
1119
|
+
open: openWorktreeMenuId === record.worktreeId,
|
|
1120
|
+
label: record.branch,
|
|
1121
|
+
copyPath: record.absolutePath,
|
|
1122
|
+
showCreate: record.status === 'active',
|
|
1123
|
+
showRemove: record.status === 'active',
|
|
1124
|
+
disabled: actionPending,
|
|
1125
|
+
onOpenChange: (open) => {
|
|
1126
|
+
setOpenWorktreeMenuId(open ? record.worktreeId : undefined);
|
|
1127
|
+
},
|
|
1128
|
+
onCreateWorktree: record.status === 'active'
|
|
1129
|
+
? () => {
|
|
1130
|
+
openWorktreeCreator(workspace, {
|
|
1131
|
+
baseBranch: record.branch,
|
|
1132
|
+
newBranch: createNumberedWorktreeName(record.branch, workspaceWorktreeNames),
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
: undefined,
|
|
1136
|
+
onRemove: record.status === 'active'
|
|
1137
|
+
? () => {
|
|
686
1138
|
setWorktreeRemoval(record);
|
|
687
1139
|
setActionError(undefined);
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
|
|
1140
|
+
}
|
|
1141
|
+
: undefined,
|
|
1142
|
+
}, drag: {
|
|
691
1143
|
active: sameWorkspaceWorktreeDrag,
|
|
692
1144
|
marker: worktreeDrag?.over?.worktreeId === record.worktreeId
|
|
693
1145
|
? worktreeDrag.over.half
|
|
@@ -718,11 +1170,8 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
718
1170
|
setWorktreeDrag(undefined);
|
|
719
1171
|
worktreeDropCommitted.current = false;
|
|
720
1172
|
},
|
|
721
|
-
} }), worktreeExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: worktreeGroupKey, sessionIds:
|
|
722
|
-
|
|
723
|
-
...current,
|
|
724
|
-
[worktreeGroupKey]: current[worktreeGroupKey] !== true,
|
|
725
|
-
}));
|
|
1173
|
+
} }), 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: () => {
|
|
1174
|
+
toggleSessionGroup(worktreeGroupKey, sessionGroupAutoExpanded);
|
|
726
1175
|
}, onStartDrag: (groupKey, sessionId) => {
|
|
727
1176
|
sessionDropCommitted.current = false;
|
|
728
1177
|
setSessionDrag({ groupKey, sessionId, over: null });
|
|
@@ -746,9 +1195,18 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
746
1195
|
}, onSubmit: confirmSessionRename }), _jsx(WorktreeWorkspaceRenameDialog, { t: t, target: workspaceRenameTarget, draft: workspaceRenameDraft, pending: workspaceRenamePending, duplicate: workspaceRenameDuplicate, error: workspaceRenameError, onClose: closeWorkspaceRename, onDraftChange: (draft) => {
|
|
747
1196
|
setWorkspaceRenameDraft(draft);
|
|
748
1197
|
setWorkspaceRenameError(undefined);
|
|
749
|
-
}, onSubmit: confirmWorkspaceRename }), _jsx(WorktreeWorkspaceDeleteDialog, { t: t, target: workspaceDeleteTarget, pending: workspaceDeletePending, error: workspaceDeleteError, onClose: closeWorkspaceDelete, onSubmit: confirmWorkspaceDelete }), _jsx(WorktreeCreateDialog, { t: t, workspace: modalWorkspace, view: modalView, setupStatus: modalSetupStatus, canCreate: modalCanCreate, selectedBranch: selectedBranch, newBranch: newBranch, actionPending: actionPending, onClose: () => {
|
|
750
|
-
|
|
751
|
-
|
|
1198
|
+
}, onSubmit: confirmWorkspaceRename }), _jsx(WorktreeWorkspaceDeleteDialog, { t: t, target: workspaceDeleteTarget, pending: workspaceDeletePending, error: workspaceDeleteError, onClose: closeWorkspaceDelete, onSubmit: confirmWorkspaceDelete }), _jsx(WorktreeCreateDialog, { t: t, workspace: modalWorkspace, view: modalView, readError: modalReadError, setupStatus: modalSetupStatus, canCreate: modalCanCreate, mode: worktreeModalMode, importCandidates: importCandidates, selectedImportPath: selectedImportPath, selectedBranch: selectedBranch, newBranch: newBranch, actionPending: actionPending, onClose: closeWorktreeCreator, onRetry: () => {
|
|
1199
|
+
if (worktreeModalWorkspaceId === undefined)
|
|
1200
|
+
return;
|
|
1201
|
+
modalReadViewRef.current = undefined;
|
|
1202
|
+
setSelectedBranch('');
|
|
1203
|
+
setNewBranch('');
|
|
1204
|
+
loadModalWorktreeView(worktreeModalWorkspaceId);
|
|
1205
|
+
}, onModeChange: changeWorktreeModalMode, onRetryImportCandidates: () => {
|
|
1206
|
+
if (worktreeModalWorkspaceId !== undefined) {
|
|
1207
|
+
void loadImportCandidates(worktreeModalWorkspaceId);
|
|
1208
|
+
}
|
|
1209
|
+
}, onSelectedImportPathChange: setSelectedImportPath, onSelectedBranchChange: setSelectedBranch, onNewBranchChange: setNewBranch, onSubmit: submitWorktree }), _jsx(WorktreeRemovalDialog, { t: t, worktree: worktreeRemoval, actionPending: actionPending, onClose: () => {
|
|
752
1210
|
setWorktreeRemoval(undefined);
|
|
753
1211
|
}, onSubmit: () => {
|
|
754
1212
|
if (manager === undefined || worktreeRemoval === undefined)
|
|
@@ -761,10 +1219,20 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
|
|
|
761
1219
|
workspaceId: target.workspaceId,
|
|
762
1220
|
worktreeId: target.worktreeId,
|
|
763
1221
|
},
|
|
764
|
-
});
|
|
1222
|
+
}, permission, onPermissionNotice);
|
|
765
1223
|
await invalidateWorktreeContext?.(target.workspaceId);
|
|
766
1224
|
setWorktreeRemoval(undefined);
|
|
767
1225
|
});
|
|
1226
|
+
} }), _jsx(RiskConfirmation, { open: fullAccessConfirmationSnapshot !== undefined, title: t('permission.fullAccessTitle'), description: fullAccessConfirmationSnapshot === undefined
|
|
1227
|
+
? ''
|
|
1228
|
+
: t('permission.fullAccessDescription', {
|
|
1229
|
+
cwd: fullAccessConfirmationSnapshot.cwd,
|
|
1230
|
+
}), acknowledgeLabel: t('permission.fullAccessAcknowledge'), cancelLabel: t('dialog.cancel'), confirmLabel: t('permission.fullAccessEnable'), acknowledged: fullAccessAcknowledged, onAcknowledgedChange: setFullAccessAcknowledged, onCancel: () => {
|
|
1231
|
+
setFullAccessAcknowledged(false);
|
|
1232
|
+
fullAccessConfirmation?.resolve(false);
|
|
1233
|
+
}, onConfirm: () => {
|
|
1234
|
+
setFullAccessAcknowledged(false);
|
|
1235
|
+
fullAccessConfirmation?.resolve(true);
|
|
768
1236
|
} })] }));
|
|
769
1237
|
}
|
|
770
1238
|
//# sourceMappingURL=WorktreeSurface.js.map
|