@cerbur/clutch-dsh-worktree 0.1.2 → 0.1.4

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 (143) hide show
  1. package/README.md +247 -92
  2. package/README.zh.md +297 -0
  3. package/lib/client/WorktreeContext.d.ts +15 -0
  4. package/lib/client/WorktreeContext.d.ts.map +1 -0
  5. package/lib/client/WorktreeContext.js +19 -0
  6. package/lib/client/WorktreeContext.js.map +1 -0
  7. package/lib/client/WorktreeHeroContext.d.ts +10 -0
  8. package/lib/client/WorktreeHeroContext.d.ts.map +1 -0
  9. package/lib/client/WorktreeHeroContext.js +128 -0
  10. package/lib/client/WorktreeHeroContext.js.map +1 -0
  11. package/lib/client/WorktreeOverlay.d.ts +6 -0
  12. package/lib/client/WorktreeOverlay.d.ts.map +1 -0
  13. package/lib/client/WorktreeOverlay.js +8 -0
  14. package/lib/client/WorktreeOverlay.js.map +1 -0
  15. package/lib/client/WorktreeSurface.d.ts +3 -29
  16. package/lib/client/WorktreeSurface.d.ts.map +1 -1
  17. package/lib/client/WorktreeSurface.js +170 -314
  18. package/lib/client/WorktreeSurface.js.map +1 -1
  19. package/lib/client/entry.d.ts +1 -1
  20. package/lib/client/entry.d.ts.map +1 -1
  21. package/lib/client/entry.js +41 -14
  22. package/lib/client/entry.js.map +1 -1
  23. package/lib/client/locales.d.ts +30 -0
  24. package/lib/client/locales.d.ts.map +1 -1
  25. package/lib/client/locales.js +32 -2
  26. package/lib/client/locales.js.map +1 -1
  27. package/lib/client/session-view.d.ts +21 -0
  28. package/lib/client/session-view.d.ts.map +1 -0
  29. package/lib/client/session-view.js +26 -0
  30. package/lib/client/session-view.js.map +1 -0
  31. package/lib/client/sidebar-overlay-geometry.d.ts +1 -0
  32. package/lib/client/sidebar-overlay-geometry.d.ts.map +1 -1
  33. package/lib/client/sidebar-overlay-geometry.js +28 -17
  34. package/lib/client/sidebar-overlay-geometry.js.map +1 -1
  35. package/lib/client/virtual-workspace-membership.d.ts.map +1 -1
  36. package/lib/client/virtual-workspace-membership.js +17 -6
  37. package/lib/client/virtual-workspace-membership.js.map +1 -1
  38. package/lib/client/worktree-connection.d.ts +1 -0
  39. package/lib/client/worktree-connection.d.ts.map +1 -1
  40. package/lib/client/worktree-connection.js +4 -0
  41. package/lib/client/worktree-connection.js.map +1 -1
  42. package/lib/client/worktree-context-store.d.ts +45 -0
  43. package/lib/client/worktree-context-store.d.ts.map +1 -0
  44. package/lib/client/worktree-context-store.js +199 -0
  45. package/lib/client/worktree-context-store.js.map +1 -0
  46. package/lib/client/worktree-context.d.ts +32 -0
  47. package/lib/client/worktree-context.d.ts.map +1 -0
  48. package/lib/client/worktree-context.js +51 -0
  49. package/lib/client/worktree-context.js.map +1 -0
  50. package/lib/client/worktree-error-copy.d.ts.map +1 -1
  51. package/lib/client/worktree-error-copy.js +14 -0
  52. package/lib/client/worktree-error-copy.js.map +1 -1
  53. package/lib/client/worktree-session.d.ts +83 -0
  54. package/lib/client/worktree-session.d.ts.map +1 -0
  55. package/lib/client/worktree-session.js +185 -0
  56. package/lib/client/worktree-session.js.map +1 -0
  57. package/lib/client/worktree-surface-dialogs.d.ts +7 -0
  58. package/lib/client/worktree-surface-dialogs.d.ts.map +1 -0
  59. package/lib/client/worktree-surface-dialogs.js +86 -0
  60. package/lib/client/worktree-surface-dialogs.js.map +1 -0
  61. package/lib/client/worktree-surface-rows.d.ts +9 -0
  62. package/lib/client/worktree-surface-rows.d.ts.map +1 -0
  63. package/lib/client/worktree-surface-rows.js +223 -0
  64. package/lib/client/worktree-surface-rows.js.map +1 -0
  65. package/lib/client/worktree-surface-selectors.d.ts +9 -0
  66. package/lib/client/worktree-surface-selectors.d.ts.map +1 -0
  67. package/lib/client/worktree-surface-selectors.js +28 -0
  68. package/lib/client/worktree-surface-selectors.js.map +1 -0
  69. package/lib/client/worktree-surface-types.d.ts +233 -0
  70. package/lib/client/worktree-surface-types.d.ts.map +1 -0
  71. package/lib/client/worktree-surface-types.js +2 -0
  72. package/lib/client/worktree-surface-types.js.map +1 -0
  73. package/lib/client/worktree-view-actions.d.ts +34 -0
  74. package/lib/client/worktree-view-actions.d.ts.map +1 -0
  75. package/lib/client/worktree-view-actions.js +69 -0
  76. package/lib/client/worktree-view-actions.js.map +1 -0
  77. package/lib/client/worktree-view-errors.d.ts +20 -0
  78. package/lib/client/worktree-view-errors.d.ts.map +1 -0
  79. package/lib/client/worktree-view-errors.js +65 -0
  80. package/lib/client/worktree-view-errors.js.map +1 -0
  81. package/lib/client/worktree-view-read.d.ts +48 -0
  82. package/lib/client/worktree-view-read.d.ts.map +1 -0
  83. package/lib/client/worktree-view-read.js +134 -0
  84. package/lib/client/worktree-view-read.js.map +1 -0
  85. package/lib/client/worktree-view.d.ts +4 -63
  86. package/lib/client/worktree-view.d.ts.map +1 -1
  87. package/lib/client/worktree-view.js +4 -125
  88. package/lib/client/worktree-view.js.map +1 -1
  89. package/lib/client.js +1313 -363
  90. package/lib/client.js.map +1 -1
  91. package/lib/contract/index.contract.js +2 -0
  92. package/lib/contract/index.contract.js.map +1 -1
  93. package/lib/contract/index.d.ts +17 -2
  94. package/lib/contract/index.d.ts.map +1 -1
  95. package/lib/contract/index.js +2 -0
  96. package/lib/contract/index.js.map +1 -1
  97. package/lib/host/remote.d.ts.map +1 -1
  98. package/lib/host/remote.js +1 -0
  99. package/lib/host/remote.js.map +1 -1
  100. package/lib/host/service.d.ts +6 -1
  101. package/lib/host/service.d.ts.map +1 -1
  102. package/lib/host/service.js +6 -0
  103. package/lib/host/service.js.map +1 -1
  104. package/lib/manage/manager-context.d.ts +11 -0
  105. package/lib/manage/manager-context.d.ts.map +1 -0
  106. package/lib/manage/manager-context.js +2 -0
  107. package/lib/manage/manager-context.js.map +1 -0
  108. package/lib/manage/manager-sessions.d.ts +18 -0
  109. package/lib/manage/manager-sessions.d.ts.map +1 -0
  110. package/lib/manage/manager-sessions.js +91 -0
  111. package/lib/manage/manager-sessions.js.map +1 -0
  112. package/lib/manage/manager-support.d.ts +18 -0
  113. package/lib/manage/manager-support.d.ts.map +1 -0
  114. package/lib/manage/manager-support.js +174 -0
  115. package/lib/manage/manager-support.js.map +1 -0
  116. package/lib/manage/manager-worktrees.d.ts +27 -0
  117. package/lib/manage/manager-worktrees.d.ts.map +1 -0
  118. package/lib/manage/manager-worktrees.js +259 -0
  119. package/lib/manage/manager-worktrees.js.map +1 -0
  120. package/lib/manage/manager.d.ts +25 -62
  121. package/lib/manage/manager.d.ts.map +1 -1
  122. package/lib/manage/manager.js +32 -554
  123. package/lib/manage/manager.js.map +1 -1
  124. package/lib/provider/sidecar-persistence.d.ts +18 -0
  125. package/lib/provider/sidecar-persistence.d.ts.map +1 -0
  126. package/lib/provider/sidecar-persistence.js +162 -0
  127. package/lib/provider/sidecar-persistence.js.map +1 -0
  128. package/lib/provider/sidecar-schema.d.ts +6 -0
  129. package/lib/provider/sidecar-schema.d.ts.map +1 -0
  130. package/lib/provider/sidecar-schema.js +123 -0
  131. package/lib/provider/sidecar-schema.js.map +1 -0
  132. package/lib/provider/sidecar.d.ts +12 -68
  133. package/lib/provider/sidecar.d.ts.map +1 -1
  134. package/lib/provider/sidecar.js +61 -346
  135. package/lib/provider/sidecar.js.map +1 -1
  136. package/lib/provider/types.d.ts +2 -1
  137. package/lib/provider/types.d.ts.map +1 -1
  138. package/lib/provider/types.js.map +1 -1
  139. package/lib/typert.host.js +60 -18
  140. package/lib/typert.remote-client.d.ts +3 -1
  141. package/lib/typert.remote-client.d.ts.map +1 -1
  142. package/lib/typert.remote-client.js +60 -18
  143. package/package.json +6 -2
