@cerbur/clutch-dsh-worktree 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +20 -5
  2. package/lib/client/WorktreeContext.d.ts +15 -0
  3. package/lib/client/WorktreeContext.d.ts.map +1 -0
  4. package/lib/client/WorktreeContext.js +19 -0
  5. package/lib/client/WorktreeContext.js.map +1 -0
  6. package/lib/client/WorktreeHeroContext.d.ts +10 -0
  7. package/lib/client/WorktreeHeroContext.d.ts.map +1 -0
  8. package/lib/client/WorktreeHeroContext.js +128 -0
  9. package/lib/client/WorktreeHeroContext.js.map +1 -0
  10. package/lib/client/WorktreeOverlay.d.ts +6 -0
  11. package/lib/client/WorktreeOverlay.d.ts.map +1 -0
  12. package/lib/client/WorktreeOverlay.js +8 -0
  13. package/lib/client/WorktreeOverlay.js.map +1 -0
  14. package/lib/client/WorktreeSurface.d.ts +3 -1
  15. package/lib/client/WorktreeSurface.d.ts.map +1 -1
  16. package/lib/client/WorktreeSurface.js +200 -57
  17. package/lib/client/WorktreeSurface.js.map +1 -1
  18. package/lib/client/entry.d.ts +1 -1
  19. package/lib/client/entry.d.ts.map +1 -1
  20. package/lib/client/entry.js +41 -14
  21. package/lib/client/entry.js.map +1 -1
  22. package/lib/client/locales.d.ts +30 -0
  23. package/lib/client/locales.d.ts.map +1 -1
  24. package/lib/client/locales.js +32 -2
  25. package/lib/client/locales.js.map +1 -1
  26. package/lib/client/session-view.d.ts +21 -0
  27. package/lib/client/session-view.d.ts.map +1 -0
  28. package/lib/client/session-view.js +26 -0
  29. package/lib/client/session-view.js.map +1 -0
  30. package/lib/client/sidebar-overlay-geometry.d.ts +1 -0
  31. package/lib/client/sidebar-overlay-geometry.d.ts.map +1 -1
  32. package/lib/client/sidebar-overlay-geometry.js +28 -17
  33. package/lib/client/sidebar-overlay-geometry.js.map +1 -1
  34. package/lib/client/virtual-workspace-membership.d.ts.map +1 -1
  35. package/lib/client/virtual-workspace-membership.js +17 -6
  36. package/lib/client/virtual-workspace-membership.js.map +1 -1
  37. package/lib/client/worktree-connection.d.ts +1 -0
  38. package/lib/client/worktree-connection.d.ts.map +1 -1
  39. package/lib/client/worktree-connection.js +4 -0
  40. package/lib/client/worktree-connection.js.map +1 -1
  41. package/lib/client/worktree-context-store.d.ts +45 -0
  42. package/lib/client/worktree-context-store.d.ts.map +1 -0
  43. package/lib/client/worktree-context-store.js +199 -0
  44. package/lib/client/worktree-context-store.js.map +1 -0
  45. package/lib/client/worktree-context.d.ts +32 -0
  46. package/lib/client/worktree-context.d.ts.map +1 -0
  47. package/lib/client/worktree-context.js +51 -0
  48. package/lib/client/worktree-context.js.map +1 -0
  49. package/lib/client/worktree-error-copy.d.ts.map +1 -1
  50. package/lib/client/worktree-error-copy.js +14 -0
  51. package/lib/client/worktree-error-copy.js.map +1 -1
  52. package/lib/client/worktree-session.d.ts +83 -0
  53. package/lib/client/worktree-session.d.ts.map +1 -0
  54. package/lib/client/worktree-session.js +185 -0
  55. package/lib/client/worktree-session.js.map +1 -0
  56. package/lib/client/worktree-view.d.ts +37 -3
  57. package/lib/client/worktree-view.d.ts.map +1 -1
  58. package/lib/client/worktree-view.js +145 -7
  59. package/lib/client/worktree-view.js.map +1 -1
  60. package/lib/client.js +1003 -133
  61. package/lib/client.js.map +1 -1
  62. package/lib/contract/index.contract.js +2 -0
  63. package/lib/contract/index.contract.js.map +1 -1
  64. package/lib/contract/index.d.ts +17 -2
  65. package/lib/contract/index.d.ts.map +1 -1
  66. package/lib/contract/index.js +2 -0
  67. package/lib/contract/index.js.map +1 -1
  68. package/lib/host/remote.d.ts.map +1 -1
  69. package/lib/host/remote.js +1 -0
  70. package/lib/host/remote.js.map +1 -1
  71. package/lib/host/service.d.ts +6 -1
  72. package/lib/host/service.d.ts.map +1 -1
  73. package/lib/host/service.js +6 -0
  74. package/lib/host/service.js.map +1 -1
  75. package/lib/manage/manager.d.ts +6 -1
  76. package/lib/manage/manager.d.ts.map +1 -1
  77. package/lib/manage/manager.js +12 -1
  78. package/lib/manage/manager.js.map +1 -1
  79. package/lib/provider/sidecar.d.ts +2 -1
  80. package/lib/provider/sidecar.d.ts.map +1 -1
  81. package/lib/provider/sidecar.js +40 -0
  82. package/lib/provider/sidecar.js.map +1 -1
  83. package/lib/provider/types.d.ts +2 -1
  84. package/lib/provider/types.d.ts.map +1 -1
  85. package/lib/provider/types.js.map +1 -1
  86. package/lib/typert.host.js +60 -18
  87. package/lib/typert.remote-client.d.ts +3 -1
  88. package/lib/typert.remote-client.d.ts.map +1 -1
  89. package/lib/typert.remote-client.js +60 -18
  90. package/package.json +6 -2
package/lib/client.js CHANGED
@@ -4,10 +4,10 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
7
8
  let react_jsx_runtime = require("react/jsx-runtime");
8
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
10
  let react = require("react");
10
- let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
11
11
  //#region lib/client/locales.js
12
12
  const WORKTREE_NS = "worktree";
