@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.
Files changed (167) hide show
  1. package/README.md +167 -24
  2. package/README.zh.md +141 -19
  3. package/cordis.patch.yml +21 -0
  4. package/lib/client/WorktreeContext.d.ts.map +1 -1
  5. package/lib/client/WorktreeContext.js +2 -2
  6. package/lib/client/WorktreeContext.js.map +1 -1
  7. package/lib/client/WorktreeHeroContext.d.ts +1 -1
  8. package/lib/client/WorktreeHeroContext.js +1 -1
  9. package/lib/client/WorktreeSurface.d.ts +2 -2
  10. package/lib/client/WorktreeSurface.d.ts.map +1 -1
  11. package/lib/client/WorktreeSurface.js +592 -124
  12. package/lib/client/WorktreeSurface.js.map +1 -1
  13. package/lib/client/entry.d.ts.map +1 -1
  14. package/lib/client/entry.js +87 -2
  15. package/lib/client/entry.js.map +1 -1
  16. package/lib/client/index.d.ts +3 -0
  17. package/lib/client/index.d.ts.map +1 -1
  18. package/lib/client/index.js +2 -0
  19. package/lib/client/index.js.map +1 -1
  20. package/lib/client/locales.d.ts +82 -0
  21. package/lib/client/locales.d.ts.map +1 -1
  22. package/lib/client/locales.js +84 -2
  23. package/lib/client/locales.js.map +1 -1
  24. package/lib/client/session-view.d.ts +34 -0
  25. package/lib/client/session-view.d.ts.map +1 -1
  26. package/lib/client/session-view.js +119 -1
  27. package/lib/client/session-view.js.map +1 -1
  28. package/lib/client/virtual-workspace-membership.js +1 -1
  29. package/lib/client/virtual-workspace-membership.js.map +1 -1
  30. package/lib/client/worktree-connection.d.ts +6 -2
  31. package/lib/client/worktree-connection.d.ts.map +1 -1
  32. package/lib/client/worktree-connection.js +8 -0
  33. package/lib/client/worktree-connection.js.map +1 -1
  34. package/lib/client/worktree-error-copy.d.ts +2 -0
  35. package/lib/client/worktree-error-copy.d.ts.map +1 -1
  36. package/lib/client/worktree-error-copy.js +22 -0
  37. package/lib/client/worktree-error-copy.js.map +1 -1
  38. package/lib/client/worktree-expand-state.d.ts +33 -0
  39. package/lib/client/worktree-expand-state.d.ts.map +1 -0
  40. package/lib/client/worktree-expand-state.js +83 -0
  41. package/lib/client/worktree-expand-state.js.map +1 -0
  42. package/lib/client/worktree-permission-icon.d.ts +7 -0
  43. package/lib/client/worktree-permission-icon.d.ts.map +1 -0
  44. package/lib/client/worktree-permission-icon.js +119 -0
  45. package/lib/client/worktree-permission-icon.js.map +1 -0
  46. package/lib/client/worktree-permission.d.ts +21 -0
  47. package/lib/client/worktree-permission.d.ts.map +1 -0
  48. package/lib/client/worktree-permission.js +70 -0
  49. package/lib/client/worktree-permission.js.map +1 -0
  50. package/lib/client/worktree-session-fork.d.ts +65 -0
  51. package/lib/client/worktree-session-fork.d.ts.map +1 -0
  52. package/lib/client/worktree-session-fork.js +174 -0
  53. package/lib/client/worktree-session-fork.js.map +1 -0
  54. package/lib/client/worktree-session-order.d.ts +36 -0
  55. package/lib/client/worktree-session-order.d.ts.map +1 -0
  56. package/lib/client/worktree-session-order.js +215 -0
  57. package/lib/client/worktree-session-order.js.map +1 -0
  58. package/lib/client/worktree-session-position.d.ts +2 -0
  59. package/lib/client/worktree-session-position.d.ts.map +1 -0
  60. package/lib/client/worktree-session-position.js +11 -0
  61. package/lib/client/worktree-session-position.js.map +1 -0
  62. package/lib/client/worktree-session.d.ts +34 -1
  63. package/lib/client/worktree-session.d.ts.map +1 -1
  64. package/lib/client/worktree-session.js +98 -2
  65. package/lib/client/worktree-session.js.map +1 -1
  66. package/lib/client/worktree-surface-dialogs.d.ts +1 -1
  67. package/lib/client/worktree-surface-dialogs.d.ts.map +1 -1
  68. package/lib/client/worktree-surface-dialogs.js +30 -13
  69. package/lib/client/worktree-surface-dialogs.js.map +1 -1
  70. package/lib/client/worktree-surface-rows.d.ts +4 -4
  71. package/lib/client/worktree-surface-rows.d.ts.map +1 -1
  72. package/lib/client/worktree-surface-rows.js +132 -47
  73. package/lib/client/worktree-surface-rows.js.map +1 -1
  74. package/lib/client/worktree-surface-selectors.d.ts +18 -0
  75. package/lib/client/worktree-surface-selectors.d.ts.map +1 -1
  76. package/lib/client/worktree-surface-selectors.js +62 -0
  77. package/lib/client/worktree-surface-selectors.js.map +1 -1
  78. package/lib/client/worktree-surface-types.d.ts +73 -3
  79. package/lib/client/worktree-surface-types.d.ts.map +1 -1
  80. package/lib/client/worktree-view-actions.d.ts +11 -3
  81. package/lib/client/worktree-view-actions.d.ts.map +1 -1
  82. package/lib/client/worktree-view-actions.js +31 -2
  83. package/lib/client/worktree-view-actions.js.map +1 -1
  84. package/lib/client/worktree-view-errors.d.ts +10 -0
  85. package/lib/client/worktree-view-errors.d.ts.map +1 -1
  86. package/lib/client/worktree-view-errors.js +22 -1
  87. package/lib/client/worktree-view-errors.js.map +1 -1
  88. package/lib/client/worktree-view-read.d.ts +8 -0
  89. package/lib/client/worktree-view-read.d.ts.map +1 -1
  90. package/lib/client/worktree-view-read.js +19 -0
  91. package/lib/client/worktree-view-read.js.map +1 -1
  92. package/lib/client.js +2047 -264
  93. package/lib/client.js.map +1 -1
  94. package/lib/contract/index.contract.js +16 -0
  95. package/lib/contract/index.contract.js.map +1 -1
  96. package/lib/contract/index.d.ts +26 -2
  97. package/lib/contract/index.d.ts.map +1 -1
  98. package/lib/contract/index.js +8 -0
  99. package/lib/contract/index.js.map +1 -1
  100. package/lib/contract/worktree-permission.d.ts +73 -0
  101. package/lib/contract/worktree-permission.d.ts.map +1 -0
  102. package/lib/contract/worktree-permission.js +32 -0
  103. package/lib/contract/worktree-permission.js.map +1 -0
  104. package/lib/host/index.d.ts +4 -0
  105. package/lib/host/index.d.ts.map +1 -1
  106. package/lib/host/index.js +2 -0
  107. package/lib/host/index.js.map +1 -1
  108. package/lib/host/remote.d.ts +2 -2
  109. package/lib/host/remote.d.ts.map +1 -1
  110. package/lib/host/remote.js +9 -1
  111. package/lib/host/remote.js.map +1 -1
  112. package/lib/host/service.d.ts +10 -1
  113. package/lib/host/service.d.ts.map +1 -1
  114. package/lib/host/service.js +48 -2
  115. package/lib/host/service.js.map +1 -1
  116. package/lib/host/worktree-permission-manager.d.ts +14 -0
  117. package/lib/host/worktree-permission-manager.d.ts.map +1 -0
  118. package/lib/host/worktree-permission-manager.js +111 -0
  119. package/lib/host/worktree-permission-manager.js.map +1 -0
  120. package/lib/host/worktree-permission.d.ts +38 -0
  121. package/lib/host/worktree-permission.d.ts.map +1 -0
  122. package/lib/host/worktree-permission.js +156 -0
  123. package/lib/host/worktree-permission.js.map +1 -0
  124. package/lib/index.d.ts +4 -3
  125. package/lib/index.d.ts.map +1 -1
  126. package/lib/index.js +2 -1
  127. package/lib/index.js.map +1 -1
  128. package/lib/manage/manager-support.d.ts +2 -0
  129. package/lib/manage/manager-support.d.ts.map +1 -1
  130. package/lib/manage/manager-support.js +9 -0
  131. package/lib/manage/manager-support.js.map +1 -1
  132. package/lib/manage/manager-worktrees.d.ts +10 -1
  133. package/lib/manage/manager-worktrees.d.ts.map +1 -1
  134. package/lib/manage/manager-worktrees.js +121 -5
  135. package/lib/manage/manager-worktrees.js.map +1 -1
  136. package/lib/manage/manager.d.ts +8 -1
  137. package/lib/manage/manager.d.ts.map +1 -1
  138. package/lib/manage/manager.js +7 -1
  139. package/lib/manage/manager.js.map +1 -1
  140. package/lib/manage/worktree-permission.d.ts +3 -0
  141. package/lib/manage/worktree-permission.d.ts.map +1 -0
  142. package/lib/manage/worktree-permission.js +3 -0
  143. package/lib/manage/worktree-permission.js.map +1 -0
  144. package/lib/provider/git.d.ts +2 -0
  145. package/lib/provider/git.d.ts.map +1 -1
  146. package/lib/provider/git.js +23 -1
  147. package/lib/provider/git.js.map +1 -1
  148. package/lib/provider/index.d.ts +1 -1
  149. package/lib/provider/index.d.ts.map +1 -1
  150. package/lib/provider/index.js +1 -1
  151. package/lib/provider/index.js.map +1 -1
  152. package/lib/provider/sidecar-schema.d.ts +1 -1
  153. package/lib/provider/sidecar-schema.d.ts.map +1 -1
  154. package/lib/provider/sidecar-schema.js +33 -37
  155. package/lib/provider/sidecar-schema.js.map +1 -1
  156. package/lib/provider/sidecar.d.ts.map +1 -1
  157. package/lib/provider/sidecar.js +3 -1
  158. package/lib/provider/sidecar.js.map +1 -1
  159. package/lib/provider/types.d.ts +9 -1
  160. package/lib/provider/types.d.ts.map +1 -1
  161. package/lib/provider/types.js +2 -1
  162. package/lib/provider/types.js.map +1 -1
  163. package/lib/typert.host.js +214 -21
  164. package/lib/typert.remote-client.d.ts +9 -1
  165. package/lib/typert.remote-client.d.ts.map +1 -1
  166. package/lib/typert.remote-client.js +214 -21
  167. 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, IconPlusOutline16, IconSearchOutline16, } from '@deepseek-ai/dsh-client-ui-primitives';
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 [expandedWorkspaces, setExpandedWorkspaces] = useState({});
45
- const [expandedMains, setExpandedMains] = useState({});
46
- const [expandedWorktrees, setExpandedWorktrees] = useState({});
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({ status: 'ready', views });
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 ? 'noLocalBranch' : undefined
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
- ? over.sessionId
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
- void insertSessionBefore(workspaceId, activeDrag.sessionId, beforeSessionId).catch((error) => {
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
- setExpandedWorkspaces((current) => ({
378
- ...current,
379
- [workspaceId]: current[workspaceId] === false,
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
- setExpandedMains((current) => ({
384
- ...current,
385
- [workspaceId]: current[workspaceId] === false,
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
- setExpandedWorktrees((current) => ({
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
- [worktreeId]: current[worktreeId] === false,
711
+ [groupKey]: !transientExpanded,
392
712
  }));
393
713
  };
394
- const openWorktreeCreator = (workspace) => {
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
- const baseBranch = selectDefaultBaseBranch(view?.branches ?? []);
397
- const existingNames = [
398
- ...(view?.branches ?? []).map((branch) => branch.name),
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
- setSelectedBranch(baseBranch);
403
- setNewBranch(createDefaultWorktreeName(existingNames));
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 submitWorktree = async () => {
407
- const worktreeName = newBranch.trim();
408
- if (manager === undefined ||
409
- modalWorkspace === undefined ||
410
- !modalCanCreate ||
411
- selectedBranch.length === 0 ||
412
- worktreeName.length === 0) {
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 input = {
416
- workspaceId: modalWorkspace.workspaceId,
417
- branch: selectedBranch,
418
- newBranch: worktreeName,
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 createdWorktree = await executeWorktreeAction(manager, {
425
- type: 'createWorktree',
426
- input,
427
- });
428
- if (createdWorktree === undefined) {
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
- setWorktreeModalWorkspaceId(undefined);
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(IconPlusOutline16, {}) })] }), _jsxs("div", { className: styles.content, tabIndex: 0, children: [actionError !== undefined && (_jsxs("div", { className: styles.error, role: "alert", "data-worktree-error": true, children: [_jsx("p", { className: styles.message, "data-error": "true", children: formatWorktreeViewError(actionError, t) }), pendingSessionBinding !== undefined && (_jsxs("div", { className: styles.recoveryActions, children: [_jsx("button", { type: "button", className: styles.actionButton, disabled: actionPending || pendingSessionArchived, onClick: () => {
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 = expandedWorkspaces[workspace.workspaceId] !== false;
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: visibleMainSessionIds, workspaceId: workspace.workspaceId, expanded: expandedSessionGroups[mainGroupKey] === true, actionPending: actionPending, sessions: sessions, dragState: sessionDrag, onToggleExpanded: () => {
627
- setExpandedSessionGroups((current) => ({
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 || sessionMatchesQuery(sessionId, sessions, query));
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 = expandedWorktrees[record.worktreeId] !== false;
667
- return (_jsxs("div", { className: styles.worktreeGroup, "data-worktree-id": record.worktreeId, children: [_jsx(WorktreeGroupRow, { t: t, kind: "worktree", label: record.branch, worktreeId: record.worktreeId, expanded: worktreeExpanded, icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, state: state, stateLabel: stateLabel, onToggle: () => {
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: record.status === 'active'
678
- ? {
679
- open: openWorktreeMenuId === record.worktreeId,
680
- label: record.branch,
681
- disabled: actionPending,
682
- onOpenChange: (open) => {
683
- setOpenWorktreeMenuId(open ? record.worktreeId : undefined);
684
- },
685
- onRemove: () => {
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
- : undefined, drag: {
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: visibleWorktreeSessionIds, workspaceId: workspace.workspaceId, expanded: expandedSessionGroups[worktreeGroupKey] === true, actionPending: actionPending, sessions: sessions, dragState: sessionDrag, onToggleExpanded: () => {
722
- setExpandedSessionGroups((current) => ({
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
- setWorktreeModalWorkspaceId(undefined);
751
- }, onSelectedBranchChange: setSelectedBranch, onNewBranchChange: setNewBranch, onSubmit: submitWorktree }), _jsx(WorktreeRemovalDialog, { t: t, worktree: worktreeRemoval, actionPending: actionPending, onClose: () => {
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