@dungsil/dsh-plugin-ui-worktree 0.4.0 → 0.6.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 +18 -9
- package/assets/light-dark-preview.webp +0 -0
- package/dist/client.js +344 -108
- package/dist/index.js +421 -99
- package/package.json +12 -2
package/dist/client.js
CHANGED
|
@@ -20,14 +20,14 @@ function installStyles(css) {
|
|
|
20
20
|
var git_branch_default = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"6\" cy=\"5\" r=\"3\"/>\n <circle cx=\"6\" cy=\"19\" r=\"3\"/>\n <circle cx=\"18\" cy=\"6\" r=\"3\"/>\n <path d=\"M6 8v8M6 14h7a5 5 0 0 0 5-5\"/>\n</svg>\n";
|
|
21
21
|
// ../../tools/ui/src/composer.tsx
|
|
22
22
|
const composerStyles = `
|
|
23
|
-
:where(.dsh-plugin-composer-trigger) { display: flex; align-items: center; gap: 4px; min-width: 0; height: 28px; padding: 0 4px 0 8px; border: 0; border-radius: var(--dsw-radius-sm); background: transparent; color: var(--dsw-alias-label-secondary); font: inherit; font-size: 13px; line-height:
|
|
23
|
+
:where(.dsh-plugin-composer-trigger) { display: flex; align-items: center; gap: 4px; min-width: 0; height: 28px; padding: 0 4px 0 8px; border: 0; border-radius: var(--dsw-radius-sm); background: transparent; color: var(--dsw-alias-label-secondary); font: inherit; font-size: 13px; line-height: 1.5385; cursor: pointer; }
|
|
24
24
|
:where(.dsh-plugin-composer-trigger):disabled { color: var(--dsw-alias-label-dimmed); cursor: default; }
|
|
25
25
|
.dsh-plugin-composer-icon, .dsh-plugin-composer-chevron { display: inline-flex; flex: none; }
|
|
26
26
|
.dsh-plugin-composer-label, .dsh-plugin-composer-detail { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
27
27
|
.dsh-plugin-composer-chevron, .dsh-plugin-composer-detail { color: var(--dsw-alias-label-caption); }
|
|
28
28
|
.dsh-plugin-composer-trigger[aria-expanded=true] .dsh-plugin-composer-chevron { transform: rotate(180deg); }
|
|
29
29
|
`;
|
|
30
|
-
const ComposerTrigger = (0, react.forwardRef)(function ComposerTrigger({ className = "", icon, label, detail, ...props }, ref) {
|
|
30
|
+
const ComposerTrigger = /* @__PURE__ */ (0, react.forwardRef)(function ComposerTrigger({ className = "", icon, label, detail, ...props }, ref) {
|
|
31
31
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
32
32
|
...props,
|
|
33
33
|
ref,
|
|
@@ -57,23 +57,37 @@ const ComposerTrigger = (0, react.forwardRef)(function ComposerTrigger({ classNa
|
|
|
57
57
|
});
|
|
58
58
|
// ../../tools/ui/src/settings.tsx
|
|
59
59
|
/** The rc.5 settings shell has no icon slot, so decorate only the matching navigation rows. */
|
|
60
|
-
function installSettingsIcon(id, labels,
|
|
60
|
+
function installSettingsIcon(id, labels, icon) {
|
|
61
61
|
const attribute = "data-dsh-settings-icon";
|
|
62
62
|
const selector = `[${attribute}="${id}"]`;
|
|
63
63
|
const removeStyles = installStyles(`
|
|
64
64
|
${selector} > svg { display: none; }
|
|
65
|
-
${selector}::before { content: ''; display: block; flex: none; width: 16px; height: 16px; background: currentColor; mask: url("data:image/svg+xml,${encodeURIComponent(
|
|
65
|
+
${typeof icon === "string" ? `${selector}::before { content: ''; display: block; flex: none; width: 16px; height: 16px; background: currentColor; mask: url("data:image/svg+xml,${encodeURIComponent(icon)}") center / contain no-repeat; }` : ""}
|
|
66
|
+
${selector} > [data-dsh-settings-icon-glyph] { display: inline-flex; flex: none; }
|
|
66
67
|
`);
|
|
67
|
-
const marked = /* @__PURE__ */ new
|
|
68
|
+
const marked = /* @__PURE__ */ new Map();
|
|
68
69
|
const refresh = () => {
|
|
69
70
|
const rows = new Set(document.querySelectorAll("[data-shortcut-modal=\"settings\"] nav button"));
|
|
70
|
-
for (const row of marked) if (!rows.has(row) || !labels.includes(row.textContent?.trim() ?? "")) {
|
|
71
|
+
for (const [row, dispose] of marked) if (!rows.has(row) || !labels.includes(row.textContent?.trim() ?? "")) {
|
|
71
72
|
row.removeAttribute(attribute);
|
|
73
|
+
dispose();
|
|
72
74
|
marked.delete(row);
|
|
73
75
|
}
|
|
74
|
-
for (const row of rows) if (labels.includes(row.textContent?.trim() ?? "")) {
|
|
76
|
+
for (const row of rows) if (!marked.has(row) && labels.includes(row.textContent?.trim() ?? "")) {
|
|
75
77
|
row.setAttribute(attribute, id);
|
|
76
|
-
|
|
78
|
+
let dispose = () => {};
|
|
79
|
+
if (typeof icon !== "string") {
|
|
80
|
+
const glyph = document.createElement("span");
|
|
81
|
+
glyph.setAttribute("data-dsh-settings-icon-glyph", "");
|
|
82
|
+
glyph.setAttribute("aria-hidden", "true");
|
|
83
|
+
row.prepend(glyph);
|
|
84
|
+
const unmount = icon(glyph);
|
|
85
|
+
dispose = () => {
|
|
86
|
+
unmount();
|
|
87
|
+
glyph.remove();
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
marked.set(row, dispose);
|
|
77
91
|
}
|
|
78
92
|
};
|
|
79
93
|
const observer = new MutationObserver(refresh);
|
|
@@ -85,7 +99,10 @@ ${selector}::before { content: ''; display: block; flex: none; width: 16px; heig
|
|
|
85
99
|
refresh();
|
|
86
100
|
return () => {
|
|
87
101
|
observer.disconnect();
|
|
88
|
-
for (const row of marked)
|
|
102
|
+
for (const [row, dispose] of marked) {
|
|
103
|
+
row.removeAttribute(attribute);
|
|
104
|
+
dispose();
|
|
105
|
+
}
|
|
89
106
|
removeStyles();
|
|
90
107
|
};
|
|
91
108
|
}
|
|
@@ -158,7 +175,10 @@ function isWorktreeProgress(value) {
|
|
|
158
175
|
].includes(progress.status) && Number.isFinite(progress.updatedAt) && Array.isArray(progress.durations) && progress.durations.length === 4 && progress.durations.every((duration) => Number.isFinite(duration) && duration >= 0) && (progress.branch === void 0 || typeof progress.branch === "string");
|
|
159
176
|
}
|
|
160
177
|
function comparablePath(path) {
|
|
161
|
-
|
|
178
|
+
const value = /^[a-z]:[\\/]/i.test(path) || path.startsWith("\\\\") || path.startsWith("//") ? path.replaceAll("\\", "/").replace(/^[A-Z]:/, (drive) => drive.toLowerCase()) : path;
|
|
179
|
+
let end = value.length;
|
|
180
|
+
while (end > 0 && value[end - 1] === "/") end--;
|
|
181
|
+
return value.slice(0, end);
|
|
162
182
|
}
|
|
163
183
|
function currentWorktree(view) {
|
|
164
184
|
if (!view) return void 0;
|
|
@@ -168,10 +188,15 @@ function currentWorktree(view) {
|
|
|
168
188
|
return cwd === path || cwd.startsWith(path + "/");
|
|
169
189
|
}).sort((a, b) => comparablePath(b.path).length - comparablePath(a.path).length)[0];
|
|
170
190
|
}
|
|
171
|
-
function
|
|
191
|
+
function isWorktreeSelectionView(value) {
|
|
172
192
|
if (!value || typeof value !== "object") return false;
|
|
173
193
|
const view = value;
|
|
174
|
-
return typeof view.cwd === "string" && (view.
|
|
194
|
+
return typeof view.cwd === "string" && (view.locked === void 0 || typeof view.locked === "boolean") && (view.pendingCreation === void 0 || view.pendingCreation !== null && typeof view.pendingCreation.name === "string" && typeof view.pendingCreation.failed === "boolean");
|
|
195
|
+
}
|
|
196
|
+
function isWorktreeView(value) {
|
|
197
|
+
if (!isWorktreeSelectionView(value)) return false;
|
|
198
|
+
const view = value;
|
|
199
|
+
return (view.needsInitialization === void 0 || typeof view.needsInitialization === "boolean") && Array.isArray(view.worktrees) && view.worktrees.every((item) => item && typeof item.path === "string" && (item.branch === null || typeof item.branch === "string"));
|
|
175
200
|
}
|
|
176
201
|
// src/errors.ts
|
|
177
202
|
const failureCodes = [
|
|
@@ -206,17 +231,22 @@ function failureCode(error) {
|
|
|
206
231
|
// src/requests.ts
|
|
207
232
|
const copy$1 = {
|
|
208
233
|
zh: {
|
|
209
|
-
title: "
|
|
234
|
+
title: "下一任务的位置",
|
|
235
|
+
choose: "选择工作树",
|
|
236
|
+
savingSelection: "正在保存下一任务的位置…",
|
|
237
|
+
selectionError: "请确认工作树选择后再发送提示。",
|
|
238
|
+
locked: "任务开始后无法更改此会话的工作树。请在其他工作树中新建会话。",
|
|
210
239
|
detached: "分离的 HEAD",
|
|
211
240
|
unknown: "无法获取分支",
|
|
212
241
|
create: "新工作树",
|
|
213
|
-
|
|
214
|
-
|
|
242
|
+
primaryBranch: "当前分支",
|
|
243
|
+
reservationHint: "发送下一条提示后,将先创建并切换到新工作树再开始任务。选择当前分支可取消。",
|
|
244
|
+
creationError: "无法确认创建结果,尚未开始任务。请选择当前分支或重新预约,然后重新发送提示。",
|
|
215
245
|
close: "关闭",
|
|
216
246
|
refresh: "刷新工作树",
|
|
217
247
|
loading: "正在加载工作树…",
|
|
218
248
|
busy: "正在切换工作树…",
|
|
219
|
-
changed: "
|
|
249
|
+
changed: "已选择下一任务的位置。",
|
|
220
250
|
empty: "未找到 Git 工作树。",
|
|
221
251
|
wait: "请等待会话准备就绪且当前任务结束。",
|
|
222
252
|
error: "无法确认当前状态。请刷新工作树以检查状态后再重试。",
|
|
@@ -228,12 +258,13 @@ const copy$1 = {
|
|
|
228
258
|
errorOperation: "请求",
|
|
229
259
|
operations: {
|
|
230
260
|
describe: "查询工作树",
|
|
261
|
+
initialize: "初始化工作树选择",
|
|
231
262
|
create: "创建工作树",
|
|
232
|
-
|
|
263
|
+
select: "保存下一任务的位置"
|
|
233
264
|
},
|
|
234
265
|
reasons: {
|
|
235
266
|
busy: "会话中有正在执行的任务。请在任务结束后重新查询。",
|
|
236
|
-
conflict: "
|
|
267
|
+
conflict: "当前工作目录已更改或工作树选择已锁定。请重新查询状态。",
|
|
237
268
|
invalid: "请求无效。请刷新列表并检查分支名称或工作树。",
|
|
238
269
|
operation_failed: "主机无法完成操作。请检查 Git 和工作树配置。",
|
|
239
270
|
session_not_found: "会话不可用。请重新打开会话后重试。",
|
|
@@ -241,23 +272,28 @@ const copy$1 = {
|
|
|
241
272
|
auth_required: "连接需要身份验证。请重新打开应用后重试。",
|
|
242
273
|
endpoint_missing: "主机没有工作树接口。请确认插件已启用并重启应用。",
|
|
243
274
|
client_unavailable: "连接缺少工作树 RPC 客户端。请刷新页面或更新应用。",
|
|
244
|
-
invalid_response: "
|
|
275
|
+
invalid_response: "主机返回的工作树状态无效。请检查应用和插件版本。",
|
|
245
276
|
connection_error: "收到有效响应前连接失败。请检查连接后重新查询。",
|
|
246
277
|
timeout: "请求超时。请在当前任务结束后重新查询。"
|
|
247
278
|
}
|
|
248
279
|
},
|
|
249
280
|
en: {
|
|
250
|
-
title: "
|
|
281
|
+
title: "Next task location",
|
|
282
|
+
choose: "Choose worktree",
|
|
283
|
+
savingSelection: "Saving next task location…",
|
|
284
|
+
selectionError: "Confirm the worktree selection before sending your prompt.",
|
|
285
|
+
locked: "The worktree cannot change after work starts. Create a new session in another worktree.",
|
|
251
286
|
detached: "Detached HEAD",
|
|
252
287
|
unknown: "Branch unavailable",
|
|
253
288
|
create: "New worktree",
|
|
254
|
-
|
|
255
|
-
|
|
289
|
+
primaryBranch: "Current branch",
|
|
290
|
+
reservationHint: "The next prompt will create and enter a new worktree before starting work. Select the current branch to cancel.",
|
|
291
|
+
creationError: "Creation could not be confirmed, so work did not start. Select the current branch or schedule a new worktree, then resend your prompt.",
|
|
256
292
|
close: "Close",
|
|
257
293
|
refresh: "Refresh worktrees",
|
|
258
294
|
loading: "Loading worktrees…",
|
|
259
295
|
busy: "Changing worktree…",
|
|
260
|
-
changed: "
|
|
296
|
+
changed: "Next task location selected.",
|
|
261
297
|
empty: "No Git worktrees found.",
|
|
262
298
|
wait: "Wait until the session is ready and idle.",
|
|
263
299
|
error: "Could not confirm the current state. Refresh worktrees to check before trying again.",
|
|
@@ -269,12 +305,13 @@ const copy$1 = {
|
|
|
269
305
|
errorOperation: "Request",
|
|
270
306
|
operations: {
|
|
271
307
|
describe: "List worktrees",
|
|
308
|
+
initialize: "Initialize worktree selection",
|
|
272
309
|
create: "Create worktree",
|
|
273
|
-
|
|
310
|
+
select: "Save next task location"
|
|
274
311
|
},
|
|
275
312
|
reasons: {
|
|
276
313
|
busy: "The session has an active task. Retry after the task stops.",
|
|
277
|
-
conflict: "The current directory changed
|
|
314
|
+
conflict: "The current directory changed or the worktree selection is locked. Reload to check the state.",
|
|
278
315
|
invalid: "The request is invalid. Reload the list and check the branch name or worktree.",
|
|
279
316
|
operation_failed: "The host could not complete the operation. Check Git and the worktree configuration.",
|
|
280
317
|
session_not_found: "The session is unavailable. Reopen the session and retry.",
|
|
@@ -282,23 +319,28 @@ const copy$1 = {
|
|
|
282
319
|
auth_required: "The connection requires authentication. Reopen the application and retry.",
|
|
283
320
|
endpoint_missing: "The host has no worktree endpoint. Check that the plugin is active and restart the application.",
|
|
284
321
|
client_unavailable: "The connection has no worktree RPC client. Reload the page or update the application.",
|
|
285
|
-
invalid_response: "The host returned an invalid worktree
|
|
322
|
+
invalid_response: "The host returned an invalid worktree state. Check the application and plugin versions.",
|
|
286
323
|
connection_error: "The connection failed before a valid response. Check the connection and retry.",
|
|
287
324
|
timeout: "The request timed out. Retry after the current task stops."
|
|
288
325
|
}
|
|
289
326
|
},
|
|
290
327
|
ko: {
|
|
291
|
-
title: "
|
|
328
|
+
title: "다음 작업 위치",
|
|
329
|
+
choose: "워크트리 선택",
|
|
330
|
+
savingSelection: "다음 작업 위치를 저장하는 중…",
|
|
331
|
+
selectionError: "워크트리 선택을 확인한 뒤 프롬프트를 전송하세요.",
|
|
332
|
+
locked: "작업을 시작한 세션에서는 워크트리를 바꿀 수 없습니다. 다른 워크트리에서 새 세션을 만드세요.",
|
|
292
333
|
detached: "분리된 HEAD",
|
|
293
334
|
unknown: "브랜치 확인 불가",
|
|
294
335
|
create: "새 워크트리",
|
|
295
|
-
|
|
296
|
-
|
|
336
|
+
primaryBranch: "현재 브랜치",
|
|
337
|
+
reservationHint: "다음 프롬프트를 전송하면 새 워크트리를 생성·전환한 뒤 작업을 시작합니다. 현재 브랜치를 선택하면 예약을 취소합니다.",
|
|
338
|
+
creationError: "생성 결과를 확인하지 못해 작업을 시작하지 않았습니다. 현재 브랜치를 선택하거나 다시 예약한 뒤 프롬프트를 다시 전송하세요.",
|
|
297
339
|
close: "닫기",
|
|
298
340
|
refresh: "워크트리 새로고침",
|
|
299
341
|
loading: "워크트리를 불러오는 중…",
|
|
300
342
|
busy: "워크트리 변경 중…",
|
|
301
|
-
changed: "
|
|
343
|
+
changed: "다음 작업 위치를 선택했습니다.",
|
|
302
344
|
empty: "Git worktree가 없습니다.",
|
|
303
345
|
wait: "세션이 준비되고 실행 중인 작업이 끝나면 사용할 수 있습니다.",
|
|
304
346
|
error: "현재 상태를 확인하지 못했습니다. 다시 시도하기 전에 새로고침으로 상태를 확인하세요.",
|
|
@@ -310,12 +352,13 @@ const copy$1 = {
|
|
|
310
352
|
errorOperation: "요청",
|
|
311
353
|
operations: {
|
|
312
354
|
describe: "워크트리 조회",
|
|
355
|
+
initialize: "워크트리 선택 초기화",
|
|
313
356
|
create: "워크트리 생성",
|
|
314
|
-
|
|
357
|
+
select: "다음 작업 위치 저장"
|
|
315
358
|
},
|
|
316
359
|
reasons: {
|
|
317
360
|
busy: "세션에서 작업을 실행 중입니다. 작업이 끝난 뒤 다시 조회하세요.",
|
|
318
|
-
conflict: "현재 작업 경로가
|
|
361
|
+
conflict: "현재 작업 경로가 바뀌었거나 워크트리 선택이 고정됐습니다. 다시 조회해 상태를 확인하세요.",
|
|
319
362
|
invalid: "요청이 올바르지 않습니다. 목록을 새로고침하고 브랜치 이름이나 워크트리를 확인하세요.",
|
|
320
363
|
operation_failed: "호스트가 작업을 완료하지 못했습니다. Git과 워크트리 설정을 확인하세요.",
|
|
321
364
|
session_not_found: "세션을 찾지 못했습니다. 세션을 다시 열고 조회하세요.",
|
|
@@ -323,7 +366,7 @@ const copy$1 = {
|
|
|
323
366
|
auth_required: "연결 인증이 필요합니다. 앱을 다시 열고 조회하세요.",
|
|
324
367
|
endpoint_missing: "호스트에 워크트리 API가 없습니다. 플러그인을 활성화했는지 확인하고 앱을 다시 시작하세요.",
|
|
325
368
|
client_unavailable: "연결에 워크트리 RPC 기능이 없습니다. 페이지를 새로고침하거나 앱을 업데이트하세요.",
|
|
326
|
-
invalid_response: "호스트가 올바르지 않은 워크트리
|
|
369
|
+
invalid_response: "호스트가 올바르지 않은 워크트리 상태를 반환했습니다. 앱과 플러그인 버전을 확인하세요.",
|
|
327
370
|
connection_error: "정상 응답을 받기 전에 연결이 실패했습니다. 연결을 확인하고 다시 조회하세요.",
|
|
328
371
|
timeout: "요청 시간이 초과됐습니다. 실행 중인 작업이 끝난 뒤 다시 조회하세요."
|
|
329
372
|
}
|
|
@@ -338,6 +381,8 @@ const copy = {
|
|
|
338
381
|
new: "새 워크트리",
|
|
339
382
|
directory: "워크트리 풀 디렉터리",
|
|
340
383
|
remote: "추적 중인 원격 브랜치 사용",
|
|
384
|
+
deleteOnArchive: "보관 시 워크트리 삭제",
|
|
385
|
+
deleteOnArchiveDescription: "이 플러그인이 만든 워크트리만 삭제합니다. 미커밋·미추적·무시된 파일이나 다른 세션이 있으면 유지하며, 브랜치는 삭제하지 않습니다.",
|
|
341
386
|
naming: "자동 브랜치 이름 형식",
|
|
342
387
|
semantic: "시멘틱 (feat/… 또는 fix/…)",
|
|
343
388
|
dsh: "DSH (dsh/*)",
|
|
@@ -358,6 +403,8 @@ const copy = {
|
|
|
358
403
|
new: "新工作树",
|
|
359
404
|
directory: "工作树池目录",
|
|
360
405
|
remote: "使用跟踪的远程分支",
|
|
406
|
+
deleteOnArchive: "归档时删除工作树",
|
|
407
|
+
deleteOnArchiveDescription: "仅删除此插件创建的工作树。有未提交、未跟踪、被忽略的文件或其他会话使用时保留,不删除分支。",
|
|
361
408
|
naming: "自动分支命名格式",
|
|
362
409
|
semantic: "语义化(feat/… 或 fix/…)",
|
|
363
410
|
dsh: "DSH(dsh/*)",
|
|
@@ -378,6 +425,8 @@ const copy = {
|
|
|
378
425
|
new: "New worktree",
|
|
379
426
|
directory: "Worktree pool directory",
|
|
380
427
|
remote: "Use tracked remote branch",
|
|
428
|
+
deleteOnArchive: "Delete worktree on archive",
|
|
429
|
+
deleteOnArchiveDescription: "Only removes worktrees created by this plugin. Keeps worktrees with uncommitted, untracked or ignored files, or other sessions. Branches are kept.",
|
|
381
430
|
naming: "Automatic branch naming",
|
|
382
431
|
semantic: "Semantic (feat/… or fix/…)",
|
|
383
432
|
dsh: "DSH (dsh/*)",
|
|
@@ -586,6 +635,18 @@ function WorktreeSettingsSection({ ctx }) {
|
|
|
586
635
|
onChange: (remote) => change({ remote })
|
|
587
636
|
})
|
|
588
637
|
}),
|
|
638
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsRow, {
|
|
639
|
+
label: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
640
|
+
className: "dsh-worktree-settings-title",
|
|
641
|
+
children: text.deleteOnArchive
|
|
642
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: text.deleteOnArchiveDescription })] }),
|
|
643
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
644
|
+
label: text.deleteOnArchive,
|
|
645
|
+
checked: draft.deleteOnArchive,
|
|
646
|
+
disabled,
|
|
647
|
+
onChange: (deleteOnArchive) => change({ deleteOnArchive })
|
|
648
|
+
})
|
|
649
|
+
}),
|
|
589
650
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsRow, {
|
|
590
651
|
label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
591
652
|
id: `${id}-naming`,
|
|
@@ -652,10 +713,17 @@ function WorktreeSettingsSection({ ctx }) {
|
|
|
652
713
|
// src/styles.ts
|
|
653
714
|
const styles = `${settingsStyles}${composerStyles}
|
|
654
715
|
.dsh-worktree-sidebar-name { flex: 0 1 auto; min-width: 0; max-width: 88px; margin-inline: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-caption); font-size: 10px; line-height: 16px; }
|
|
716
|
+
[data-row-key]:has(.dsh-vcs-session-request) > .dsh-worktree-sidebar-name { display: none; }
|
|
655
717
|
.dsh-worktree { display: inline-flex; flex-wrap: wrap; align-items: center; min-width: 0; max-width: 100%; color: var(--dsw-alias-label-secondary); font: inherit; }
|
|
656
718
|
.dsh-worktree-picker { display: inline-flex; min-width: 0; max-width: 100%; }
|
|
719
|
+
.dsh-worktree-pinned { position: sticky; top: 0; z-index: 1; background: var(--dsw-alias-bg-layer-1); }
|
|
720
|
+
.dsh-worktree-option { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
721
|
+
.dsh-worktree-option > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
722
|
+
.dsh-worktree-option > svg { flex: none; width: 14px; height: 14px; margin-inline-start: auto; color: var(--dsw-alias-label-primary); }
|
|
723
|
+
.dsh-worktree-badge { flex: none; padding: 1px 5px; border: 1px solid var(--dsw-alias-border-l2); border-radius: var(--dsw-radius-sm); color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 16px; font-weight: 400; }
|
|
657
724
|
.dsh-worktree-trigger { display: inline-flex; max-width: 220px; font-weight: 500; }
|
|
658
725
|
.dsh-worktree-trigger .dsh-plugin-composer-icon svg { width: 14px; height: 14px; }
|
|
726
|
+
.dsh-worktree-trigger:not([aria-haspopup]) .dsh-plugin-composer-chevron { display: none; }
|
|
659
727
|
.dsh-worktree-trigger:focus-visible { outline: 2px solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); outline-offset: 0; }
|
|
660
728
|
@container (width <= 460px) {
|
|
661
729
|
.dsh-worktree-trigger .dsh-plugin-composer-label { display: none; }
|
|
@@ -894,7 +962,8 @@ const inject = [
|
|
|
894
962
|
"slots",
|
|
895
963
|
"locale",
|
|
896
964
|
"connection",
|
|
897
|
-
"uiConversation"
|
|
965
|
+
"uiConversation",
|
|
966
|
+
"conversation"
|
|
898
967
|
];
|
|
899
968
|
function apply(ctx) {
|
|
900
969
|
ctx.effect(() => installSettingsIcon(name, [
|
|
@@ -904,19 +973,47 @@ function apply(ctx) {
|
|
|
904
973
|
].map(settingsLabel), git_branch_default));
|
|
905
974
|
const connection = ctx.get("connection");
|
|
906
975
|
installProgress(ctx);
|
|
907
|
-
|
|
976
|
+
const synced = /* @__PURE__ */ new Map();
|
|
977
|
+
const ownedBlocks = /* @__PURE__ */ new Map();
|
|
978
|
+
const blocks = ctx.conversation.blocks;
|
|
979
|
+
ctx.effect(() => () => {
|
|
980
|
+
const owned = [...ownedBlocks];
|
|
981
|
+
ownedBlocks.clear();
|
|
982
|
+
for (const [id, block] of owned) if (blocks.storeFor(id).getSnapshot() === block) blocks.set(id, void 0);
|
|
983
|
+
});
|
|
984
|
+
function Picker({ sessionId, useSession, useProjection, useInput }) {
|
|
908
985
|
const { active: language } = useLocaleSnapshot(ctx.locale);
|
|
909
986
|
const text = copy$1[language.startsWith("ko") ? "ko" : language.startsWith("zh") ? "zh" : "en"];
|
|
910
|
-
const
|
|
911
|
-
const
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
const
|
|
915
|
-
const
|
|
987
|
+
const inputBusy = useInput((input) => input.phase !== "plain");
|
|
988
|
+
const unavailable = useSession((s) => s.running || s.removed || s.openState !== "open" || s.pendingSubmissions.length > 0) || inputBusy;
|
|
989
|
+
const unavailableRef = (0, react.useRef)(unavailable);
|
|
990
|
+
unavailableRef.current = unavailable;
|
|
991
|
+
const unreadable = useSession((s) => s.removed || s.openState !== "open");
|
|
992
|
+
const unreadableRef = (0, react.useRef)(unreadable);
|
|
993
|
+
unreadableRef.current = unreadable;
|
|
994
|
+
const cwd = useProjection("workingDirectory");
|
|
995
|
+
const started = useProjection("sessionListMetadata")?.blank === false;
|
|
996
|
+
const cached = synced.get(sessionId);
|
|
997
|
+
const initialView = cached?.cwd === cwd ? cached : void 0;
|
|
998
|
+
const previousReadiness = (0, react.useRef)({
|
|
999
|
+
unreadable: true,
|
|
1000
|
+
unavailable,
|
|
1001
|
+
cwd
|
|
1002
|
+
});
|
|
1003
|
+
const [view, setView] = (0, react.useState)(initialView);
|
|
1004
|
+
const [loading, setLoading] = (0, react.useState)(!initialView);
|
|
1005
|
+
const viewRef = (0, react.useRef)(view);
|
|
1006
|
+
viewRef.current = view;
|
|
1007
|
+
const selected = (0, react.useRef)();
|
|
1008
|
+
const saving = (0, react.useRef)(false);
|
|
1009
|
+
const version = (0, react.useRef)(0);
|
|
916
1010
|
const [error, setError] = (0, react.useState)();
|
|
917
1011
|
const [changed, setChanged] = (0, react.useState)(false);
|
|
918
1012
|
const [open, setOpen] = (0, react.useState)(false);
|
|
1013
|
+
const needsSync = (0, react.useRef)(true);
|
|
1014
|
+
needsSync.current = !view || !view.locked && (!!view.needsInitialization || !!view.pendingCreation);
|
|
919
1015
|
const pending = (0, react.useRef)(false);
|
|
1016
|
+
const reloadRequested = (0, react.useRef)(false);
|
|
920
1017
|
const needsRefresh = (0, react.useRef)(false);
|
|
921
1018
|
const lifetime = (0, react.useRef)(null);
|
|
922
1019
|
const trigger = (0, react.useRef)(null);
|
|
@@ -924,52 +1021,112 @@ function apply(ctx) {
|
|
|
924
1021
|
const errorTitle = (0, react.useId)();
|
|
925
1022
|
const errorDescription = (0, react.useId)();
|
|
926
1023
|
const status = (0, react.useId)();
|
|
927
|
-
const disabled = unavailable || loading || busy || !!error || !view;
|
|
928
1024
|
const creation = view?.pendingCreation;
|
|
1025
|
+
const locked = !!view?.locked || started && !creation;
|
|
1026
|
+
const disabled = locked || unavailable || loading || !view;
|
|
929
1027
|
const detailError = error ?? (creation?.failed ? {
|
|
930
1028
|
operation: "create",
|
|
931
1029
|
code: "operation_failed",
|
|
932
1030
|
uncertain: true
|
|
933
1031
|
} : void 0);
|
|
934
1032
|
const errorLabel = detailError?.uncertain ? text.mutationError : text.lookupError;
|
|
1033
|
+
const gate = (reason) => {
|
|
1034
|
+
const store = blocks.storeFor(sessionId);
|
|
1035
|
+
const owned = ownedBlocks.get(sessionId);
|
|
1036
|
+
const current = store.getSnapshot();
|
|
1037
|
+
if (!reason) {
|
|
1038
|
+
ownedBlocks.delete(sessionId);
|
|
1039
|
+
if (owned && current === owned) blocks.set(sessionId, void 0);
|
|
1040
|
+
} else {
|
|
1041
|
+
synced.delete(sessionId);
|
|
1042
|
+
const block = owned?.reason === reason ? owned : { reason };
|
|
1043
|
+
ownedBlocks.set(sessionId, block);
|
|
1044
|
+
if (!current || current === owned) blocks.set(sessionId, block);
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
935
1047
|
const recordFailure = (operation, cause) => {
|
|
936
1048
|
const code = failureCode(cause);
|
|
937
1049
|
needsRefresh.current = true;
|
|
1050
|
+
const uncertain = operation !== "describe" || ownedBlocks.has(sessionId);
|
|
1051
|
+
if (uncertain) gate(text.selectionError);
|
|
938
1052
|
setError((previous) => ({
|
|
939
1053
|
operation,
|
|
940
1054
|
code,
|
|
941
|
-
uncertain:
|
|
1055
|
+
uncertain: uncertain || !!previous?.uncertain
|
|
942
1056
|
}));
|
|
943
1057
|
console.warn("[dungsil-ui-worktree]", {
|
|
944
1058
|
operation,
|
|
945
1059
|
code
|
|
946
1060
|
});
|
|
947
1061
|
};
|
|
948
|
-
const
|
|
1062
|
+
const call = async (operation, payload, signal) => {
|
|
949
1063
|
if (typeof connection?.rpc?.call !== "function") throw { code: "client_unavailable" };
|
|
950
1064
|
const result = await connection.rpc.call(channel, endpointPrefix + operation, payload, signal);
|
|
951
1065
|
signal.throwIfAborted();
|
|
952
1066
|
if (!result.ok) throw result.error;
|
|
953
|
-
if (!isWorktreeView(result.value)) throw { code: "invalid_response" };
|
|
954
1067
|
return result.value;
|
|
955
1068
|
};
|
|
1069
|
+
const request = async (operation, payload, signal) => {
|
|
1070
|
+
const value = await call(operation, payload, signal);
|
|
1071
|
+
if (!isWorktreeView(value)) throw { code: "invalid_response" };
|
|
1072
|
+
return value;
|
|
1073
|
+
};
|
|
1074
|
+
const updateView = (next) => {
|
|
1075
|
+
viewRef.current = next;
|
|
1076
|
+
setView(next);
|
|
1077
|
+
};
|
|
1078
|
+
const mergeSelection = (next) => {
|
|
1079
|
+
const previous = viewRef.current;
|
|
1080
|
+
if (!previous) return;
|
|
1081
|
+
updateView({
|
|
1082
|
+
...previous,
|
|
1083
|
+
cwd: next.cwd,
|
|
1084
|
+
pendingCreation: next.pendingCreation,
|
|
1085
|
+
locked: next.locked,
|
|
1086
|
+
needsInitialization: false
|
|
1087
|
+
});
|
|
1088
|
+
};
|
|
956
1089
|
const load = async (explicit = false) => {
|
|
957
1090
|
const controller = lifetime.current;
|
|
958
|
-
if (!controller || controller.signal.aborted ||
|
|
1091
|
+
if (!controller || controller.signal.aborted || unreadableRef.current || saving.current || needsRefresh.current && !explicit) return;
|
|
1092
|
+
if (pending.current) {
|
|
1093
|
+
reloadRequested.current = true;
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
reloadRequested.current = false;
|
|
959
1097
|
pending.current = true;
|
|
960
1098
|
setLoading(true);
|
|
1099
|
+
let operation = "describe";
|
|
961
1100
|
try {
|
|
962
1101
|
const view = await request("describe", { sessionId }, controller.signal);
|
|
963
1102
|
if (controller.signal.aborted) return;
|
|
964
|
-
|
|
1103
|
+
updateView(view);
|
|
1104
|
+
if (view.needsInitialization && !view.locked && !unavailableRef.current) {
|
|
1105
|
+
operation = "initialize";
|
|
1106
|
+
gate(text.savingSelection);
|
|
1107
|
+
const initialized = await request("initialize", {
|
|
1108
|
+
sessionId,
|
|
1109
|
+
cwd: view.cwd
|
|
1110
|
+
}, controller.signal);
|
|
1111
|
+
if (controller.signal.aborted) return;
|
|
1112
|
+
updateView(initialized);
|
|
1113
|
+
}
|
|
965
1114
|
needsRefresh.current = false;
|
|
966
1115
|
setError(void 0);
|
|
1116
|
+
gate();
|
|
967
1117
|
} catch (cause) {
|
|
968
|
-
if (!controller.signal.aborted)
|
|
1118
|
+
if (!controller.signal.aborted) {
|
|
1119
|
+
if (failureCode(cause) !== "busy") recordFailure(operation, cause);
|
|
1120
|
+
else if (!needsRefresh.current && operation === "initialize") gate();
|
|
1121
|
+
}
|
|
969
1122
|
} finally {
|
|
970
1123
|
if (!controller.signal.aborted) {
|
|
971
1124
|
pending.current = false;
|
|
972
1125
|
setLoading(false);
|
|
1126
|
+
if (reloadRequested.current) {
|
|
1127
|
+
reloadRequested.current = false;
|
|
1128
|
+
load();
|
|
1129
|
+
}
|
|
973
1130
|
}
|
|
974
1131
|
}
|
|
975
1132
|
};
|
|
@@ -977,9 +1134,15 @@ function apply(ctx) {
|
|
|
977
1134
|
const controller = new AbortController();
|
|
978
1135
|
lifetime.current = controller;
|
|
979
1136
|
pending.current = false;
|
|
980
|
-
|
|
1137
|
+
const store = blocks.storeFor(sessionId);
|
|
1138
|
+
const restoreGate = () => {
|
|
1139
|
+
const block = ownedBlocks.get(sessionId);
|
|
1140
|
+
if (block && !store.getSnapshot()) blocks.set(sessionId, block);
|
|
1141
|
+
};
|
|
1142
|
+
restoreGate();
|
|
1143
|
+
const unsubscribe = store.subscribe(restoreGate);
|
|
981
1144
|
const focus = () => {
|
|
982
|
-
load();
|
|
1145
|
+
if (needsSync.current && !pending.current) load();
|
|
983
1146
|
};
|
|
984
1147
|
const resize = () => {
|
|
985
1148
|
setOpen(false);
|
|
@@ -988,50 +1151,94 @@ function apply(ctx) {
|
|
|
988
1151
|
window.addEventListener("resize", resize);
|
|
989
1152
|
return () => {
|
|
990
1153
|
controller.abort();
|
|
1154
|
+
unsubscribe();
|
|
991
1155
|
window.removeEventListener("focus", focus);
|
|
992
1156
|
window.removeEventListener("resize", resize);
|
|
993
1157
|
};
|
|
994
1158
|
}, []);
|
|
995
1159
|
(0, react.useEffect)(() => {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1160
|
+
const previous = previousReadiness.current;
|
|
1161
|
+
previousReadiness.current = {
|
|
1162
|
+
unreadable,
|
|
1163
|
+
unavailable,
|
|
1164
|
+
cwd
|
|
1165
|
+
};
|
|
1166
|
+
if (!unreadable && ((previous.unreadable || previous.unavailable && !unavailable) && needsSync.current || previous.cwd !== cwd)) load();
|
|
1167
|
+
}, [
|
|
1168
|
+
unreadable,
|
|
1169
|
+
unavailable,
|
|
1170
|
+
cwd
|
|
1171
|
+
]);
|
|
1172
|
+
(0, react.useEffect)(() => {
|
|
1173
|
+
if (locked && view && !creation && view.cwd === cwd && !error && !ownedBlocks.has(sessionId)) synced.set(sessionId, {
|
|
1174
|
+
...view,
|
|
1175
|
+
locked: true,
|
|
1176
|
+
needsInitialization: false
|
|
1177
|
+
});
|
|
1178
|
+
else synced.delete(sessionId);
|
|
1179
|
+
}, [
|
|
1180
|
+
sessionId,
|
|
1181
|
+
view,
|
|
1182
|
+
locked,
|
|
1183
|
+
creation,
|
|
1184
|
+
cwd,
|
|
1185
|
+
error
|
|
1186
|
+
]);
|
|
999
1187
|
(0, react.useEffect)(() => {
|
|
1000
1188
|
if (disabled) setOpen(false);
|
|
1001
1189
|
}, [disabled]);
|
|
1002
|
-
const change = async (
|
|
1190
|
+
const change = async (choice) => {
|
|
1003
1191
|
const controller = lifetime.current;
|
|
1004
|
-
|
|
1005
|
-
pending.current
|
|
1006
|
-
|
|
1007
|
-
|
|
1192
|
+
const currentView = viewRef.current;
|
|
1193
|
+
if (locked || unavailableRef.current || loading || pending.current || !currentView || !controller || controller.signal.aborted) return;
|
|
1194
|
+
const intent = {
|
|
1195
|
+
version: ++version.current,
|
|
1196
|
+
cwd: currentView.cwd,
|
|
1197
|
+
choice
|
|
1198
|
+
};
|
|
1199
|
+
selected.current = intent;
|
|
1200
|
+
mergeSelection({
|
|
1201
|
+
cwd: currentView.cwd,
|
|
1202
|
+
pendingCreation: choice === "new" ? {
|
|
1203
|
+
name: text.create,
|
|
1204
|
+
failed: false
|
|
1205
|
+
} : void 0
|
|
1206
|
+
});
|
|
1207
|
+
setError(void 0);
|
|
1208
|
+
setChanged(true);
|
|
1209
|
+
needsRefresh.current = false;
|
|
1210
|
+
gate(text.savingSelection);
|
|
1211
|
+
if (saving.current) return;
|
|
1212
|
+
saving.current = true;
|
|
1008
1213
|
try {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1214
|
+
while (selected.current && !controller.signal.aborted) {
|
|
1215
|
+
const next = selected.current;
|
|
1216
|
+
try {
|
|
1217
|
+
const value = await call("select", {
|
|
1218
|
+
sessionId,
|
|
1219
|
+
cwd: next.cwd,
|
|
1220
|
+
choice: next.choice
|
|
1221
|
+
}, controller.signal);
|
|
1222
|
+
if (!isWorktreeSelectionView(value)) throw { code: "invalid_response" };
|
|
1223
|
+
if (selected.current?.version !== next.version) continue;
|
|
1224
|
+
mergeSelection(value);
|
|
1225
|
+
selected.current = void 0;
|
|
1226
|
+
gate();
|
|
1227
|
+
} catch (cause) {
|
|
1228
|
+
if (controller.signal.aborted) return;
|
|
1229
|
+
if (selected.current?.version !== next.version) continue;
|
|
1230
|
+
selected.current = void 0;
|
|
1231
|
+
recordFailure("select", cause);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1029
1234
|
}
|
|
1235
|
+
} finally {
|
|
1236
|
+
saving.current = false;
|
|
1030
1237
|
}
|
|
1031
1238
|
};
|
|
1032
1239
|
(0, react.useEffect)(() => {
|
|
1033
|
-
if (
|
|
1034
|
-
}, [
|
|
1240
|
+
if (changed) trigger.current?.focus();
|
|
1241
|
+
}, [changed]);
|
|
1035
1242
|
(0, react.useEffect)(() => {
|
|
1036
1243
|
if (!detailError && errorDialog.current?.open) errorDialog.current.close();
|
|
1037
1244
|
}, [error, creation?.failed]);
|
|
@@ -1047,9 +1254,14 @@ function apply(ctx) {
|
|
|
1047
1254
|
current,
|
|
1048
1255
|
view
|
|
1049
1256
|
]);
|
|
1050
|
-
const
|
|
1257
|
+
const selectCurrent = () => {
|
|
1258
|
+
setOpen(false);
|
|
1259
|
+
if (disabled) return;
|
|
1260
|
+
if (viewRef.current?.pendingCreation || needsRefresh.current) change("current");
|
|
1261
|
+
};
|
|
1262
|
+
const branch = creation ? text.create : current ? current.branch ?? text.detached : text.unknown;
|
|
1051
1263
|
const label = detailError ? errorLabel : view ? branch : loading ? text.loading : text.title;
|
|
1052
|
-
const notice = loading ? text.loading :
|
|
1264
|
+
const notice = loading ? text.loading : error ? errorLabel : unavailable ? text.wait : locked ? text.locked : creation ? creation.failed ? text.creationError : text.reservationHint : changed ? text.changed : view?.worktrees.length === 0 ? text.empty : "";
|
|
1053
1265
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1054
1266
|
className: "dsh-worktree",
|
|
1055
1267
|
children: [
|
|
@@ -1058,39 +1270,23 @@ function apply(ctx) {
|
|
|
1058
1270
|
open,
|
|
1059
1271
|
side: "top",
|
|
1060
1272
|
portal: true,
|
|
1061
|
-
|
|
1062
|
-
items: [{
|
|
1063
|
-
id: "create",
|
|
1064
|
-
label: text.create,
|
|
1065
|
-
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, {})
|
|
1066
|
-
}, ...view?.worktrees.map((item) => ({
|
|
1067
|
-
id: `path:${item.path}`,
|
|
1068
|
-
label: item.branch ?? text.detached,
|
|
1069
|
-
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutlineRegular, {})
|
|
1070
|
-
})) ?? []],
|
|
1273
|
+
listClassName: "dsh-worktree-menu",
|
|
1071
1274
|
onClose: () => setOpen(false),
|
|
1072
|
-
onSelect: (id) => {
|
|
1073
|
-
setOpen(false);
|
|
1074
|
-
if (disabled || pending.current) return;
|
|
1075
|
-
if (id === "create") {
|
|
1076
|
-
if (!creation || creation.failed) change("create");
|
|
1077
|
-
} else if (id.startsWith("path:") && (creation || id !== `path:${current?.path}`)) change("switch", id.slice(5));
|
|
1078
|
-
},
|
|
1079
1275
|
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerTrigger, {
|
|
1080
1276
|
ref: trigger,
|
|
1081
1277
|
className: "dsh-worktree-trigger",
|
|
1082
1278
|
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutlineRegular, {}),
|
|
1083
1279
|
label,
|
|
1084
|
-
disabled: detailError ? loading
|
|
1280
|
+
disabled: detailError ? loading : disabled,
|
|
1085
1281
|
"aria-label": `${text.title}: ${label}`,
|
|
1086
|
-
"aria-haspopup": detailError ? "dialog" : "menu",
|
|
1087
|
-
"aria-expanded": detailError ? void 0 : open,
|
|
1282
|
+
"aria-haspopup": detailError ? "dialog" : locked ? void 0 : "menu",
|
|
1283
|
+
"aria-expanded": detailError || locked ? void 0 : open,
|
|
1088
1284
|
"aria-describedby": status,
|
|
1089
|
-
"aria-busy": loading
|
|
1090
|
-
title:
|
|
1285
|
+
"aria-busy": loading,
|
|
1286
|
+
title: locked ? text.locked : `${text.title}: ${branch}${creation ? ` — ${text.reservationHint}` : ""}`,
|
|
1091
1287
|
onClick: () => {
|
|
1092
1288
|
if (detailError) errorDialog.current?.showModal();
|
|
1093
|
-
else setOpen((value) => !value);
|
|
1289
|
+
else if (!disabled) setOpen((value) => !value);
|
|
1094
1290
|
},
|
|
1095
1291
|
onKeyDown: (event) => {
|
|
1096
1292
|
if (!detailError && !disabled && !open && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
|
|
@@ -1098,6 +1294,37 @@ function apply(ctx) {
|
|
|
1098
1294
|
setOpen(true);
|
|
1099
1295
|
}
|
|
1100
1296
|
}
|
|
1297
|
+
}),
|
|
1298
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1299
|
+
className: "dsh-worktree-pinned",
|
|
1300
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MenuItemButton, {
|
|
1301
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, {}),
|
|
1302
|
+
current: !!creation,
|
|
1303
|
+
onSelect: () => {
|
|
1304
|
+
setOpen(false);
|
|
1305
|
+
const latest = viewRef.current?.pendingCreation;
|
|
1306
|
+
if (!disabled && (!latest || latest.failed || needsRefresh.current)) change("new");
|
|
1307
|
+
},
|
|
1308
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1309
|
+
className: "dsh-worktree-option",
|
|
1310
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text.create }), creation && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutlineRegular, {})]
|
|
1311
|
+
})
|
|
1312
|
+
}), current && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MenuItemButton, {
|
|
1313
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutlineRegular, {}),
|
|
1314
|
+
current: !creation,
|
|
1315
|
+
onSelect: selectCurrent,
|
|
1316
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1317
|
+
className: "dsh-worktree-option",
|
|
1318
|
+
children: [
|
|
1319
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: current.branch ?? text.detached }),
|
|
1320
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1321
|
+
className: "dsh-worktree-badge",
|
|
1322
|
+
children: text.primaryBranch
|
|
1323
|
+
}),
|
|
1324
|
+
!creation && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutlineRegular, {})
|
|
1325
|
+
]
|
|
1326
|
+
})
|
|
1327
|
+
}, current.path)]
|
|
1101
1328
|
})
|
|
1102
1329
|
}),
|
|
1103
1330
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1132,13 +1359,22 @@ function apply(ctx) {
|
|
|
1132
1359
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: text.errorCode }),
|
|
1133
1360
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: detailError.code }) })
|
|
1134
1361
|
] })] }),
|
|
1135
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1362
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", { children: [view && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1363
|
+
type: "button",
|
|
1364
|
+
"data-worktree-choose": true,
|
|
1365
|
+
disabled,
|
|
1366
|
+
onClick: () => {
|
|
1367
|
+
errorDialog.current?.close();
|
|
1368
|
+
setOpen(true);
|
|
1369
|
+
},
|
|
1370
|
+
children: text.choose
|
|
1371
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1136
1372
|
type: "button",
|
|
1137
1373
|
"data-worktree-retry": true,
|
|
1138
|
-
disabled: loading
|
|
1374
|
+
disabled: loading,
|
|
1139
1375
|
onClick: () => void load(true),
|
|
1140
1376
|
children: loading ? text.loading : text.refresh
|
|
1141
|
-
}) })
|
|
1377
|
+
})] })
|
|
1142
1378
|
]
|
|
1143
1379
|
})
|
|
1144
1380
|
]
|