@@ -1,11 +1,16 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
- import { Button, HoverCard, IconArchiveOutline20, IconBranchOutline16, IconChevronDownOutline14, IconChevronRightOutline14, IconCloseOutline16, IconEditOutline16, IconEllipsisOutline16, IconFolderClose16, IconFolderOpen16, IconPlusOutline16, IconSearchOutline16, IconTrashOutline16, Input, Menu, Modal, StateDot, } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { IconBranchOutline16, IconCloseOutline16, IconPlusOutline16, IconSearchOutline16, } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { openWorktreeSession } from './navigation.js';
5
5
  import { useSidebarOverlayGeometry } from './sidebar-overlay-geometry.js';
6
6
  import { effectiveViewMode, unboundSessionIds, workspaceSessionIds } from './view-mode.js';
7
7
  import { formatWorktreeViewError } from './worktree-error-copy.js';
8
- import { executeWorktreeAction, createDefaultWorktreeName, filterArchivedSessionIds, loadWorktreeViews, selectDefaultBaseBranch, stableWorkspaceIds, toWorktreeViewError, WorktreeSessionBindingError, } from './worktree-view.js';
8
+ import { filterVisibleSessionIds, sessionMatchesQuery, } from './session-view.js';
9
+ import { retryWorktreeSessionBinding } from './worktree-session.js';
10
+ import { WorktreeCreateDialog, WorktreeRemovalDialog, WorktreeSessionRenameDialog, WorktreeWorkspaceDeleteDialog, WorktreeWorkspaceRenameDialog, } from './worktree-surface-dialogs.js';
11
+ import { bindingIdsFor, includesText, workspaceMatches, } from './worktree-surface-selectors.js';
12
+ 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';
9
14
  import styles from './worktree.css';
