@cerbur/clutch-dsh-worktree 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -0
- package/cordis.patch.yml +5 -0
- package/lib/client/WorktreeModeAction.d.ts +11 -0
- package/lib/client/WorktreeModeAction.d.ts.map +1 -0
- package/lib/client/WorktreeModeAction.js +14 -0
- package/lib/client/WorktreeModeAction.js.map +1 -0
- package/lib/client/WorktreeSurface.d.ts +33 -0
- package/lib/client/WorktreeSurface.d.ts.map +1 -0
- package/lib/client/WorktreeSurface.js +834 -0
- package/lib/client/WorktreeSurface.js.map +1 -0
- package/lib/client/entry.d.ts +18 -0
- package/lib/client/entry.d.ts.map +1 -0
- package/lib/client/entry.js +100 -0
- package/lib/client/entry.js.map +1 -0
- package/lib/client/index.d.ts +7 -0
- package/lib/client/index.d.ts.map +1 -0
- package/lib/client/index.js +6 -0
- package/lib/client/index.js.map +1 -0
- package/lib/client/navigation.d.ts +7 -0
- package/lib/client/navigation.d.ts.map +1 -0
- package/lib/client/navigation.js +5 -0
- package/lib/client/navigation.js.map +1 -0
- package/lib/client/overlay-bounds.d.ts +11 -0
- package/lib/client/overlay-bounds.d.ts.map +1 -0
- package/lib/client/overlay-bounds.js +9 -0
- package/lib/client/overlay-bounds.js.map +1 -0
- package/lib/client/sidebar-overlay-geometry.d.ts +10 -0
- package/lib/client/sidebar-overlay-geometry.d.ts.map +1 -0
- package/lib/client/sidebar-overlay-geometry.js +96 -0
- package/lib/client/sidebar-overlay-geometry.js.map +1 -0
- package/lib/client/view-mode-store.d.ts +5 -0
- package/lib/client/view-mode-store.d.ts.map +1 -0
- package/lib/client/view-mode-store.js +15 -0
- package/lib/client/view-mode-store.js.map +1 -0
- package/lib/client/view-mode.d.ts +52 -0
- package/lib/client/view-mode.d.ts.map +1 -0
- package/lib/client/view-mode.js +76 -0
- package/lib/client/view-mode.js.map +1 -0
- package/lib/client/virtual-workspace-membership.d.ts +27 -0
- package/lib/client/virtual-workspace-membership.d.ts.map +1 -0
- package/lib/client/virtual-workspace-membership.js +95 -0
- package/lib/client/virtual-workspace-membership.js.map +1 -0
- package/lib/client/worktree-connection.d.ts +42 -0
- package/lib/client/worktree-connection.d.ts.map +1 -0
- package/lib/client/worktree-connection.js +139 -0
- package/lib/client/worktree-connection.js.map +1 -0
- package/lib/client/worktree-view.d.ts +63 -0
- package/lib/client/worktree-view.d.ts.map +1 -0
- package/lib/client/worktree-view.js +116 -0
- package/lib/client/worktree-view.js.map +1 -0
- package/lib/client.js +1952 -0
- package/lib/client.js.map +1 -0
- package/lib/contract/index.contract.d.ts +2 -0
- package/lib/contract/index.contract.d.ts.map +1 -0
- package/lib/contract/index.contract.js +51 -0
- package/lib/contract/index.contract.js.map +1 -0
- package/lib/contract/index.d.ts +181 -0
- package/lib/contract/index.d.ts.map +1 -0
- package/lib/contract/index.js +45 -0
- package/lib/contract/index.js.map +1 -0
- package/lib/host/dsh-read-adapter.d.ts +58 -0
- package/lib/host/dsh-read-adapter.d.ts.map +1 -0
- package/lib/host/dsh-read-adapter.js +59 -0
- package/lib/host/dsh-read-adapter.js.map +1 -0
- package/lib/host/index.d.ts +16 -0
- package/lib/host/index.d.ts.map +1 -0
- package/lib/host/index.js +14 -0
- package/lib/host/index.js.map +1 -0
- package/lib/host/remote.d.ts +16 -0
- package/lib/host/remote.d.ts.map +1 -0
- package/lib/host/remote.js +61 -0
- package/lib/host/remote.js.map +1 -0
- package/lib/host/service.d.ts +58 -0
- package/lib/host/service.d.ts.map +1 -0
- package/lib/host/service.js +121 -0
- package/lib/host/service.js.map +1 -0
- package/lib/index.d.ts +23 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +22 -0
- package/lib/index.js.map +1 -0
- package/lib/manage/index.d.ts +9 -0
- package/lib/manage/index.d.ts.map +1 -0
- package/lib/manage/index.js +8 -0
- package/lib/manage/index.js.map +1 -0
- package/lib/manage/manager.d.ts +86 -0
- package/lib/manage/manager.d.ts.map +1 -0
- package/lib/manage/manager.js +584 -0
- package/lib/manage/manager.js.map +1 -0
- package/lib/manage/types.d.ts +41 -0
- package/lib/manage/types.d.ts.map +1 -0
- package/lib/manage/types.js +2 -0
- package/lib/manage/types.js.map +1 -0
- package/lib/provider/git.d.ts +42 -0
- package/lib/provider/git.d.ts.map +1 -0
- package/lib/provider/git.js +222 -0
- package/lib/provider/git.js.map +1 -0
- package/lib/provider/index.d.ts +13 -0
- package/lib/provider/index.d.ts.map +1 -0
- package/lib/provider/index.js +12 -0
- package/lib/provider/index.js.map +1 -0
- package/lib/provider/sidecar.d.ts +85 -0
- package/lib/provider/sidecar.d.ts.map +1 -0
- package/lib/provider/sidecar.js +410 -0
- package/lib/provider/sidecar.js.map +1 -0
- package/lib/provider/types.d.ts +126 -0
- package/lib/provider/types.d.ts.map +1 -0
- package/lib/provider/types.js +46 -0
- package/lib/provider/types.js.map +1 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +290 -0
- package/lib/typert.remote-client.d.ts +32 -0
- package/lib/typert.remote-client.d.ts.map +1 -0
- package/lib/typert.remote-client.js +284 -0
- package/package.json +118 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,1952 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@cerbur/clutch-dsh-worktree",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
|
+
let react = require("react");
|
|
10
|
+
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
11
|
+
//#region lib/client/worktree-connection.js
|
|
12
|
+
/** The one logical channel shared by the DSH Connection and Typert Gateway. */
|
|
13
|
+
const WORKTREE_CONNECTION_CHANNEL = "/api";
|
|
14
|
+
/**
|
|
15
|
+
* Canonical Worktree endpoints owned by this adapter. Keeping the table here makes
|
|
16
|
+
* the browser call surface auditable without making React know wire names.
|
|
17
|
+
*/
|
|
18
|
+
const WORKTREE_CONNECTION_ENDPOINTS = Object.freeze({
|
|
19
|
+
listWorktrees: "worktreeManager/listWorktrees",
|
|
20
|
+
listBranches: "worktreeManager/listBranches",
|
|
21
|
+
createWorktree: "worktreeManager/createWorktree",
|
|
22
|
+
removeWorktree: "worktreeManager/removeWorktree",
|
|
23
|
+
listBindings: "worktreeManager/listBindings",
|
|
24
|
+
bindSession: "worktreeManager/bindSession"
|
|
25
|
+
});
|
|
26
|
+
/** Browser-safe error shared by transport, Gateway, endpoint and domain failures. */
|
|
27
|
+
var WorktreeConnectionError = class extends Error {
|
|
28
|
+
code;
|
|
29
|
+
details;
|
|
30
|
+
retryable;
|
|
31
|
+
constructor(options) {
|
|
32
|
+
super(options.message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
33
|
+
this.name = "WorktreeConnectionError";
|
|
34
|
+
this.code = options.code;
|
|
35
|
+
this.details = Object.freeze({ ...options.details ?? {} });
|
|
36
|
+
this.retryable = options.retryable;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
function isRecord(value) {
|
|
40
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
|
+
}
|
|
42
|
+
function isWorktreeError(value) {
|
|
43
|
+
return isRecord(value) && typeof value.code === "string" && typeof value.message === "string" && isRecord(value.details);
|
|
44
|
+
}
|
|
45
|
+
function isWorktreeRemoteResult(value) {
|
|
46
|
+
if (!isRecord(value) || typeof value.ok !== "boolean") return false;
|
|
47
|
+
return value.ok || isWorktreeError(value.error) && value.error.details !== void 0;
|
|
48
|
+
}
|
|
49
|
+
function disposedError() {
|
|
50
|
+
return new WorktreeConnectionError({
|
|
51
|
+
code: "CLIENT_DISPOSED",
|
|
52
|
+
message: "Worktree connection is disposed; reload the plugin and retry.",
|
|
53
|
+
details: {},
|
|
54
|
+
retryable: false
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function connectionFailure(endpoint, error, code = "CONNECTION_CALL_FAILED") {
|
|
58
|
+
return new WorktreeConnectionError({
|
|
59
|
+
code,
|
|
60
|
+
message: `Worktree endpoint ${endpoint} failed; retryable connection error: ${error instanceof Error ? error.message : String(error)}`,
|
|
61
|
+
details: { endpoint },
|
|
62
|
+
retryable: true,
|
|
63
|
+
cause: error
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function gatewayFailure(endpoint, result) {
|
|
67
|
+
throw new WorktreeConnectionError({
|
|
68
|
+
code: result.error.code,
|
|
69
|
+
message: `Worktree endpoint ${endpoint} failed: ${result.error.message}`,
|
|
70
|
+
details: isRecord(result.error.details) ? result.error.details : {},
|
|
71
|
+
retryable: true
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function invalidResult(endpoint) {
|
|
75
|
+
return new WorktreeConnectionError({
|
|
76
|
+
code: "WORKTREE_RPC_INVALID_RESULT",
|
|
77
|
+
message: `Worktree endpoint ${endpoint} returned an invalid result; retry the request.`,
|
|
78
|
+
details: { endpoint },
|
|
79
|
+
retryable: true
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function unwrap(endpoint, result) {
|
|
83
|
+
if (!result.ok) gatewayFailure(endpoint, result);
|
|
84
|
+
if (!isWorktreeRemoteResult(result.value)) throw invalidResult(endpoint);
|
|
85
|
+
if (!result.value.ok) throw new WorktreeConnectionError({
|
|
86
|
+
code: result.value.error.code,
|
|
87
|
+
message: result.value.error.message,
|
|
88
|
+
details: result.value.error.details,
|
|
89
|
+
retryable: false
|
|
90
|
+
});
|
|
91
|
+
return result.value.value;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Adapt the shared DSH Connection RPC into the existing WorktreeManager contract.
|
|
95
|
+
* All wire details and request cancellation live behind this one deep module.
|
|
96
|
+
*/
|
|
97
|
+
function createWorktreeConnectionAdapter(rpc) {
|
|
98
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
99
|
+
let disposed = false;
|
|
100
|
+
async function invoke(method, input) {
|
|
101
|
+
if (disposed) throw disposedError();
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
inFlight.add(controller);
|
|
104
|
+
const endpoint = WORKTREE_CONNECTION_ENDPOINTS[method];
|
|
105
|
+
try {
|
|
106
|
+
let result;
|
|
107
|
+
try {
|
|
108
|
+
result = await rpc.call(WORKTREE_CONNECTION_CHANNEL, endpoint, { args: { input } }, controller.signal);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (disposed && controller.signal.aborted) throw disposedError();
|
|
111
|
+
throw connectionFailure(endpoint, error);
|
|
112
|
+
}
|
|
113
|
+
return unwrap(endpoint, result);
|
|
114
|
+
} finally {
|
|
115
|
+
inFlight.delete(controller);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
listWorktrees: (input) => invoke("listWorktrees", input),
|
|
120
|
+
listBranches: (input) => invoke("listBranches", input),
|
|
121
|
+
createWorktree: (input) => invoke("createWorktree", input),
|
|
122
|
+
async removeWorktree(input) {
|
|
123
|
+
await invoke("removeWorktree", input);
|
|
124
|
+
},
|
|
125
|
+
listBindings: (input) => invoke("listBindings", input),
|
|
126
|
+
bindSession: (input) => invoke("bindSession", input),
|
|
127
|
+
dispose() {
|
|
128
|
+
if (disposed) return;
|
|
129
|
+
disposed = true;
|
|
130
|
+
for (const controller of inFlight) controller.abort();
|
|
131
|
+
inFlight.clear();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region \0clutch-dsh-worktree-css:/Users/yuancheng/Documents/Code/clutch-dsh/packages/clutch-dsh-worktree/src/client/worktree.css.mjs
|
|
137
|
+
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 display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-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: 16px;\n margin-right: 2px;\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-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 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-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";
|
|
138
|
+
const styleId = "@cerbur/clutch-dsh-worktree/worktree.css";
|
|
139
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree.css\"]") === null) {
|
|
140
|
+
const tag = document.createElement("style");
|
|
141
|
+
tag.dataset.plugin = "@cerbur/clutch-dsh-worktree";
|
|
142
|
+
tag.dataset.pluginCss = styleId;
|
|
143
|
+
tag.textContent = css;
|
|
144
|
+
document.head.appendChild(tag);
|
|
145
|
+
}
|
|
146
|
+
var worktree_css_default = {
|
|
147
|
+
"action": "-cerbur-clutch-dsh-worktree-action",
|
|
148
|
+
"actionLabel": "-cerbur-clutch-dsh-worktree-actionLabel",
|
|
149
|
+
"surface": "-cerbur-clutch-dsh-worktree-surface",
|
|
150
|
+
"wideContent": "-cerbur-clutch-dsh-worktree-wideContent",
|
|
151
|
+
"railContent": "-cerbur-clutch-dsh-worktree-railContent",
|
|
152
|
+
"railButton": "-cerbur-clutch-dsh-worktree-railButton",
|
|
153
|
+
"closeButton": "-cerbur-clutch-dsh-worktree-closeButton",
|
|
154
|
+
"header": "-cerbur-clutch-dsh-worktree-header",
|
|
155
|
+
"title": "-cerbur-clutch-dsh-worktree-title",
|
|
156
|
+
"modeButton": "-cerbur-clutch-dsh-worktree-modeButton",
|
|
157
|
+
"workspacePicker": "-cerbur-clutch-dsh-worktree-workspacePicker",
|
|
158
|
+
"workspaceLabel": "-cerbur-clutch-dsh-worktree-workspaceLabel",
|
|
159
|
+
"content": "-cerbur-clutch-dsh-worktree-content",
|
|
160
|
+
"error": "-cerbur-clutch-dsh-worktree-error",
|
|
161
|
+
"retryButton": "-cerbur-clutch-dsh-worktree-retryButton",
|
|
162
|
+
"actionButton": "-cerbur-clutch-dsh-worktree-actionButton",
|
|
163
|
+
"recoveryActions": "-cerbur-clutch-dsh-worktree-recoveryActions",
|
|
164
|
+
"actions": "-cerbur-clutch-dsh-worktree-actions",
|
|
165
|
+
"actionSelect": "-cerbur-clutch-dsh-worktree-actionSelect",
|
|
166
|
+
"section": "-cerbur-clutch-dsh-worktree-section",
|
|
167
|
+
"sectionTitle": "-cerbur-clutch-dsh-worktree-sectionTitle",
|
|
168
|
+
"empty": "-cerbur-clutch-dsh-worktree-empty",
|
|
169
|
+
"message": "-cerbur-clutch-dsh-worktree-message",
|
|
170
|
+
"worktree": "-cerbur-clutch-dsh-worktree-worktree",
|
|
171
|
+
"session": "-cerbur-clutch-dsh-worktree-session",
|
|
172
|
+
"worktreeLabel": "-cerbur-clutch-dsh-worktree-worktreeLabel",
|
|
173
|
+
"sessionLabel": "-cerbur-clutch-dsh-worktree-sessionLabel",
|
|
174
|
+
"status": "-cerbur-clutch-dsh-worktree-status",
|
|
175
|
+
"searchRow": "-cerbur-clutch-dsh-worktree-searchRow",
|
|
176
|
+
"searchIcon": "-cerbur-clutch-dsh-worktree-searchIcon",
|
|
177
|
+
"searchInput": "-cerbur-clutch-dsh-worktree-searchInput",
|
|
178
|
+
"iconButton": "-cerbur-clutch-dsh-worktree-iconButton",
|
|
179
|
+
"workspaceList": "-cerbur-clutch-dsh-worktree-workspaceList",
|
|
180
|
+
"workspaceGroup": "-cerbur-clutch-dsh-worktree-workspaceGroup",
|
|
181
|
+
"workspaceRow": "-cerbur-clutch-dsh-worktree-workspaceRow",
|
|
182
|
+
"worktreeRow": "-cerbur-clutch-dsh-worktree-worktreeRow",
|
|
183
|
+
"workspaceDisclosure": "-cerbur-clutch-dsh-worktree-workspaceDisclosure",
|
|
184
|
+
"workspaceIcon": "-cerbur-clutch-dsh-worktree-workspaceIcon",
|
|
185
|
+
"worktreeDisclosure": "-cerbur-clutch-dsh-worktree-worktreeDisclosure",
|
|
186
|
+
"worktreeIcon": "-cerbur-clutch-dsh-worktree-worktreeIcon",
|
|
187
|
+
"disclosureButton": "-cerbur-clutch-dsh-worktree-disclosureButton",
|
|
188
|
+
"worktreeState": "-cerbur-clutch-dsh-worktree-worktreeState",
|
|
189
|
+
"workspaceTitle": "-cerbur-clutch-dsh-worktree-workspaceTitle",
|
|
190
|
+
"treeActionSlot": "-cerbur-clutch-dsh-worktree-treeActionSlot",
|
|
191
|
+
"workspaceActions": "-cerbur-clutch-dsh-worktree-workspaceActions",
|
|
192
|
+
"menuAction": "-cerbur-clutch-dsh-worktree-menuAction",
|
|
193
|
+
"dropBefore": "-cerbur-clutch-dsh-worktree-dropBefore",
|
|
194
|
+
"dropAfter": "-cerbur-clutch-dsh-worktree-dropAfter",
|
|
195
|
+
"treeChildren": "-cerbur-clutch-dsh-worktree-treeChildren",
|
|
196
|
+
"groupLabel": "-cerbur-clutch-dsh-worktree-groupLabel",
|
|
197
|
+
"groupHeader": "-cerbur-clutch-dsh-worktree-groupHeader",
|
|
198
|
+
"worktreeGroup": "-cerbur-clutch-dsh-worktree-worktreeGroup",
|
|
199
|
+
"treeSessionRow": "-cerbur-clutch-dsh-worktree-treeSessionRow",
|
|
200
|
+
"treeSessionContent": "-cerbur-clutch-dsh-worktree-treeSessionContent",
|
|
201
|
+
"rowActions": "-cerbur-clutch-dsh-worktree-rowActions",
|
|
202
|
+
"sessionOverflowButton": "-cerbur-clutch-dsh-worktree-sessionOverflowButton",
|
|
203
|
+
"renameInput": "-cerbur-clutch-dsh-worktree-renameInput",
|
|
204
|
+
"renameError": "-cerbur-clutch-dsh-worktree-renameError",
|
|
205
|
+
"treeGuide": "-cerbur-clutch-dsh-worktree-treeGuide",
|
|
206
|
+
"emptyNested": "-cerbur-clutch-dsh-worktree-emptyNested",
|
|
207
|
+
"modalField": "-cerbur-clutch-dsh-worktree-modalField"
|
|
208
|
+
};
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region lib/client/WorktreeModeAction.js
|
|
211
|
+
/** Footer action that enters/exits the peer Worktree navigation mode. */
|
|
212
|
+
function WorktreeModeAction({ wide, useStore, actions, available }) {
|
|
213
|
+
if (!available) return null;
|
|
214
|
+
const active = useStore((state) => state.viewMode) === "worktree";
|
|
215
|
+
return (0, react_jsx_runtime.jsxs)("button", {
|
|
216
|
+
type: "button",
|
|
217
|
+
className: worktree_css_default.action,
|
|
218
|
+
"data-worktree-mode-action": true,
|
|
219
|
+
"data-active": active || void 0,
|
|
220
|
+
"data-collapsed": !wide || void 0,
|
|
221
|
+
"aria-label": active ? "Exit Worktree mode" : "Open Worktree mode",
|
|
222
|
+
"aria-pressed": active,
|
|
223
|
+
title: active ? "Exit Worktree mode" : "Worktree mode",
|
|
224
|
+
onClick: () => {
|
|
225
|
+
actions.setViewMode(active ? "workspace-session" : "worktree");
|
|
226
|
+
},
|
|
227
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, { size: wide ? 16 : 18 }), wide && (0, react_jsx_runtime.jsx)("span", {
|
|
228
|
+
className: worktree_css_default.actionLabel,
|
|
229
|
+
children: "Worktree"
|
|
230
|
+
})]
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region lib/client/navigation.js
|
|
235
|
+
/** Open an existing Session through DSH without touching the peer view mode. */
|
|
236
|
+
function openWorktreeSession(opener, sessionId) {
|
|
237
|
+
opener.open(sessionId);
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region lib/client/overlay-bounds.js
|
|
241
|
+
function computeOverlayBounds(frame, newSession, footer) {
|
|
242
|
+
if (newSession === void 0 || footer === void 0) return {
|
|
243
|
+
ready: false,
|
|
244
|
+
top: 0,
|
|
245
|
+
height: 0
|
|
246
|
+
};
|
|
247
|
+
const top = Math.max(0, newSession.top - frame.top);
|
|
248
|
+
return {
|
|
249
|
+
ready: true,
|
|
250
|
+
top,
|
|
251
|
+
height: Math.max(top, footer.top - frame.top) - top
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region lib/client/sidebar-overlay-geometry.js
|
|
256
|
+
const EMPTY_BOUNDS = {
|
|
257
|
+
ready: false,
|
|
258
|
+
top: 0,
|
|
259
|
+
height: 0
|
|
260
|
+
};
|
|
261
|
+
function asRect(element) {
|
|
262
|
+
const rect = element.getBoundingClientRect();
|
|
263
|
+
return {
|
|
264
|
+
top: rect.top,
|
|
265
|
+
bottom: rect.bottom
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function resolveNativeSidebarRoot(sidebar) {
|
|
269
|
+
const directChild = sidebar.firstElementChild;
|
|
270
|
+
if (directChild === null) return void 0;
|
|
271
|
+
const directRect = directChild.getBoundingClientRect();
|
|
272
|
+
if (directRect.width > 0 || directRect.height > 0) return directChild;
|
|
273
|
+
return directChild.firstElementChild ?? directChild;
|
|
274
|
+
}
|
|
275
|
+
function findNewSessionAnchor(root) {
|
|
276
|
+
const labels = [
|
|
277
|
+
"新建会话",
|
|
278
|
+
"New session",
|
|
279
|
+
"新会话",
|
|
280
|
+
"New Session"
|
|
281
|
+
];
|
|
282
|
+
const buttons = [...root.querySelectorAll("button")];
|
|
283
|
+
const visibleLabel = buttons.find((button) => {
|
|
284
|
+
const text = button.textContent?.trim();
|
|
285
|
+
return text === "新会话" || text === "New Session";
|
|
286
|
+
});
|
|
287
|
+
if (visibleLabel !== void 0) return visibleLabel;
|
|
288
|
+
for (const label of labels) {
|
|
289
|
+
const button = buttons.find((candidate) => candidate.getAttribute("aria-label") === label);
|
|
290
|
+
if (button !== void 0) return button;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function useSidebarOverlayGeometry(active) {
|
|
294
|
+
const ref = (0, react.useRef)(null);
|
|
295
|
+
const [width, setWidth] = (0, react.useState)(280);
|
|
296
|
+
const [bounds, setBounds] = (0, react.useState)(EMPTY_BOUNDS);
|
|
297
|
+
(0, react.useLayoutEffect)(() => {
|
|
298
|
+
if (!active) {
|
|
299
|
+
setBounds(EMPTY_BOUNDS);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const overlay = ref.current?.closest("[data-shell-overlay]");
|
|
303
|
+
const sidebar = (overlay?.parentElement)?.firstElementChild;
|
|
304
|
+
const nativeRoot = sidebar === null || sidebar === void 0 ? void 0 : resolveNativeSidebarRoot(sidebar);
|
|
305
|
+
if (!(overlay instanceof HTMLElement) || !(sidebar instanceof HTMLElement) || !(nativeRoot instanceof HTMLElement)) {
|
|
306
|
+
setBounds(EMPTY_BOUNDS);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
let frameHandle;
|
|
310
|
+
const update = () => {
|
|
311
|
+
const newSession = findNewSessionAnchor(nativeRoot);
|
|
312
|
+
const footer = nativeRoot.lastElementChild;
|
|
313
|
+
const nextWidth = sidebar.getBoundingClientRect().width;
|
|
314
|
+
if (nextWidth > 0) setWidth(nextWidth);
|
|
315
|
+
setBounds(computeOverlayBounds(asRect(overlay), newSession === void 0 ? void 0 : asRect(newSession), footer === null ? void 0 : asRect(footer)));
|
|
316
|
+
};
|
|
317
|
+
const scheduleUpdate = () => {
|
|
318
|
+
if (frameHandle !== void 0) return;
|
|
319
|
+
frameHandle = requestAnimationFrame(() => {
|
|
320
|
+
frameHandle = void 0;
|
|
321
|
+
update();
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
update();
|
|
325
|
+
const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(scheduleUpdate) : void 0;
|
|
326
|
+
for (const element of [
|
|
327
|
+
overlay,
|
|
328
|
+
sidebar,
|
|
329
|
+
nativeRoot
|
|
330
|
+
]) resizeObserver?.observe(element);
|
|
331
|
+
const newSession = findNewSessionAnchor(nativeRoot);
|
|
332
|
+
const footer = nativeRoot.lastElementChild;
|
|
333
|
+
if (newSession !== void 0) resizeObserver?.observe(newSession);
|
|
334
|
+
if (footer !== null) resizeObserver?.observe(footer);
|
|
335
|
+
const mutationObserver = typeof MutationObserver === "function" ? new MutationObserver(scheduleUpdate) : void 0;
|
|
336
|
+
mutationObserver?.observe(nativeRoot, {
|
|
337
|
+
childList: true,
|
|
338
|
+
subtree: true
|
|
339
|
+
});
|
|
340
|
+
return () => {
|
|
341
|
+
resizeObserver?.disconnect();
|
|
342
|
+
mutationObserver?.disconnect();
|
|
343
|
+
if (frameHandle !== void 0) cancelAnimationFrame(frameHandle);
|
|
344
|
+
};
|
|
345
|
+
}, [active]);
|
|
346
|
+
return {
|
|
347
|
+
ref,
|
|
348
|
+
width,
|
|
349
|
+
bounds
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region lib/client/view-mode.js
|
|
354
|
+
/** Browser-local storage key; it is never sent to DSH or the sidecar. */
|
|
355
|
+
const WORKTREE_VIEW_MODE_STORAGE_KEY = "clutch-dsh-worktree.view-mode";
|
|
356
|
+
/** Guard persisted values before they become a visible mode. */
|
|
357
|
+
function isWorktreeViewMode(value) {
|
|
358
|
+
return value === "workspace-session" || value === "worktree";
|
|
359
|
+
}
|
|
360
|
+
/** Degraded/unavailable plugin state always exposes the original DSH view. */
|
|
361
|
+
function effectiveViewMode(preferred, worktreeServiceAvailable) {
|
|
362
|
+
if (!worktreeServiceAvailable || !isWorktreeViewMode(preferred)) return "workspace-session";
|
|
363
|
+
return preferred;
|
|
364
|
+
}
|
|
365
|
+
/** Keep the Main bucket scoped to the selected DSH Workspace before filtering bindings. */
|
|
366
|
+
function workspaceSessionIds(workspaces, workspaceId, sessionIds) {
|
|
367
|
+
if (workspaceId === void 0) return [];
|
|
368
|
+
const workspace = workspaces.items.find((candidate) => candidate.workspaceId === workspaceId);
|
|
369
|
+
if (workspace === void 0) return [];
|
|
370
|
+
const listed = new Set(sessionIds);
|
|
371
|
+
return workspace.sessionIds.filter((sessionId) => listed.has(sessionId));
|
|
372
|
+
}
|
|
373
|
+
function unboundSessionIds(sessionIds, boundSessionIds) {
|
|
374
|
+
const bound = new Set(boundSessionIds);
|
|
375
|
+
return sessionIds.filter((sessionId) => !bound.has(sessionId));
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Apply a browser-only membership delta to a DSH Workspace list snapshot.
|
|
379
|
+
* `previousBindings` are removed first so the same function can also undo a
|
|
380
|
+
* projection after a binding is deleted or the plugin is disposed.
|
|
381
|
+
*/
|
|
382
|
+
function projectVirtualWorkspaceMembership(snapshot, previousBindings, nextBindings) {
|
|
383
|
+
const previousSessionIds = new Set(previousBindings.map((binding) => binding.sessionId));
|
|
384
|
+
const nextByWorkspace = /* @__PURE__ */ new Map();
|
|
385
|
+
for (const binding of nextBindings) {
|
|
386
|
+
const sessionIds = nextByWorkspace.get(binding.workspaceId) ?? [];
|
|
387
|
+
if (!sessionIds.includes(binding.sessionId)) sessionIds.push(binding.sessionId);
|
|
388
|
+
nextByWorkspace.set(binding.workspaceId, sessionIds);
|
|
389
|
+
}
|
|
390
|
+
const items = snapshot.items.map((workspace) => {
|
|
391
|
+
const nativeSessionIds = workspace.sessionIds.filter((sessionId) => !previousSessionIds.has(sessionId));
|
|
392
|
+
const virtualSessionIds = nextByWorkspace.get(workspace.workspaceId) ?? [];
|
|
393
|
+
const sessionIds = [...nativeSessionIds, ...virtualSessionIds.filter((sessionId) => !nativeSessionIds.includes(sessionId))];
|
|
394
|
+
if (sessionIds.length === workspace.sessionIds.length && sessionIds.every((sessionId, index) => sessionId === workspace.sessionIds[index])) return workspace;
|
|
395
|
+
return {
|
|
396
|
+
...workspace,
|
|
397
|
+
sessionIds
|
|
398
|
+
};
|
|
399
|
+
});
|
|
400
|
+
if (items.every((workspace, index) => workspace === snapshot.items[index])) return snapshot;
|
|
401
|
+
return {
|
|
402
|
+
...snapshot,
|
|
403
|
+
items
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region lib/client/worktree-view.js
|
|
408
|
+
/** A DSH Session exists even when the external Worktree binding needs repair. */
|
|
409
|
+
var WorktreeSessionBindingError = class extends Error {
|
|
410
|
+
code = "SESSION_BINDING_FAILED";
|
|
411
|
+
retryable = true;
|
|
412
|
+
sessionId;
|
|
413
|
+
cause;
|
|
414
|
+
constructor(sessionId, cause) {
|
|
415
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
416
|
+
super(`Session ${sessionId} was created, but Worktree binding failed: ${reason}`);
|
|
417
|
+
this.name = "WorktreeSessionBindingError";
|
|
418
|
+
this.sessionId = sessionId;
|
|
419
|
+
this.cause = cause;
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
/** Reuse the previous ID snapshot when DSH republishes equivalent Workspace items. */
|
|
423
|
+
function stableWorkspaceIds(previous, next) {
|
|
424
|
+
if (previous.length !== next.length) return next;
|
|
425
|
+
for (let index = 0; index < previous.length; index += 1) if (previous[index] !== next[index]) return next;
|
|
426
|
+
return previous;
|
|
427
|
+
}
|
|
428
|
+
/** Prefer the branch checked out by the DSH Workspace, then fall back to any local branch. */
|
|
429
|
+
function selectDefaultBaseBranch(branches) {
|
|
430
|
+
return branches.find((branch) => branch.isCurrent)?.name ?? branches[0]?.name ?? "";
|
|
431
|
+
}
|
|
432
|
+
function browserRandomUuid() {
|
|
433
|
+
const randomUUID = globalThis.crypto?.randomUUID;
|
|
434
|
+
if (typeof randomUUID === "function") return randomUUID.call(globalThis.crypto);
|
|
435
|
+
return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;
|
|
436
|
+
}
|
|
437
|
+
/** Generate a short, editable default branch name without colliding with known local names. */
|
|
438
|
+
function createDefaultWorktreeName(existingNames, randomUuid = browserRandomUuid) {
|
|
439
|
+
const usedNames = new Set([...existingNames].map((name) => name.trim()));
|
|
440
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
441
|
+
const suffix = randomUuid().replace(/[^a-z0-9]/gi, "").slice(0, 8).toLowerCase();
|
|
442
|
+
if (suffix.length < 8) continue;
|
|
443
|
+
const candidate = `dsh/${suffix}`;
|
|
444
|
+
if (!usedNames.has(candidate)) return candidate;
|
|
445
|
+
}
|
|
446
|
+
throw new Error("Unable to generate an available default Worktree name.");
|
|
447
|
+
}
|
|
448
|
+
/** Hide DSH-archived Sessions from the browser-local Worktree projection. */
|
|
449
|
+
function filterArchivedSessionIds(sessionIds, archivedSessionIds) {
|
|
450
|
+
return sessionIds.filter((sessionId) => !archivedSessionIds.includes(sessionId));
|
|
451
|
+
}
|
|
452
|
+
/** Read all three Worktree projections needed by the surface in one refresh. */
|
|
453
|
+
async function loadWorktreeView(manager, workspaceId) {
|
|
454
|
+
const [worktrees, branches, bindings] = await Promise.all([
|
|
455
|
+
manager.listWorktrees({ workspaceId }),
|
|
456
|
+
manager.listBranches({ workspaceId }),
|
|
457
|
+
manager.listBindings({ workspaceId })
|
|
458
|
+
]);
|
|
459
|
+
return {
|
|
460
|
+
worktrees,
|
|
461
|
+
branches,
|
|
462
|
+
bindings
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
/** Load one independent projection per DSH Workspace for the flat sidebar hierarchy. */
|
|
466
|
+
async function loadWorktreeViews(manager, workspaceIds) {
|
|
467
|
+
return Promise.all(workspaceIds.map(async (workspaceId) => ({
|
|
468
|
+
workspaceId,
|
|
469
|
+
...await loadWorktreeView(manager, workspaceId)
|
|
470
|
+
})));
|
|
471
|
+
}
|
|
472
|
+
/** Keep mutation routing in the browser Consumer while leaving wire details to the adapter. */
|
|
473
|
+
async function executeWorktreeAction(manager, action) {
|
|
474
|
+
if (action.type === "createWorktree") return manager.createWorktree(action.input);
|
|
475
|
+
if (action.type === "removeWorktree") {
|
|
476
|
+
await manager.removeWorktree(action.input);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Create a normal DSH Session at a Worktree cwd, then add the external binding.
|
|
482
|
+
* A binding failure deliberately leaves the DSH-created Session intact.
|
|
483
|
+
*/
|
|
484
|
+
async function createSessionForWorktree(input) {
|
|
485
|
+
const sessionId = await input.createSession({ cwd: input.cwd });
|
|
486
|
+
try {
|
|
487
|
+
await input.manager.bindSession({
|
|
488
|
+
workspaceId: input.workspaceId,
|
|
489
|
+
worktreeId: input.worktreeId,
|
|
490
|
+
sessionId
|
|
491
|
+
});
|
|
492
|
+
} catch (error) {
|
|
493
|
+
throw new WorktreeSessionBindingError(sessionId, error);
|
|
494
|
+
}
|
|
495
|
+
input.beforeOpen?.(sessionId);
|
|
496
|
+
input.openSession(sessionId);
|
|
497
|
+
return sessionId;
|
|
498
|
+
}
|
|
499
|
+
/** Convert any adapter/Gateway failure into renderable, retry-aware UI data. */
|
|
500
|
+
function toWorktreeViewError(error) {
|
|
501
|
+
if (typeof error === "object" && error !== null) {
|
|
502
|
+
const candidate = error;
|
|
503
|
+
return {
|
|
504
|
+
code: typeof candidate.code === "string" ? candidate.code : "WORKTREE_VIEW_FAILED",
|
|
505
|
+
message: typeof candidate.message === "string" ? candidate.message : "Worktree data is unavailable. Retry the request.",
|
|
506
|
+
retryable: candidate.retryable !== false
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
code: "WORKTREE_VIEW_FAILED",
|
|
511
|
+
message: "Worktree data is unavailable. Retry the request.",
|
|
512
|
+
retryable: true
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region lib/client/WorktreeSurface.js
|
|
517
|
+
const EMPTY_READ_STATE = {
|
|
518
|
+
status: "idle",
|
|
519
|
+
views: []
|
|
520
|
+
};
|
|
521
|
+
function useStableWorkspaceIds(workspaces) {
|
|
522
|
+
const next = workspaces.map((workspace) => workspace.workspaceId);
|
|
523
|
+
const previousRef = (0, react.useRef)([]);
|
|
524
|
+
const stable = stableWorkspaceIds(previousRef.current, next);
|
|
525
|
+
if (stable !== previousRef.current) previousRef.current = stable;
|
|
526
|
+
return previousRef.current;
|
|
527
|
+
}
|
|
528
|
+
function sessionLabel(sessionId, sessions) {
|
|
529
|
+
return sessions.byId[sessionId]?.displayTitle ?? sessionId;
|
|
530
|
+
}
|
|
531
|
+
function rowHalf(event) {
|
|
532
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
533
|
+
return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
534
|
+
}
|
|
535
|
+
function bindingIdsFor(bindings, worktreeId) {
|
|
536
|
+
return bindings.filter((binding) => binding.worktreeId === worktreeId).map((binding) => binding.sessionId);
|
|
537
|
+
}
|
|
538
|
+
function includesText(value, query) {
|
|
539
|
+
return value.toLocaleLowerCase().includes(query);
|
|
540
|
+
}
|
|
541
|
+
function workspaceMatches(workspace, view, sessions, query) {
|
|
542
|
+
if (query.length === 0) return true;
|
|
543
|
+
if (includesText(workspace.title, query)) return true;
|
|
544
|
+
if (workspace.sessionIds.some((sessionId) => includesText(sessionLabel(sessionId, sessions), query))) return true;
|
|
545
|
+
if (view === void 0) return false;
|
|
546
|
+
if (view.worktrees.some((record) => includesText(record.branch, query) || includesText(record.absolutePath, query))) return true;
|
|
547
|
+
return view.bindings.some((binding) => includesText(sessionLabel(binding.sessionId, sessions), query));
|
|
548
|
+
}
|
|
549
|
+
/** Workspace row using the native DSH menu, fixed action column, and drag contract. */
|
|
550
|
+
function WorktreeWorkspaceRow({ workspace, expanded, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange }) {
|
|
551
|
+
const workspaceMenuItems = [{
|
|
552
|
+
id: "rename",
|
|
553
|
+
label: "Rename",
|
|
554
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {}),
|
|
555
|
+
disabled: actionPending
|
|
556
|
+
}, {
|
|
557
|
+
id: "delete",
|
|
558
|
+
label: "Delete",
|
|
559
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}),
|
|
560
|
+
danger: true,
|
|
561
|
+
disabled: actionPending
|
|
562
|
+
}];
|
|
563
|
+
const markerClass = drag.marker === "before" ? worktree_css_default.dropBefore : drag.marker === "after" ? worktree_css_default.dropAfter : "";
|
|
564
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
565
|
+
className: `${worktree_css_default.workspaceRow} ${markerClass}`,
|
|
566
|
+
"data-workspace-drag": drag.active ? "active" : void 0,
|
|
567
|
+
"data-menu-open": menuOpen || void 0,
|
|
568
|
+
draggable: true,
|
|
569
|
+
onClick: () => {
|
|
570
|
+
onToggle();
|
|
571
|
+
},
|
|
572
|
+
onDragStart: (event) => {
|
|
573
|
+
event.dataTransfer.effectAllowed = "move";
|
|
574
|
+
event.dataTransfer.setData("text/plain", workspace.workspaceId);
|
|
575
|
+
drag.start();
|
|
576
|
+
},
|
|
577
|
+
onDragEnd: drag.end,
|
|
578
|
+
onDragOver: (event) => {
|
|
579
|
+
if (!drag.active) return;
|
|
580
|
+
event.preventDefault();
|
|
581
|
+
event.dataTransfer.dropEffect = "move";
|
|
582
|
+
drag.hover(rowHalf(event));
|
|
583
|
+
},
|
|
584
|
+
onDrop: (event) => {
|
|
585
|
+
if (!drag.active) return;
|
|
586
|
+
event.preventDefault();
|
|
587
|
+
drag.drop(rowHalf(event));
|
|
588
|
+
},
|
|
589
|
+
children: [
|
|
590
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
591
|
+
type: "button",
|
|
592
|
+
className: `${worktree_css_default.disclosureButton} ${worktree_css_default.workspaceDisclosure}`,
|
|
593
|
+
"aria-label": `${expanded ? "Collapse" : "Expand"} ${workspace.title}`,
|
|
594
|
+
"aria-expanded": expanded,
|
|
595
|
+
onClick: (event) => {
|
|
596
|
+
event.stopPropagation();
|
|
597
|
+
onToggle();
|
|
598
|
+
},
|
|
599
|
+
children: expanded ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 18 }) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { size: 18 })
|
|
600
|
+
}),
|
|
601
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
602
|
+
className: worktree_css_default.workspaceIcon,
|
|
603
|
+
"aria-hidden": "true",
|
|
604
|
+
children: expanded ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpen16, {}) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderClose16, {})
|
|
605
|
+
}),
|
|
606
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
607
|
+
className: worktree_css_default.workspaceTitle,
|
|
608
|
+
children: workspace.title
|
|
609
|
+
}),
|
|
610
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
611
|
+
className: `${worktree_css_default.treeActionSlot} ${worktree_css_default.workspaceActions}`,
|
|
612
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
613
|
+
className: worktree_css_default.menuAction,
|
|
614
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
615
|
+
open: menuOpen,
|
|
616
|
+
onClose: () => {
|
|
617
|
+
onMenuOpenChange(false);
|
|
618
|
+
},
|
|
619
|
+
items: workspaceMenuItems,
|
|
620
|
+
onSelect: (id) => {
|
|
621
|
+
onMenuOpenChange(false);
|
|
622
|
+
if (id === "rename") onRename();
|
|
623
|
+
if (id === "delete") onDelete();
|
|
624
|
+
},
|
|
625
|
+
portal: true,
|
|
626
|
+
closeOnPointerLeave: true,
|
|
627
|
+
anchor: (0, react_jsx_runtime.jsx)("button", {
|
|
628
|
+
type: "button",
|
|
629
|
+
className: worktree_css_default.iconButton,
|
|
630
|
+
"data-workspace-menu": true,
|
|
631
|
+
"aria-label": `Workspace options for ${workspace.title}`,
|
|
632
|
+
onClick: (event) => {
|
|
633
|
+
event.stopPropagation();
|
|
634
|
+
onMenuOpenChange(!menuOpen);
|
|
635
|
+
},
|
|
636
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
|
|
637
|
+
})
|
|
638
|
+
})
|
|
639
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
640
|
+
type: "button",
|
|
641
|
+
className: worktree_css_default.iconButton,
|
|
642
|
+
"data-add-worktree": true,
|
|
643
|
+
"aria-label": `Add Worktree to ${workspace.title}`,
|
|
644
|
+
onClick: (event) => {
|
|
645
|
+
event.stopPropagation();
|
|
646
|
+
onCreateWorktree();
|
|
647
|
+
},
|
|
648
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
|
|
649
|
+
})]
|
|
650
|
+
})
|
|
651
|
+
]
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
/** Shared Main/Worktree group row with parameterized icon, state, and actions. */
|
|
655
|
+
function WorktreeGroupRow({ kind, label, expanded, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession }) {
|
|
656
|
+
const main = kind === "main";
|
|
657
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
658
|
+
className: worktree_css_default.worktreeRow,
|
|
659
|
+
"data-main-group": main ? "true" : void 0,
|
|
660
|
+
"data-main-expanded": main ? String(expanded) : void 0,
|
|
661
|
+
onClick: onToggle,
|
|
662
|
+
children: [
|
|
663
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
664
|
+
type: "button",
|
|
665
|
+
className: `${worktree_css_default.disclosureButton} ${worktree_css_default.worktreeDisclosure}`,
|
|
666
|
+
"aria-label": `${expanded ? "Collapse" : "Expand"} ${label}`,
|
|
667
|
+
"aria-expanded": expanded,
|
|
668
|
+
onClick: (event) => {
|
|
669
|
+
event.stopPropagation();
|
|
670
|
+
onToggle();
|
|
671
|
+
},
|
|
672
|
+
children: expanded ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 18 }) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { size: 18 })
|
|
673
|
+
}),
|
|
674
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
675
|
+
className: worktree_css_default.worktreeIcon,
|
|
676
|
+
"aria-hidden": "true",
|
|
677
|
+
children: icon
|
|
678
|
+
}),
|
|
679
|
+
state !== void 0 && stateLabel !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
680
|
+
className: worktree_css_default.worktreeState,
|
|
681
|
+
role: "img",
|
|
682
|
+
"aria-label": stateLabel,
|
|
683
|
+
title: stateLabel,
|
|
684
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state })
|
|
685
|
+
}),
|
|
686
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
687
|
+
className: worktree_css_default.worktreeLabel,
|
|
688
|
+
children: label
|
|
689
|
+
}),
|
|
690
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
691
|
+
className: worktree_css_default.treeActionSlot,
|
|
692
|
+
children: onCreateSession !== void 0 && (0, react_jsx_runtime.jsx)("button", {
|
|
693
|
+
type: "button",
|
|
694
|
+
className: worktree_css_default.iconButton,
|
|
695
|
+
"data-add-main-session": main ? "true" : void 0,
|
|
696
|
+
"data-add-session": main ? void 0 : "true",
|
|
697
|
+
"aria-label": `Add Session to ${workspaceTitle}`,
|
|
698
|
+
onClick: (event) => {
|
|
699
|
+
event.stopPropagation();
|
|
700
|
+
onCreateSession();
|
|
701
|
+
},
|
|
702
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
|
|
703
|
+
})
|
|
704
|
+
})
|
|
705
|
+
]
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
/** Worktree-mode Session row with the same trailing options menu as native DSH rows. */
|
|
709
|
+
function WorktreeSessionRow({ sessionId, label, drag, actionPending, onOpen, onRename, onFork, onArchive }) {
|
|
710
|
+
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
711
|
+
const markerClass = drag.marker === "before" ? worktree_css_default.dropBefore : drag.marker === "after" ? worktree_css_default.dropAfter : "";
|
|
712
|
+
const sessionMenuItems = [
|
|
713
|
+
{
|
|
714
|
+
id: "rename",
|
|
715
|
+
label: "Rename",
|
|
716
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {}),
|
|
717
|
+
disabled: onRename === void 0 || actionPending
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
id: "fork",
|
|
721
|
+
label: "Fork session",
|
|
722
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
|
|
723
|
+
disabled: onFork === void 0 || actionPending
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
id: "archive",
|
|
727
|
+
label: "Archive session",
|
|
728
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutline20, { size: 16 }),
|
|
729
|
+
disabled: onArchive === void 0 || actionPending
|
|
730
|
+
}
|
|
731
|
+
];
|
|
732
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
733
|
+
className: `${worktree_css_default.treeSessionRow} ${markerClass}`,
|
|
734
|
+
"data-session-id": sessionId,
|
|
735
|
+
"data-session-drag": drag.active ? "active" : void 0,
|
|
736
|
+
"data-menu-open": menuOpen || void 0,
|
|
737
|
+
role: "treeitem",
|
|
738
|
+
draggable: true,
|
|
739
|
+
onDragStart: (event) => {
|
|
740
|
+
event.dataTransfer.effectAllowed = "move";
|
|
741
|
+
event.dataTransfer.setData("text/plain", sessionId);
|
|
742
|
+
drag.start();
|
|
743
|
+
},
|
|
744
|
+
onDragEnd: drag.end,
|
|
745
|
+
onDragOver: (event) => {
|
|
746
|
+
if (!drag.active) return;
|
|
747
|
+
event.preventDefault();
|
|
748
|
+
event.dataTransfer.dropEffect = "move";
|
|
749
|
+
drag.hover(rowHalf(event));
|
|
750
|
+
},
|
|
751
|
+
onDrop: (event) => {
|
|
752
|
+
if (!drag.active) return;
|
|
753
|
+
event.preventDefault();
|
|
754
|
+
drag.drop(rowHalf(event));
|
|
755
|
+
},
|
|
756
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
757
|
+
type: "button",
|
|
758
|
+
className: worktree_css_default.treeSessionContent,
|
|
759
|
+
onClick: onOpen,
|
|
760
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
761
|
+
className: worktree_css_default.treeGuide,
|
|
762
|
+
"aria-hidden": "true",
|
|
763
|
+
children: "└"
|
|
764
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
765
|
+
className: worktree_css_default.sessionLabel,
|
|
766
|
+
children: label
|
|
767
|
+
})]
|
|
768
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
769
|
+
className: worktree_css_default.rowActions,
|
|
770
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
771
|
+
open: menuOpen,
|
|
772
|
+
onClose: () => {
|
|
773
|
+
setMenuOpen(false);
|
|
774
|
+
},
|
|
775
|
+
items: sessionMenuItems,
|
|
776
|
+
onSelect: (id) => {
|
|
777
|
+
setMenuOpen(false);
|
|
778
|
+
if (id === "rename") onRename?.(sessionId, label);
|
|
779
|
+
if (id === "fork") onFork?.(sessionId);
|
|
780
|
+
if (id === "archive") onArchive?.(sessionId);
|
|
781
|
+
},
|
|
782
|
+
portal: true,
|
|
783
|
+
closeOnPointerLeave: true,
|
|
784
|
+
anchor: (0, react_jsx_runtime.jsx)("button", {
|
|
785
|
+
type: "button",
|
|
786
|
+
className: worktree_css_default.iconButton,
|
|
787
|
+
"data-session-menu": true,
|
|
788
|
+
"aria-label": `Session options for ${label}`,
|
|
789
|
+
onClick: (event) => {
|
|
790
|
+
event.stopPropagation();
|
|
791
|
+
setMenuOpen((current) => !current);
|
|
792
|
+
},
|
|
793
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
|
|
794
|
+
})
|
|
795
|
+
})
|
|
796
|
+
})]
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
function WorktreeSessionGroup({ groupKey, sessionIds, workspaceId, expanded, actionPending, sessions, dragState, onToggleExpanded, onStartDrag, onHoverDrag, onClearDrag, onFinishDrag, onCommitDrag, onOpen, onRename, onFork, onArchive }) {
|
|
800
|
+
const visibleSessionIds = expanded ? sessionIds : sessionIds.slice(0, 5);
|
|
801
|
+
const sameGroupDrag = dragState?.groupKey === groupKey;
|
|
802
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [visibleSessionIds.map((sessionId) => (0, react_jsx_runtime.jsx)(WorktreeSessionRow, {
|
|
803
|
+
sessionId,
|
|
804
|
+
label: sessionLabel(sessionId, sessions),
|
|
805
|
+
drag: {
|
|
806
|
+
active: sameGroupDrag,
|
|
807
|
+
marker: sameGroupDrag && dragState.over?.sessionId === sessionId ? dragState.over.half : null,
|
|
808
|
+
start: () => {
|
|
809
|
+
onStartDrag(groupKey, sessionId);
|
|
810
|
+
},
|
|
811
|
+
hover: (half) => {
|
|
812
|
+
onHoverDrag(sessionId, half);
|
|
813
|
+
},
|
|
814
|
+
drop: (half) => {
|
|
815
|
+
if (dragState === void 0) return;
|
|
816
|
+
onCommitDrag(dragState, {
|
|
817
|
+
sessionId,
|
|
818
|
+
half
|
|
819
|
+
}, sessionIds, workspaceId);
|
|
820
|
+
},
|
|
821
|
+
end: () => {
|
|
822
|
+
if (dragState?.over !== null && dragState?.over !== void 0) onCommitDrag(dragState, dragState.over, sessionIds, workspaceId);
|
|
823
|
+
else onClearDrag();
|
|
824
|
+
onFinishDrag();
|
|
825
|
+
}
|
|
826
|
+
},
|
|
827
|
+
actionPending,
|
|
828
|
+
onOpen: () => {
|
|
829
|
+
onOpen(sessionId);
|
|
830
|
+
},
|
|
831
|
+
onRename,
|
|
832
|
+
onFork,
|
|
833
|
+
onArchive
|
|
834
|
+
}, `${groupKey}:${sessionId}`)), sessionIds.length > 5 && (0, react_jsx_runtime.jsx)("button", {
|
|
835
|
+
type: "button",
|
|
836
|
+
className: worktree_css_default.sessionOverflowButton,
|
|
837
|
+
"aria-expanded": expanded,
|
|
838
|
+
onClick: onToggleExpanded,
|
|
839
|
+
children: expanded ? "Collapse" : `Expand ${sessionIds.length - 5} more`
|
|
840
|
+
})] });
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Peer Worktree navigation surface. It keeps the original DSH Workspace mode
|
|
844
|
+
* intact and renders a browser-local Workspace → Worktree → Session projection.
|
|
845
|
+
*/
|
|
846
|
+
function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, available, manager, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession }) {
|
|
847
|
+
const mode = effectiveViewMode(useStore((state) => state.viewMode), available && manager !== void 0);
|
|
848
|
+
const sessions = useSessions((state) => state);
|
|
849
|
+
const workspaces = useWorkspaces((state) => state);
|
|
850
|
+
const workspaceIds = useStableWorkspaceIds(workspaces.items);
|
|
851
|
+
const [readState, setReadState] = (0, react.useState)(EMPTY_READ_STATE);
|
|
852
|
+
const [searchQuery, setSearchQuery] = (0, react.useState)("");
|
|
853
|
+
const [expandedWorkspaces, setExpandedWorkspaces] = (0, react.useState)({});
|
|
854
|
+
const [expandedMains, setExpandedMains] = (0, react.useState)({});
|
|
855
|
+
const [expandedWorktrees, setExpandedWorktrees] = (0, react.useState)({});
|
|
856
|
+
const [worktreeModalWorkspaceId, setWorktreeModalWorkspaceId] = (0, react.useState)();
|
|
857
|
+
const [openWorkspaceMenuId, setOpenWorkspaceMenuId] = (0, react.useState)();
|
|
858
|
+
const [selectedBranch, setSelectedBranch] = (0, react.useState)("");
|
|
859
|
+
const [newBranch, setNewBranch] = (0, react.useState)("");
|
|
860
|
+
const [pendingSessionBinding, setPendingSessionBinding] = (0, react.useState)();
|
|
861
|
+
const [actionError, setActionError] = (0, react.useState)();
|
|
862
|
+
const [actionPending, setActionPending] = (0, react.useState)(false);
|
|
863
|
+
const [sessionRenameTarget, setSessionRenameTarget] = (0, react.useState)();
|
|
864
|
+
const [sessionRenameDraft, setSessionRenameDraft] = (0, react.useState)("");
|
|
865
|
+
const [sessionRenamePending, setSessionRenamePending] = (0, react.useState)(false);
|
|
866
|
+
const [sessionRenameError, setSessionRenameError] = (0, react.useState)();
|
|
867
|
+
const [workspaceDrag, setWorkspaceDrag] = (0, react.useState)();
|
|
868
|
+
const [workspaceRenameTarget, setWorkspaceRenameTarget] = (0, react.useState)();
|
|
869
|
+
const [workspaceRenameDraft, setWorkspaceRenameDraft] = (0, react.useState)("");
|
|
870
|
+
const [workspaceRenamePending, setWorkspaceRenamePending] = (0, react.useState)(false);
|
|
871
|
+
const [workspaceRenameError, setWorkspaceRenameError] = (0, react.useState)();
|
|
872
|
+
const [workspaceDeleteTarget, setWorkspaceDeleteTarget] = (0, react.useState)();
|
|
873
|
+
const [workspaceDeletePending, setWorkspaceDeletePending] = (0, react.useState)(false);
|
|
874
|
+
const [workspaceDeleteError, setWorkspaceDeleteError] = (0, react.useState)();
|
|
875
|
+
const workspaceDropCommitted = (0, react.useRef)(false);
|
|
876
|
+
const [sessionDrag, setSessionDrag] = (0, react.useState)();
|
|
877
|
+
const [expandedSessionGroups, setExpandedSessionGroups] = (0, react.useState)({});
|
|
878
|
+
const sessionDropCommitted = (0, react.useRef)(false);
|
|
879
|
+
const { ref, width, bounds } = useSidebarOverlayGeometry(mode === "worktree");
|
|
880
|
+
const collapsed = width <= 64;
|
|
881
|
+
const query = searchQuery.trim().toLocaleLowerCase();
|
|
882
|
+
const archivedSessionIds = workspaces.archivedSessionIds ?? [];
|
|
883
|
+
const refresh = (0, react.useCallback)(async () => {
|
|
884
|
+
if (manager === void 0) {
|
|
885
|
+
setReadState(EMPTY_READ_STATE);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
setReadState({
|
|
889
|
+
status: "loading",
|
|
890
|
+
views: []
|
|
891
|
+
});
|
|
892
|
+
try {
|
|
893
|
+
const views = await loadWorktreeViews(manager, workspaceIds);
|
|
894
|
+
setReadState({
|
|
895
|
+
status: "ready",
|
|
896
|
+
views
|
|
897
|
+
});
|
|
898
|
+
} catch (error) {
|
|
899
|
+
setReadState({
|
|
900
|
+
status: "error",
|
|
901
|
+
views: [],
|
|
902
|
+
error: toWorktreeViewError(error)
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
}, [manager, workspaceIds]);
|
|
906
|
+
(0, react.useEffect)(() => {
|
|
907
|
+
if (mode === "worktree") refresh();
|
|
908
|
+
else setReadState(EMPTY_READ_STATE);
|
|
909
|
+
}, [mode, refresh]);
|
|
910
|
+
(0, react.useEffect)(() => {
|
|
911
|
+
if (readState.status !== "ready" || syncSessionWorkspaces === void 0) return;
|
|
912
|
+
syncSessionWorkspaces(readState.views.flatMap((view) => view.bindings.map((binding) => ({
|
|
913
|
+
workspaceId: binding.workspaceId,
|
|
914
|
+
sessionId: binding.sessionId
|
|
915
|
+
}))));
|
|
916
|
+
}, [
|
|
917
|
+
readState.status,
|
|
918
|
+
readState.views,
|
|
919
|
+
syncSessionWorkspaces
|
|
920
|
+
]);
|
|
921
|
+
const viewByWorkspace = (0, react.useMemo)(() => new Map(readState.views.map((view) => [view.workspaceId, view])), [readState.views]);
|
|
922
|
+
const modalWorkspace = workspaces.items.find((workspace) => workspace.workspaceId === worktreeModalWorkspaceId);
|
|
923
|
+
const modalView = worktreeModalWorkspaceId === void 0 ? void 0 : viewByWorkspace.get(worktreeModalWorkspaceId);
|
|
924
|
+
const runMutation = async (operation) => {
|
|
925
|
+
setActionPending(true);
|
|
926
|
+
setActionError(void 0);
|
|
927
|
+
try {
|
|
928
|
+
await operation();
|
|
929
|
+
await refresh();
|
|
930
|
+
} catch (error) {
|
|
931
|
+
setActionError(toWorktreeViewError(error));
|
|
932
|
+
} finally {
|
|
933
|
+
setActionPending(false);
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
const workspaceRenameTrimmed = workspaceRenameDraft.trim();
|
|
937
|
+
const workspaceRenameDuplicate = workspaceRenameTarget !== void 0 && workspaceRenameTrimmed.length > 0 && workspaces.items.some((workspace) => workspace.workspaceId !== workspaceRenameTarget.workspaceId && workspace.title === workspaceRenameTrimmed);
|
|
938
|
+
const workspaceRenameBlocked = workspaceRenamePending || workspaceRenameTarget === void 0 || workspaceRenameTrimmed.length === 0 || workspaceRenameTrimmed === workspaceRenameTarget?.currentTitle || workspaceRenameDuplicate;
|
|
939
|
+
const openWorkspaceRename = (workspace) => {
|
|
940
|
+
setWorkspaceRenameTarget({
|
|
941
|
+
workspaceId: workspace.workspaceId,
|
|
942
|
+
currentTitle: workspace.title
|
|
943
|
+
});
|
|
944
|
+
setWorkspaceRenameDraft(workspace.title);
|
|
945
|
+
setWorkspaceRenameError(void 0);
|
|
946
|
+
};
|
|
947
|
+
const closeWorkspaceRename = () => {
|
|
948
|
+
if (workspaceRenamePending) return;
|
|
949
|
+
setWorkspaceRenameTarget(void 0);
|
|
950
|
+
setWorkspaceRenameError(void 0);
|
|
951
|
+
};
|
|
952
|
+
const confirmWorkspaceRename = async () => {
|
|
953
|
+
const target = workspaceRenameTarget;
|
|
954
|
+
if (workspaceRenameBlocked || target === void 0) return;
|
|
955
|
+
if (renameWorkspace === void 0) {
|
|
956
|
+
setWorkspaceRenameError("Workspace rename is unavailable; retry after reconnecting.");
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
setWorkspaceRenamePending(true);
|
|
960
|
+
setWorkspaceRenameError(void 0);
|
|
961
|
+
try {
|
|
962
|
+
await renameWorkspace(target.workspaceId, workspaceRenameTrimmed);
|
|
963
|
+
setWorkspaceRenameTarget(void 0);
|
|
964
|
+
} catch (error) {
|
|
965
|
+
setWorkspaceRenameError(error instanceof Error ? error.message : String(error));
|
|
966
|
+
} finally {
|
|
967
|
+
setWorkspaceRenamePending(false);
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
const openWorkspaceDelete = (workspace) => {
|
|
971
|
+
setWorkspaceDeleteTarget({
|
|
972
|
+
workspaceId: workspace.workspaceId,
|
|
973
|
+
title: workspace.title
|
|
974
|
+
});
|
|
975
|
+
setWorkspaceDeleteError(void 0);
|
|
976
|
+
};
|
|
977
|
+
const closeWorkspaceDelete = () => {
|
|
978
|
+
if (workspaceDeletePending) return;
|
|
979
|
+
setWorkspaceDeleteTarget(void 0);
|
|
980
|
+
setWorkspaceDeleteError(void 0);
|
|
981
|
+
};
|
|
982
|
+
const confirmWorkspaceDelete = async () => {
|
|
983
|
+
const target = workspaceDeleteTarget;
|
|
984
|
+
if (target === void 0 || workspaceDeletePending) return;
|
|
985
|
+
if (deleteWorkspace === void 0) {
|
|
986
|
+
setWorkspaceDeleteError("Workspace delete is unavailable; retry after reconnecting.");
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
setWorkspaceDeletePending(true);
|
|
990
|
+
setWorkspaceDeleteError(void 0);
|
|
991
|
+
try {
|
|
992
|
+
await deleteWorkspace(target.workspaceId);
|
|
993
|
+
setWorkspaceDeleteTarget(void 0);
|
|
994
|
+
} catch (error) {
|
|
995
|
+
setWorkspaceDeleteError(error instanceof Error ? error.message : String(error));
|
|
996
|
+
} finally {
|
|
997
|
+
setWorkspaceDeletePending(false);
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
const commitWorkspaceDrag = (activeDrag, over) => {
|
|
1001
|
+
if (workspaceDropCommitted.current) return;
|
|
1002
|
+
workspaceDropCommitted.current = true;
|
|
1003
|
+
setWorkspaceDrag(void 0);
|
|
1004
|
+
const rows = workspaces.items;
|
|
1005
|
+
const targetIndex = rows.findIndex((workspace) => workspace.workspaceId === over.workspaceId);
|
|
1006
|
+
const sourceIndex = rows.findIndex((workspace) => workspace.workspaceId === activeDrag.workspaceId);
|
|
1007
|
+
if (targetIndex === -1 || sourceIndex === -1) return;
|
|
1008
|
+
const beforeWorkspaceId = over.half === "before" ? over.workspaceId : rows[targetIndex + 1]?.workspaceId;
|
|
1009
|
+
const anchorIndex = beforeWorkspaceId === void 0 ? rows.length : rows.findIndex((workspace) => workspace.workspaceId === beforeWorkspaceId);
|
|
1010
|
+
if (beforeWorkspaceId === activeDrag.workspaceId || anchorIndex === sourceIndex || anchorIndex === sourceIndex + 1) return;
|
|
1011
|
+
if (insertWorkspaceBefore === void 0) {
|
|
1012
|
+
setActionError({
|
|
1013
|
+
code: "WORKSPACE_ORDER_UNAVAILABLE",
|
|
1014
|
+
message: "Workspace ordering is unavailable; retry after reconnecting.",
|
|
1015
|
+
retryable: true
|
|
1016
|
+
});
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
setActionError(void 0);
|
|
1020
|
+
insertWorkspaceBefore(activeDrag.workspaceId, beforeWorkspaceId).catch((error) => {
|
|
1021
|
+
setActionError(toWorktreeViewError(error));
|
|
1022
|
+
});
|
|
1023
|
+
};
|
|
1024
|
+
const commitSessionDrag = (activeDrag, over, sessionIds, workspaceId) => {
|
|
1025
|
+
if (sessionDropCommitted.current) return;
|
|
1026
|
+
sessionDropCommitted.current = true;
|
|
1027
|
+
setSessionDrag(void 0);
|
|
1028
|
+
const targetIndex = sessionIds.indexOf(over.sessionId);
|
|
1029
|
+
const sourceIndex = sessionIds.indexOf(activeDrag.sessionId);
|
|
1030
|
+
if (targetIndex === -1 || sourceIndex === -1) return;
|
|
1031
|
+
const beforeSessionId = over.half === "before" ? over.sessionId : sessionIds[targetIndex + 1];
|
|
1032
|
+
const anchorIndex = beforeSessionId === void 0 ? sessionIds.length : sessionIds.indexOf(beforeSessionId);
|
|
1033
|
+
if (beforeSessionId === activeDrag.sessionId || anchorIndex === sourceIndex || anchorIndex === sourceIndex + 1) return;
|
|
1034
|
+
if (insertSessionBefore === void 0) {
|
|
1035
|
+
setActionError({
|
|
1036
|
+
code: "SESSION_ORDER_UNAVAILABLE",
|
|
1037
|
+
message: "Session ordering is unavailable; retry after reconnecting.",
|
|
1038
|
+
retryable: true
|
|
1039
|
+
});
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
setActionError(void 0);
|
|
1043
|
+
insertSessionBefore(workspaceId, activeDrag.sessionId, beforeSessionId).catch((error) => {
|
|
1044
|
+
setActionError(toWorktreeViewError(error));
|
|
1045
|
+
});
|
|
1046
|
+
};
|
|
1047
|
+
const openSessionRename = (sessionId, currentTitle) => {
|
|
1048
|
+
setSessionRenameTarget({
|
|
1049
|
+
sessionId,
|
|
1050
|
+
currentTitle
|
|
1051
|
+
});
|
|
1052
|
+
setSessionRenameDraft(currentTitle);
|
|
1053
|
+
setSessionRenameError(void 0);
|
|
1054
|
+
};
|
|
1055
|
+
const closeSessionRename = () => {
|
|
1056
|
+
if (sessionRenamePending) return;
|
|
1057
|
+
setSessionRenameTarget(void 0);
|
|
1058
|
+
setSessionRenameError(void 0);
|
|
1059
|
+
};
|
|
1060
|
+
const confirmSessionRename = async () => {
|
|
1061
|
+
const target = sessionRenameTarget;
|
|
1062
|
+
const title = sessionRenameDraft.trim();
|
|
1063
|
+
if (target === void 0 || title.length === 0 || sessionRenamePending) return;
|
|
1064
|
+
if (renameSession === void 0) {
|
|
1065
|
+
setSessionRenameError("Session rename is unavailable; retry after reconnecting.");
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
setSessionRenamePending(true);
|
|
1069
|
+
setSessionRenameError(void 0);
|
|
1070
|
+
try {
|
|
1071
|
+
await renameSession(target.sessionId, title);
|
|
1072
|
+
setSessionRenameTarget(void 0);
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
setSessionRenameError(error instanceof Error ? error.message : String(error));
|
|
1075
|
+
} finally {
|
|
1076
|
+
setSessionRenamePending(false);
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
const archiveWorktreeSession = async (sessionId) => {
|
|
1080
|
+
if (archiveSession === void 0) return;
|
|
1081
|
+
await runMutation(() => archiveSession(sessionId));
|
|
1082
|
+
};
|
|
1083
|
+
const toggleWorkspace = (workspaceId) => {
|
|
1084
|
+
setExpandedWorkspaces((current) => ({
|
|
1085
|
+
...current,
|
|
1086
|
+
[workspaceId]: current[workspaceId] === false
|
|
1087
|
+
}));
|
|
1088
|
+
};
|
|
1089
|
+
const toggleMain = (workspaceId) => {
|
|
1090
|
+
setExpandedMains((current) => ({
|
|
1091
|
+
...current,
|
|
1092
|
+
[workspaceId]: current[workspaceId] === false
|
|
1093
|
+
}));
|
|
1094
|
+
};
|
|
1095
|
+
const toggleWorktree = (worktreeId) => {
|
|
1096
|
+
setExpandedWorktrees((current) => ({
|
|
1097
|
+
...current,
|
|
1098
|
+
[worktreeId]: current[worktreeId] === false
|
|
1099
|
+
}));
|
|
1100
|
+
};
|
|
1101
|
+
const openWorktreeCreator = (workspace) => {
|
|
1102
|
+
const view = viewByWorkspace.get(workspace.workspaceId);
|
|
1103
|
+
const baseBranch = selectDefaultBaseBranch(view?.branches ?? []);
|
|
1104
|
+
const existingNames = [...(view?.branches ?? []).map((branch) => branch.name), ...(view?.worktrees ?? []).map((worktree) => worktree.branch)];
|
|
1105
|
+
setWorktreeModalWorkspaceId(workspace.workspaceId);
|
|
1106
|
+
setSelectedBranch(baseBranch);
|
|
1107
|
+
setNewBranch(createDefaultWorktreeName(existingNames));
|
|
1108
|
+
setActionError(void 0);
|
|
1109
|
+
};
|
|
1110
|
+
const submitWorktree = async () => {
|
|
1111
|
+
const worktreeName = newBranch.trim();
|
|
1112
|
+
if (manager === void 0 || modalWorkspace === void 0 || selectedBranch.length === 0 || worktreeName.length === 0) return;
|
|
1113
|
+
const input = {
|
|
1114
|
+
workspaceId: modalWorkspace.workspaceId,
|
|
1115
|
+
branch: selectedBranch,
|
|
1116
|
+
newBranch: worktreeName
|
|
1117
|
+
};
|
|
1118
|
+
setActionPending(true);
|
|
1119
|
+
setActionError(void 0);
|
|
1120
|
+
setPendingSessionBinding(void 0);
|
|
1121
|
+
try {
|
|
1122
|
+
const createdWorktree = await executeWorktreeAction(manager, {
|
|
1123
|
+
type: "createWorktree",
|
|
1124
|
+
input
|
|
1125
|
+
});
|
|
1126
|
+
if (createdWorktree === void 0) throw new Error("Worktree creation returned no Worktree record; retry the request.");
|
|
1127
|
+
setWorktreeModalWorkspaceId(void 0);
|
|
1128
|
+
if (createSessionCallback === void 0) {
|
|
1129
|
+
await refresh();
|
|
1130
|
+
setActionError({
|
|
1131
|
+
code: "SESSION_CREATE_UNAVAILABLE",
|
|
1132
|
+
message: "Worktree created, but Session creation is unavailable; retry after reconnecting.",
|
|
1133
|
+
retryable: true
|
|
1134
|
+
});
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
const sessionInput = {
|
|
1138
|
+
workspaceId: createdWorktree.workspaceId,
|
|
1139
|
+
worktreeId: createdWorktree.worktreeId,
|
|
1140
|
+
cwd: createdWorktree.absolutePath
|
|
1141
|
+
};
|
|
1142
|
+
try {
|
|
1143
|
+
await createSessionCallback(sessionInput);
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
if (error instanceof WorktreeSessionBindingError) setPendingSessionBinding({
|
|
1146
|
+
...sessionInput,
|
|
1147
|
+
sessionId: error.sessionId
|
|
1148
|
+
});
|
|
1149
|
+
throw error;
|
|
1150
|
+
}
|
|
1151
|
+
await refresh();
|
|
1152
|
+
} catch (error) {
|
|
1153
|
+
setActionError(toWorktreeViewError(error));
|
|
1154
|
+
} finally {
|
|
1155
|
+
setActionPending(false);
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
const createSession = async (input) => {
|
|
1159
|
+
if (createSessionCallback === void 0) {
|
|
1160
|
+
setActionError({
|
|
1161
|
+
code: "SESSION_CREATE_UNAVAILABLE",
|
|
1162
|
+
message: "Session creation is unavailable; retry after reconnecting.",
|
|
1163
|
+
retryable: true
|
|
1164
|
+
});
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
setActionPending(true);
|
|
1168
|
+
setActionError(void 0);
|
|
1169
|
+
setPendingSessionBinding(void 0);
|
|
1170
|
+
try {
|
|
1171
|
+
await createSessionCallback(input);
|
|
1172
|
+
await refresh();
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
if (error instanceof WorktreeSessionBindingError) setPendingSessionBinding({
|
|
1175
|
+
...input,
|
|
1176
|
+
sessionId: error.sessionId
|
|
1177
|
+
});
|
|
1178
|
+
setActionError(toWorktreeViewError(error));
|
|
1179
|
+
} finally {
|
|
1180
|
+
setActionPending(false);
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
const retrySessionBinding = async () => {
|
|
1184
|
+
if (manager === void 0 || pendingSessionBinding === void 0) return;
|
|
1185
|
+
setActionPending(true);
|
|
1186
|
+
setActionError(void 0);
|
|
1187
|
+
try {
|
|
1188
|
+
await manager.bindSession({
|
|
1189
|
+
workspaceId: pendingSessionBinding.workspaceId,
|
|
1190
|
+
worktreeId: pendingSessionBinding.worktreeId,
|
|
1191
|
+
sessionId: pendingSessionBinding.sessionId
|
|
1192
|
+
});
|
|
1193
|
+
const sessionId = pendingSessionBinding.sessionId;
|
|
1194
|
+
setPendingSessionBinding(void 0);
|
|
1195
|
+
ensureSessionWorkspace?.(pendingSessionBinding.workspaceId, sessionId);
|
|
1196
|
+
await refresh();
|
|
1197
|
+
openSession(sessionId);
|
|
1198
|
+
} catch (error) {
|
|
1199
|
+
setActionError(toWorktreeViewError(error));
|
|
1200
|
+
} finally {
|
|
1201
|
+
setActionPending(false);
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
const openWorkspaceSession = (workspaceId, sessionId) => {
|
|
1205
|
+
ensureSessionWorkspace?.(workspaceId, sessionId);
|
|
1206
|
+
openWorktreeSession({ open: openSession }, sessionId);
|
|
1207
|
+
};
|
|
1208
|
+
if (mode !== "worktree") return null;
|
|
1209
|
+
const visibleWorkspaces = workspaces.items.filter((workspace) => workspaceMatches(workspace, viewByWorkspace.get(workspace.workspaceId), sessions, query));
|
|
1210
|
+
return (0, react_jsx_runtime.jsxs)("aside", {
|
|
1211
|
+
ref,
|
|
1212
|
+
className: worktree_css_default.surface,
|
|
1213
|
+
"data-worktree-surface": true,
|
|
1214
|
+
"data-collapsed": collapsed || void 0,
|
|
1215
|
+
"aria-label": "Worktree navigation",
|
|
1216
|
+
style: {
|
|
1217
|
+
width: `${width}px`,
|
|
1218
|
+
...bounds.ready ? {
|
|
1219
|
+
top: `${bounds.top}px`,
|
|
1220
|
+
height: `${bounds.height}px`
|
|
1221
|
+
} : {
|
|
1222
|
+
height: "0px",
|
|
1223
|
+
visibility: "hidden"
|
|
1224
|
+
}
|
|
1225
|
+
},
|
|
1226
|
+
children: [
|
|
1227
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1228
|
+
className: worktree_css_default.wideContent,
|
|
1229
|
+
children: [
|
|
1230
|
+
(0, react_jsx_runtime.jsxs)("header", {
|
|
1231
|
+
className: worktree_css_default.header,
|
|
1232
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1233
|
+
className: worktree_css_default.title,
|
|
1234
|
+
children: "Worktrees"
|
|
1235
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1236
|
+
type: "button",
|
|
1237
|
+
className: worktree_css_default.closeButton,
|
|
1238
|
+
"aria-label": "Exit Worktree mode",
|
|
1239
|
+
onClick: () => {
|
|
1240
|
+
actions.setViewMode("workspace-session");
|
|
1241
|
+
},
|
|
1242
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
|
|
1243
|
+
})]
|
|
1244
|
+
}),
|
|
1245
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1246
|
+
className: worktree_css_default.searchRow,
|
|
1247
|
+
children: [
|
|
1248
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1249
|
+
className: worktree_css_default.searchIcon,
|
|
1250
|
+
"aria-hidden": "true",
|
|
1251
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, {})
|
|
1252
|
+
}),
|
|
1253
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
1254
|
+
className: worktree_css_default.searchInput,
|
|
1255
|
+
"aria-label": "Search Workspaces and Sessions",
|
|
1256
|
+
placeholder: "Search Workspaces and Sessions",
|
|
1257
|
+
value: searchQuery,
|
|
1258
|
+
onChange: (event) => {
|
|
1259
|
+
setSearchQuery(event.currentTarget.value);
|
|
1260
|
+
}
|
|
1261
|
+
}),
|
|
1262
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
1263
|
+
type: "button",
|
|
1264
|
+
className: worktree_css_default.iconButton,
|
|
1265
|
+
"aria-label": "Add Workspace",
|
|
1266
|
+
onClick: () => {
|
|
1267
|
+
if (createWorkspace !== void 0) runMutation(createWorkspace);
|
|
1268
|
+
},
|
|
1269
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
|
|
1270
|
+
})
|
|
1271
|
+
]
|
|
1272
|
+
}),
|
|
1273
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1274
|
+
className: worktree_css_default.content,
|
|
1275
|
+
tabIndex: 0,
|
|
1276
|
+
children: [
|
|
1277
|
+
actionError !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
1278
|
+
className: worktree_css_default.error,
|
|
1279
|
+
role: "alert",
|
|
1280
|
+
"data-worktree-error": true,
|
|
1281
|
+
children: [
|
|
1282
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1283
|
+
className: worktree_css_default.message,
|
|
1284
|
+
"data-error": "true",
|
|
1285
|
+
children: actionError.message
|
|
1286
|
+
}),
|
|
1287
|
+
pendingSessionBinding !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
1288
|
+
className: worktree_css_default.recoveryActions,
|
|
1289
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
1290
|
+
type: "button",
|
|
1291
|
+
className: worktree_css_default.actionButton,
|
|
1292
|
+
disabled: actionPending,
|
|
1293
|
+
onClick: () => {
|
|
1294
|
+
retrySessionBinding();
|
|
1295
|
+
},
|
|
1296
|
+
children: "Retry Binding"
|
|
1297
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1298
|
+
type: "button",
|
|
1299
|
+
className: worktree_css_default.actionButton,
|
|
1300
|
+
disabled: actionPending,
|
|
1301
|
+
onClick: () => {
|
|
1302
|
+
const sessionId = pendingSessionBinding.sessionId;
|
|
1303
|
+
setPendingSessionBinding(void 0);
|
|
1304
|
+
setActionError(void 0);
|
|
1305
|
+
openSession(sessionId);
|
|
1306
|
+
},
|
|
1307
|
+
children: "Open Created Session"
|
|
1308
|
+
})]
|
|
1309
|
+
}),
|
|
1310
|
+
actionError.retryable && (0, react_jsx_runtime.jsx)("button", {
|
|
1311
|
+
type: "button",
|
|
1312
|
+
className: worktree_css_default.retryButton,
|
|
1313
|
+
onClick: () => {
|
|
1314
|
+
setActionError(void 0);
|
|
1315
|
+
refresh();
|
|
1316
|
+
},
|
|
1317
|
+
children: "Retry"
|
|
1318
|
+
})
|
|
1319
|
+
]
|
|
1320
|
+
}),
|
|
1321
|
+
readState.status === "loading" && (0, react_jsx_runtime.jsx)("p", {
|
|
1322
|
+
className: worktree_css_default.message,
|
|
1323
|
+
children: "Loading Workspaces…"
|
|
1324
|
+
}),
|
|
1325
|
+
readState.status === "error" && readState.error !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
1326
|
+
className: worktree_css_default.error,
|
|
1327
|
+
role: "alert",
|
|
1328
|
+
"data-worktree-error": true,
|
|
1329
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
1330
|
+
className: worktree_css_default.message,
|
|
1331
|
+
"data-error": "true",
|
|
1332
|
+
children: readState.error.message
|
|
1333
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1334
|
+
type: "button",
|
|
1335
|
+
className: worktree_css_default.retryButton,
|
|
1336
|
+
onClick: () => {
|
|
1337
|
+
refresh();
|
|
1338
|
+
},
|
|
1339
|
+
children: "Retry"
|
|
1340
|
+
})]
|
|
1341
|
+
}) : readState.status === "ready" ? (0, react_jsx_runtime.jsx)("div", {
|
|
1342
|
+
className: worktree_css_default.workspaceList,
|
|
1343
|
+
children: visibleWorkspaces.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
1344
|
+
className: worktree_css_default.empty,
|
|
1345
|
+
children: "No matching Workspaces"
|
|
1346
|
+
}) : visibleWorkspaces.map((workspace) => {
|
|
1347
|
+
const view = viewByWorkspace.get(workspace.workspaceId);
|
|
1348
|
+
const expanded = expandedWorkspaces[workspace.workspaceId] !== false;
|
|
1349
|
+
const workspaceMatchesQuery = includesText(workspace.title, query);
|
|
1350
|
+
const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
|
|
1351
|
+
const bindings = view?.bindings ?? [];
|
|
1352
|
+
const visibleMainSessionIds = unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]).filter((sessionId) => workspaceMatchesQuery || includesText(sessionLabel(sessionId, sessions), query));
|
|
1353
|
+
const mainExpanded = expandedMains[workspace.workspaceId] !== false;
|
|
1354
|
+
const mainGroupKey = `main:${workspace.workspaceId}`;
|
|
1355
|
+
const worktrees = view?.worktrees ?? [];
|
|
1356
|
+
return (0, react_jsx_runtime.jsxs)("section", {
|
|
1357
|
+
className: worktree_css_default.workspaceGroup,
|
|
1358
|
+
"data-workspace-id": workspace.workspaceId,
|
|
1359
|
+
children: [(0, react_jsx_runtime.jsx)(WorktreeWorkspaceRow, {
|
|
1360
|
+
workspace,
|
|
1361
|
+
expanded,
|
|
1362
|
+
actionPending,
|
|
1363
|
+
menuOpen: openWorkspaceMenuId === workspace.workspaceId,
|
|
1364
|
+
drag: {
|
|
1365
|
+
active: workspaceDrag !== void 0,
|
|
1366
|
+
marker: workspaceDrag?.over?.workspaceId === workspace.workspaceId ? workspaceDrag.over.half : null,
|
|
1367
|
+
start: () => {
|
|
1368
|
+
workspaceDropCommitted.current = false;
|
|
1369
|
+
setWorkspaceDrag({
|
|
1370
|
+
workspaceId: workspace.workspaceId,
|
|
1371
|
+
over: null
|
|
1372
|
+
});
|
|
1373
|
+
},
|
|
1374
|
+
hover: (half) => {
|
|
1375
|
+
setWorkspaceDrag((current) => current === void 0 ? current : {
|
|
1376
|
+
...current,
|
|
1377
|
+
over: {
|
|
1378
|
+
workspaceId: workspace.workspaceId,
|
|
1379
|
+
half
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
},
|
|
1383
|
+
drop: (half) => {
|
|
1384
|
+
if (workspaceDrag === void 0) return;
|
|
1385
|
+
commitWorkspaceDrag(workspaceDrag, {
|
|
1386
|
+
workspaceId: workspace.workspaceId,
|
|
1387
|
+
half
|
|
1388
|
+
});
|
|
1389
|
+
},
|
|
1390
|
+
end: () => {
|
|
1391
|
+
if (workspaceDrag?.over !== null && workspaceDrag?.over !== void 0) commitWorkspaceDrag(workspaceDrag, workspaceDrag.over);
|
|
1392
|
+
else setWorkspaceDrag(void 0);
|
|
1393
|
+
workspaceDropCommitted.current = false;
|
|
1394
|
+
}
|
|
1395
|
+
},
|
|
1396
|
+
onToggle: () => {
|
|
1397
|
+
toggleWorkspace(workspace.workspaceId);
|
|
1398
|
+
},
|
|
1399
|
+
onCreateWorktree: () => {
|
|
1400
|
+
openWorktreeCreator(workspace);
|
|
1401
|
+
},
|
|
1402
|
+
onRename: () => {
|
|
1403
|
+
openWorkspaceRename(workspace);
|
|
1404
|
+
},
|
|
1405
|
+
onDelete: () => {
|
|
1406
|
+
openWorkspaceDelete(workspace);
|
|
1407
|
+
},
|
|
1408
|
+
onMenuOpenChange: (open) => {
|
|
1409
|
+
setOpenWorkspaceMenuId(open ? workspace.workspaceId : void 0);
|
|
1410
|
+
}
|
|
1411
|
+
}), expanded && (0, react_jsx_runtime.jsxs)("div", {
|
|
1412
|
+
className: worktree_css_default.treeChildren,
|
|
1413
|
+
children: [
|
|
1414
|
+
(0, react_jsx_runtime.jsx)(WorktreeGroupRow, {
|
|
1415
|
+
kind: "main",
|
|
1416
|
+
label: "Main",
|
|
1417
|
+
expanded: mainExpanded,
|
|
1418
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
|
|
1419
|
+
workspaceTitle: workspace.title,
|
|
1420
|
+
onToggle: () => {
|
|
1421
|
+
toggleMain(workspace.workspaceId);
|
|
1422
|
+
},
|
|
1423
|
+
onCreateSession: createMainSession === void 0 ? void 0 : () => {
|
|
1424
|
+
createMainSession(workspace.workspaceId);
|
|
1425
|
+
}
|
|
1426
|
+
}),
|
|
1427
|
+
mainExpanded && (0, react_jsx_runtime.jsx)(WorktreeSessionGroup, {
|
|
1428
|
+
groupKey: mainGroupKey,
|
|
1429
|
+
sessionIds: visibleMainSessionIds,
|
|
1430
|
+
workspaceId: workspace.workspaceId,
|
|
1431
|
+
expanded: expandedSessionGroups[mainGroupKey] === true,
|
|
1432
|
+
actionPending,
|
|
1433
|
+
sessions,
|
|
1434
|
+
dragState: sessionDrag,
|
|
1435
|
+
onToggleExpanded: () => {
|
|
1436
|
+
setExpandedSessionGroups((current) => ({
|
|
1437
|
+
...current,
|
|
1438
|
+
[mainGroupKey]: current[mainGroupKey] !== true
|
|
1439
|
+
}));
|
|
1440
|
+
},
|
|
1441
|
+
onStartDrag: (groupKey, sessionId) => {
|
|
1442
|
+
sessionDropCommitted.current = false;
|
|
1443
|
+
setSessionDrag({
|
|
1444
|
+
groupKey,
|
|
1445
|
+
sessionId,
|
|
1446
|
+
over: null
|
|
1447
|
+
});
|
|
1448
|
+
},
|
|
1449
|
+
onHoverDrag: (sessionId, half) => {
|
|
1450
|
+
setSessionDrag((current) => current === void 0 ? current : {
|
|
1451
|
+
...current,
|
|
1452
|
+
over: {
|
|
1453
|
+
sessionId,
|
|
1454
|
+
half
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1457
|
+
},
|
|
1458
|
+
onClearDrag: () => {
|
|
1459
|
+
setSessionDrag(void 0);
|
|
1460
|
+
},
|
|
1461
|
+
onFinishDrag: () => {
|
|
1462
|
+
sessionDropCommitted.current = false;
|
|
1463
|
+
},
|
|
1464
|
+
onCommitDrag: commitSessionDrag,
|
|
1465
|
+
onOpen: (sessionId) => {
|
|
1466
|
+
openWorktreeSession({ open: openSession }, sessionId);
|
|
1467
|
+
},
|
|
1468
|
+
onRename: renameSession === void 0 ? void 0 : openSessionRename,
|
|
1469
|
+
onFork: forkSession,
|
|
1470
|
+
onArchive: archiveSession === void 0 ? void 0 : archiveWorktreeSession
|
|
1471
|
+
}),
|
|
1472
|
+
worktrees.length === 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
1473
|
+
className: worktree_css_default.emptyNested,
|
|
1474
|
+
children: "No Worktrees"
|
|
1475
|
+
}),
|
|
1476
|
+
worktrees.map((record) => {
|
|
1477
|
+
const worktreeSessionIds = filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds);
|
|
1478
|
+
const worktreeMatchesQuery = workspaceMatchesQuery || includesText(record.branch, query) || includesText(record.absolutePath, query);
|
|
1479
|
+
if (query.length > 0 && !worktreeMatchesQuery && !worktreeSessionIds.some((sessionId) => includesText(sessionLabel(sessionId, sessions), query))) return null;
|
|
1480
|
+
const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery || includesText(sessionLabel(sessionId, sessions), query));
|
|
1481
|
+
const worktreeGroupKey = `worktree:${record.worktreeId}`;
|
|
1482
|
+
const state = record.status === "removed" ? "warning" : record.health === "repair" ? "error" : "done";
|
|
1483
|
+
const stateLabel = record.status === "removed" ? "Detached Worktree" : record.health === "repair" ? "Worktree needs repair" : "Active Worktree ready";
|
|
1484
|
+
const worktreeExpanded = expandedWorktrees[record.worktreeId] !== false;
|
|
1485
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1486
|
+
className: worktree_css_default.worktreeGroup,
|
|
1487
|
+
"data-worktree-id": record.worktreeId,
|
|
1488
|
+
children: [(0, react_jsx_runtime.jsx)(WorktreeGroupRow, {
|
|
1489
|
+
kind: "worktree",
|
|
1490
|
+
label: record.branch,
|
|
1491
|
+
expanded: worktreeExpanded,
|
|
1492
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
|
|
1493
|
+
workspaceTitle: workspace.title,
|
|
1494
|
+
state,
|
|
1495
|
+
stateLabel,
|
|
1496
|
+
onToggle: () => {
|
|
1497
|
+
toggleWorktree(record.worktreeId);
|
|
1498
|
+
},
|
|
1499
|
+
onCreateSession: record.status === "active" ? () => {
|
|
1500
|
+
createSession({
|
|
1501
|
+
workspaceId: record.workspaceId,
|
|
1502
|
+
worktreeId: record.worktreeId,
|
|
1503
|
+
cwd: record.absolutePath
|
|
1504
|
+
});
|
|
1505
|
+
} : void 0
|
|
1506
|
+
}), worktreeExpanded && (0, react_jsx_runtime.jsx)(WorktreeSessionGroup, {
|
|
1507
|
+
groupKey: worktreeGroupKey,
|
|
1508
|
+
sessionIds: visibleWorktreeSessionIds,
|
|
1509
|
+
workspaceId: workspace.workspaceId,
|
|
1510
|
+
expanded: expandedSessionGroups[worktreeGroupKey] === true,
|
|
1511
|
+
actionPending,
|
|
1512
|
+
sessions,
|
|
1513
|
+
dragState: sessionDrag,
|
|
1514
|
+
onToggleExpanded: () => {
|
|
1515
|
+
setExpandedSessionGroups((current) => ({
|
|
1516
|
+
...current,
|
|
1517
|
+
[worktreeGroupKey]: current[worktreeGroupKey] !== true
|
|
1518
|
+
}));
|
|
1519
|
+
},
|
|
1520
|
+
onStartDrag: (groupKey, sessionId) => {
|
|
1521
|
+
sessionDropCommitted.current = false;
|
|
1522
|
+
setSessionDrag({
|
|
1523
|
+
groupKey,
|
|
1524
|
+
sessionId,
|
|
1525
|
+
over: null
|
|
1526
|
+
});
|
|
1527
|
+
},
|
|
1528
|
+
onHoverDrag: (sessionId, half) => {
|
|
1529
|
+
setSessionDrag((current) => current === void 0 ? current : {
|
|
1530
|
+
...current,
|
|
1531
|
+
over: {
|
|
1532
|
+
sessionId,
|
|
1533
|
+
half
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
},
|
|
1537
|
+
onClearDrag: () => {
|
|
1538
|
+
setSessionDrag(void 0);
|
|
1539
|
+
},
|
|
1540
|
+
onFinishDrag: () => {
|
|
1541
|
+
sessionDropCommitted.current = false;
|
|
1542
|
+
},
|
|
1543
|
+
onCommitDrag: commitSessionDrag,
|
|
1544
|
+
onOpen: (sessionId) => {
|
|
1545
|
+
openWorkspaceSession(workspace.workspaceId, sessionId);
|
|
1546
|
+
},
|
|
1547
|
+
onRename: renameSession === void 0 ? void 0 : openSessionRename,
|
|
1548
|
+
onFork: forkSession,
|
|
1549
|
+
onArchive: archiveSession === void 0 ? void 0 : archiveWorktreeSession
|
|
1550
|
+
})]
|
|
1551
|
+
}, record.worktreeId);
|
|
1552
|
+
})
|
|
1553
|
+
]
|
|
1554
|
+
})]
|
|
1555
|
+
}, workspace.workspaceId);
|
|
1556
|
+
})
|
|
1557
|
+
}) : null
|
|
1558
|
+
]
|
|
1559
|
+
})
|
|
1560
|
+
]
|
|
1561
|
+
}),
|
|
1562
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1563
|
+
className: worktree_css_default.railContent,
|
|
1564
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
1565
|
+
type: "button",
|
|
1566
|
+
className: worktree_css_default.railButton,
|
|
1567
|
+
"aria-label": "Exit Worktree mode",
|
|
1568
|
+
onClick: () => {
|
|
1569
|
+
actions.setViewMode("workspace-session");
|
|
1570
|
+
},
|
|
1571
|
+
children: "WT"
|
|
1572
|
+
})
|
|
1573
|
+
}),
|
|
1574
|
+
(0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1575
|
+
open: sessionRenameTarget !== void 0,
|
|
1576
|
+
onClose: closeSessionRename,
|
|
1577
|
+
closeLabel: "Close",
|
|
1578
|
+
title: "Rename session",
|
|
1579
|
+
footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1580
|
+
variant: "outline",
|
|
1581
|
+
disabled: sessionRenamePending,
|
|
1582
|
+
onClick: closeSessionRename,
|
|
1583
|
+
children: "Cancel"
|
|
1584
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1585
|
+
variant: "primary",
|
|
1586
|
+
disabled: sessionRenamePending || sessionRenameDraft.trim().length === 0,
|
|
1587
|
+
onClick: () => {
|
|
1588
|
+
confirmSessionRename();
|
|
1589
|
+
},
|
|
1590
|
+
children: "Rename"
|
|
1591
|
+
})] }),
|
|
1592
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
1593
|
+
className: worktree_css_default.renameInput,
|
|
1594
|
+
value: sessionRenameDraft,
|
|
1595
|
+
"aria-label": "Session name",
|
|
1596
|
+
autoFocus: true,
|
|
1597
|
+
disabled: sessionRenamePending,
|
|
1598
|
+
onFocus: (event) => {
|
|
1599
|
+
event.currentTarget.select();
|
|
1600
|
+
},
|
|
1601
|
+
onChange: (event) => {
|
|
1602
|
+
setSessionRenameDraft(event.currentTarget.value);
|
|
1603
|
+
setSessionRenameError(void 0);
|
|
1604
|
+
},
|
|
1605
|
+
onKeyDown: (event) => {
|
|
1606
|
+
if (event.key === "Enter" && !event.nativeEvent.isComposing) {
|
|
1607
|
+
event.preventDefault();
|
|
1608
|
+
confirmSessionRename();
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}), sessionRenameError !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
1612
|
+
className: worktree_css_default.renameError,
|
|
1613
|
+
role: "alert",
|
|
1614
|
+
children: sessionRenameError
|
|
1615
|
+
})]
|
|
1616
|
+
}),
|
|
1617
|
+
workspaceRenameTarget !== void 0 && (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1618
|
+
open: workspaceRenameTarget !== void 0,
|
|
1619
|
+
onClose: closeWorkspaceRename,
|
|
1620
|
+
closeLabel: "Close Rename Workspace dialog",
|
|
1621
|
+
title: "Rename Workspace",
|
|
1622
|
+
footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1623
|
+
variant: "outline",
|
|
1624
|
+
disabled: workspaceRenamePending,
|
|
1625
|
+
onClick: closeWorkspaceRename,
|
|
1626
|
+
children: "Cancel"
|
|
1627
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1628
|
+
variant: "primary",
|
|
1629
|
+
disabled: workspaceRenameBlocked,
|
|
1630
|
+
onClick: () => {
|
|
1631
|
+
confirmWorkspaceRename();
|
|
1632
|
+
},
|
|
1633
|
+
children: "Rename"
|
|
1634
|
+
})] }),
|
|
1635
|
+
children: [
|
|
1636
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
1637
|
+
className: worktree_css_default.renameInput,
|
|
1638
|
+
value: workspaceRenameDraft,
|
|
1639
|
+
"aria-label": "Workspace name",
|
|
1640
|
+
autoFocus: true,
|
|
1641
|
+
disabled: workspaceRenamePending,
|
|
1642
|
+
onFocus: (event) => {
|
|
1643
|
+
event.currentTarget.select();
|
|
1644
|
+
},
|
|
1645
|
+
onChange: (event) => {
|
|
1646
|
+
setWorkspaceRenameDraft(event.currentTarget.value);
|
|
1647
|
+
setWorkspaceRenameError(void 0);
|
|
1648
|
+
},
|
|
1649
|
+
onKeyDown: (event) => {
|
|
1650
|
+
if (event.key === "Enter" && !event.nativeEvent.isComposing) {
|
|
1651
|
+
event.preventDefault();
|
|
1652
|
+
confirmWorkspaceRename();
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
}),
|
|
1656
|
+
workspaceRenameDuplicate && (0, react_jsx_runtime.jsx)("p", {
|
|
1657
|
+
className: worktree_css_default.renameError,
|
|
1658
|
+
role: "alert",
|
|
1659
|
+
children: "A Workspace with this name already exists."
|
|
1660
|
+
}),
|
|
1661
|
+
workspaceRenameError !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
1662
|
+
className: worktree_css_default.renameError,
|
|
1663
|
+
role: "alert",
|
|
1664
|
+
children: workspaceRenameError
|
|
1665
|
+
})
|
|
1666
|
+
]
|
|
1667
|
+
}),
|
|
1668
|
+
workspaceDeleteTarget !== void 0 && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1669
|
+
open: workspaceDeleteTarget !== void 0,
|
|
1670
|
+
onClose: closeWorkspaceDelete,
|
|
1671
|
+
closeLabel: "Close Delete Workspace dialog",
|
|
1672
|
+
title: "Delete Workspace",
|
|
1673
|
+
description: `Delete ${workspaceDeleteTarget.title}? This removes only the DSH Workspace registration. The directory, Sessions, and Git Worktrees will be retained.`,
|
|
1674
|
+
footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1675
|
+
variant: "outline",
|
|
1676
|
+
disabled: workspaceDeletePending,
|
|
1677
|
+
onClick: closeWorkspaceDelete,
|
|
1678
|
+
children: "Cancel"
|
|
1679
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1680
|
+
variant: "outline",
|
|
1681
|
+
disabled: workspaceDeletePending,
|
|
1682
|
+
onClick: () => {
|
|
1683
|
+
confirmWorkspaceDelete();
|
|
1684
|
+
},
|
|
1685
|
+
children: "Delete"
|
|
1686
|
+
})] }),
|
|
1687
|
+
children: workspaceDeleteError !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
1688
|
+
className: worktree_css_default.renameError,
|
|
1689
|
+
role: "alert",
|
|
1690
|
+
children: workspaceDeleteError
|
|
1691
|
+
})
|
|
1692
|
+
}),
|
|
1693
|
+
worktreeModalWorkspaceId !== void 0 && modalWorkspace !== void 0 && (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1694
|
+
open: worktreeModalWorkspaceId !== void 0,
|
|
1695
|
+
onClose: () => {
|
|
1696
|
+
if (actionPending) return;
|
|
1697
|
+
setWorktreeModalWorkspaceId(void 0);
|
|
1698
|
+
},
|
|
1699
|
+
closeLabel: "Close Create Worktree dialog",
|
|
1700
|
+
title: "New Worktree",
|
|
1701
|
+
description: `${modalWorkspace.title} · the Worktree path is managed by DSH.`,
|
|
1702
|
+
footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1703
|
+
variant: "outline",
|
|
1704
|
+
disabled: actionPending,
|
|
1705
|
+
onClick: () => {
|
|
1706
|
+
setWorktreeModalWorkspaceId(void 0);
|
|
1707
|
+
},
|
|
1708
|
+
children: "Cancel"
|
|
1709
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1710
|
+
variant: "primary",
|
|
1711
|
+
disabled: actionPending || selectedBranch.length === 0 || newBranch.trim().length === 0,
|
|
1712
|
+
onClick: () => {
|
|
1713
|
+
submitWorktree();
|
|
1714
|
+
},
|
|
1715
|
+
children: "Create Worktree"
|
|
1716
|
+
})] }),
|
|
1717
|
+
children: [
|
|
1718
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
1719
|
+
className: worktree_css_default.modalField,
|
|
1720
|
+
children: ["Base branch", (0, react_jsx_runtime.jsxs)("select", {
|
|
1721
|
+
className: worktree_css_default.actionSelect,
|
|
1722
|
+
"aria-label": "Worktree base branch",
|
|
1723
|
+
value: selectedBranch,
|
|
1724
|
+
disabled: actionPending,
|
|
1725
|
+
onChange: (event) => {
|
|
1726
|
+
setSelectedBranch(event.currentTarget.value);
|
|
1727
|
+
},
|
|
1728
|
+
children: [(0, react_jsx_runtime.jsx)("option", {
|
|
1729
|
+
value: "",
|
|
1730
|
+
children: "No local branch"
|
|
1731
|
+
}), (modalView?.branches ?? []).map((branch) => (0, react_jsx_runtime.jsxs)("option", {
|
|
1732
|
+
value: branch.name,
|
|
1733
|
+
children: [
|
|
1734
|
+
branch.name,
|
|
1735
|
+
branch.isCurrent ? " (current)" : "",
|
|
1736
|
+
branch.checkedOut ? " (checked out)" : ""
|
|
1737
|
+
]
|
|
1738
|
+
}, branch.name))]
|
|
1739
|
+
})]
|
|
1740
|
+
}),
|
|
1741
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
1742
|
+
className: worktree_css_default.modalField,
|
|
1743
|
+
children: ["Worktree name", (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
1744
|
+
className: worktree_css_default.renameInput,
|
|
1745
|
+
"aria-label": "Worktree name",
|
|
1746
|
+
value: newBranch,
|
|
1747
|
+
placeholder: "dsh/12345678",
|
|
1748
|
+
disabled: actionPending,
|
|
1749
|
+
onChange: (event) => {
|
|
1750
|
+
setNewBranch(event.currentTarget.value);
|
|
1751
|
+
}
|
|
1752
|
+
})]
|
|
1753
|
+
}),
|
|
1754
|
+
modalView?.branches.length === 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
1755
|
+
className: worktree_css_default.empty,
|
|
1756
|
+
children: "No local branches found in this Workspace."
|
|
1757
|
+
})
|
|
1758
|
+
]
|
|
1759
|
+
})
|
|
1760
|
+
]
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
//#endregion
|
|
1764
|
+
//#region lib/client/view-mode-store.js
|
|
1765
|
+
/** Create one root-scoped, browser-local view-mode store for the two slot entries. */
|
|
1766
|
+
function createWorktreeViewStore() {
|
|
1767
|
+
return (0, _deepseek_ai_dsh_client_runtime_client.defineStore)({
|
|
1768
|
+
init: () => ({ viewMode: "workspace-session" }),
|
|
1769
|
+
persist: WORKTREE_VIEW_MODE_STORAGE_KEY,
|
|
1770
|
+
actions: { setViewMode: (draft, mode) => {
|
|
1771
|
+
draft.viewMode = mode;
|
|
1772
|
+
} }
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
//#endregion
|
|
1776
|
+
//#region lib/client/virtual-workspace-membership.js
|
|
1777
|
+
function normalizeBindings(bindings) {
|
|
1778
|
+
const bySession = /* @__PURE__ */ new Map();
|
|
1779
|
+
for (const binding of bindings) {
|
|
1780
|
+
if (binding.workspaceId.length === 0 || binding.sessionId.length === 0) continue;
|
|
1781
|
+
bySession.set(binding.sessionId, binding);
|
|
1782
|
+
}
|
|
1783
|
+
return [...bySession.values()];
|
|
1784
|
+
}
|
|
1785
|
+
function sameWorkspaceItems(left, right) {
|
|
1786
|
+
if (left.items.length !== right.items.length) return false;
|
|
1787
|
+
return left.items.every((item, index) => {
|
|
1788
|
+
const other = right.items[index];
|
|
1789
|
+
if (item === other) return true;
|
|
1790
|
+
if (typeof item !== "object" || item === null || typeof other !== "object" || other === null || !("workspaceId" in item) || !("workspaceId" in other) || !("sessionIds" in item) || !("sessionIds" in other)) return false;
|
|
1791
|
+
const leftSessionIds = item.sessionIds;
|
|
1792
|
+
const rightSessionIds = other.sessionIds;
|
|
1793
|
+
return item.workspaceId === other.workspaceId && Array.isArray(leftSessionIds) && Array.isArray(rightSessionIds) && leftSessionIds.length === rightSessionIds.length && leftSessionIds.every((sessionId, sessionIndex) => sessionId === rightSessionIds[sessionIndex]);
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
/**
|
|
1797
|
+
* Maintains a re-playable in-memory overlay over DSH's Workspace list. Native
|
|
1798
|
+
* refreshes are observed and the current sidecar bindings are applied again;
|
|
1799
|
+
* no DSH Host mutation or persistence is involved.
|
|
1800
|
+
*/
|
|
1801
|
+
function createVirtualWorkspaceMembership(list) {
|
|
1802
|
+
let bindings = [];
|
|
1803
|
+
let disposed = false;
|
|
1804
|
+
let applying = false;
|
|
1805
|
+
const apply = (nextBindings) => {
|
|
1806
|
+
const current = list.getSnapshot();
|
|
1807
|
+
const next = projectVirtualWorkspaceMembership(current, bindings, nextBindings);
|
|
1808
|
+
if (sameWorkspaceItems(current, next)) return;
|
|
1809
|
+
applying = true;
|
|
1810
|
+
try {
|
|
1811
|
+
list.set(next);
|
|
1812
|
+
} finally {
|
|
1813
|
+
applying = false;
|
|
1814
|
+
}
|
|
1815
|
+
};
|
|
1816
|
+
const replay = () => {
|
|
1817
|
+
if (disposed || applying) return;
|
|
1818
|
+
apply(bindings);
|
|
1819
|
+
};
|
|
1820
|
+
const unsubscribe = list.subscribe(replay);
|
|
1821
|
+
const sync = (nextBindings) => {
|
|
1822
|
+
if (disposed) return;
|
|
1823
|
+
const normalized = normalizeBindings(nextBindings);
|
|
1824
|
+
apply(normalized);
|
|
1825
|
+
bindings = normalized;
|
|
1826
|
+
};
|
|
1827
|
+
return {
|
|
1828
|
+
ensure(binding) {
|
|
1829
|
+
if (disposed) return;
|
|
1830
|
+
if (bindings.some((current) => current.workspaceId === binding.workspaceId && current.sessionId === binding.sessionId)) return;
|
|
1831
|
+
sync([...bindings.filter((current) => current.sessionId !== binding.sessionId), binding]);
|
|
1832
|
+
},
|
|
1833
|
+
sync,
|
|
1834
|
+
removeSession(sessionId) {
|
|
1835
|
+
sync(bindings.filter((binding) => binding.sessionId !== sessionId));
|
|
1836
|
+
},
|
|
1837
|
+
dispose() {
|
|
1838
|
+
if (disposed) return;
|
|
1839
|
+
apply([]);
|
|
1840
|
+
bindings = [];
|
|
1841
|
+
disposed = true;
|
|
1842
|
+
unsubscribe();
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
//#endregion
|
|
1847
|
+
//#region lib/client/entry.js
|
|
1848
|
+
/** Required DSH Client services; Connection is the sole Worktree wire dependency. */
|
|
1849
|
+
const inject = [
|
|
1850
|
+
"connection",
|
|
1851
|
+
"slots",
|
|
1852
|
+
"sessions",
|
|
1853
|
+
"workspaces"
|
|
1854
|
+
];
|
|
1855
|
+
/**
|
|
1856
|
+
* DSH Client entry. The adapter is composed once per plugin fiber and is disposed
|
|
1857
|
+
* with that fiber, so slot consumers share one manager and one request lifetime.
|
|
1858
|
+
*/
|
|
1859
|
+
function apply(ctx) {
|
|
1860
|
+
const manager = createWorktreeConnectionAdapter(ctx.connection.rpc);
|
|
1861
|
+
const sessions = ctx.sessions;
|
|
1862
|
+
const virtualWorkspaceMembership = createVirtualWorkspaceMembership(ctx.workspaces.list);
|
|
1863
|
+
ctx.effect(() => () => manager.dispose(), "clutch-dsh-worktree: connection cleanup");
|
|
1864
|
+
ctx.effect(() => () => virtualWorkspaceMembership.dispose(), "clutch-dsh-worktree: Workspace membership cleanup");
|
|
1865
|
+
const viewStore = createWorktreeViewStore();
|
|
1866
|
+
const ensureSessionWorkspace = (workspaceId, sessionId) => {
|
|
1867
|
+
virtualWorkspaceMembership.ensure({
|
|
1868
|
+
workspaceId,
|
|
1869
|
+
sessionId
|
|
1870
|
+
});
|
|
1871
|
+
};
|
|
1872
|
+
const syncSessionWorkspaces = (bindings) => {
|
|
1873
|
+
virtualWorkspaceMembership.sync(bindings);
|
|
1874
|
+
};
|
|
1875
|
+
ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
|
|
1876
|
+
name: "sidebar.footer.action",
|
|
1877
|
+
id: "clutch-dsh-worktree-mode-action",
|
|
1878
|
+
store: viewStore,
|
|
1879
|
+
inject: () => ({ available: true })
|
|
1880
|
+
}, WorktreeModeAction));
|
|
1881
|
+
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
1882
|
+
name: "shell.overlay",
|
|
1883
|
+
id: "clutch-dsh-worktree-navigation",
|
|
1884
|
+
store: viewStore,
|
|
1885
|
+
inject: () => ({
|
|
1886
|
+
available: true,
|
|
1887
|
+
manager,
|
|
1888
|
+
createWorkspace: async () => {
|
|
1889
|
+
const workspacePath = await ctx.workspaces.pickDirectory();
|
|
1890
|
+
if (workspacePath !== null) await ctx.workspaces.create({ path: workspacePath });
|
|
1891
|
+
},
|
|
1892
|
+
createSessionForWorktree: (input) => createSessionForWorktree({
|
|
1893
|
+
...input,
|
|
1894
|
+
createSession: (sessionInput) => sessions.create(sessionInput),
|
|
1895
|
+
manager,
|
|
1896
|
+
beforeOpen: (sessionId) => {
|
|
1897
|
+
ensureSessionWorkspace(input.workspaceId, sessionId);
|
|
1898
|
+
},
|
|
1899
|
+
openSession: (sessionId) => {
|
|
1900
|
+
ctx.sessions.open(sessionId);
|
|
1901
|
+
}
|
|
1902
|
+
}),
|
|
1903
|
+
createMainSession: (workspaceId) => {
|
|
1904
|
+
ctx.workspaces.startSession(workspaceId);
|
|
1905
|
+
},
|
|
1906
|
+
renameWorkspace: async (workspaceId, title) => {
|
|
1907
|
+
await ctx.workspaces.rename(workspaceId, title);
|
|
1908
|
+
},
|
|
1909
|
+
deleteWorkspace: async (workspaceId) => {
|
|
1910
|
+
await ctx.workspaces.delete(workspaceId);
|
|
1911
|
+
},
|
|
1912
|
+
insertWorkspaceBefore: async (workspaceId, beforeWorkspaceId) => {
|
|
1913
|
+
await ctx.workspaces.insertBefore(workspaceId, beforeWorkspaceId);
|
|
1914
|
+
},
|
|
1915
|
+
insertSessionBefore: async (workspaceId, sessionId, beforeSessionId) => {
|
|
1916
|
+
await ctx.workspaces.insertSessionBefore(workspaceId, sessionId, beforeSessionId);
|
|
1917
|
+
},
|
|
1918
|
+
renameSession: async (sessionId, title) => {
|
|
1919
|
+
const session = ctx.sessions.binding(sessionId)?.session;
|
|
1920
|
+
if (session === void 0) throw new Error(`unknown session "${sessionId}"`);
|
|
1921
|
+
const result = await session.rename(title);
|
|
1922
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
1923
|
+
},
|
|
1924
|
+
forkSession: (sessionId) => {
|
|
1925
|
+
ctx.sessions.fork({
|
|
1926
|
+
sessionId,
|
|
1927
|
+
increaseTitle: true
|
|
1928
|
+
}).then((childId) => {
|
|
1929
|
+
ctx.sessions.open(childId);
|
|
1930
|
+
}).catch(() => {});
|
|
1931
|
+
},
|
|
1932
|
+
archiveSession: (sessionId) => ctx.workspaces.archiveSession(sessionId),
|
|
1933
|
+
ensureSessionWorkspace,
|
|
1934
|
+
syncSessionWorkspaces,
|
|
1935
|
+
openSession: (sessionId) => {
|
|
1936
|
+
ctx.sessions.open(sessionId);
|
|
1937
|
+
}
|
|
1938
|
+
})
|
|
1939
|
+
}, WorktreeSurface));
|
|
1940
|
+
}
|
|
1941
|
+
//#endregion
|
|
1942
|
+
exports.WORKTREE_CONNECTION_CHANNEL = WORKTREE_CONNECTION_CHANNEL;
|
|
1943
|
+
exports.WORKTREE_CONNECTION_ENDPOINTS = WORKTREE_CONNECTION_ENDPOINTS;
|
|
1944
|
+
exports.WorktreeConnectionError = WorktreeConnectionError;
|
|
1945
|
+
exports.apply = apply;
|
|
1946
|
+
exports.createWorktreeConnectionAdapter = createWorktreeConnectionAdapter;
|
|
1947
|
+
exports.inject = inject;
|
|
1948
|
+
return module.exports;
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
|
|
1952
|
+
//# sourceMappingURL=client.js.map
|