@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
package/lib/client.js CHANGED
@@ -40,12 +40,22 @@ window.__ModuleLoader__.load({
40
40
  "worktree.expand": "展开 {name}",
41
41
  "worktree.collapse": "收起 {name}",
42
42
  "worktree.options": "Worktree 选项:{name}",
43
+ "worktree.copyPath": "复制路径",
44
+ "worktree.createNew": "创建新的 Worktree",
43
45
  "worktree.remove": "移除 Worktree",
44
46
  "worktree.removeDescription": "移除“{name}”?Session 会保留在 DSH 中,并作为已分离关系继续可用。",
47
+ "worktree.removeExternalDescription": "移除“{name}”?这会删除关联的外部 Worktree 目录。Session 会保留在 DSH 中,并作为已分离关系继续可用。",
45
48
  "worktree.addSession": "向 {name} 添加会话",
46
49
  "worktree.noWorktrees": "暂无 Worktree",
47
50
  "worktree.createTitle": "新建 Worktree",
48
51
  "worktree.createDescription": "“{name}” · Worktree 路径由 DSH 管理。",
52
+ "worktree.tabCreate": "创建",
53
+ "worktree.tabImport": "导入",
54
+ "worktree.importDescription": "向“{name}”注册已有的 Worktree。注册不会移动目录;之后移除会删除关联的 Worktree 目录。",
55
+ "worktree.importLoading": "正在查找可导入的 Worktree…",
56
+ "worktree.importEmpty": "没有可导入的 Worktree。",
57
+ "worktree.importPlaceholder": "请选择要导入的 Worktree",
58
+ "worktree.importSelected": "已选择",
49
59
  "worktree.baseBranch": "Worktree 基线分支",
50
60
  "worktree.noLocalBranch": "没有本地分支",
51
61
  "worktree.noBranches": "此工作区中没有找到本地分支。",
@@ -56,6 +66,7 @@ window.__ModuleLoader__.load({
56
66
  "worktree.setup.commands": "可复制的命令",
57
67
  "worktree.name": "Worktree 名称",
58
68
  "worktree.create": "创建 Worktree",
69
+ "worktree.import": "导入 Worktree",
59
70
  "session.options": "会话选项:{name}",
60
71
  "session.new": "新会话",
61
72
  "session.rename": "重命名会话",
@@ -64,6 +75,23 @@ window.__ModuleLoader__.load({
64
75
  "session.expandMore": "展开其余 {count} 个会话",
65
76
  "session.collapse": "收起",
66
77
  "session.name": "会话名称",
78
+ "session.status.running": "进行中",
79
+ "session.status.subagentsRunning.one": "{n} 个子代理运行中",
80
+ "session.status.subagentsRunning.other": "{n} 个子代理运行中",
81
+ "session.status.idle": "空闲",
82
+ "session.status.waitingApproval": "等待审批",
83
+ "session.status.planReview": "计划待审",
84
+ "session.status.waitingAnswer": "等待回答",
85
+ "session.status.completed": "已完成",
86
+ copy: "复制",
87
+ "hover.copied": "已复制",
88
+ "session.time.now": "刚刚",
89
+ "session.time.minutes": "{n}分钟",
90
+ "session.time.hours": "{n}小时",
91
+ "session.time.days": "{n}天",
92
+ "session.time.months": "{n}个月",
93
+ "session.time.years": "{n}年",
94
+ "session.time.ago": "{t}前",
67
95
  "dialog.close": "关闭",
68
96
  "dialog.closeWorkspaceRename": "关闭重命名工作区对话框",
69
97
  "dialog.closeWorkspaceDelete": "关闭删除工作区对话框",
@@ -75,6 +103,14 @@ window.__ModuleLoader__.load({
75
103
  "action.retry": "重试",
76
104
  "action.retryBinding": "重试绑定",
77
105
  "action.openCreatedSession": "打开已创建的 Session",
106
+ "permission.fallbackWorkspaceWrite": "无法使用 Worktree 完全访问预设,已降级为 workspace-write;保留审批提示。",
107
+ "permission.userRestricted": "已保留你在 DSH 原生权限界面选择的限制,未自动提升此 Worktree Session。",
108
+ "permission.unverified": "无法验证 Worktree Session 的权限服务;Session 已保留,但完全访问未得到确认。",
109
+ "permission.unavailable": "Worktree Session 权限状态暂时不可用,请重试。",
110
+ "permission.fullAccessTitle": "确认启用 Worktree Full access?",
111
+ "permission.fullAccessDescription": "启用 Worktree Full access 后,此 Session 可能访问 Session 目录之外的关联 Git 元数据和 Worktree 文件。完全访问会关闭此 Session 的文件系统沙箱,但会保留审批提示;网络和进程策略不变。目标 Worktree:{cwd}",
112
+ "permission.fullAccessAcknowledge": "我已了解风险,并愿意继续",
113
+ "permission.fullAccessEnable": "启用 Worktree Full access",
78
114
  "status.loading": "正在加载工作区…",
79
115
  "error.worktreeDataUnavailable": "Worktree 数据不可用,请重试。",
80
116
  "error.gitNotInstalled": "Git 未安装或不可用,请安装 Git 后重试。",
@@ -86,12 +122,17 @@ window.__ModuleLoader__.load({
86
122
  "error.worktreeOrderingUnavailable": "Worktree 排序不可用,请重新连接后重试。",
87
123
  "error.worktreeCreatedSessionUnavailable": "Worktree 已创建,但 Session 创建不可用,请重新连接后重试。",
88
124
  "error.sessionCreationUnavailable": "Session 创建不可用,请重新连接后重试。",
89
- "error.worktreeRecordMissing": "Worktree 创建未返回 Worktree 记录,请重试请求。",
125
+ "error.worktreeRecordMissing": "Worktree 注册未返回 Worktree 记录,请重试请求。",
126
+ "error.worktreeImportInvalid": "这个 Worktree 无法导入,请选择同一 Workspace 中有效的绝对路径。",
127
+ "error.worktreeAlreadyManaged": "这个 Worktree 已由插件管理。",
128
+ "error.worktreeRegistrationSessionUnavailable": "Worktree 已注册,但 Session 创建不可用,请重新连接后重试。",
90
129
  "error.workspaceRenameUnavailable": "工作区重命名不可用,请重新连接后重试。",
91
130
  "error.workspaceDeleteUnavailable": "工作区删除不可用,请重新连接后重试。",
92
131
  "error.sessionRenameUnavailable": "会话重命名不可用,请重新连接后重试。",
93
132
  "error.sessionBindingFailed": "Session {sessionId} 已创建,但 Worktree 绑定失败:{reason}",
94
133
  "error.sessionAlreadyBound": "Session 已绑定到其他 Worktree,未创建新的 Session。",
134
+ "error.worktreePermissionConfirmationRequired": "Session 和绑定已保留;确认完全访问后重试,未打开 Session。",
135
+ "error.worktreePermissionFailed": "Worktree Session 权限设置失败,可重试:{reason}",
95
136
  "error.sessionListNotReady": "Session 列表尚未就绪,请重试。",
96
137
  "error.sessionFactsIncomplete": "Session 信息尚未完整,请重试。",
97
138
  "error.worktreeSessionRepairRequired": "当前 Worktree 的 Session 绑定需要修复,请重试。",
@@ -130,12 +171,22 @@ window.__ModuleLoader__.load({
130
171
  "worktree.expand": "Expand {name}",
131
172
  "worktree.collapse": "Collapse {name}",
132
173
  "worktree.options": "Worktree options for {name}",
174
+ "worktree.copyPath": "Copy path",
175
+ "worktree.createNew": "Create new Worktree",
133
176
  "worktree.remove": "Remove Worktree",
134
177
  "worktree.removeDescription": "Remove {name}? Sessions stay in DSH and remain available as detached bindings.",
178
+ "worktree.removeExternalDescription": "Remove {name}? This deletes the linked external Worktree directory. Sessions stay in DSH and remain available as detached bindings.",
135
179
  "worktree.addSession": "Add Session to {name}",
136
180
  "worktree.noWorktrees": "No Worktrees",
137
181
  "worktree.createTitle": "New Worktree",
138
182
  "worktree.createDescription": "{name} · the Worktree path is managed by DSH.",
183
+ "worktree.tabCreate": "Create",
184
+ "worktree.tabImport": "Import",
185
+ "worktree.importDescription": "Register an existing Worktree for {name}. Registration leaves the directory in place; removing it later deletes the linked Worktree directory.",
186
+ "worktree.importLoading": "Finding importable Worktrees…",
187
+ "worktree.importEmpty": "No importable Worktrees found.",
188
+ "worktree.importPlaceholder": "Select a Worktree to import",
189
+ "worktree.importSelected": "Selected",
139
190
  "worktree.baseBranch": "Worktree base branch",
140
191
  "worktree.noLocalBranch": "No local branch",
141
192
  "worktree.noBranches": "No local branches found in this Workspace.",
@@ -146,6 +197,7 @@ window.__ModuleLoader__.load({
146
197
  "worktree.setup.commands": "Copyable commands",
147
198
  "worktree.name": "Worktree name",
148
199
  "worktree.create": "Create Worktree",
200
+ "worktree.import": "Import Worktree",
149
201
  "session.options": "Session options for {name}",
150
202
  "session.new": "New Session",
151
203
  "session.rename": "Rename session",
@@ -154,6 +206,23 @@ window.__ModuleLoader__.load({
154
206
  "session.expandMore": "Expand {count} more",
155
207
  "session.collapse": "Collapse",
156
208
  "session.name": "Session name",
209
+ "session.status.running": "Running",
210
+ "session.status.subagentsRunning.one": "{n} subagent running",
211
+ "session.status.subagentsRunning.other": "{n} subagents running",
212
+ "session.status.idle": "Idle",
213
+ "session.status.waitingApproval": "Waiting for approval",
214
+ "session.status.planReview": "Plan awaiting review",
215
+ "session.status.waitingAnswer": "Waiting for answer",
216
+ "session.status.completed": "Completed",
217
+ copy: "Copy",
218
+ "hover.copied": "Copied",
219
+ "session.time.now": "now",
220
+ "session.time.minutes": "{n}min",
221
+ "session.time.hours": "{n}h",
222
+ "session.time.days": "{n}d",
223
+ "session.time.months": "{n}mo",
224
+ "session.time.years": "{n}y",
225
+ "session.time.ago": "{t} ago",
157
226
  "dialog.close": "Close",
158
227
  "dialog.closeWorkspaceRename": "Close Rename Workspace dialog",
159
228
  "dialog.closeWorkspaceDelete": "Close Delete Workspace dialog",
@@ -165,6 +234,14 @@ window.__ModuleLoader__.load({
165
234
  "action.retry": "Retry",
166
235
  "action.retryBinding": "Retry Binding",
167
236
  "action.openCreatedSession": "Open Created Session",
237
+ "permission.fallbackWorkspaceWrite": "The Worktree Full Access preset is unavailable; fell back to workspace-write with approval prompts enabled.",
238
+ "permission.userRestricted": "The restriction selected in DSH’s native permission UI was preserved; this Worktree Session was not elevated.",
239
+ "permission.unverified": "The Worktree Session permission service could not be verified; the Session was retained, but Full Access is not confirmed.",
240
+ "permission.unavailable": "The Worktree Session permission state is temporarily unavailable; retry the request.",
241
+ "permission.fullAccessTitle": "Enable Worktree Full access?",
242
+ "permission.fullAccessDescription": "Worktree Full access lets this Session access linked Git metadata and Worktree files outside the Session directory. It disables filesystem confinement for this Session while keeping approval prompts enabled; network and process policy are unchanged. Target Worktree: {cwd}",
243
+ "permission.fullAccessAcknowledge": "I understand the risks and want to continue",
244
+ "permission.fullAccessEnable": "Enable Worktree Full access",
168
245
  "status.loading": "Loading Workspaces…",
169
246
  "error.worktreeDataUnavailable": "Worktree data is unavailable. Retry the request.",
170
247
  "error.gitNotInstalled": "Git is not installed or unavailable. Install Git and retry.",
@@ -176,12 +253,17 @@ window.__ModuleLoader__.load({
176
253
  "error.worktreeOrderingUnavailable": "Worktree ordering is unavailable; retry after reconnecting.",
177
254
  "error.worktreeCreatedSessionUnavailable": "Worktree created, but Session creation is unavailable; retry after reconnecting.",
178
255
  "error.sessionCreationUnavailable": "Session creation is unavailable; retry after reconnecting.",
179
- "error.worktreeRecordMissing": "Worktree creation returned no Worktree record; retry the request.",
256
+ "error.worktreeRecordMissing": "Worktree registration returned no Worktree record; retry the request.",
257
+ "error.worktreeImportInvalid": "This Worktree cannot be imported. Choose a valid absolute path in this Workspace.",
258
+ "error.worktreeAlreadyManaged": "This Worktree is already managed by the plugin.",
259
+ "error.worktreeRegistrationSessionUnavailable": "The Worktree was registered, but Session creation is unavailable; retry after reconnecting.",
180
260
  "error.workspaceRenameUnavailable": "Workspace rename is unavailable; retry after reconnecting.",
181
261
  "error.workspaceDeleteUnavailable": "Workspace delete is unavailable; retry after reconnecting.",
182
262
  "error.sessionRenameUnavailable": "Session rename is unavailable; retry after reconnecting.",
183
263
  "error.sessionBindingFailed": "Session {sessionId} was created, but Worktree binding failed: {reason}",
184
264
  "error.sessionAlreadyBound": "The Session is already bound to another Worktree; no new Session was created.",
265
+ "error.worktreePermissionConfirmationRequired": "The Session and binding were retained; confirm Full Access and retry. The Session was not opened.",
266
+ "error.worktreePermissionFailed": "Worktree Session permission setup failed; retryable error: {reason}",
185
267
  "error.sessionListNotReady": "The Session list is not ready yet; retry the request.",
186
268
  "error.sessionFactsIncomplete": "Session facts are incomplete; retry the request.",
187
269
  "error.worktreeSessionRepairRequired": "The current Worktree Session binding needs repair; retry the request.",
@@ -200,12 +282,16 @@ window.__ModuleLoader__.load({
200
282
  */
201
283
  const WORKTREE_CONNECTION_ENDPOINTS = Object.freeze({
202
284
  listWorktrees: "worktreeManager/listWorktrees",
285
+ listImportCandidates: "worktreeManager/listImportCandidates",
203
286
  listBranches: "worktreeManager/listBranches",
204
287
  createWorktree: "worktreeManager/createWorktree",
288
+ importWorktree: "worktreeManager/importWorktree",
205
289
  removeWorktree: "worktreeManager/removeWorktree",
206
290
  insertWorktreeBefore: "worktreeManager/insertWorktreeBefore",
207
291
  listBindings: "worktreeManager/listBindings",
208
- bindSession: "worktreeManager/bindSession"
292
+ bindSession: "worktreeManager/bindSession",
293
+ ensureWorktreePermission: "worktreeManager/ensureWorktreePermission",
294
+ normalizeDetachedWorktreePermissions: "worktreeManager/normalizeDetachedWorktreePermissions"
209
295
  });
210
296
  /** Browser-safe error shared by transport, Gateway, endpoint and domain failures. */
211
297
  var WorktreeConnectionError = class extends Error {
@@ -220,14 +306,14 @@ window.__ModuleLoader__.load({
220
306
  this.retryable = options.retryable;
221
307
  }
222
308
  };
223
- function isRecord(value) {
309
+ function isRecord$2(value) {
224
310
  return typeof value === "object" && value !== null && !Array.isArray(value);
225
311
  }
226
312
  function isWorktreeError(value) {
227
- return isRecord(value) && typeof value.code === "string" && typeof value.message === "string" && isRecord(value.details);
313
+ return isRecord$2(value) && typeof value.code === "string" && typeof value.message === "string" && isRecord$2(value.details);
228
314
  }
229
315
  function isWorktreeRemoteResult(value) {
230
- if (!isRecord(value) || typeof value.ok !== "boolean") return false;
316
+ if (!isRecord$2(value) || typeof value.ok !== "boolean") return false;
231
317
  return value.ok || isWorktreeError(value.error) && value.error.details !== void 0;
232
318
  }
233
319
  function disposedError() {
@@ -248,7 +334,7 @@ window.__ModuleLoader__.load({
248
334
  });
249
335
  }
250
336
  function gatewayFailure(endpoint, result) {
251
- const rawDetails = isRecord(result.error.details) ? result.error.details : {};
337
+ const rawDetails = isRecord$2(result.error.details) ? result.error.details : {};
252
338
  throw new WorktreeConnectionError({
253
339
  code: result.error.code,
254
340
  message: result.error.message,
@@ -305,8 +391,10 @@ window.__ModuleLoader__.load({
305
391
  }
306
392
  return {
307
393
  listWorktrees: (input) => invoke("listWorktrees", input),
394
+ listImportCandidates: (input) => invoke("listImportCandidates", input),
308
395
  listBranches: (input) => invoke("listBranches", input),
309
396
  createWorktree: (input) => invoke("createWorktree", input),
397
+ importWorktree: (input) => invoke("importWorktree", input),
310
398
  async removeWorktree(input) {
311
399
  await invoke("removeWorktree", input);
312
400
  },
@@ -315,6 +403,8 @@ window.__ModuleLoader__.load({
315
403
  },
316
404
  listBindings: (input) => invoke("listBindings", input),
317
405
  bindSession: (input) => invoke("bindSession", input),
406
+ ensureWorktreePermission: (input) => invoke("ensureWorktreePermission", input),
407
+ normalizeDetachedWorktreePermissions: (input) => invoke("normalizeDetachedWorktreePermissions", input),
318
408
  dispose() {
319
409
  if (disposed) return;
320
410
  disposed = true;
@@ -324,7 +414,7 @@ window.__ModuleLoader__.load({
324
414
  };
325
415
  }
326
416
  //#endregion
327
- //#region \0clutch-dsh-worktree-css:/private/tmp/clutch-dsh-wt-worktree-0.1.5-release/packages/clutch-dsh-worktree/src/client/worktree-context.css.mjs
417
+ //#region \0clutch-dsh-worktree-css:/private/tmp/clutch-dsh-wt-worktree-0.1.7-release/packages/clutch-dsh-worktree/src/client/worktree-context.css.mjs
328
418
  const css$1 = ".-cerbur-clutch-dsh-worktree-headerContext {\n display: inline-flex;\n min-width: 0;\n height: 22px;\n align-items: center;\n gap: 4px;\n max-width: 180px;\n overflow: hidden;\n border-radius: 6px;\n padding: 0 2px 0 0;\n background: var(--dsw-alias-fill-tsp-secondary);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 22px;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-contextIcon {\n flex: none;\n opacity: 0.7;\n}\n\n.-cerbur-clutch-dsh-worktree-contextLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-contextHoverTitle {\n max-width: 360px;\n color: var(--dsw-static-neutral-bluish-00);\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: anywhere;\n white-space: normal;\n}\n\n.-cerbur-clutch-dsh-worktree-heroContextHost {\n position: absolute;\n z-index: 1;\n display: block;\n width: max-content;\n max-width: 260px;\n min-height: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-heroContext {\n display: inline-flex;\n box-sizing: border-box;\n max-width: 260px;\n min-height: 22px;\n align-items: center;\n overflow: hidden;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n padding: 0 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 20px;\n pointer-events: auto;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n";
329
419
  const styleId$1 = "@cerbur/clutch-dsh-worktree/worktree-context.css";
330
420
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree-context.css\"]") === null) {
@@ -347,18 +437,25 @@ window.__ModuleLoader__.load({
347
437
  function WorktreeContextLabel({ value, t }) {
348
438
  if (value.kind === "none") return null;
349
439
  const ariaLabel = value.kind === "main" ? t("context.main", { name: value.label }) : t("context.worktree", { name: value.label });
350
- return (0, react_jsx_runtime.jsxs)("span", {
351
- className: worktree_context_css_default.headerContext,
352
- title: value.label,
353
- "aria-label": ariaLabel,
354
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {
355
- size: 14,
356
- className: worktree_context_css_default.contextIcon,
357
- "aria-hidden": "true"
358
- }), (0, react_jsx_runtime.jsx)("span", {
359
- className: worktree_context_css_default.contextLabel,
440
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.HoverCard, {
441
+ anchor: (0, react_jsx_runtime.jsxs)("span", {
442
+ className: worktree_context_css_default.headerContext,
443
+ title: value.label,
444
+ "aria-label": ariaLabel,
445
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {
446
+ size: 14,
447
+ className: worktree_context_css_default.contextIcon,
448
+ "aria-hidden": "true"
449
+ }), (0, react_jsx_runtime.jsx)("span", {
450
+ className: worktree_context_css_default.contextLabel,
451
+ children: value.label
452
+ })]
453
+ }),
454
+ content: (0, react_jsx_runtime.jsx)("div", {
455
+ className: worktree_context_css_default.contextHoverTitle,
360
456
  children: value.label
361
- })]
457
+ }),
458
+ openDelayMs: 500
362
459
  });
363
460
  }
364
461
  /** Render the current branch or active Worktree branch beside the Session title. */
@@ -371,8 +468,8 @@ window.__ModuleLoader__.load({
371
468
  });
372
469
  }
373
470
  //#endregion
374
- //#region \0clutch-dsh-worktree-css:/private/tmp/clutch-dsh-wt-worktree-0.1.5-release/packages/clutch-dsh-worktree/src/client/worktree.css.mjs
375
- const css = ".-cerbur-clutch-dsh-worktree-action {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: 8px;\n width: calc(100% + 4px);\n height: 42px;\n margin: 4px -2px;\n box-sizing: border-box;\n border: 0;\n border-radius: 12px;\n padding: 0 10px 0 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n overflow: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-action:hover,\n.-cerbur-clutch-dsh-worktree-action[data-active='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-action[data-collapsed='true'] {\n width: 36px;\n height: 36px;\n margin: 8px 0 10px;\n justify-content: center;\n gap: 0;\n padding: 0;\n border-radius: 50%;\n}\n\n.-cerbur-clutch-dsh-worktree-actionLabel {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n height: 22px;\n overflow: hidden;\n font-weight: 400;\n line-height: 22px;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-surface {\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n min-width: 56px;\n overflow: hidden;\n border-right: 1px solid var(--dsw-alias-border-l1);\n background: var(--dsw-specific-sidebar-fill);\n color: var(--dsw-alias-label-primary);\n pointer-events: auto;\n transition: width var(--ds-transition-duration-slow) var(--ds-ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .-cerbur-clutch-dsh-worktree-surface {\n transition: none;\n }\n}\n\n.-cerbur-clutch-dsh-worktree-wideContent {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n}\n\n.-cerbur-clutch-dsh-worktree-surface[data-collapsed='true'] .-cerbur-clutch-dsh-worktree-wideContent {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: inherit;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-header {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n min-height: 44px;\n padding: 6px 10px;\n box-sizing: border-box;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-title {\n min-width: 0;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n flex: none;\n width: 28px;\n height: 28px;\n font-size: 18px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-modeButton[data-active='true'] {\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspacePicker {\n display: block;\n width: calc(100% - 20px);\n margin: 6px 10px 8px;\n padding: 6px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceLabel {\n display: block;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-content {\n min-height: 0;\n flex: 1;\n overflow: auto;\n padding: 0 10px 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-content:focus-visible {\n outline: 2px solid var(--dsw-alias-border-l3);\n outline-offset: -2px;\n}\n\n.-cerbur-clutch-dsh-worktree-error {\n margin: 12px 0 0;\n padding: 8px;\n border: 1px solid var(--dsw-alias-state-error-primary);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n border: 0;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n width: 100%;\n padding: 6px 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-recoveryActions {\n display: grid;\n gap: 6px;\n margin-top: 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton:disabled,\n.-cerbur-clutch-dsh-worktree-actionButton:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.-cerbur-clutch-dsh-worktree-actions {\n display: grid;\n gap: 6px;\n margin-top: 10px;\n padding: 8px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-actionSelect {\n min-width: 0;\n padding: 5px 6px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-section {\n margin: 12px 0 0;\n}\n\n.-cerbur-clutch-dsh-worktree-sectionTitle {\n margin: 0 2px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-empty,\n.-cerbur-clutch-dsh-worktree-message {\n margin: 0;\n padding: 8px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-gitReadiness {\n display: grid;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-commandBlock {\n margin: 6px 0 0;\n padding: 8px;\n overflow-x: auto;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-secondary);\n font: 11px/16px ui-monospace, SFMono-Regular, Menlo, monospace;\n white-space: pre-wrap;\n}\n\n.-cerbur-clutch-dsh-worktree-message[data-error='true'] {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktree,\n.-cerbur-clutch-dsh-worktree-session {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 6px 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree {\n cursor: default;\n}\n\n.-cerbur-clutch-dsh-worktree-session {\n cursor: pointer;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree:hover,\n.-cerbur-clutch-dsh-worktree-session:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeLabel,\n.-cerbur-clutch-dsh-worktree-sessionLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-status {\n flex: none;\n margin-left: auto;\n padding-left: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchRow {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchIcon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n outline: none;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n width: 100%;\n padding: 6px 8px;\n font-size: 13px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput:focus,\n.-cerbur-clutch-dsh-worktree-actionSelect:focus {\n border-color: #3b82f6;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 20px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceList {\n min-width: 0;\n min-height: 0;\n display: grid;\n gap: 4px;\n overflow-x: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceGroup {\n min-width: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow,\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n position: relative;\n display: flex;\n align-items: center;\n min-width: 0;\n min-height: 36px;\n box-sizing: border-box;\n border-radius: 8px;\n padding: 3px 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceDisclosure,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeDisclosure,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 28px;\n border: 0;\n border-radius: 6px;\n padding: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 17px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton > svg {\n display: block;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-worktreeIcon {\n flex: none;\n width: 24px;\n color: var(--dsw-alias-label-secondary);\n font-size: 20px;\n line-height: 1;\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeState {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 12px;\n margin-right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon {\n color: var(--dsw-alias-brand-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceTitle,\n.-cerbur-clutch-dsh-worktree-worktreeLabel {\n min-width: 0;\n overflow: hidden;\n flex: 1;\n padding: 0 6px;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-main-group='true'] .-cerbur-clutch-dsh-worktree-worktreeLabel {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeHoverTitle {\n color: var(--dsw-static-neutral-bluish-00);\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: anywhere;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot,\n.-cerbur-clutch-dsh-worktree-workspaceActions {\n position: relative;\n display: inline-flex;\n flex: 0 0 64px;\n align-items: center;\n justify-content: flex-end;\n width: 64px;\n height: 28px;\n margin-left: auto;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-menuAction {\n position: absolute;\n top: 0;\n right: 32px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n position: absolute;\n top: 0;\n right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-menuAction {\n display: inline-flex;\n width: 28px;\n height: 28px;\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeChildren {\n margin-left: 24px;\n padding: 2px 0 5px 12px;\n border-left: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-groupLabel {\n padding: 6px 8px 3px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader {\n display: flex;\n align-items: center;\n min-height: 34px;\n padding-right: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader .-cerbur-clutch-dsh-worktree-groupLabel {\n flex: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeGroup {\n margin-top: 2px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n min-height: 34px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-disclosureButton {\n width: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent {\n display: flex;\n align-items: center;\n flex: 1;\n width: 0;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 4px 4px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent:hover {\n background: transparent;\n}\n\n.-cerbur-clutch-dsh-worktree-rowActions {\n display: none;\n flex: none;\n align-items: center;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton {\n display: block;\n width: calc(100% - 24px);\n margin: 2px 0 4px 24px;\n padding: 4px 8px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 12px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-rowActions {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-renameInput {\n width: 100%;\n}\n\n.-cerbur-clutch-dsh-worktree-renameError {\n margin: 8px 0 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeGuide {\n flex: none;\n width: 20px;\n color: var(--dsw-alias-label-tertiary);\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-emptyNested {\n margin: 0;\n padding: 4px 8px 5px 28px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-modalField {\n display: grid;\n gap: 5px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n";
471
+ //#region \0clutch-dsh-worktree-css:/private/tmp/clutch-dsh-wt-worktree-0.1.7-release/packages/clutch-dsh-worktree/src/client/worktree.css.mjs
472
+ const css = ".-cerbur-clutch-dsh-worktree-action {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: 8px;\n width: calc(100% + 4px);\n height: 42px;\n margin: 4px -2px;\n box-sizing: border-box;\n border: 0;\n border-radius: 12px;\n padding: 0 10px 0 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n overflow: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-action:hover,\n.-cerbur-clutch-dsh-worktree-action[data-active='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-action[data-collapsed='true'] {\n width: 36px;\n height: 36px;\n margin: 8px 0 10px;\n justify-content: center;\n gap: 0;\n padding: 0;\n border-radius: 50%;\n}\n\n.-cerbur-clutch-dsh-worktree-actionLabel {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n height: 22px;\n overflow: hidden;\n font-weight: 400;\n line-height: 22px;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-surface {\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n min-width: 56px;\n overflow: hidden;\n border-right: 1px solid var(--dsw-alias-border-l1);\n background: var(--dsw-specific-sidebar-fill);\n color: var(--dsw-alias-label-primary);\n pointer-events: auto;\n transition: width var(--ds-transition-duration-slow) var(--ds-ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .-cerbur-clutch-dsh-worktree-surface {\n transition: none;\n }\n}\n\n.-cerbur-clutch-dsh-worktree-wideContent {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n}\n\n.-cerbur-clutch-dsh-worktree-surface[data-collapsed='true'] .-cerbur-clutch-dsh-worktree-wideContent {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTabs {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTab {\n border: 0;\n border-bottom: 2px solid transparent;\n padding: 4px 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTab[aria-selected='true'] {\n border-bottom-color: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportCandidates {\n display: grid;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportSelect {\n width: 100%;\n min-width: 0;\n padding: 7px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportError {\n display: grid;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: inherit;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-header {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n min-height: 44px;\n padding: 6px 10px;\n box-sizing: border-box;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-title {\n min-width: 0;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n flex: none;\n width: 28px;\n height: 28px;\n font-size: 18px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-modeButton[data-active='true'] {\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspacePicker {\n display: block;\n width: calc(100% - 20px);\n margin: 6px 10px 8px;\n padding: 6px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceLabel {\n display: block;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-content {\n min-height: 0;\n flex: 1;\n overflow: auto;\n padding: 0 10px 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-content:focus-visible {\n outline: 2px solid var(--dsw-alias-border-l3);\n outline-offset: -2px;\n}\n\n.-cerbur-clutch-dsh-worktree-error {\n margin: 12px 0 0;\n padding: 8px;\n border: 1px solid var(--dsw-alias-state-error-primary);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-notice {\n margin: 12px 0 0;\n padding: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n border: 0;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n width: 100%;\n padding: 6px 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-recoveryActions {\n display: grid;\n gap: 6px;\n margin-top: 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton:disabled,\n.-cerbur-clutch-dsh-worktree-actionButton:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.-cerbur-clutch-dsh-worktree-actions {\n display: grid;\n gap: 6px;\n margin-top: 10px;\n padding: 8px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-actionSelect {\n min-width: 0;\n padding: 5px 6px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-section {\n margin: 12px 0 0;\n}\n\n.-cerbur-clutch-dsh-worktree-sectionTitle {\n margin: 0 2px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-empty,\n.-cerbur-clutch-dsh-worktree-message {\n margin: 0;\n padding: 8px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-gitReadiness {\n display: grid;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-commandBlock {\n margin: 6px 0 0;\n padding: 8px;\n overflow-x: auto;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-secondary);\n font:\n 11px/16px ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n white-space: pre-wrap;\n}\n\n.-cerbur-clutch-dsh-worktree-message[data-error='true'] {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktree,\n.-cerbur-clutch-dsh-worktree-session {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 6px 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree {\n cursor: default;\n}\n\n.-cerbur-clutch-dsh-worktree-session {\n cursor: pointer;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree:hover,\n.-cerbur-clutch-dsh-worktree-session:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeLabel,\n.-cerbur-clutch-dsh-worktree-sessionLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-status {\n flex: none;\n margin-left: auto;\n padding-left: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchRow {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchIcon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n outline: none;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n width: 100%;\n padding: 6px 8px;\n font-size: 13px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput:focus,\n.-cerbur-clutch-dsh-worktree-actionSelect:focus,\n.-cerbur-clutch-dsh-worktree-worktreeImportSelect:focus {\n border-color: #3b82f6;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 20px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceList {\n min-width: 0;\n min-height: 0;\n display: grid;\n gap: 4px;\n overflow-x: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceGroup {\n min-width: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow,\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n position: relative;\n display: flex;\n align-items: center;\n min-width: 0;\n min-height: 36px;\n box-sizing: border-box;\n border-radius: 8px;\n padding: 3px 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceDisclosure,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeDisclosure,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 28px;\n border: 0;\n border-radius: 6px;\n padding: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 17px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton > svg {\n display: block;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-worktreeIcon {\n flex: none;\n width: 24px;\n color: var(--dsw-alias-label-secondary);\n font-size: 20px;\n line-height: 1;\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeState {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 12px;\n margin-right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon {\n color: var(--dsw-alias-brand-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceTitle,\n.-cerbur-clutch-dsh-worktree-worktreeLabel {\n min-width: 0;\n overflow: hidden;\n flex: 1;\n padding: 0 6px;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-main-group='true'] .-cerbur-clutch-dsh-worktree-worktreeLabel {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeHoverTitle {\n color: var(--dsw-static-neutral-bluish-00);\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: anywhere;\n}\n\n/* Native Session hover card: full title, relative time, and current status. */\n.-cerbur-clutch-dsh-worktree-sessionHoverContent {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionHoverTitle {\n color: #FFFFFF;\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: break-word;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionHoverTime {\n color: #CFD3D6;\n font-size: 12px;\n line-height: 16px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionHoverStatus {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #ADB2B8;\n font-size: 12px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot,\n.-cerbur-clutch-dsh-worktree-workspaceActions {\n position: relative;\n display: inline-flex;\n flex: 0 0 64px;\n align-items: center;\n justify-content: flex-end;\n width: 64px;\n height: 28px;\n margin-left: auto;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-menuAction {\n position: absolute;\n top: 0;\n right: 32px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n position: absolute;\n top: 0;\n right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-groupActivity {\n position: absolute;\n top: 0;\n right: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n visibility: hidden;\n pointer-events: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-groupActivity {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'][data-menu-open='true'] .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'][data-menu-open='true'] .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-groupActivity {\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'][data-menu-open='true']\n .-cerbur-clutch-dsh-worktree-treeActionSlot\n > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'][data-menu-open='true']\n .-cerbur-clutch-dsh-worktree-treeActionSlot\n > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-menuAction {\n display: inline-flex;\n width: 28px;\n height: 28px;\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeChildren {\n margin-left: 16px;\n padding: 2px 0 5px 12px;\n border-left: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-groupLabel {\n padding: 6px 8px 3px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader {\n display: flex;\n align-items: center;\n min-height: 34px;\n padding-right: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader .-cerbur-clutch-dsh-worktree-groupLabel {\n flex: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeGroup {\n margin-top: 2px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n min-height: 34px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-disclosureButton {\n width: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow {\n position: relative;\n display: flex;\n align-items: center;\n width: calc(100% - 22px);\n margin-left: 22px;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-session-current='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-session-current='true'] .-cerbur-clutch-dsh-worktree-treeSessionContent {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent {\n display: flex;\n align-items: center;\n flex: 1;\n width: 0;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 4px 4px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent:hover {\n background: transparent;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionTrailing {\n position: relative;\n display: inline-flex;\n flex: 0 0 52px;\n align-items: center;\n justify-content: flex-end;\n width: 52px;\n height: 28px;\n margin-left: auto;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionMeta,\n.-cerbur-clutch-dsh-worktree-rowActions {\n position: absolute;\n inset: 0;\n display: inline-flex;\n align-items: center;\n justify-content: flex-end;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionMeta {\n color: var(--dsw-alias-label-tertiary);\n pointer-events: none;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionStatus {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionTime {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-right: 4px;\n font-size: 12px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton {\n display: block;\n width: calc(100% - 24px);\n margin: 2px 0 4px 24px;\n padding: 4px 8px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 12px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-rowActions {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-sessionMeta,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-sessionMeta,\n.-cerbur-clutch-dsh-worktree-sessionTrailing:focus-within .-cerbur-clutch-dsh-worktree-sessionMeta {\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-sessionTrailing:focus-within .-cerbur-clutch-dsh-worktree-rowActions {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-renameInput {\n width: 100%;\n}\n\n.-cerbur-clutch-dsh-worktree-renameError {\n margin: 8px 0 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-emptyNested {\n margin: 0;\n padding: 4px 8px 5px 28px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-modalField {\n display: grid;\n gap: 5px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n";
376
473
  const styleId = "@cerbur/clutch-dsh-worktree/worktree.css";
377
474
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree.css\"]") === null) {
378
475
  const tag = document.createElement("style");
@@ -386,6 +483,11 @@ window.__ModuleLoader__.load({
386
483
  "actionLabel": "-cerbur-clutch-dsh-worktree-actionLabel",
387
484
  "surface": "-cerbur-clutch-dsh-worktree-surface",
388
485
  "wideContent": "-cerbur-clutch-dsh-worktree-wideContent",
486
+ "worktreeModalTabs": "-cerbur-clutch-dsh-worktree-worktreeModalTabs",
487
+ "worktreeModalTab": "-cerbur-clutch-dsh-worktree-worktreeModalTab",
488
+ "worktreeImportCandidates": "-cerbur-clutch-dsh-worktree-worktreeImportCandidates",
489
+ "worktreeImportSelect": "-cerbur-clutch-dsh-worktree-worktreeImportSelect",
490
+ "worktreeImportError": "-cerbur-clutch-dsh-worktree-worktreeImportError",
389
491
  "closeButton": "-cerbur-clutch-dsh-worktree-closeButton",
390
492
  "header": "-cerbur-clutch-dsh-worktree-header",
391
493
  "title": "-cerbur-clutch-dsh-worktree-title",
@@ -394,6 +496,7 @@ window.__ModuleLoader__.load({
394
496
  "workspaceLabel": "-cerbur-clutch-dsh-worktree-workspaceLabel",
395
497
  "content": "-cerbur-clutch-dsh-worktree-content",
396
498
  "error": "-cerbur-clutch-dsh-worktree-error",
499
+ "notice": "-cerbur-clutch-dsh-worktree-notice",
397
500
  "retryButton": "-cerbur-clutch-dsh-worktree-retryButton",
398
501
  "actionButton": "-cerbur-clutch-dsh-worktree-actionButton",
399
502
  "recoveryActions": "-cerbur-clutch-dsh-worktree-recoveryActions",
@@ -426,9 +529,14 @@ window.__ModuleLoader__.load({
426
529
  "worktreeState": "-cerbur-clutch-dsh-worktree-worktreeState",
427
530
  "workspaceTitle": "-cerbur-clutch-dsh-worktree-workspaceTitle",
428
531
  "worktreeHoverTitle": "-cerbur-clutch-dsh-worktree-worktreeHoverTitle",
532
+ "sessionHoverContent": "-cerbur-clutch-dsh-worktree-sessionHoverContent",
533
+ "sessionHoverTitle": "-cerbur-clutch-dsh-worktree-sessionHoverTitle",
534
+ "sessionHoverTime": "-cerbur-clutch-dsh-worktree-sessionHoverTime",
535
+ "sessionHoverStatus": "-cerbur-clutch-dsh-worktree-sessionHoverStatus",
429
536
  "treeActionSlot": "-cerbur-clutch-dsh-worktree-treeActionSlot",
430
537
  "workspaceActions": "-cerbur-clutch-dsh-worktree-workspaceActions",
431
538
  "menuAction": "-cerbur-clutch-dsh-worktree-menuAction",
539
+ "groupActivity": "-cerbur-clutch-dsh-worktree-groupActivity",
432
540
  "dropBefore": "-cerbur-clutch-dsh-worktree-dropBefore",
433
541
  "dropAfter": "-cerbur-clutch-dsh-worktree-dropAfter",
434
542
  "treeChildren": "-cerbur-clutch-dsh-worktree-treeChildren",
@@ -437,11 +545,14 @@ window.__ModuleLoader__.load({
437
545
  "worktreeGroup": "-cerbur-clutch-dsh-worktree-worktreeGroup",
438
546
  "treeSessionRow": "-cerbur-clutch-dsh-worktree-treeSessionRow",
439
547
  "treeSessionContent": "-cerbur-clutch-dsh-worktree-treeSessionContent",
548
+ "sessionTrailing": "-cerbur-clutch-dsh-worktree-sessionTrailing",
549
+ "sessionMeta": "-cerbur-clutch-dsh-worktree-sessionMeta",
440
550
  "rowActions": "-cerbur-clutch-dsh-worktree-rowActions",
551
+ "sessionStatus": "-cerbur-clutch-dsh-worktree-sessionStatus",
552
+ "sessionTime": "-cerbur-clutch-dsh-worktree-sessionTime",
441
553
  "sessionOverflowButton": "-cerbur-clutch-dsh-worktree-sessionOverflowButton",
442
554
  "renameInput": "-cerbur-clutch-dsh-worktree-renameInput",
443
555
  "renameError": "-cerbur-clutch-dsh-worktree-renameError",
444
- "treeGuide": "-cerbur-clutch-dsh-worktree-treeGuide",
445
556
  "emptyNested": "-cerbur-clutch-dsh-worktree-emptyNested",
446
557
  "modalField": "-cerbur-clutch-dsh-worktree-modalField"
447
558
  };
@@ -512,7 +623,7 @@ window.__ModuleLoader__.load({
512
623
  }
513
624
  /**
514
625
  * Browser-local Hero suffix. It intentionally uses the frame overlay because
515
- * rc.8 exposes no additive slot beside the native Hero headline.
626
+ * The current upstream DSH exposes no additive slot beside the native Hero headline.
516
627
  */
517
628
  function WorktreeHeroContext({ useWorktreeContext, t }) {
518
629
  const state = useWorktreeContext((snapshot) => snapshot);
@@ -711,6 +822,84 @@ window.__ModuleLoader__.load({
711
822
  };
712
823
  }
713
824
  //#endregion
825
+ //#region lib/client/worktree-expand-state.js
826
+ /** Browser-local storage identity; never sent to DSH or the plugin sidecar. */
827
+ const WORKTREE_EXPAND_STATE_STORAGE_KEY = "clutch-dsh-worktree.expand-state";
828
+ function emptyState$1() {
829
+ return {
830
+ collapsedWorkspaceIds: {},
831
+ collapsedMainWorkspaceIds: {},
832
+ collapsedWorktreeIds: {}
833
+ };
834
+ }
835
+ function isRecord$1(value) {
836
+ return typeof value === "object" && value !== null && !Array.isArray(value);
837
+ }
838
+ function normalizeIds$1(value) {
839
+ if (!isRecord$1(value)) return {};
840
+ return Object.fromEntries(Object.entries(value).filter(([id, collapsed]) => id.length > 0 && collapsed === true));
841
+ }
842
+ /** Turn any persisted JSON value into the current state shape. */
843
+ function normalizeWorktreeExpandState(value) {
844
+ const source = isRecord$1(value) ? value : {};
845
+ return {
846
+ collapsedWorkspaceIds: normalizeIds$1(source.collapsedWorkspaceIds),
847
+ collapsedMainWorkspaceIds: normalizeIds$1(source.collapsedMainWorkspaceIds),
848
+ collapsedWorktreeIds: normalizeIds$1(source.collapsedWorktreeIds)
849
+ };
850
+ }
851
+ function isWorkspaceExpanded(state, workspaceId) {
852
+ return state.collapsedWorkspaceIds[workspaceId] !== true;
853
+ }
854
+ function isMainExpanded(state, workspaceId) {
855
+ return state.collapsedMainWorkspaceIds[workspaceId] !== true;
856
+ }
857
+ function isWorktreeExpanded(state, worktreeId) {
858
+ return state.collapsedWorktreeIds[worktreeId] !== true;
859
+ }
860
+ function toggle(record, id) {
861
+ if (record[id] === true) delete record[id];
862
+ else record[id] = true;
863
+ }
864
+ function retain(record, ids) {
865
+ const allowed = new Set(ids);
866
+ for (const id of Object.keys(record)) if (!allowed.has(id)) delete record[id];
867
+ }
868
+ /** Create one apply-scoped store; component remounts share this instance. */
869
+ function createWorktreeExpandStateStore(snapshotStoreFactory) {
870
+ let store;
871
+ try {
872
+ store = snapshotStoreFactory(emptyState$1(), { persist: { name: WORKTREE_EXPAND_STATE_STORAGE_KEY } });
873
+ } catch {
874
+ store = snapshotStoreFactory(emptyState$1());
875
+ }
876
+ store.set(normalizeWorktreeExpandState(store.getSnapshot()));
877
+ return Object.assign(store, { actions: {
878
+ toggleWorkspace: (workspaceId) => {
879
+ store.update((draft) => {
880
+ toggle(draft.collapsedWorkspaceIds, workspaceId);
881
+ });
882
+ },
883
+ toggleMain: (workspaceId) => {
884
+ store.update((draft) => {
885
+ toggle(draft.collapsedMainWorkspaceIds, workspaceId);
886
+ });
887
+ },
888
+ toggleWorktree: (worktreeId) => {
889
+ store.update((draft) => {
890
+ toggle(draft.collapsedWorktreeIds, worktreeId);
891
+ });
892
+ },
893
+ retain: (workspaceIds, worktreeIds) => {
894
+ store.update((draft) => {
895
+ retain(draft.collapsedWorkspaceIds, workspaceIds);
896
+ retain(draft.collapsedMainWorkspaceIds, workspaceIds);
897
+ retain(draft.collapsedWorktreeIds, worktreeIds);
898
+ });
899
+ }
900
+ } });
901
+ }
902
+ //#endregion
714
903
  //#region lib/client/view-mode.js
715
904
  /** Browser-local storage key; it is never sent to DSH or the sidecar. */
716
905
  const WORKTREE_VIEW_MODE_STORAGE_KEY = "clutch-dsh-worktree.view-mode";
@@ -773,6 +962,14 @@ window.__ModuleLoader__.load({
773
962
  function reason(error, t) {
774
963
  return error.message.length > 0 ? error.message : t("error.worktreeDataUnavailable");
775
964
  }
965
+ function formatWorktreePermissionNotice(result, t) {
966
+ switch (result.status) {
967
+ case "fallback-workspace-write": return t("permission.fallbackWorkspaceWrite");
968
+ case "user-restricted": return t("permission.userRestricted");
969
+ case "unverified": return t("permission.unverified");
970
+ default: return t("permission.unavailable");
971
+ }
972
+ }
776
973
  function formatWorktreeViewError(error, t) {
777
974
  switch (error.code) {
778
975
  case "CLIENT_DISPOSED": return t("error.connectionDisposed");
@@ -787,6 +984,9 @@ window.__ModuleLoader__.load({
787
984
  case "SESSION_CREATE_UNAVAILABLE": return t("error.sessionCreationUnavailable");
788
985
  case "WORKTREE_CREATED_SESSION_UNAVAILABLE": return t("error.worktreeCreatedSessionUnavailable");
789
986
  case "WORKTREE_RECORD_MISSING": return t("error.worktreeRecordMissing");
987
+ case "WORKTREE_IMPORT_INVALID": return t("error.worktreeImportInvalid");
988
+ case "WORKTREE_ALREADY_MANAGED": return t("error.worktreeAlreadyManaged");
989
+ case "WORKTREE_REGISTRATION_SESSION_UNAVAILABLE": return t("error.worktreeRegistrationSessionUnavailable");
790
990
  case "WORKSPACE_RENAME_UNAVAILABLE": return t("error.workspaceRenameUnavailable");
791
991
  case "WORKSPACE_DELETE_UNAVAILABLE": return t("error.workspaceDeleteUnavailable");
792
992
  case "SESSION_RENAME_UNAVAILABLE": return t("error.sessionRenameUnavailable");
@@ -795,6 +995,8 @@ window.__ModuleLoader__.load({
795
995
  reason: reason(error, t)
796
996
  });
797
997
  case "SESSION_ALREADY_BOUND": return t("error.sessionAlreadyBound");
998
+ case "WORKTREE_PERMISSION_CONFIRMATION_REQUIRED": return t("error.worktreePermissionConfirmationRequired");
999
+ case "WORKTREE_PERMISSION_FAILED": return t("error.worktreePermissionFailed", { reason: reason(error, t) });
798
1000
  case "SESSION_LIST_NOT_READY": return t("error.sessionListNotReady");
799
1001
  case "SESSION_FACTS_INCOMPLETE": return t("error.sessionFactsIncomplete");
800
1002
  case "WORKTREE_SESSION_REPAIR_REQUIRED": return t("error.worktreeSessionRepairRequired");
@@ -806,6 +1008,127 @@ window.__ModuleLoader__.load({
806
1008
  }
807
1009
  //#endregion
808
1010
  //#region lib/client/session-view.js
1011
+ function isPendingInteractionStatus(value) {
1012
+ return value === "approval" || value === "plan-review" || value === "question";
1013
+ }
1014
+ function validUpdatedAt(value) {
1015
+ return value !== void 0 && Number.isFinite(value);
1016
+ }
1017
+ /**
1018
+ * Native-compatible subagent lineage index. The upstream runtime's public client
1019
+ * entry is a browser module-loader bundle, so keeping this pure mirror local lets
1020
+ * the browser Consumer and its Node tests share the same status semantics.
1021
+ */
1022
+ function indexSubagentDescendants(summaries) {
1023
+ const indexed = /* @__PURE__ */ new Map();
1024
+ for (const descendant of Object.values(summaries)) {
1025
+ if (descendant.origin !== "subagent") continue;
1026
+ const seen = /* @__PURE__ */ new Set();
1027
+ let current = descendant;
1028
+ while (current?.origin === "subagent" && current.parentId !== void 0 && !seen.has(current.id)) {
1029
+ seen.add(current.id);
1030
+ const aggregate = indexed.get(current.parentId);
1031
+ if (aggregate === void 0) indexed.set(current.parentId, {
1032
+ count: 1,
1033
+ runningCount: descendant.running ? 1 : 0
1034
+ });
1035
+ else {
1036
+ aggregate.count += 1;
1037
+ if (descendant.running) aggregate.runningCount += 1;
1038
+ }
1039
+ current = summaries[current.parentId];
1040
+ }
1041
+ }
1042
+ return indexed;
1043
+ }
1044
+ /** Native Workspace relative-time buckets, kept pure for deterministic rendering/tests. */
1045
+ function relativeTime(updatedAt, now) {
1046
+ if (!validUpdatedAt(updatedAt) || !Number.isFinite(now)) return void 0;
1047
+ const MIN = 6e4;
1048
+ const HOUR = 36e5;
1049
+ const DAY = 864e5;
1050
+ const diff = Math.max(0, now - updatedAt);
1051
+ if (diff < MIN) return {
1052
+ unit: "now",
1053
+ n: 0
1054
+ };
1055
+ if (diff < HOUR) return {
1056
+ unit: "minutes",
1057
+ n: Math.floor(diff / MIN)
1058
+ };
1059
+ if (diff < DAY) return {
1060
+ unit: "hours",
1061
+ n: Math.floor(diff / HOUR)
1062
+ };
1063
+ if (diff < 30 * DAY) return {
1064
+ unit: "days",
1065
+ n: Math.floor(diff / DAY)
1066
+ };
1067
+ if (diff < 365 * DAY) return {
1068
+ unit: "months",
1069
+ n: Math.floor(diff / (30 * DAY))
1070
+ };
1071
+ return {
1072
+ unit: "years",
1073
+ n: Math.floor(diff / (365 * DAY))
1074
+ };
1075
+ }
1076
+ /** Match native Workspace status priority while retaining the aggregate subagent count. */
1077
+ function sessionStatus(session, runningSubagentCount) {
1078
+ const count = Number.isFinite(runningSubagentCount) && runningSubagentCount > 0 ? Math.floor(runningSubagentCount) : 0;
1079
+ if (isPendingInteractionStatus(session.pendingInteraction)) return {
1080
+ state: "warning",
1081
+ labelKey: session.pendingInteraction === "approval" ? "waitingApproval" : session.pendingInteraction === "plan-review" ? "planReview" : "waitingAnswer",
1082
+ runningSubagentCount: count
1083
+ };
1084
+ if (session.running === true) return {
1085
+ state: "ongoing",
1086
+ labelKey: "running",
1087
+ runningSubagentCount: count
1088
+ };
1089
+ if (count > 0) return {
1090
+ state: "ongoing",
1091
+ labelKey: "subagentsRunning",
1092
+ runningSubagentCount: count
1093
+ };
1094
+ if (session.completed === true) return {
1095
+ state: "done",
1096
+ labelKey: "completed",
1097
+ runningSubagentCount: 0
1098
+ };
1099
+ return {
1100
+ state: "done",
1101
+ labelKey: "idle",
1102
+ runningSubagentCount: 0
1103
+ };
1104
+ }
1105
+ function toLineageSummary(sessionId, summary) {
1106
+ return {
1107
+ id: sessionId,
1108
+ running: summary.running === true,
1109
+ ...summary.parentId === void 0 ? {} : { parentId: summary.parentId },
1110
+ ...summary.origin === "subagent" ? { origin: "subagent" } : {}
1111
+ };
1112
+ }
1113
+ /** Derive row and group activity facts from one retained DSH Session snapshot. */
1114
+ function deriveSessionPresentationIndex(sessions) {
1115
+ const descendants = indexSubagentDescendants(Object.fromEntries(Object.entries(sessions.byId).map(([sessionId, summary]) => [sessionId, toLineageSummary(sessionId, summary)])));
1116
+ return Object.fromEntries(Object.entries(sessions.byId).map(([sessionId, summary]) => {
1117
+ const runningSubagentCount = descendants.get(sessionId)?.runningCount ?? 0;
1118
+ return [sessionId, {
1119
+ status: sessionStatus(summary, runningSubagentCount),
1120
+ running: summary.running === true,
1121
+ ongoing: summary.running === true || runningSubagentCount > 0,
1122
+ runningSubagentCount,
1123
+ completed: summary.completed === true,
1124
+ ...validUpdatedAt(summary.updatedAt) ? { updatedAt: summary.updatedAt } : {}
1125
+ }];
1126
+ }));
1127
+ }
1128
+ /** Aggregate only live ongoing activity; callers decide which IDs are in the group. */
1129
+ function hasOngoingSession(sessionIds, presentations) {
1130
+ return sessionIds.some((sessionId) => presentations[sessionId]?.ongoing === true);
1131
+ }
809
1132
  /** Read the native blank flag without inferring blankness from title or id. */
810
1133
  function isBlankSession(sessionId, sessions) {
811
1134
  return sessions.byId[sessionId]?.blank === true;
@@ -830,6 +1153,193 @@ window.__ModuleLoader__.load({
830
1153
  return (summary?.displayTitle ?? sessionId).toLocaleLowerCase().includes(normalizedQuery);
831
1154
  }
832
1155
  //#endregion
1156
+ //#region lib/client/worktree-session-order.js
1157
+ /** Browser-only persistence key; this state never crosses the DSH or sidecar boundary. */
1158
+ const WORKTREE_SESSION_ORDER_STORAGE_KEY = "clutch-dsh-worktree.session-order";
1159
+ function emptyState() {
1160
+ return { accounts: {} };
1161
+ }
1162
+ function isRecord(value) {
1163
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1164
+ }
1165
+ function isValidTimestamp(value) {
1166
+ return typeof value === "number" && Number.isFinite(value);
1167
+ }
1168
+ function normalizeIds(value) {
1169
+ if (!Array.isArray(value)) return [];
1170
+ const seen = /* @__PURE__ */ new Set();
1171
+ const ids = [];
1172
+ for (const id of value) {
1173
+ if (typeof id !== "string" || id.length === 0 || seen.has(id)) continue;
1174
+ seen.add(id);
1175
+ ids.push(id);
1176
+ }
1177
+ return ids;
1178
+ }
1179
+ function normalizeAccount(value) {
1180
+ if (!isRecord(value)) return void 0;
1181
+ const order = normalizeIds(value.order);
1182
+ const allowed = new Set(order);
1183
+ const observedUpdatedAt = {};
1184
+ if (isRecord(value.observedUpdatedAt)) {
1185
+ for (const [id, timestamp] of Object.entries(value.observedUpdatedAt)) if (allowed.has(id) && isValidTimestamp(timestamp)) observedUpdatedAt[id] = timestamp;
1186
+ }
1187
+ return {
1188
+ order,
1189
+ observedUpdatedAt
1190
+ };
1191
+ }
1192
+ /** Normalize persisted state without trusting any browser-local JSON shape. */
1193
+ function normalizeWorktreeSessionOrderState(value) {
1194
+ if (!isRecord(value) || !isRecord(value.accounts)) return emptyState();
1195
+ const accounts = {};
1196
+ for (const [accountKey, account] of Object.entries(value.accounts)) {
1197
+ if (accountKey.length === 0) continue;
1198
+ const normalized = normalizeAccount(account);
1199
+ if (normalized !== void 0) accounts[accountKey] = normalized;
1200
+ }
1201
+ return { accounts };
1202
+ }
1203
+ function validIds(ids) {
1204
+ return normalizeIds([...ids]);
1205
+ }
1206
+ function sameRecord(left, right) {
1207
+ if (left === void 0) return false;
1208
+ const leftKeys = Object.keys(left);
1209
+ const rightKeys = Object.keys(right);
1210
+ if (leftKeys.length !== rightKeys.length) return false;
1211
+ return rightKeys.every((key) => left[key] === right[key]);
1212
+ }
1213
+ function sameAccount(left, right) {
1214
+ return left !== void 0 && left.order.length === right.order.length && left.order.every((id, index) => id === right.order[index]) && sameRecord(left.observedUpdatedAt, right.observedUpdatedAt);
1215
+ }
1216
+ /**
1217
+ * Native-style activity order transition. The first observation records timestamps
1218
+ * without turning an existing list into a recency sort.
1219
+ */
1220
+ function nextSessionOrderAccount(input) {
1221
+ const baseIds = validIds(input.baseIds);
1222
+ const previous = input.previous;
1223
+ if (previous === void 0) {
1224
+ const observedUpdatedAt = {};
1225
+ for (const id of baseIds) {
1226
+ const timestamp = input.updatedAtById[id];
1227
+ if (isValidTimestamp(timestamp)) observedUpdatedAt[id] = timestamp;
1228
+ }
1229
+ return {
1230
+ order: baseIds,
1231
+ observedUpdatedAt
1232
+ };
1233
+ }
1234
+ const available = new Set(baseIds);
1235
+ const order = [];
1236
+ const seen = /* @__PURE__ */ new Set();
1237
+ for (const id of previous.order) if (available.has(id) && !seen.has(id)) {
1238
+ seen.add(id);
1239
+ order.push(id);
1240
+ }
1241
+ for (const id of baseIds) if (!seen.has(id)) {
1242
+ seen.add(id);
1243
+ order.push(id);
1244
+ }
1245
+ const observedUpdatedAt = {};
1246
+ const previousIds = new Set(previous.order);
1247
+ const newlyObserved = [];
1248
+ const promoted = [];
1249
+ for (const [index, id] of order.entries()) {
1250
+ if (!previousIds.has(id)) newlyObserved.push(id);
1251
+ const previousTimestamp = previous.observedUpdatedAt[id];
1252
+ const timestamp = input.updatedAtById[id];
1253
+ if (isValidTimestamp(previousTimestamp)) observedUpdatedAt[id] = previousTimestamp;
1254
+ if (isValidTimestamp(timestamp)) {
1255
+ if (!isValidTimestamp(previousTimestamp) || timestamp > previousTimestamp) {
1256
+ if (isValidTimestamp(previousTimestamp)) promoted.push({
1257
+ id,
1258
+ timestamp,
1259
+ index
1260
+ });
1261
+ observedUpdatedAt[id] = Math.max(observedUpdatedAt[id] ?? timestamp, timestamp);
1262
+ }
1263
+ }
1264
+ }
1265
+ promoted.sort((left, right) => right.timestamp - left.timestamp || left.index - right.index);
1266
+ const promotedIds = /* @__PURE__ */ new Set([...newlyObserved, ...promoted.map(({ id }) => id)]);
1267
+ return {
1268
+ order: [
1269
+ ...newlyObserved,
1270
+ ...promoted.map(({ id }) => id),
1271
+ ...order.filter((id) => !promotedIds.has(id))
1272
+ ],
1273
+ observedUpdatedAt
1274
+ };
1275
+ }
1276
+ /** Apply a successful manual drag without dropping IDs hidden by search. */
1277
+ function reorderSessionIds(order, sessionId, overSessionId, half) {
1278
+ const next = order.filter((id) => id !== sessionId);
1279
+ const targetIndex = next.indexOf(overSessionId);
1280
+ if (targetIndex === -1) return [...order];
1281
+ next.splice(half === "before" ? targetIndex : targetIndex + 1, 0, sessionId);
1282
+ return next;
1283
+ }
1284
+ function cloneAccount(account) {
1285
+ return {
1286
+ order: [...account.order],
1287
+ observedUpdatedAt: { ...account.observedUpdatedAt }
1288
+ };
1289
+ }
1290
+ /** Create one apply-scoped, browser-local Session ordering store. */
1291
+ function createWorktreeSessionOrderStore(snapshotStoreFactory) {
1292
+ let store;
1293
+ try {
1294
+ store = snapshotStoreFactory(emptyState(), { persist: { name: WORKTREE_SESSION_ORDER_STORAGE_KEY } });
1295
+ } catch {
1296
+ store = snapshotStoreFactory(emptyState());
1297
+ }
1298
+ store.set(normalizeWorktreeSessionOrderState(store.getSnapshot()));
1299
+ let disposed = false;
1300
+ return Object.assign(store, {
1301
+ actions: {
1302
+ reconcile: (accountKey, baseIds, updatedAtById) => {
1303
+ if (disposed || accountKey.length === 0) return;
1304
+ store.update((draft) => {
1305
+ const next = nextSessionOrderAccount({
1306
+ baseIds,
1307
+ updatedAtById,
1308
+ previous: draft.accounts[accountKey]
1309
+ });
1310
+ if (sameAccount(draft.accounts[accountKey], next)) return;
1311
+ draft.accounts[accountKey] = cloneAccount(next);
1312
+ });
1313
+ },
1314
+ setOrder: (accountKey, order) => {
1315
+ if (disposed || accountKey.length === 0) return;
1316
+ store.update((draft) => {
1317
+ const current = draft.accounts[accountKey] ?? {
1318
+ order: [],
1319
+ observedUpdatedAt: {}
1320
+ };
1321
+ const next = {
1322
+ order: validIds(order),
1323
+ observedUpdatedAt: { ...current.observedUpdatedAt }
1324
+ };
1325
+ if (sameAccount(current, next)) return;
1326
+ draft.accounts[accountKey] = next;
1327
+ });
1328
+ },
1329
+ retain: (accountKeys) => {
1330
+ if (disposed) return;
1331
+ store.update((draft) => {
1332
+ const allowed = new Set(accountKeys);
1333
+ for (const accountKey of Object.keys(draft.accounts)) if (!allowed.has(accountKey)) delete draft.accounts[accountKey];
1334
+ });
1335
+ }
1336
+ },
1337
+ dispose: () => {
1338
+ disposed = true;
1339
+ }
1340
+ });
1341
+ }
1342
+ //#endregion
833
1343
  //#region lib/client/worktree-view-errors.js
834
1344
  /** A DSH Session exists even when the external Worktree binding needs repair. */
835
1345
  var WorktreeSessionBindingError = class extends Error {
@@ -851,6 +1361,25 @@ window.__ModuleLoader__.load({
851
1361
  this.cause = cause;
852
1362
  }
853
1363
  };
1364
+ /** A Session is retained after permission confirmation or permission setup needs retry. */
1365
+ var WorktreeSessionPermissionError = class extends Error {
1366
+ code;
1367
+ retryable = true;
1368
+ details;
1369
+ sessionId;
1370
+ createdSession;
1371
+ cause;
1372
+ constructor(sessionId, createdSession, cause) {
1373
+ const reason = cause instanceof Error ? cause.message : cause === void 0 ? "" : String(cause);
1374
+ super(reason);
1375
+ this.name = "WorktreeSessionPermissionError";
1376
+ this.code = cause === void 0 ? "WORKTREE_PERMISSION_CONFIRMATION_REQUIRED" : "WORKTREE_PERMISSION_FAILED";
1377
+ this.details = { sessionId };
1378
+ this.sessionId = sessionId;
1379
+ this.createdSession = createdSession;
1380
+ this.cause = cause;
1381
+ }
1382
+ };
854
1383
  function recordDetails(value) {
855
1384
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
856
1385
  const details = value.details;
@@ -861,7 +1390,7 @@ window.__ModuleLoader__.load({
861
1390
  function toWorktreeViewError(error) {
862
1391
  if (typeof error === "object" && error !== null) {
863
1392
  const candidate = error;
864
- const details = error instanceof WorktreeSessionBindingError ? {
1393
+ const details = error instanceof WorktreeSessionBindingError || error instanceof WorktreeSessionPermissionError ? {
865
1394
  ...recordDetails(error) ?? {},
866
1395
  sessionId: error.sessionId
867
1396
  } : recordDetails(error);
@@ -903,6 +1432,21 @@ window.__ModuleLoader__.load({
903
1432
  }
904
1433
  throw new Error("Unable to generate an available default Worktree name.");
905
1434
  }
1435
+ /** Generate the next available numeric sibling name for a selected Worktree. */
1436
+ function createNumberedWorktreeName(baseName, existingNames) {
1437
+ const normalizedBaseName = baseName.trim();
1438
+ const usedNames = new Set([...existingNames].map((name) => name.trim()));
1439
+ const numberedBase = normalizedBaseName.match(/^(.*)-(\d+)$/);
1440
+ const prefix = numberedBase?.[1] || normalizedBaseName || "worktree";
1441
+ const parsedNumber = numberedBase === null ? 1 : Number(numberedBase[2]);
1442
+ let number = Number.isSafeInteger(parsedNumber) ? parsedNumber + 1 : 2;
1443
+ for (let attempt = 0; attempt < 1e4; attempt += 1) {
1444
+ const candidate = `${prefix}-${number}`;
1445
+ if (!usedNames.has(candidate)) return candidate;
1446
+ number += 1;
1447
+ }
1448
+ throw new Error("Unable to generate an available numbered Worktree name.");
1449
+ }
906
1450
  /** Resolve a row-half drop target to native-style optional before-anchor semantics. */
907
1451
  function resolveWorktreeMove(worktreeIds, sourceWorktreeId, targetWorktreeId, half) {
908
1452
  const sourceIndex = worktreeIds.indexOf(sourceWorktreeId);
@@ -915,10 +1459,19 @@ window.__ModuleLoader__.load({
915
1459
  return beforeWorktreeId === void 0 ? {} : { beforeWorktreeId };
916
1460
  }
917
1461
  /** Keep mutation routing in the browser Consumer while leaving wire details to the adapter. */
918
- async function executeWorktreeAction(manager, action) {
1462
+ async function executeWorktreeAction(manager, action, permission, onPermissionResult) {
919
1463
  if (action.type === "createWorktree") return manager.createWorktree(action.input);
1464
+ if (action.type === "importWorktree") return manager.importWorktree(action.input);
920
1465
  if (action.type === "removeWorktree") {
921
1466
  await manager.removeWorktree(action.input);
1467
+ const result = await permission?.normalizeDetachedWorktreePermissions({
1468
+ workspaceId: action.input.workspaceId,
1469
+ worktreeId: action.input.worktreeId
1470
+ });
1471
+ if (result !== void 0) onPermissionResult?.({
1472
+ workspaceId: action.input.workspaceId,
1473
+ worktreeId: action.input.worktreeId
1474
+ }, result);
922
1475
  return;
923
1476
  }
924
1477
  }
@@ -937,12 +1490,20 @@ window.__ModuleLoader__.load({
937
1490
  } catch (error) {
938
1491
  throw new WorktreeSessionBindingError(sessionId, error);
939
1492
  }
940
- input.beforeOpen?.(sessionId);
1493
+ await input.beforeOpen?.(sessionId);
941
1494
  input.openSession(sessionId);
942
1495
  return sessionId;
943
1496
  }
944
1497
  //#endregion
945
1498
  //#region lib/client/worktree-view-read.js
1499
+ /** Merge one freshly read Workspace projection without clearing other ready views. */
1500
+ function mergeWorktreeView(views, nextView) {
1501
+ const index = views.findIndex((view) => view.workspaceId === nextView.workspaceId);
1502
+ if (index === -1) return [...views, nextView];
1503
+ const merged = [...views];
1504
+ merged[index] = nextView;
1505
+ return merged;
1506
+ }
946
1507
  /** Reuse the previous ID snapshot when DSH republishes equivalent Workspace items. */
947
1508
  function stableWorkspaceIds(previous, next) {
948
1509
  if (previous.length !== next.length) return next;
@@ -1048,6 +1609,19 @@ window.__ModuleLoader__.load({
1048
1609
  readiness: branchesResult.value.length > 0 ? { status: "ready" } : { status: "noLocalBranch" }
1049
1610
  };
1050
1611
  }
1612
+ /** Isolate modal-target reads from full-surface refresh generations. */
1613
+ function createWorktreeModalViewLoader() {
1614
+ const guard = createWorktreeRefreshGuard();
1615
+ return {
1616
+ invalidate: guard.invalidate,
1617
+ load(manager, workspaceId, onSuccess, onError) {
1618
+ return guard.run(() => loadWorktreeView(manager, workspaceId), (data) => onSuccess({
1619
+ workspaceId,
1620
+ ...data
1621
+ }), (error) => onError(toWorktreeViewError(error)));
1622
+ }
1623
+ };
1624
+ }
1051
1625
  /** Load one independent projection per DSH Workspace for the flat sidebar hierarchy. */
1052
1626
  async function loadWorktreeViews(manager, workspaceIds, options = {}) {
1053
1627
  const views = await Promise.all(workspaceIds.map(async (workspaceId) => ({
@@ -1075,6 +1649,54 @@ window.__ModuleLoader__.load({
1075
1649
  this.details = details;
1076
1650
  }
1077
1651
  };
1652
+ /**
1653
+ * Keep the confirmation and second permission call in one place for new,
1654
+ * reused, and retried Sessions. The Host still validates the relation.
1655
+ */
1656
+ async function ensureWorktreeSessionPermission(input) {
1657
+ if (input.permission === void 0) return;
1658
+ const request = {
1659
+ workspaceId: input.workspaceId,
1660
+ worktreeId: input.worktreeId,
1661
+ sessionId: input.sessionId,
1662
+ binding: "active"
1663
+ };
1664
+ let result;
1665
+ try {
1666
+ result = await input.permission.ensureWorktreePermission(request);
1667
+ } catch (error) {
1668
+ throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession, error);
1669
+ }
1670
+ if (result.status === "confirmation-required") {
1671
+ let confirmed;
1672
+ try {
1673
+ confirmed = input.confirmFullAccess === void 0 ? false : await input.confirmFullAccess({
1674
+ workspaceId: input.workspaceId,
1675
+ worktreeId: input.worktreeId,
1676
+ sessionId: input.sessionId,
1677
+ cwd: input.cwd
1678
+ });
1679
+ } catch (error) {
1680
+ throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession, error);
1681
+ }
1682
+ if (!confirmed) throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession);
1683
+ try {
1684
+ result = await input.permission.ensureWorktreePermission({
1685
+ ...request,
1686
+ confirmed: true
1687
+ });
1688
+ } catch (error) {
1689
+ throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession, error);
1690
+ }
1691
+ }
1692
+ if (result.status === "confirmation-required") throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession);
1693
+ input.onPermissionResult?.({
1694
+ workspaceId: input.workspaceId,
1695
+ worktreeId: input.worktreeId,
1696
+ sessionId: input.sessionId,
1697
+ cwd: input.cwd
1698
+ }, result);
1699
+ }
1078
1700
  function resolveWorktreeSessionAction(input) {
1079
1701
  if (input.target.status !== "active" || input.target.health === "repair") return {
1080
1702
  kind: "reject",
@@ -1138,6 +1760,16 @@ window.__ModuleLoader__.load({
1138
1760
  } catch (error) {
1139
1761
  throw new WorktreeSessionBindingError(input.pending.sessionId, error);
1140
1762
  }
1763
+ await ensureWorktreeSessionPermission({
1764
+ permission: input.permission,
1765
+ confirmFullAccess: input.confirmFullAccess,
1766
+ onPermissionResult: input.onPermissionResult,
1767
+ sessionId: input.pending.sessionId,
1768
+ workspaceId: input.pending.workspaceId,
1769
+ worktreeId: input.pending.worktreeId,
1770
+ cwd: input.pending.cwd,
1771
+ createdSession: true
1772
+ });
1141
1773
  input.ensureSessionWorkspace(input.pending.workspaceId, input.pending.sessionId);
1142
1774
  input.openSession(input.pending.sessionId);
1143
1775
  return input.pending.sessionId;
@@ -1161,6 +1793,16 @@ window.__ModuleLoader__.load({
1161
1793
  bindings
1162
1794
  });
1163
1795
  if (action.kind === "open-bound") {
1796
+ await ensureWorktreeSessionPermission({
1797
+ permission: options.permission,
1798
+ confirmFullAccess: options.confirmFullAccess,
1799
+ onPermissionResult: options.onPermissionResult,
1800
+ sessionId: action.sessionId,
1801
+ workspaceId: input.workspaceId,
1802
+ worktreeId: input.worktreeId,
1803
+ cwd: input.cwd,
1804
+ createdSession: false
1805
+ });
1164
1806
  options.ensureSessionWorkspace(input.workspaceId, action.sessionId);
1165
1807
  if (!disposed) options.openSession(action.sessionId);
1166
1808
  return action.sessionId;
@@ -1175,6 +1817,16 @@ window.__ModuleLoader__.load({
1175
1817
  } catch (error) {
1176
1818
  throw new WorktreeSessionBindingError(action.sessionId, error);
1177
1819
  }
1820
+ await ensureWorktreeSessionPermission({
1821
+ permission: options.permission,
1822
+ confirmFullAccess: options.confirmFullAccess,
1823
+ onPermissionResult: options.onPermissionResult,
1824
+ sessionId: action.sessionId,
1825
+ workspaceId: input.workspaceId,
1826
+ worktreeId: input.worktreeId,
1827
+ cwd: input.cwd,
1828
+ createdSession: false
1829
+ });
1178
1830
  if (!disposed) {
1179
1831
  options.ensureSessionWorkspace(input.workspaceId, action.sessionId);
1180
1832
  if (!disposed) options.openSession(action.sessionId);
@@ -1187,9 +1839,18 @@ window.__ModuleLoader__.load({
1187
1839
  ...input,
1188
1840
  createSession: options.createSession,
1189
1841
  manager: options.manager,
1190
- beforeOpen: (sessionId) => {
1842
+ beforeOpen: async (sessionId) => {
1191
1843
  if (disposed) throw new WorktreeSessionActionError("CLIENT_DISPOSED", "", false);
1192
- options.ensureSessionWorkspace(input.workspaceId, sessionId);
1844
+ await ensureWorktreeSessionPermission({
1845
+ permission: options.permission,
1846
+ confirmFullAccess: options.confirmFullAccess,
1847
+ onPermissionResult: options.onPermissionResult,
1848
+ sessionId,
1849
+ workspaceId: input.workspaceId,
1850
+ worktreeId: input.worktreeId,
1851
+ cwd: input.cwd,
1852
+ createdSession: true
1853
+ });
1193
1854
  },
1194
1855
  openSession: (sessionId) => {
1195
1856
  if (!disposed) options.openSession(sessionId);
@@ -1348,10 +2009,12 @@ window.__ModuleLoader__.load({
1348
2009
  })
1349
2010
  });
1350
2011
  }
1351
- function WorktreeCreateDialog({ t, workspace, view, setupStatus, canCreate, selectedBranch, newBranch, actionPending, onClose, onSelectedBranchChange, onNewBranchChange, onSubmit }) {
2012
+ function WorktreeCreateDialog({ t, workspace, view, readError, setupStatus, canCreate, mode, importCandidates, selectedImportPath, selectedBranch, newBranch, actionPending, onClose, onRetry, onModeChange, onRetryImportCandidates, onSelectedImportPathChange, onSelectedBranchChange, onNewBranchChange, onSubmit }) {
1352
2013
  if (workspace === void 0) return null;
1353
2014
  const setupCommands = setupStatus === void 0 ? [] : worktreeSetupCommands(setupStatus);
1354
- return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2015
+ const importing = mode === "import";
2016
+ const importBlocked = actionPending || importCandidates.status === "loading" || selectedImportPath === void 0;
2017
+ return (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1355
2018
  open: true,
1356
2019
  onClose: () => {
1357
2020
  if (actionPending) return;
@@ -1359,7 +2022,7 @@ window.__ModuleLoader__.load({
1359
2022
  },
1360
2023
  closeLabel: t("dialog.closeWorktreeCreate"),
1361
2024
  title: t("worktree.createTitle"),
1362
- description: t("worktree.createDescription", { name: workspace.title }),
2025
+ description: importing ? t("worktree.importDescription", { name: workspace.title }) : t("worktree.createDescription", { name: workspace.title }),
1363
2026
  footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1364
2027
  variant: "outline",
1365
2028
  disabled: actionPending,
@@ -1367,13 +2030,104 @@ window.__ModuleLoader__.load({
1367
2030
  children: t("dialog.cancel")
1368
2031
  }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1369
2032
  variant: "primary",
1370
- disabled: actionPending || selectedBranch.length === 0 || newBranch.trim().length === 0,
2033
+ disabled: importing ? importBlocked : actionPending || !canCreate || selectedBranch.length === 0 || newBranch.trim().length === 0,
1371
2034
  onClick: () => {
1372
2035
  onSubmit();
1373
2036
  },
1374
- children: t("worktree.create")
2037
+ children: importing ? t("worktree.import") : t("worktree.create")
1375
2038
  })] }),
1376
- children: canCreate ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("label", {
2039
+ children: [(0, react_jsx_runtime.jsxs)("div", {
2040
+ className: worktree_css_default.worktreeModalTabs,
2041
+ role: "tablist",
2042
+ "aria-label": t("worktree.title"),
2043
+ children: [(0, react_jsx_runtime.jsx)("button", {
2044
+ className: worktree_css_default.worktreeModalTab,
2045
+ type: "button",
2046
+ role: "tab",
2047
+ "aria-selected": mode === "create",
2048
+ disabled: actionPending,
2049
+ onClick: () => {
2050
+ onModeChange("create");
2051
+ },
2052
+ children: t("worktree.tabCreate")
2053
+ }), (0, react_jsx_runtime.jsx)("button", {
2054
+ className: worktree_css_default.worktreeModalTab,
2055
+ type: "button",
2056
+ role: "tab",
2057
+ "aria-selected": mode === "import",
2058
+ disabled: actionPending,
2059
+ onClick: () => {
2060
+ onModeChange("import");
2061
+ },
2062
+ children: t("worktree.tabImport")
2063
+ })]
2064
+ }), importing ? (0, react_jsx_runtime.jsxs)("div", {
2065
+ className: worktree_css_default.worktreeImportCandidates,
2066
+ role: "tabpanel",
2067
+ children: [
2068
+ importCandidates.status === "loading" && importCandidates.candidates.length === 0 && (0, react_jsx_runtime.jsx)("p", {
2069
+ className: worktree_css_default.message,
2070
+ children: t("worktree.importLoading")
2071
+ }),
2072
+ importCandidates.status === "error" && (0, react_jsx_runtime.jsxs)("div", {
2073
+ className: worktree_css_default.worktreeImportError,
2074
+ role: "alert",
2075
+ children: [(0, react_jsx_runtime.jsx)("p", {
2076
+ className: worktree_css_default.message,
2077
+ children: formatWorktreeViewError(importCandidates.error, t)
2078
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2079
+ variant: "outline",
2080
+ disabled: actionPending,
2081
+ onClick: onRetryImportCandidates,
2082
+ children: t("action.retry")
2083
+ })]
2084
+ }),
2085
+ importCandidates.candidates.length === 0 && importCandidates.status === "ready" && (0, react_jsx_runtime.jsx)("p", {
2086
+ className: worktree_css_default.message,
2087
+ children: t("worktree.importEmpty")
2088
+ }),
2089
+ importCandidates.candidates.length > 0 && (0, react_jsx_runtime.jsxs)("label", {
2090
+ className: worktree_css_default.modalField,
2091
+ children: [t("worktree.tabImport"), (0, react_jsx_runtime.jsxs)("select", {
2092
+ className: worktree_css_default.worktreeImportSelect,
2093
+ "data-worktree-import-select": true,
2094
+ "aria-label": t("worktree.tabImport"),
2095
+ value: selectedImportPath ?? "",
2096
+ disabled: actionPending,
2097
+ onChange: (event) => {
2098
+ onSelectedImportPathChange(event.currentTarget.value);
2099
+ },
2100
+ children: [(0, react_jsx_runtime.jsx)("option", {
2101
+ value: "",
2102
+ disabled: true,
2103
+ children: t("worktree.importPlaceholder")
2104
+ }), importCandidates.candidates.map((candidate) => (0, react_jsx_runtime.jsxs)("option", {
2105
+ value: candidate.absolutePath,
2106
+ children: [
2107
+ candidate.branch,
2108
+ " — ",
2109
+ candidate.absolutePath
2110
+ ]
2111
+ }, candidate.absolutePath))]
2112
+ })]
2113
+ })
2114
+ ]
2115
+ }) : readError !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
2116
+ className: worktree_css_default.gitReadiness,
2117
+ "data-worktree-readiness": "error",
2118
+ role: "alert",
2119
+ children: [(0, react_jsx_runtime.jsx)("p", {
2120
+ className: worktree_css_default.message,
2121
+ "data-error": "true",
2122
+ children: formatWorktreeViewError(readError, t)
2123
+ }), readError.retryable && onRetry !== void 0 && (0, react_jsx_runtime.jsx)("button", {
2124
+ type: "button",
2125
+ className: worktree_css_default.retryButton,
2126
+ disabled: actionPending,
2127
+ onClick: onRetry,
2128
+ children: t("action.retry")
2129
+ })]
2130
+ }) : canCreate ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("label", {
1377
2131
  className: worktree_css_default.modalField,
1378
2132
  children: [t("worktree.baseBranch"), (0, react_jsx_runtime.jsx)("select", {
1379
2133
  className: worktree_css_default.actionSelect,
@@ -1416,7 +2170,7 @@ window.__ModuleLoader__.load({
1416
2170
  "aria-label": t("worktree.setup.commands"),
1417
2171
  children: setupCommands.join("\n")
1418
2172
  })]
1419
- })
2173
+ })]
1420
2174
  });
1421
2175
  }
1422
2176
  function WorktreeRemovalDialog({ t, worktree, actionPending, onClose, onSubmit }) {
@@ -1429,7 +2183,7 @@ window.__ModuleLoader__.load({
1429
2183
  },
1430
2184
  closeLabel: t("dialog.closeWorktreeRemove"),
1431
2185
  title: t("worktree.remove"),
1432
- description: t("worktree.removeDescription", { name: worktree.branch }),
2186
+ description: worktree.source === "external" ? t("worktree.removeExternalDescription", { name: worktree.branch }) : t("worktree.removeDescription", { name: worktree.branch }),
1433
2187
  footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1434
2188
  variant: "outline",
1435
2189
  disabled: actionPending,
@@ -1453,6 +2207,17 @@ window.__ModuleLoader__.load({
1453
2207
  function bindingIdsFor(bindings, worktreeId) {
1454
2208
  return bindings.filter((binding) => binding.worktreeId === worktreeId).map((binding) => binding.sessionId);
1455
2209
  }
2210
+ function clearSessionGroupExpansion(current, groupKeys) {
2211
+ const next = { ...current };
2212
+ for (const key of groupKeys) delete next[key];
2213
+ return next;
2214
+ }
2215
+ function isCompleteWorktreeWorkspaceSnapshot(workspaceIds, views) {
2216
+ if (views.length !== workspaceIds.length) return false;
2217
+ const expected = new Set(workspaceIds);
2218
+ const actual = new Set(views.map((view) => view.workspaceId));
2219
+ return actual.size === expected.size && [...expected].every((id) => actual.has(id));
2220
+ }
1456
2221
  function includesText(value, query) {
1457
2222
  return value.toLocaleLowerCase().includes(query);
1458
2223
  }
@@ -1464,14 +2229,118 @@ window.__ModuleLoader__.load({
1464
2229
  if (view.worktrees.some((record) => includesText(record.branch, query) || includesText(record.absolutePath, query))) return true;
1465
2230
  return view.bindings.some((binding) => sessionMatchesQuery(binding.sessionId, sessions, query));
1466
2231
  }
2232
+ function resolveCurrentSessionLocation(currentSessionId, workspaces, views) {
2233
+ if (currentSessionId === void 0) return void 0;
2234
+ const workspace = workspaces.find((candidate) => candidate.sessionIds.includes(currentSessionId));
2235
+ if (workspace === void 0) return void 0;
2236
+ const view = views.find((candidate) => candidate.workspaceId === workspace.workspaceId);
2237
+ if (view === void 0) return void 0;
2238
+ const binding = view.bindings.find((candidate) => candidate.sessionId === currentSessionId);
2239
+ if (binding === void 0) return {
2240
+ sessionId: currentSessionId,
2241
+ workspaceId: workspace.workspaceId,
2242
+ groupKey: "main:" + workspace.workspaceId,
2243
+ kind: "main"
2244
+ };
2245
+ const worktree = view.worktrees.find((candidate) => candidate.worktreeId === binding.worktreeId && candidate.workspaceId === workspace.workspaceId);
2246
+ if (worktree === void 0) return void 0;
2247
+ return {
2248
+ sessionId: currentSessionId,
2249
+ workspaceId: workspace.workspaceId,
2250
+ groupKey: "worktree:" + worktree.worktreeId,
2251
+ kind: "worktree",
2252
+ worktreeId: worktree.worktreeId
2253
+ };
2254
+ }
2255
+ function currentSessionRevealKeys(location) {
2256
+ if (location === void 0) return [];
2257
+ return [
2258
+ "workspace:" + location.workspaceId,
2259
+ location.kind === "main" ? "main:" + location.workspaceId : "worktree:" + location.worktreeId,
2260
+ "session-group:" + location.groupKey
2261
+ ];
2262
+ }
2263
+ function shouldRevealCurrentSessionGroup(sessionIds, currentSessionId) {
2264
+ if (currentSessionId === void 0 || sessionIds.length <= 5) return false;
2265
+ return sessionIds.indexOf(currentSessionId) >= 5;
2266
+ }
2267
+ function isSessionGroupAutoExpanded(sessionIds, currentSessionId, currentSessionRevealActive) {
2268
+ return currentSessionRevealActive && shouldRevealCurrentSessionGroup(sessionIds, currentSessionId);
2269
+ }
2270
+ //#endregion
2271
+ //#region lib/client/worktree-session-position.js
2272
+ function scrollCurrentSessionIntoView(root, sessionId) {
2273
+ if (root === null) return false;
2274
+ const row = Array.from(root.querySelectorAll("[data-session-id]")).find((candidate) => candidate.dataset.sessionId === sessionId);
2275
+ if (row === void 0) return false;
2276
+ row.scrollIntoView({ block: "nearest" });
2277
+ return true;
2278
+ }
1467
2279
  //#endregion
1468
2280
  //#region lib/client/worktree-surface-rows.js
1469
2281
  function rowHalf(event) {
1470
2282
  const rect = event.currentTarget.getBoundingClientRect();
1471
2283
  return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
1472
2284
  }
2285
+ function sessionStatusLabel(t, status) {
2286
+ switch (status.labelKey) {
2287
+ case "running": return t("session.status.running");
2288
+ case "subagentsRunning": return t(status.runningSubagentCount === 1 ? "session.status.subagentsRunning.one" : "session.status.subagentsRunning.other", { n: status.runningSubagentCount });
2289
+ case "idle": return t("session.status.idle");
2290
+ case "waitingApproval": return t("session.status.waitingApproval");
2291
+ case "planReview": return t("session.status.planReview");
2292
+ case "waitingAnswer": return t("session.status.waitingAnswer");
2293
+ case "completed": return t("session.status.completed");
2294
+ }
2295
+ }
2296
+ function sessionTimeLabel(t, value) {
2297
+ switch (value.unit) {
2298
+ case "now": return t("session.time.now");
2299
+ case "minutes": return t("session.time.minutes", { n: value.n });
2300
+ case "hours": return t("session.time.hours", { n: value.n });
2301
+ case "days": return t("session.time.days", { n: value.n });
2302
+ case "months": return t("session.time.months", { n: value.n });
2303
+ case "years": return t("session.time.years", { n: value.n });
2304
+ }
2305
+ }
2306
+ /** Native Workspace hover-card time copy, including the trailing "ago" marker. */
2307
+ function sessionHoverTimeLabel(t, value) {
2308
+ const label = sessionTimeLabel(t, value);
2309
+ return value.unit === "now" ? label : t("session.time.ago", { t: label });
2310
+ }
2311
+ /** Native Workspace hover-card status list, including a secondary subagent status when relevant. */
2312
+ function sessionHoverStatuses(presentation) {
2313
+ if (presentation.runningSubagentCount === 0 || presentation.status.labelKey === "subagentsRunning") return [presentation.status];
2314
+ return [presentation.status, {
2315
+ state: "ongoing",
2316
+ labelKey: "subagentsRunning",
2317
+ runningSubagentCount: presentation.runningSubagentCount
2318
+ }];
2319
+ }
2320
+ /** Native Workspace Session hover-card details, adapted to the Worktree row projection. */
2321
+ function WorktreeSessionHoverContent({ label, blank, presentation, t }) {
2322
+ const statuses = presentation === void 0 ? [] : sessionHoverStatuses(presentation);
2323
+ const timeValue = !blank && presentation?.updatedAt !== void 0 ? relativeTime(presentation.updatedAt, Date.now()) : void 0;
2324
+ return (0, react_jsx_runtime.jsxs)("div", {
2325
+ className: worktree_css_default.sessionHoverContent,
2326
+ children: [
2327
+ (0, react_jsx_runtime.jsx)("div", {
2328
+ className: worktree_css_default.sessionHoverTitle,
2329
+ children: label
2330
+ }),
2331
+ !blank && timeValue !== void 0 && (0, react_jsx_runtime.jsx)("div", {
2332
+ className: worktree_css_default.sessionHoverTime,
2333
+ children: sessionHoverTimeLabel(t, timeValue)
2334
+ }),
2335
+ statuses.map((status) => (0, react_jsx_runtime.jsxs)("div", {
2336
+ className: worktree_css_default.sessionHoverStatus,
2337
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: status.state }), (0, react_jsx_runtime.jsx)("span", { children: sessionStatusLabel(t, status) })]
2338
+ }, `${status.labelKey}-${status.runningSubagentCount}`))
2339
+ ]
2340
+ });
2341
+ }
1473
2342
  /** Workspace row using the native DSH menu, fixed action column, and drag contract. */
1474
- function WorktreeWorkspaceRow({ t, workspace, expanded, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange }) {
2343
+ function WorktreeWorkspaceRow({ t, workspace, expanded, hasOngoingSession, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange }) {
1475
2344
  const workspaceMenuItems = [{
1476
2345
  id: "rename",
1477
2346
  label: t("workspace.rename"),
@@ -1488,6 +2357,7 @@ window.__ModuleLoader__.load({
1488
2357
  return (0, react_jsx_runtime.jsxs)("div", {
1489
2358
  className: `${worktree_css_default.workspaceRow} ${markerClass}`,
1490
2359
  "data-workspace-drag": drag.active ? "active" : void 0,
2360
+ "data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
1491
2361
  "data-menu-open": menuOpen || void 0,
1492
2362
  draggable: true,
1493
2363
  onClick: () => {
@@ -1533,50 +2403,61 @@ window.__ModuleLoader__.load({
1533
2403
  }),
1534
2404
  (0, react_jsx_runtime.jsxs)("span", {
1535
2405
  className: `${worktree_css_default.treeActionSlot} ${worktree_css_default.workspaceActions}`,
1536
- children: [(0, react_jsx_runtime.jsx)("span", {
1537
- className: worktree_css_default.menuAction,
1538
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1539
- open: menuOpen,
1540
- onClose: () => {
1541
- onMenuOpenChange(false);
1542
- },
1543
- items: workspaceMenuItems,
1544
- onSelect: (id) => {
1545
- onMenuOpenChange(false);
1546
- if (id === "rename") onRename();
1547
- if (id === "delete") onDelete();
1548
- },
1549
- portal: true,
1550
- closeOnPointerLeave: true,
1551
- anchor: (0, react_jsx_runtime.jsx)("button", {
1552
- type: "button",
1553
- className: worktree_css_default.iconButton,
1554
- "data-workspace-menu": true,
1555
- "aria-label": t("workspace.options", { name: workspace.title }),
1556
- onClick: (event) => {
1557
- event.stopPropagation();
1558
- onMenuOpenChange(!menuOpen);
2406
+ children: [
2407
+ (0, react_jsx_runtime.jsx)("span", {
2408
+ className: worktree_css_default.groupActivity,
2409
+ "data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
2410
+ role: hasOngoingSession && !expanded ? "img" : void 0,
2411
+ "aria-label": hasOngoingSession && !expanded ? t("session.status.running") : void 0,
2412
+ title: hasOngoingSession && !expanded ? t("session.status.running") : void 0,
2413
+ children: hasOngoingSession && !expanded && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" })
2414
+ }),
2415
+ (0, react_jsx_runtime.jsx)("span", {
2416
+ className: worktree_css_default.menuAction,
2417
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2418
+ open: menuOpen,
2419
+ onClose: () => {
2420
+ onMenuOpenChange(false);
1559
2421
  },
1560
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2422
+ items: workspaceMenuItems,
2423
+ onSelect: (id) => {
2424
+ onMenuOpenChange(false);
2425
+ if (id === "rename") onRename();
2426
+ if (id === "delete") onDelete();
2427
+ },
2428
+ portal: true,
2429
+ closeOnPointerLeave: true,
2430
+ anchor: (0, react_jsx_runtime.jsx)("button", {
2431
+ type: "button",
2432
+ className: worktree_css_default.iconButton,
2433
+ "data-workspace-menu": true,
2434
+ "aria-label": t("workspace.options", { name: workspace.title }),
2435
+ onClick: (event) => {
2436
+ event.stopPropagation();
2437
+ onMenuOpenChange(!menuOpen);
2438
+ },
2439
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2440
+ })
1561
2441
  })
2442
+ }),
2443
+ (0, react_jsx_runtime.jsx)("button", {
2444
+ type: "button",
2445
+ className: worktree_css_default.iconButton,
2446
+ "data-add-worktree": true,
2447
+ "aria-label": t("workspace.addWorktree", { name: workspace.title }),
2448
+ onClick: (event) => {
2449
+ event.stopPropagation();
2450
+ onCreateWorktree();
2451
+ },
2452
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
1562
2453
  })
1563
- }), (0, react_jsx_runtime.jsx)("button", {
1564
- type: "button",
1565
- className: worktree_css_default.iconButton,
1566
- "data-add-worktree": true,
1567
- "aria-label": t("workspace.addWorktree", { name: workspace.title }),
1568
- onClick: (event) => {
1569
- event.stopPropagation();
1570
- onCreateWorktree();
1571
- },
1572
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
1573
- })]
2454
+ ]
1574
2455
  })
1575
2456
  ]
1576
2457
  });
1577
2458
  }
1578
2459
  /** Shared Main/Worktree group row with parameterized icon, state, and actions. */
1579
- function WorktreeGroupRow({ t, kind, label, worktreeId, expanded, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession, menu, drag }) {
2460
+ function WorktreeGroupRow({ t, kind, label, worktreeId, expanded, hasOngoingSession, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession, menu, drag }) {
1580
2461
  const main = kind === "main";
1581
2462
  const markerClass = drag?.marker === "before" ? worktree_css_default.dropBefore : drag?.marker === "after" ? worktree_css_default.dropAfter : "";
1582
2463
  const dragProps = drag === void 0 ? {} : {
@@ -1603,6 +2484,7 @@ window.__ModuleLoader__.load({
1603
2484
  className: `${worktree_css_default.worktreeRow} ${markerClass}`,
1604
2485
  "data-main-group": main ? "true" : void 0,
1605
2486
  "data-main-expanded": main ? String(expanded) : void 0,
2487
+ "data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
1606
2488
  "data-menu-open": menu?.open ? "true" : void 0,
1607
2489
  "data-worktree-drag": drag?.active ? "active" : void 0,
1608
2490
  ...dragProps,
@@ -1637,50 +2519,77 @@ window.__ModuleLoader__.load({
1637
2519
  }),
1638
2520
  (0, react_jsx_runtime.jsxs)("span", {
1639
2521
  className: worktree_css_default.treeActionSlot,
1640
- children: [menu !== void 0 && (0, react_jsx_runtime.jsx)("span", {
1641
- className: worktree_css_default.menuAction,
1642
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1643
- open: menu.open,
1644
- onClose: () => {
1645
- menu.onOpenChange(false);
1646
- },
1647
- items: [{
1648
- id: "remove",
1649
- label: t("worktree.remove"),
1650
- icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}),
1651
- danger: true,
1652
- disabled: menu.disabled
1653
- }],
1654
- onSelect: (id) => {
1655
- menu.onOpenChange(false);
1656
- if (id === "remove") menu.onRemove();
1657
- },
1658
- portal: true,
1659
- closeOnPointerLeave: true,
1660
- anchor: (0, react_jsx_runtime.jsx)("button", {
1661
- type: "button",
1662
- className: worktree_css_default.iconButton,
1663
- "data-worktree-menu": true,
1664
- "aria-label": t("worktree.options", { name: menu.label }),
1665
- onClick: (event) => {
1666
- event.stopPropagation();
1667
- menu.onOpenChange(!menu.open);
2522
+ children: [
2523
+ (0, react_jsx_runtime.jsx)("span", {
2524
+ className: worktree_css_default.groupActivity,
2525
+ "data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
2526
+ role: hasOngoingSession && !expanded ? "img" : void 0,
2527
+ "aria-label": hasOngoingSession && !expanded ? t("session.status.running") : void 0,
2528
+ title: hasOngoingSession && !expanded ? t("session.status.running") : void 0,
2529
+ children: hasOngoingSession && !expanded && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" })
2530
+ }),
2531
+ menu !== void 0 && (0, react_jsx_runtime.jsx)("span", {
2532
+ className: worktree_css_default.menuAction,
2533
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2534
+ open: menu.open,
2535
+ onClose: () => {
2536
+ menu.onOpenChange(false);
1668
2537
  },
1669
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2538
+ items: [
2539
+ ...menu.showCreate ? [{
2540
+ id: "create",
2541
+ label: t("worktree.createNew"),
2542
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {}),
2543
+ disabled: menu.disabled || menu.onCreateWorktree === void 0
2544
+ }] : [],
2545
+ {
2546
+ id: "copy-path",
2547
+ label: t("worktree.copyPath"),
2548
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}),
2549
+ disabled: menu.disabled
2550
+ },
2551
+ ...menu.showRemove ? [{
2552
+ id: "remove",
2553
+ label: t("worktree.remove"),
2554
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}),
2555
+ danger: true,
2556
+ disabled: menu.disabled || menu.onRemove === void 0
2557
+ }] : []
2558
+ ],
2559
+ onSelect: (id) => {
2560
+ menu.onOpenChange(false);
2561
+ if (id === "create" && menu.showCreate) menu.onCreateWorktree?.();
2562
+ if (id === "copy-path") (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(menu.copyPath);
2563
+ if (id === "remove" && menu.showRemove) menu.onRemove?.();
2564
+ },
2565
+ portal: true,
2566
+ closeOnPointerLeave: true,
2567
+ anchor: (0, react_jsx_runtime.jsx)("button", {
2568
+ type: "button",
2569
+ className: worktree_css_default.iconButton,
2570
+ "data-worktree-menu": true,
2571
+ "aria-label": t("worktree.options", { name: menu.label }),
2572
+ onClick: (event) => {
2573
+ event.stopPropagation();
2574
+ menu.onOpenChange(!menu.open);
2575
+ },
2576
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2577
+ })
1670
2578
  })
2579
+ }),
2580
+ onCreateSession !== void 0 && (0, react_jsx_runtime.jsx)("button", {
2581
+ type: "button",
2582
+ className: worktree_css_default.iconButton,
2583
+ "data-add-main-session": main ? "true" : void 0,
2584
+ "data-add-session": main ? void 0 : "true",
2585
+ "aria-label": t("worktree.addSession", { name: workspaceTitle }),
2586
+ onClick: (event) => {
2587
+ event.stopPropagation();
2588
+ onCreateSession();
2589
+ },
2590
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
1671
2591
  })
1672
- }), onCreateSession !== void 0 && (0, react_jsx_runtime.jsx)("button", {
1673
- type: "button",
1674
- className: worktree_css_default.iconButton,
1675
- "data-add-main-session": main ? "true" : void 0,
1676
- "data-add-session": main ? void 0 : "true",
1677
- "aria-label": t("worktree.addSession", { name: workspaceTitle }),
1678
- onClick: (event) => {
1679
- event.stopPropagation();
1680
- onCreateSession();
1681
- },
1682
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
1683
- })]
2592
+ ]
1684
2593
  })
1685
2594
  ]
1686
2595
  });
@@ -1696,7 +2605,7 @@ window.__ModuleLoader__.load({
1696
2605
  });
1697
2606
  }
1698
2607
  /** Worktree-mode Session row with the same trailing options menu as native DSH rows. */
1699
- function WorktreeSessionRow({ t, sessionId, blank, label, drag, actionPending, onOpen, onRename, onFork, onArchive }) {
2608
+ function WorktreeSessionRow({ t, sessionId, blank, current, label, drag, actionPending, onOpen, onRename, onFork, onArchive, presentation }) {
1700
2609
  const [menuOpen, setMenuOpen] = (0, react.useState)(false);
1701
2610
  const markerClass = drag.marker === "before" ? worktree_css_default.dropBefore : drag.marker === "after" ? worktree_css_default.dropAfter : "";
1702
2611
  const sessionMenuItems = [
@@ -1719,82 +2628,116 @@ window.__ModuleLoader__.load({
1719
2628
  disabled: onArchive === void 0 || actionPending
1720
2629
  }
1721
2630
  ];
1722
- return (0, react_jsx_runtime.jsxs)("div", {
1723
- className: `${worktree_css_default.treeSessionRow} ${markerClass}`,
1724
- "data-session-id": sessionId,
1725
- "data-session-blank": blank ? "true" : void 0,
1726
- "data-session-drag": drag.active ? "active" : void 0,
1727
- "data-menu-open": menuOpen || void 0,
1728
- role: "treeitem",
1729
- draggable: true,
1730
- onDragStart: (event) => {
1731
- event.dataTransfer.effectAllowed = "move";
1732
- event.dataTransfer.setData("text/plain", sessionId);
1733
- drag.start();
1734
- },
1735
- onDragEnd: drag.end,
1736
- onDragOver: (event) => {
1737
- if (!drag.active) return;
1738
- event.preventDefault();
1739
- event.dataTransfer.dropEffect = "move";
1740
- drag.hover(rowHalf(event));
1741
- },
1742
- onDrop: (event) => {
1743
- if (!drag.active) return;
1744
- event.preventDefault();
1745
- drag.drop(rowHalf(event));
1746
- },
1747
- children: [(0, react_jsx_runtime.jsxs)("button", {
1748
- type: "button",
1749
- className: worktree_css_default.treeSessionContent,
1750
- onClick: onOpen,
1751
- children: [(0, react_jsx_runtime.jsx)("span", {
1752
- className: worktree_css_default.treeGuide,
1753
- "aria-hidden": "true",
1754
- children: "└"
1755
- }), (0, react_jsx_runtime.jsx)("span", {
1756
- className: worktree_css_default.sessionLabel,
1757
- children: label
2631
+ const statusLabel = presentation === void 0 ? void 0 : sessionStatusLabel(t, presentation.status);
2632
+ const showTrailingStatus = !blank && presentation !== void 0 && (presentation.status.state !== "done" || presentation.completed);
2633
+ const timeValue = !blank && !showTrailingStatus && presentation?.updatedAt !== void 0 ? relativeTime(presentation.updatedAt, Date.now()) : void 0;
2634
+ const timeLabel = timeValue === void 0 ? void 0 : sessionTimeLabel(t, timeValue);
2635
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.HoverCard, {
2636
+ anchor: (0, react_jsx_runtime.jsxs)("div", {
2637
+ className: `${worktree_css_default.treeSessionRow} ${markerClass}`,
2638
+ "data-session-id": sessionId,
2639
+ "data-session-blank": blank ? "true" : void 0,
2640
+ "data-session-current": current ? "true" : void 0,
2641
+ "data-session-drag": drag.active ? "active" : void 0,
2642
+ "data-menu-open": menuOpen || void 0,
2643
+ role: "treeitem",
2644
+ "aria-current": current ? "true" : void 0,
2645
+ draggable: true,
2646
+ onDragStart: (event) => {
2647
+ event.dataTransfer.effectAllowed = "move";
2648
+ event.dataTransfer.setData("text/plain", sessionId);
2649
+ drag.start();
2650
+ },
2651
+ onDragEnd: drag.end,
2652
+ onDragOver: (event) => {
2653
+ if (!drag.active) return;
2654
+ event.preventDefault();
2655
+ event.dataTransfer.dropEffect = "move";
2656
+ drag.hover(rowHalf(event));
2657
+ },
2658
+ onDrop: (event) => {
2659
+ if (!drag.active) return;
2660
+ event.preventDefault();
2661
+ drag.drop(rowHalf(event));
2662
+ },
2663
+ children: [(0, react_jsx_runtime.jsx)("button", {
2664
+ type: "button",
2665
+ className: worktree_css_default.treeSessionContent,
2666
+ onClick: onOpen,
2667
+ children: (0, react_jsx_runtime.jsx)("span", {
2668
+ className: worktree_css_default.sessionLabel,
2669
+ children: label
2670
+ })
2671
+ }), !blank && (0, react_jsx_runtime.jsxs)("span", {
2672
+ className: worktree_css_default.sessionTrailing,
2673
+ children: [(0, react_jsx_runtime.jsx)("span", {
2674
+ className: worktree_css_default.sessionMeta,
2675
+ children: showTrailingStatus && presentation !== void 0 ? (0, react_jsx_runtime.jsx)("span", {
2676
+ className: worktree_css_default.sessionStatus,
2677
+ "data-session-status": statusLabel,
2678
+ role: "img",
2679
+ "aria-label": statusLabel,
2680
+ title: statusLabel,
2681
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: presentation.status.state })
2682
+ }) : timeLabel !== void 0 ? (0, react_jsx_runtime.jsx)("span", {
2683
+ className: worktree_css_default.sessionTime,
2684
+ "data-session-time": true,
2685
+ children: timeLabel
2686
+ }) : null
2687
+ }), (0, react_jsx_runtime.jsx)("span", {
2688
+ className: worktree_css_default.rowActions,
2689
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2690
+ open: menuOpen,
2691
+ onClose: () => {
2692
+ setMenuOpen(false);
2693
+ },
2694
+ items: sessionMenuItems,
2695
+ onSelect: (id) => {
2696
+ setMenuOpen(false);
2697
+ if (id === "rename") onRename?.(sessionId, label);
2698
+ if (id === "fork") onFork?.(sessionId);
2699
+ if (id === "archive") onArchive?.(sessionId);
2700
+ },
2701
+ portal: true,
2702
+ closeOnPointerLeave: true,
2703
+ anchor: (0, react_jsx_runtime.jsx)("button", {
2704
+ type: "button",
2705
+ className: worktree_css_default.iconButton,
2706
+ "data-session-menu": true,
2707
+ "aria-label": t("session.options", { name: label }),
2708
+ onClick: (event) => {
2709
+ event.stopPropagation();
2710
+ setMenuOpen((current) => !current);
2711
+ },
2712
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2713
+ })
2714
+ })
2715
+ })]
1758
2716
  })]
1759
- }), !blank && (0, react_jsx_runtime.jsx)("span", {
1760
- className: worktree_css_default.rowActions,
1761
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1762
- open: menuOpen,
1763
- onClose: () => {
1764
- setMenuOpen(false);
1765
- },
1766
- items: sessionMenuItems,
1767
- onSelect: (id) => {
1768
- setMenuOpen(false);
1769
- if (id === "rename") onRename?.(sessionId, label);
1770
- if (id === "fork") onFork?.(sessionId);
1771
- if (id === "archive") onArchive?.(sessionId);
1772
- },
1773
- portal: true,
1774
- closeOnPointerLeave: true,
1775
- anchor: (0, react_jsx_runtime.jsx)("button", {
1776
- type: "button",
1777
- className: worktree_css_default.iconButton,
1778
- "data-session-menu": true,
1779
- "aria-label": t("session.options", { name: label }),
1780
- onClick: (event) => {
1781
- event.stopPropagation();
1782
- setMenuOpen((current) => !current);
1783
- },
1784
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
1785
- })
1786
- })
1787
- })]
2717
+ }),
2718
+ content: (0, react_jsx_runtime.jsx)(WorktreeSessionHoverContent, {
2719
+ label,
2720
+ blank,
2721
+ presentation,
2722
+ t
2723
+ }),
2724
+ openDelayMs: 500,
2725
+ disabled: menuOpen || drag.active,
2726
+ copyText: blank ? void 0 : label,
2727
+ copyLabel: t("copy"),
2728
+ copiedLabel: t("hover.copied")
1788
2729
  });
1789
2730
  }
1790
- function WorktreeSessionGroup({ t, groupKey, sessionIds, workspaceId, expanded, actionPending, sessions, dragState, onToggleExpanded, onStartDrag, onHoverDrag, onClearDrag, onFinishDrag, onCommitDrag, onOpen, onRename, onFork, onArchive }) {
2731
+ function WorktreeSessionGroup({ t, groupKey, sessionIds, workspaceId, currentSessionId, expanded, actionPending, sessions, sessionPresentations, dragState, onToggleExpanded, onStartDrag, onHoverDrag, onClearDrag, onFinishDrag, onCommitDrag, onOpen, onRename, onFork, onArchive }) {
1791
2732
  const visibleSessionIds = expanded ? sessionIds : sessionIds.slice(0, 5);
1792
2733
  const sameGroupDrag = dragState?.groupKey === groupKey;
1793
2734
  return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [visibleSessionIds.map((sessionId) => (0, react_jsx_runtime.jsx)(WorktreeSessionRow, {
1794
2735
  t,
1795
2736
  sessionId,
1796
2737
  blank: isBlankSession(sessionId, sessions),
2738
+ current: sessionId === currentSessionId,
1797
2739
  label: sessionLabel(sessionId, sessions, t),
2740
+ presentation: sessionPresentations[sessionId],
1798
2741
  drag: {
1799
2742
  active: sameGroupDrag,
1800
2743
  marker: sameGroupDrag && dragState.over?.sessionId === sessionId ? dragState.over.half : null,
@@ -1846,6 +2789,33 @@ window.__ModuleLoader__.load({
1846
2789
  status: "idle",
1847
2790
  views: []
1848
2791
  };
2792
+ const EMPTY_PERMISSION_NOTICE = void 0;
2793
+ const EMPTY_PERMISSION_NOTICE_SUBSCRIBE = () => () => {};
2794
+ const EMPTY_PERMISSION_NOTICE_SNAPSHOT = () => EMPTY_PERMISSION_NOTICE;
2795
+ const EMPTY_FULL_ACCESS_CONFIRMATION_SUBSCRIBE = () => () => {};
2796
+ const EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT = () => void 0;
2797
+ const EMPTY_FORK_RECOVERY_SNAPSHOT = {
2798
+ revision: 0,
2799
+ pending: []
2800
+ };
2801
+ const EMPTY_FORK_RECOVERY_STORE = {
2802
+ getSnapshot: () => EMPTY_FORK_RECOVERY_SNAPSHOT,
2803
+ subscribe: () => () => {}
2804
+ };
2805
+ function updatedAtById(sessionIds, sessions) {
2806
+ return Object.fromEntries(sessionIds.map((sessionId) => [sessionId, sessions.byId[sessionId]?.updatedAt]));
2807
+ }
2808
+ function forkRecoveryError(recovery) {
2809
+ const error = toNativeWorktreeViewError(recovery.error);
2810
+ return {
2811
+ ...error,
2812
+ code: error.code === "WORKTREE_VIEW_FAILED" ? "SESSION_BINDING_FAILED" : error.code,
2813
+ details: {
2814
+ ...error.details ?? {},
2815
+ sessionId: recovery.childSessionId
2816
+ }
2817
+ };
2818
+ }
1849
2819
  function useStableWorkspaceIds(workspaces) {
1850
2820
  const next = workspaces.map((workspace) => workspace.workspaceId);
1851
2821
  const previousRef = (0, react.useRef)([]);
@@ -1857,20 +2827,49 @@ window.__ModuleLoader__.load({
1857
2827
  * Peer Worktree navigation surface. It keeps the original DSH Workspace mode
1858
2828
  * intact and renders a browser-local Workspace → Worktree → Session projection.
1859
2829
  */
1860
- 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 }) {
2830
+ 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 }) {
1861
2831
  const mode = effectiveViewMode(useStore((state) => state.viewMode), available && manager !== void 0);
1862
2832
  const sessions = useSessions((state) => state);
1863
2833
  const workspaces = useWorkspaces((state) => state);
2834
+ const forkRecoveryStore = forkRecovery ?? EMPTY_FORK_RECOVERY_STORE;
2835
+ const forkRecoverySnapshot = (0, react.useSyncExternalStore)(forkRecoveryStore.subscribe, forkRecoveryStore.getSnapshot, forkRecoveryStore.getSnapshot);
2836
+ const currentSessionId = sessions.current;
1864
2837
  const workspaceIds = useStableWorkspaceIds(workspaces.items);
1865
2838
  const [readState, setReadState] = (0, react.useState)(EMPTY_READ_STATE);
2839
+ const expandSnapshot = (0, react.useSyncExternalStore)(expandState.subscribe, expandState.getSnapshot, expandState.getSnapshot);
2840
+ const permissionNoticeSnapshot = (0, react.useSyncExternalStore)(permissionNotice?.subscribe ?? EMPTY_PERMISSION_NOTICE_SUBSCRIBE, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT);
2841
+ const fullAccessConfirmationSnapshot = (0, react.useSyncExternalStore)(fullAccessConfirmation?.subscribe ?? EMPTY_FULL_ACCESS_CONFIRMATION_SUBSCRIBE, fullAccessConfirmation?.getSnapshot ?? EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT, fullAccessConfirmation?.getSnapshot ?? EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT);
2842
+ const fullAccessConfirmationKey = fullAccessConfirmationSnapshot === void 0 ? "" : [
2843
+ fullAccessConfirmationSnapshot.workspaceId,
2844
+ fullAccessConfirmationSnapshot.worktreeId,
2845
+ fullAccessConfirmationSnapshot.sessionId,
2846
+ fullAccessConfirmationSnapshot.cwd
2847
+ ].join("\0");
2848
+ const [fullAccessAcknowledged, setFullAccessAcknowledged] = (0, react.useState)(false);
2849
+ (0, react.useEffect)(() => {
2850
+ setFullAccessAcknowledged(false);
2851
+ }, [fullAccessConfirmationKey]);
2852
+ const sessionOrderSnapshot = (0, react.useSyncExternalStore)(sessionOrder.subscribe, sessionOrder.getSnapshot, sessionOrder.getSnapshot);
2853
+ const sessionPresentations = (0, react.useMemo)(() => deriveSessionPresentationIndex(sessions), [sessions.byId]);
1866
2854
  const readStateRef = (0, react.useRef)(readState);
1867
2855
  readStateRef.current = readState;
1868
2856
  const [searchQuery, setSearchQuery] = (0, react.useState)("");
1869
- const [expandedWorkspaces, setExpandedWorkspaces] = (0, react.useState)({});
1870
- const [expandedMains, setExpandedMains] = (0, react.useState)({});
1871
- const [expandedWorktrees, setExpandedWorktrees] = (0, react.useState)({});
2857
+ const [currentSessionReveal, setCurrentSessionReveal] = (0, react.useState)();
2858
+ const searchQueryRef = (0, react.useRef)(searchQuery);
2859
+ searchQueryRef.current = searchQuery;
2860
+ const locateGenerationRef = (0, react.useRef)(0);
2861
+ const positionedLocateGenerationRef = (0, react.useRef)();
1872
2862
  const [worktreeModalWorkspaceId, setWorktreeModalWorkspaceId] = (0, react.useState)();
2863
+ const [modalReadError, setModalReadError] = (0, react.useState)();
2864
+ const [modalReadLoading, setModalReadLoading] = (0, react.useState)(false);
2865
+ const [worktreeModalMode, setWorktreeModalMode] = (0, react.useState)("create");
2866
+ const [importCandidates, setImportCandidates] = (0, react.useState)({
2867
+ status: "idle",
2868
+ candidates: []
2869
+ });
2870
+ const [selectedImportPath, setSelectedImportPath] = (0, react.useState)();
1873
2871
  const [openWorkspaceMenuId, setOpenWorkspaceMenuId] = (0, react.useState)();
2872
+ const [openMainMenuId, setOpenMainMenuId] = (0, react.useState)();
1874
2873
  const [openWorktreeMenuId, setOpenWorktreeMenuId] = (0, react.useState)();
1875
2874
  const [worktreeRemoval, setWorktreeRemoval] = (0, react.useState)();
1876
2875
  const [selectedBranch, setSelectedBranch] = (0, react.useState)("");
@@ -1878,6 +2877,7 @@ window.__ModuleLoader__.load({
1878
2877
  const [pendingSessionBinding, setPendingSessionBinding] = (0, react.useState)();
1879
2878
  const [actionError, setActionError] = (0, react.useState)();
1880
2879
  const [actionPending, setActionPending] = (0, react.useState)(false);
2880
+ const [forkRetryKey, setForkRetryKey] = (0, react.useState)();
1881
2881
  const [sessionRenameTarget, setSessionRenameTarget] = (0, react.useState)();
1882
2882
  const [sessionRenameDraft, setSessionRenameDraft] = (0, react.useState)("");
1883
2883
  const [sessionRenamePending, setSessionRenamePending] = (0, react.useState)(false);
@@ -1897,6 +2897,12 @@ window.__ModuleLoader__.load({
1897
2897
  const [worktreeDrag, setWorktreeDrag] = (0, react.useState)();
1898
2898
  const worktreeDropCommitted = (0, react.useRef)(false);
1899
2899
  const refreshGuard = (0, react.useRef)(createWorktreeRefreshGuard());
2900
+ const forkRecoveryRevisionRef = (0, react.useRef)(forkRecoverySnapshot.revision);
2901
+ const modalReadLoader = (0, react.useRef)(createWorktreeModalViewLoader());
2902
+ const modalReadViewRef = (0, react.useRef)();
2903
+ const importCandidatesGuard = (0, react.useRef)(createWorktreeRefreshGuard());
2904
+ const modalWorkspaceIdRef = (0, react.useRef)(worktreeModalWorkspaceId);
2905
+ modalWorkspaceIdRef.current = worktreeModalWorkspaceId;
1900
2906
  const { ref, width, bounds } = useSidebarOverlayGeometry(mode === "worktree");
1901
2907
  const collapsed = width <= 64;
1902
2908
  const query = searchQuery.trim().toLocaleLowerCase();
@@ -1919,13 +2925,13 @@ window.__ModuleLoader__.load({
1919
2925
  }), (views) => {
1920
2926
  setReadState({
1921
2927
  status: "ready",
1922
- views
2928
+ views: modalReadViewRef.current === void 0 ? views : mergeWorktreeView(views, modalReadViewRef.current)
1923
2929
  });
1924
2930
  }, (error) => {
1925
2931
  if (preserveCurrent) throw error;
1926
2932
  setReadState({
1927
2933
  status: "error",
1928
- views: [],
2934
+ views: modalReadViewRef.current === void 0 ? [] : [modalReadViewRef.current],
1929
2935
  error: toWorktreeViewError(error)
1930
2936
  });
1931
2937
  });
@@ -1934,13 +2940,56 @@ window.__ModuleLoader__.load({
1934
2940
  manager,
1935
2941
  workspaceIds
1936
2942
  ]);
2943
+ const loadModalWorktreeView = (0, react.useCallback)((workspaceId) => {
2944
+ setModalReadError(void 0);
2945
+ setModalReadLoading(true);
2946
+ if (manager === void 0) {
2947
+ setModalReadLoading(false);
2948
+ setModalReadError({
2949
+ code: "WORKTREE_VIEW_UNAVAILABLE",
2950
+ message: "",
2951
+ retryable: true
2952
+ });
2953
+ return;
2954
+ }
2955
+ modalReadLoader.current.load(manager, workspaceId, (view) => {
2956
+ modalReadViewRef.current = view;
2957
+ setModalReadLoading(false);
2958
+ setReadState((current) => ({
2959
+ ...current,
2960
+ views: mergeWorktreeView(current.views, view)
2961
+ }));
2962
+ }, (error) => {
2963
+ setModalReadLoading(false);
2964
+ setModalReadError(toWorktreeViewError(error));
2965
+ });
2966
+ }, [manager]);
1937
2967
  (0, react.useEffect)(() => {
1938
2968
  if (mode === "worktree") refresh({ preserveCurrent: readStateRef.current.status === "ready" });
1939
2969
  else {
1940
2970
  refreshGuard.current.invalidate();
2971
+ modalReadLoader.current.invalidate();
2972
+ modalReadViewRef.current = void 0;
2973
+ setWorktreeModalWorkspaceId(void 0);
2974
+ setModalReadError(void 0);
2975
+ setModalReadLoading(false);
1941
2976
  setReadState(EMPTY_READ_STATE);
1942
2977
  }
1943
2978
  }, [mode, refresh]);
2979
+ (0, react.useEffect)(() => {
2980
+ if (forkRecoveryRevisionRef.current === forkRecoverySnapshot.revision) return;
2981
+ forkRecoveryRevisionRef.current = forkRecoverySnapshot.revision;
2982
+ if (mode !== "worktree" || manager === void 0) return;
2983
+ refresh({
2984
+ preserveCurrent: readStateRef.current.status === "ready",
2985
+ invalidateContext: false
2986
+ }).catch(() => {});
2987
+ }, [
2988
+ forkRecoverySnapshot.revision,
2989
+ manager,
2990
+ mode,
2991
+ refresh
2992
+ ]);
1944
2993
  (0, react.useEffect)(() => {
1945
2994
  if (readState.status !== "ready" || syncSessionWorkspaces === void 0) return;
1946
2995
  syncSessionWorkspaces(readState.views.flatMap((view) => view.bindings.map((binding) => ({
@@ -1953,18 +3002,130 @@ window.__ModuleLoader__.load({
1953
3002
  syncSessionWorkspaces
1954
3003
  ]);
1955
3004
  const viewByWorkspace = (0, react.useMemo)(() => new Map(readState.views.map((view) => [view.workspaceId, view])), [readState.views]);
3005
+ const sessionOrderInputs = (0, react.useMemo)(() => {
3006
+ if (readState.status !== "ready") return [];
3007
+ const inputs = [];
3008
+ for (const workspace of workspaces.items) {
3009
+ const view = viewByWorkspace.get(workspace.workspaceId);
3010
+ const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
3011
+ const bindings = view?.bindings ?? [];
3012
+ const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]), sessions);
3013
+ inputs.push({
3014
+ accountKey: `main:${workspace.workspaceId}`,
3015
+ sessionIds: mainSessionIds,
3016
+ updatedAtById: updatedAtById(mainSessionIds, sessions)
3017
+ });
3018
+ for (const record of view?.worktrees ?? []) {
3019
+ const worktreeSessionIds = filterVisibleSessionIds(filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds), sessions);
3020
+ inputs.push({
3021
+ accountKey: `worktree:${record.worktreeId}`,
3022
+ sessionIds: worktreeSessionIds,
3023
+ updatedAtById: updatedAtById(worktreeSessionIds, sessions)
3024
+ });
3025
+ }
3026
+ }
3027
+ return inputs;
3028
+ }, [
3029
+ archivedSessionIds,
3030
+ readState.status,
3031
+ sessions.byId,
3032
+ sessions.ids,
3033
+ viewByWorkspace,
3034
+ workspaces,
3035
+ workspaces.items
3036
+ ]);
3037
+ const orderedSessionIdsByAccount = (0, react.useMemo)(() => {
3038
+ const ordered = /* @__PURE__ */ new Map();
3039
+ for (const input of sessionOrderInputs) {
3040
+ const previous = sessionOrderSnapshot.accounts[input.accountKey];
3041
+ ordered.set(input.accountKey, nextSessionOrderAccount({
3042
+ baseIds: input.sessionIds,
3043
+ updatedAtById: input.updatedAtById,
3044
+ previous
3045
+ }).order);
3046
+ }
3047
+ return ordered;
3048
+ }, [sessionOrderInputs, sessionOrderSnapshot]);
3049
+ (0, react.useEffect)(() => {
3050
+ if (mode !== "worktree" || readState.status !== "ready") return;
3051
+ for (const input of sessionOrderInputs) sessionOrder.actions.reconcile(input.accountKey, input.sessionIds, input.updatedAtById);
3052
+ sessionOrder.actions.retain(sessionOrderInputs.map((input) => input.accountKey));
3053
+ }, [
3054
+ mode,
3055
+ readState.status,
3056
+ sessionOrder,
3057
+ sessionOrderInputs
3058
+ ]);
3059
+ const currentSessionLocation = (0, react.useMemo)(() => readState.status === "ready" ? resolveCurrentSessionLocation(currentSessionId, workspaces.items, readState.views) : void 0, [
3060
+ currentSessionId,
3061
+ readState.status,
3062
+ readState.views,
3063
+ workspaces.items
3064
+ ]);
3065
+ const currentRevealKeys = (0, react.useMemo)(() => new Set(currentSessionRevealKeys(currentSessionLocation)), [currentSessionLocation]);
3066
+ const isCurrentSessionReveal = (key) => currentSessionReveal !== void 0 && currentSessionReveal.sessionId === currentSessionId && currentRevealKeys.has(key) && currentSessionReveal.suppressedKeys[key] !== true;
3067
+ (0, react.useLayoutEffect)(() => {
3068
+ locateGenerationRef.current += 1;
3069
+ positionedLocateGenerationRef.current = void 0;
3070
+ if (mode !== "worktree") {
3071
+ setCurrentSessionReveal(void 0);
3072
+ return;
3073
+ }
3074
+ if (searchQueryRef.current.trim().length > 0) setSearchQuery("");
3075
+ setCurrentSessionReveal(currentSessionId === void 0 ? void 0 : {
3076
+ sessionId: currentSessionId,
3077
+ suppressedKeys: {}
3078
+ });
3079
+ }, [currentSessionId, mode]);
3080
+ (0, react.useLayoutEffect)(() => {
3081
+ if (mode !== "worktree" || currentSessionId === void 0 || currentSessionLocation === void 0) return;
3082
+ const generation = locateGenerationRef.current;
3083
+ if (positionedLocateGenerationRef.current === generation) return;
3084
+ let cancelled = false;
3085
+ const frame = requestAnimationFrame(() => {
3086
+ if (cancelled || generation !== locateGenerationRef.current) return;
3087
+ if (!scrollCurrentSessionIntoView(ref.current, currentSessionId)) return;
3088
+ positionedLocateGenerationRef.current = generation;
3089
+ });
3090
+ return () => {
3091
+ cancelled = true;
3092
+ cancelAnimationFrame(frame);
3093
+ };
3094
+ }, [
3095
+ currentSessionId,
3096
+ currentSessionLocation,
3097
+ currentSessionReveal,
3098
+ expandSnapshot,
3099
+ mode,
3100
+ query,
3101
+ readState.status
3102
+ ]);
3103
+ (0, react.useEffect)(() => {
3104
+ if (readState.status !== "ready" || !isCompleteWorktreeWorkspaceSnapshot(workspaceIds, readState.views)) return;
3105
+ expandState.actions.retain(workspaceIds, readState.views.flatMap((view) => view.worktrees.map((record) => record.worktreeId)));
3106
+ }, [
3107
+ expandState,
3108
+ readState.status,
3109
+ readState.views,
3110
+ workspaceIds
3111
+ ]);
1956
3112
  const modalWorkspace = workspaces.items.find((workspace) => workspace.workspaceId === worktreeModalWorkspaceId);
1957
3113
  const modalView = worktreeModalWorkspaceId === void 0 ? void 0 : viewByWorkspace.get(worktreeModalWorkspaceId);
1958
3114
  const modalReadiness = modalView?.readiness;
1959
3115
  const modalSetupStatus = modalView === void 0 ? void 0 : modalReadiness?.status === "ready" ? modalView.branches.length === 0 ? "noLocalBranch" : void 0 : modalReadiness?.status;
1960
- const modalCanCreate = modalSetupStatus === void 0 && modalReadiness?.status === "ready";
3116
+ const modalCanCreate = !modalReadLoading && modalSetupStatus === void 0 && modalReadiness?.status === "ready";
1961
3117
  (0, react.useEffect)(() => {
1962
3118
  if (worktreeModalWorkspaceId === void 0 || modalView === void 0) return;
1963
3119
  if (modalView.readiness.status !== "ready") {
1964
3120
  setSelectedBranch("");
3121
+ setNewBranch("");
1965
3122
  return;
1966
3123
  }
1967
3124
  setSelectedBranch((current) => reconcileBaseBranchSelection(current, modalView.branches));
3125
+ setNewBranch((current) => {
3126
+ if (current.length > 0) return current;
3127
+ return createDefaultWorktreeName([...modalView.branches.map((branch) => branch.name), ...modalView.worktrees.map((worktree) => worktree.branch)]);
3128
+ });
1968
3129
  }, [modalView, worktreeModalWorkspaceId]);
1969
3130
  const runMutation = async (operation) => {
1970
3131
  setActionPending(true);
@@ -1978,6 +3139,27 @@ window.__ModuleLoader__.load({
1978
3139
  setActionPending(false);
1979
3140
  }
1980
3141
  };
3142
+ const loadImportCandidates = (0, react.useCallback)(async (workspaceId) => {
3143
+ if (manager === void 0) return;
3144
+ setImportCandidates((current) => ({
3145
+ status: "loading",
3146
+ candidates: current.candidates
3147
+ }));
3148
+ await importCandidatesGuard.current.run(() => manager.listImportCandidates({ workspaceId }), (candidates) => {
3149
+ if (modalWorkspaceIdRef.current !== workspaceId) return;
3150
+ setImportCandidates({
3151
+ status: "ready",
3152
+ candidates
3153
+ });
3154
+ }, (error) => {
3155
+ if (modalWorkspaceIdRef.current !== workspaceId) return;
3156
+ setImportCandidates((current) => ({
3157
+ status: "error",
3158
+ candidates: current.candidates,
3159
+ error: toWorktreeViewError(error)
3160
+ }));
3161
+ });
3162
+ }, [manager]);
1981
3163
  const workspaceRenameTrimmed = workspaceRenameDraft.trim();
1982
3164
  const workspaceRenameDuplicate = workspaceRenameTarget !== void 0 && workspaceRenameTrimmed.length > 0 && workspaces.items.some((workspace) => workspace.workspaceId !== workspaceRenameTarget.workspaceId && workspace.title === workspaceRenameTrimmed);
1983
3165
  const workspaceRenameBlocked = workspaceRenamePending || workspaceRenameTarget === void 0 || workspaceRenameTrimmed.length === 0 || workspaceRenameTrimmed === workspaceRenameTarget?.currentTitle || workspaceRenameDuplicate;
@@ -2093,7 +3275,10 @@ window.__ModuleLoader__.load({
2093
3275
  return;
2094
3276
  }
2095
3277
  setActionError(void 0);
2096
- insertSessionBefore(workspaceId, activeDrag.sessionId, beforeSessionId).catch((error) => {
3278
+ const currentOrder = sessionOrderSnapshot.accounts[activeDrag.groupKey]?.order ?? sessionIds;
3279
+ insertSessionBefore(workspaceId, activeDrag.sessionId, beforeSessionId).then(() => {
3280
+ sessionOrder.actions.setOrder(activeDrag.groupKey, reorderSessionIds(currentOrder, activeDrag.sessionId, over.sessionId, over.half));
3281
+ }).catch((error) => {
2097
3282
  setActionError(toWorktreeViewError(error));
2098
3283
  });
2099
3284
  };
@@ -2160,80 +3345,176 @@ window.__ModuleLoader__.load({
2160
3345
  if (archiveSession === void 0) return;
2161
3346
  await runMutation(() => archiveSession(sessionId));
2162
3347
  };
3348
+ const clearSessionGroups = (groupKeys) => {
3349
+ if (groupKeys.length === 0) return;
3350
+ setExpandedSessionGroups((current) => clearSessionGroupExpansion(current, groupKeys));
3351
+ };
3352
+ const suppressCurrentSessionReveal = (key) => {
3353
+ if (!isCurrentSessionReveal(key)) return false;
3354
+ setCurrentSessionReveal((current) => {
3355
+ if (current === void 0 || current.sessionId !== currentSessionId) return current;
3356
+ return {
3357
+ ...current,
3358
+ suppressedKeys: {
3359
+ ...current.suppressedKeys,
3360
+ [key]: true
3361
+ }
3362
+ };
3363
+ });
3364
+ return true;
3365
+ };
2163
3366
  const toggleWorkspace = (workspaceId) => {
2164
- setExpandedWorkspaces((current) => ({
2165
- ...current,
2166
- [workspaceId]: current[workspaceId] === false
2167
- }));
3367
+ const persistedExpanded = isWorkspaceExpanded(expandSnapshot, workspaceId);
3368
+ const autoExpanded = isCurrentSessionReveal("workspace:" + workspaceId);
3369
+ const visuallyExpanded = persistedExpanded || autoExpanded;
3370
+ if (visuallyExpanded && autoExpanded) suppressCurrentSessionReveal("workspace:" + workspaceId);
3371
+ if (visuallyExpanded && !persistedExpanded) {
3372
+ clearSessionGroups(["main:" + workspaceId, ...(viewByWorkspace.get(workspaceId)?.worktrees.map((record) => record.worktreeId) ?? []).map((worktreeId) => "worktree:" + worktreeId)]);
3373
+ return;
3374
+ }
3375
+ expandState.actions.toggleWorkspace(workspaceId);
3376
+ if (visuallyExpanded) clearSessionGroups(["main:" + workspaceId, ...(viewByWorkspace.get(workspaceId)?.worktrees.map((record) => record.worktreeId) ?? []).map((worktreeId) => "worktree:" + worktreeId)]);
2168
3377
  };
2169
3378
  const toggleMain = (workspaceId) => {
2170
- setExpandedMains((current) => ({
2171
- ...current,
2172
- [workspaceId]: current[workspaceId] === false
2173
- }));
3379
+ const persistedExpanded = isMainExpanded(expandSnapshot, workspaceId);
3380
+ const autoExpanded = isCurrentSessionReveal("main:" + workspaceId);
3381
+ const visuallyExpanded = persistedExpanded || autoExpanded;
3382
+ if (visuallyExpanded && autoExpanded) suppressCurrentSessionReveal("main:" + workspaceId);
3383
+ if (visuallyExpanded && !persistedExpanded) {
3384
+ clearSessionGroups(["main:" + workspaceId]);
3385
+ return;
3386
+ }
3387
+ expandState.actions.toggleMain(workspaceId);
3388
+ if (visuallyExpanded) clearSessionGroups(["main:" + workspaceId]);
2174
3389
  };
2175
3390
  const toggleWorktree = (worktreeId) => {
2176
- setExpandedWorktrees((current) => ({
3391
+ const persistedExpanded = isWorktreeExpanded(expandSnapshot, worktreeId);
3392
+ const autoExpanded = isCurrentSessionReveal("worktree:" + worktreeId);
3393
+ const visuallyExpanded = persistedExpanded || autoExpanded;
3394
+ if (visuallyExpanded && autoExpanded) suppressCurrentSessionReveal("worktree:" + worktreeId);
3395
+ if (visuallyExpanded && !persistedExpanded) {
3396
+ clearSessionGroups(["worktree:" + worktreeId]);
3397
+ return;
3398
+ }
3399
+ expandState.actions.toggleWorktree(worktreeId);
3400
+ if (visuallyExpanded) clearSessionGroups(["worktree:" + worktreeId]);
3401
+ };
3402
+ const toggleSessionGroup = (groupKey, autoExpanded) => {
3403
+ const transientExpanded = expandedSessionGroups[groupKey] === true;
3404
+ if (autoExpanded) {
3405
+ suppressCurrentSessionReveal("session-group:" + groupKey);
3406
+ setExpandedSessionGroups((current) => {
3407
+ const next = { ...current };
3408
+ delete next[groupKey];
3409
+ return next;
3410
+ });
3411
+ return;
3412
+ }
3413
+ setExpandedSessionGroups((current) => ({
2177
3414
  ...current,
2178
- [worktreeId]: current[worktreeId] === false
3415
+ [groupKey]: !transientExpanded
2179
3416
  }));
2180
3417
  };
2181
- const openWorktreeCreator = (workspace) => {
3418
+ const closeWorktreeCreator = (force = false) => {
3419
+ if (actionPending && !force) return;
3420
+ modalReadLoader.current.invalidate();
3421
+ modalReadViewRef.current = void 0;
3422
+ importCandidatesGuard.current.invalidate();
3423
+ setWorktreeModalWorkspaceId(void 0);
3424
+ setModalReadError(void 0);
3425
+ setModalReadLoading(false);
3426
+ };
3427
+ const openWorktreeCreator = (workspace, defaults = {}) => {
2182
3428
  const view = viewByWorkspace.get(workspace.workspaceId);
2183
- const baseBranch = selectDefaultBaseBranch(view?.branches ?? []);
2184
- const existingNames = [...(view?.branches ?? []).map((branch) => branch.name), ...(view?.worktrees ?? []).map((worktree) => worktree.branch)];
3429
+ modalReadLoader.current.invalidate();
3430
+ modalReadViewRef.current = void 0;
3431
+ importCandidatesGuard.current.invalidate();
2185
3432
  setWorktreeModalWorkspaceId(workspace.workspaceId);
2186
- setSelectedBranch(baseBranch);
2187
- setNewBranch(createDefaultWorktreeName(existingNames));
3433
+ setWorktreeModalMode("create");
3434
+ setImportCandidates({
3435
+ status: "idle",
3436
+ candidates: []
3437
+ });
3438
+ setSelectedImportPath(void 0);
2188
3439
  setActionError(void 0);
3440
+ setModalReadError(void 0);
3441
+ setModalReadLoading(false);
3442
+ if (view === void 0) {
3443
+ setSelectedBranch(defaults.baseBranch ?? "");
3444
+ setNewBranch(defaults.newBranch ?? "");
3445
+ loadModalWorktreeView(workspace.workspaceId);
3446
+ return;
3447
+ }
3448
+ setSelectedBranch(defaults.baseBranch ?? selectDefaultBaseBranch(view.branches));
3449
+ setNewBranch(defaults.newBranch ?? createDefaultWorktreeName([...view.branches.map((branch) => branch.name), ...view.worktrees.map((worktree) => worktree.branch)]));
3450
+ };
3451
+ const changeWorktreeModalMode = (mode) => {
3452
+ setWorktreeModalMode(mode);
3453
+ if (mode === "import" && worktreeModalWorkspaceId !== void 0 && importCandidates.status === "idle") loadImportCandidates(worktreeModalWorkspaceId);
3454
+ };
3455
+ const continueWorktreeRegistration = async (registeredWorktree) => {
3456
+ closeWorktreeCreator(true);
3457
+ if (createSessionCallback === void 0) {
3458
+ await refresh({ preserveCurrent: true });
3459
+ setActionError({
3460
+ code: "WORKTREE_REGISTRATION_SESSION_UNAVAILABLE",
3461
+ message: "",
3462
+ retryable: true
3463
+ });
3464
+ return;
3465
+ }
3466
+ const sessionInput = {
3467
+ workspaceId: registeredWorktree.workspaceId,
3468
+ worktreeId: registeredWorktree.worktreeId,
3469
+ cwd: registeredWorktree.absolutePath
3470
+ };
3471
+ try {
3472
+ await createSessionCallback(sessionInput);
3473
+ await invalidateWorktreeContext?.(registeredWorktree.workspaceId);
3474
+ } catch (error) {
3475
+ if (error instanceof WorktreeSessionBindingError && error.retryable) setPendingSessionBinding({
3476
+ ...sessionInput,
3477
+ sessionId: error.sessionId
3478
+ });
3479
+ if (error instanceof WorktreeSessionPermissionError && error.retryable) setPendingSessionBinding({
3480
+ ...sessionInput,
3481
+ sessionId: error.sessionId,
3482
+ permissionRequired: true
3483
+ });
3484
+ throw error;
3485
+ }
3486
+ await refresh({ preserveCurrent: true });
2189
3487
  };
2190
3488
  const submitWorktree = async () => {
2191
3489
  const worktreeName = newBranch.trim();
2192
- if (manager === void 0 || modalWorkspace === void 0 || !modalCanCreate || selectedBranch.length === 0 || worktreeName.length === 0) return;
2193
- const input = {
2194
- workspaceId: modalWorkspace.workspaceId,
2195
- branch: selectedBranch,
2196
- newBranch: worktreeName
2197
- };
3490
+ const selectedImportCandidate = importCandidates.candidates.find((candidate) => candidate.absolutePath === selectedImportPath);
3491
+ if (manager === void 0 || modalWorkspace === void 0) return;
3492
+ if (worktreeModalMode === "create" && (!modalCanCreate || selectedBranch.length === 0 || worktreeName.length === 0)) return;
3493
+ if (worktreeModalMode === "import" && selectedImportCandidate === void 0) return;
2198
3494
  setActionPending(true);
2199
3495
  setActionError(void 0);
2200
3496
  setPendingSessionBinding(void 0);
2201
3497
  try {
2202
- const createdWorktree = await executeWorktreeAction(manager, {
3498
+ const registeredWorktree = worktreeModalMode === "create" ? await executeWorktreeAction(manager, {
2203
3499
  type: "createWorktree",
2204
- input
3500
+ input: {
3501
+ workspaceId: modalWorkspace.workspaceId,
3502
+ branch: selectedBranch,
3503
+ newBranch: worktreeName
3504
+ }
3505
+ }) : await executeWorktreeAction(manager, {
3506
+ type: "importWorktree",
3507
+ input: {
3508
+ workspaceId: modalWorkspace.workspaceId,
3509
+ absolutePath: selectedImportCandidate.absolutePath
3510
+ }
2205
3511
  });
2206
- if (createdWorktree === void 0) throw {
3512
+ if (registeredWorktree === void 0) throw {
2207
3513
  code: "WORKTREE_RECORD_MISSING",
2208
3514
  message: "",
2209
3515
  retryable: true
2210
3516
  };
2211
- setWorktreeModalWorkspaceId(void 0);
2212
- if (createSessionCallback === void 0) {
2213
- await refresh({ preserveCurrent: true });
2214
- setActionError({
2215
- code: "WORKTREE_CREATED_SESSION_UNAVAILABLE",
2216
- message: "",
2217
- retryable: true
2218
- });
2219
- return;
2220
- }
2221
- const sessionInput = {
2222
- workspaceId: createdWorktree.workspaceId,
2223
- worktreeId: createdWorktree.worktreeId,
2224
- cwd: createdWorktree.absolutePath
2225
- };
2226
- try {
2227
- await createSessionCallback(sessionInput);
2228
- await invalidateWorktreeContext?.(createdWorktree.workspaceId);
2229
- } catch (error) {
2230
- if (error instanceof WorktreeSessionBindingError && error.retryable) setPendingSessionBinding({
2231
- ...sessionInput,
2232
- sessionId: error.sessionId
2233
- });
2234
- throw error;
2235
- }
2236
- await refresh({ preserveCurrent: true });
3517
+ await continueWorktreeRegistration(registeredWorktree);
2237
3518
  } catch (error) {
2238
3519
  setActionError(toWorktreeViewError(error));
2239
3520
  } finally {
@@ -2261,6 +3542,11 @@ window.__ModuleLoader__.load({
2261
3542
  ...input,
2262
3543
  sessionId: error.sessionId
2263
3544
  });
3545
+ if (error instanceof WorktreeSessionPermissionError && error.retryable) setPendingSessionBinding({
3546
+ ...input,
3547
+ sessionId: error.sessionId,
3548
+ permissionRequired: true
3549
+ });
2264
3550
  setActionError(toWorktreeViewError(error));
2265
3551
  } finally {
2266
3552
  setActionPending(false);
@@ -2279,6 +3565,9 @@ window.__ModuleLoader__.load({
2279
3565
  ensureSessionWorkspace: (workspaceId, sessionId) => {
2280
3566
  ensureSessionWorkspace?.(workspaceId, sessionId);
2281
3567
  },
3568
+ permission,
3569
+ confirmFullAccess,
3570
+ onPermissionResult,
2282
3571
  openSession
2283
3572
  });
2284
3573
  setPendingSessionBinding(void 0);
@@ -2291,6 +3580,17 @@ window.__ModuleLoader__.load({
2291
3580
  setActionPending(false);
2292
3581
  }
2293
3582
  };
3583
+ const retryForkBinding = async (key) => {
3584
+ if (retryForkSession === void 0 || forkRetryKey !== void 0) return;
3585
+ setForkRetryKey(key);
3586
+ try {
3587
+ await retryForkSession(key);
3588
+ } catch (error) {
3589
+ setActionError(toNativeWorktreeViewError(error));
3590
+ } finally {
3591
+ setForkRetryKey(void 0);
3592
+ }
3593
+ };
2294
3594
  const openWorkspaceSession = (workspaceId, sessionId) => {
2295
3595
  ensureSessionWorkspace?.(workspaceId, sessionId);
2296
3596
  openWorktreeSession({ open: openSession }, sessionId);
@@ -2356,7 +3656,7 @@ window.__ModuleLoader__.load({
2356
3656
  onClick: () => {
2357
3657
  if (createWorkspace !== void 0) runMutation(createWorkspace);
2358
3658
  },
2359
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
3659
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconProjectAddOutline16, {})
2360
3660
  })
2361
3661
  ]
2362
3662
  }),
@@ -2364,6 +3664,47 @@ window.__ModuleLoader__.load({
2364
3664
  className: worktree_css_default.content,
2365
3665
  tabIndex: 0,
2366
3666
  children: [
3667
+ permissionNoticeSnapshot !== void 0 && (0, react_jsx_runtime.jsx)("div", {
3668
+ className: worktree_css_default.notice,
3669
+ role: "status",
3670
+ "data-worktree-permission-notice": true,
3671
+ children: (0, react_jsx_runtime.jsx)("p", {
3672
+ className: worktree_css_default.message,
3673
+ children: formatWorktreePermissionNotice(permissionNoticeSnapshot.result, t)
3674
+ })
3675
+ }),
3676
+ forkRecoverySnapshot.pending.map((recovery) => {
3677
+ const recoveryViewError = forkRecoveryError(recovery);
3678
+ return (0, react_jsx_runtime.jsxs)("div", {
3679
+ className: worktree_css_default.error,
3680
+ role: "alert",
3681
+ "data-fork-recovery": recovery.key,
3682
+ children: [(0, react_jsx_runtime.jsx)("p", {
3683
+ className: worktree_css_default.message,
3684
+ "data-error": "true",
3685
+ children: formatWorktreeViewError(recoveryViewError, t)
3686
+ }), (0, react_jsx_runtime.jsxs)("div", {
3687
+ className: worktree_css_default.recoveryActions,
3688
+ children: [retryForkSession !== void 0 && recoveryViewError.retryable && (0, react_jsx_runtime.jsx)("button", {
3689
+ type: "button",
3690
+ className: worktree_css_default.actionButton,
3691
+ disabled: forkRetryKey !== void 0,
3692
+ onClick: () => {
3693
+ retryForkBinding(recovery.key);
3694
+ },
3695
+ children: t("action.retryBinding")
3696
+ }), (0, react_jsx_runtime.jsx)("button", {
3697
+ type: "button",
3698
+ className: worktree_css_default.actionButton,
3699
+ disabled: forkRetryKey !== void 0,
3700
+ onClick: () => {
3701
+ openSession(recovery.childSessionId);
3702
+ },
3703
+ children: t("action.openCreatedSession")
3704
+ })]
3705
+ })]
3706
+ }, recovery.key);
3707
+ }),
2367
3708
  actionError !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
2368
3709
  className: worktree_css_default.error,
2369
3710
  role: "alert",
@@ -2384,7 +3725,7 @@ window.__ModuleLoader__.load({
2384
3725
  retrySessionBinding();
2385
3726
  },
2386
3727
  children: t("action.retryBinding")
2387
- }), (0, react_jsx_runtime.jsx)("button", {
3728
+ }), !pendingSessionBinding.permissionRequired && (0, react_jsx_runtime.jsx)("button", {
2388
3729
  type: "button",
2389
3730
  className: worktree_css_default.actionButton,
2390
3731
  disabled: actionPending,
@@ -2437,14 +3778,22 @@ window.__ModuleLoader__.load({
2437
3778
  const view = viewByWorkspace.get(workspace.workspaceId);
2438
3779
  const currentBranch = view?.branches.find((branch) => branch.isCurrent)?.name;
2439
3780
  const mainLabel = currentBranch === void 0 ? t("worktree.main") : t("worktree.mainWithBranch", { branch: currentBranch });
2440
- const expanded = expandedWorkspaces[workspace.workspaceId] !== false;
3781
+ const expanded = isWorkspaceExpanded(expandSnapshot, workspace.workspaceId) || isCurrentSessionReveal("workspace:" + workspace.workspaceId);
2441
3782
  const workspaceMatchesQuery = includesText(workspace.title, query);
2442
3783
  const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
3784
+ const workspaceHasOngoingSession = hasOngoingSession(filterVisibleSessionIds(allWorkspaceSessionIds, sessions), sessionPresentations);
2443
3785
  const bindings = view?.bindings ?? [];
2444
- const visibleMainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]), sessions).filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
2445
- const mainExpanded = expandedMains[workspace.workspaceId] !== false;
3786
+ const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]), sessions);
2446
3787
  const mainGroupKey = `main:${workspace.workspaceId}`;
3788
+ const orderedMainSessionIds = orderedSessionIdsByAccount.get(mainGroupKey) ?? mainSessionIds;
3789
+ const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
3790
+ const orderedVisibleMainSessionIds = orderedMainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
3791
+ const mainExpanded = isMainExpanded(expandSnapshot, workspace.workspaceId) || isCurrentSessionReveal("main:" + workspace.workspaceId);
3792
+ const sessionIds = orderedVisibleMainSessionIds.length === visibleMainSessionIds.length ? orderedVisibleMainSessionIds : visibleMainSessionIds;
3793
+ const mainSessionGroupAutoExpanded = isSessionGroupAutoExpanded(sessionIds, currentSessionId, isCurrentSessionReveal("session-group:" + mainGroupKey));
3794
+ const mainSessionGroupExpanded = expandedSessionGroups[mainGroupKey] === true || mainSessionGroupAutoExpanded;
2447
3795
  const worktrees = view?.worktrees ?? [];
3796
+ const workspaceWorktreeNames = [...view?.branches.map((branch) => branch.name) ?? [], ...view?.worktrees.map((worktree) => worktree.branch) ?? []];
2448
3797
  const sameWorkspaceWorktreeDrag = worktreeDrag?.workspaceId === workspace.workspaceId;
2449
3798
  return (0, react_jsx_runtime.jsxs)("section", {
2450
3799
  className: worktree_css_default.workspaceGroup,
@@ -2453,6 +3802,7 @@ window.__ModuleLoader__.load({
2453
3802
  t,
2454
3803
  workspace,
2455
3804
  expanded,
3805
+ hasOngoingSession: workspaceHasOngoingSession,
2456
3806
  actionPending,
2457
3807
  menuOpen: openWorkspaceMenuId === workspace.workspaceId,
2458
3808
  drag: {
@@ -2510,11 +3860,29 @@ window.__ModuleLoader__.load({
2510
3860
  kind: "main",
2511
3861
  label: mainLabel,
2512
3862
  expanded: mainExpanded,
3863
+ hasOngoingSession: hasOngoingSession(mainSessionIds, sessionPresentations),
2513
3864
  icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
2514
3865
  workspaceTitle: workspace.title,
2515
3866
  onToggle: () => {
2516
3867
  toggleMain(workspace.workspaceId);
2517
3868
  },
3869
+ menu: {
3870
+ open: openMainMenuId === workspace.workspaceId,
3871
+ label: mainLabel,
3872
+ copyPath: workspace.path,
3873
+ showCreate: currentBranch !== void 0,
3874
+ showRemove: false,
3875
+ disabled: actionPending,
3876
+ onOpenChange: (open) => {
3877
+ setOpenMainMenuId(open ? workspace.workspaceId : void 0);
3878
+ },
3879
+ onCreateWorktree: currentBranch === void 0 ? void 0 : () => {
3880
+ openWorktreeCreator(workspace, {
3881
+ baseBranch: currentBranch,
3882
+ newBranch: createNumberedWorktreeName(currentBranch, workspaceWorktreeNames)
3883
+ });
3884
+ }
3885
+ },
2518
3886
  onCreateSession: createMainSession === void 0 ? void 0 : () => {
2519
3887
  createMainSession(workspace.workspaceId);
2520
3888
  }
@@ -2522,17 +3890,16 @@ window.__ModuleLoader__.load({
2522
3890
  mainExpanded && (0, react_jsx_runtime.jsx)(WorktreeSessionGroup, {
2523
3891
  t,
2524
3892
  groupKey: mainGroupKey,
2525
- sessionIds: visibleMainSessionIds,
3893
+ sessionIds,
2526
3894
  workspaceId: workspace.workspaceId,
2527
- expanded: expandedSessionGroups[mainGroupKey] === true,
3895
+ currentSessionId,
3896
+ expanded: mainSessionGroupExpanded,
2528
3897
  actionPending,
2529
3898
  sessions,
3899
+ sessionPresentations,
2530
3900
  dragState: sessionDrag,
2531
3901
  onToggleExpanded: () => {
2532
- setExpandedSessionGroups((current) => ({
2533
- ...current,
2534
- [mainGroupKey]: current[mainGroupKey] !== true
2535
- }));
3902
+ toggleSessionGroup(mainGroupKey, mainSessionGroupAutoExpanded);
2536
3903
  },
2537
3904
  onStartDrag: (groupKey, sessionId) => {
2538
3905
  sessionDropCommitted.current = false;
@@ -2575,9 +3942,13 @@ window.__ModuleLoader__.load({
2575
3942
  if (query.length > 0 && !worktreeMatchesQuery && !worktreeSessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) return null;
2576
3943
  const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
2577
3944
  const worktreeGroupKey = `worktree:${record.worktreeId}`;
3945
+ const orderedVisibleWorktreeSessionIds = (orderedSessionIdsByAccount.get(worktreeGroupKey) ?? worktreeSessionIds).filter((sessionId) => worktreeMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
3946
+ const sessionIds = orderedVisibleWorktreeSessionIds.length === visibleWorktreeSessionIds.length ? orderedVisibleWorktreeSessionIds : visibleWorktreeSessionIds;
3947
+ const sessionGroupAutoExpanded = isSessionGroupAutoExpanded(sessionIds, currentSessionId, isCurrentSessionReveal("session-group:" + worktreeGroupKey));
3948
+ const sessionGroupExpanded = expandedSessionGroups[worktreeGroupKey] === true || sessionGroupAutoExpanded;
2578
3949
  const state = record.status === "removed" ? "warning" : record.health === "repair" ? "error" : "done";
2579
3950
  const stateLabel = record.status === "removed" ? t("worktree.detached") : record.health === "repair" ? t("worktree.repair") : t("worktree.ready");
2580
- const worktreeExpanded = expandedWorktrees[record.worktreeId] !== false;
3951
+ const worktreeExpanded = isWorktreeExpanded(expandSnapshot, record.worktreeId) || isCurrentSessionReveal("worktree:" + record.worktreeId);
2581
3952
  return (0, react_jsx_runtime.jsxs)("div", {
2582
3953
  className: worktree_css_default.worktreeGroup,
2583
3954
  "data-worktree-id": record.worktreeId,
@@ -2587,6 +3958,7 @@ window.__ModuleLoader__.load({
2587
3958
  label: record.branch,
2588
3959
  worktreeId: record.worktreeId,
2589
3960
  expanded: worktreeExpanded,
3961
+ hasOngoingSession: hasOngoingSession(worktreeSessionIds, sessionPresentations),
2590
3962
  icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
2591
3963
  workspaceTitle: workspace.title,
2592
3964
  state,
@@ -2601,18 +3973,27 @@ window.__ModuleLoader__.load({
2601
3973
  cwd: record.absolutePath
2602
3974
  });
2603
3975
  } : void 0,
2604
- menu: record.status === "active" ? {
3976
+ menu: {
2605
3977
  open: openWorktreeMenuId === record.worktreeId,
2606
3978
  label: record.branch,
3979
+ copyPath: record.absolutePath,
3980
+ showCreate: record.status === "active",
3981
+ showRemove: record.status === "active",
2607
3982
  disabled: actionPending,
2608
3983
  onOpenChange: (open) => {
2609
3984
  setOpenWorktreeMenuId(open ? record.worktreeId : void 0);
2610
3985
  },
2611
- onRemove: () => {
3986
+ onCreateWorktree: record.status === "active" ? () => {
3987
+ openWorktreeCreator(workspace, {
3988
+ baseBranch: record.branch,
3989
+ newBranch: createNumberedWorktreeName(record.branch, workspaceWorktreeNames)
3990
+ });
3991
+ } : void 0,
3992
+ onRemove: record.status === "active" ? () => {
2612
3993
  setWorktreeRemoval(record);
2613
3994
  setActionError(void 0);
2614
- }
2615
- } : void 0,
3995
+ } : void 0
3996
+ },
2616
3997
  drag: {
2617
3998
  active: sameWorkspaceWorktreeDrag,
2618
3999
  marker: worktreeDrag?.over?.worktreeId === record.worktreeId ? worktreeDrag.over.half : null,
@@ -2648,17 +4029,16 @@ window.__ModuleLoader__.load({
2648
4029
  }), worktreeExpanded && (0, react_jsx_runtime.jsx)(WorktreeSessionGroup, {
2649
4030
  t,
2650
4031
  groupKey: worktreeGroupKey,
2651
- sessionIds: visibleWorktreeSessionIds,
4032
+ sessionIds,
2652
4033
  workspaceId: workspace.workspaceId,
2653
- expanded: expandedSessionGroups[worktreeGroupKey] === true,
4034
+ currentSessionId,
4035
+ expanded: sessionGroupExpanded,
2654
4036
  actionPending,
2655
4037
  sessions,
4038
+ sessionPresentations,
2656
4039
  dragState: sessionDrag,
2657
4040
  onToggleExpanded: () => {
2658
- setExpandedSessionGroups((current) => ({
2659
- ...current,
2660
- [worktreeGroupKey]: current[worktreeGroupKey] !== true
2661
- }));
4041
+ toggleSessionGroup(worktreeGroupKey, sessionGroupAutoExpanded);
2662
4042
  },
2663
4043
  onStartDrag: (groupKey, sessionId) => {
2664
4044
  sessionDropCommitted.current = false;
@@ -2741,14 +4121,28 @@ window.__ModuleLoader__.load({
2741
4121
  t,
2742
4122
  workspace: modalWorkspace,
2743
4123
  view: modalView,
4124
+ readError: modalReadError,
2744
4125
  setupStatus: modalSetupStatus,
2745
4126
  canCreate: modalCanCreate,
4127
+ mode: worktreeModalMode,
4128
+ importCandidates,
4129
+ selectedImportPath,
2746
4130
  selectedBranch,
2747
4131
  newBranch,
2748
4132
  actionPending,
2749
- onClose: () => {
2750
- setWorktreeModalWorkspaceId(void 0);
4133
+ onClose: closeWorktreeCreator,
4134
+ onRetry: () => {
4135
+ if (worktreeModalWorkspaceId === void 0) return;
4136
+ modalReadViewRef.current = void 0;
4137
+ setSelectedBranch("");
4138
+ setNewBranch("");
4139
+ loadModalWorktreeView(worktreeModalWorkspaceId);
2751
4140
  },
4141
+ onModeChange: changeWorktreeModalMode,
4142
+ onRetryImportCandidates: () => {
4143
+ if (worktreeModalWorkspaceId !== void 0) loadImportCandidates(worktreeModalWorkspaceId);
4144
+ },
4145
+ onSelectedImportPathChange: setSelectedImportPath,
2752
4146
  onSelectedBranchChange: setSelectedBranch,
2753
4147
  onNewBranchChange: setNewBranch,
2754
4148
  onSubmit: submitWorktree
@@ -2770,11 +4164,29 @@ window.__ModuleLoader__.load({
2770
4164
  workspaceId: target.workspaceId,
2771
4165
  worktreeId: target.worktreeId
2772
4166
  }
2773
- });
4167
+ }, permission, onPermissionNotice);
2774
4168
  await invalidateWorktreeContext?.(target.workspaceId);
2775
4169
  setWorktreeRemoval(void 0);
2776
4170
  });
2777
4171
  }
4172
+ }),
4173
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.RiskConfirmation, {
4174
+ open: fullAccessConfirmationSnapshot !== void 0,
4175
+ title: t("permission.fullAccessTitle"),
4176
+ description: fullAccessConfirmationSnapshot === void 0 ? "" : t("permission.fullAccessDescription", { cwd: fullAccessConfirmationSnapshot.cwd }),
4177
+ acknowledgeLabel: t("permission.fullAccessAcknowledge"),
4178
+ cancelLabel: t("dialog.cancel"),
4179
+ confirmLabel: t("permission.fullAccessEnable"),
4180
+ acknowledged: fullAccessAcknowledged,
4181
+ onAcknowledgedChange: setFullAccessAcknowledged,
4182
+ onCancel: () => {
4183
+ setFullAccessAcknowledged(false);
4184
+ fullAccessConfirmation?.resolve(false);
4185
+ },
4186
+ onConfirm: () => {
4187
+ setFullAccessAcknowledged(false);
4188
+ fullAccessConfirmation?.resolve(true);
4189
+ }
2778
4190
  })
2779
4191
  ]
2780
4192
  });
@@ -3140,6 +4552,313 @@ window.__ModuleLoader__.load({
3140
4552
  };
3141
4553
  }
3142
4554
  //#endregion
4555
+ //#region lib/client/worktree-permission.js
4556
+ /**
4557
+ * Coordinate async permission requests with the one in-page confirmation
4558
+ * rendered by the Worktree surface. Requests are serialized so concurrent
4559
+ * Worktree Session creations cannot replace one another's dialog.
4560
+ */
4561
+ function createWorktreeFullAccessConfirmationController() {
4562
+ let active;
4563
+ const queue = [];
4564
+ const listeners = /* @__PURE__ */ new Set();
4565
+ let disposed = false;
4566
+ const notify = () => {
4567
+ for (const listener of listeners) listener();
4568
+ };
4569
+ const showNext = () => {
4570
+ if (active !== void 0) return;
4571
+ active = queue.shift();
4572
+ notify();
4573
+ };
4574
+ return {
4575
+ getSnapshot: () => active?.input,
4576
+ subscribe: (listener) => {
4577
+ listeners.add(listener);
4578
+ return () => {
4579
+ listeners.delete(listener);
4580
+ };
4581
+ },
4582
+ request: (input) => {
4583
+ if (disposed) return Promise.resolve(false);
4584
+ return new Promise((resolve) => {
4585
+ queue.push({
4586
+ input,
4587
+ resolve
4588
+ });
4589
+ showNext();
4590
+ });
4591
+ },
4592
+ resolve: (confirmed) => {
4593
+ if (active === void 0) return;
4594
+ const current = active;
4595
+ active = void 0;
4596
+ current.resolve(confirmed);
4597
+ showNext();
4598
+ },
4599
+ dispose: () => {
4600
+ if (disposed) return;
4601
+ disposed = true;
4602
+ active?.resolve(false);
4603
+ active = void 0;
4604
+ for (const pending of queue.splice(0)) pending.resolve(false);
4605
+ notify();
4606
+ }
4607
+ };
4608
+ }
4609
+ //#endregion
4610
+ //#region lib/client/worktree-permission-icon.js
4611
+ const PLUGIN_ID = "@cerbur/clutch-dsh-worktree";
4612
+ const STYLE_ID = `${PLUGIN_ID}/permission-icon`;
4613
+ const WORKTREE_PERMISSION_LABEL = "Worktree Full Access";
4614
+ /** DOM marker owned only by this plugin's permission icon decorator. */
4615
+ const WORKTREE_PERMISSION_ICON_ATTRIBUTE = "data-clutch-dsh-worktree-permission-icon";
4616
+ const ICON_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path d=\"M13.0762 1.37207C14.0846 1.37228 14.9021 2.19077 14.9023 3.19922C14.9022 4.20772 14.0847 5.02518 13.0762 5.02539C12.2967 5.02539 11.6325 4.53691 11.3701 3.84961H4.35547C4.79397 4.26458 5.15861 4.7644 5.41699 5.33496L7.10645 9.06738C7.88526 10.7875 9.55104 11.9228 11.4189 12.0371C11.7085 11.4109 12.3411 10.9756 13.0762 10.9756C14.0843 10.9759 14.9023 11.7936 14.9023 12.8018C14.9023 13.81 14.0843 14.6277 13.0762 14.6279C12.2534 14.6279 11.5574 14.0832 11.3291 13.335C8.9868 13.1879 6.89981 11.7612 5.92285 9.60352L4.23242 5.87109C3.67503 4.64033 2.44878 3.84961 1.09766 3.84961V2.54883C1.10665 2.54883 1.11601 2.54975 1.125 2.5498L11.3701 2.54883C11.6326 1.86151 12.2969 1.37207 13.0762 1.37207ZM13.0762 12.2764C12.7858 12.2764 12.5508 12.5114 12.5508 12.8018C12.5508 13.0921 12.7858 13.3281 13.0762 13.3281C13.3664 13.3279 13.6025 13.092 13.6025 12.8018C13.6025 12.5115 13.3664 12.2766 13.0762 12.2764ZM13.0762 2.67285C12.7855 2.67285 12.55 2.90861 12.5498 3.19922C12.5499 3.48987 12.7855 3.72559 13.0762 3.72559C13.3667 3.72538 13.6024 3.48975 13.6025 3.19922C13.6023 2.90874 13.3666 2.67306 13.0762 2.67285Z\" fill=\"black\"/></svg>";
4617
+ function iconStyles() {
4618
+ const mask = `url("data:image/svg+xml,${encodeURIComponent(ICON_SVG)}")`;
4619
+ return `
4620
+ [${WORKTREE_PERMISSION_ICON_ATTRIBUTE}]::before {
4621
+ content: "";
4622
+ display: inline-block;
4623
+ flex: 0 0 auto;
4624
+ width: 16px;
4625
+ height: 16px;
4626
+ background-color: var(--dsw-alias-label-tertiary, currentColor);
4627
+ -webkit-mask-image: ${mask};
4628
+ mask-image: ${mask};
4629
+ -webkit-mask-position: center;
4630
+ mask-position: center;
4631
+ -webkit-mask-repeat: no-repeat;
4632
+ mask-repeat: no-repeat;
4633
+ -webkit-mask-size: contain;
4634
+ mask-size: contain;
4635
+ }
4636
+ [${WORKTREE_PERMISSION_ICON_ATTRIBUTE}="trigger"]::before {
4637
+ width: 14px;
4638
+ height: 14px;
4639
+ }
4640
+ @container (max-width: 460px) {
4641
+ [${WORKTREE_PERMISSION_ICON_ATTRIBUTE}="trigger"] > span:first-of-type {
4642
+ display: none;
4643
+ }
4644
+ }
4645
+ `;
4646
+ }
4647
+ function normalizedText(element) {
4648
+ return (element.textContent ?? "").replace(/\s+/g, " ").trim();
4649
+ }
4650
+ function isWorktreeMenuItem(element) {
4651
+ return element.matches("button[role=\"menuitem\"]") && normalizedText(element) === WORKTREE_PERMISSION_LABEL && element.closest("[role=\"menu\"]") !== null;
4652
+ }
4653
+ function isWorktreeTrigger(element) {
4654
+ if (!element.matches("button[aria-label]")) return false;
4655
+ const label = element.getAttribute("aria-label") ?? "";
4656
+ return /^(?:access mode,\s*current:\s*|访问模式,当前:\s*)worktree full access$/iu.test(label.trim());
4657
+ }
4658
+ /** Mark the native custom permission row and trigger for CSS decoration. */
4659
+ function decorateWorktreePermissionIcons(document) {
4660
+ for (const marked of document.querySelectorAll(`[${WORKTREE_PERMISSION_ICON_ATTRIBUTE}]`)) {
4661
+ const kind = marked.getAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE);
4662
+ if (kind === "menu" && !isWorktreeMenuItem(marked) || kind === "trigger" && !isWorktreeTrigger(marked)) marked.removeAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE);
4663
+ }
4664
+ for (const item of document.querySelectorAll("button[role=\"menuitem\"]")) if (isWorktreeMenuItem(item)) item.setAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE, "menu");
4665
+ for (const button of document.querySelectorAll("button[aria-label]")) if (isWorktreeTrigger(button)) button.setAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE, "trigger");
4666
+ }
4667
+ /** Install the Worktree permission icon and return a plugin-local disposer. */
4668
+ function installWorktreePermissionIcon(document) {
4669
+ for (const existing of document.querySelectorAll("style[data-plugin-css]")) if (existing.getAttribute("data-plugin-css") === STYLE_ID) existing.remove();
4670
+ const style = document.createElement("style");
4671
+ style.setAttribute("data-plugin", PLUGIN_ID);
4672
+ style.setAttribute("data-plugin-css", STYLE_ID);
4673
+ style.textContent = iconStyles();
4674
+ document.head.appendChild(style);
4675
+ let active = true;
4676
+ let queued = false;
4677
+ const scan = () => {
4678
+ if (!active || queued) return;
4679
+ queued = true;
4680
+ queueMicrotask(() => {
4681
+ queued = false;
4682
+ if (active) decorateWorktreePermissionIcons(document);
4683
+ });
4684
+ };
4685
+ decorateWorktreePermissionIcons(document);
4686
+ const observer = typeof MutationObserver === "function" ? new MutationObserver(scan) : void 0;
4687
+ observer?.observe(document.documentElement, {
4688
+ attributes: true,
4689
+ attributeFilter: ["aria-label"],
4690
+ characterData: true,
4691
+ childList: true,
4692
+ subtree: true
4693
+ });
4694
+ return () => {
4695
+ if (!active) return;
4696
+ active = false;
4697
+ observer?.disconnect();
4698
+ style.remove();
4699
+ for (const marked of document.querySelectorAll(`[${WORKTREE_PERMISSION_ICON_ATTRIBUTE}]`)) marked.removeAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE);
4700
+ };
4701
+ }
4702
+ //#endregion
4703
+ //#region lib/client/worktree-session-fork.js
4704
+ function recoveryKey(sourceSessionId, childSessionId) {
4705
+ return `${sourceSessionId}\u0000${childSessionId}`;
4706
+ }
4707
+ /**
4708
+ * Coordinate native Session fork with the plugin-owned Worktree relation.
4709
+ * Native Session creation always wins: plugin lookup/bind failures are kept as
4710
+ * recovery state and never turn an already-created child into a failed fork.
4711
+ */
4712
+ function createWorktreeSessionForkCoordinator(options) {
4713
+ let disposed = false;
4714
+ let revision = 0;
4715
+ let pending = /* @__PURE__ */ new Map();
4716
+ let recoverySnapshot = {
4717
+ revision: 0,
4718
+ pending: []
4719
+ };
4720
+ const subscribers = /* @__PURE__ */ new Set();
4721
+ const boundChildren = /* @__PURE__ */ new Set();
4722
+ const bindingInFlight = /* @__PURE__ */ new Map();
4723
+ let reconciliationInFlight;
4724
+ let reconciliationAgain = false;
4725
+ const publish = () => {
4726
+ revision += 1;
4727
+ recoverySnapshot = {
4728
+ revision,
4729
+ pending: [...pending.values()]
4730
+ };
4731
+ for (const subscriber of subscribers) subscriber();
4732
+ };
4733
+ const setRecovery = (sourceSessionId, childSessionId, error, target) => {
4734
+ if (disposed) return;
4735
+ const key = recoveryKey(sourceSessionId, childSessionId);
4736
+ pending = new Map(pending).set(key, {
4737
+ key,
4738
+ sourceSessionId,
4739
+ childSessionId,
4740
+ ...target === void 0 ? {} : { binding: target },
4741
+ error
4742
+ });
4743
+ publish();
4744
+ };
4745
+ const clearRecovery = (sourceSessionId, childSessionId) => {
4746
+ const key = recoveryKey(sourceSessionId, childSessionId);
4747
+ if (!pending.has(key)) return;
4748
+ const next = new Map(pending);
4749
+ next.delete(key);
4750
+ pending = next;
4751
+ publish();
4752
+ };
4753
+ const attemptBinding = async (sourceSessionId, childSessionId) => {
4754
+ if (disposed || boundChildren.has(childSessionId)) return { bound: false };
4755
+ let target;
4756
+ try {
4757
+ target = await options.findBinding(sourceSessionId);
4758
+ } catch (error) {
4759
+ setRecovery(sourceSessionId, childSessionId, error);
4760
+ return { bound: false };
4761
+ }
4762
+ if (disposed) return { bound: false };
4763
+ if (target === void 0 || target.status !== "active") {
4764
+ clearRecovery(sourceSessionId, childSessionId);
4765
+ return { bound: false };
4766
+ }
4767
+ try {
4768
+ await options.bindSession({
4769
+ workspaceId: target.workspaceId,
4770
+ worktreeId: target.worktreeId,
4771
+ sessionId: childSessionId
4772
+ });
4773
+ if (disposed) return { bound: false };
4774
+ const childBinding = {
4775
+ workspaceId: target.workspaceId,
4776
+ worktreeId: target.worktreeId,
4777
+ sessionId: childSessionId,
4778
+ status: "active"
4779
+ };
4780
+ boundChildren.add(childSessionId);
4781
+ clearRecovery(sourceSessionId, childSessionId);
4782
+ options.onBound?.(childBinding);
4783
+ publish();
4784
+ return { bound: true };
4785
+ } catch (error) {
4786
+ setRecovery(sourceSessionId, childSessionId, error, target);
4787
+ return { bound: false };
4788
+ }
4789
+ };
4790
+ const bindChild = (sourceSessionId, childSessionId) => {
4791
+ if (disposed || boundChildren.has(childSessionId)) return Promise.resolve({ bound: false });
4792
+ const key = recoveryKey(sourceSessionId, childSessionId);
4793
+ const current = bindingInFlight.get(key);
4794
+ if (current !== void 0) return current;
4795
+ const promise = attemptBinding(sourceSessionId, childSessionId).finally(() => {
4796
+ if (bindingInFlight.get(key) === promise) bindingInFlight.delete(key);
4797
+ });
4798
+ bindingInFlight.set(key, promise);
4799
+ return promise;
4800
+ };
4801
+ const reconcileNow = async () => {
4802
+ if (disposed || options.sessions === void 0) return;
4803
+ const snapshot = options.sessions.getSnapshot();
4804
+ if (snapshot.phase === "pending") return;
4805
+ if (!Array.isArray(snapshot.ids) || typeof snapshot.byId !== "object" || snapshot.byId === null) return;
4806
+ const candidates = snapshot.ids.flatMap((childSessionId) => {
4807
+ const summary = snapshot.byId[childSessionId];
4808
+ if (summary?.parentId === void 0 || summary.parentId === childSessionId || summary.blank === true || summary.origin === "subagent" || snapshot.byId[summary.parentId] === void 0) return [];
4809
+ return [{
4810
+ childSessionId,
4811
+ sourceSessionId: summary.parentId
4812
+ }];
4813
+ });
4814
+ await Promise.all(candidates.map(({ childSessionId, sourceSessionId }) => bindChild(sourceSessionId, childSessionId).then(() => void 0)));
4815
+ };
4816
+ const reconcile = () => {
4817
+ if (reconciliationInFlight !== void 0) {
4818
+ reconciliationAgain = true;
4819
+ return reconciliationInFlight;
4820
+ }
4821
+ const run = async () => {
4822
+ do {
4823
+ reconciliationAgain = false;
4824
+ await reconcileNow();
4825
+ } while (!disposed && reconciliationAgain);
4826
+ };
4827
+ const promise = run().finally(() => {
4828
+ if (reconciliationInFlight === promise) reconciliationInFlight = void 0;
4829
+ });
4830
+ reconciliationInFlight = promise;
4831
+ return promise;
4832
+ };
4833
+ return {
4834
+ recovery: {
4835
+ getSnapshot() {
4836
+ return recoverySnapshot;
4837
+ },
4838
+ subscribe(listener) {
4839
+ subscribers.add(listener);
4840
+ return () => subscribers.delete(listener);
4841
+ }
4842
+ },
4843
+ async fork(input) {
4844
+ const childSessionId = await options.fork(input);
4845
+ if (!disposed) await bindChild(input.sessionId, childSessionId);
4846
+ return childSessionId;
4847
+ },
4848
+ reconcile,
4849
+ async retry(key) {
4850
+ const item = pending.get(key);
4851
+ if (item === void 0 || disposed) return false;
4852
+ return (await bindChild(item.sourceSessionId, item.childSessionId)).bound;
4853
+ },
4854
+ dispose() {
4855
+ disposed = true;
4856
+ bindingInFlight.clear();
4857
+ subscribers.clear();
4858
+ }
4859
+ };
4860
+ }
4861
+ //#endregion
3143
4862
  //#region lib/client/entry.js
3144
4863
  /** Required DSH Client services; Connection is the sole Worktree wire dependency. */
3145
4864
  const inject = [
@@ -3154,11 +4873,22 @@ window.__ModuleLoader__.load({
3154
4873
  * with that fiber, so slot consumers share one manager and one request lifetime.
3155
4874
  */
3156
4875
  function apply(ctx) {
4876
+ if (typeof document !== "undefined") ctx.effect(() => installWorktreePermissionIcon(document), "clutch-dsh-worktree: permission icon cleanup");
3157
4877
  ctx.effect(() => ctx.locale.register(WORKTREE_NS, {
3158
4878
  zh,
3159
4879
  en
3160
4880
  }), "clutch-dsh-worktree: locale dictionaries");
3161
4881
  const manager = createWorktreeConnectionAdapter(ctx.connection.rpc);
4882
+ const fullAccessConfirmation = createWorktreeFullAccessConfirmationController();
4883
+ const permissionNotice = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(void 0);
4884
+ const permissionManager = typeof document !== "undefined" ? manager : void 0;
4885
+ const reportPermissionNotice = (input, result) => {
4886
+ const visible = result.status === "fallback-workspace-write" || result.status === "user-restricted" || result.status === "unverified";
4887
+ permissionNotice.set(visible ? {
4888
+ ...input,
4889
+ result
4890
+ } : void 0);
4891
+ };
3162
4892
  const sessions = ctx.sessions;
3163
4893
  const virtualWorkspaceMembership = createVirtualWorkspaceMembership(ctx.workspaces.list);
3164
4894
  const contextProjection = createWorktreeContextProjection({
@@ -3168,10 +4898,14 @@ window.__ModuleLoader__.load({
3168
4898
  storeFactory: _deepseek_ai_dsh_client_runtime_client.createSnapshotStore
3169
4899
  });
3170
4900
  ctx.effect(() => () => manager.dispose(), "clutch-dsh-worktree: connection cleanup");
4901
+ ctx.effect(() => () => fullAccessConfirmation.dispose(), "clutch-dsh-worktree: Full Access confirmation cleanup");
3171
4902
  ctx.effect(() => () => virtualWorkspaceMembership.dispose(), "clutch-dsh-worktree: Workspace membership cleanup");
3172
4903
  ctx.effect(() => () => contextProjection.dispose(), "clutch-dsh-worktree: Session context cleanup");
3173
4904
  contextProjection.refresh();
3174
4905
  const viewStore = createWorktreeViewStore();
4906
+ const expandState = createWorktreeExpandStateStore(_deepseek_ai_dsh_client_runtime_client.createSnapshotStore);
4907
+ const sessionOrder = createWorktreeSessionOrderStore(_deepseek_ai_dsh_client_runtime_client.createSnapshotStore);
4908
+ ctx.effect(() => () => sessionOrder.dispose(), "clutch-dsh-worktree: Session order cleanup");
3175
4909
  const ensureSessionWorkspace = (workspaceId, sessionId) => {
3176
4910
  virtualWorkspaceMembership.ensure({
3177
4911
  workspaceId,
@@ -3181,12 +4915,48 @@ window.__ModuleLoader__.load({
3181
4915
  const syncSessionWorkspaces = (bindings) => {
3182
4916
  virtualWorkspaceMembership.sync(bindings);
3183
4917
  };
4918
+ const forkableSessions = ctx.sessions;
4919
+ const nativeFork = forkableSessions.fork;
4920
+ const findWorktreeSessionBinding = async (sessionId) => {
4921
+ const workspaceIds = ctx.workspaces.list.getSnapshot().items.map((workspace) => workspace.workspaceId);
4922
+ const results = await Promise.allSettled(workspaceIds.map(async (workspaceId) => {
4923
+ return (await manager.listBindings({ workspaceId })).find((binding) => binding.sessionId === sessionId && binding.status === "active");
4924
+ }));
4925
+ const found = results.find((result) => result.status === "fulfilled" && result.value !== void 0);
4926
+ if (found !== void 0) return found.value;
4927
+ const failed = results.find((result) => result.status === "rejected");
4928
+ if (failed !== void 0) throw failed.reason;
4929
+ };
4930
+ const forkCoordinator = typeof nativeFork !== "function" ? void 0 : createWorktreeSessionForkCoordinator({
4931
+ fork: (input) => nativeFork.call(ctx.sessions, input),
4932
+ findBinding: findWorktreeSessionBinding,
4933
+ bindSession: (input) => manager.bindSession(input),
4934
+ sessions: ctx.sessions.list
4935
+ });
4936
+ if (forkCoordinator !== void 0) {
4937
+ forkableSessions.fork = (input) => forkCoordinator.fork(input);
4938
+ const reconcileForkChildren = () => {
4939
+ forkCoordinator.reconcile();
4940
+ };
4941
+ const unsubscribeSessionList = ctx.sessions.list.subscribe(reconcileForkChildren);
4942
+ const unsubscribeWorkspaceList = ctx.workspaces.list.subscribe(reconcileForkChildren);
4943
+ ctx.effect(() => () => {
4944
+ unsubscribeSessionList();
4945
+ unsubscribeWorkspaceList();
4946
+ forkCoordinator.dispose();
4947
+ forkableSessions.fork = nativeFork;
4948
+ }, "clutch-dsh-worktree: Session fork cleanup");
4949
+ forkCoordinator.reconcile();
4950
+ }
3184
4951
  const worktreeSessionConnector = createWorktreeSessionConnector({
3185
4952
  manager,
3186
4953
  sessions: ctx.sessions.list,
3187
4954
  archivedSessionIds: () => ctx.workspaces.list.getSnapshot().archivedSessionIds ?? [],
3188
4955
  createSession: async (input) => String(await sessions.create(input)),
3189
4956
  ensureSessionWorkspace,
4957
+ permission: permissionManager,
4958
+ confirmFullAccess: permissionManager === void 0 ? void 0 : fullAccessConfirmation.request,
4959
+ onPermissionResult: reportPermissionNotice,
3190
4960
  openSession: (sessionId) => {
3191
4961
  ctx.sessions.open(sessionId);
3192
4962
  }
@@ -3213,8 +4983,16 @@ window.__ModuleLoader__.load({
3213
4983
  locale: WORKTREE_NS,
3214
4984
  inject: () => ({
3215
4985
  available: true,
4986
+ expandState,
4987
+ sessionOrder,
3216
4988
  hooks: { worktreeContext: contextProjection.store },
3217
4989
  manager,
4990
+ permission: permissionManager,
4991
+ confirmFullAccess: permissionManager === void 0 ? void 0 : fullAccessConfirmation.request,
4992
+ fullAccessConfirmation: typeof document === "undefined" ? void 0 : fullAccessConfirmation,
4993
+ onPermissionResult: reportPermissionNotice,
4994
+ onPermissionNotice: reportPermissionNotice,
4995
+ permissionNotice,
3218
4996
  createWorkspace: async () => {
3219
4997
  const workspacePath = await ctx.workspaces.pickDirectory();
3220
4998
  if (workspacePath !== null) await ctx.workspaces.create({ path: workspacePath });
@@ -3248,6 +5026,7 @@ window.__ModuleLoader__.load({
3248
5026
  if (!result.ok) throw new Error(result.error.message);
3249
5027
  },
3250
5028
  forkSession: (sessionId) => {
5029
+ if (forkableSessions.fork === void 0) return;
3251
5030
  ctx.sessions.fork({
3252
5031
  sessionId,
3253
5032
  increaseTitle: true
@@ -3258,6 +5037,10 @@ window.__ModuleLoader__.load({
3258
5037
  archiveSession: (sessionId) => ctx.workspaces.archiveSession(sessionId),
3259
5038
  ensureSessionWorkspace,
3260
5039
  syncSessionWorkspaces,
5040
+ forkRecovery: forkCoordinator?.recovery,
5041
+ retryForkSession: forkCoordinator === void 0 ? void 0 : async (key) => {
5042
+ await forkCoordinator.retry(key);
5043
+ },
3261
5044
  openSession: (sessionId) => {
3262
5045
  ctx.sessions.open(sessionId);
3263
5046
  }