10
15
  function toNativeWorktreeViewError(error) {
11
16
  const viewError = toWorktreeViewError(error);
@@ -22,230 +27,19 @@ function useStableWorkspaceIds(workspaces) {
22
27
  previousRef.current = stable;
23
28
  return previousRef.current;
24
29
  }
25
- function sessionLabel(sessionId, sessions) {
26
- return sessions.byId[sessionId]?.displayTitle ?? sessionId;
27
- }
28
- function rowHalf(event) {
29
- const rect = event.currentTarget.getBoundingClientRect();
30
- return event.clientY < rect.top + rect.height / 2 ? 'before' : 'after';
31
- }
32
- function bindingIdsFor(bindings, worktreeId) {
33
- return bindings
34
- .filter((binding) => binding.worktreeId === worktreeId)
35
- .map((binding) => binding.sessionId);
36
- }
37
- function includesText(value, query) {
38
- return value.toLocaleLowerCase().includes(query);
39
- }
40
- function workspaceMatches(workspace, view, sessions, query) {
41
- if (query.length === 0)
42
- return true;
43
- if (includesText(workspace.title, query))
44
- return true;
45
- if (workspace.sessionIds.some((sessionId) => includesText(sessionLabel(sessionId, sessions), query))) {
46
- return true;
47
- }
48
- if (view === undefined)
49
- return false;
50
- if (view.worktrees.some((record) => includesText(record.branch, query) || includesText(record.absolutePath, query))) {
51
- return true;
52
- }
53
- return view.bindings.some((binding) => includesText(sessionLabel(binding.sessionId, sessions), query));
54
- }
55
- /** Workspace row using the native DSH menu, fixed action column, and drag contract. */
56
- function WorktreeWorkspaceRow({ t, workspace, expanded, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange, }) {
57
- const workspaceMenuItems = [
58
- {
59
- id: 'rename',
60
- label: t('workspace.rename'),
61
- icon: _jsx(IconEditOutline16, {}),
62
- disabled: actionPending,
63
- },
64
- {
65
- id: 'delete',
66
- label: t('workspace.delete'),
67
- icon: _jsx(IconTrashOutline16, {}),
68
- danger: true,
69
- disabled: actionPending,
70
- },
71
- ];
72
- const markerClass = drag.marker === 'before'
73
- ? styles.dropBefore
74
- : drag.marker === 'after'
75
- ? styles.dropAfter
76
- : '';
77
- return (_jsxs("div", { className: `${styles.workspaceRow} ${markerClass}`, "data-workspace-drag": drag.active ? 'active' : undefined, "data-menu-open": menuOpen || undefined, draggable: true, onClick: () => {
78
- onToggle();
79
- }, onDragStart: (event) => {
80
- event.dataTransfer.effectAllowed = 'move';
81
- event.dataTransfer.setData('text/plain', workspace.workspaceId);
82
- drag.start();
83
- }, onDragEnd: drag.end, onDragOver: (event) => {
84
- if (!drag.active)
85
- return;
86
- event.preventDefault();
87
- event.dataTransfer.dropEffect = 'move';
88
- drag.hover(rowHalf(event));
89
- }, onDrop: (event) => {
90
- if (!drag.active)
91
- return;
92
- event.preventDefault();
93
- drag.drop(rowHalf(event));
94
- }, children: [_jsx("button", { type: "button", className: `${styles.disclosureButton} ${styles.workspaceDisclosure}`, "aria-label": t(expanded ? 'workspace.collapse' : 'workspace.expand', {
95
- name: workspace.title,
96
- }), "aria-expanded": expanded, onClick: (event) => {
97
- event.stopPropagation();
98
- onToggle();
99
- }, children: expanded ? (_jsx(IconChevronDownOutline14, { size: 18 })) : (_jsx(IconChevronRightOutline14, { size: 18 })) }), _jsx("span", { className: styles.workspaceIcon, "aria-hidden": "true", children: expanded ? _jsx(IconFolderOpen16, {}) : _jsx(IconFolderClose16, {}) }), _jsx("span", { className: styles.workspaceTitle, children: workspace.title }), _jsxs("span", { className: `${styles.treeActionSlot} ${styles.workspaceActions}`, children: [_jsx("span", { className: styles.menuAction, children: _jsx(Menu, { open: menuOpen, onClose: () => {
100
- onMenuOpenChange(false);
101
- }, items: workspaceMenuItems, onSelect: (id) => {
102
- onMenuOpenChange(false);
103
- if (id === 'rename')
104
- onRename();
105
- if (id === 'delete')
106
- onDelete();
107
- }, portal: true, closeOnPointerLeave: true, anchor: (_jsx("button", { type: "button", className: styles.iconButton, "data-workspace-menu": true, "aria-label": t('workspace.options', { name: workspace.title }), onClick: (event) => {
108
- event.stopPropagation();
109
- onMenuOpenChange(!menuOpen);
110
- }, children: _jsx(IconEllipsisOutline16, {}) })) }) }), _jsx("button", { type: "button", className: styles.iconButton, "data-add-worktree": true, "aria-label": t('workspace.addWorktree', { name: workspace.title }), onClick: (event) => {
111
- event.stopPropagation();
112
- onCreateWorktree();
113
- }, children: _jsx(IconPlusOutline16, {}) })] })] }));
114
- }
115
- /** Shared Main/Worktree group row with parameterized icon, state, and actions. */
116
- function WorktreeGroupRow({ t, kind, label, expanded, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession, menu, }) {
117
- const main = kind === 'main';
118
- const row = (_jsxs("div", { className: styles.worktreeRow, "data-main-group": main ? 'true' : undefined, "data-main-expanded": main ? String(expanded) : undefined, "data-menu-open": menu?.open ? 'true' : undefined, onClick: onToggle, children: [_jsx("button", { type: "button", className: `${styles.disclosureButton} ${styles.worktreeDisclosure}`, "aria-label": t(expanded ? 'worktree.collapse' : 'worktree.expand', { name: label }), "aria-expanded": expanded, onClick: (event) => {
119
- event.stopPropagation();
120
- onToggle();
121
- }, children: expanded ? (_jsx(IconChevronDownOutline14, { size: 18 })) : (_jsx(IconChevronRightOutline14, { size: 18 })) }), _jsx("span", { className: styles.worktreeIcon, "aria-hidden": "true", children: icon }), state !== undefined && stateLabel !== undefined && (_jsx("span", { className: styles.worktreeState, role: "img", "aria-label": stateLabel, title: stateLabel, children: _jsx(StateDot, { state: state }) })), _jsx("span", { className: styles.worktreeLabel, children: label }), _jsxs("span", { className: styles.treeActionSlot, children: [menu !== undefined && (_jsx("span", { className: styles.menuAction, children: _jsx(Menu, { open: menu.open, onClose: () => {
122
- menu.onOpenChange(false);
123
- }, items: [
124
- {
125
- id: 'remove',
126
- label: t('worktree.remove'),
127
- icon: _jsx(IconTrashOutline16, {}),
128
- danger: true,
129
- disabled: menu.disabled,
130
- },
131
- ], onSelect: (id) => {
132
- menu.onOpenChange(false);
133
- if (id === 'remove')
134
- menu.onRemove();
135
- }, portal: true, closeOnPointerLeave: true, anchor: _jsx("button", { type: "button", className: styles.iconButton, "data-worktree-menu": true, "aria-label": t('worktree.options', { name: menu.label }), onClick: (event) => {
136
- event.stopPropagation();
137
- menu.onOpenChange(!menu.open);
138
- }, children: _jsx(IconEllipsisOutline16, {}) }) }) })), onCreateSession !== undefined && (_jsx("button", { type: "button", className: styles.iconButton, "data-add-main-session": main ? 'true' : undefined, "data-add-session": main ? undefined : 'true', "aria-label": t('worktree.addSession', { name: workspaceTitle }), onClick: (event) => {
139
- event.stopPropagation();
140
- onCreateSession();
141
- }, children: _jsx(IconPlusOutline16, {}) }))] })] }));
142
- if (main)
143
- return row;
144
- return (_jsx(HoverCard, { anchor: row, content: _jsx("div", { className: styles.worktreeHoverTitle, children: label }), openDelayMs: 500, disabled: menu?.open === true }));
145
- }
146
- /** Worktree-mode Session row with the same trailing options menu as native DSH rows. */
147
- function WorktreeSessionRow({ t, sessionId, label, drag, actionPending, onOpen, onRename, onFork, onArchive, }) {
148
- const [menuOpen, setMenuOpen] = useState(false);
149
- const markerClass = drag.marker === 'before'
150
- ? styles.dropBefore
151
- : drag.marker === 'after'
152
- ? styles.dropAfter
153
- : '';
154
- const sessionMenuItems = [
155
- {
156
- id: 'rename',
157
- label: t('session.rename'),
158
- icon: _jsx(IconEditOutline16, {}),
159
- disabled: onRename === undefined || actionPending,
160
- },
161
- {
162
- id: 'fork',
163
- label: t('session.fork'),
164
- icon: _jsx(IconBranchOutline16, {}),
165
- disabled: onFork === undefined || actionPending,
166
- },
167
- {
168
- id: 'archive',
169
- label: t('session.archive'),
170
- icon: _jsx(IconArchiveOutline20, { size: 16 }),
171
- disabled: onArchive === undefined || actionPending,
172
- },
173
- ];
174
- return (_jsxs("div", { className: `${styles.treeSessionRow} ${markerClass}`, "data-session-id": sessionId, "data-session-drag": drag.active ? 'active' : undefined, "data-menu-open": menuOpen || undefined, role: "treeitem", draggable: true, onDragStart: (event) => {
175
- event.dataTransfer.effectAllowed = 'move';
176
- event.dataTransfer.setData('text/plain', sessionId);
177
- drag.start();
178
- }, onDragEnd: drag.end, onDragOver: (event) => {
179
- if (!drag.active)
180
- return;
181
- event.preventDefault();
182
- event.dataTransfer.dropEffect = 'move';
183
- drag.hover(rowHalf(event));
184
- }, onDrop: (event) => {
185
- if (!drag.active)
186
- return;
187
- event.preventDefault();
188
- drag.drop(rowHalf(event));
189
- }, children: [_jsxs("button", { type: "button", className: styles.treeSessionContent, onClick: onOpen, children: [_jsx("span", { className: styles.treeGuide, "aria-hidden": "true", children: "\u2514" }), _jsx("span", { className: styles.sessionLabel, children: label })] }), _jsx("span", { className: styles.rowActions, children: _jsx(Menu, { open: menuOpen, onClose: () => {
190
- setMenuOpen(false);
191
- }, items: sessionMenuItems, onSelect: (id) => {
192
- setMenuOpen(false);
193
- if (id === 'rename')
194
- onRename?.(sessionId, label);
195
- if (id === 'fork')
196
- onFork?.(sessionId);
197
- if (id === 'archive')
198
- onArchive?.(sessionId);
199
- }, portal: true, closeOnPointerLeave: true, anchor: (_jsx("button", { type: "button", className: styles.iconButton, "data-session-menu": true, "aria-label": t('session.options', { name: label }), onClick: (event) => {
200
- event.stopPropagation();
201
- setMenuOpen((current) => !current);
202
- }, children: _jsx(IconEllipsisOutline16, {}) })) }) })] }));
203
- }
204
- function WorktreeSessionGroup({ t, groupKey, sessionIds, workspaceId, expanded, actionPending, sessions, dragState, onToggleExpanded, onStartDrag, onHoverDrag, onClearDrag, onFinishDrag, onCommitDrag, onOpen, onRename, onFork, onArchive, }) {
205
- const visibleSessionIds = expanded ? sessionIds : sessionIds.slice(0, 5);
206
- const sameGroupDrag = dragState?.groupKey === groupKey;
207
- return (_jsxs(_Fragment, { children: [visibleSessionIds.map((sessionId) => (_jsx(WorktreeSessionRow, { t: t, sessionId: sessionId, label: sessionLabel(sessionId, sessions), drag: {
208
- active: sameGroupDrag,
209
- marker: sameGroupDrag && dragState.over?.sessionId === sessionId
210
- ? dragState.over.half
211
- : null,
212
- start: () => {
213
- onStartDrag(groupKey, sessionId);
214
- },
215
- hover: (half) => {
216
- onHoverDrag(sessionId, half);
217
- },
218
- drop: (half) => {
219
- if (dragState === undefined)
220
- return;
221
- onCommitDrag(dragState, { sessionId, half }, sessionIds, workspaceId);
222
- },
223
- end: () => {
224
- if (dragState?.over !== null && dragState?.over !== undefined) {
225
- onCommitDrag(dragState, dragState.over, sessionIds, workspaceId);
226
- }
227
- else {
228
- onClearDrag();
229
- }
230
- onFinishDrag();
231
- },
232
- }, actionPending: actionPending, onOpen: () => {
233
- onOpen(sessionId);
234
- }, onRename: onRename, onFork: onFork, onArchive: onArchive }, `${groupKey}:${sessionId}`))), sessionIds.length > 5 && (_jsx("button", { type: "button", className: styles.sessionOverflowButton, "aria-expanded": expanded, onClick: onToggleExpanded, children: expanded
235
- ? t('session.collapse')
236
- : t('session.expandMore', { count: sessionIds.length - 5 }) }))] }));
237
- }
238
30
  /**
239
31
  * Peer Worktree navigation surface. It keeps the original DSH Workspace mode
240
32
  * intact and renders a browser-local Workspace → Worktree → Session projection.
241
33
  */
242
- export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, manager, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession, }) {
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, }) {
243
35
  const preferredMode = useStore((state) => state.viewMode);
244
36
  const mode = effectiveViewMode(preferredMode, available && manager !== undefined);
245
37
  const sessions = useSessions((state) => state);
246
38
  const workspaces = useWorkspaces((state) => state);
247
39
  const workspaceIds = useStableWorkspaceIds(workspaces.items);
248
40
  const [readState, setReadState] = useState(EMPTY_READ_STATE);
41
+ const readStateRef = useRef(readState);
42
+ readStateRef.current = readState;
249
43
  const [searchQuery, setSearchQuery] = useState('');
250
44
  const [expandedWorkspaces, setExpandedWorkspaces] = useState({});
251
45
  const [expandedMains, setExpandedMains] = useState({});
@@ -275,33 +69,46 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
275
69
  const [sessionDrag, setSessionDrag] = useState();
276
70
  const [expandedSessionGroups, setExpandedSessionGroups] = useState({});
277
71
  const sessionDropCommitted = useRef(false);
72
+ const [worktreeDrag, setWorktreeDrag] = useState();
73
+ const worktreeDropCommitted = useRef(false);
74
+ const refreshGuard = useRef(createWorktreeRefreshGuard());
278
75
  const { ref, width, bounds } = useSidebarOverlayGeometry(mode === 'worktree');
279
76
  const collapsed = width <= 64;
280
77
  const query = searchQuery.trim().toLocaleLowerCase();
281
78
  const archivedSessionIds = workspaces.archivedSessionIds ?? [];
282
- const refresh = useCallback(async () => {
79
+ const pendingSessionArchived = pendingSessionBinding !== undefined &&
80
+ archivedSessionIds.includes(pendingSessionBinding.sessionId);
81
+ const refresh = useCallback(async (options = {}) => {
283
82
  if (manager === undefined) {
83
+ refreshGuard.current.invalidate();
284
84
  setReadState(EMPTY_READ_STATE);
285
85
  return;
286
86
  }
287
- setReadState({ status: 'loading', views: [] });
288
- try {
289
- const views = await loadWorktreeViews(manager, workspaceIds);
290
- setReadState({ status: 'ready', views });
87
+ const preserveCurrent = options.preserveCurrent === true;
88
+ if (!preserveCurrent) {
89
+ setReadState({ status: 'loading', views: [] });
291
90
  }
292
- catch (error) {
91
+ await refreshGuard.current.run(() => loadWorktreeViews(manager, workspaceIds, {
92
+ invalidateContext: options.invalidateContext !== false,
93
+ invalidateWorktreeContext,
94
+ }), (views) => {
95
+ setReadState({ status: 'ready', views });
96
+ }, (error) => {
97
+ if (preserveCurrent)
98
+ throw error;
293
99
  setReadState({
294
100
  status: 'error',
295
101
  views: [],
296
102
  error: toWorktreeViewError(error),
297
103
  });
298
- }
299
- }, [manager, workspaceIds]);
104
+ });
105
+ }, [invalidateWorktreeContext, manager, workspaceIds]);
300
106
  useEffect(() => {
301
107
  if (mode === 'worktree') {
302
- void refresh();
108
+ void refresh({ preserveCurrent: readStateRef.current.status === 'ready' });
303
109
  }
304
110
  else {
111
+ refreshGuard.current.invalidate();
305
112
  setReadState(EMPTY_READ_STATE);
306
113
  }
307
114
  }, [mode, refresh]);
@@ -318,12 +125,28 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
318
125
  const modalView = worktreeModalWorkspaceId === undefined
319
126
  ? undefined
320
127
  : viewByWorkspace.get(worktreeModalWorkspaceId);
128
+ const modalReadiness = modalView?.readiness;
129
+ const modalSetupStatus = modalView === undefined
130
+ ? undefined
131
+ : modalReadiness?.status === 'ready'
132
+ ? modalView.branches.length === 0 ? 'noLocalBranch' : undefined
133
+ : modalReadiness?.status;
134
+ const modalCanCreate = modalSetupStatus === undefined && modalReadiness?.status === 'ready';
135
+ useEffect(() => {
136
+ if (worktreeModalWorkspaceId === undefined || modalView === undefined)
137
+ return;
138
+ if (modalView.readiness.status !== 'ready') {
139
+ setSelectedBranch('');
140
+ return;
141
+ }
142
+ setSelectedBranch((current) => reconcileBaseBranchSelection(current, modalView.branches));
143
+ }, [modalView, worktreeModalWorkspaceId]);
321
144
  const runMutation = async (operation) => {
322
145
  setActionPending(true);
323
146
  setActionError(undefined);
324
147
  try {
325
148
  await operation();
326
- await refresh();
149
+ await refresh({ preserveCurrent: true });
327
150
  }
328
151
  catch (error) {
329
152
  setActionError(toWorktreeViewError(error));
@@ -483,6 +306,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
483
306
  setActionError(toWorktreeViewError(error));
484
307
  });
485
308
  };
309
+ const commitWorktreeDrag = (activeDrag, over, worktreeIds, workspaceId) => {
310
+ if (worktreeDropCommitted.current)
311
+ return;
312
+ worktreeDropCommitted.current = true;
313
+ setWorktreeDrag(undefined);
314
+ if (activeDrag.workspaceId !== workspaceId)
315
+ return;
316
+ const move = resolveWorktreeMove(worktreeIds, activeDrag.worktreeId, over.worktreeId, over.half);
317
+ if (move === undefined)
318
+ return;
319
+ if (insertWorktreeBefore === undefined) {
320
+ setActionError({
321
+ code: 'WORKTREE_ORDER_UNAVAILABLE',
322
+ message: '',
323
+ retryable: true,
324
+ });
325
+ return;
326
+ }
327
+ setActionError(undefined);
328
+ void insertWorktreeBefore(workspaceId, activeDrag.worktreeId, move.beforeWorktreeId)
329
+ .then(() => refresh({ preserveCurrent: true, invalidateContext: false }))
330
+ .catch((error) => {
331
+ setActionError(toRetryableWorktreeOrderError(error));
332
+ });
333
+ };
486
334
  const openSessionRename = (sessionId, currentTitle) => {
487
335
  setSessionRenameTarget({ sessionId, currentTitle });
488
336
  setSessionRenameDraft(currentTitle);
@@ -559,6 +407,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
559
407
  const worktreeName = newBranch.trim();
560
408
  if (manager === undefined ||
561
409
  modalWorkspace === undefined ||
410
+ !modalCanCreate ||
562
411
  selectedBranch.length === 0 ||
563
412
  worktreeName.length === 0) {
564
413
  return;
@@ -585,7 +434,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
585
434
  }
586
435
  setWorktreeModalWorkspaceId(undefined);
587
436
  if (createSessionCallback === undefined) {
588
- await refresh();
437
+ await refresh({ preserveCurrent: true });
589
438
  setActionError({
590
439
  code: 'WORKTREE_CREATED_SESSION_UNAVAILABLE',
591
440
  message: '',
@@ -600,14 +449,15 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
600
449
  };
601
450
  try {
602
451
  await createSessionCallback(sessionInput);
452
+ await invalidateWorktreeContext?.(createdWorktree.workspaceId);
603
453
  }
604
454
  catch (error) {
605
- if (error instanceof WorktreeSessionBindingError) {
455
+ if (error instanceof WorktreeSessionBindingError && error.retryable) {
606
456
  setPendingSessionBinding({ ...sessionInput, sessionId: error.sessionId });
607
457
  }
608
458
  throw error;
609
459
  }
610
- await refresh();
460
+ await refresh({ preserveCurrent: true });
611
461
  }
612
462
  catch (error) {
613
463
  setActionError(toWorktreeViewError(error));
@@ -630,10 +480,11 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
630
480
  setPendingSessionBinding(undefined);
631
481
  try {
632
482
  await createSessionCallback(input);
633
- await refresh();
483
+ await invalidateWorktreeContext?.(input.workspaceId);
484
+ await refresh({ preserveCurrent: true });
634
485
  }
635
486
  catch (error) {
636
- if (error instanceof WorktreeSessionBindingError) {
487
+ if (error instanceof WorktreeSessionBindingError && error.retryable) {
637
488
  setPendingSessionBinding({ ...input, sessionId: error.sessionId });
638
489
  }
639
490
  setActionError(toWorktreeViewError(error));
@@ -643,23 +494,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
643
494
  }
644
495
  };
645
496
  const retrySessionBinding = async () => {
646
- if (manager === undefined || pendingSessionBinding === undefined)
497
+ if (manager === undefined ||
498
+ pendingSessionBinding === undefined ||
499
+ pendingSessionArchived)
647
500
  return;
501
+ const pending = pendingSessionBinding;
648
502
  setActionPending(true);
649
503
  setActionError(undefined);
650
504
  try {
651
- await manager.bindSession({
652
- workspaceId: pendingSessionBinding.workspaceId,
653
- worktreeId: pendingSessionBinding.worktreeId,
654
- sessionId: pendingSessionBinding.sessionId,
505
+ await retryWorktreeSessionBinding({
506
+ manager,
507
+ pending,
508
+ archived: false,
509
+ ensureSessionWorkspace: (workspaceId, sessionId) => {
510
+ ensureSessionWorkspace?.(workspaceId, sessionId);
511
+ },
512
+ openSession,
655
513
  });
656
- const sessionId = pendingSessionBinding.sessionId;
657
514
  setPendingSessionBinding(undefined);
658
- ensureSessionWorkspace?.(pendingSessionBinding.workspaceId, sessionId);
659
- await refresh();
660
- openSession(sessionId);
515
+ await refresh({ preserveCurrent: true });
516
+ await invalidateWorktreeContext?.(pending.workspaceId);
661
517
  }
662
518
  catch (error) {
519
+ if (error instanceof WorktreeSessionBindingError && !error.retryable) {
520
+ setPendingSessionBinding(undefined);
521
+ }
663
522
  setActionError(toWorktreeViewError(error));
664
523
  }
665
524
  finally {
@@ -685,7 +544,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
685
544
  } }), _jsx("button", { type: "button", className: styles.iconButton, "aria-label": t('workspace.add'), onClick: () => {
686
545
  if (createWorkspace !== undefined)
687
546
  void runMutation(createWorkspace);
688
- }, children: _jsx(IconPlusOutline16, {}) })] }), _jsxs("div", { className: styles.content, tabIndex: 0, children: [actionError !== undefined && (_jsxs("div", { className: styles.error, role: "alert", "data-worktree-error": true, children: [_jsx("p", { className: styles.message, "data-error": "true", children: formatWorktreeViewError(actionError, t) }), pendingSessionBinding !== undefined && (_jsxs("div", { className: styles.recoveryActions, children: [_jsx("button", { type: "button", className: styles.actionButton, disabled: actionPending, onClick: () => {
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: () => {
689
548
  void retrySessionBinding();
690
549
  }, children: t('action.retryBinding') }), _jsx("button", { type: "button", className: styles.actionButton, disabled: actionPending, onClick: () => {
691
550
  const sessionId = pendingSessionBinding.sessionId;
@@ -699,19 +558,21 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
699
558
  void refresh();
700
559
  }, children: t('action.retry') })] })) : readState.status === 'ready' ? (_jsx("div", { className: styles.workspaceList, children: visibleWorkspaces.length === 0 ? (_jsx("p", { className: styles.empty, children: t('workspace.noMatches') })) : (visibleWorkspaces.map((workspace) => {
701
560
  const view = viewByWorkspace.get(workspace.workspaceId);
561
+ const currentBranch = view?.branches.find((branch) => branch.isCurrent)?.name;
562
+ const mainLabel = currentBranch === undefined
563
+ ? t('worktree.main')
564
+ : t('worktree.mainWithBranch', { branch: currentBranch });
702
565
  const expanded = expandedWorkspaces[workspace.workspaceId] !== false;
703
566
  const workspaceMatchesQuery = includesText(workspace.title, query);
704
567
  const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
705
568
  const bindings = view?.bindings ?? [];
706
569
  const boundSessionIds = new Set(bindings.map((binding) => binding.sessionId));
707
- const mainSessionIds = unboundSessionIds(allWorkspaceSessionIds, [
708
- ...boundSessionIds,
709
- ]);
710
- const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery ||
711
- includesText(sessionLabel(sessionId, sessions), query));
570
+ const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...boundSessionIds]), sessions);
571
+ const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
712
572
  const mainExpanded = expandedMains[workspace.workspaceId] !== false;
713
573
  const mainGroupKey = `main:${workspace.workspaceId}`;
714
574
  const worktrees = view?.worktrees ?? [];
575
+ const sameWorkspaceWorktreeDrag = worktreeDrag?.workspaceId === workspace.workspaceId;
715
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: {
716
577
  active: workspaceDrag !== undefined,
717
578
  marker: workspaceDrag?.over?.workspaceId === workspace.workspaceId
@@ -756,7 +617,7 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
756
617
  openWorkspaceDelete(workspace);
757
618
  }, onMenuOpenChange: (open) => {
758
619
  setOpenWorkspaceMenuId(open ? workspace.workspaceId : undefined);
759
- } }), expanded && (_jsxs("div", { className: styles.treeChildren, children: [_jsx(WorktreeGroupRow, { t: t, kind: "main", label: t('worktree.main'), expanded: mainExpanded, icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, onToggle: () => {
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: () => {
760
621
  toggleMain(workspace.workspaceId);
761
622
  }, onCreateSession: createMainSession === undefined
762
623
  ? undefined
@@ -781,17 +642,16 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
781
642
  }, onCommitDrag: commitSessionDrag, onOpen: (sessionId) => {
782
643
  openWorktreeSession({ open: openSession }, sessionId);
783
644
  }, onRename: renameSession === undefined ? undefined : openSessionRename, onFork: forkSession, onArchive: archiveSession === undefined ? undefined : archiveWorktreeSession })), worktrees.length === 0 && (_jsx("p", { className: styles.emptyNested, children: t('worktree.noWorktrees') })), worktrees.map((record) => {
784
- const worktreeSessionIds = filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds);
645
+ const worktreeSessionIds = filterVisibleSessionIds(filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds), sessions);
785
646
  const worktreeMatchesQuery = workspaceMatchesQuery ||
786
647
  includesText(record.branch, query) ||
787
648
  includesText(record.absolutePath, query);
788
649
  if (query.length > 0 &&
789
650
  !worktreeMatchesQuery &&
790
- !worktreeSessionIds.some((sessionId) => includesText(sessionLabel(sessionId, sessions), query))) {
651
+ !worktreeSessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) {
791
652
  return null;
792
653
  }
793
- const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery ||
794
- includesText(sessionLabel(sessionId, sessions), query));
654
+ const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
795
655
  const worktreeGroupKey = `worktree:${record.worktreeId}`;
796
656
  const state = record.status === 'removed'
797
657
  ? 'warning'
@@ -804,9 +664,9 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
804
664
  ? t('worktree.repair')
805
665
  : t('worktree.ready');
806
666
  const worktreeExpanded = expandedWorktrees[record.worktreeId] !== false;
807
- return (_jsxs("div", { className: styles.worktreeGroup, "data-worktree-id": record.worktreeId, children: [_jsx(WorktreeGroupRow, { t: t, kind: "worktree", label: record.branch, expanded: worktreeExpanded, icon: _jsx(IconBranchOutline16, {}), workspaceTitle: workspace.title, state: state, stateLabel: stateLabel, onToggle: () => {
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: () => {
808
668
  toggleWorktree(record.worktreeId);
809
- }, onCreateSession: record.status === 'active'
669
+ }, onCreateSession: record.status === 'active' && record.health !== 'repair'
810
670
  ? () => {
811
671
  void createSession({
812
672
  workspaceId: record.workspaceId,
@@ -827,7 +687,38 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
827
687
  setActionError(undefined);
828
688
  },
829
689
  }
830
- : undefined }), worktreeExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: worktreeGroupKey, sessionIds: visibleWorktreeSessionIds, workspaceId: workspace.workspaceId, expanded: expandedSessionGroups[worktreeGroupKey] === true, actionPending: actionPending, sessions: sessions, dragState: sessionDrag, onToggleExpanded: () => {
690
+ : undefined, drag: {
691
+ active: sameWorkspaceWorktreeDrag,
692
+ marker: worktreeDrag?.over?.worktreeId === record.worktreeId
693
+ ? worktreeDrag.over.half
694
+ : null,
695
+ start: () => {
696
+ worktreeDropCommitted.current = false;
697
+ setWorktreeDrag({
698
+ workspaceId: workspace.workspaceId,
699
+ worktreeId: record.worktreeId,
700
+ over: null,
701
+ });
702
+ },
703
+ hover: (half) => {
704
+ setWorktreeDrag((current) => current === undefined ||
705
+ current.workspaceId !== workspace.workspaceId
706
+ ? current
707
+ : {
708
+ ...current,
709
+ over: { worktreeId: record.worktreeId, half },
710
+ });
711
+ },
712
+ drop: (half) => {
713
+ if (worktreeDrag === undefined)
714
+ return;
715
+ commitWorktreeDrag(worktreeDrag, { worktreeId: record.worktreeId, half }, worktrees.map((candidate) => candidate.worktreeId), workspace.workspaceId);
716
+ },
717
+ end: () => {
718
+ setWorktreeDrag(undefined);
719
+ worktreeDropCommitted.current = false;
720
+ },
721
+ } }), worktreeExpanded && (_jsx(WorktreeSessionGroup, { t: t, groupKey: worktreeGroupKey, sessionIds: visibleWorktreeSessionIds, workspaceId: workspace.workspaceId, expanded: expandedSessionGroups[worktreeGroupKey] === true, actionPending: actionPending, sessions: sessions, dragState: sessionDrag, onToggleExpanded: () => {
831
722
  setExpandedSessionGroups((current) => ({
832
723
  ...current,
833
724
  [worktreeGroupKey]: current[worktreeGroupKey] !== true,
@@ -851,66 +742,31 @@ export function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces,
851
742
  })] }))] }, workspace.workspaceId));
852
743
  })) })) : null] })] }), _jsx("div", { className: styles.railContent, children: _jsx("button", { type: "button", className: styles.railButton, "aria-label": t('mode.exit'), onClick: () => {
853
744
  actions.setViewMode('workspace-session');
854
- }, children: "WT" }) }), _jsxs(Modal, { open: sessionRenameTarget !== undefined, onClose: closeSessionRename, closeLabel: t('dialog.close'), title: t('session.rename'), footer: (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: sessionRenamePending, onClick: closeSessionRename, children: t('dialog.cancel') }), _jsx(Button, { variant: "primary", disabled: sessionRenamePending || sessionRenameDraft.trim().length === 0, onClick: () => {
855
- void confirmSessionRename();
856
- }, children: t('dialog.rename') })] })), children: [_jsx(Input, { className: styles.renameInput, value: sessionRenameDraft, "aria-label": t('session.name'), autoFocus: true, disabled: sessionRenamePending, onFocus: (event) => {
857
- event.currentTarget.select();
858
- }, onChange: (event) => {
859
- setSessionRenameDraft(event.currentTarget.value);
860
- setSessionRenameError(undefined);
861
- }, onKeyDown: (event) => {
862
- if (event.key === 'Enter' && !event.nativeEvent.isComposing) {
863
- event.preventDefault();
864
- void confirmSessionRename();
865
- }
866
- } }), sessionRenameError !== undefined && (_jsx("p", { className: styles.renameError, role: "alert", children: formatWorktreeViewError(sessionRenameError, t) }))] }), workspaceRenameTarget !== undefined && (_jsxs(Modal, { open: workspaceRenameTarget !== undefined, onClose: closeWorkspaceRename, closeLabel: t('dialog.closeWorkspaceRename'), title: t('workspace.renameTitle'), footer: (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: workspaceRenamePending, onClick: closeWorkspaceRename, children: t('dialog.cancel') }), _jsx(Button, { variant: "primary", disabled: workspaceRenameBlocked, onClick: () => {
867
- void confirmWorkspaceRename();
868
- }, children: t('dialog.rename') })] })), children: [_jsx(Input, { className: styles.renameInput, value: workspaceRenameDraft, "aria-label": t('workspace.name'), autoFocus: true, disabled: workspaceRenamePending, onFocus: (event) => {
869
- event.currentTarget.select();
870
- }, onChange: (event) => {
871
- setWorkspaceRenameDraft(event.currentTarget.value);
872
- setWorkspaceRenameError(undefined);
873
- }, onKeyDown: (event) => {
874
- if (event.key === 'Enter' && !event.nativeEvent.isComposing) {
875
- event.preventDefault();
876
- void confirmWorkspaceRename();
877
- }
878
- } }), workspaceRenameDuplicate && (_jsx("p", { className: styles.renameError, role: "alert", children: t('workspace.duplicate') })), workspaceRenameError !== undefined && (_jsx("p", { className: styles.renameError, role: "alert", children: formatWorktreeViewError(workspaceRenameError, t) }))] })), workspaceDeleteTarget !== undefined && (_jsx(Modal, { open: workspaceDeleteTarget !== undefined, onClose: closeWorkspaceDelete, closeLabel: t('dialog.closeWorkspaceDelete'), title: t('workspace.deleteTitle'), description: t('workspace.deleteDescription', { name: workspaceDeleteTarget.title }), footer: (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: workspaceDeletePending, onClick: closeWorkspaceDelete, children: t('dialog.cancel') }), _jsx(Button, { variant: "outline", disabled: workspaceDeletePending, onClick: () => {
879
- void confirmWorkspaceDelete();
880
- }, children: t('dialog.delete') })] })), children: workspaceDeleteError !== undefined && (_jsx("p", { className: styles.renameError, role: "alert", children: formatWorktreeViewError(workspaceDeleteError, t) })) })), worktreeModalWorkspaceId !== undefined && modalWorkspace !== undefined && (_jsxs(Modal, { open: worktreeModalWorkspaceId !== undefined, onClose: () => {
881
- if (actionPending)
882
- return;
745
+ }, children: "WT" }) }), _jsx(WorktreeSessionRenameDialog, { t: t, target: sessionRenameTarget, draft: sessionRenameDraft, pending: sessionRenamePending, error: sessionRenameError, onClose: closeSessionRename, onDraftChange: (draft) => {
746
+ setSessionRenameDraft(draft);
747
+ setSessionRenameError(undefined);
748
+ }, onSubmit: confirmSessionRename }), _jsx(WorktreeWorkspaceRenameDialog, { t: t, target: workspaceRenameTarget, draft: workspaceRenameDraft, pending: workspaceRenamePending, duplicate: workspaceRenameDuplicate, error: workspaceRenameError, onClose: closeWorkspaceRename, onDraftChange: (draft) => {
749
+ setWorkspaceRenameDraft(draft);
750
+ setWorkspaceRenameError(undefined);
751
+ }, 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: () => {
883
752
  setWorktreeModalWorkspaceId(undefined);
884
- }, closeLabel: t('dialog.closeWorktreeCreate'), title: t('worktree.createTitle'), description: t('worktree.createDescription', { name: modalWorkspace.title }), footer: (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: actionPending, onClick: () => {
885
- setWorktreeModalWorkspaceId(undefined);
886
- }, children: t('dialog.cancel') }), _jsx(Button, { variant: "primary", disabled: actionPending ||
887
- selectedBranch.length === 0 ||
888
- newBranch.trim().length === 0, onClick: () => {
889
- void submitWorktree();
890
- }, children: t('worktree.create') })] })), children: [_jsxs("label", { className: styles.modalField, children: [t('worktree.baseBranch'), _jsxs("select", { className: styles.actionSelect, "aria-label": t('worktree.baseBranch'), value: selectedBranch, disabled: actionPending, onChange: (event) => {
891
- setSelectedBranch(event.currentTarget.value);
892
- }, children: [_jsx("option", { value: "", children: t('worktree.noLocalBranch') }), (modalView?.branches ?? []).map((branch) => (_jsxs("option", { value: branch.name, children: [branch.name, branch.isCurrent ? t('branch.current') : '', branch.checkedOut ? t('branch.checkedOut') : ''] }, branch.name)))] })] }), _jsxs("label", { className: styles.modalField, children: [t('worktree.name'), _jsx(Input, { className: styles.renameInput, "aria-label": t('worktree.name'), value: newBranch, placeholder: "dsh/12345678", disabled: actionPending, onChange: (event) => {
893
- setNewBranch(event.currentTarget.value);
894
- } })] }), modalView?.branches.length === 0 && (_jsx("p", { className: styles.empty, children: t('worktree.noBranches') }))] })), worktreeRemoval !== undefined && (_jsx(Modal, { open: worktreeRemoval !== undefined, onClose: () => {
895
- if (actionPending)
896
- return;
753
+ }, onSelectedBranchChange: setSelectedBranch, onNewBranchChange: setNewBranch, onSubmit: submitWorktree }), _jsx(WorktreeRemovalDialog, { t: t, worktree: worktreeRemoval, actionPending: actionPending, onClose: () => {
897
754
  setWorktreeRemoval(undefined);
898
- }, closeLabel: t('dialog.closeWorktreeRemove'), title: t('worktree.remove'), description: t('worktree.removeDescription', { name: worktreeRemoval.branch }), footer: (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: actionPending, onClick: () => {
899
- setWorktreeRemoval(undefined);
900
- }, children: t('dialog.cancel') }), _jsx(Button, { variant: "primary", disabled: actionPending, onClick: () => {
901
- if (manager === undefined)
902
- return;
903
- const target = worktreeRemoval;
904
- void runMutation(async () => {
905
- await executeWorktreeAction(manager, {
906
- type: 'removeWorktree',
907
- input: {
908
- workspaceId: target.workspaceId,
909
- worktreeId: target.worktreeId,
910
- },
911
- });
912
- setWorktreeRemoval(undefined);
913
- });
914
- }, children: t('worktree.remove') })] })) }))] }));
755
+ }, onSubmit: () => {
756
+ if (manager === undefined || worktreeRemoval === undefined)
757
+ return;
758
+ const target = worktreeRemoval;
759
+ void runMutation(async () => {
760
+ await executeWorktreeAction(manager, {
761
+ type: 'removeWorktree',
762
+ input: {
763
+ workspaceId: target.workspaceId,
764
+ worktreeId: target.worktreeId,
765
+ },
766
+ });
767
+ await invalidateWorktreeContext?.(target.workspaceId);
768
+ setWorktreeRemoval(undefined);
769
+ });
770
+ } })] }));
915
771
  }
916
772
  //# sourceMappingURL=WorktreeSurface.js.map