@cerbur/clutch-dsh-worktree 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -15
- package/README.zh.md +86 -10
- package/cordis.patch.yml +21 -0
- package/lib/client/WorktreeContext.d.ts.map +1 -1
- package/lib/client/WorktreeContext.js +2 -2
- package/lib/client/WorktreeContext.js.map +1 -1
- package/lib/client/WorktreeSurface.d.ts +2 -2
- package/lib/client/WorktreeSurface.d.ts.map +1 -1
- package/lib/client/WorktreeSurface.js +270 -65
- package/lib/client/WorktreeSurface.js.map +1 -1
- package/lib/client/entry.d.ts.map +1 -1
- package/lib/client/entry.js +84 -2
- package/lib/client/entry.js.map +1 -1
- package/lib/client/index.d.ts +3 -0
- package/lib/client/index.d.ts.map +1 -1
- package/lib/client/index.js +2 -0
- package/lib/client/index.js.map +1 -1
- package/lib/client/locales.d.ts +58 -0
- package/lib/client/locales.d.ts.map +1 -1
- package/lib/client/locales.js +58 -0
- package/lib/client/locales.js.map +1 -1
- package/lib/client/session-view.d.ts +34 -0
- package/lib/client/session-view.d.ts.map +1 -1
- package/lib/client/session-view.js +119 -1
- package/lib/client/session-view.js.map +1 -1
- package/lib/client/worktree-connection.d.ts +4 -2
- package/lib/client/worktree-connection.d.ts.map +1 -1
- package/lib/client/worktree-connection.js +4 -0
- package/lib/client/worktree-connection.js.map +1 -1
- package/lib/client/worktree-error-copy.d.ts +2 -0
- package/lib/client/worktree-error-copy.d.ts.map +1 -1
- package/lib/client/worktree-error-copy.js +16 -0
- package/lib/client/worktree-error-copy.js.map +1 -1
- package/lib/client/worktree-permission-icon.d.ts +7 -0
- package/lib/client/worktree-permission-icon.d.ts.map +1 -0
- package/lib/client/worktree-permission-icon.js +119 -0
- package/lib/client/worktree-permission-icon.js.map +1 -0
- package/lib/client/worktree-permission.d.ts +21 -0
- package/lib/client/worktree-permission.d.ts.map +1 -0
- package/lib/client/worktree-permission.js +70 -0
- package/lib/client/worktree-permission.js.map +1 -0
- package/lib/client/worktree-session-fork.d.ts +65 -0
- package/lib/client/worktree-session-fork.d.ts.map +1 -0
- package/lib/client/worktree-session-fork.js +174 -0
- package/lib/client/worktree-session-fork.js.map +1 -0
- package/lib/client/worktree-session-order.d.ts +36 -0
- package/lib/client/worktree-session-order.d.ts.map +1 -0
- package/lib/client/worktree-session-order.js +215 -0
- package/lib/client/worktree-session-order.js.map +1 -0
- package/lib/client/worktree-session.d.ts +34 -1
- package/lib/client/worktree-session.d.ts.map +1 -1
- package/lib/client/worktree-session.js +98 -2
- package/lib/client/worktree-session.js.map +1 -1
- package/lib/client/worktree-surface-rows.d.ts +4 -4
- package/lib/client/worktree-surface-rows.d.ts.map +1 -1
- package/lib/client/worktree-surface-rows.js +132 -47
- package/lib/client/worktree-surface-rows.js.map +1 -1
- package/lib/client/worktree-surface-selectors.d.ts +2 -0
- package/lib/client/worktree-surface-selectors.d.ts.map +1 -1
- package/lib/client/worktree-surface-selectors.js +8 -0
- package/lib/client/worktree-surface-selectors.js.map +1 -1
- package/lib/client/worktree-surface-types.d.ts +46 -3
- package/lib/client/worktree-surface-types.d.ts.map +1 -1
- package/lib/client/worktree-view-actions.d.ts +8 -3
- package/lib/client/worktree-view-actions.d.ts.map +1 -1
- package/lib/client/worktree-view-actions.js +28 -2
- package/lib/client/worktree-view-actions.js.map +1 -1
- package/lib/client/worktree-view-errors.d.ts +10 -0
- package/lib/client/worktree-view-errors.d.ts.map +1 -1
- package/lib/client/worktree-view-errors.js +22 -1
- package/lib/client/worktree-view-errors.js.map +1 -1
- package/lib/client.js +1473 -201
- package/lib/client.js.map +1 -1
- package/lib/contract/index.contract.js +2 -0
- package/lib/contract/index.contract.js.map +1 -1
- package/lib/contract/index.d.ts +6 -2
- package/lib/contract/index.d.ts.map +1 -1
- package/lib/contract/index.js +4 -0
- package/lib/contract/index.js.map +1 -1
- package/lib/contract/worktree-permission.d.ts +73 -0
- package/lib/contract/worktree-permission.d.ts.map +1 -0
- package/lib/contract/worktree-permission.js +32 -0
- package/lib/contract/worktree-permission.js.map +1 -0
- package/lib/host/index.d.ts +4 -0
- package/lib/host/index.d.ts.map +1 -1
- package/lib/host/index.js +2 -0
- package/lib/host/index.js.map +1 -1
- package/lib/host/remote.d.ts +2 -2
- package/lib/host/remote.d.ts.map +1 -1
- package/lib/host/remote.js +7 -1
- package/lib/host/remote.js.map +1 -1
- package/lib/host/service.d.ts +3 -1
- package/lib/host/service.d.ts.map +1 -1
- package/lib/host/service.js +36 -2
- package/lib/host/service.js.map +1 -1
- package/lib/host/worktree-permission-manager.d.ts +14 -0
- package/lib/host/worktree-permission-manager.d.ts.map +1 -0
- package/lib/host/worktree-permission-manager.js +111 -0
- package/lib/host/worktree-permission-manager.js.map +1 -0
- package/lib/host/worktree-permission.d.ts +38 -0
- package/lib/host/worktree-permission.d.ts.map +1 -0
- package/lib/host/worktree-permission.js +156 -0
- package/lib/host/worktree-permission.js.map +1 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/manage/worktree-permission.d.ts +3 -0
- package/lib/manage/worktree-permission.d.ts.map +1 -0
- package/lib/manage/worktree-permission.js +3 -0
- package/lib/manage/worktree-permission.js.map +1 -0
- package/lib/typert.host.js +126 -27
- package/lib/typert.remote-client.d.ts +5 -1
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +126 -27
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -40,6 +40,8 @@ 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 中,并作为已分离关系继续可用。",
|
|
45
47
|
"worktree.removeExternalDescription": "移除“{name}”?这会删除关联的外部 Worktree 目录。Session 会保留在 DSH 中,并作为已分离关系继续可用。",
|
|
@@ -73,6 +75,23 @@ window.__ModuleLoader__.load({
|
|
|
73
75
|
"session.expandMore": "展开其余 {count} 个会话",
|
|
74
76
|
"session.collapse": "收起",
|
|
75
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}前",
|
|
76
95
|
"dialog.close": "关闭",
|
|
77
96
|
"dialog.closeWorkspaceRename": "关闭重命名工作区对话框",
|
|
78
97
|
"dialog.closeWorkspaceDelete": "关闭删除工作区对话框",
|
|
@@ -84,6 +103,14 @@ window.__ModuleLoader__.load({
|
|
|
84
103
|
"action.retry": "重试",
|
|
85
104
|
"action.retryBinding": "重试绑定",
|
|
86
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",
|
|
87
114
|
"status.loading": "正在加载工作区…",
|
|
88
115
|
"error.worktreeDataUnavailable": "Worktree 数据不可用,请重试。",
|
|
89
116
|
"error.gitNotInstalled": "Git 未安装或不可用,请安装 Git 后重试。",
|
|
@@ -104,6 +131,8 @@ window.__ModuleLoader__.load({
|
|
|
104
131
|
"error.sessionRenameUnavailable": "会话重命名不可用,请重新连接后重试。",
|
|
105
132
|
"error.sessionBindingFailed": "Session {sessionId} 已创建,但 Worktree 绑定失败:{reason}",
|
|
106
133
|
"error.sessionAlreadyBound": "Session 已绑定到其他 Worktree,未创建新的 Session。",
|
|
134
|
+
"error.worktreePermissionConfirmationRequired": "Session 和绑定已保留;确认完全访问后重试,未打开 Session。",
|
|
135
|
+
"error.worktreePermissionFailed": "Worktree Session 权限设置失败,可重试:{reason}",
|
|
107
136
|
"error.sessionListNotReady": "Session 列表尚未就绪,请重试。",
|
|
108
137
|
"error.sessionFactsIncomplete": "Session 信息尚未完整,请重试。",
|
|
109
138
|
"error.worktreeSessionRepairRequired": "当前 Worktree 的 Session 绑定需要修复,请重试。",
|
|
@@ -142,6 +171,8 @@ window.__ModuleLoader__.load({
|
|
|
142
171
|
"worktree.expand": "Expand {name}",
|
|
143
172
|
"worktree.collapse": "Collapse {name}",
|
|
144
173
|
"worktree.options": "Worktree options for {name}",
|
|
174
|
+
"worktree.copyPath": "Copy path",
|
|
175
|
+
"worktree.createNew": "Create new Worktree",
|
|
145
176
|
"worktree.remove": "Remove Worktree",
|
|
146
177
|
"worktree.removeDescription": "Remove {name}? Sessions stay in DSH and remain available as detached bindings.",
|
|
147
178
|
"worktree.removeExternalDescription": "Remove {name}? This deletes the linked external Worktree directory. Sessions stay in DSH and remain available as detached bindings.",
|
|
@@ -175,6 +206,23 @@ window.__ModuleLoader__.load({
|
|
|
175
206
|
"session.expandMore": "Expand {count} more",
|
|
176
207
|
"session.collapse": "Collapse",
|
|
177
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",
|
|
178
226
|
"dialog.close": "Close",
|
|
179
227
|
"dialog.closeWorkspaceRename": "Close Rename Workspace dialog",
|
|
180
228
|
"dialog.closeWorkspaceDelete": "Close Delete Workspace dialog",
|
|
@@ -186,6 +234,14 @@ window.__ModuleLoader__.load({
|
|
|
186
234
|
"action.retry": "Retry",
|
|
187
235
|
"action.retryBinding": "Retry Binding",
|
|
188
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",
|
|
189
245
|
"status.loading": "Loading Workspaces…",
|
|
190
246
|
"error.worktreeDataUnavailable": "Worktree data is unavailable. Retry the request.",
|
|
191
247
|
"error.gitNotInstalled": "Git is not installed or unavailable. Install Git and retry.",
|
|
@@ -206,6 +262,8 @@ window.__ModuleLoader__.load({
|
|
|
206
262
|
"error.sessionRenameUnavailable": "Session rename is unavailable; retry after reconnecting.",
|
|
207
263
|
"error.sessionBindingFailed": "Session {sessionId} was created, but Worktree binding failed: {reason}",
|
|
208
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}",
|
|
209
267
|
"error.sessionListNotReady": "The Session list is not ready yet; retry the request.",
|
|
210
268
|
"error.sessionFactsIncomplete": "Session facts are incomplete; retry the request.",
|
|
211
269
|
"error.worktreeSessionRepairRequired": "The current Worktree Session binding needs repair; retry the request.",
|
|
@@ -231,7 +289,9 @@ window.__ModuleLoader__.load({
|
|
|
231
289
|
removeWorktree: "worktreeManager/removeWorktree",
|
|
232
290
|
insertWorktreeBefore: "worktreeManager/insertWorktreeBefore",
|
|
233
291
|
listBindings: "worktreeManager/listBindings",
|
|
234
|
-
bindSession: "worktreeManager/bindSession"
|
|
292
|
+
bindSession: "worktreeManager/bindSession",
|
|
293
|
+
ensureWorktreePermission: "worktreeManager/ensureWorktreePermission",
|
|
294
|
+
normalizeDetachedWorktreePermissions: "worktreeManager/normalizeDetachedWorktreePermissions"
|
|
235
295
|
});
|
|
236
296
|
/** Browser-safe error shared by transport, Gateway, endpoint and domain failures. */
|
|
237
297
|
var WorktreeConnectionError = class extends Error {
|
|
@@ -246,14 +306,14 @@ window.__ModuleLoader__.load({
|
|
|
246
306
|
this.retryable = options.retryable;
|
|
247
307
|
}
|
|
248
308
|
};
|
|
249
|
-
function isRecord$
|
|
309
|
+
function isRecord$2(value) {
|
|
250
310
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
251
311
|
}
|
|
252
312
|
function isWorktreeError(value) {
|
|
253
|
-
return isRecord$
|
|
313
|
+
return isRecord$2(value) && typeof value.code === "string" && typeof value.message === "string" && isRecord$2(value.details);
|
|
254
314
|
}
|
|
255
315
|
function isWorktreeRemoteResult(value) {
|
|
256
|
-
if (!isRecord$
|
|
316
|
+
if (!isRecord$2(value) || typeof value.ok !== "boolean") return false;
|
|
257
317
|
return value.ok || isWorktreeError(value.error) && value.error.details !== void 0;
|
|
258
318
|
}
|
|
259
319
|
function disposedError() {
|
|
@@ -274,7 +334,7 @@ window.__ModuleLoader__.load({
|
|
|
274
334
|
});
|
|
275
335
|
}
|
|
276
336
|
function gatewayFailure(endpoint, result) {
|
|
277
|
-
const rawDetails = isRecord$
|
|
337
|
+
const rawDetails = isRecord$2(result.error.details) ? result.error.details : {};
|
|
278
338
|
throw new WorktreeConnectionError({
|
|
279
339
|
code: result.error.code,
|
|
280
340
|
message: result.error.message,
|
|
@@ -343,6 +403,8 @@ window.__ModuleLoader__.load({
|
|
|
343
403
|
},
|
|
344
404
|
listBindings: (input) => invoke("listBindings", input),
|
|
345
405
|
bindSession: (input) => invoke("bindSession", input),
|
|
406
|
+
ensureWorktreePermission: (input) => invoke("ensureWorktreePermission", input),
|
|
407
|
+
normalizeDetachedWorktreePermissions: (input) => invoke("normalizeDetachedWorktreePermissions", input),
|
|
346
408
|
dispose() {
|
|
347
409
|
if (disposed) return;
|
|
348
410
|
disposed = true;
|
|
@@ -352,7 +414,7 @@ window.__ModuleLoader__.load({
|
|
|
352
414
|
};
|
|
353
415
|
}
|
|
354
416
|
//#endregion
|
|
355
|
-
//#region \0clutch-dsh-worktree-css:/
|
|
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
|
|
356
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";
|
|
357
419
|
const styleId$1 = "@cerbur/clutch-dsh-worktree/worktree-context.css";
|
|
358
420
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree-context.css\"]") === null) {
|
|
@@ -375,18 +437,25 @@ window.__ModuleLoader__.load({
|
|
|
375
437
|
function WorktreeContextLabel({ value, t }) {
|
|
376
438
|
if (value.kind === "none") return null;
|
|
377
439
|
const ariaLabel = value.kind === "main" ? t("context.main", { name: value.label }) : t("context.worktree", { name: value.label });
|
|
378
|
-
return (0, react_jsx_runtime.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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,
|
|
388
456
|
children: value.label
|
|
389
|
-
})
|
|
457
|
+
}),
|
|
458
|
+
openDelayMs: 500
|
|
390
459
|
});
|
|
391
460
|
}
|
|
392
461
|
/** Render the current branch or active Worktree branch beside the Session title. */
|
|
@@ -399,8 +468,8 @@ window.__ModuleLoader__.load({
|
|
|
399
468
|
});
|
|
400
469
|
}
|
|
401
470
|
//#endregion
|
|
402
|
-
//#region \0clutch-dsh-worktree-css:/
|
|
403
|
-
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-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.-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.-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: 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-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-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";
|
|
404
473
|
const styleId = "@cerbur/clutch-dsh-worktree/worktree.css";
|
|
405
474
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree.css\"]") === null) {
|
|
406
475
|
const tag = document.createElement("style");
|
|
@@ -427,6 +496,7 @@ window.__ModuleLoader__.load({
|
|
|
427
496
|
"workspaceLabel": "-cerbur-clutch-dsh-worktree-workspaceLabel",
|
|
428
497
|
"content": "-cerbur-clutch-dsh-worktree-content",
|
|
429
498
|
"error": "-cerbur-clutch-dsh-worktree-error",
|
|
499
|
+
"notice": "-cerbur-clutch-dsh-worktree-notice",
|
|
430
500
|
"retryButton": "-cerbur-clutch-dsh-worktree-retryButton",
|
|
431
501
|
"actionButton": "-cerbur-clutch-dsh-worktree-actionButton",
|
|
432
502
|
"recoveryActions": "-cerbur-clutch-dsh-worktree-recoveryActions",
|
|
@@ -459,9 +529,14 @@ window.__ModuleLoader__.load({
|
|
|
459
529
|
"worktreeState": "-cerbur-clutch-dsh-worktree-worktreeState",
|
|
460
530
|
"workspaceTitle": "-cerbur-clutch-dsh-worktree-workspaceTitle",
|
|
461
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",
|
|
462
536
|
"treeActionSlot": "-cerbur-clutch-dsh-worktree-treeActionSlot",
|
|
463
537
|
"workspaceActions": "-cerbur-clutch-dsh-worktree-workspaceActions",
|
|
464
538
|
"menuAction": "-cerbur-clutch-dsh-worktree-menuAction",
|
|
539
|
+
"groupActivity": "-cerbur-clutch-dsh-worktree-groupActivity",
|
|
465
540
|
"dropBefore": "-cerbur-clutch-dsh-worktree-dropBefore",
|
|
466
541
|
"dropAfter": "-cerbur-clutch-dsh-worktree-dropAfter",
|
|
467
542
|
"treeChildren": "-cerbur-clutch-dsh-worktree-treeChildren",
|
|
@@ -470,7 +545,11 @@ window.__ModuleLoader__.load({
|
|
|
470
545
|
"worktreeGroup": "-cerbur-clutch-dsh-worktree-worktreeGroup",
|
|
471
546
|
"treeSessionRow": "-cerbur-clutch-dsh-worktree-treeSessionRow",
|
|
472
547
|
"treeSessionContent": "-cerbur-clutch-dsh-worktree-treeSessionContent",
|
|
548
|
+
"sessionTrailing": "-cerbur-clutch-dsh-worktree-sessionTrailing",
|
|
549
|
+
"sessionMeta": "-cerbur-clutch-dsh-worktree-sessionMeta",
|
|
473
550
|
"rowActions": "-cerbur-clutch-dsh-worktree-rowActions",
|
|
551
|
+
"sessionStatus": "-cerbur-clutch-dsh-worktree-sessionStatus",
|
|
552
|
+
"sessionTime": "-cerbur-clutch-dsh-worktree-sessionTime",
|
|
474
553
|
"sessionOverflowButton": "-cerbur-clutch-dsh-worktree-sessionOverflowButton",
|
|
475
554
|
"renameInput": "-cerbur-clutch-dsh-worktree-renameInput",
|
|
476
555
|
"renameError": "-cerbur-clutch-dsh-worktree-renameError",
|
|
@@ -746,27 +825,27 @@ window.__ModuleLoader__.load({
|
|
|
746
825
|
//#region lib/client/worktree-expand-state.js
|
|
747
826
|
/** Browser-local storage identity; never sent to DSH or the plugin sidecar. */
|
|
748
827
|
const WORKTREE_EXPAND_STATE_STORAGE_KEY = "clutch-dsh-worktree.expand-state";
|
|
749
|
-
function emptyState() {
|
|
828
|
+
function emptyState$1() {
|
|
750
829
|
return {
|
|
751
830
|
collapsedWorkspaceIds: {},
|
|
752
831
|
collapsedMainWorkspaceIds: {},
|
|
753
832
|
collapsedWorktreeIds: {}
|
|
754
833
|
};
|
|
755
834
|
}
|
|
756
|
-
function isRecord(value) {
|
|
835
|
+
function isRecord$1(value) {
|
|
757
836
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
758
837
|
}
|
|
759
|
-
function normalizeIds(value) {
|
|
760
|
-
if (!isRecord(value)) return {};
|
|
838
|
+
function normalizeIds$1(value) {
|
|
839
|
+
if (!isRecord$1(value)) return {};
|
|
761
840
|
return Object.fromEntries(Object.entries(value).filter(([id, collapsed]) => id.length > 0 && collapsed === true));
|
|
762
841
|
}
|
|
763
842
|
/** Turn any persisted JSON value into the current state shape. */
|
|
764
843
|
function normalizeWorktreeExpandState(value) {
|
|
765
|
-
const source = isRecord(value) ? value : {};
|
|
844
|
+
const source = isRecord$1(value) ? value : {};
|
|
766
845
|
return {
|
|
767
|
-
collapsedWorkspaceIds: normalizeIds(source.collapsedWorkspaceIds),
|
|
768
|
-
collapsedMainWorkspaceIds: normalizeIds(source.collapsedMainWorkspaceIds),
|
|
769
|
-
collapsedWorktreeIds: normalizeIds(source.collapsedWorktreeIds)
|
|
846
|
+
collapsedWorkspaceIds: normalizeIds$1(source.collapsedWorkspaceIds),
|
|
847
|
+
collapsedMainWorkspaceIds: normalizeIds$1(source.collapsedMainWorkspaceIds),
|
|
848
|
+
collapsedWorktreeIds: normalizeIds$1(source.collapsedWorktreeIds)
|
|
770
849
|
};
|
|
771
850
|
}
|
|
772
851
|
function isWorkspaceExpanded(state, workspaceId) {
|
|
@@ -790,9 +869,9 @@ window.__ModuleLoader__.load({
|
|
|
790
869
|
function createWorktreeExpandStateStore(snapshotStoreFactory) {
|
|
791
870
|
let store;
|
|
792
871
|
try {
|
|
793
|
-
store = snapshotStoreFactory(emptyState(), { persist: { name: WORKTREE_EXPAND_STATE_STORAGE_KEY } });
|
|
872
|
+
store = snapshotStoreFactory(emptyState$1(), { persist: { name: WORKTREE_EXPAND_STATE_STORAGE_KEY } });
|
|
794
873
|
} catch {
|
|
795
|
-
store = snapshotStoreFactory(emptyState());
|
|
874
|
+
store = snapshotStoreFactory(emptyState$1());
|
|
796
875
|
}
|
|
797
876
|
store.set(normalizeWorktreeExpandState(store.getSnapshot()));
|
|
798
877
|
return Object.assign(store, { actions: {
|
|
@@ -883,6 +962,14 @@ window.__ModuleLoader__.load({
|
|
|
883
962
|
function reason(error, t) {
|
|
884
963
|
return error.message.length > 0 ? error.message : t("error.worktreeDataUnavailable");
|
|
885
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
|
+
}
|
|
886
973
|
function formatWorktreeViewError(error, t) {
|
|
887
974
|
switch (error.code) {
|
|
888
975
|
case "CLIENT_DISPOSED": return t("error.connectionDisposed");
|
|
@@ -908,6 +995,8 @@ window.__ModuleLoader__.load({
|
|
|
908
995
|
reason: reason(error, t)
|
|
909
996
|
});
|
|
910
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) });
|
|
911
1000
|
case "SESSION_LIST_NOT_READY": return t("error.sessionListNotReady");
|
|
912
1001
|
case "SESSION_FACTS_INCOMPLETE": return t("error.sessionFactsIncomplete");
|
|
913
1002
|
case "WORKTREE_SESSION_REPAIR_REQUIRED": return t("error.worktreeSessionRepairRequired");
|
|
@@ -919,6 +1008,127 @@ window.__ModuleLoader__.load({
|
|
|
919
1008
|
}
|
|
920
1009
|
//#endregion
|
|
921
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
|
+
}
|
|
922
1132
|
/** Read the native blank flag without inferring blankness from title or id. */
|
|
923
1133
|
function isBlankSession(sessionId, sessions) {
|
|
924
1134
|
return sessions.byId[sessionId]?.blank === true;
|
|
@@ -943,6 +1153,193 @@ window.__ModuleLoader__.load({
|
|
|
943
1153
|
return (summary?.displayTitle ?? sessionId).toLocaleLowerCase().includes(normalizedQuery);
|
|
944
1154
|
}
|
|
945
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
|
|
946
1343
|
//#region lib/client/worktree-view-errors.js
|
|
947
1344
|
/** A DSH Session exists even when the external Worktree binding needs repair. */
|
|
948
1345
|
var WorktreeSessionBindingError = class extends Error {
|
|
@@ -964,6 +1361,25 @@ window.__ModuleLoader__.load({
|
|
|
964
1361
|
this.cause = cause;
|
|
965
1362
|
}
|
|
966
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
|
+
};
|
|
967
1383
|
function recordDetails(value) {
|
|
968
1384
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
969
1385
|
const details = value.details;
|
|
@@ -974,7 +1390,7 @@ window.__ModuleLoader__.load({
|
|
|
974
1390
|
function toWorktreeViewError(error) {
|
|
975
1391
|
if (typeof error === "object" && error !== null) {
|
|
976
1392
|
const candidate = error;
|
|
977
|
-
const details = error instanceof WorktreeSessionBindingError ? {
|
|
1393
|
+
const details = error instanceof WorktreeSessionBindingError || error instanceof WorktreeSessionPermissionError ? {
|
|
978
1394
|
...recordDetails(error) ?? {},
|
|
979
1395
|
sessionId: error.sessionId
|
|
980
1396
|
} : recordDetails(error);
|
|
@@ -1016,6 +1432,21 @@ window.__ModuleLoader__.load({
|
|
|
1016
1432
|
}
|
|
1017
1433
|
throw new Error("Unable to generate an available default Worktree name.");
|
|
1018
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
|
+
}
|
|
1019
1450
|
/** Resolve a row-half drop target to native-style optional before-anchor semantics. */
|
|
1020
1451
|
function resolveWorktreeMove(worktreeIds, sourceWorktreeId, targetWorktreeId, half) {
|
|
1021
1452
|
const sourceIndex = worktreeIds.indexOf(sourceWorktreeId);
|
|
@@ -1028,11 +1459,19 @@ window.__ModuleLoader__.load({
|
|
|
1028
1459
|
return beforeWorktreeId === void 0 ? {} : { beforeWorktreeId };
|
|
1029
1460
|
}
|
|
1030
1461
|
/** Keep mutation routing in the browser Consumer while leaving wire details to the adapter. */
|
|
1031
|
-
async function executeWorktreeAction(manager, action) {
|
|
1462
|
+
async function executeWorktreeAction(manager, action, permission, onPermissionResult) {
|
|
1032
1463
|
if (action.type === "createWorktree") return manager.createWorktree(action.input);
|
|
1033
1464
|
if (action.type === "importWorktree") return manager.importWorktree(action.input);
|
|
1034
1465
|
if (action.type === "removeWorktree") {
|
|
1035
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);
|
|
1036
1475
|
return;
|
|
1037
1476
|
}
|
|
1038
1477
|
}
|
|
@@ -1051,7 +1490,7 @@ window.__ModuleLoader__.load({
|
|
|
1051
1490
|
} catch (error) {
|
|
1052
1491
|
throw new WorktreeSessionBindingError(sessionId, error);
|
|
1053
1492
|
}
|
|
1054
|
-
input.beforeOpen?.(sessionId);
|
|
1493
|
+
await input.beforeOpen?.(sessionId);
|
|
1055
1494
|
input.openSession(sessionId);
|
|
1056
1495
|
return sessionId;
|
|
1057
1496
|
}
|
|
@@ -1210,6 +1649,54 @@ window.__ModuleLoader__.load({
|
|
|
1210
1649
|
this.details = details;
|
|
1211
1650
|
}
|
|
1212
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
|
+
}
|
|
1213
1700
|
function resolveWorktreeSessionAction(input) {
|
|
1214
1701
|
if (input.target.status !== "active" || input.target.health === "repair") return {
|
|
1215
1702
|
kind: "reject",
|
|
@@ -1273,6 +1760,16 @@ window.__ModuleLoader__.load({
|
|
|
1273
1760
|
} catch (error) {
|
|
1274
1761
|
throw new WorktreeSessionBindingError(input.pending.sessionId, error);
|
|
1275
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
|
+
});
|
|
1276
1773
|
input.ensureSessionWorkspace(input.pending.workspaceId, input.pending.sessionId);
|
|
1277
1774
|
input.openSession(input.pending.sessionId);
|
|
1278
1775
|
return input.pending.sessionId;
|
|
@@ -1296,6 +1793,16 @@ window.__ModuleLoader__.load({
|
|
|
1296
1793
|
bindings
|
|
1297
1794
|
});
|
|
1298
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
|
+
});
|
|
1299
1806
|
options.ensureSessionWorkspace(input.workspaceId, action.sessionId);
|
|
1300
1807
|
if (!disposed) options.openSession(action.sessionId);
|
|
1301
1808
|
return action.sessionId;
|
|
@@ -1310,6 +1817,16 @@ window.__ModuleLoader__.load({
|
|
|
1310
1817
|
} catch (error) {
|
|
1311
1818
|
throw new WorktreeSessionBindingError(action.sessionId, error);
|
|
1312
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
|
+
});
|
|
1313
1830
|
if (!disposed) {
|
|
1314
1831
|
options.ensureSessionWorkspace(input.workspaceId, action.sessionId);
|
|
1315
1832
|
if (!disposed) options.openSession(action.sessionId);
|
|
@@ -1322,9 +1839,18 @@ window.__ModuleLoader__.load({
|
|
|
1322
1839
|
...input,
|
|
1323
1840
|
createSession: options.createSession,
|
|
1324
1841
|
manager: options.manager,
|
|
1325
|
-
beforeOpen: (sessionId) => {
|
|
1842
|
+
beforeOpen: async (sessionId) => {
|
|
1326
1843
|
if (disposed) throw new WorktreeSessionActionError("CLIENT_DISPOSED", "", false);
|
|
1327
|
-
|
|
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
|
+
});
|
|
1328
1854
|
},
|
|
1329
1855
|
openSession: (sessionId) => {
|
|
1330
1856
|
if (!disposed) options.openSession(sessionId);
|
|
@@ -1734,6 +2260,13 @@ window.__ModuleLoader__.load({
|
|
|
1734
2260
|
"session-group:" + location.groupKey
|
|
1735
2261
|
];
|
|
1736
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
|
+
}
|
|
1737
2270
|
//#endregion
|
|
1738
2271
|
//#region lib/client/worktree-session-position.js
|
|
1739
2272
|
function scrollCurrentSessionIntoView(root, sessionId) {
|
|
@@ -1749,8 +2282,65 @@ window.__ModuleLoader__.load({
|
|
|
1749
2282
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1750
2283
|
return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
1751
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
|
+
}
|
|
1752
2342
|
/** Workspace row using the native DSH menu, fixed action column, and drag contract. */
|
|
1753
|
-
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 }) {
|
|
1754
2344
|
const workspaceMenuItems = [{
|
|
1755
2345
|
id: "rename",
|
|
1756
2346
|
label: t("workspace.rename"),
|
|
@@ -1767,6 +2357,7 @@ window.__ModuleLoader__.load({
|
|
|
1767
2357
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1768
2358
|
className: `${worktree_css_default.workspaceRow} ${markerClass}`,
|
|
1769
2359
|
"data-workspace-drag": drag.active ? "active" : void 0,
|
|
2360
|
+
"data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
|
|
1770
2361
|
"data-menu-open": menuOpen || void 0,
|
|
1771
2362
|
draggable: true,
|
|
1772
2363
|
onClick: () => {
|
|
@@ -1812,50 +2403,61 @@ window.__ModuleLoader__.load({
|
|
|
1812
2403
|
}),
|
|
1813
2404
|
(0, react_jsx_runtime.jsxs)("span", {
|
|
1814
2405
|
className: `${worktree_css_default.treeActionSlot} ${worktree_css_default.workspaceActions}`,
|
|
1815
|
-
children: [
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
anchor: (0, react_jsx_runtime.jsx)("button", {
|
|
1831
|
-
type: "button",
|
|
1832
|
-
className: worktree_css_default.iconButton,
|
|
1833
|
-
"data-workspace-menu": true,
|
|
1834
|
-
"aria-label": t("workspace.options", { name: workspace.title }),
|
|
1835
|
-
onClick: (event) => {
|
|
1836
|
-
event.stopPropagation();
|
|
1837
|
-
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);
|
|
1838
2421
|
},
|
|
1839
|
-
|
|
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
|
+
})
|
|
1840
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, {})
|
|
1841
2453
|
})
|
|
1842
|
-
|
|
1843
|
-
type: "button",
|
|
1844
|
-
className: worktree_css_default.iconButton,
|
|
1845
|
-
"data-add-worktree": true,
|
|
1846
|
-
"aria-label": t("workspace.addWorktree", { name: workspace.title }),
|
|
1847
|
-
onClick: (event) => {
|
|
1848
|
-
event.stopPropagation();
|
|
1849
|
-
onCreateWorktree();
|
|
1850
|
-
},
|
|
1851
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
|
|
1852
|
-
})]
|
|
2454
|
+
]
|
|
1853
2455
|
})
|
|
1854
2456
|
]
|
|
1855
2457
|
});
|
|
1856
2458
|
}
|
|
1857
2459
|
/** Shared Main/Worktree group row with parameterized icon, state, and actions. */
|
|
1858
|
-
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 }) {
|
|
1859
2461
|
const main = kind === "main";
|
|
1860
2462
|
const markerClass = drag?.marker === "before" ? worktree_css_default.dropBefore : drag?.marker === "after" ? worktree_css_default.dropAfter : "";
|
|
1861
2463
|
const dragProps = drag === void 0 ? {} : {
|
|
@@ -1882,6 +2484,7 @@ window.__ModuleLoader__.load({
|
|
|
1882
2484
|
className: `${worktree_css_default.worktreeRow} ${markerClass}`,
|
|
1883
2485
|
"data-main-group": main ? "true" : void 0,
|
|
1884
2486
|
"data-main-expanded": main ? String(expanded) : void 0,
|
|
2487
|
+
"data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
|
|
1885
2488
|
"data-menu-open": menu?.open ? "true" : void 0,
|
|
1886
2489
|
"data-worktree-drag": drag?.active ? "active" : void 0,
|
|
1887
2490
|
...dragProps,
|
|
@@ -1916,50 +2519,77 @@ window.__ModuleLoader__.load({
|
|
|
1916
2519
|
}),
|
|
1917
2520
|
(0, react_jsx_runtime.jsxs)("span", {
|
|
1918
2521
|
className: worktree_css_default.treeActionSlot,
|
|
1919
|
-
children: [
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
menu.onOpenChange(false);
|
|
1935
|
-
if (id === "remove") menu.onRemove();
|
|
1936
|
-
},
|
|
1937
|
-
portal: true,
|
|
1938
|
-
closeOnPointerLeave: true,
|
|
1939
|
-
anchor: (0, react_jsx_runtime.jsx)("button", {
|
|
1940
|
-
type: "button",
|
|
1941
|
-
className: worktree_css_default.iconButton,
|
|
1942
|
-
"data-worktree-menu": true,
|
|
1943
|
-
"aria-label": t("worktree.options", { name: menu.label }),
|
|
1944
|
-
onClick: (event) => {
|
|
1945
|
-
event.stopPropagation();
|
|
1946
|
-
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);
|
|
1947
2537
|
},
|
|
1948
|
-
|
|
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
|
+
})
|
|
1949
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, {})
|
|
1950
2591
|
})
|
|
1951
|
-
|
|
1952
|
-
type: "button",
|
|
1953
|
-
className: worktree_css_default.iconButton,
|
|
1954
|
-
"data-add-main-session": main ? "true" : void 0,
|
|
1955
|
-
"data-add-session": main ? void 0 : "true",
|
|
1956
|
-
"aria-label": t("worktree.addSession", { name: workspaceTitle }),
|
|
1957
|
-
onClick: (event) => {
|
|
1958
|
-
event.stopPropagation();
|
|
1959
|
-
onCreateSession();
|
|
1960
|
-
},
|
|
1961
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
|
|
1962
|
-
})]
|
|
2592
|
+
]
|
|
1963
2593
|
})
|
|
1964
2594
|
]
|
|
1965
2595
|
});
|
|
@@ -1975,7 +2605,7 @@ window.__ModuleLoader__.load({
|
|
|
1975
2605
|
});
|
|
1976
2606
|
}
|
|
1977
2607
|
/** Worktree-mode Session row with the same trailing options menu as native DSH rows. */
|
|
1978
|
-
function WorktreeSessionRow({ t, sessionId, blank, current, label, drag, actionPending, onOpen, onRename, onFork, onArchive }) {
|
|
2608
|
+
function WorktreeSessionRow({ t, sessionId, blank, current, label, drag, actionPending, onOpen, onRename, onFork, onArchive, presentation }) {
|
|
1979
2609
|
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
1980
2610
|
const markerClass = drag.marker === "before" ? worktree_css_default.dropBefore : drag.marker === "after" ? worktree_css_default.dropAfter : "";
|
|
1981
2611
|
const sessionMenuItems = [
|
|
@@ -1998,73 +2628,107 @@ window.__ModuleLoader__.load({
|
|
|
1998
2628
|
disabled: onArchive === void 0 || actionPending
|
|
1999
2629
|
}
|
|
2000
2630
|
];
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
drag.
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
onClose: () => {
|
|
2041
|
-
setMenuOpen(false);
|
|
2042
|
-
},
|
|
2043
|
-
items: sessionMenuItems,
|
|
2044
|
-
onSelect: (id) => {
|
|
2045
|
-
setMenuOpen(false);
|
|
2046
|
-
if (id === "rename") onRename?.(sessionId, label);
|
|
2047
|
-
if (id === "fork") onFork?.(sessionId);
|
|
2048
|
-
if (id === "archive") onArchive?.(sessionId);
|
|
2049
|
-
},
|
|
2050
|
-
portal: true,
|
|
2051
|
-
closeOnPointerLeave: true,
|
|
2052
|
-
anchor: (0, react_jsx_runtime.jsx)("button", {
|
|
2053
|
-
type: "button",
|
|
2054
|
-
className: worktree_css_default.iconButton,
|
|
2055
|
-
"data-session-menu": true,
|
|
2056
|
-
"aria-label": t("session.options", { name: label }),
|
|
2057
|
-
onClick: (event) => {
|
|
2058
|
-
event.stopPropagation();
|
|
2059
|
-
setMenuOpen((current) => !current);
|
|
2060
|
-
},
|
|
2061
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
|
|
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
|
|
2062
2670
|
})
|
|
2063
|
-
})
|
|
2064
|
-
|
|
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
|
+
})]
|
|
2716
|
+
})]
|
|
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")
|
|
2065
2729
|
});
|
|
2066
2730
|
}
|
|
2067
|
-
function WorktreeSessionGroup({ t, groupKey, sessionIds, workspaceId, currentSessionId, 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 }) {
|
|
2068
2732
|
const visibleSessionIds = expanded ? sessionIds : sessionIds.slice(0, 5);
|
|
2069
2733
|
const sameGroupDrag = dragState?.groupKey === groupKey;
|
|
2070
2734
|
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [visibleSessionIds.map((sessionId) => (0, react_jsx_runtime.jsx)(WorktreeSessionRow, {
|
|
@@ -2073,6 +2737,7 @@ window.__ModuleLoader__.load({
|
|
|
2073
2737
|
blank: isBlankSession(sessionId, sessions),
|
|
2074
2738
|
current: sessionId === currentSessionId,
|
|
2075
2739
|
label: sessionLabel(sessionId, sessions, t),
|
|
2740
|
+
presentation: sessionPresentations[sessionId],
|
|
2076
2741
|
drag: {
|
|
2077
2742
|
active: sameGroupDrag,
|
|
2078
2743
|
marker: sameGroupDrag && dragState.over?.sessionId === sessionId ? dragState.over.half : null,
|
|
@@ -2124,6 +2789,33 @@ window.__ModuleLoader__.load({
|
|
|
2124
2789
|
status: "idle",
|
|
2125
2790
|
views: []
|
|
2126
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
|
+
}
|
|
2127
2819
|
function useStableWorkspaceIds(workspaces) {
|
|
2128
2820
|
const next = workspaces.map((workspace) => workspace.workspaceId);
|
|
2129
2821
|
const previousRef = (0, react.useRef)([]);
|
|
@@ -2135,14 +2827,30 @@ window.__ModuleLoader__.load({
|
|
|
2135
2827
|
* Peer Worktree navigation surface. It keeps the original DSH Workspace mode
|
|
2136
2828
|
* intact and renders a browser-local Workspace → Worktree → Session projection.
|
|
2137
2829
|
*/
|
|
2138
|
-
function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, expandState, manager, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession }) {
|
|
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 }) {
|
|
2139
2831
|
const mode = effectiveViewMode(useStore((state) => state.viewMode), available && manager !== void 0);
|
|
2140
2832
|
const sessions = useSessions((state) => state);
|
|
2141
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);
|
|
2142
2836
|
const currentSessionId = sessions.current;
|
|
2143
2837
|
const workspaceIds = useStableWorkspaceIds(workspaces.items);
|
|
2144
2838
|
const [readState, setReadState] = (0, react.useState)(EMPTY_READ_STATE);
|
|
2145
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]);
|
|
2146
2854
|
const readStateRef = (0, react.useRef)(readState);
|
|
2147
2855
|
readStateRef.current = readState;
|
|
2148
2856
|
const [searchQuery, setSearchQuery] = (0, react.useState)("");
|
|
@@ -2161,6 +2869,7 @@ window.__ModuleLoader__.load({
|
|
|
2161
2869
|
});
|
|
2162
2870
|
const [selectedImportPath, setSelectedImportPath] = (0, react.useState)();
|
|
2163
2871
|
const [openWorkspaceMenuId, setOpenWorkspaceMenuId] = (0, react.useState)();
|
|
2872
|
+
const [openMainMenuId, setOpenMainMenuId] = (0, react.useState)();
|
|
2164
2873
|
const [openWorktreeMenuId, setOpenWorktreeMenuId] = (0, react.useState)();
|
|
2165
2874
|
const [worktreeRemoval, setWorktreeRemoval] = (0, react.useState)();
|
|
2166
2875
|
const [selectedBranch, setSelectedBranch] = (0, react.useState)("");
|
|
@@ -2168,6 +2877,7 @@ window.__ModuleLoader__.load({
|
|
|
2168
2877
|
const [pendingSessionBinding, setPendingSessionBinding] = (0, react.useState)();
|
|
2169
2878
|
const [actionError, setActionError] = (0, react.useState)();
|
|
2170
2879
|
const [actionPending, setActionPending] = (0, react.useState)(false);
|
|
2880
|
+
const [forkRetryKey, setForkRetryKey] = (0, react.useState)();
|
|
2171
2881
|
const [sessionRenameTarget, setSessionRenameTarget] = (0, react.useState)();
|
|
2172
2882
|
const [sessionRenameDraft, setSessionRenameDraft] = (0, react.useState)("");
|
|
2173
2883
|
const [sessionRenamePending, setSessionRenamePending] = (0, react.useState)(false);
|
|
@@ -2187,6 +2897,7 @@ window.__ModuleLoader__.load({
|
|
|
2187
2897
|
const [worktreeDrag, setWorktreeDrag] = (0, react.useState)();
|
|
2188
2898
|
const worktreeDropCommitted = (0, react.useRef)(false);
|
|
2189
2899
|
const refreshGuard = (0, react.useRef)(createWorktreeRefreshGuard());
|
|
2900
|
+
const forkRecoveryRevisionRef = (0, react.useRef)(forkRecoverySnapshot.revision);
|
|
2190
2901
|
const modalReadLoader = (0, react.useRef)(createWorktreeModalViewLoader());
|
|
2191
2902
|
const modalReadViewRef = (0, react.useRef)();
|
|
2192
2903
|
const importCandidatesGuard = (0, react.useRef)(createWorktreeRefreshGuard());
|
|
@@ -2265,6 +2976,20 @@ window.__ModuleLoader__.load({
|
|
|
2265
2976
|
setReadState(EMPTY_READ_STATE);
|
|
2266
2977
|
}
|
|
2267
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
|
+
]);
|
|
2268
2993
|
(0, react.useEffect)(() => {
|
|
2269
2994
|
if (readState.status !== "ready" || syncSessionWorkspaces === void 0) return;
|
|
2270
2995
|
syncSessionWorkspaces(readState.views.flatMap((view) => view.bindings.map((binding) => ({
|
|
@@ -2277,6 +3002,60 @@ window.__ModuleLoader__.load({
|
|
|
2277
3002
|
syncSessionWorkspaces
|
|
2278
3003
|
]);
|
|
2279
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
|
+
]);
|
|
2280
3059
|
const currentSessionLocation = (0, react.useMemo)(() => readState.status === "ready" ? resolveCurrentSessionLocation(currentSessionId, workspaces.items, readState.views) : void 0, [
|
|
2281
3060
|
currentSessionId,
|
|
2282
3061
|
readState.status,
|
|
@@ -2496,7 +3275,10 @@ window.__ModuleLoader__.load({
|
|
|
2496
3275
|
return;
|
|
2497
3276
|
}
|
|
2498
3277
|
setActionError(void 0);
|
|
2499
|
-
|
|
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) => {
|
|
2500
3282
|
setActionError(toWorktreeViewError(error));
|
|
2501
3283
|
});
|
|
2502
3284
|
};
|
|
@@ -2617,8 +3399,7 @@ window.__ModuleLoader__.load({
|
|
|
2617
3399
|
expandState.actions.toggleWorktree(worktreeId);
|
|
2618
3400
|
if (visuallyExpanded) clearSessionGroups(["worktree:" + worktreeId]);
|
|
2619
3401
|
};
|
|
2620
|
-
const toggleSessionGroup = (groupKey) => {
|
|
2621
|
-
const autoExpanded = isCurrentSessionReveal("session-group:" + groupKey);
|
|
3402
|
+
const toggleSessionGroup = (groupKey, autoExpanded) => {
|
|
2622
3403
|
const transientExpanded = expandedSessionGroups[groupKey] === true;
|
|
2623
3404
|
if (autoExpanded) {
|
|
2624
3405
|
suppressCurrentSessionReveal("session-group:" + groupKey);
|
|
@@ -2643,7 +3424,7 @@ window.__ModuleLoader__.load({
|
|
|
2643
3424
|
setModalReadError(void 0);
|
|
2644
3425
|
setModalReadLoading(false);
|
|
2645
3426
|
};
|
|
2646
|
-
const openWorktreeCreator = (workspace) => {
|
|
3427
|
+
const openWorktreeCreator = (workspace, defaults = {}) => {
|
|
2647
3428
|
const view = viewByWorkspace.get(workspace.workspaceId);
|
|
2648
3429
|
modalReadLoader.current.invalidate();
|
|
2649
3430
|
modalReadViewRef.current = void 0;
|
|
@@ -2659,13 +3440,13 @@ window.__ModuleLoader__.load({
|
|
|
2659
3440
|
setModalReadError(void 0);
|
|
2660
3441
|
setModalReadLoading(false);
|
|
2661
3442
|
if (view === void 0) {
|
|
2662
|
-
setSelectedBranch("");
|
|
2663
|
-
setNewBranch("");
|
|
3443
|
+
setSelectedBranch(defaults.baseBranch ?? "");
|
|
3444
|
+
setNewBranch(defaults.newBranch ?? "");
|
|
2664
3445
|
loadModalWorktreeView(workspace.workspaceId);
|
|
2665
3446
|
return;
|
|
2666
3447
|
}
|
|
2667
|
-
setSelectedBranch(selectDefaultBaseBranch(view.branches));
|
|
2668
|
-
setNewBranch(createDefaultWorktreeName([...view.branches.map((branch) => branch.name), ...view.worktrees.map((worktree) => worktree.branch)]));
|
|
3448
|
+
setSelectedBranch(defaults.baseBranch ?? selectDefaultBaseBranch(view.branches));
|
|
3449
|
+
setNewBranch(defaults.newBranch ?? createDefaultWorktreeName([...view.branches.map((branch) => branch.name), ...view.worktrees.map((worktree) => worktree.branch)]));
|
|
2669
3450
|
};
|
|
2670
3451
|
const changeWorktreeModalMode = (mode) => {
|
|
2671
3452
|
setWorktreeModalMode(mode);
|
|
@@ -2695,6 +3476,11 @@ window.__ModuleLoader__.load({
|
|
|
2695
3476
|
...sessionInput,
|
|
2696
3477
|
sessionId: error.sessionId
|
|
2697
3478
|
});
|
|
3479
|
+
if (error instanceof WorktreeSessionPermissionError && error.retryable) setPendingSessionBinding({
|
|
3480
|
+
...sessionInput,
|
|
3481
|
+
sessionId: error.sessionId,
|
|
3482
|
+
permissionRequired: true
|
|
3483
|
+
});
|
|
2698
3484
|
throw error;
|
|
2699
3485
|
}
|
|
2700
3486
|
await refresh({ preserveCurrent: true });
|
|
@@ -2756,6 +3542,11 @@ window.__ModuleLoader__.load({
|
|
|
2756
3542
|
...input,
|
|
2757
3543
|
sessionId: error.sessionId
|
|
2758
3544
|
});
|
|
3545
|
+
if (error instanceof WorktreeSessionPermissionError && error.retryable) setPendingSessionBinding({
|
|
3546
|
+
...input,
|
|
3547
|
+
sessionId: error.sessionId,
|
|
3548
|
+
permissionRequired: true
|
|
3549
|
+
});
|
|
2759
3550
|
setActionError(toWorktreeViewError(error));
|
|
2760
3551
|
} finally {
|
|
2761
3552
|
setActionPending(false);
|
|
@@ -2774,6 +3565,9 @@ window.__ModuleLoader__.load({
|
|
|
2774
3565
|
ensureSessionWorkspace: (workspaceId, sessionId) => {
|
|
2775
3566
|
ensureSessionWorkspace?.(workspaceId, sessionId);
|
|
2776
3567
|
},
|
|
3568
|
+
permission,
|
|
3569
|
+
confirmFullAccess,
|
|
3570
|
+
onPermissionResult,
|
|
2777
3571
|
openSession
|
|
2778
3572
|
});
|
|
2779
3573
|
setPendingSessionBinding(void 0);
|
|
@@ -2786,6 +3580,17 @@ window.__ModuleLoader__.load({
|
|
|
2786
3580
|
setActionPending(false);
|
|
2787
3581
|
}
|
|
2788
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
|
+
};
|
|
2789
3594
|
const openWorkspaceSession = (workspaceId, sessionId) => {
|
|
2790
3595
|
ensureSessionWorkspace?.(workspaceId, sessionId);
|
|
2791
3596
|
openWorktreeSession({ open: openSession }, sessionId);
|
|
@@ -2851,7 +3656,7 @@ window.__ModuleLoader__.load({
|
|
|
2851
3656
|
onClick: () => {
|
|
2852
3657
|
if (createWorkspace !== void 0) runMutation(createWorkspace);
|
|
2853
3658
|
},
|
|
2854
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
3659
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconProjectAddOutline16, {})
|
|
2855
3660
|
})
|
|
2856
3661
|
]
|
|
2857
3662
|
}),
|
|
@@ -2859,6 +3664,47 @@ window.__ModuleLoader__.load({
|
|
|
2859
3664
|
className: worktree_css_default.content,
|
|
2860
3665
|
tabIndex: 0,
|
|
2861
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
|
+
}),
|
|
2862
3708
|
actionError !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
2863
3709
|
className: worktree_css_default.error,
|
|
2864
3710
|
role: "alert",
|
|
@@ -2879,7 +3725,7 @@ window.__ModuleLoader__.load({
|
|
|
2879
3725
|
retrySessionBinding();
|
|
2880
3726
|
},
|
|
2881
3727
|
children: t("action.retryBinding")
|
|
2882
|
-
}), (0, react_jsx_runtime.jsx)("button", {
|
|
3728
|
+
}), !pendingSessionBinding.permissionRequired && (0, react_jsx_runtime.jsx)("button", {
|
|
2883
3729
|
type: "button",
|
|
2884
3730
|
className: worktree_css_default.actionButton,
|
|
2885
3731
|
disabled: actionPending,
|
|
@@ -2935,13 +3781,19 @@ window.__ModuleLoader__.load({
|
|
|
2935
3781
|
const expanded = isWorkspaceExpanded(expandSnapshot, workspace.workspaceId) || isCurrentSessionReveal("workspace:" + workspace.workspaceId);
|
|
2936
3782
|
const workspaceMatchesQuery = includesText(workspace.title, query);
|
|
2937
3783
|
const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
|
|
3784
|
+
const workspaceHasOngoingSession = hasOngoingSession(filterVisibleSessionIds(allWorkspaceSessionIds, sessions), sessionPresentations);
|
|
2938
3785
|
const bindings = view?.bindings ?? [];
|
|
2939
|
-
const
|
|
2940
|
-
const mainExpanded = isMainExpanded(expandSnapshot, workspace.workspaceId) || isCurrentSessionReveal("main:" + workspace.workspaceId);
|
|
3786
|
+
const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]), sessions);
|
|
2941
3787
|
const mainGroupKey = `main:${workspace.workspaceId}`;
|
|
2942
|
-
const
|
|
2943
|
-
const
|
|
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;
|
|
2944
3795
|
const worktrees = view?.worktrees ?? [];
|
|
3796
|
+
const workspaceWorktreeNames = [...view?.branches.map((branch) => branch.name) ?? [], ...view?.worktrees.map((worktree) => worktree.branch) ?? []];
|
|
2945
3797
|
const sameWorkspaceWorktreeDrag = worktreeDrag?.workspaceId === workspace.workspaceId;
|
|
2946
3798
|
return (0, react_jsx_runtime.jsxs)("section", {
|
|
2947
3799
|
className: worktree_css_default.workspaceGroup,
|
|
@@ -2950,6 +3802,7 @@ window.__ModuleLoader__.load({
|
|
|
2950
3802
|
t,
|
|
2951
3803
|
workspace,
|
|
2952
3804
|
expanded,
|
|
3805
|
+
hasOngoingSession: workspaceHasOngoingSession,
|
|
2953
3806
|
actionPending,
|
|
2954
3807
|
menuOpen: openWorkspaceMenuId === workspace.workspaceId,
|
|
2955
3808
|
drag: {
|
|
@@ -3007,11 +3860,29 @@ window.__ModuleLoader__.load({
|
|
|
3007
3860
|
kind: "main",
|
|
3008
3861
|
label: mainLabel,
|
|
3009
3862
|
expanded: mainExpanded,
|
|
3863
|
+
hasOngoingSession: hasOngoingSession(mainSessionIds, sessionPresentations),
|
|
3010
3864
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
|
|
3011
3865
|
workspaceTitle: workspace.title,
|
|
3012
3866
|
onToggle: () => {
|
|
3013
3867
|
toggleMain(workspace.workspaceId);
|
|
3014
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
|
+
},
|
|
3015
3886
|
onCreateSession: createMainSession === void 0 ? void 0 : () => {
|
|
3016
3887
|
createMainSession(workspace.workspaceId);
|
|
3017
3888
|
}
|
|
@@ -3025,9 +3896,10 @@ window.__ModuleLoader__.load({
|
|
|
3025
3896
|
expanded: mainSessionGroupExpanded,
|
|
3026
3897
|
actionPending,
|
|
3027
3898
|
sessions,
|
|
3899
|
+
sessionPresentations,
|
|
3028
3900
|
dragState: sessionDrag,
|
|
3029
3901
|
onToggleExpanded: () => {
|
|
3030
|
-
toggleSessionGroup(mainGroupKey);
|
|
3902
|
+
toggleSessionGroup(mainGroupKey, mainSessionGroupAutoExpanded);
|
|
3031
3903
|
},
|
|
3032
3904
|
onStartDrag: (groupKey, sessionId) => {
|
|
3033
3905
|
sessionDropCommitted.current = false;
|
|
@@ -3070,8 +3942,10 @@ window.__ModuleLoader__.load({
|
|
|
3070
3942
|
if (query.length > 0 && !worktreeMatchesQuery && !worktreeSessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) return null;
|
|
3071
3943
|
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
|
|
3072
3944
|
const worktreeGroupKey = `worktree:${record.worktreeId}`;
|
|
3073
|
-
const
|
|
3074
|
-
const
|
|
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;
|
|
3075
3949
|
const state = record.status === "removed" ? "warning" : record.health === "repair" ? "error" : "done";
|
|
3076
3950
|
const stateLabel = record.status === "removed" ? t("worktree.detached") : record.health === "repair" ? t("worktree.repair") : t("worktree.ready");
|
|
3077
3951
|
const worktreeExpanded = isWorktreeExpanded(expandSnapshot, record.worktreeId) || isCurrentSessionReveal("worktree:" + record.worktreeId);
|
|
@@ -3084,6 +3958,7 @@ window.__ModuleLoader__.load({
|
|
|
3084
3958
|
label: record.branch,
|
|
3085
3959
|
worktreeId: record.worktreeId,
|
|
3086
3960
|
expanded: worktreeExpanded,
|
|
3961
|
+
hasOngoingSession: hasOngoingSession(worktreeSessionIds, sessionPresentations),
|
|
3087
3962
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
|
|
3088
3963
|
workspaceTitle: workspace.title,
|
|
3089
3964
|
state,
|
|
@@ -3098,18 +3973,27 @@ window.__ModuleLoader__.load({
|
|
|
3098
3973
|
cwd: record.absolutePath
|
|
3099
3974
|
});
|
|
3100
3975
|
} : void 0,
|
|
3101
|
-
menu:
|
|
3976
|
+
menu: {
|
|
3102
3977
|
open: openWorktreeMenuId === record.worktreeId,
|
|
3103
3978
|
label: record.branch,
|
|
3979
|
+
copyPath: record.absolutePath,
|
|
3980
|
+
showCreate: record.status === "active",
|
|
3981
|
+
showRemove: record.status === "active",
|
|
3104
3982
|
disabled: actionPending,
|
|
3105
3983
|
onOpenChange: (open) => {
|
|
3106
3984
|
setOpenWorktreeMenuId(open ? record.worktreeId : void 0);
|
|
3107
3985
|
},
|
|
3108
|
-
|
|
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" ? () => {
|
|
3109
3993
|
setWorktreeRemoval(record);
|
|
3110
3994
|
setActionError(void 0);
|
|
3111
|
-
}
|
|
3112
|
-
}
|
|
3995
|
+
} : void 0
|
|
3996
|
+
},
|
|
3113
3997
|
drag: {
|
|
3114
3998
|
active: sameWorkspaceWorktreeDrag,
|
|
3115
3999
|
marker: worktreeDrag?.over?.worktreeId === record.worktreeId ? worktreeDrag.over.half : null,
|
|
@@ -3151,9 +4035,10 @@ window.__ModuleLoader__.load({
|
|
|
3151
4035
|
expanded: sessionGroupExpanded,
|
|
3152
4036
|
actionPending,
|
|
3153
4037
|
sessions,
|
|
4038
|
+
sessionPresentations,
|
|
3154
4039
|
dragState: sessionDrag,
|
|
3155
4040
|
onToggleExpanded: () => {
|
|
3156
|
-
toggleSessionGroup(worktreeGroupKey);
|
|
4041
|
+
toggleSessionGroup(worktreeGroupKey, sessionGroupAutoExpanded);
|
|
3157
4042
|
},
|
|
3158
4043
|
onStartDrag: (groupKey, sessionId) => {
|
|
3159
4044
|
sessionDropCommitted.current = false;
|
|
@@ -3279,11 +4164,29 @@ window.__ModuleLoader__.load({
|
|
|
3279
4164
|
workspaceId: target.workspaceId,
|
|
3280
4165
|
worktreeId: target.worktreeId
|
|
3281
4166
|
}
|
|
3282
|
-
});
|
|
4167
|
+
}, permission, onPermissionNotice);
|
|
3283
4168
|
await invalidateWorktreeContext?.(target.workspaceId);
|
|
3284
4169
|
setWorktreeRemoval(void 0);
|
|
3285
4170
|
});
|
|
3286
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
|
+
}
|
|
3287
4190
|
})
|
|
3288
4191
|
]
|
|
3289
4192
|
});
|
|
@@ -3649,6 +4552,313 @@ window.__ModuleLoader__.load({
|
|
|
3649
4552
|
};
|
|
3650
4553
|
}
|
|
3651
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
|
|
3652
4862
|
//#region lib/client/entry.js
|
|
3653
4863
|
/** Required DSH Client services; Connection is the sole Worktree wire dependency. */
|
|
3654
4864
|
const inject = [
|
|
@@ -3663,11 +4873,22 @@ window.__ModuleLoader__.load({
|
|
|
3663
4873
|
* with that fiber, so slot consumers share one manager and one request lifetime.
|
|
3664
4874
|
*/
|
|
3665
4875
|
function apply(ctx) {
|
|
4876
|
+
if (typeof document !== "undefined") ctx.effect(() => installWorktreePermissionIcon(document), "clutch-dsh-worktree: permission icon cleanup");
|
|
3666
4877
|
ctx.effect(() => ctx.locale.register(WORKTREE_NS, {
|
|
3667
4878
|
zh,
|
|
3668
4879
|
en
|
|
3669
4880
|
}), "clutch-dsh-worktree: locale dictionaries");
|
|
3670
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
|
+
};
|
|
3671
4892
|
const sessions = ctx.sessions;
|
|
3672
4893
|
const virtualWorkspaceMembership = createVirtualWorkspaceMembership(ctx.workspaces.list);
|
|
3673
4894
|
const contextProjection = createWorktreeContextProjection({
|
|
@@ -3677,11 +4898,14 @@ window.__ModuleLoader__.load({
|
|
|
3677
4898
|
storeFactory: _deepseek_ai_dsh_client_runtime_client.createSnapshotStore
|
|
3678
4899
|
});
|
|
3679
4900
|
ctx.effect(() => () => manager.dispose(), "clutch-dsh-worktree: connection cleanup");
|
|
4901
|
+
ctx.effect(() => () => fullAccessConfirmation.dispose(), "clutch-dsh-worktree: Full Access confirmation cleanup");
|
|
3680
4902
|
ctx.effect(() => () => virtualWorkspaceMembership.dispose(), "clutch-dsh-worktree: Workspace membership cleanup");
|
|
3681
4903
|
ctx.effect(() => () => contextProjection.dispose(), "clutch-dsh-worktree: Session context cleanup");
|
|
3682
4904
|
contextProjection.refresh();
|
|
3683
4905
|
const viewStore = createWorktreeViewStore();
|
|
3684
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");
|
|
3685
4909
|
const ensureSessionWorkspace = (workspaceId, sessionId) => {
|
|
3686
4910
|
virtualWorkspaceMembership.ensure({
|
|
3687
4911
|
workspaceId,
|
|
@@ -3691,12 +4915,48 @@ window.__ModuleLoader__.load({
|
|
|
3691
4915
|
const syncSessionWorkspaces = (bindings) => {
|
|
3692
4916
|
virtualWorkspaceMembership.sync(bindings);
|
|
3693
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
|
+
}
|
|
3694
4951
|
const worktreeSessionConnector = createWorktreeSessionConnector({
|
|
3695
4952
|
manager,
|
|
3696
4953
|
sessions: ctx.sessions.list,
|
|
3697
4954
|
archivedSessionIds: () => ctx.workspaces.list.getSnapshot().archivedSessionIds ?? [],
|
|
3698
4955
|
createSession: async (input) => String(await sessions.create(input)),
|
|
3699
4956
|
ensureSessionWorkspace,
|
|
4957
|
+
permission: permissionManager,
|
|
4958
|
+
confirmFullAccess: permissionManager === void 0 ? void 0 : fullAccessConfirmation.request,
|
|
4959
|
+
onPermissionResult: reportPermissionNotice,
|
|
3700
4960
|
openSession: (sessionId) => {
|
|
3701
4961
|
ctx.sessions.open(sessionId);
|
|
3702
4962
|
}
|
|
@@ -3724,8 +4984,15 @@ window.__ModuleLoader__.load({
|
|
|
3724
4984
|
inject: () => ({
|
|
3725
4985
|
available: true,
|
|
3726
4986
|
expandState,
|
|
4987
|
+
sessionOrder,
|
|
3727
4988
|
hooks: { worktreeContext: contextProjection.store },
|
|
3728
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,
|
|
3729
4996
|
createWorkspace: async () => {
|
|
3730
4997
|
const workspacePath = await ctx.workspaces.pickDirectory();
|
|
3731
4998
|
if (workspacePath !== null) await ctx.workspaces.create({ path: workspacePath });
|
|
@@ -3759,6 +5026,7 @@ window.__ModuleLoader__.load({
|
|
|
3759
5026
|
if (!result.ok) throw new Error(result.error.message);
|
|
3760
5027
|
},
|
|
3761
5028
|
forkSession: (sessionId) => {
|
|
5029
|
+
if (forkableSessions.fork === void 0) return;
|
|
3762
5030
|
ctx.sessions.fork({
|
|
3763
5031
|
sessionId,
|
|
3764
5032
|
increaseTitle: true
|
|
@@ -3769,6 +5037,10 @@ window.__ModuleLoader__.load({
|
|
|
3769
5037
|
archiveSession: (sessionId) => ctx.workspaces.archiveSession(sessionId),
|
|
3770
5038
|
ensureSessionWorkspace,
|
|
3771
5039
|
syncSessionWorkspaces,
|
|
5040
|
+
forkRecovery: forkCoordinator?.recovery,
|
|
5041
|
+
retryForkSession: forkCoordinator === void 0 ? void 0 : async (key) => {
|
|
5042
|
+
await forkCoordinator.retry(key);
|
|
5043
|
+
},
|
|
3772
5044
|
openSession: (sessionId) => {
|
|
3773
5045
|
ctx.sessions.open(sessionId);
|
|
3774
5046
|
}
|