13
13
  const zh = {
@@ -15,6 +15,8 @@ window.__ModuleLoader__.load({
15
15
  "mode.exit": "退出 Worktree 模式",
16
16
  "mode.label": "Worktree",
17
17
  "mode.navigation": "Worktree 导航",
18
+ "context.main": "当前分支:{name}",
19
+ "context.worktree": "Worktree:{name}",
18
20
  "workspace.rename": "重命名",
19
21
  "workspace.delete": "删除",
20
22
  "workspace.expand": "展开 {name}",
@@ -30,7 +32,8 @@ window.__ModuleLoader__.load({
30
32
  "workspace.deleteTitle": "删除工作区",
31
33
  "workspace.deleteDescription": "删除“{name}”?这只会移除 DSH 工作区注册。目录、会话和 Git Worktree 会保留。",
32
34
  "worktree.title": "Worktree",
33
- "worktree.main": "Main",
35
+ "worktree.main": "本地",
36
+ "worktree.mainWithBranch": "本地({branch})",
34
37
  "worktree.detached": "已分离 Worktree",
35
38
  "worktree.repair": "Worktree 需要修复",
36
39
  "worktree.ready": "活动 Worktree 已就绪",
@@ -46,9 +49,14 @@ window.__ModuleLoader__.load({
46
49
  "worktree.baseBranch": "Worktree 基线分支",
47
50
  "worktree.noLocalBranch": "没有本地分支",
48
51
  "worktree.noBranches": "此工作区中没有找到本地分支。",
52
+ "worktree.setup.noRepository": "此 Workspace 不是 Git 仓库。请在 Workspace 目录执行以下命令:",
53
+ "worktree.setup.noInitialCommit": "此 Git 仓库还没有首次 commit。请先执行以下命令:",
54
+ "worktree.setup.noLocalBranch": "此 Git 仓库没有本地分支。请先创建一个本地分支:",
55
+ "worktree.setup.commands": "可复制的命令",
49
56
  "worktree.name": "Worktree 名称",
50
57
  "worktree.create": "创建 Worktree",
51
58
  "session.options": "会话选项:{name}",
59
+ "session.new": "新会话",
52
60
  "session.rename": "重命名会话",
53
61
  "session.fork": "Fork 会话",
54
62
  "session.archive": "归档会话",
@@ -73,6 +81,7 @@ window.__ModuleLoader__.load({
73
81
  "error.invalidResult": "Worktree endpoint {endpoint} 返回了无效结果,请重试请求。",
74
82
  "error.workspaceOrderingUnavailable": "工作区排序不可用,请重新连接后重试。",
75
83
  "error.sessionOrderingUnavailable": "会话排序不可用,请重新连接后重试。",
84
+ "error.worktreeOrderingUnavailable": "Worktree 排序不可用,请重新连接后重试。",
76
85
  "error.worktreeCreatedSessionUnavailable": "Worktree 已创建,但 Session 创建不可用,请重新连接后重试。",
77
86
  "error.sessionCreationUnavailable": "Session 创建不可用,请重新连接后重试。",
78
87
  "error.worktreeRecordMissing": "Worktree 创建未返回 Worktree 记录,请重试请求。",
@@ -80,6 +89,12 @@ window.__ModuleLoader__.load({
80
89
  "error.workspaceDeleteUnavailable": "工作区删除不可用,请重新连接后重试。",
81
90
  "error.sessionRenameUnavailable": "会话重命名不可用,请重新连接后重试。",
82
91
  "error.sessionBindingFailed": "Session {sessionId} 已创建,但 Worktree 绑定失败:{reason}",
92
+ "error.sessionAlreadyBound": "Session 已绑定到其他 Worktree,未创建新的 Session。",
93
+ "error.sessionListNotReady": "Session 列表尚未就绪,请重试。",
94
+ "error.sessionFactsIncomplete": "Session 信息尚未完整,请重试。",
95
+ "error.worktreeSessionRepairRequired": "当前 Worktree 的 Session 绑定需要修复,请重试。",
96
+ "error.worktreeSessionUnavailable": "当前 Worktree 不可用,请重试。",
97
+ "error.sessionArchived": "该 Session 已归档,无法重试绑定。",
83
98
  "branch.current": "(当前)",
84
99
  "branch.checkedOut": "(已检出)"
85
100
  };
@@ -88,6 +103,8 @@ window.__ModuleLoader__.load({
88
103
  "mode.exit": "Exit Worktree mode",
89
104
  "mode.label": "Worktree",
90
105
  "mode.navigation": "Worktree navigation",
106
+ "context.main": "Current branch: {name}",
107
+ "context.worktree": "Worktree: {name}",
91
108
  "workspace.rename": "Rename",
92
109
  "workspace.delete": "Delete",
93
110
  "workspace.expand": "Expand {name}",
@@ -103,7 +120,8 @@ window.__ModuleLoader__.load({
103
120
  "workspace.deleteTitle": "Delete Workspace",
104
121
  "workspace.deleteDescription": "Delete {name}? This removes only the DSH Workspace registration. The directory, Sessions, and Git Worktrees will be retained.",
105
122
  "worktree.title": "Worktrees",
106
- "worktree.main": "Main",
123
+ "worktree.main": "Local",
124
+ "worktree.mainWithBranch": "Local ({branch})",
107
125
  "worktree.detached": "Detached Worktree",
108
126
  "worktree.repair": "Worktree needs repair",
109
127
  "worktree.ready": "Active Worktree ready",
@@ -119,9 +137,14 @@ window.__ModuleLoader__.load({
119
137
  "worktree.baseBranch": "Worktree base branch",
120
138
  "worktree.noLocalBranch": "No local branch",
121
139
  "worktree.noBranches": "No local branches found in this Workspace.",
140
+ "worktree.setup.noRepository": "This Workspace is not a Git repository. Run these commands in the Workspace directory:",
141
+ "worktree.setup.noInitialCommit": "This Git repository has no initial commit. Run these commands first:",
142
+ "worktree.setup.noLocalBranch": "This Git repository has no local branch. Create one first:",
143
+ "worktree.setup.commands": "Copyable commands",
122
144
  "worktree.name": "Worktree name",
123
145
  "worktree.create": "Create Worktree",
124
146
  "session.options": "Session options for {name}",
147
+ "session.new": "New Session",
125
148
  "session.rename": "Rename session",
126
149
  "session.fork": "Fork session",
127
150
  "session.archive": "Archive session",
@@ -146,6 +169,7 @@ window.__ModuleLoader__.load({
146
169
  "error.invalidResult": "Worktree endpoint {endpoint} returned an invalid result; retry the request.",
147
170
  "error.workspaceOrderingUnavailable": "Workspace ordering is unavailable; retry after reconnecting.",
148
171
  "error.sessionOrderingUnavailable": "Session ordering is unavailable; retry after reconnecting.",
172
+ "error.worktreeOrderingUnavailable": "Worktree ordering is unavailable; retry after reconnecting.",
149
173
  "error.worktreeCreatedSessionUnavailable": "Worktree created, but Session creation is unavailable; retry after reconnecting.",
150
174
  "error.sessionCreationUnavailable": "Session creation is unavailable; retry after reconnecting.",
151
175
  "error.worktreeRecordMissing": "Worktree creation returned no Worktree record; retry the request.",
@@ -153,6 +177,12 @@ window.__ModuleLoader__.load({
153
177
  "error.workspaceDeleteUnavailable": "Workspace delete is unavailable; retry after reconnecting.",
154
178
  "error.sessionRenameUnavailable": "Session rename is unavailable; retry after reconnecting.",
155
179
  "error.sessionBindingFailed": "Session {sessionId} was created, but Worktree binding failed: {reason}",
180
+ "error.sessionAlreadyBound": "The Session is already bound to another Worktree; no new Session was created.",
181
+ "error.sessionListNotReady": "The Session list is not ready yet; retry the request.",
182
+ "error.sessionFactsIncomplete": "Session facts are incomplete; retry the request.",
183
+ "error.worktreeSessionRepairRequired": "The current Worktree Session binding needs repair; retry the request.",
184
+ "error.worktreeSessionUnavailable": "The current Worktree is unavailable; retry the request.",
185
+ "error.sessionArchived": "This Session is archived and cannot be retried for binding.",
156
186
  "branch.current": " (current)",
157
187
  "branch.checkedOut": " (checked out)"
158
188
  };
@@ -169,6 +199,7 @@ window.__ModuleLoader__.load({
169
199
  listBranches: "worktreeManager/listBranches",
170
200
  createWorktree: "worktreeManager/createWorktree",
171
201
  removeWorktree: "worktreeManager/removeWorktree",
202
+ insertWorktreeBefore: "worktreeManager/insertWorktreeBefore",
172
203
  listBindings: "worktreeManager/listBindings",
173
204
  bindSession: "worktreeManager/bindSession"
174
205
  });
@@ -275,6 +306,9 @@ window.__ModuleLoader__.load({
275
306
  async removeWorktree(input) {
276
307
  await invoke("removeWorktree", input);
277
308
  },
309
+ async insertWorktreeBefore(input) {
310
+ return invoke("insertWorktreeBefore", input);
311
+ },
278
312
  listBindings: (input) => invoke("listBindings", input),
279
313
  bindSession: (input) => invoke("bindSession", input),
280
314
  dispose() {
@@ -286,8 +320,53 @@ window.__ModuleLoader__.load({
286
320
  };
287
321
  }
288
322
  //#endregion
323
+ //#region \0clutch-dsh-worktree-css:/Users/yuancheng/Documents/Code/clutch-dsh/packages/clutch-dsh-worktree/src/client/worktree-context.css.mjs
324
+ 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-heroContext {\n position: absolute;\n z-index: 1;\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: none;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n";
325
+ const styleId$1 = "@cerbur/clutch-dsh-worktree/worktree-context.css";
326
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree-context.css\"]") === null) {
327
+ const tag = document.createElement("style");
328
+ tag.dataset.plugin = "@cerbur/clutch-dsh-worktree";
329
+ tag.dataset.pluginCss = styleId$1;
330
+ tag.textContent = css$1;
331
+ document.head.appendChild(tag);
332
+ }
333
+ var worktree_context_css_default = {
334
+ "headerContext": "-cerbur-clutch-dsh-worktree-headerContext",
335
+ "contextIcon": "-cerbur-clutch-dsh-worktree-contextIcon",
336
+ "contextLabel": "-cerbur-clutch-dsh-worktree-contextLabel",
337
+ "heroContext": "-cerbur-clutch-dsh-worktree-heroContext"
338
+ };
339
+ //#endregion
340
+ //#region lib/client/WorktreeContext.js
341
+ function WorktreeContextLabel({ value, t }) {
342
+ if (value.kind === "none") return null;
343
+ const ariaLabel = value.kind === "main" ? t("context.main", { name: value.label }) : t("context.worktree", { name: value.label });
344
+ return (0, react_jsx_runtime.jsxs)("span", {
345
+ className: worktree_context_css_default.headerContext,
346
+ title: value.label,
347
+ "aria-label": ariaLabel,
348
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {
349
+ size: 14,
350
+ className: worktree_context_css_default.contextIcon,
351
+ "aria-hidden": "true"
352
+ }), (0, react_jsx_runtime.jsx)("span", {
353
+ className: worktree_context_css_default.contextLabel,
354
+ children: value.label
355
+ })]
356
+ });
357
+ }
358
+ /** Render the current branch or active Worktree branch beside the Session title. */
359
+ function WorktreeHeaderContext({ sessionId, useWorktreeContext, t }) {
360
+ const state = useWorktreeContext((snapshot) => snapshot);
361
+ if (state.sessionId !== sessionId) return null;
362
+ return (0, react_jsx_runtime.jsx)(WorktreeContextLabel, {
363
+ value: state.value,
364
+ t
365
+ });
366
+ }
367
+ //#endregion
289
368
  //#region \0clutch-dsh-worktree-css:/Users/yuancheng/Documents/Code/clutch-dsh/packages/clutch-dsh-worktree/src/client/worktree.css.mjs
290
- 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 overflow: hidden;\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.-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-railContent {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-surface[data-collapsed='true'] .-cerbur-clutch-dsh-worktree-railContent {\n display: flex;\n flex: 1;\n align-items: flex-start;\n justify-content: center;\n padding-top: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-railButton,\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-railButton {\n width: 36px;\n height: 36px;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-railButton:hover,\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-actionLabel {\n display: grid;\n gap: 3px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\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-message[data-error='true'] {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktree,\n.-cerbur-clutch-dsh-worktree-session {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 6px 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree {\n cursor: default;\n}\n\n.-cerbur-clutch-dsh-worktree-session {\n cursor: pointer;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree:hover,\n.-cerbur-clutch-dsh-worktree-session:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeLabel,\n.-cerbur-clutch-dsh-worktree-sessionLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-status {\n flex: none;\n margin-left: auto;\n padding-left: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchRow {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchIcon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n outline: none;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n width: 100%;\n padding: 6px 8px;\n font-size: 13px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput:focus,\n.-cerbur-clutch-dsh-worktree-actionSelect:focus {\n border-color: #3b82f6;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 20px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceList {\n min-width: 0;\n min-height: 0;\n display: grid;\n gap: 4px;\n overflow-x: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceGroup {\n min-width: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow,\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n position: relative;\n display: flex;\n align-items: center;\n min-width: 0;\n min-height: 36px;\n box-sizing: border-box;\n border-radius: 8px;\n padding: 3px 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceDisclosure,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeDisclosure,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 28px;\n border: 0;\n border-radius: 6px;\n padding: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 17px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton > svg {\n display: block;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-worktreeIcon {\n flex: none;\n width: 24px;\n color: var(--dsw-alias-label-secondary);\n font-size: 20px;\n line-height: 1;\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeState {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 12px;\n margin-right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon {\n color: var(--dsw-alias-brand-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceTitle,\n.-cerbur-clutch-dsh-worktree-worktreeLabel {\n min-width: 0;\n overflow: hidden;\n flex: 1;\n padding: 0 6px;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-main-group='true'] .-cerbur-clutch-dsh-worktree-worktreeLabel {\n font-weight: 600;\n text-transform: uppercase;\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-treeChildren {\n margin-left: 24px;\n padding: 2px 0 5px 12px;\n border-left: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-groupLabel {\n padding: 6px 8px 3px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader {\n display: flex;\n align-items: center;\n min-height: 34px;\n padding-right: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader .-cerbur-clutch-dsh-worktree-groupLabel {\n flex: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeGroup {\n margin-top: 2px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n min-height: 34px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-disclosureButton {\n width: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent {\n display: flex;\n align-items: center;\n flex: 1;\n width: 0;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 4px 4px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent:hover {\n background: transparent;\n}\n\n.-cerbur-clutch-dsh-worktree-rowActions {\n display: none;\n flex: none;\n align-items: center;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton {\n display: block;\n width: calc(100% - 24px);\n margin: 2px 0 4px 24px;\n padding: 4px 8px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 12px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-rowActions {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-renameInput {\n width: 100%;\n}\n\n.-cerbur-clutch-dsh-worktree-renameError {\n margin: 8px 0 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeGuide {\n flex: none;\n width: 20px;\n color: var(--dsw-alias-label-tertiary);\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-emptyNested {\n margin: 0;\n padding: 4px 8px 5px 28px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-modalField {\n display: grid;\n gap: 5px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n";
369
+ 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 overflow: hidden;\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-railContent {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-surface[data-collapsed='true'] .-cerbur-clutch-dsh-worktree-railContent {\n display: flex;\n flex: 1;\n align-items: flex-start;\n justify-content: center;\n padding-top: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-railButton,\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-railButton {\n width: 36px;\n height: 36px;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-railButton:hover,\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-actionLabel {\n display: grid;\n gap: 3px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-actionSelect {\n min-width: 0;\n padding: 5px 6px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-section {\n margin: 12px 0 0;\n}\n\n.-cerbur-clutch-dsh-worktree-sectionTitle {\n margin: 0 2px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-empty,\n.-cerbur-clutch-dsh-worktree-message {\n margin: 0;\n padding: 8px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-gitReadiness {\n display: grid;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-commandBlock {\n margin: 6px 0 0;\n padding: 8px;\n overflow-x: auto;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-secondary);\n font: 11px/16px ui-monospace, SFMono-Regular, Menlo, monospace;\n white-space: pre-wrap;\n}\n\n.-cerbur-clutch-dsh-worktree-message[data-error='true'] {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktree,\n.-cerbur-clutch-dsh-worktree-session {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 6px 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree {\n cursor: default;\n}\n\n.-cerbur-clutch-dsh-worktree-session {\n cursor: pointer;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree:hover,\n.-cerbur-clutch-dsh-worktree-session:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeLabel,\n.-cerbur-clutch-dsh-worktree-sessionLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-status {\n flex: none;\n margin-left: auto;\n padding-left: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchRow {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchIcon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n outline: none;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n width: 100%;\n padding: 6px 8px;\n font-size: 13px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput:focus,\n.-cerbur-clutch-dsh-worktree-actionSelect:focus {\n border-color: #3b82f6;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 20px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceList {\n min-width: 0;\n min-height: 0;\n display: grid;\n gap: 4px;\n overflow-x: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceGroup {\n min-width: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow,\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n position: relative;\n display: flex;\n align-items: center;\n min-width: 0;\n min-height: 36px;\n box-sizing: border-box;\n border-radius: 8px;\n padding: 3px 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceDisclosure,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeDisclosure,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 28px;\n border: 0;\n border-radius: 6px;\n padding: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 17px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton > svg {\n display: block;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-worktreeIcon {\n flex: none;\n width: 24px;\n color: var(--dsw-alias-label-secondary);\n font-size: 20px;\n line-height: 1;\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeState {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 12px;\n margin-right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon {\n color: var(--dsw-alias-brand-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceTitle,\n.-cerbur-clutch-dsh-worktree-worktreeLabel {\n min-width: 0;\n overflow: hidden;\n flex: 1;\n padding: 0 6px;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-main-group='true'] .-cerbur-clutch-dsh-worktree-worktreeLabel {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeHoverTitle {\n color: var(--dsw-static-neutral-bluish-00);\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: anywhere;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot,\n.-cerbur-clutch-dsh-worktree-workspaceActions {\n position: relative;\n display: inline-flex;\n flex: 0 0 64px;\n align-items: center;\n justify-content: flex-end;\n width: 64px;\n height: 28px;\n margin-left: auto;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-menuAction {\n position: absolute;\n top: 0;\n right: 32px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n position: absolute;\n top: 0;\n right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-menuAction {\n display: inline-flex;\n width: 28px;\n height: 28px;\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeChildren {\n margin-left: 24px;\n padding: 2px 0 5px 12px;\n border-left: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-groupLabel {\n padding: 6px 8px 3px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader {\n display: flex;\n align-items: center;\n min-height: 34px;\n padding-right: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader .-cerbur-clutch-dsh-worktree-groupLabel {\n flex: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeGroup {\n margin-top: 2px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n min-height: 34px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-disclosureButton {\n width: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent {\n display: flex;\n align-items: center;\n flex: 1;\n width: 0;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 4px 4px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent:hover {\n background: transparent;\n}\n\n.-cerbur-clutch-dsh-worktree-rowActions {\n display: none;\n flex: none;\n align-items: center;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton {\n display: block;\n width: calc(100% - 24px);\n margin: 2px 0 4px 24px;\n padding: 4px 8px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 12px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-rowActions {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-renameInput {\n width: 100%;\n}\n\n.-cerbur-clutch-dsh-worktree-renameError {\n margin: 8px 0 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeGuide {\n flex: none;\n width: 20px;\n color: var(--dsw-alias-label-tertiary);\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-emptyNested {\n margin: 0;\n padding: 4px 8px 5px 28px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-modalField {\n display: grid;\n gap: 5px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n";
291
370
  const styleId = "@cerbur/clutch-dsh-worktree/worktree.css";
292
371
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree.css\"]") === null) {
293
372
  const tag = document.createElement("style");
@@ -320,6 +399,8 @@ window.__ModuleLoader__.load({
320
399
  "sectionTitle": "-cerbur-clutch-dsh-worktree-sectionTitle",
321
400
  "empty": "-cerbur-clutch-dsh-worktree-empty",
322
401
  "message": "-cerbur-clutch-dsh-worktree-message",
402
+ "gitReadiness": "-cerbur-clutch-dsh-worktree-gitReadiness",
403
+ "commandBlock": "-cerbur-clutch-dsh-worktree-commandBlock",
323
404
  "worktree": "-cerbur-clutch-dsh-worktree-worktree",
324
405
  "session": "-cerbur-clutch-dsh-worktree-session",
325
406
  "worktreeLabel": "-cerbur-clutch-dsh-worktree-worktreeLabel",
@@ -385,6 +466,104 @@ window.__ModuleLoader__.load({
385
466
  });
386
467
  }
387
468
  //#endregion
469
+ //#region lib/client/WorktreeHeroContext.js
470
+ function labelFor(state) {
471
+ if (state.status !== "ready" || state.workspaceTitle === void 0 || state.value.kind === "none") return;
472
+ return `${state.workspaceTitle} (${state.value.label})`;
473
+ }
474
+ function elementWithClassSuffix(root, suffix) {
475
+ const candidates = root.querySelectorAll("[class]");
476
+ for (const candidate of candidates) if ([...candidate.classList].some((token) => token === suffix || token.endsWith(`_${suffix}`))) return candidate;
477
+ return null;
478
+ }
479
+ function headlineElement(root) {
480
+ return elementWithClassSuffix(root, "previewBadge") ?? elementWithClassSuffix(root, "headlineText") ?? elementWithClassSuffix(root, "headline");
481
+ }
482
+ function positionedAncestor(element) {
483
+ if (element.offsetParent instanceof HTMLElement) return element.offsetParent;
484
+ for (let ancestor = element.parentElement; ancestor !== null; ancestor = ancestor.parentElement) if (getComputedStyle(ancestor).position !== "static") return ancestor;
485
+ return null;
486
+ }
487
+ function measurePlacement(element) {
488
+ const hero = document.querySelector("[data-phase=\"hero\"]");
489
+ const overlay = positionedAncestor(element);
490
+ const headline = hero === null ? null : headlineElement(hero);
491
+ if (hero === null || headline === null || overlay === null) return null;
492
+ const anchor = headline.getBoundingClientRect();
493
+ const frame = overlay.getBoundingClientRect();
494
+ const width = element.getBoundingClientRect().width;
495
+ const afterHeadlineLeft = anchor.right - frame.left + 8;
496
+ const fitsAfterHeadline = afterHeadlineLeft + width <= frame.width - 8;
497
+ return {
498
+ left: fitsAfterHeadline ? afterHeadlineLeft : Math.max(8, frame.width - width - 8),
499
+ top: fitsAfterHeadline ? Math.max(8, anchor.top - frame.top + (anchor.height - 22) / 2) : Math.max(8, anchor.bottom - frame.top + 8)
500
+ };
501
+ }
502
+ function contextAriaLabel(state, t) {
503
+ if (state.value.kind === "main") return t("context.main", { name: state.value.label });
504
+ if (state.value.kind === "worktree") return t("context.worktree", { name: state.value.label });
505
+ }
506
+ /**
507
+ * Browser-local Hero suffix. It intentionally uses the frame overlay because
508
+ * rc.8 exposes no additive slot beside the native Hero headline.
509
+ */
510
+ function WorktreeHeroContext({ useWorktreeContext, t }) {
511
+ const state = useWorktreeContext((snapshot) => snapshot);
512
+ const label = labelFor(state);
513
+ const [placement, setPlacement] = (0, react.useState)(null);
514
+ const measure = (0, react.useCallback)((element) => {
515
+ if (element === null) {
516
+ setPlacement(null);
517
+ return;
518
+ }
519
+ setPlacement(measurePlacement(element));
520
+ }, []);
521
+ (0, react.useLayoutEffect)(() => {
522
+ if (typeof document === "undefined" || typeof window === "undefined") return void 0;
523
+ const element = document.querySelector("[data-worktree-hero-context]");
524
+ if (element === null) return void 0;
525
+ let frameRequest;
526
+ const schedule = () => {
527
+ if (frameRequest !== void 0) window.cancelAnimationFrame(frameRequest);
528
+ frameRequest = window.requestAnimationFrame(() => {
529
+ frameRequest = void 0;
530
+ measure(element);
531
+ });
532
+ };
533
+ const mutationObserver = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(schedule);
534
+ mutationObserver?.observe(document.body, {
535
+ subtree: true,
536
+ childList: true,
537
+ attributes: true,
538
+ attributeFilter: ["class", "data-phase"]
539
+ });
540
+ const resizeObserver = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule);
541
+ resizeObserver?.observe(positionedAncestor(element) ?? element);
542
+ window.addEventListener("resize", schedule);
543
+ schedule();
544
+ return () => {
545
+ if (frameRequest !== void 0) window.cancelAnimationFrame(frameRequest);
546
+ mutationObserver?.disconnect();
547
+ resizeObserver?.disconnect();
548
+ window.removeEventListener("resize", schedule);
549
+ };
550
+ }, [label, measure]);
551
+ const ariaLabel = contextAriaLabel(state, t);
552
+ if (label === void 0) return null;
553
+ return (0, react_jsx_runtime.jsx)("span", {
554
+ ref: measure,
555
+ className: worktree_context_css_default.heroContext,
556
+ "data-worktree-hero-context": true,
557
+ "aria-label": ariaLabel,
558
+ title: label,
559
+ style: placement === null ? { visibility: "hidden" } : {
560
+ left: `${placement.left}px`,
561
+ top: `${placement.top}px`
562
+ },
563
+ children: label
564
+ });
565
+ }
566
+ //#endregion
388
567
  //#region lib/client/navigation.js
389
568
  /** Open an existing Session through DSH without touching the peer view mode. */
390
569
  function openWorktreeSession(opener, sessionId) {
@@ -419,6 +598,13 @@ window.__ModuleLoader__.load({
419
598
  bottom: rect.bottom
420
599
  };
421
600
  }
601
+ function syncObservedElement(observer, previous, next) {
602
+ const nextElement = next ?? void 0;
603
+ if (previous === nextElement) return previous;
604
+ if (previous !== void 0) observer?.unobserve(previous);
605
+ if (nextElement !== void 0) observer?.observe(nextElement);
606
+ return nextElement;
607
+ }
422
608
  function resolveNativeSidebarRoot(sidebar) {
423
609
  const directChild = sidebar.firstElementChild;
424
610
  if (directChild === null) return void 0;
@@ -460,34 +646,37 @@ window.__ModuleLoader__.load({
460
646
  setBounds(EMPTY_BOUNDS);
461
647
  return;
462
648
  }
649
+ const overlayElement = overlay;
650
+ const sidebarElement = sidebar;
651
+ const nativeRootElement = nativeRoot;
463
652
  let frameHandle;
464
- const update = () => {
465
- const newSession = findNewSessionAnchor(nativeRoot);
466
- const footer = nativeRoot.lastElementChild;
467
- const nextWidth = sidebar.getBoundingClientRect().width;
653
+ let observedNewSession;
654
+ let observedFooter;
655
+ function update() {
656
+ const newSession = findNewSessionAnchor(nativeRootElement);
657
+ const footer = nativeRootElement.lastElementChild;
658
+ observedNewSession = syncObservedElement(resizeObserver, observedNewSession, newSession);
659
+ observedFooter = syncObservedElement(resizeObserver, observedFooter, footer);
660
+ const nextWidth = sidebarElement.getBoundingClientRect().width;
468
661
  if (nextWidth > 0) setWidth(nextWidth);
469
- setBounds(computeOverlayBounds(asRect(overlay), newSession === void 0 ? void 0 : asRect(newSession), footer === null ? void 0 : asRect(footer)));
470
- };
471
- const scheduleUpdate = () => {
662
+ setBounds(computeOverlayBounds(asRect(overlayElement), newSession === void 0 ? void 0 : asRect(newSession), footer === null ? void 0 : asRect(footer)));
663
+ }
664
+ function scheduleUpdate() {
472
665
  if (frameHandle !== void 0) return;
473
666
  frameHandle = requestAnimationFrame(() => {
474
667
  frameHandle = void 0;
475
668
  update();
476
669
  });
477
- };
478
- update();
670
+ }
479
671
  const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(scheduleUpdate) : void 0;
480
672
  for (const element of [
481
- overlay,
482
- sidebar,
483
- nativeRoot
673
+ overlayElement,
674
+ sidebarElement,
675
+ nativeRootElement
484
676
  ]) resizeObserver?.observe(element);
485
- const newSession = findNewSessionAnchor(nativeRoot);
486
- const footer = nativeRoot.lastElementChild;
487
- if (newSession !== void 0) resizeObserver?.observe(newSession);
488
- if (footer !== null) resizeObserver?.observe(footer);
677
+ update();
489
678
  const mutationObserver = typeof MutationObserver === "function" ? new MutationObserver(scheduleUpdate) : void 0;
490
- mutationObserver?.observe(nativeRoot, {
679
+ mutationObserver?.observe(nativeRootElement, {
491
680
  childList: true,
492
681
  subtree: true
493
682
  });
@@ -576,6 +765,7 @@ window.__ModuleLoader__.load({
576
765
  case "WORKTREE_RPC_INVALID_RESULT": return t("error.invalidResult", { endpoint: detail(error, "endpoint") });
577
766
  case "WORKSPACE_ORDER_UNAVAILABLE": return t("error.workspaceOrderingUnavailable");
578
767
  case "SESSION_ORDER_UNAVAILABLE": return t("error.sessionOrderingUnavailable");
768
+ case "WORKTREE_ORDER_UNAVAILABLE": return t("error.worktreeOrderingUnavailable");
579
769
  case "SESSION_CREATE_UNAVAILABLE": return t("error.sessionCreationUnavailable");
580
770
  case "WORKTREE_CREATED_SESSION_UNAVAILABLE": return t("error.worktreeCreatedSessionUnavailable");
581
771
  case "WORKTREE_RECORD_MISSING": return t("error.worktreeRecordMissing");
@@ -586,21 +776,58 @@ window.__ModuleLoader__.load({
586
776
  sessionId: detail(error, "sessionId"),
587
777
  reason: reason(error, t)
588
778
  });
779
+ case "SESSION_ALREADY_BOUND": return t("error.sessionAlreadyBound");
780
+ case "SESSION_LIST_NOT_READY": return t("error.sessionListNotReady");
781
+ case "SESSION_FACTS_INCOMPLETE": return t("error.sessionFactsIncomplete");
782
+ case "WORKTREE_SESSION_REPAIR_REQUIRED": return t("error.worktreeSessionRepairRequired");
783
+ case "WORKTREE_SESSION_UNAVAILABLE": return t("error.worktreeSessionUnavailable");
784
+ case "SESSION_ARCHIVED": return t("error.sessionArchived");
589
785
  default: return error.message.length > 0 ? error.message : t("error.worktreeDataUnavailable");
590
786
  }
591
787
  }
592
788
  //#endregion
789
+ //#region lib/client/session-view.js
790
+ /** Read the native blank flag without inferring blankness from title or id. */
791
+ function isBlankSession(sessionId, sessions) {
792
+ return sessions.byId[sessionId]?.blank === true;
793
+ }
794
+ /** Native tree visibility: ordinary rows plus the currently selected blank row. */
795
+ function isVisibleSession(sessionId, sessions) {
796
+ return !isBlankSession(sessionId, sessions) || sessions.current === sessionId;
797
+ }
798
+ /** Filter a group without mutating its source order. */
799
+ function filterVisibleSessionIds(sessionIds, sessions) {
800
+ return sessionIds.filter((sessionId) => isVisibleSession(sessionId, sessions));
801
+ }
802
+ /** Resolve a row title while keeping the blank label outside the DSH summary. */
803
+ function sessionDisplayLabel(sessionId, sessions, blankLabel) {
804
+ const summary = sessions.byId[sessionId];
805
+ return summary?.blank === true ? blankLabel : summary?.displayTitle ?? sessionId;
806
+ }
807
+ /** Match only durable Session titles or the final diagnostic id fallback. */
808
+ function sessionMatchesQuery(sessionId, sessions, normalizedQuery) {
809
+ const summary = sessions.byId[sessionId];
810
+ if (summary?.blank === true) return false;
811
+ return (summary?.displayTitle ?? sessionId).toLocaleLowerCase().includes(normalizedQuery);
812
+ }
813
+ //#endregion
593
814
  //#region lib/client/worktree-view.js
594
815
  /** A DSH Session exists even when the external Worktree binding needs repair. */
595
816
  var WorktreeSessionBindingError = class extends Error {
596
- code = "SESSION_BINDING_FAILED";
597
- retryable = true;
817
+ code;
818
+ retryable;
819
+ details;
598
820
  sessionId;
599
821
  cause;
600
822
  constructor(sessionId, cause) {
601
- const reason = cause instanceof Error ? cause.message : String(cause);
823
+ const candidate = typeof cause === "object" && cause !== null ? cause : void 0;
824
+ const reason = cause instanceof Error ? cause.message : typeof candidate?.message === "string" ? candidate.message : String(cause);
602
825
  super(reason);
603
826
  this.name = "WorktreeSessionBindingError";
827
+ const conflict = candidate?.code === "SESSION_ALREADY_BOUND";
828
+ this.code = conflict ? "SESSION_ALREADY_BOUND" : "SESSION_BINDING_FAILED";
829
+ this.retryable = conflict ? false : candidate?.retryable !== false;
830
+ this.details = typeof candidate?.details === "object" && candidate.details !== null && !Array.isArray(candidate.details) ? candidate.details : {};
604
831
  this.sessionId = sessionId;
605
832
  this.cause = cause;
606
833
  }
@@ -611,10 +838,63 @@ window.__ModuleLoader__.load({
611
838
  for (let index = 0; index < previous.length; index += 1) if (previous[index] !== next[index]) return next;
612
839
  return previous;
613
840
  }
841
+ /** Keep only the most recently started Worktree surface refresh authoritative. */
842
+ function createWorktreeRefreshGuard() {
843
+ let latestGeneration = 0;
844
+ const begin = () => {
845
+ latestGeneration += 1;
846
+ return latestGeneration;
847
+ };
848
+ const isCurrent = (generation) => generation === latestGeneration;
849
+ const invalidate = () => {
850
+ latestGeneration += 1;
851
+ };
852
+ return {
853
+ begin,
854
+ isCurrent,
855
+ invalidate,
856
+ async run(load, onSuccess, onError) {
857
+ const generation = begin();
858
+ let value;
859
+ try {
860
+ value = await load();
861
+ } catch (error) {
862
+ if (!isCurrent(generation)) return;
863
+ onError(error);
864
+ return;
865
+ }
866
+ if (!isCurrent(generation)) return;
867
+ onSuccess(value);
868
+ }
869
+ };
870
+ }
614
871
  /** Prefer the branch checked out by the DSH Workspace, then fall back to any local branch. */
615
872
  function selectDefaultBaseBranch(branches) {
616
873
  return branches.find((branch) => branch.isCurrent)?.name ?? branches[0]?.name ?? "";
617
874
  }
875
+ /** Keep a valid user choice across branch-list refreshes, otherwise choose the current branch. */
876
+ function reconcileBaseBranchSelection(selectedBranch, branches) {
877
+ if (branches.some((branch) => branch.name === selectedBranch)) return selectedBranch;
878
+ return selectDefaultBaseBranch(branches);
879
+ }
880
+ /** Return copyable, read-only setup commands for a Git readiness state. */
881
+ function worktreeSetupCommands(status) {
882
+ switch (status) {
883
+ case "noRepository": return [
884
+ "git init",
885
+ "printf \"# README\\n\" > README.md",
886
+ "git add README.md",
887
+ "git commit -m \"Initial commit\""
888
+ ];
889
+ case "noInitialCommit": return [
890
+ "printf \"# README\\n\" > README.md",
891
+ "git add README.md",
892
+ "git commit -m \"Initial commit\""
893
+ ];
894
+ case "noLocalBranch": return ["git switch -c main"];
895
+ case "ready": return [];
896
+ }
897
+ }
618
898
  function browserRandomUuid() {
619
899
  const randomUUID = globalThis.crypto?.randomUUID;
620
900
  if (typeof randomUUID === "function") return randomUUID.call(globalThis.crypto);
@@ -635,25 +915,62 @@ window.__ModuleLoader__.load({
635
915
  function filterArchivedSessionIds(sessionIds, archivedSessionIds) {
636
916
  return sessionIds.filter((sessionId) => !archivedSessionIds.includes(sessionId));
637
917
  }
918
+ function readinessFromBranchError(error) {
919
+ const viewError = toWorktreeViewError(error);
920
+ if (viewError.code === "WORKSPACE_NOT_GIT_REPOSITORY") return {
921
+ status: "noRepository",
922
+ error: viewError
923
+ };
924
+ if (viewError.code === "WORKTREE_REQUIRES_INITIAL_COMMIT") return {
925
+ status: "noInitialCommit",
926
+ error: viewError
927
+ };
928
+ }
638
929
  /** Read all three Worktree projections needed by the surface in one refresh. */
639
930
  async function loadWorktreeView(manager, workspaceId) {
640
- const [worktrees, branches, bindings] = await Promise.all([
931
+ const [worktreesResult, branchesResult, bindingsResult] = await Promise.allSettled([
641
932
  manager.listWorktrees({ workspaceId }),
642
933
  manager.listBranches({ workspaceId }),
643
934
  manager.listBindings({ workspaceId })
644
935
  ]);
936
+ if (worktreesResult.status === "rejected") throw worktreesResult.reason;
937
+ if (bindingsResult.status === "rejected") throw bindingsResult.reason;
938
+ if (branchesResult.status === "rejected") {
939
+ const readiness = readinessFromBranchError(branchesResult.reason);
940
+ if (readiness !== void 0) return {
941
+ worktrees: worktreesResult.value,
942
+ branches: [],
943
+ bindings: bindingsResult.value,
944
+ readiness
945
+ };
946
+ throw branchesResult.reason;
947
+ }
645
948
  return {
646
- worktrees,
647
- branches,
648
- bindings
949
+ worktrees: worktreesResult.value,
950
+ branches: branchesResult.value,
951
+ bindings: bindingsResult.value,
952
+ readiness: branchesResult.value.length > 0 ? { status: "ready" } : { status: "noLocalBranch" }
649
953
  };
650
954
  }
651
955
  /** Load one independent projection per DSH Workspace for the flat sidebar hierarchy. */
652
- async function loadWorktreeViews(manager, workspaceIds) {
653
- return Promise.all(workspaceIds.map(async (workspaceId) => ({
956
+ async function loadWorktreeViews(manager, workspaceIds, options = {}) {
957
+ const views = await Promise.all(workspaceIds.map(async (workspaceId) => ({
654
958
  workspaceId,
655
959
  ...await loadWorktreeView(manager, workspaceId)
656
960
  })));
961
+ if (options.invalidateContext !== false) await options.invalidateWorktreeContext?.();
962
+ return views;
963
+ }
964
+ /** Resolve a row-half drop target to native-style optional before-anchor semantics. */
965
+ function resolveWorktreeMove(worktreeIds, sourceWorktreeId, targetWorktreeId, half) {
966
+ const sourceIndex = worktreeIds.indexOf(sourceWorktreeId);
967
+ const targetIndex = worktreeIds.indexOf(targetWorktreeId);
968
+ if (sourceIndex === -1 || targetIndex === -1) return void 0;
969
+ const beforeWorktreeId = half === "before" ? targetWorktreeId : worktreeIds[targetIndex + 1];
970
+ if (beforeWorktreeId === sourceWorktreeId) return void 0;
971
+ const anchorIndex = beforeWorktreeId === void 0 ? worktreeIds.length : worktreeIds.indexOf(beforeWorktreeId);
972
+ if (anchorIndex === sourceIndex || anchorIndex === sourceIndex + 1) return void 0;
973
+ return beforeWorktreeId === void 0 ? {} : { beforeWorktreeId };
657
974
  }
658
975
  /** Keep mutation routing in the browser Consumer while leaving wire details to the adapter. */
659
976
  async function executeWorktreeAction(manager, action) {
@@ -709,8 +1026,174 @@ window.__ModuleLoader__.load({
709
1026
  retryable: true
710
1027
  };
711
1028
  }
1029
+ /** Keep ordering mutation/refresh failures reachable from the surface Retry control. */
1030
+ function toRetryableWorktreeOrderError(error) {
1031
+ return {
1032
+ ...toWorktreeViewError(error),
1033
+ retryable: true
1034
+ };
1035
+ }
1036
+ //#endregion
1037
+ //#region lib/client/worktree-session.js
1038
+ var WorktreeSessionActionError = class extends Error {
1039
+ code;
1040
+ retryable;
1041
+ details;
1042
+ constructor(code, message, retryable, details = {}) {
1043
+ super(message);
1044
+ this.code = code;
1045
+ this.retryable = retryable;
1046
+ this.name = "WorktreeSessionActionError";
1047
+ this.details = details;
1048
+ }
1049
+ };
1050
+ function resolveWorktreeSessionAction(input) {
1051
+ if (input.target.status !== "active" || input.target.health === "repair") return {
1052
+ kind: "reject",
1053
+ reason: "worktree-not-available"
1054
+ };
1055
+ if (input.sessions.phase !== "ready") return {
1056
+ kind: "wait",
1057
+ reason: "sessions-not-ready"
1058
+ };
1059
+ const targetBinding = input.bindings.find((binding) => binding.worktreeId === input.target.worktreeId && binding.status === "active");
1060
+ if (targetBinding !== void 0) {
1061
+ const summary = input.sessions.byId[targetBinding.sessionId];
1062
+ if (summary === void 0) return {
1063
+ kind: "repair",
1064
+ reason: "active-binding-session-missing",
1065
+ sessionId: targetBinding.sessionId
1066
+ };
1067
+ if (summary.cwd !== input.target.absolutePath) return {
1068
+ kind: "repair",
1069
+ reason: "active-binding-cwd-mismatch",
1070
+ sessionId: targetBinding.sessionId
1071
+ };
1072
+ if (summary.blank === true && !input.archivedSessionIds.includes(targetBinding.sessionId)) return {
1073
+ kind: "open-bound",
1074
+ sessionId: targetBinding.sessionId
1075
+ };
1076
+ }
1077
+ for (const sessionId of input.sessions.ids) {
1078
+ const summary = input.sessions.byId[sessionId];
1079
+ if (summary === void 0 || summary.blank === void 0 || summary.blank === true && summary.cwd === void 0) return {
1080
+ kind: "wait",
1081
+ reason: "session-facts-incomplete"
1082
+ };
1083
+ }
1084
+ for (const sessionId of input.sessions.ids) {
1085
+ const summary = input.sessions.byId[sessionId];
1086
+ if (summary?.blank !== true || summary.cwd !== input.target.absolutePath || input.archivedSessionIds.includes(sessionId)) continue;
1087
+ if (!input.bindings.some((binding) => binding.sessionId === sessionId)) return {
1088
+ kind: "bind-existing",
1089
+ sessionId
1090
+ };
1091
+ }
1092
+ return { kind: "create" };
1093
+ }
1094
+ function actionError(action) {
1095
+ switch (action.kind) {
1096
+ case "wait": return new WorktreeSessionActionError(action.reason === "sessions-not-ready" ? "SESSION_LIST_NOT_READY" : "SESSION_FACTS_INCOMPLETE", "", true);
1097
+ case "repair": return new WorktreeSessionActionError("WORKTREE_SESSION_REPAIR_REQUIRED", action.reason, true, { ...action.sessionId === void 0 ? {} : { sessionId: action.sessionId } });
1098
+ case "reject": return new WorktreeSessionActionError("WORKTREE_SESSION_UNAVAILABLE", action.reason, false);
1099
+ default: return new WorktreeSessionActionError("WORKTREE_SESSION_ACTION_FAILED", "", true);
1100
+ }
1101
+ }
1102
+ async function retryWorktreeSessionBinding(input) {
1103
+ if (input.archived) throw new WorktreeSessionActionError("SESSION_ARCHIVED", "", false, { sessionId: input.pending.sessionId });
1104
+ try {
1105
+ await input.manager.bindSession({
1106
+ workspaceId: input.pending.workspaceId,
1107
+ worktreeId: input.pending.worktreeId,
1108
+ sessionId: input.pending.sessionId
1109
+ });
1110
+ } catch (error) {
1111
+ throw new WorktreeSessionBindingError(input.pending.sessionId, error);
1112
+ }
1113
+ input.ensureSessionWorkspace(input.pending.workspaceId, input.pending.sessionId);
1114
+ input.openSession(input.pending.sessionId);
1115
+ return input.pending.sessionId;
1116
+ }
1117
+ /**
1118
+ * Browser-only Worktree `+` connector. It owns the read/resolve/bind/create/open
1119
+ * sequence and coalesces repeated calls for one Worktree within one Client fiber.
1120
+ */
1121
+ function createWorktreeSessionConnector(options) {
1122
+ const inFlight = /* @__PURE__ */ new Map();
1123
+ let disposed = false;
1124
+ const run = async (input) => {
1125
+ const [worktrees, bindings] = await Promise.all([options.manager.listWorktrees({ workspaceId: input.workspaceId }), options.manager.listBindings({ workspaceId: input.workspaceId })]);
1126
+ if (disposed) throw new WorktreeSessionActionError("CLIENT_DISPOSED", "", false);
1127
+ const target = worktrees.find((record) => record.worktreeId === input.worktreeId);
1128
+ if (target === void 0 || target.absolutePath !== input.cwd) throw new WorktreeSessionActionError("WORKTREE_SESSION_REPAIR_REQUIRED", "worktree target unavailable", true);
1129
+ const action = resolveWorktreeSessionAction({
1130
+ target,
1131
+ sessions: options.sessions.getSnapshot(),
1132
+ archivedSessionIds: options.archivedSessionIds(),
1133
+ bindings
1134
+ });
1135
+ if (action.kind === "open-bound") {
1136
+ options.ensureSessionWorkspace(input.workspaceId, action.sessionId);
1137
+ if (!disposed) options.openSession(action.sessionId);
1138
+ return action.sessionId;
1139
+ }
1140
+ if (action.kind === "bind-existing") {
1141
+ try {
1142
+ await options.manager.bindSession({
1143
+ workspaceId: input.workspaceId,
1144
+ worktreeId: input.worktreeId,
1145
+ sessionId: action.sessionId
1146
+ });
1147
+ } catch (error) {
1148
+ throw new WorktreeSessionBindingError(action.sessionId, error);
1149
+ }
1150
+ if (!disposed) {
1151
+ options.ensureSessionWorkspace(input.workspaceId, action.sessionId);
1152
+ if (!disposed) options.openSession(action.sessionId);
1153
+ }
1154
+ return action.sessionId;
1155
+ }
1156
+ if (action.kind !== "create") throw actionError(action);
1157
+ if (disposed) throw new WorktreeSessionActionError("CLIENT_DISPOSED", "", false);
1158
+ return createSessionForWorktree({
1159
+ ...input,
1160
+ createSession: options.createSession,
1161
+ manager: options.manager,
1162
+ beforeOpen: (sessionId) => {
1163
+ if (disposed) throw new WorktreeSessionActionError("CLIENT_DISPOSED", "", false);
1164
+ options.ensureSessionWorkspace(input.workspaceId, sessionId);
1165
+ },
1166
+ openSession: (sessionId) => {
1167
+ if (!disposed) options.openSession(sessionId);
1168
+ }
1169
+ });
1170
+ };
1171
+ return {
1172
+ create(input) {
1173
+ const key = `${input.workspaceId}\u0000${input.worktreeId}`;
1174
+ const current = inFlight.get(key);
1175
+ if (current !== void 0) return current;
1176
+ const promise = run(input).finally(() => {
1177
+ if (inFlight.get(key) === promise) inFlight.delete(key);
1178
+ });
1179
+ inFlight.set(key, promise);
1180
+ return promise;
1181
+ },
1182
+ dispose() {
1183
+ disposed = true;
1184
+ inFlight.clear();
1185
+ }
1186
+ };
1187
+ }
712
1188
  //#endregion
713
1189
  //#region lib/client/WorktreeSurface.js
1190
+ function worktreeSetupMessage(status, t) {
1191
+ switch (status) {
1192
+ case "noRepository": return t("worktree.setup.noRepository");
1193
+ case "noInitialCommit": return t("worktree.setup.noInitialCommit");
1194
+ case "noLocalBranch": return t("worktree.setup.noLocalBranch");
1195
+ }
1196
+ }
714
1197
  function toNativeWorktreeViewError(error) {
715
1198
  const viewError = toWorktreeViewError(error);
716
1199
  if (typeof error === "object" && error !== null) return viewError;
@@ -730,8 +1213,8 @@ window.__ModuleLoader__.load({
730
1213
  if (stable !== previousRef.current) previousRef.current = stable;
731
1214
  return previousRef.current;
732
1215
  }
733
- function sessionLabel(sessionId, sessions) {
734
- return sessions.byId[sessionId]?.displayTitle ?? sessionId;
1216
+ function sessionLabel(sessionId, sessions, t) {
1217
+ return sessionDisplayLabel(sessionId, sessions, t("session.new"));
735
1218
  }
736
1219
  function rowHalf(event) {
737
1220
  const rect = event.currentTarget.getBoundingClientRect();
@@ -746,10 +1229,10 @@ window.__ModuleLoader__.load({
746
1229
  function workspaceMatches(workspace, view, sessions, query) {
747
1230
  if (query.length === 0) return true;
748
1231
  if (includesText(workspace.title, query)) return true;
749
- if (workspace.sessionIds.some((sessionId) => includesText(sessionLabel(sessionId, sessions), query))) return true;
1232
+ if (workspace.sessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) return true;
750
1233
  if (view === void 0) return false;
751
1234
  if (view.worktrees.some((record) => includesText(record.branch, query) || includesText(record.absolutePath, query))) return true;
752
- return view.bindings.some((binding) => includesText(sessionLabel(binding.sessionId, sessions), query));
1235
+ return view.bindings.some((binding) => sessionMatchesQuery(binding.sessionId, sessions, query));
753
1236
  }
754
1237
  /** Workspace row using the native DSH menu, fixed action column, and drag contract. */
755
1238
  function WorktreeWorkspaceRow({ t, workspace, expanded, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange }) {
@@ -857,13 +1340,36 @@ window.__ModuleLoader__.load({
857
1340
  });
858
1341
  }
859
1342
  /** Shared Main/Worktree group row with parameterized icon, state, and actions. */
860
- function WorktreeGroupRow({ t, kind, label, expanded, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession, menu }) {
1343
+ function WorktreeGroupRow({ t, kind, label, worktreeId, expanded, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession, menu, drag }) {
861
1344
  const main = kind === "main";
1345
+ const markerClass = drag?.marker === "before" ? worktree_css_default.dropBefore : drag?.marker === "after" ? worktree_css_default.dropAfter : "";
1346
+ const dragProps = drag === void 0 ? {} : {
1347
+ draggable: true,
1348
+ onDragStart: (event) => {
1349
+ event.dataTransfer.effectAllowed = "move";
1350
+ event.dataTransfer.setData("text/plain", worktreeId ?? label);
1351
+ drag.start();
1352
+ },
1353
+ onDragEnd: drag.end,
1354
+ onDragOver: (event) => {
1355
+ if (!drag.active) return;
1356
+ event.preventDefault();
1357
+ event.dataTransfer.dropEffect = "move";
1358
+ drag.hover(rowHalf(event));
1359
+ },
1360
+ onDrop: (event) => {
1361
+ if (!drag.active) return;
1362
+ event.preventDefault();
1363
+ drag.drop(rowHalf(event));
1364
+ }
1365
+ };
862
1366
  const row = (0, react_jsx_runtime.jsxs)("div", {
863
- className: worktree_css_default.worktreeRow,
1367
+ className: `${worktree_css_default.worktreeRow} ${markerClass}`,
864
1368
  "data-main-group": main ? "true" : void 0,
865
1369
  "data-main-expanded": main ? String(expanded) : void 0,
866
1370
  "data-menu-open": menu?.open ? "true" : void 0,
1371
+ "data-worktree-drag": drag?.active ? "active" : void 0,
1372
+ ...dragProps,
867
1373
  onClick: onToggle,
868
1374
  children: [
869
1375
  (0, react_jsx_runtime.jsx)("button", {
@@ -954,7 +1460,7 @@ window.__ModuleLoader__.load({
954
1460
  });
955
1461
  }
956
1462
  /** Worktree-mode Session row with the same trailing options menu as native DSH rows. */
957
- function WorktreeSessionRow({ t, sessionId, label, drag, actionPending, onOpen, onRename, onFork, onArchive }) {
1463
+ function WorktreeSessionRow({ t, sessionId, blank, label, drag, actionPending, onOpen, onRename, onFork, onArchive }) {
958
1464
  const [menuOpen, setMenuOpen] = (0, react.useState)(false);
959
1465
  const markerClass = drag.marker === "before" ? worktree_css_default.dropBefore : drag.marker === "after" ? worktree_css_default.dropAfter : "";
960
1466
  const sessionMenuItems = [
@@ -980,6 +1486,7 @@ window.__ModuleLoader__.load({
980
1486
  return (0, react_jsx_runtime.jsxs)("div", {
981
1487
  className: `${worktree_css_default.treeSessionRow} ${markerClass}`,
982
1488
  "data-session-id": sessionId,
1489
+ "data-session-blank": blank ? "true" : void 0,
983
1490
  "data-session-drag": drag.active ? "active" : void 0,
984
1491
  "data-menu-open": menuOpen || void 0,
985
1492
  role: "treeitem",
@@ -1013,7 +1520,7 @@ window.__ModuleLoader__.load({
1013
1520
  className: worktree_css_default.sessionLabel,
1014
1521
  children: label
1015
1522
  })]
1016
- }), (0, react_jsx_runtime.jsx)("span", {
1523
+ }), !blank && (0, react_jsx_runtime.jsx)("span", {
1017
1524
  className: worktree_css_default.rowActions,
1018
1525
  children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1019
1526
  open: menuOpen,
@@ -1050,7 +1557,8 @@ window.__ModuleLoader__.load({
1050
1557
  return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [visibleSessionIds.map((sessionId) => (0, react_jsx_runtime.jsx)(WorktreeSessionRow, {
1051
1558
  t,
1052
1559
  sessionId,
1053
- label: sessionLabel(sessionId, sessions),
1560
+ blank: isBlankSession(sessionId, sessions),
1561
+ label: sessionLabel(sessionId, sessions, t),
1054
1562
  drag: {
1055
1563
  active: sameGroupDrag,
1056
1564
  marker: sameGroupDrag && dragState.over?.sessionId === sessionId ? dragState.over.half : null,
@@ -1092,12 +1600,14 @@ window.__ModuleLoader__.load({
1092
1600
  * Peer Worktree navigation surface. It keeps the original DSH Workspace mode
1093
1601
  * intact and renders a browser-local Workspace → Worktree → Session projection.
1094
1602
  */
1095
- function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, manager, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession }) {
1603
+ function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, manager, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession }) {
1096
1604
  const mode = effectiveViewMode(useStore((state) => state.viewMode), available && manager !== void 0);
1097
1605
  const sessions = useSessions((state) => state);
1098
1606
  const workspaces = useWorkspaces((state) => state);
1099
1607
  const workspaceIds = useStableWorkspaceIds(workspaces.items);
1100
1608
  const [readState, setReadState] = (0, react.useState)(EMPTY_READ_STATE);
1609
+ const readStateRef = (0, react.useRef)(readState);
1610
+ readStateRef.current = readState;
1101
1611
  const [searchQuery, setSearchQuery] = (0, react.useState)("");
1102
1612
  const [expandedWorkspaces, setExpandedWorkspaces] = (0, react.useState)({});
1103
1613
  const [expandedMains, setExpandedMains] = (0, react.useState)({});
@@ -1127,36 +1637,52 @@ window.__ModuleLoader__.load({
1127
1637
  const [sessionDrag, setSessionDrag] = (0, react.useState)();
1128
1638
  const [expandedSessionGroups, setExpandedSessionGroups] = (0, react.useState)({});
1129
1639
  const sessionDropCommitted = (0, react.useRef)(false);
1640
+ const [worktreeDrag, setWorktreeDrag] = (0, react.useState)();
1641
+ const worktreeDropCommitted = (0, react.useRef)(false);
1642
+ const refreshGuard = (0, react.useRef)(createWorktreeRefreshGuard());
1130
1643
  const { ref, width, bounds } = useSidebarOverlayGeometry(mode === "worktree");
1131
1644
  const collapsed = width <= 64;
1132
1645
  const query = searchQuery.trim().toLocaleLowerCase();
1133
1646
  const archivedSessionIds = workspaces.archivedSessionIds ?? [];
1134
- const refresh = (0, react.useCallback)(async () => {
1647
+ const pendingSessionArchived = pendingSessionBinding !== void 0 && archivedSessionIds.includes(pendingSessionBinding.sessionId);
1648
+ const refresh = (0, react.useCallback)(async (options = {}) => {
1135
1649
  if (manager === void 0) {
1650
+ refreshGuard.current.invalidate();
1136
1651
  setReadState(EMPTY_READ_STATE);
1137
1652
  return;
1138
1653
  }
1139
- setReadState({
1654
+ const preserveCurrent = options.preserveCurrent === true;
1655
+ if (!preserveCurrent) setReadState({
1140
1656
  status: "loading",
1141
1657
  views: []
1142
1658
  });
1143
- try {
1144
- const views = await loadWorktreeViews(manager, workspaceIds);
1659
+ await refreshGuard.current.run(() => loadWorktreeViews(manager, workspaceIds, {
1660
+ invalidateContext: options.invalidateContext !== false,
1661
+ invalidateWorktreeContext
1662
+ }), (views) => {
1145
1663
  setReadState({
1146
1664
  status: "ready",
1147
1665
  views
1148
1666
  });
1149
- } catch (error) {
1667
+ }, (error) => {
1668
+ if (preserveCurrent) throw error;
1150
1669
  setReadState({
1151
1670
  status: "error",
1152
1671
  views: [],
1153
1672
  error: toWorktreeViewError(error)
1154
1673
  });
1155
- }
1156
- }, [manager, workspaceIds]);
1674
+ });
1675
+ }, [
1676
+ invalidateWorktreeContext,
1677
+ manager,
1678
+ workspaceIds
1679
+ ]);
1157
1680
  (0, react.useEffect)(() => {
1158
- if (mode === "worktree") refresh();
1159
- else setReadState(EMPTY_READ_STATE);
1681
+ if (mode === "worktree") refresh({ preserveCurrent: readStateRef.current.status === "ready" });
1682
+ else {
1683
+ refreshGuard.current.invalidate();
1684
+ setReadState(EMPTY_READ_STATE);
1685
+ }
1160
1686
  }, [mode, refresh]);
1161
1687
  (0, react.useEffect)(() => {
1162
1688
  if (readState.status !== "ready" || syncSessionWorkspaces === void 0) return;
@@ -1172,12 +1698,23 @@ window.__ModuleLoader__.load({
1172
1698
  const viewByWorkspace = (0, react.useMemo)(() => new Map(readState.views.map((view) => [view.workspaceId, view])), [readState.views]);
1173
1699
  const modalWorkspace = workspaces.items.find((workspace) => workspace.workspaceId === worktreeModalWorkspaceId);
1174
1700
  const modalView = worktreeModalWorkspaceId === void 0 ? void 0 : viewByWorkspace.get(worktreeModalWorkspaceId);
1701
+ const modalReadiness = modalView?.readiness;
1702
+ const modalSetupStatus = modalView === void 0 ? void 0 : modalReadiness?.status === "ready" ? modalView.branches.length === 0 ? "noLocalBranch" : void 0 : modalReadiness?.status;
1703
+ const modalCanCreate = modalSetupStatus === void 0 && modalReadiness?.status === "ready";
1704
+ (0, react.useEffect)(() => {
1705
+ if (worktreeModalWorkspaceId === void 0 || modalView === void 0) return;
1706
+ if (modalView.readiness.status !== "ready") {
1707
+ setSelectedBranch("");
1708
+ return;
1709
+ }
1710
+ setSelectedBranch((current) => reconcileBaseBranchSelection(current, modalView.branches));
1711
+ }, [modalView, worktreeModalWorkspaceId]);
1175
1712
  const runMutation = async (operation) => {
1176
1713
  setActionPending(true);
1177
1714
  setActionError(void 0);
1178
1715
  try {
1179
1716
  await operation();
1180
- await refresh();
1717
+ await refresh({ preserveCurrent: true });
1181
1718
  } catch (error) {
1182
1719
  setActionError(toWorktreeViewError(error));
1183
1720
  } finally {
@@ -1303,6 +1840,29 @@ window.__ModuleLoader__.load({
1303
1840
  setActionError(toWorktreeViewError(error));
1304
1841
  });
1305
1842
  };
1843
+ const commitWorktreeDrag = (activeDrag, over, worktreeIds, workspaceId) => {
1844
+ if (worktreeDropCommitted.current) return;
1845
+ worktreeDropCommitted.current = true;
1846
+ setWorktreeDrag(void 0);
1847
+ if (activeDrag.workspaceId !== workspaceId) return;
1848
+ const move = resolveWorktreeMove(worktreeIds, activeDrag.worktreeId, over.worktreeId, over.half);
1849
+ if (move === void 0) return;
1850
+ if (insertWorktreeBefore === void 0) {
1851
+ setActionError({
1852
+ code: "WORKTREE_ORDER_UNAVAILABLE",
1853
+ message: "",
1854
+ retryable: true
1855
+ });
1856
+ return;
1857
+ }
1858
+ setActionError(void 0);
1859
+ insertWorktreeBefore(workspaceId, activeDrag.worktreeId, move.beforeWorktreeId).then(() => refresh({
1860
+ preserveCurrent: true,
1861
+ invalidateContext: false
1862
+ })).catch((error) => {
1863
+ setActionError(toRetryableWorktreeOrderError(error));
1864
+ });
1865
+ };
1306
1866
  const openSessionRename = (sessionId, currentTitle) => {
1307
1867
  setSessionRenameTarget({
1308
1868
  sessionId,
@@ -1372,7 +1932,7 @@ window.__ModuleLoader__.load({
1372
1932
  };
1373
1933
  const submitWorktree = async () => {
1374
1934
  const worktreeName = newBranch.trim();
1375
- if (manager === void 0 || modalWorkspace === void 0 || selectedBranch.length === 0 || worktreeName.length === 0) return;
1935
+ if (manager === void 0 || modalWorkspace === void 0 || !modalCanCreate || selectedBranch.length === 0 || worktreeName.length === 0) return;
1376
1936
  const input = {
1377
1937
  workspaceId: modalWorkspace.workspaceId,
1378
1938
  branch: selectedBranch,
@@ -1393,7 +1953,7 @@ window.__ModuleLoader__.load({
1393
1953
  };
1394
1954
  setWorktreeModalWorkspaceId(void 0);
1395
1955
  if (createSessionCallback === void 0) {
1396
- await refresh();
1956
+ await refresh({ preserveCurrent: true });
1397
1957
  setActionError({
1398
1958
  code: "WORKTREE_CREATED_SESSION_UNAVAILABLE",
1399
1959
  message: "",
@@ -1408,14 +1968,15 @@ window.__ModuleLoader__.load({
1408
1968
  };
1409
1969
  try {
1410
1970
  await createSessionCallback(sessionInput);
1971
+ await invalidateWorktreeContext?.(createdWorktree.workspaceId);
1411
1972
  } catch (error) {
1412
- if (error instanceof WorktreeSessionBindingError) setPendingSessionBinding({
1973
+ if (error instanceof WorktreeSessionBindingError && error.retryable) setPendingSessionBinding({
1413
1974
  ...sessionInput,
1414
1975
  sessionId: error.sessionId
1415
1976
  });
1416
1977
  throw error;
1417
1978
  }
1418
- await refresh();
1979
+ await refresh({ preserveCurrent: true });
1419
1980
  } catch (error) {
1420
1981
  setActionError(toWorktreeViewError(error));
1421
1982
  } finally {
@@ -1436,9 +1997,10 @@ window.__ModuleLoader__.load({
1436
1997
  setPendingSessionBinding(void 0);
1437
1998
  try {
1438
1999
  await createSessionCallback(input);
1439
- await refresh();
2000
+ await invalidateWorktreeContext?.(input.workspaceId);
2001
+ await refresh({ preserveCurrent: true });
1440
2002
  } catch (error) {
1441
- if (error instanceof WorktreeSessionBindingError) setPendingSessionBinding({
2003
+ if (error instanceof WorktreeSessionBindingError && error.retryable) setPendingSessionBinding({
1442
2004
  ...input,
1443
2005
  sessionId: error.sessionId
1444
2006
  });
@@ -1448,21 +2010,25 @@ window.__ModuleLoader__.load({
1448
2010
  }
1449
2011
  };
1450
2012
  const retrySessionBinding = async () => {
1451
- if (manager === void 0 || pendingSessionBinding === void 0) return;
2013
+ if (manager === void 0 || pendingSessionBinding === void 0 || pendingSessionArchived) return;
2014
+ const pending = pendingSessionBinding;
1452
2015
  setActionPending(true);
1453
2016
  setActionError(void 0);
1454
2017
  try {
1455
- await manager.bindSession({
1456
- workspaceId: pendingSessionBinding.workspaceId,
1457
- worktreeId: pendingSessionBinding.worktreeId,
1458
- sessionId: pendingSessionBinding.sessionId
2018
+ await retryWorktreeSessionBinding({
2019
+ manager,
2020
+ pending,
2021
+ archived: false,
2022
+ ensureSessionWorkspace: (workspaceId, sessionId) => {
2023
+ ensureSessionWorkspace?.(workspaceId, sessionId);
2024
+ },
2025
+ openSession
1459
2026
  });
1460
- const sessionId = pendingSessionBinding.sessionId;
1461
2027
  setPendingSessionBinding(void 0);
1462
- ensureSessionWorkspace?.(pendingSessionBinding.workspaceId, sessionId);
1463
- await refresh();
1464
- openSession(sessionId);
2028
+ await refresh({ preserveCurrent: true });
2029
+ await invalidateWorktreeContext?.(pending.workspaceId);
1465
2030
  } catch (error) {
2031
+ if (error instanceof WorktreeSessionBindingError && !error.retryable) setPendingSessionBinding(void 0);
1466
2032
  setActionError(toWorktreeViewError(error));
1467
2033
  } finally {
1468
2034
  setActionPending(false);
@@ -1556,7 +2122,7 @@ window.__ModuleLoader__.load({
1556
2122
  children: [(0, react_jsx_runtime.jsx)("button", {
1557
2123
  type: "button",
1558
2124
  className: worktree_css_default.actionButton,
1559
- disabled: actionPending,
2125
+ disabled: actionPending || pendingSessionArchived,
1560
2126
  onClick: () => {
1561
2127
  retrySessionBinding();
1562
2128
  },
@@ -1612,14 +2178,17 @@ window.__ModuleLoader__.load({
1612
2178
  children: t("workspace.noMatches")
1613
2179
  }) : visibleWorkspaces.map((workspace) => {
1614
2180
  const view = viewByWorkspace.get(workspace.workspaceId);
2181
+ const currentBranch = view?.branches.find((branch) => branch.isCurrent)?.name;
2182
+ const mainLabel = currentBranch === void 0 ? t("worktree.main") : t("worktree.mainWithBranch", { branch: currentBranch });
1615
2183
  const expanded = expandedWorkspaces[workspace.workspaceId] !== false;
1616
2184
  const workspaceMatchesQuery = includesText(workspace.title, query);
1617
2185
  const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
1618
2186
  const bindings = view?.bindings ?? [];
1619
- const visibleMainSessionIds = unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]).filter((sessionId) => workspaceMatchesQuery || includesText(sessionLabel(sessionId, sessions), query));
2187
+ const visibleMainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]), sessions).filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
1620
2188
  const mainExpanded = expandedMains[workspace.workspaceId] !== false;
1621
2189
  const mainGroupKey = `main:${workspace.workspaceId}`;
1622
2190
  const worktrees = view?.worktrees ?? [];
2191
+ const sameWorkspaceWorktreeDrag = worktreeDrag?.workspaceId === workspace.workspaceId;
1623
2192
  return (0, react_jsx_runtime.jsxs)("section", {
1624
2193
  className: worktree_css_default.workspaceGroup,
1625
2194
  "data-workspace-id": workspace.workspaceId,
@@ -1682,7 +2251,7 @@ window.__ModuleLoader__.load({
1682
2251
  (0, react_jsx_runtime.jsx)(WorktreeGroupRow, {
1683
2252
  t,
1684
2253
  kind: "main",
1685
- label: t("worktree.main"),
2254
+ label: mainLabel,
1686
2255
  expanded: mainExpanded,
1687
2256
  icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
1688
2257
  workspaceTitle: workspace.title,
@@ -1744,10 +2313,10 @@ window.__ModuleLoader__.load({
1744
2313
  children: t("worktree.noWorktrees")
1745
2314
  }),
1746
2315
  worktrees.map((record) => {
1747
- const worktreeSessionIds = filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds);
2316
+ const worktreeSessionIds = filterVisibleSessionIds(filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds), sessions);
1748
2317
  const worktreeMatchesQuery = workspaceMatchesQuery || includesText(record.branch, query) || includesText(record.absolutePath, query);
1749
- if (query.length > 0 && !worktreeMatchesQuery && !worktreeSessionIds.some((sessionId) => includesText(sessionLabel(sessionId, sessions), query))) return null;
1750
- const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery || includesText(sessionLabel(sessionId, sessions), query));
2318
+ if (query.length > 0 && !worktreeMatchesQuery && !worktreeSessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) return null;
2319
+ const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
1751
2320
  const worktreeGroupKey = `worktree:${record.worktreeId}`;
1752
2321
  const state = record.status === "removed" ? "warning" : record.health === "repair" ? "error" : "done";
1753
2322
  const stateLabel = record.status === "removed" ? t("worktree.detached") : record.health === "repair" ? t("worktree.repair") : t("worktree.ready");
@@ -1759,6 +2328,7 @@ window.__ModuleLoader__.load({
1759
2328
  t,
1760
2329
  kind: "worktree",
1761
2330
  label: record.branch,
2331
+ worktreeId: record.worktreeId,
1762
2332
  expanded: worktreeExpanded,
1763
2333
  icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
1764
2334
  workspaceTitle: workspace.title,
@@ -1767,7 +2337,7 @@ window.__ModuleLoader__.load({
1767
2337
  onToggle: () => {
1768
2338
  toggleWorktree(record.worktreeId);
1769
2339
  },
1770
- onCreateSession: record.status === "active" ? () => {
2340
+ onCreateSession: record.status === "active" && record.health !== "repair" ? () => {
1771
2341
  createSession({
1772
2342
  workspaceId: record.workspaceId,
1773
2343
  worktreeId: record.worktreeId,
@@ -1785,7 +2355,39 @@ window.__ModuleLoader__.load({
1785
2355
  setWorktreeRemoval(record);
1786
2356
  setActionError(void 0);
1787
2357
  }
1788
- } : void 0
2358
+ } : void 0,
2359
+ drag: {
2360
+ active: sameWorkspaceWorktreeDrag,
2361
+ marker: worktreeDrag?.over?.worktreeId === record.worktreeId ? worktreeDrag.over.half : null,
2362
+ start: () => {
2363
+ worktreeDropCommitted.current = false;
2364
+ setWorktreeDrag({
2365
+ workspaceId: workspace.workspaceId,
2366
+ worktreeId: record.worktreeId,
2367
+ over: null
2368
+ });
2369
+ },
2370
+ hover: (half) => {
2371
+ setWorktreeDrag((current) => current === void 0 || current.workspaceId !== workspace.workspaceId ? current : {
2372
+ ...current,
2373
+ over: {
2374
+ worktreeId: record.worktreeId,
2375
+ half
2376
+ }
2377
+ });
2378
+ },
2379
+ drop: (half) => {
2380
+ if (worktreeDrag === void 0) return;
2381
+ commitWorktreeDrag(worktreeDrag, {
2382
+ worktreeId: record.worktreeId,
2383
+ half
2384
+ }, worktrees.map((candidate) => candidate.worktreeId), workspace.workspaceId);
2385
+ },
2386
+ end: () => {
2387
+ setWorktreeDrag(void 0);
2388
+ worktreeDropCommitted.current = false;
2389
+ }
2390
+ }
1789
2391
  }), worktreeExpanded && (0, react_jsx_runtime.jsx)(WorktreeSessionGroup, {
1790
2392
  t,
1791
2393
  groupKey: worktreeGroupKey,
@@ -1974,7 +2576,7 @@ window.__ModuleLoader__.load({
1974
2576
  children: formatWorktreeViewError(workspaceDeleteError, t)
1975
2577
  })
1976
2578
  }),
1977
- worktreeModalWorkspaceId !== void 0 && modalWorkspace !== void 0 && (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2579
+ worktreeModalWorkspaceId !== void 0 && modalWorkspace !== void 0 && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1978
2580
  open: worktreeModalWorkspaceId !== void 0,
1979
2581
  onClose: () => {
1980
2582
  if (actionPending) return;
@@ -1998,48 +2600,50 @@ window.__ModuleLoader__.load({
1998
2600
  },
1999
2601
  children: t("worktree.create")
2000
2602
  })] }),
2001
- children: [
2002
- (0, react_jsx_runtime.jsxs)("label", {
2003
- className: worktree_css_default.modalField,
2004
- children: [t("worktree.baseBranch"), (0, react_jsx_runtime.jsxs)("select", {
2005
- className: worktree_css_default.actionSelect,
2006
- "aria-label": t("worktree.baseBranch"),
2007
- value: selectedBranch,
2008
- disabled: actionPending,
2009
- onChange: (event) => {
2010
- setSelectedBranch(event.currentTarget.value);
2011
- },
2012
- children: [(0, react_jsx_runtime.jsx)("option", {
2013
- value: "",
2014
- children: t("worktree.noLocalBranch")
2015
- }), (modalView?.branches ?? []).map((branch) => (0, react_jsx_runtime.jsxs)("option", {
2016
- value: branch.name,
2017
- children: [
2018
- branch.name,
2019
- branch.isCurrent ? t("branch.current") : "",
2020
- branch.checkedOut ? t("branch.checkedOut") : ""
2021
- ]
2022
- }, branch.name))]
2023
- })]
2024
- }),
2025
- (0, react_jsx_runtime.jsxs)("label", {
2026
- className: worktree_css_default.modalField,
2027
- children: [t("worktree.name"), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2028
- className: worktree_css_default.renameInput,
2029
- "aria-label": t("worktree.name"),
2030
- value: newBranch,
2031
- placeholder: "dsh/12345678",
2032
- disabled: actionPending,
2033
- onChange: (event) => {
2034
- setNewBranch(event.currentTarget.value);
2035
- }
2036
- })]
2037
- }),
2038
- modalView?.branches.length === 0 && (0, react_jsx_runtime.jsx)("p", {
2039
- className: worktree_css_default.empty,
2040
- children: t("worktree.noBranches")
2041
- })
2042
- ]
2603
+ children: modalCanCreate ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("label", {
2604
+ className: worktree_css_default.modalField,
2605
+ children: [t("worktree.baseBranch"), (0, react_jsx_runtime.jsx)("select", {
2606
+ className: worktree_css_default.actionSelect,
2607
+ "aria-label": t("worktree.baseBranch"),
2608
+ value: selectedBranch,
2609
+ disabled: actionPending,
2610
+ onChange: (event) => {
2611
+ setSelectedBranch(event.currentTarget.value);
2612
+ },
2613
+ children: modalView?.branches.map((branch) => (0, react_jsx_runtime.jsxs)("option", {
2614
+ value: branch.name,
2615
+ children: [
2616
+ branch.name,
2617
+ branch.isCurrent ? t("branch.current") : "",
2618
+ branch.checkedOut ? t("branch.checkedOut") : ""
2619
+ ]
2620
+ }, branch.name))
2621
+ })]
2622
+ }), (0, react_jsx_runtime.jsxs)("label", {
2623
+ className: worktree_css_default.modalField,
2624
+ children: [t("worktree.name"), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2625
+ className: worktree_css_default.renameInput,
2626
+ "aria-label": t("worktree.name"),
2627
+ value: newBranch,
2628
+ placeholder: "dsh/12345678",
2629
+ disabled: actionPending,
2630
+ onChange: (event) => {
2631
+ setNewBranch(event.currentTarget.value);
2632
+ }
2633
+ })]
2634
+ })] }) : (0, react_jsx_runtime.jsxs)("div", {
2635
+ className: worktree_css_default.gitReadiness,
2636
+ "data-worktree-readiness": modalSetupStatus ?? "loading",
2637
+ role: "alert",
2638
+ children: [(0, react_jsx_runtime.jsx)("p", {
2639
+ className: worktree_css_default.message,
2640
+ children: modalSetupStatus === void 0 ? t("status.loading") : worktreeSetupMessage(modalSetupStatus, t)
2641
+ }), modalSetupStatus !== void 0 && (0, react_jsx_runtime.jsx)("pre", {
2642
+ className: worktree_css_default.commandBlock,
2643
+ "aria-label": t("worktree.setup.commands"),
2644
+ children: worktreeSetupCommands(modalSetupStatus).join("\n")
2645
+ })]
2646
+ })
2043
2647
  }),
2044
2648
  worktreeRemoval !== void 0 && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2045
2649
  open: worktreeRemoval !== void 0,
@@ -2071,6 +2675,7 @@ window.__ModuleLoader__.load({
2071
2675
  worktreeId: target.worktreeId
2072
2676
  }
2073
2677
  });
2678
+ await invalidateWorktreeContext?.(target.workspaceId);
2074
2679
  setWorktreeRemoval(void 0);
2075
2680
  });
2076
2681
  },
@@ -2081,6 +2686,240 @@ window.__ModuleLoader__.load({
2081
2686
  });
2082
2687
  }
2083
2688
  //#endregion
2689
+ //#region lib/client/WorktreeOverlay.js
2690
+ /** Compose the independent Hero context and Worktree navigation overlays. */
2691
+ function WorktreeOverlay(props) {
2692
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(WorktreeHeroContext, { ...props }), (0, react_jsx_runtime.jsx)(WorktreeSurface, { ...props })] });
2693
+ }
2694
+ //#endregion
2695
+ //#region lib/client/worktree-context.js
2696
+ const none = (reason) => ({
2697
+ kind: "none",
2698
+ reason
2699
+ });
2700
+ /** Resolve the branch or active Worktree label for one current DSH Session. */
2701
+ function resolveWorktreeSessionContext(input) {
2702
+ const sessionId = input.currentSessionId;
2703
+ if (sessionId === void 0 && input.currentWorkspaceId === void 0) return none("no-session");
2704
+ const workspace = sessionId === void 0 ? input.workspaces.find(({ workspaceId }) => workspaceId === input.currentWorkspaceId) : input.workspaces.find(({ sessionIds }) => sessionIds.includes(sessionId));
2705
+ if (workspace === void 0) return none("unbound");
2706
+ const binding = sessionId === void 0 ? void 0 : input.bindings.find((candidate) => candidate.sessionId === sessionId);
2707
+ if (binding !== void 0) {
2708
+ if (binding.workspaceId !== workspace.workspaceId) return none("workspace-mismatch");
2709
+ if (binding.status === "detached") return none("detached");
2710
+ const record = input.worktrees.find((candidate) => candidate.worktreeId === binding.worktreeId);
2711
+ if (record === void 0 || record.workspaceId !== workspace.workspaceId) return none(record === void 0 ? "stale" : "workspace-mismatch");
2712
+ if (record.status !== "active") return none("stale");
2713
+ if (record.health === "repair") return none("repair");
2714
+ if (record.branch.length === 0) return none("not-ready");
2715
+ return {
2716
+ kind: "worktree",
2717
+ workspaceId: workspace.workspaceId,
2718
+ worktreeId: record.worktreeId,
2719
+ label: record.branch,
2720
+ source: "active-binding"
2721
+ };
2722
+ }
2723
+ const currentBranch = input.branches.find(({ isCurrent }) => isCurrent);
2724
+ if (currentBranch === void 0 || currentBranch.name.length === 0) return none("not-ready");
2725
+ return {
2726
+ kind: "main",
2727
+ workspaceId: workspace.workspaceId,
2728
+ label: currentBranch.name,
2729
+ source: "current-branch"
2730
+ };
2731
+ }
2732
+ //#endregion
2733
+ //#region lib/client/worktree-context-store.js
2734
+ const notReady = () => ({
2735
+ kind: "none",
2736
+ reason: "not-ready"
2737
+ });
2738
+ function scheduledRefresh() {
2739
+ let resolve;
2740
+ return {
2741
+ promise: new Promise((resolvePromise) => {
2742
+ resolve = resolvePromise;
2743
+ }),
2744
+ resolve
2745
+ };
2746
+ }
2747
+ function identityFrom(sessions, workspaces) {
2748
+ const sessionId = sessions.getSnapshot().current;
2749
+ if (sessionId === void 0) return { workspaceId: workspaces.getSnapshot().recentWorkspaceId };
2750
+ return {
2751
+ sessionId,
2752
+ workspaceId: workspaces.getSnapshot().items.find((candidate) => candidate.sessionIds.includes(sessionId))?.workspaceId
2753
+ };
2754
+ }
2755
+ /**
2756
+ * Build one browser-local current-Session projection shared by the header
2757
+ * consumer. Superseded requests may still finish on the transport, but their
2758
+ * results never become visible in the current snapshot.
2759
+ */
2760
+ function createWorktreeContextProjection(input) {
2761
+ const store = input.storeFactory({
2762
+ status: "idle",
2763
+ value: notReady()
2764
+ });
2765
+ let disposed = false;
2766
+ let generation = 0;
2767
+ let scheduled = false;
2768
+ let scheduleTicket = 0;
2769
+ let scheduledCompletion;
2770
+ let latestRefresh = Promise.resolve();
2771
+ const setPending = (identity) => {
2772
+ if (identity.workspaceId === void 0) {
2773
+ store.set({
2774
+ status: "ready",
2775
+ value: resolveWorktreeSessionContext({
2776
+ currentSessionId: identity.sessionId,
2777
+ currentWorkspaceId: identity.workspaceId,
2778
+ workspaces: input.workspaces.getSnapshot().items,
2779
+ branches: [],
2780
+ worktrees: [],
2781
+ bindings: []
2782
+ })
2783
+ });
2784
+ return;
2785
+ }
2786
+ const workspace = input.workspaces.getSnapshot().items.find((candidate) => candidate.workspaceId === identity.workspaceId);
2787
+ store.set({
2788
+ status: "loading",
2789
+ ...identity.sessionId === void 0 ? {} : { sessionId: identity.sessionId },
2790
+ workspaceId: identity.workspaceId,
2791
+ workspaceTitle: workspace?.title,
2792
+ value: notReady()
2793
+ });
2794
+ };
2795
+ const matchesCurrent = (request, requestGeneration) => {
2796
+ if (disposed || generation !== requestGeneration) return false;
2797
+ const current = identityFrom(input.sessions, input.workspaces);
2798
+ return current.sessionId === request.sessionId && current.workspaceId === request.workspaceId;
2799
+ };
2800
+ const runRefresh = async () => {
2801
+ if (disposed) return;
2802
+ const requestGeneration = ++generation;
2803
+ const identity = identityFrom(input.sessions, input.workspaces);
2804
+ setPending(identity);
2805
+ if (identity.workspaceId === void 0) return;
2806
+ const request = {
2807
+ sessionId: identity.sessionId,
2808
+ workspaceId: identity.workspaceId
2809
+ };
2810
+ try {
2811
+ const data = await loadWorktreeView(input.manager, request.workspaceId);
2812
+ if (!matchesCurrent(request, requestGeneration)) return;
2813
+ const workspace = input.workspaces.getSnapshot().items.find((candidate) => candidate.workspaceId === request.workspaceId);
2814
+ if ("error" in data.readiness) {
2815
+ store.set({
2816
+ status: "error",
2817
+ ...request.sessionId === void 0 ? {} : { sessionId: request.sessionId },
2818
+ workspaceId: request.workspaceId,
2819
+ workspaceTitle: workspace?.title,
2820
+ value: notReady(),
2821
+ error: data.readiness.error
2822
+ });
2823
+ return;
2824
+ }
2825
+ store.set({
2826
+ status: "ready",
2827
+ ...request.sessionId === void 0 ? {} : { sessionId: request.sessionId },
2828
+ workspaceId: request.workspaceId,
2829
+ workspaceTitle: workspace?.title,
2830
+ value: resolveWorktreeSessionContext({
2831
+ currentSessionId: request.sessionId,
2832
+ currentWorkspaceId: request.workspaceId,
2833
+ workspaces: input.workspaces.getSnapshot().items,
2834
+ branches: data.branches,
2835
+ worktrees: data.worktrees,
2836
+ bindings: data.bindings
2837
+ })
2838
+ });
2839
+ } catch (error) {
2840
+ if (!matchesCurrent(request, requestGeneration)) return;
2841
+ store.set({
2842
+ status: "error",
2843
+ ...request.sessionId === void 0 ? {} : { sessionId: request.sessionId },
2844
+ workspaceId: request.workspaceId,
2845
+ workspaceTitle: input.workspaces.getSnapshot().items.find((candidate) => candidate.workspaceId === request.workspaceId)?.title,
2846
+ value: notReady(),
2847
+ error: toWorktreeViewError(error)
2848
+ });
2849
+ }
2850
+ };
2851
+ const cancelScheduledRefresh = () => {
2852
+ scheduleTicket += 1;
2853
+ scheduled = false;
2854
+ const completion = scheduledCompletion;
2855
+ scheduledCompletion = void 0;
2856
+ completion?.resolve();
2857
+ };
2858
+ const refresh = () => {
2859
+ cancelScheduledRefresh();
2860
+ const completion = runRefresh();
2861
+ latestRefresh = completion;
2862
+ return completion;
2863
+ };
2864
+ const scheduleRefresh = () => {
2865
+ if (disposed) return;
2866
+ generation += 1;
2867
+ if (scheduled) return;
2868
+ setPending(identityFrom(input.sessions, input.workspaces));
2869
+ scheduled = true;
2870
+ const ticket = ++scheduleTicket;
2871
+ const completion = scheduledRefresh();
2872
+ scheduledCompletion = completion;
2873
+ latestRefresh = completion.promise;
2874
+ queueMicrotask(() => {
2875
+ if (disposed || ticket !== scheduleTicket) {
2876
+ completion.resolve();
2877
+ return;
2878
+ }
2879
+ scheduled = false;
2880
+ runRefresh().finally(() => {
2881
+ if (scheduledCompletion === completion) scheduledCompletion = void 0;
2882
+ completion.resolve();
2883
+ });
2884
+ });
2885
+ };
2886
+ const waitForLatestRefresh = async (workspaceId) => {
2887
+ let observed;
2888
+ while (!disposed) {
2889
+ if (workspaceId !== void 0 && identityFrom(input.sessions, input.workspaces).workspaceId !== workspaceId) return;
2890
+ const completion = latestRefresh;
2891
+ if (completion === observed) return;
2892
+ observed = completion;
2893
+ await completion;
2894
+ }
2895
+ };
2896
+ const unsubscribeSessions = input.sessions.subscribe(scheduleRefresh);
2897
+ const unsubscribeWorkspaces = input.workspaces.subscribe(scheduleRefresh);
2898
+ return {
2899
+ store,
2900
+ refresh,
2901
+ async invalidate(workspaceId) {
2902
+ if (disposed) return;
2903
+ const identity = identityFrom(input.sessions, input.workspaces);
2904
+ if (workspaceId !== void 0 && workspaceId !== identity.workspaceId) return;
2905
+ await refresh();
2906
+ await waitForLatestRefresh(workspaceId ?? identity.workspaceId);
2907
+ },
2908
+ dispose() {
2909
+ if (disposed) return;
2910
+ disposed = true;
2911
+ generation += 1;
2912
+ cancelScheduledRefresh();
2913
+ unsubscribeSessions();
2914
+ unsubscribeWorkspaces();
2915
+ store.set({
2916
+ status: "idle",
2917
+ value: notReady()
2918
+ });
2919
+ }
2920
+ };
2921
+ }
2922
+ //#endregion
2084
2923
  //#region lib/client/view-mode-store.js
2085
2924
  /** Create one root-scoped, browser-local view-mode store for the two slot entries. */
2086
2925
  function createWorktreeViewStore() {
@@ -2122,17 +2961,24 @@ window.__ModuleLoader__.load({
2122
2961
  let bindings = [];
2123
2962
  let disposed = false;
2124
2963
  let applying = false;
2125
- const apply = (nextBindings) => {
2126
- const current = list.getSnapshot();
2127
- const next = projectVirtualWorkspaceMembership(current, bindings, nextBindings);
2128
- if (sameWorkspaceItems(current, next)) return;
2964
+ const nativeSet = list.set;
2965
+ const write = (next) => {
2129
2966
  applying = true;
2130
2967
  try {
2131
- list.set(next);
2968
+ nativeSet(next);
2132
2969
  } finally {
2133
2970
  applying = false;
2134
2971
  }
2135
2972
  };
2973
+ list.set = (next) => {
2974
+ write(projectVirtualWorkspaceMembership(next, bindings, bindings));
2975
+ };
2976
+ const apply = (nextBindings) => {
2977
+ const current = list.getSnapshot();
2978
+ const next = projectVirtualWorkspaceMembership(current, bindings, nextBindings);
2979
+ if (sameWorkspaceItems(current, next)) return;
2980
+ write(next);
2981
+ };
2136
2982
  const replay = () => {
2137
2983
  if (disposed || applying) return;
2138
2984
  apply(bindings);
@@ -2160,6 +3006,7 @@ window.__ModuleLoader__.load({
2160
3006
  bindings = [];
2161
3007
  disposed = true;
2162
3008
  unsubscribe();
3009
+ list.set = nativeSet;
2163
3010
  }
2164
3011
  };
2165
3012
  }
@@ -2185,8 +3032,16 @@ window.__ModuleLoader__.load({
2185
3032
  const manager = createWorktreeConnectionAdapter(ctx.connection.rpc);
2186
3033
  const sessions = ctx.sessions;
2187
3034
  const virtualWorkspaceMembership = createVirtualWorkspaceMembership(ctx.workspaces.list);
3035
+ const contextProjection = createWorktreeContextProjection({
3036
+ sessions: ctx.sessions.list,
3037
+ workspaces: ctx.workspaces.list,
3038
+ manager,
3039
+ storeFactory: _deepseek_ai_dsh_client_runtime_client.createSnapshotStore
3040
+ });
2188
3041
  ctx.effect(() => () => manager.dispose(), "clutch-dsh-worktree: connection cleanup");
2189
3042
  ctx.effect(() => () => virtualWorkspaceMembership.dispose(), "clutch-dsh-worktree: Workspace membership cleanup");
3043
+ ctx.effect(() => () => contextProjection.dispose(), "clutch-dsh-worktree: Session context cleanup");
3044
+ contextProjection.refresh();
2190
3045
  const viewStore = createWorktreeViewStore();
2191
3046
  const ensureSessionWorkspace = (workspaceId, sessionId) => {
2192
3047
  virtualWorkspaceMembership.ensure({
@@ -2197,6 +3052,24 @@ window.__ModuleLoader__.load({
2197
3052
  const syncSessionWorkspaces = (bindings) => {
2198
3053
  virtualWorkspaceMembership.sync(bindings);
2199
3054
  };
3055
+ const worktreeSessionConnector = createWorktreeSessionConnector({
3056
+ manager,
3057
+ sessions: ctx.sessions.list,
3058
+ archivedSessionIds: () => ctx.workspaces.list.getSnapshot().archivedSessionIds ?? [],
3059
+ createSession: async (input) => String(await sessions.create(input)),
3060
+ ensureSessionWorkspace,
3061
+ openSession: (sessionId) => {
3062
+ ctx.sessions.open(sessionId);
3063
+ }
3064
+ });
3065
+ ctx.effect(() => () => worktreeSessionConnector.dispose(), "clutch-dsh-worktree: Worktree Session connector cleanup");
3066
+ ctx.slots.inject("conversation.session.header.actions", () => ctx.slots.register({
3067
+ name: "conversation.session.header.actions",
3068
+ id: "clutch-dsh-worktree-context-header",
3069
+ order: -5,
3070
+ locale: WORKTREE_NS,
3071
+ inject: () => ({ hooks: { worktreeContext: contextProjection.store } })
3072
+ }, WorktreeHeaderContext));
2200
3073
  ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
2201
3074
  name: "sidebar.footer.action",
2202
3075
  id: "clutch-dsh-worktree-mode-action",
@@ -2211,22 +3084,14 @@ window.__ModuleLoader__.load({
2211
3084
  locale: WORKTREE_NS,
2212
3085
  inject: () => ({
2213
3086
  available: true,
3087
+ hooks: { worktreeContext: contextProjection.store },
2214
3088
  manager,
2215
3089
  createWorkspace: async () => {
2216
3090
  const workspacePath = await ctx.workspaces.pickDirectory();
2217
3091
  if (workspacePath !== null) await ctx.workspaces.create({ path: workspacePath });
2218
3092
  },
2219
- createSessionForWorktree: (input) => createSessionForWorktree({
2220
- ...input,
2221
- createSession: (sessionInput) => sessions.create(sessionInput),
2222
- manager,
2223
- beforeOpen: (sessionId) => {
2224
- ensureSessionWorkspace(input.workspaceId, sessionId);
2225
- },
2226
- openSession: (sessionId) => {
2227
- ctx.sessions.open(sessionId);
2228
- }
2229
- }),
3093
+ createSessionForWorktree: (input) => worktreeSessionConnector.create(input),
3094
+ invalidateWorktreeContext: (workspaceId) => contextProjection.invalidate(workspaceId),
2230
3095
  createMainSession: (workspaceId) => {
2231
3096
  ctx.workspaces.startSession(workspaceId);
2232
3097
  },
@@ -2242,6 +3107,11 @@ window.__ModuleLoader__.load({
2242
3107
  insertSessionBefore: async (workspaceId, sessionId, beforeSessionId) => {
2243
3108
  await ctx.workspaces.insertSessionBefore(workspaceId, sessionId, beforeSessionId);
2244
3109
  },
3110
+ insertWorktreeBefore: (workspaceId, worktreeId, beforeWorktreeId) => manager.insertWorktreeBefore({
3111
+ workspaceId,
3112
+ worktreeId,
3113
+ beforeWorktreeId
3114
+ }),
2245
3115
  renameSession: async (sessionId, title) => {
2246
3116
  const session = ctx.sessions.binding(sessionId)?.session;
2247
3117
  if (session === void 0) throw new Error(`unknown session "${sessionId}"`);
@@ -2263,7 +3133,7 @@ window.__ModuleLoader__.load({
2263
3133
  ctx.sessions.open(sessionId);
2264
3134
  }
2265
3135
  })
2266
- }, WorktreeSurface));
3136
+ }, WorktreeOverlay));
2267
3137
  }
2268
3138
  //#endregion
2269
3139
  exports.WORKTREE_CONNECTION_CHANNEL = WORKTREE_CONNECTION_CHANNEL;