dsh-git-workspace 0.0.0-stage → 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/LICENSE +28 -0
- package/README.md +217 -2
- package/cordis.patch.yml +9 -0
- package/lib/client.js +736 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +815 -0
- package/lib/index.js.map +1 -0
- package/lib/types/index.d.ts +24 -0
- package/package.json +61 -4
package/lib/client.js
ADDED
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-git-workspace",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
// src/client/index.tsx
|
|
11
|
+
/**
|
|
12
|
+
* @dsh-external/dsh-git-workspace — client 半。
|
|
13
|
+
*
|
|
14
|
+
* 接管 ui-workspace 声明的两个 directory-flow 单占洞,把原来的目录浏览
|
|
15
|
+
* 对话框换成「本地目录 / GitHub 仓库」双来源对话框。
|
|
16
|
+
*
|
|
17
|
+
* 洞的契约(来自 ui-workspace / directory-picker-browse 实现):
|
|
18
|
+
* props: open, busy, onPicked(path), onCancel(), onError(err)
|
|
19
|
+
* inject: t, listDirectory, createDirectory, pickNativeDirectory
|
|
20
|
+
* 本实现只消费 open / busy / onPicked / onCancel:错误留在对话框内显示,
|
|
21
|
+
* 不驱动 owner 的 onError arm(与内置 browse 一致)。
|
|
22
|
+
*
|
|
23
|
+
* 遮蔽规则来自 dsh-client-ui-slots 的 single 分支:同 priority 二次注册会抛错,
|
|
24
|
+
* 不同 priority 按升序投影取胜者。内置 browse 用默认 0,这里用负数接管。
|
|
25
|
+
*/
|
|
26
|
+
const LOCALE_NS = "github-workspace";
|
|
27
|
+
const CLONE_ENDPOINT = "/gh-workspace/clone";
|
|
28
|
+
/**
|
|
29
|
+
* 远低于内置 browse(默认 0)与任何已知占用者。实测洞里存在 priority -1 的
|
|
30
|
+
* 注册(error 文案里的 "registered by Gb"),所以 -1 不够低,用 -1000 打底,
|
|
31
|
+
* 每次 apply 再递减一档,既保证胜出也不会撞同优先级。
|
|
32
|
+
*/
|
|
33
|
+
const DEPTH_PRIORITY = -1e3;
|
|
34
|
+
/**
|
|
35
|
+
* 每次 apply 递减一档。装配器的动态注入与启动自动恢复可能让 apply 跑多遍,
|
|
36
|
+
* 而 single 洞对同优先级二次注册是硬抛错。让每份注册各占一档后:
|
|
37
|
+
* 全部注册成功,投影取最小者渲染;任一份被 dispose 回收,另一份仍然在。
|
|
38
|
+
*/
|
|
39
|
+
let applyGeneration = 0;
|
|
40
|
+
const zh = {
|
|
41
|
+
title: "添加工作区",
|
|
42
|
+
tabLocal: "本地目录",
|
|
43
|
+
tabGithub: "Git 仓库",
|
|
44
|
+
localHint: "填写一个已存在的目录绝对路径,也可以用系统文件夹选择器。",
|
|
45
|
+
localPlaceholder: "例如 D:\\projects\\my-repo",
|
|
46
|
+
pickNative: "选择文件夹…",
|
|
47
|
+
open: "使用此目录",
|
|
48
|
+
repoLabel: "仓库地址",
|
|
49
|
+
repoHint: "支持 owner/repo(默认 GitHub)、https://、ssh:// 与 git@host:path,任意 Git 服务器都可以。",
|
|
50
|
+
repoPlaceholder: "deepseek-ai/DeepSeek-Harness 或 https://gitlab.com/group/repo",
|
|
51
|
+
branchLabel: "分支",
|
|
52
|
+
branchPlaceholder: "留空使用默认分支",
|
|
53
|
+
parentLabel: "克隆到",
|
|
54
|
+
parentPlaceholder: "留空使用默认目录",
|
|
55
|
+
clone: "克隆并添加",
|
|
56
|
+
cloning: "正在克隆…",
|
|
57
|
+
cancel: "取消",
|
|
58
|
+
errPathRequired: "请先填写目录路径。",
|
|
59
|
+
errRepoRequired: "请先填写仓库。",
|
|
60
|
+
errPickUnavailable: "当前环境没有系统文件夹选择器,请直接填写路径。",
|
|
61
|
+
errClone: "克隆失败。",
|
|
62
|
+
loading: "加载中…",
|
|
63
|
+
emptyDir: "这里没有子目录。",
|
|
64
|
+
cloningTitle: "正在克隆仓库",
|
|
65
|
+
cloneFailed: "克隆失败",
|
|
66
|
+
cancelled: "已取消",
|
|
67
|
+
dismiss: "关闭",
|
|
68
|
+
tokenLabel: "访问令牌",
|
|
69
|
+
tokenHint: "留空则复用本机 git 凭据(如 Windows 凭据管理器的 credential.helper)。需要指定账号时再填该服务器的 Personal Access Token,按仓库地址的主机注入,只用于本次克隆,不保存。",
|
|
70
|
+
tokenPlaceholder: "留空使用本机凭据,或填 ghp_… / glpat-… 等"
|
|
71
|
+
};
|
|
72
|
+
const en = {
|
|
73
|
+
title: "Add Workspace",
|
|
74
|
+
tabLocal: "Local folder",
|
|
75
|
+
tabGithub: "Git repository",
|
|
76
|
+
localHint: "Enter an existing absolute folder path, or use the system folder picker.",
|
|
77
|
+
localPlaceholder: "e.g. D:\\projects\\my-repo",
|
|
78
|
+
pickNative: "Choose folder…",
|
|
79
|
+
open: "Use this folder",
|
|
80
|
+
repoLabel: "Repository",
|
|
81
|
+
repoHint: "owner/repo (GitHub), https://, ssh:// or git@host:path — any Git server works.",
|
|
82
|
+
repoPlaceholder: "deepseek-ai/DeepSeek-Harness or https://gitlab.com/group/repo",
|
|
83
|
+
branchLabel: "Branch",
|
|
84
|
+
branchPlaceholder: "Leave empty for the default branch",
|
|
85
|
+
parentLabel: "Clone into",
|
|
86
|
+
parentPlaceholder: "Leave empty for the default folder",
|
|
87
|
+
clone: "Clone and add",
|
|
88
|
+
cloning: "Cloning…",
|
|
89
|
+
cancel: "Cancel",
|
|
90
|
+
errPathRequired: "Enter a folder path first.",
|
|
91
|
+
errRepoRequired: "Enter a repository first.",
|
|
92
|
+
errPickUnavailable: "No system folder picker in this environment; type the path instead.",
|
|
93
|
+
errClone: "Clone failed.",
|
|
94
|
+
loading: "Loading…",
|
|
95
|
+
emptyDir: "No subfolders here.",
|
|
96
|
+
cloningTitle: "Cloning repository",
|
|
97
|
+
cloneFailed: "Clone failed",
|
|
98
|
+
cancelled: "Cancelled",
|
|
99
|
+
dismiss: "Dismiss",
|
|
100
|
+
tokenLabel: "Access token",
|
|
101
|
+
tokenHint: "Leave empty to reuse the local git credential helper (e.g. Windows Credential Manager). Fill a personal access token for that server only when you need a specific account; it is injected for the repository host, used once, never stored.",
|
|
102
|
+
tokenPlaceholder: "Empty = local credentials, or ghp_… / glpat-…"
|
|
103
|
+
};
|
|
104
|
+
const styles = {
|
|
105
|
+
overlay: {
|
|
106
|
+
position: "fixed",
|
|
107
|
+
inset: 0,
|
|
108
|
+
zIndex: 4e3,
|
|
109
|
+
display: "flex",
|
|
110
|
+
alignItems: "center",
|
|
111
|
+
justifyContent: "center",
|
|
112
|
+
background: "rgba(0,0,0,0.45)"
|
|
113
|
+
},
|
|
114
|
+
dialog: {
|
|
115
|
+
boxSizing: "border-box",
|
|
116
|
+
display: "flex",
|
|
117
|
+
flexDirection: "column",
|
|
118
|
+
gap: "14px",
|
|
119
|
+
width: "560px",
|
|
120
|
+
maxWidth: "92vw",
|
|
121
|
+
maxHeight: "86vh",
|
|
122
|
+
overflowY: "auto",
|
|
123
|
+
padding: "20px",
|
|
124
|
+
borderRadius: "16px",
|
|
125
|
+
background: "Canvas",
|
|
126
|
+
color: "CanvasText",
|
|
127
|
+
boxShadow: "0 20px 60px rgba(0,0,0,0.45)",
|
|
128
|
+
font: "inherit"
|
|
129
|
+
},
|
|
130
|
+
title: {
|
|
131
|
+
margin: 0,
|
|
132
|
+
fontSize: "16px",
|
|
133
|
+
fontWeight: 600
|
|
134
|
+
},
|
|
135
|
+
tabs: {
|
|
136
|
+
display: "flex",
|
|
137
|
+
gap: "6px"
|
|
138
|
+
},
|
|
139
|
+
tab: (active) => ({
|
|
140
|
+
appearance: "none",
|
|
141
|
+
border: "1px solid transparent",
|
|
142
|
+
borderRadius: "8px",
|
|
143
|
+
padding: "6px 14px",
|
|
144
|
+
cursor: "pointer",
|
|
145
|
+
font: "inherit",
|
|
146
|
+
fontWeight: active ? 600 : 400,
|
|
147
|
+
color: "inherit",
|
|
148
|
+
background: active ? "rgba(127,127,127,0.22)" : "transparent",
|
|
149
|
+
borderColor: active ? "rgba(127,127,127,0.35)" : "transparent"
|
|
150
|
+
}),
|
|
151
|
+
hint: {
|
|
152
|
+
fontSize: "12px",
|
|
153
|
+
opacity: .62,
|
|
154
|
+
margin: 0
|
|
155
|
+
},
|
|
156
|
+
field: {
|
|
157
|
+
display: "flex",
|
|
158
|
+
flexDirection: "column",
|
|
159
|
+
gap: "5px"
|
|
160
|
+
},
|
|
161
|
+
label: {
|
|
162
|
+
fontSize: "12px",
|
|
163
|
+
fontWeight: 600,
|
|
164
|
+
opacity: .78
|
|
165
|
+
},
|
|
166
|
+
input: {
|
|
167
|
+
boxSizing: "border-box",
|
|
168
|
+
width: "100%",
|
|
169
|
+
padding: "8px 10px",
|
|
170
|
+
borderRadius: "8px",
|
|
171
|
+
border: "1px solid rgba(127,127,127,0.36)",
|
|
172
|
+
background: "Field",
|
|
173
|
+
color: "FieldText",
|
|
174
|
+
font: "inherit",
|
|
175
|
+
fontSize: "13px"
|
|
176
|
+
},
|
|
177
|
+
error: {
|
|
178
|
+
margin: 0,
|
|
179
|
+
padding: "8px 10px",
|
|
180
|
+
borderRadius: "8px",
|
|
181
|
+
fontSize: "12px",
|
|
182
|
+
whiteSpace: "pre-wrap",
|
|
183
|
+
wordBreak: "break-word",
|
|
184
|
+
color: "#ff8080",
|
|
185
|
+
background: "rgba(255,80,80,0.12)"
|
|
186
|
+
},
|
|
187
|
+
footer: {
|
|
188
|
+
display: "flex",
|
|
189
|
+
justifyContent: "flex-end",
|
|
190
|
+
gap: "8px"
|
|
191
|
+
},
|
|
192
|
+
listBox: {
|
|
193
|
+
display: "flex",
|
|
194
|
+
flexDirection: "column",
|
|
195
|
+
maxHeight: "240px",
|
|
196
|
+
overflowY: "auto",
|
|
197
|
+
border: "1px solid rgba(127,127,127,0.28)",
|
|
198
|
+
borderRadius: "10px"
|
|
199
|
+
},
|
|
200
|
+
listRow: {
|
|
201
|
+
display: "block",
|
|
202
|
+
width: "100%",
|
|
203
|
+
textAlign: "left",
|
|
204
|
+
appearance: "none",
|
|
205
|
+
border: 0,
|
|
206
|
+
borderBottom: "1px solid rgba(127,127,127,0.14)",
|
|
207
|
+
background: "transparent",
|
|
208
|
+
color: "inherit",
|
|
209
|
+
font: "inherit",
|
|
210
|
+
fontSize: "13px",
|
|
211
|
+
padding: "8px 12px",
|
|
212
|
+
cursor: "pointer"
|
|
213
|
+
},
|
|
214
|
+
listMeta: {
|
|
215
|
+
fontSize: "11px",
|
|
216
|
+
opacity: .6,
|
|
217
|
+
padding: "8px 12px"
|
|
218
|
+
},
|
|
219
|
+
floating: {
|
|
220
|
+
position: "fixed",
|
|
221
|
+
right: "18px",
|
|
222
|
+
bottom: "18px",
|
|
223
|
+
zIndex: 4001,
|
|
224
|
+
width: "330px",
|
|
225
|
+
maxWidth: "90vw",
|
|
226
|
+
boxSizing: "border-box",
|
|
227
|
+
display: "flex",
|
|
228
|
+
flexDirection: "column",
|
|
229
|
+
gap: "8px",
|
|
230
|
+
padding: "14px 16px",
|
|
231
|
+
borderRadius: "12px",
|
|
232
|
+
background: "Canvas",
|
|
233
|
+
color: "CanvasText",
|
|
234
|
+
border: "1px solid rgba(127,127,127,0.28)",
|
|
235
|
+
boxShadow: "0 12px 36px rgba(0,0,0,0.35)",
|
|
236
|
+
fontSize: "13px"
|
|
237
|
+
},
|
|
238
|
+
floatingHead: {
|
|
239
|
+
display: "flex",
|
|
240
|
+
alignItems: "center",
|
|
241
|
+
justifyContent: "space-between",
|
|
242
|
+
gap: "8px"
|
|
243
|
+
},
|
|
244
|
+
floatingTitle: {
|
|
245
|
+
fontWeight: 600,
|
|
246
|
+
fontSize: "13px"
|
|
247
|
+
},
|
|
248
|
+
floatingClose: {
|
|
249
|
+
appearance: "none",
|
|
250
|
+
border: 0,
|
|
251
|
+
background: "transparent",
|
|
252
|
+
color: "inherit",
|
|
253
|
+
cursor: "pointer",
|
|
254
|
+
fontSize: "16px",
|
|
255
|
+
lineHeight: 1,
|
|
256
|
+
opacity: .65,
|
|
257
|
+
padding: 0
|
|
258
|
+
},
|
|
259
|
+
floatingRepo: {
|
|
260
|
+
fontSize: "12px",
|
|
261
|
+
opacity: .72,
|
|
262
|
+
wordBreak: "break-word"
|
|
263
|
+
},
|
|
264
|
+
progressTrack: {
|
|
265
|
+
height: "6px",
|
|
266
|
+
borderRadius: "3px",
|
|
267
|
+
background: "rgba(127,127,127,0.25)",
|
|
268
|
+
overflow: "hidden"
|
|
269
|
+
},
|
|
270
|
+
progressFill: {
|
|
271
|
+
height: "100%",
|
|
272
|
+
background: "#4a7dff",
|
|
273
|
+
transition: "width 0.3s ease"
|
|
274
|
+
},
|
|
275
|
+
floatingMeta: {
|
|
276
|
+
display: "flex",
|
|
277
|
+
justifyContent: "space-between",
|
|
278
|
+
fontSize: "11px",
|
|
279
|
+
opacity: .7
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* 克隆进度浮层:固定在右下角,不遮住主界面,用户可继续在 DSH 里操作。
|
|
284
|
+
* 运行中只提供取消,失败后提供关闭。
|
|
285
|
+
*/
|
|
286
|
+
function FloatingJob(props) {
|
|
287
|
+
const { job, t, onCancel, onDismiss } = props;
|
|
288
|
+
const raw = Number(job?.percent);
|
|
289
|
+
const percent = Number.isFinite(raw) ? Math.max(0, Math.min(100, raw)) : 0;
|
|
290
|
+
const failed = job?.state === "failed";
|
|
291
|
+
const cancelled = job?.state === "cancelled";
|
|
292
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
293
|
+
style: styles.floating,
|
|
294
|
+
children: [
|
|
295
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
296
|
+
style: styles.floatingHead,
|
|
297
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
298
|
+
style: styles.floatingTitle,
|
|
299
|
+
children: failed ? t("cloneFailed") : cancelled ? t("cancelled") : t("cloningTitle")
|
|
300
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
301
|
+
type: "button",
|
|
302
|
+
style: styles.floatingClose,
|
|
303
|
+
"aria-label": failed || cancelled ? t("dismiss") : t("cancel"),
|
|
304
|
+
onClick: failed || cancelled ? onDismiss : onCancel,
|
|
305
|
+
children: "×"
|
|
306
|
+
})]
|
|
307
|
+
}),
|
|
308
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
309
|
+
style: styles.floatingRepo,
|
|
310
|
+
children: String(job?.repo ?? "")
|
|
311
|
+
}),
|
|
312
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
313
|
+
style: styles.progressTrack,
|
|
314
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
315
|
+
...styles.progressFill,
|
|
316
|
+
width: percent + "%"
|
|
317
|
+
} })
|
|
318
|
+
}),
|
|
319
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
320
|
+
style: styles.floatingMeta,
|
|
321
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: failed ? t("cloneFailed") : String(job?.detail ?? "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [percent, "%"] })]
|
|
322
|
+
}),
|
|
323
|
+
failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
324
|
+
style: styles.error,
|
|
325
|
+
children: String(job?.error ?? "")
|
|
326
|
+
}) : null
|
|
327
|
+
]
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
function WorkspaceSourceDialog(props) {
|
|
331
|
+
const { open, onPicked, onCancel, t, listDirectory, pickNativeDirectory } = props;
|
|
332
|
+
const ownerBusy = props.busy === true;
|
|
333
|
+
const [tab, setTab] = (0, react.useState)("local");
|
|
334
|
+
const [localPath, setLocalPath] = (0, react.useState)("");
|
|
335
|
+
const [repo, setRepo] = (0, react.useState)("");
|
|
336
|
+
const [branch, setBranch] = (0, react.useState)("");
|
|
337
|
+
const [parentDir, setParentDir] = (0, react.useState)("");
|
|
338
|
+
const [error, setError] = (0, react.useState)(null);
|
|
339
|
+
const [cloning, setCloning] = (0, react.useState)(false);
|
|
340
|
+
const [listing, setListing] = (0, react.useState)(null);
|
|
341
|
+
const [listingBusy, setListingBusy] = (0, react.useState)(false);
|
|
342
|
+
const [token, setToken] = (0, react.useState)("");
|
|
343
|
+
const [job, setJob] = (0, react.useState)(null);
|
|
344
|
+
const submittedJobRef = (0, react.useRef)(null);
|
|
345
|
+
(0, react.useEffect)(() => {
|
|
346
|
+
if (open) return;
|
|
347
|
+
setTab("local");
|
|
348
|
+
setLocalPath("");
|
|
349
|
+
setRepo("");
|
|
350
|
+
setBranch("");
|
|
351
|
+
setParentDir("");
|
|
352
|
+
setError(null);
|
|
353
|
+
setCloning(false);
|
|
354
|
+
setListing(null);
|
|
355
|
+
setListingBusy(false);
|
|
356
|
+
setJob(null);
|
|
357
|
+
setToken("");
|
|
358
|
+
submittedJobRef.current = null;
|
|
359
|
+
}, [open]);
|
|
360
|
+
/** 列一层目录;不传路径时列 Host 主目录。 */
|
|
361
|
+
const load = (0, react.useCallback)(async (path) => {
|
|
362
|
+
if (typeof listDirectory !== "function") return;
|
|
363
|
+
setListingBusy(true);
|
|
364
|
+
setError(null);
|
|
365
|
+
try {
|
|
366
|
+
const result = await listDirectory(path);
|
|
367
|
+
setListing(result ?? null);
|
|
368
|
+
if (result && typeof result.path === "string") setLocalPath(result.path);
|
|
369
|
+
} catch (reason) {
|
|
370
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
371
|
+
} finally {
|
|
372
|
+
setListingBusy(false);
|
|
373
|
+
}
|
|
374
|
+
}, [listDirectory]);
|
|
375
|
+
(0, react.useEffect)(() => {
|
|
376
|
+
if (!open || tab !== "local" || listing !== null) return;
|
|
377
|
+
load(void 0);
|
|
378
|
+
}, [
|
|
379
|
+
open,
|
|
380
|
+
tab,
|
|
381
|
+
listing,
|
|
382
|
+
load
|
|
383
|
+
]);
|
|
384
|
+
const submitLocal = (0, react.useCallback)(() => {
|
|
385
|
+
const value = localPath.trim();
|
|
386
|
+
if (value === "") {
|
|
387
|
+
setError(t("errPathRequired"));
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
setError(null);
|
|
391
|
+
onPicked(value);
|
|
392
|
+
}, [
|
|
393
|
+
localPath,
|
|
394
|
+
onPicked,
|
|
395
|
+
t
|
|
396
|
+
]);
|
|
397
|
+
const pickNative = (0, react.useCallback)(async () => {
|
|
398
|
+
if (typeof pickNativeDirectory !== "function") {
|
|
399
|
+
setError(t("errPickUnavailable"));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
const picked = await pickNativeDirectory();
|
|
404
|
+
if (typeof picked === "string" && picked.trim() !== "") {
|
|
405
|
+
setError(null);
|
|
406
|
+
onPicked(picked.trim());
|
|
407
|
+
}
|
|
408
|
+
} catch (reason) {
|
|
409
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
410
|
+
}
|
|
411
|
+
}, [
|
|
412
|
+
pickNativeDirectory,
|
|
413
|
+
onPicked,
|
|
414
|
+
t
|
|
415
|
+
]);
|
|
416
|
+
const cloneRepo = (0, react.useCallback)(async () => {
|
|
417
|
+
const value = repo.trim();
|
|
418
|
+
if (value === "") {
|
|
419
|
+
setError(t("errRepoRequired"));
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
setCloning(true);
|
|
423
|
+
setError(null);
|
|
424
|
+
try {
|
|
425
|
+
const data = await (await fetch(CLONE_ENDPOINT, {
|
|
426
|
+
method: "POST",
|
|
427
|
+
headers: { "content-type": "application/json" },
|
|
428
|
+
body: JSON.stringify({
|
|
429
|
+
repo: value,
|
|
430
|
+
branch: branch.trim(),
|
|
431
|
+
parentDir: parentDir.trim(),
|
|
432
|
+
token: token.trim()
|
|
433
|
+
})
|
|
434
|
+
})).json();
|
|
435
|
+
if (data?.ok !== true) throw new Error(data?.error?.message ?? t("errClone"));
|
|
436
|
+
setCloning(false);
|
|
437
|
+
if (data.value?.state === "done" && data.value?.path) {
|
|
438
|
+
onPicked(String(data.value.path));
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
setJob(data.value);
|
|
442
|
+
} catch (reason) {
|
|
443
|
+
setCloning(false);
|
|
444
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
445
|
+
}
|
|
446
|
+
}, [
|
|
447
|
+
repo,
|
|
448
|
+
branch,
|
|
449
|
+
parentDir,
|
|
450
|
+
token,
|
|
451
|
+
onPicked,
|
|
452
|
+
t
|
|
453
|
+
]);
|
|
454
|
+
const jobId = job?.id;
|
|
455
|
+
const jobRunning = job?.state === "running";
|
|
456
|
+
(0, react.useEffect)(() => {
|
|
457
|
+
if (jobId === void 0 || !jobRunning) return;
|
|
458
|
+
const timer = setInterval(() => {
|
|
459
|
+
(async () => {
|
|
460
|
+
try {
|
|
461
|
+
const response = await fetch("/gh-workspace/job?id=" + encodeURIComponent(jobId));
|
|
462
|
+
if (response.status === 404) {
|
|
463
|
+
setJob(null);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
if (!response.ok) return;
|
|
467
|
+
const data = await response.json();
|
|
468
|
+
if (data?.ok === true && data.value) setJob(data.value);
|
|
469
|
+
} catch {}
|
|
470
|
+
})();
|
|
471
|
+
}, 700);
|
|
472
|
+
return () => clearInterval(timer);
|
|
473
|
+
}, [jobId, jobRunning]);
|
|
474
|
+
(0, react.useEffect)(() => {
|
|
475
|
+
if (job === null || job.state !== "done" || typeof job.path !== "string") return;
|
|
476
|
+
if (submittedJobRef.current === job.id) return;
|
|
477
|
+
submittedJobRef.current = job.id;
|
|
478
|
+
onPicked(job.path);
|
|
479
|
+
}, [job, onPicked]);
|
|
480
|
+
const cancelJob = (0, react.useCallback)(async () => {
|
|
481
|
+
if (job === null) return;
|
|
482
|
+
const id = job.id;
|
|
483
|
+
setJob(null);
|
|
484
|
+
try {
|
|
485
|
+
await fetch("/gh-workspace/job/cancel", {
|
|
486
|
+
method: "POST",
|
|
487
|
+
headers: { "content-type": "application/json" },
|
|
488
|
+
body: JSON.stringify({ id })
|
|
489
|
+
});
|
|
490
|
+
} catch {}
|
|
491
|
+
onCancel();
|
|
492
|
+
}, [job, onCancel]);
|
|
493
|
+
const restoredRef = (0, react.useRef)(false);
|
|
494
|
+
(0, react.useEffect)(() => {
|
|
495
|
+
if (!open) {
|
|
496
|
+
restoredRef.current = false;
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (restoredRef.current) return;
|
|
500
|
+
restoredRef.current = true;
|
|
501
|
+
(async () => {
|
|
502
|
+
try {
|
|
503
|
+
const data = await (await fetch("/gh-workspace/jobs")).json();
|
|
504
|
+
const active = data?.ok === true ? data.value?.jobs : void 0;
|
|
505
|
+
if (Array.isArray(active) && active.length > 0) setJob(active[0]);
|
|
506
|
+
} catch {}
|
|
507
|
+
})();
|
|
508
|
+
}, [open]);
|
|
509
|
+
if (!open) return null;
|
|
510
|
+
if (job !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FloatingJob, {
|
|
511
|
+
job,
|
|
512
|
+
t,
|
|
513
|
+
onCancel: () => void cancelJob(),
|
|
514
|
+
onDismiss: onCancel
|
|
515
|
+
});
|
|
516
|
+
const busy = cloning === true || ownerBusy;
|
|
517
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
518
|
+
style: styles.overlay,
|
|
519
|
+
onClick: onCancel,
|
|
520
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
521
|
+
style: styles.dialog,
|
|
522
|
+
onClick: (event) => event.stopPropagation(),
|
|
523
|
+
children: [
|
|
524
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
525
|
+
style: styles.title,
|
|
526
|
+
children: t("title")
|
|
527
|
+
}),
|
|
528
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
529
|
+
style: styles.tabs,
|
|
530
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
531
|
+
type: "button",
|
|
532
|
+
style: styles.tab(tab === "local"),
|
|
533
|
+
onClick: () => setTab("local"),
|
|
534
|
+
children: t("tabLocal")
|
|
535
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
536
|
+
type: "button",
|
|
537
|
+
style: styles.tab(tab === "github"),
|
|
538
|
+
onClick: () => setTab("github"),
|
|
539
|
+
children: t("tabGithub")
|
|
540
|
+
})]
|
|
541
|
+
}),
|
|
542
|
+
tab === "local" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
543
|
+
style: styles.field,
|
|
544
|
+
children: [
|
|
545
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
546
|
+
style: styles.hint,
|
|
547
|
+
children: t("localHint")
|
|
548
|
+
}),
|
|
549
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
550
|
+
style: styles.input,
|
|
551
|
+
value: localPath,
|
|
552
|
+
placeholder: t("localPlaceholder"),
|
|
553
|
+
onChange: (event) => setLocalPath(event.target.value),
|
|
554
|
+
onKeyDown: (event) => {
|
|
555
|
+
if (event.key === "Enter") {
|
|
556
|
+
event.preventDefault();
|
|
557
|
+
submitLocal();
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}),
|
|
561
|
+
typeof pickNativeDirectory === "function" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
562
|
+
variant: "outline",
|
|
563
|
+
onClick: () => void pickNative(),
|
|
564
|
+
children: t("pickNative")
|
|
565
|
+
}) }) : null,
|
|
566
|
+
listing !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
567
|
+
style: styles.listBox,
|
|
568
|
+
children: Array.isArray(listing.entries) && listing.entries.length > 0 ? listing.entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
569
|
+
type: "button",
|
|
570
|
+
style: styles.listRow,
|
|
571
|
+
title: String(entry.path),
|
|
572
|
+
onClick: () => void load(String(entry.path)),
|
|
573
|
+
children: String(entry.name)
|
|
574
|
+
}, String(entry.path))) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
575
|
+
style: styles.listMeta,
|
|
576
|
+
children: listingBusy ? t("loading") : t("emptyDir")
|
|
577
|
+
})
|
|
578
|
+
}) : listingBusy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
579
|
+
style: styles.hint,
|
|
580
|
+
children: t("loading")
|
|
581
|
+
}) : null
|
|
582
|
+
]
|
|
583
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
584
|
+
style: {
|
|
585
|
+
display: "flex",
|
|
586
|
+
flexDirection: "column",
|
|
587
|
+
gap: "12px"
|
|
588
|
+
},
|
|
589
|
+
children: [
|
|
590
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
591
|
+
style: styles.field,
|
|
592
|
+
children: [
|
|
593
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
594
|
+
style: styles.label,
|
|
595
|
+
children: t("repoLabel")
|
|
596
|
+
}),
|
|
597
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
598
|
+
style: styles.input,
|
|
599
|
+
value: repo,
|
|
600
|
+
placeholder: t("repoPlaceholder"),
|
|
601
|
+
disabled: busy,
|
|
602
|
+
onChange: (event) => setRepo(event.target.value)
|
|
603
|
+
}),
|
|
604
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
605
|
+
style: styles.hint,
|
|
606
|
+
children: t("repoHint")
|
|
607
|
+
})
|
|
608
|
+
]
|
|
609
|
+
}),
|
|
610
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
611
|
+
style: styles.field,
|
|
612
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
613
|
+
style: styles.label,
|
|
614
|
+
children: t("branchLabel")
|
|
615
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
616
|
+
style: styles.input,
|
|
617
|
+
value: branch,
|
|
618
|
+
placeholder: t("branchPlaceholder"),
|
|
619
|
+
disabled: busy,
|
|
620
|
+
onChange: (event) => setBranch(event.target.value)
|
|
621
|
+
})]
|
|
622
|
+
}),
|
|
623
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
624
|
+
style: styles.field,
|
|
625
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
626
|
+
style: styles.label,
|
|
627
|
+
children: t("parentLabel")
|
|
628
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
629
|
+
style: styles.input,
|
|
630
|
+
value: parentDir,
|
|
631
|
+
placeholder: t("parentPlaceholder"),
|
|
632
|
+
disabled: busy,
|
|
633
|
+
onChange: (event) => setParentDir(event.target.value)
|
|
634
|
+
})]
|
|
635
|
+
}),
|
|
636
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
637
|
+
style: styles.field,
|
|
638
|
+
children: [
|
|
639
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
640
|
+
style: styles.label,
|
|
641
|
+
children: t("tokenLabel")
|
|
642
|
+
}),
|
|
643
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
644
|
+
style: styles.input,
|
|
645
|
+
type: "password",
|
|
646
|
+
autoComplete: "off",
|
|
647
|
+
value: token,
|
|
648
|
+
placeholder: t("tokenPlaceholder"),
|
|
649
|
+
disabled: busy,
|
|
650
|
+
onChange: (event) => setToken(event.target.value)
|
|
651
|
+
}),
|
|
652
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
653
|
+
style: styles.hint,
|
|
654
|
+
children: t("tokenHint")
|
|
655
|
+
})
|
|
656
|
+
]
|
|
657
|
+
})
|
|
658
|
+
]
|
|
659
|
+
}),
|
|
660
|
+
error !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
661
|
+
style: styles.error,
|
|
662
|
+
children: error
|
|
663
|
+
}) : null,
|
|
664
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
665
|
+
style: styles.footer,
|
|
666
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
667
|
+
variant: "outline",
|
|
668
|
+
disabled: busy,
|
|
669
|
+
onClick: onCancel,
|
|
670
|
+
children: t("cancel")
|
|
671
|
+
}), tab === "local" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
672
|
+
variant: "primary",
|
|
673
|
+
disabled: busy,
|
|
674
|
+
onClick: submitLocal,
|
|
675
|
+
children: t("open")
|
|
676
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
677
|
+
variant: "primary",
|
|
678
|
+
disabled: busy,
|
|
679
|
+
onClick: () => void cloneRepo(),
|
|
680
|
+
children: busy ? t("cloning") : t("clone")
|
|
681
|
+
})]
|
|
682
|
+
})
|
|
683
|
+
]
|
|
684
|
+
})
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
const inject = [
|
|
688
|
+
"slots",
|
|
689
|
+
"uiWorkspace",
|
|
690
|
+
"locale"
|
|
691
|
+
];
|
|
692
|
+
function apply(ctx) {
|
|
693
|
+
const priority = DEPTH_PRIORITY - applyGeneration++;
|
|
694
|
+
ctx.effect(() => {
|
|
695
|
+
try {
|
|
696
|
+
return ctx.locale.register(LOCALE_NS, {
|
|
697
|
+
zh,
|
|
698
|
+
en
|
|
699
|
+
});
|
|
700
|
+
} catch {
|
|
701
|
+
return () => {};
|
|
702
|
+
}
|
|
703
|
+
}, "dsh-git-workspace: dictionaries");
|
|
704
|
+
const injected = () => {
|
|
705
|
+
const picker = typeof globalThis.__DSH_DIRECTORY_PICKER__?.pick === "function" ? () => globalThis.__DSH_DIRECTORY_PICKER__.pick() : typeof ctx.uiWorkspace.pickDirectory === "function" ? () => ctx.uiWorkspace.pickDirectory() : void 0;
|
|
706
|
+
return {
|
|
707
|
+
t: ctx.locale.bind(LOCALE_NS),
|
|
708
|
+
listDirectory: (path, signal) => ctx.uiWorkspace.listDirectory(path, signal),
|
|
709
|
+
createDirectory: (path, name) => ctx.uiWorkspace.createDirectory(path, name),
|
|
710
|
+
pickNativeDirectory: picker
|
|
711
|
+
};
|
|
712
|
+
};
|
|
713
|
+
ctx.slots.inject("conversation.hero.workspace.directoryFlow", () => ctx.slots.inject("sidebar.workspaces.directoryFlow", function* () {
|
|
714
|
+
try {
|
|
715
|
+
yield ctx.slots.register({
|
|
716
|
+
name: "conversation.hero.workspace.directoryFlow",
|
|
717
|
+
priority,
|
|
718
|
+
inject: injected
|
|
719
|
+
}, WorkspaceSourceDialog);
|
|
720
|
+
} catch {}
|
|
721
|
+
try {
|
|
722
|
+
yield ctx.slots.register({
|
|
723
|
+
name: "sidebar.workspaces.directoryFlow",
|
|
724
|
+
priority,
|
|
725
|
+
inject: injected
|
|
726
|
+
}, WorkspaceSourceDialog);
|
|
727
|
+
} catch {}
|
|
728
|
+
}));
|
|
729
|
+
}
|
|
730
|
+
exports.apply = apply;
|
|
731
|
+
exports.inject = inject;
|
|
732
|
+
return module.exports;
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
//# sourceMappingURL=client.js.map
|