zen-gitsync 2.13.4 → 2.13.5
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/package.json +1 -1
- package/src/ui/public/assets/EditorView-ClwvgXDr.js +0 -0
- package/src/ui/public/assets/SourceMapView-JtcpliBT.js +3 -0
- package/src/ui/public/assets/WorkbenchView-DbuvMS0c.css +1 -0
- package/src/ui/public/assets/WorkbenchView-N_UzEo17.js +6 -0
- package/src/ui/public/assets/{_plugin-vue_export-helper-CCKxXnb6.js → _plugin-vue_export-helper-Bjo1gczS.js} +3 -3
- package/src/ui/public/assets/index-DYzZstrt.js +66 -0
- package/src/ui/public/assets/{vendor-DpYOE9sy.css → vendor-Bq2rS2vY.css} +1 -1
- package/src/ui/public/assets/{vendor-WSdfT3f8.js → vendor-C0_tGl5f.js} +242 -242
- package/src/ui/public/index.html +4 -4
- package/src/ui/server/routes/workbench.js +358 -4
- package/src/ui/public/assets/EditorView-CNH166hf.js +0 -0
- package/src/ui/public/assets/SourceMapView-p6CE2eoi.js +0 -3
- package/src/ui/public/assets/WorkbenchView-Cmrq7MJT.js +0 -2
- package/src/ui/public/assets/WorkbenchView-DnSRDRkp.css +0 -1
- package/src/ui/public/assets/index-B9iw-Qvb.js +0 -66
package/src/ui/public/index.html
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
11
11
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
12
12
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-DYzZstrt.js"></script>
|
|
14
14
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-CMxvf4Kt.js">
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
16
|
-
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-
|
|
17
|
-
<link rel="stylesheet" crossorigin href="/assets/vendor-
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-C0_tGl5f.js">
|
|
16
|
+
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-Bjo1gczS.js">
|
|
17
|
+
<link rel="stylesheet" crossorigin href="/assets/vendor-Bq2rS2vY.css">
|
|
18
18
|
<link rel="stylesheet" crossorigin href="/assets/index-CV4VteDa.css">
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
|
@@ -30,6 +30,15 @@ const TASKS_FILE = path.join(DATA_DIR, 'tasks.json');
|
|
|
30
30
|
const IMAGES_DIR = path.join(DATA_DIR, 'workbench-images');
|
|
31
31
|
const INSTRUCTION_FILE = path.join(DATA_DIR, 'ai-instruction.json');
|
|
32
32
|
const SUBTASK_INSTRUCTION_FILE = path.join(DATA_DIR, 'ai-subtask-instruction.json');
|
|
33
|
+
// 执行日志持久化:jobs.json 是历史档案,jobs-config.json 是保留策略。
|
|
34
|
+
// jobs Map 仍只承载当前进程产出的活跃 job;管理页直接读 jobs.json。
|
|
35
|
+
const JOBS_FILE = path.join(DATA_DIR, 'jobs.json');
|
|
36
|
+
const JOBS_CONFIG_FILE = path.join(DATA_DIR, 'jobs-config.json');
|
|
37
|
+
// 流式 chunk 写盘会撑爆 IO;用 1.5s debounce 把高频写入折叠成一次。
|
|
38
|
+
// 终态时由 flushJobsSaveNow() 强制立即落盘。
|
|
39
|
+
const JOBS_SAVE_DEBOUNCE_MS = 1500;
|
|
40
|
+
let jobsSaveTimer = null;
|
|
41
|
+
const DEFAULT_JOBS_CONFIG = { maxCount: 500, maxSizeMB: 256 };
|
|
33
42
|
|
|
34
43
|
// 子项目识别 / 文件扫描时需要跳过的目录
|
|
35
44
|
const SKIP_DIRS = new Set([
|
|
@@ -434,6 +443,131 @@ async function writeJson(file, data) {
|
|
|
434
443
|
await fsp.rename(tmp, file);
|
|
435
444
|
}
|
|
436
445
|
|
|
446
|
+
// ── 执行日志持久化 ────────────────────────────────────────────
|
|
447
|
+
// 设计要点:
|
|
448
|
+
// - 写盘在流式 chunk 阶段走 1.5s debounce;终态时(finally / cancel)强制 flush。
|
|
449
|
+
// - 永不持久化 child 引用(参考 cancel 路由的浅拷贝模式)。
|
|
450
|
+
// - hydrate 时把 running/pending 降级为 error:原 child 进程已不存在。
|
|
451
|
+
// - enforceRetention 在每次落盘后跑,按 endedAt desc FIFO 裁剪。
|
|
452
|
+
|
|
453
|
+
function serializeJob(j, taskMap) {
|
|
454
|
+
// child 是 ChildProcess 引用,序列化会爆;剥离后 size 用三字段累加预计算
|
|
455
|
+
const { child, ...rest } = j
|
|
456
|
+
const t = taskMap ? taskMap.get(rest.taskId) : null
|
|
457
|
+
const sub = t && Array.isArray(t.subtasks) ? t.subtasks.find(s => s.id === rest.subId) : null
|
|
458
|
+
const size = ((rest.prompt || '').length
|
|
459
|
+
+ (rest.output || '').length
|
|
460
|
+
+ (rest.thinking || '').length)
|
|
461
|
+
return {
|
|
462
|
+
...rest,
|
|
463
|
+
taskTitle: t ? t.title : '',
|
|
464
|
+
subTitle: sub ? sub.title : '',
|
|
465
|
+
size
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function scheduleJobsSave() {
|
|
470
|
+
if (jobsSaveTimer) clearTimeout(jobsSaveTimer)
|
|
471
|
+
jobsSaveTimer = setTimeout(() => {
|
|
472
|
+
jobsSaveTimer = null
|
|
473
|
+
flushJobsSaveNow().catch(err => console.warn('[workbench] jobs save failed:', err.message))
|
|
474
|
+
}, JOBS_SAVE_DEBOUNCE_MS)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
async function flushJobsSaveNow() {
|
|
478
|
+
if (jobsSaveTimer) { clearTimeout(jobsSaveTimer); jobsSaveTimer = null }
|
|
479
|
+
// 读 tasks.json 给落盘 job 反范式 taskTitle/subTitle——父任务被删后管理页仍可读
|
|
480
|
+
const tasksData = await readJson(TASKS_FILE, { tasks: [] })
|
|
481
|
+
const taskMap = new Map((tasksData.tasks || []).map(t => [t.id, t]))
|
|
482
|
+
const payload = {
|
|
483
|
+
version: 1,
|
|
484
|
+
jobs: Array.from(jobs.values()).map(j => serializeJob(j, taskMap))
|
|
485
|
+
}
|
|
486
|
+
await writeJson(JOBS_FILE, payload)
|
|
487
|
+
await enforceRetention()
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async function readJobsConfig() {
|
|
491
|
+
const cfg = await readJson(JOBS_CONFIG_FILE, null)
|
|
492
|
+
if (!cfg || typeof cfg !== 'object') return { ...DEFAULT_JOBS_CONFIG }
|
|
493
|
+
return {
|
|
494
|
+
maxCount: Number.isFinite(cfg.maxCount) ? Math.max(0, Math.floor(cfg.maxCount)) : DEFAULT_JOBS_CONFIG.maxCount,
|
|
495
|
+
maxSizeMB: Number.isFinite(cfg.maxSizeMB) ? Math.max(0, Math.floor(cfg.maxSizeMB)) : DEFAULT_JOBS_CONFIG.maxSizeMB
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async function writeJobsConfig(cfg) {
|
|
500
|
+
// 校验:非负整数;硬上限防误填爆盘
|
|
501
|
+
const out = {}
|
|
502
|
+
if (cfg.maxCount !== undefined) {
|
|
503
|
+
const n = Math.floor(Number(cfg.maxCount))
|
|
504
|
+
if (!Number.isFinite(n) || n < 0 || n > 10000) throw new Error('maxCount 必须在 0-10000 之间')
|
|
505
|
+
out.maxCount = n
|
|
506
|
+
}
|
|
507
|
+
if (cfg.maxSizeMB !== undefined) {
|
|
508
|
+
const n = Math.floor(Number(cfg.maxSizeMB))
|
|
509
|
+
if (!Number.isFinite(n) || n < 0 || n > 10240) throw new Error('maxSizeMB 必须在 0-10240 之间')
|
|
510
|
+
out.maxSizeMB = n
|
|
511
|
+
}
|
|
512
|
+
const merged = { ...(await readJobsConfig()), ...out }
|
|
513
|
+
await writeJson(JOBS_CONFIG_FILE, merged)
|
|
514
|
+
return merged
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// 进程启动时把磁盘上的历史拉回内存 Map;陈旧的 running/pending 强制降级。
|
|
518
|
+
// 陈旧 job 的 child 进程已退出,标记为 error 方便用户识别。
|
|
519
|
+
async function hydrateJobs() {
|
|
520
|
+
let data
|
|
521
|
+
try {
|
|
522
|
+
data = await readJson(JOBS_FILE, null)
|
|
523
|
+
} catch (err) {
|
|
524
|
+
// 损坏文件:改名备份避免下次 flush 静默覆盖用户数据
|
|
525
|
+
console.warn('[workbench] jobs.json 解析失败,备份原文件后重置:', err.message)
|
|
526
|
+
try { await fsp.rename(JOBS_FILE, `${JOBS_FILE}.bak-${Date.now()}`) } catch { /* 文件可能已不在 */ }
|
|
527
|
+
return
|
|
528
|
+
}
|
|
529
|
+
if (!data || !Array.isArray(data.jobs)) return
|
|
530
|
+
for (const j of data.jobs) {
|
|
531
|
+
if (j.status === 'running' || j.status === 'pending') {
|
|
532
|
+
j.status = 'error'
|
|
533
|
+
j.error = (j.error || '') + ' [重启后回收:原进程已退出]'
|
|
534
|
+
j.endedAt = j.endedAt || nowIso()
|
|
535
|
+
j.exitCode = typeof j.exitCode === 'number' ? j.exitCode : 1
|
|
536
|
+
}
|
|
537
|
+
// 旧版本可能没 size 字段;补齐以兼容历史文件
|
|
538
|
+
if (typeof j.size !== 'number') {
|
|
539
|
+
j.size = ((j.prompt || '').length + (j.output || '').length + (j.thinking || '').length)
|
|
540
|
+
}
|
|
541
|
+
jobs.set(j.id, j)
|
|
542
|
+
}
|
|
543
|
+
// 启动后也跑一遍保留策略,让历史文件立刻缩到当前配置
|
|
544
|
+
try { await enforceRetention() } catch (err) { console.warn('[workbench] 启动时 enforceRetention 失败:', err.message) }
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// 保留策略:按 endedAt desc(fallback startedAt / id)排序,先按 maxCount 截,
|
|
548
|
+
// 再按 maxSizeMB 累计裁,淘汰同步从内存 Map 删除。
|
|
549
|
+
async function enforceRetention() {
|
|
550
|
+
const cfg = await readJobsConfig()
|
|
551
|
+
const data = await readJson(JOBS_FILE, { version: 1, jobs: [] })
|
|
552
|
+
if (!data || !Array.isArray(data.jobs) || data.jobs.length === 0) return
|
|
553
|
+
const sortKey = (j) => j.endedAt || j.startedAt || j.id || ''
|
|
554
|
+
data.jobs.sort((a, b) => sortKey(b).localeCompare(sortKey(a)))
|
|
555
|
+
if (cfg.maxCount > 0) data.jobs = data.jobs.slice(0, cfg.maxCount)
|
|
556
|
+
if (cfg.maxSizeMB > 0) {
|
|
557
|
+
const cap = cfg.maxSizeMB * 1024 * 1024
|
|
558
|
+
let total = data.jobs.reduce((s, j) => s + (j.size || 0), 0)
|
|
559
|
+
while (total > cap && data.jobs.length > 1) {
|
|
560
|
+
const dropped = data.jobs.pop()
|
|
561
|
+
total -= (dropped && dropped.size) || 0
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
await writeJson(JOBS_FILE, data)
|
|
565
|
+
const keepIds = new Set(data.jobs.map(j => j.id))
|
|
566
|
+
for (const id of Array.from(jobs.keys())) {
|
|
567
|
+
if (!keepIds.has(id)) jobs.delete(id)
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
437
571
|
// 简单的 Mustache 风格变量插值:{{task.title}} / {{task.desc}} / {{repo.path}} / {{branch}}
|
|
438
572
|
function interpolate(template, ctx) {
|
|
439
573
|
if (typeof template !== 'string') return template;
|
|
@@ -456,6 +590,8 @@ const jobs = new Map(); // jobId -> { id, taskId, subId, status, pid, startedAt,
|
|
|
456
590
|
// 用 Set 而不是 job.cancelled 标志,是为了在 SIGTERM 发出后到 child 真正退出之间
|
|
457
591
|
// 有一个简洁的"待回收"窗口。
|
|
458
592
|
const cancelledJobs = new Set();
|
|
593
|
+
// 启动时从磁盘拉回历史 job(陈旧 running/pending 自动降级 error)
|
|
594
|
+
hydrateJobs().catch(err => console.warn('[workbench] hydrate jobs failed:', err.message))
|
|
459
595
|
|
|
460
596
|
// ── 生成指令持久化(~/.zen-gitsync/ai-instruction.json) ────────────────────
|
|
461
597
|
async function readInstruction() {
|
|
@@ -745,9 +881,11 @@ ${prompt}`
|
|
|
745
881
|
// assistant.thinking → job.thinking (折叠展示,让用户知道 Claude 在想)
|
|
746
882
|
// 其他事件(init / tool_use / result 等)忽略,避免噪声
|
|
747
883
|
// 解析失败的行原样进 output,便于排查协议异常。
|
|
748
|
-
//
|
|
749
|
-
|
|
750
|
-
|
|
884
|
+
// thinking 几乎不做服务端截断:Claude reasoning 一般在 KB~几十 MB 之间,
|
|
885
|
+
// 100MB 兜底只是为了防止内存爆炸。流式 publish 时改推增量 delta(仅新拼接的
|
|
886
|
+
// 那部分),终态或重连时才随 job:update 全量同步,避免每帧重复广播整个累积文本。
|
|
887
|
+
const MAX_OUTPUT = 100 * 1024 * 1024;
|
|
888
|
+
const MAX_THINKING = 100 * 1024 * 1024;
|
|
751
889
|
job.output = '';
|
|
752
890
|
job.thinking = '';
|
|
753
891
|
const lineBuf = { stdout: '', stderr: '' };
|
|
@@ -757,12 +895,17 @@ ${prompt}`
|
|
|
757
895
|
lineBuf[channel] += chunk;
|
|
758
896
|
const lines = lineBuf[channel].split('\n');
|
|
759
897
|
lineBuf[channel] = lines.pop() ?? ''; // 最后一段可能不完整,留给下次
|
|
898
|
+
let pendingThinkingDelta = '';
|
|
760
899
|
for (const line of lines) {
|
|
761
900
|
const trimmed = line.trim();
|
|
762
901
|
if (!trimmed) continue;
|
|
763
902
|
if (channel === 'stderr' || !trimmed.startsWith('{')) {
|
|
764
903
|
// 非 stream-json 行:原样塞进 output(兼容老版本 claude / 错误信息)
|
|
904
|
+
const prevLen = job.output.length;
|
|
765
905
|
job.output = (job.output + trimmed + '\n').slice(-MAX_OUTPUT);
|
|
906
|
+
// output 也用 delta 推送,前端按"以 length 为锚追加"语义合并
|
|
907
|
+
const delta = job.output.slice(prevLen);
|
|
908
|
+
if (delta) publish('job:output-delta', { id: job.id, delta });
|
|
766
909
|
continue;
|
|
767
910
|
}
|
|
768
911
|
let evt;
|
|
@@ -772,13 +915,22 @@ ${prompt}`
|
|
|
772
915
|
if (!Array.isArray(blocks)) continue;
|
|
773
916
|
for (const b of blocks) {
|
|
774
917
|
if (b.type === 'text' && typeof b.text === 'string') {
|
|
918
|
+
const prevLen = job.output.length;
|
|
775
919
|
job.output = (job.output + b.text).slice(-MAX_OUTPUT);
|
|
920
|
+
const delta = job.output.slice(prevLen);
|
|
921
|
+
if (delta) publish('job:output-delta', { id: job.id, delta });
|
|
776
922
|
} else if (b.type === 'thinking' && typeof b.thinking === 'string') {
|
|
923
|
+
const prevLen = job.thinking.length;
|
|
777
924
|
job.thinking = (job.thinking + b.thinking).slice(-MAX_THINKING);
|
|
925
|
+
const delta = job.thinking.slice(prevLen);
|
|
926
|
+
if (delta) pendingThinkingDelta += delta;
|
|
778
927
|
}
|
|
779
928
|
}
|
|
780
929
|
}
|
|
781
|
-
|
|
930
|
+
// 一批 NDJSON 处理完后统一发一次 thinking delta,避免高频小块 socket 占用
|
|
931
|
+
if (pendingThinkingDelta) {
|
|
932
|
+
publish('job:thinking-delta', { id: job.id, delta: pendingThinkingDelta });
|
|
933
|
+
}
|
|
782
934
|
};
|
|
783
935
|
if (child.stdout) child.stdout.on('data', (buf) => parseLines('stdout', buf));
|
|
784
936
|
if (child.stderr) child.stderr.on('data', (buf) => parseLines('stderr', buf));
|
|
@@ -788,7 +940,10 @@ ${prompt}`
|
|
|
788
940
|
const wasCancelled = cancelledJobs.has(jobId)
|
|
789
941
|
if (wasCancelled) cancelledJobs.delete(jobId)
|
|
790
942
|
// 进程退出时 stdout 可能残留最后一段未换行的 NDJSON,flush 一次
|
|
943
|
+
// flush 内部也按 delta 推送,保持与流式阶段一致
|
|
791
944
|
if (lineBuf.stdout.trim()) {
|
|
945
|
+
const outPrev = job.output.length;
|
|
946
|
+
const thinkPrev = job.thinking.length;
|
|
792
947
|
try {
|
|
793
948
|
const evt = JSON.parse(lineBuf.stdout.trim())
|
|
794
949
|
if (evt.type === 'assistant' && Array.isArray(evt.message?.content)) {
|
|
@@ -801,6 +956,10 @@ ${prompt}`
|
|
|
801
956
|
}
|
|
802
957
|
}
|
|
803
958
|
} catch { /* 不是 JSON,忽略 */ }
|
|
959
|
+
const outDelta = job.output.slice(outPrev);
|
|
960
|
+
if (outDelta) publish('job:output-delta', { id: job.id, delta: outDelta });
|
|
961
|
+
const thinkDelta = job.thinking.slice(thinkPrev);
|
|
962
|
+
if (thinkDelta) publish('job:thinking-delta', { id: job.id, delta: thinkDelta });
|
|
804
963
|
}
|
|
805
964
|
job.endedAt = nowIso();
|
|
806
965
|
if (wasCancelled) {
|
|
@@ -824,6 +983,8 @@ ${prompt}`
|
|
|
824
983
|
delete job.child
|
|
825
984
|
publish('job:update', job);
|
|
826
985
|
publish('sub:update', { taskId: task.id, sub });
|
|
986
|
+
// 终态:fire-and-forget 同步落盘,确保 done/cancelled/error 都立即归档
|
|
987
|
+
flushJobsSaveNow().catch(err => console.warn('[workbench] jobs save failed:', err.message))
|
|
827
988
|
}
|
|
828
989
|
}
|
|
829
990
|
// 写回 tasks.json
|
|
@@ -1362,6 +1523,17 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
|
|
|
1362
1523
|
}
|
|
1363
1524
|
});
|
|
1364
1525
|
|
|
1526
|
+
// 当前选中的项目路径(侧边栏按项目分组时要用)
|
|
1527
|
+
app.get('/api/workbench/current-project', async (_req, res) => {
|
|
1528
|
+
try {
|
|
1529
|
+
const projectPath = typeof getCurrentProjectPath === 'function' ? getCurrentProjectPath() : '';
|
|
1530
|
+
const projectName = projectPath ? projectPath.split(/[\\/]/).filter(Boolean).pop() : '';
|
|
1531
|
+
res.json({ success: true, projectPath, projectName });
|
|
1532
|
+
} catch (err) {
|
|
1533
|
+
res.status(500).json({ success: false, error: err.message });
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
|
|
1365
1537
|
app.post('/api/workbench/tasks', async (req, res) => {
|
|
1366
1538
|
try {
|
|
1367
1539
|
const { id, title, desc, promptId, subtasks } = req.body || {};
|
|
@@ -1369,6 +1541,8 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
|
|
|
1369
1541
|
const data = await readJson(TASKS_FILE, { tasks: [] });
|
|
1370
1542
|
const tasks = data.tasks || [];
|
|
1371
1543
|
const now = nowIso();
|
|
1544
|
+
// 创建任务时记录当时所属项目;编辑已有任务不覆盖(避免切换项目后老任务被改归属)
|
|
1545
|
+
const currentProjectPath = typeof getCurrentProjectPath === 'function' ? getCurrentProjectPath() : '';
|
|
1372
1546
|
if (id) {
|
|
1373
1547
|
const i = tasks.findIndex(t => t.id === id);
|
|
1374
1548
|
if (i < 0) return res.status(404).json({ success: false, error: '任务不存在' });
|
|
@@ -1405,6 +1579,7 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
|
|
|
1405
1579
|
title,
|
|
1406
1580
|
desc: desc || '',
|
|
1407
1581
|
promptId: promptId || null,
|
|
1582
|
+
projectPath: currentProjectPath || '',
|
|
1408
1583
|
subtasks: Array.isArray(subtasks) ? subtasks.map(s => ({
|
|
1409
1584
|
id: s.id || genId(),
|
|
1410
1585
|
title: s.title || '',
|
|
@@ -1482,6 +1657,8 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
|
|
|
1482
1657
|
job.error = '用户已停止执行'
|
|
1483
1658
|
job.endedAt = nowIso()
|
|
1484
1659
|
publish('job:update', { ...job }) // 用浅拷贝避免序列化 child 引用
|
|
1660
|
+
// 终态:fire-and-forget 同步落盘,cancel 是显式操作,要保证不丢
|
|
1661
|
+
flushJobsSaveNow().catch(err => console.warn('[workbench] jobs save failed:', err.message))
|
|
1485
1662
|
const child = job.child
|
|
1486
1663
|
if (!child) {
|
|
1487
1664
|
return res.json({ success: true, message: '已标记取消,进程将尽快结束' })
|
|
@@ -1504,6 +1681,183 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
|
|
|
1504
1681
|
}
|
|
1505
1682
|
});
|
|
1506
1683
|
|
|
1684
|
+
// ── 执行日志管理 API(持久化 + 清理) ────────────────────────────
|
|
1685
|
+
// 路径都在 /api/workbench/jobs/* 下;/list、/config、/batch-delete、/clear
|
|
1686
|
+
// 是字面路径,必须排在 :id 路由之前注册,避免被 :id 匹配吞掉。
|
|
1687
|
+
//
|
|
1688
|
+
// 数据来源:
|
|
1689
|
+
// - 文件 jobs.json:已落盘的历史 job(含反范式 taskTitle/subTitle)
|
|
1690
|
+
// - 内存 jobs Map:当前进程刚创建还没刷盘的(尤其是 running/pending)
|
|
1691
|
+
// 合并后再过滤分页,保证管理页能看到"刚启动还没结束"的任务。
|
|
1692
|
+
|
|
1693
|
+
async function loadAllJobs() {
|
|
1694
|
+
// 读 tasks.json 一次,给内存里没落盘的 job 反范式补 title
|
|
1695
|
+
const tasksData = await readJson(TASKS_FILE, { tasks: [] })
|
|
1696
|
+
const taskMap = new Map((tasksData.tasks || []).map(t => [t.id, t]))
|
|
1697
|
+
const data = await readJson(JOBS_FILE, { version: 1, jobs: [] })
|
|
1698
|
+
const fileJobs = (data && Array.isArray(data.jobs)) ? data.jobs : []
|
|
1699
|
+
const fileIds = new Set(fileJobs.map(j => j.id))
|
|
1700
|
+
// 内存里有但文件里没有:running/pending 或最近刚起还没刷盘的
|
|
1701
|
+
const liveOnly = Array.from(jobs.values())
|
|
1702
|
+
.filter(j => !fileIds.has(j.id))
|
|
1703
|
+
.map(j => serializeJob(j, taskMap))
|
|
1704
|
+
return [...fileJobs, ...liveOnly]
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
function applyJobsFilter(list, q) {
|
|
1708
|
+
const status = (q.status || '').trim()
|
|
1709
|
+
const taskId = (q.taskId || '').trim()
|
|
1710
|
+
const term = (q.q || '').trim().toLowerCase()
|
|
1711
|
+
return list.filter(j => {
|
|
1712
|
+
if (status && j.status !== status) return false
|
|
1713
|
+
if (taskId && j.taskId !== taskId) return false
|
|
1714
|
+
if (term) {
|
|
1715
|
+
const hay = `${j.title || ''} ${j.taskTitle || ''} ${j.subTitle || ''}`.toLowerCase()
|
|
1716
|
+
if (!hay.includes(term)) return false
|
|
1717
|
+
}
|
|
1718
|
+
return true
|
|
1719
|
+
})
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
// GET /api/workbench/jobs/list?status=&q=&taskId=&limit=&offset=
|
|
1723
|
+
app.get('/api/workbench/jobs/list', async (req, res) => {
|
|
1724
|
+
try {
|
|
1725
|
+
const limit = Math.min(200, Math.max(1, parseInt(req.query.limit, 10) || 50))
|
|
1726
|
+
const offset = Math.max(0, parseInt(req.query.offset, 10) || 0)
|
|
1727
|
+
const all = await loadAllJobs()
|
|
1728
|
+
const filtered = applyJobsFilter(all, req.query)
|
|
1729
|
+
// 按 endedAt desc;没有就 startedAt desc;都没有就 id desc(id 时间戳前缀可比)
|
|
1730
|
+
const sortKey = (j) => j.endedAt || j.startedAt || j.id || ''
|
|
1731
|
+
filtered.sort((a, b) => sortKey(b).localeCompare(sortKey(a)))
|
|
1732
|
+
const total = filtered.length
|
|
1733
|
+
const page = filtered.slice(offset, offset + limit)
|
|
1734
|
+
// 统计:按 status 分组 + 总 size(基于全集,给顶部条用)
|
|
1735
|
+
const byStatus = {}
|
|
1736
|
+
let totalSize = 0
|
|
1737
|
+
for (const j of all) {
|
|
1738
|
+
byStatus[j.status] = (byStatus[j.status] || 0) + 1
|
|
1739
|
+
totalSize += j.size || 0
|
|
1740
|
+
}
|
|
1741
|
+
res.json({
|
|
1742
|
+
success: true,
|
|
1743
|
+
jobs: page,
|
|
1744
|
+
total,
|
|
1745
|
+
stats: { count: all.length, sizeMB: +(totalSize / 1024 / 1024).toFixed(2), byStatus }
|
|
1746
|
+
})
|
|
1747
|
+
} catch (err) {
|
|
1748
|
+
res.status(500).json({ success: false, error: 'list jobs 失败: ' + (err.message || String(err)) })
|
|
1749
|
+
}
|
|
1750
|
+
})
|
|
1751
|
+
|
|
1752
|
+
// GET /api/workbench/jobs/config
|
|
1753
|
+
app.get('/api/workbench/jobs/config', async (_req, res) => {
|
|
1754
|
+
try {
|
|
1755
|
+
res.json({ success: true, config: await readJobsConfig() })
|
|
1756
|
+
} catch (err) {
|
|
1757
|
+
res.status(500).json({ success: false, error: '读取配置失败: ' + err.message })
|
|
1758
|
+
}
|
|
1759
|
+
})
|
|
1760
|
+
|
|
1761
|
+
// PUT /api/workbench/jobs/config
|
|
1762
|
+
app.put('/api/workbench/jobs/config', async (req, res) => {
|
|
1763
|
+
try {
|
|
1764
|
+
const cfg = await writeJobsConfig(req.body || {})
|
|
1765
|
+
// 配置变更后立刻 enforce,让已落盘的多余记录立刻被裁掉
|
|
1766
|
+
await enforceRetention()
|
|
1767
|
+
res.json({ success: true, config: cfg })
|
|
1768
|
+
} catch (err) {
|
|
1769
|
+
res.status(400).json({ success: false, error: err.message || String(err) })
|
|
1770
|
+
}
|
|
1771
|
+
})
|
|
1772
|
+
|
|
1773
|
+
// POST /api/workbench/jobs/batch-delete
|
|
1774
|
+
app.post('/api/workbench/jobs/batch-delete', async (req, res) => {
|
|
1775
|
+
try {
|
|
1776
|
+
const ids = Array.isArray(req.body?.ids) ? req.body.ids.filter(s => typeof s === 'string') : []
|
|
1777
|
+
if (ids.length === 0) return res.json({ success: true, removed: 0 })
|
|
1778
|
+
// 1) 删内存 Map
|
|
1779
|
+
let removed = 0
|
|
1780
|
+
for (const id of ids) {
|
|
1781
|
+
if (jobs.delete(id)) removed++
|
|
1782
|
+
}
|
|
1783
|
+
// 2) 改写文件
|
|
1784
|
+
const data = await readJson(JOBS_FILE, { version: 1, jobs: [] })
|
|
1785
|
+
if (data && Array.isArray(data.jobs)) {
|
|
1786
|
+
const set = new Set(ids)
|
|
1787
|
+
const before = data.jobs.length
|
|
1788
|
+
data.jobs = data.jobs.filter(j => !set.has(j.id))
|
|
1789
|
+
removed += before - data.jobs.length
|
|
1790
|
+
await writeJson(JOBS_FILE, data)
|
|
1791
|
+
}
|
|
1792
|
+
res.json({ success: true, removed })
|
|
1793
|
+
} catch (err) {
|
|
1794
|
+
res.status(500).json({ success: false, error: '批量删除失败: ' + (err.message || String(err)) })
|
|
1795
|
+
}
|
|
1796
|
+
})
|
|
1797
|
+
|
|
1798
|
+
// POST /api/workbench/jobs/clear
|
|
1799
|
+
app.post('/api/workbench/jobs/clear', async (req, res) => {
|
|
1800
|
+
try {
|
|
1801
|
+
if (req.body?.confirm !== true) {
|
|
1802
|
+
return res.status(400).json({ success: false, error: '需要 confirm: true' })
|
|
1803
|
+
}
|
|
1804
|
+
let removed = 0
|
|
1805
|
+
for (const j of jobs.values()) {
|
|
1806
|
+
// 不清当前还在跑/排队的(防止误清活跃 job;用户应逐个取消或等结束)
|
|
1807
|
+
if (j.status === 'running' || j.status === 'pending') continue
|
|
1808
|
+
jobs.delete(j.id)
|
|
1809
|
+
removed++
|
|
1810
|
+
}
|
|
1811
|
+
// 写一个空 jobs.json
|
|
1812
|
+
await writeJson(JOBS_FILE, { version: 1, jobs: [] })
|
|
1813
|
+
res.json({ success: true, removed })
|
|
1814
|
+
} catch (err) {
|
|
1815
|
+
res.status(500).json({ success: false, error: '清空失败: ' + (err.message || String(err)) })
|
|
1816
|
+
}
|
|
1817
|
+
})
|
|
1818
|
+
|
|
1819
|
+
// GET /api/workbench/jobs/:id
|
|
1820
|
+
app.get('/api/workbench/jobs/:id', async (req, res) => {
|
|
1821
|
+
try {
|
|
1822
|
+
// 优先查内存(含活跃)
|
|
1823
|
+
const live = jobs.get(req.params.id)
|
|
1824
|
+
if (live) {
|
|
1825
|
+
const tasksData = await readJson(TASKS_FILE, { tasks: [] })
|
|
1826
|
+
const taskMap = new Map((tasksData.tasks || []).map(t => [t.id, t]))
|
|
1827
|
+
return res.json({ success: true, job: serializeJob(live, taskMap) })
|
|
1828
|
+
}
|
|
1829
|
+
// 退回文件
|
|
1830
|
+
const data = await readJson(JOBS_FILE, { version: 1, jobs: [] })
|
|
1831
|
+
const j = (data.jobs || []).find(x => x.id === req.params.id)
|
|
1832
|
+
if (!j) return res.status(404).json({ success: false, error: 'job 不存在' })
|
|
1833
|
+
res.json({ success: true, job: j })
|
|
1834
|
+
} catch (err) {
|
|
1835
|
+
res.status(500).json({ success: false, error: '查询失败: ' + (err.message || String(err)) })
|
|
1836
|
+
}
|
|
1837
|
+
})
|
|
1838
|
+
|
|
1839
|
+
// DELETE /api/workbench/jobs/:id
|
|
1840
|
+
app.delete('/api/workbench/jobs/:id', async (req, res) => {
|
|
1841
|
+
try {
|
|
1842
|
+
const id = req.params.id
|
|
1843
|
+
let removed = false
|
|
1844
|
+
if (jobs.delete(id)) removed = true
|
|
1845
|
+
const data = await readJson(JOBS_FILE, { version: 1, jobs: [] })
|
|
1846
|
+
if (data && Array.isArray(data.jobs)) {
|
|
1847
|
+
const before = data.jobs.length
|
|
1848
|
+
data.jobs = data.jobs.filter(j => j.id !== id)
|
|
1849
|
+
if (data.jobs.length !== before) {
|
|
1850
|
+
removed = true
|
|
1851
|
+
await writeJson(JOBS_FILE, data)
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
if (!removed) return res.status(404).json({ success: false, error: 'job 不存在' })
|
|
1855
|
+
res.json({ success: true, removed: 1 })
|
|
1856
|
+
} catch (err) {
|
|
1857
|
+
res.status(500).json({ success: false, error: '删除失败: ' + (err.message || String(err)) })
|
|
1858
|
+
}
|
|
1859
|
+
})
|
|
1860
|
+
|
|
1507
1861
|
// ── 子任务附件:上传 / 删除 / 列表 ───────────────────────────────
|
|
1508
1862
|
// 上传:POST /api/workbench/subtasks/:subId/attachments
|
|
1509
1863
|
// header: X-Original-Name, X-Mime-Type
|
|
Binary file
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{o as e}from"./rolldown-runtime-CMxvf4Kt.js";import{An as t,Fn as n,Gn as r,Hn as i,In as a,Jn as o,Mn as s,On as c,P as ee,Qn as l,Rn as u,Un as d,Vn as te,Xn as ne,Yn as re,_ as ie,ar as f,c as p,d as ae,dr as m,er as h,f as oe,fr as g,g as se,gt as ce,h as le,ir as _,kn as v,lr as y,m as ue,mr as b,or as x,p as de,pr as S,t as fe,ur as pe,v as me,y as he,zn as C}from"./vendor-WSdfT3f8.js";import{n as w,t as T}from"./_plugin-vue_export-helper-CCKxXnb6.js";import{a as ge,r as _e}from"./index-B9iw-Qvb.js";var ve=e(ae(),1),ye={class:`source-map-view`},be={class:`sm-toolbar`},xe={class:`sm-toolbar-left`},Se={class:`sm-title`},Ce={class:`sm-toolbar-center`},we=[`placeholder`,`disabled`],Te=[`disabled`],Ee={class:`sm-toolbar-right`},De={class:`sm-body`},Oe={class:`sm-panel-header sm-panel-header--tabs`},ke={key:0,class:`sm-badge`},Ae={class:`sm-panel-body sm-file-tree`},je=[`onClick`],Me={key:1,class:`sm-tree-arrow-spacer`},Ne={key:2,class:`sm-tree-icon mit-icon`,"aria-hidden":`true`},Pe=[`xlink:href`],Fe={key:3,class:`sm-tree-icon mit-icon`,"aria-hidden":`true`},Ie=[`xlink:href`],Le=[`title`],Re={key:1,class:`sm-tree-empty`},ze={class:`sm-panel-body sm-outline-body`},Be={class:`sm-badge`,style:{"margin-left":`auto`}},Ve=[`onClick`],He=[`title`],Ue={key:0,class:`sm-outline-desc`},We={key:1,class:`sm-tree-empty`},Ge={class:`sm-panel sm-panel-graph`},Ke={key:0,class:`sm-project-info`},qe={class:`sm-lang-badge`},Je={class:`sm-summary-text`},Ye={class:`sm-graph-container`},Xe={class:`sm-layout-btn-wrap`},Ze=[`disabled`],Qe=[`title`],$e={class:`sm-fn-label`},et={key:0,class:`sm-fn-desc`},tt={key:0,class:`sm-graph-empty`},nt={viewBox:`0 0 24 24`,width:`48`,height:`48`,fill:`none`,stroke:`currentColor`,"stroke-width":`1`,style:{opacity:`0.3`}},rt={key:1,class:`sm-graph-loading`},it={class:`sm-log-header`},at={key:0,class:`sm-log-indicator`},ot={class:`sm-log-body`,ref:`logBodyRef`},st={key:0,class:`sm-empty`},ct={class:`sm-panel-header`},lt={key:0,class:`sm-badge sm-badge-amber`},ut={key:0,class:`sm-node-detail`},dt={class:`sm-node-name`},ft={class:`sm-node-file`},pt={key:0},mt={key:0,class:`sm-node-desc`},ht={class:`sm-panel-body sm-source-body`},gt={key:0,class:`sm-source-overlay`},_t={key:1,class:`sm-source-overlay sm-source-placeholder`},E=T(r({__name:`SourceMapView`,setup(e){let r=y(document.documentElement.getAttribute(`data-theme`)===`dark`?`dark`:`light`);function ae(){r.value=document.documentElement.getAttribute(`data-theme`)===`dark`?`dark`:`light`}let T=null,E=ge(),D=y(E.currentDirectory||``),O=y(`idle`),k=y([]),vt=0,A=y(null),j=y(null),M=y(`files`),N=y(``),P=y(``),F=y(!1),I=y([]),L=y(new Set),R=y({files:!0,graph:!0,source:!0}),z=y(!1),B=y(220),V=y(360),H=y(140),U=null,W=0,yt=0,G=0;function K(e,t){U=e,W=t.clientX,yt=t.clientY,G=e===`files`?B.value:e===`source`?V.value:H.value,document.addEventListener(`mousemove`,bt),document.addEventListener(`mouseup`,q),t.preventDefault()}function bt(e){U&&(U===`files`?B.value=Math.max(120,Math.min(480,G+e.clientX-W)):U===`source`?V.value=Math.max(200,Math.min(700,G+W-e.clientX)):H.value=Math.max(60,Math.min(500,G+yt-e.clientY)))}function q(){U=null,document.removeEventListener(`mousemove`,bt),document.removeEventListener(`mouseup`,q)}let J=y(null),Y=pe(null),{fitView:xt,setNodes:X,setEdges:St,getNodes:Ct,getEdges:wt,updateNodeInternals:Tt}=he(),Z=n(()=>O.value===`scanning`||O.value===`analyzing`),Et=n(()=>r.value===`dark`?`#334155`:`#cbd5e1`),Q=n(()=>A.value?.nodes.find(e=>e.id===j.value)??null),Dt=n(()=>{if(!A.value)return[];let e=new Map;for(let t of A.value.nodes){let n=t.subsystem??`__default__`;if(!e.has(n)){let r=A.value.subsystems?.find(e=>e.name===n);e.set(n,{displayName:r?.displayName||t.subsystem||w(`@SRCMAP:默认`),color:t.subsystemColor||kt[0],nodes:[]})}e.get(n).nodes.push(t)}return[...e.entries()].map(([,e])=>e)});function $(e,t=`info`){k.value.push({id:++vt,message:e,type:t,timestamp:Date.now()}),k.value.length>200&&k.value.splice(0,k.value.length-200)}function Ot(e){let t={name:``,path:``,kind:`dir`,children:[],childMap:new Map};for(let n of e){let e=n.trim().split(`/`).filter(Boolean),r=t,i=``;for(let t=0;t<e.length;t++){let n=e[t];i=i?`${i}/${n}`:n;let a=t===e.length-1,o=r.childMap.get(n);o||(o={name:n,path:i,kind:a?`file`:`dir`,children:[],childMap:new Map},r.childMap.set(n,o),r.children.push(o)),r=o}}let n=e=>{e.sort((e,t)=>e.kind===t.kind?e.name.localeCompare(t.name):e.kind===`dir`?-1:1),e.forEach(e=>{e.kind===`dir`&&n(e.children)})};return n(t.children),t.children}let kt=[`#f59e0b`,`#3b82f6`,`#10b981`,`#8b5cf6`];function At(e){return e.subsystemColor?e.subsystemColor:e.subsystemIndex===void 0?e.importance===`high`?`#f59e0b`:e.importance===`low`?`#94a3b8`:`#3b82f6`:kt[e.subsystemIndex%kt.length]}function jt(e){return{ts:`typescript`,tsx:`typescript`,js:`javascript`,mjs:`javascript`,cjs:`javascript`,jsx:`javascript`,vue:`html`,svelte:`html`,html:`html`,py:`python`,java:`java`,go:`go`,rs:`rust`,cpp:`cpp`,cc:`cpp`,cxx:`cpp`,c:`c`,h:`c`,hpp:`cpp`,cs:`csharp`,rb:`ruby`,php:`php`,swift:`swift`,kt:`kotlin`,sh:`shell`,bash:`shell`,json:`json`,yaml:`yaml`,yml:`yaml`,md:`markdown`,css:`css`,scss:`scss`,less:`less`,sql:`sql`}[e.split(`.`).pop()?.toLowerCase()||``]||`plaintext`}function Mt(){!J.value||Y.value||(Y.value=p.create(J.value,{value:``,language:`plaintext`,theme:r.value===`dark`?`vs-dark`:`vs`,readOnly:!0,fontSize:12,lineHeight:19,fontFamily:`'JetBrains Mono', 'Fira Code', Consolas, monospace`,minimap:{enabled:!1},scrollBeyondLastLine:!1,automaticLayout:!0,wordWrap:`off`,padding:{top:8,bottom:8},scrollbar:{verticalScrollbarSize:6,horizontalScrollbarSize:6}}))}_(r,e=>{Y.value&&p.setTheme(e===`dark`?`vs-dark`:`vs`)});function Nt(e,t){let n={};t.forEach(e=>{n[e.source]||(n[e.source]=[]),n[e.source].push(e.target)});let r=new Map;e.forEach(e=>{let t=e.subsystem??`__default`;r.has(t)||r.set(t,[]),r.get(t).push(e)});let i=[],a=0;for(let[,e]of r){let t=new Set(e.map(e=>e.id)),r={},o=e.length?[e[0].id]:[];for(o[0]&&(r[o[0]]=0);o.length;){let e=o.shift();(n[e]||[]).filter(e=>t.has(e)).forEach(t=>{r[t]===void 0&&(r[t]=(r[e]??0)+1,o.push(t))})}let s={};e.forEach(e=>{let t=r[e.id]??0;s[t]=(s[t]||0)+1});let c={};e.forEach(e=>{let t=r[e.id]??0;c[t]=(c[t]??-1)+1;let n=c[t],o=s[t]??1,ee=a*600+n*220-(o-1)*220/2,l=t*110,u=At(e);i.push({id:e.id,type:`default`,position:{x:ee,y:l},label:e.label,class:`sm-fn-node`,data:{...e,_accentColor:u},style:{"--node-accent":u}})}),a++}return{flowNodes:i,flowEdges:t.map((e,t)=>({id:`e_${t}_${e.source}_${e.target}`,source:e.source,target:e.target,type:`smoothstep`,animated:!1,class:`sm-fn-edge`,markerEnd:{type:le.ArrowClosed}}))}}async function Pt(){let e=Ct.value,t=wt.value;if(e.length!==0){z.value=!0;try{await o(),await new Promise(e=>requestAnimationFrame(()=>e())),Tt(e.map(e=>e.id)),await new Promise(e=>requestAnimationFrame(()=>e()));let n=new Map;e.forEach(e=>{let t=e.data?.subsystem??`__default__`;n.has(t)||n.set(t,[]),n.get(t).push(e)});let r=new Map,i=0;for(let[,e]of n){let n=new ve.default.graphlib.Graph;n.setDefaultEdgeLabel(()=>({})),n.setGraph({rankdir:`TB`,nodesep:55,ranksep:75,marginx:40,marginy:40});let a=new Set(e.map(e=>e.id));e.forEach(e=>{let t=190,r=e.data?.description?68:48,i=document.querySelector(`.vue-flow__node[data-id="${e.id}"]`);i&&i.offsetWidth>0&&(t=i.offsetWidth,r=i.offsetHeight),n.setNode(e.id,{width:t,height:r})}),t.forEach(e=>{a.has(e.source)&&a.has(e.target)&&n.setEdge(e.source,e.target)}),ve.default.layout(n);let o=0;e.forEach(e=>{let t=n.node(e.id);t&&(r.set(e.id,{x:i+t.x-t.width/2,y:t.y-t.height/2}),o=Math.max(o,t.x+t.width/2))}),i+=o+120}X(e.map(e=>({...e,position:r.get(e.id)??e.position}))),await o(),xt({padding:.18})}finally{z.value=!1}}}async function Ft(){if(!D.value.trim()){ee.warning(w(`@SRCMAP:请先输入项目路径`));return}if(!Z.value){k.value=[],A.value=null,j.value=null,N.value=``,P.value=``,X([]),St([]),O.value=`scanning`,$(w(`@SRCMAP:开始分析项目...`),`info`);try{let e=await fetch(`/api/code-analysis/analyze`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:D.value})});if(!e.ok){let t=await e.json().catch(()=>({error:`请求失败`}));throw Error(t.error||`HTTP ${e.status}`)}let t=e.body.getReader(),n=new TextDecoder,r=``;O.value=`analyzing`;let i=``,a=[];for(;;){let{done:e,value:o}=await t.read();if(e)break;r+=n.decode(o,{stream:!0});let s=r.split(`
|
|
2
|
-
`);r=s.pop()??``;for(let e of s)if(e.startsWith(`event:`))i=e.slice(6).trim(),a=[];else if(e.startsWith(`data:`))a.push(e.slice(5).trim());else if(e===``){if(i&&a.length){let e=a.join(``);try{let t=JSON.parse(e);It(i,t)}catch{}}i=``,a=[]}}}catch(e){$(`${w(`@SRCMAP:分析失败`)}: ${e.message}`,`error`),O.value=`error`}}}function It(e,t){if(e===`log`)$(t.message,t.type||`info`);else if(e===`files`)I.value=Ot(t.files||[]),I.value.forEach(e=>{e.kind===`dir`&&L.value.add(e.path)});else if(e===`result`){A.value={language:t.language||``,entryFile:t.entryFile||``,entryFunction:t.entryFunction||``,nodes:Array.isArray(t.nodes)?t.nodes:[],edges:Array.isArray(t.edges)?t.edges:[],techStack:Array.isArray(t.techStack)?t.techStack:[],summary:t.summary||``,allFiles:Array.isArray(t.allFiles)?t.allFiles:[],codeFiles:Array.isArray(t.codeFiles)?t.codeFiles:[],subsystems:Array.isArray(t.subsystems)?t.subsystems:void 0};let{flowNodes:e,flowEdges:n}=Nt(A.value.nodes,A.value.edges);X(e),St(n),o(()=>Pt())}else e===`done`&&(t.error?($(`${w(`@SRCMAP:分析失败`)}: ${t.error}`,`error`),O.value=`error`):O.value=`done`)}async function Lt(e){if(!(!e||F.value)&&P.value!==e){F.value=!0,P.value=e,N.value=``;try{let t=await(await fetch(`/api/code-analysis/file-content?path=${encodeURIComponent(D.value)}&file=${encodeURIComponent(e)}`)).json();if(t.error)throw Error(t.error);N.value=t.content||``}catch(e){N.value=`// ${w(`@SRCMAP:加载失败`)}: ${e.message}`}finally{F.value=!1}}}function Rt(e){let t=e.node.data;j.value=t.id,t.file&&Lt(t.file)}function zt(e){L.value.has(e)?L.value.delete(e):L.value.add(e)}let Bt=n(()=>{let e=[];function t(n,r){for(let i of n){let n=L.value.has(i.path);e.push({name:i.name,path:i.path,kind:i.kind,depth:r,expanded:n}),i.kind===`dir`&&n&&t(i.children,r+1)}}return t(I.value,0),e});return _(()=>E.currentDirectory,e=>{e&&!D.value&&(D.value=e)}),_([N,P],([e,t])=>{let n=Y.value;if(!n)return;let r=jt(t||``),i=n.getModel(),a=p.createModel(e||``,r);n.setModel(a),i?.dispose(),n.setScrollPosition({scrollTop:0,scrollLeft:0})}),ne(()=>{Mt(),!D.value&&E.currentDirectory&&(D.value=E.currentDirectory),T=new MutationObserver(()=>ae()),T.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-theme`]})}),re(()=>{Y.value?.getModel()?.dispose(),Y.value?.dispose(),q(),T?.disconnect(),T=null}),(e,n)=>{let o=ce;return l(),C(`div`,ye,[a(`div`,be,[a(`div`,xe,[n[9]||=a(`svg`,{viewBox:`0 0 24 24`,width:`18`,height:`18`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`,class:`sm-icon-map`},[a(`polygon`,{points:`3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21`}),a(`line`,{x1:`9`,y1:`3`,x2:`9`,y2:`18`}),a(`line`,{x1:`15`,y1:`6`,x2:`15`,y2:`21`})],-1),a(`span`,Se,b(m(w)(`@SRCMAP:源码地图`)),1)]),a(`div`,Ce,[x(a(`input`,{"onUpdate:modelValue":n[0]||=e=>D.value=e,class:`sm-path-input`,placeholder:m(w)(`@SRCMAP:输入项目目录路径`),disabled:Z.value,onKeydown:t(Ft,[`enter`])},null,40,we),[[c,D.value]]),a(`button`,{class:`sm-btn sm-btn-primary`,disabled:Z.value,onClick:Ft},[Z.value?(l(),C(s,{key:0},[n[10]||=a(`span`,{class:`sm-spinner`},null,-1),i(` `+b(m(w)(`@SRCMAP:分析中...`)),1)],64)):(l(),C(s,{key:1},[i(b(O.value===`done`?m(w)(`@SRCMAP:重新分析`):m(w)(`@SRCMAP:开始分析`)),1)],64))],8,Te)]),a(`div`,Ee,[d(o,{content:m(w)(`@SRCMAP:文件列表`),placement:`bottom`},{default:f(()=>[a(`button`,{class:g([`sm-panel-btn`,{active:R.value.files}]),onClick:n[1]||=e=>R.value.files=!R.value.files},[...n[11]||=[a(`svg`,{viewBox:`0 0 24 24`,width:`16`,height:`16`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[a(`path`,{d:`M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z`})],-1)]],2)]),_:1},8,[`content`]),d(o,{content:m(w)(`@SRCMAP:调用图`),placement:`bottom`},{default:f(()=>[a(`button`,{class:g([`sm-panel-btn`,{active:R.value.graph}]),onClick:n[2]||=e=>R.value.graph=!R.value.graph},[...n[12]||=[a(`svg`,{viewBox:`0 0 24 24`,width:`16`,height:`16`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[a(`circle`,{cx:`12`,cy:`12`,r:`3`}),a(`circle`,{cx:`3`,cy:`6`,r:`2`}),a(`circle`,{cx:`21`,cy:`6`,r:`2`}),a(`circle`,{cx:`3`,cy:`18`,r:`2`}),a(`circle`,{cx:`21`,cy:`18`,r:`2`}),a(`line`,{x1:`5`,y1:`6`,x2:`9`,y2:`11`}),a(`line`,{x1:`19`,y1:`6`,x2:`15`,y2:`11`}),a(`line`,{x1:`5`,y1:`18`,x2:`9`,y2:`13`}),a(`line`,{x1:`19`,y1:`18`,x2:`15`,y2:`13`})],-1)]],2)]),_:1},8,[`content`]),d(o,{content:m(w)(`@SRCMAP:源码面板`),placement:`bottom`},{default:f(()=>[a(`button`,{class:g([`sm-panel-btn`,{active:R.value.source}]),onClick:n[3]||=e=>R.value.source=!R.value.source},[...n[13]||=[a(`svg`,{viewBox:`0 0 24 24`,width:`16`,height:`16`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[a(`polyline`,{points:`16 18 22 12 16 6`}),a(`polyline`,{points:`8 6 2 12 8 18`})],-1)]],2)]),_:1},8,[`content`])])]),a(`div`,De,[x(a(`div`,{class:`sm-panel sm-panel-files`,style:S({width:B.value+`px`})},[a(`div`,Oe,[a(`button`,{class:g([`sm-tab-btn`,{active:M.value===`files`}]),onClick:n[4]||=e=>M.value=`files`},[n[14]||=a(`svg`,{viewBox:`0 0 24 24`,width:`12`,height:`12`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[a(`path`,{d:`M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z`})],-1),i(` `+b(m(w)(`@SRCMAP:文件列表`))+` `,1),A.value?(l(),C(`span`,ke,b(A.value.allFiles.length),1)):u(``,!0)],2),a(`button`,{class:g([`sm-tab-btn`,{active:M.value===`outline`}]),onClick:n[5]||=e=>M.value=`outline`},[n[15]||=te(`<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1.8" data-v-71d3569e><line x1="8" y1="6" x2="21" y2="6" data-v-71d3569e></line><line x1="8" y1="12" x2="21" y2="12" data-v-71d3569e></line><line x1="8" y1="18" x2="21" y2="18" data-v-71d3569e></line><line x1="3" y1="6" x2="3.01" y2="6" data-v-71d3569e></line><line x1="3" y1="12" x2="3.01" y2="12" data-v-71d3569e></line><line x1="3" y1="18" x2="3.01" y2="18" data-v-71d3569e></line></svg>`,1),i(` `+b(m(w)(`@SRCMAP:大纲`)),1)],2)]),x(a(`div`,Ae,[Bt.value.length>0?(l(!0),C(s,{key:0},h(Bt.value,e=>(l(),C(`div`,{key:e.path,class:g([`sm-tree-node`,{"sm-tree-node--dir":e.kind===`dir`,"sm-tree-node--active":e.kind===`file`&&P.value===e.path}]),style:S({paddingLeft:10+e.depth*14+`px`}),onClick:t=>e.kind===`dir`?zt(e.path):Lt(e.path)},[e.kind===`dir`?(l(),C(`span`,{key:0,class:g([`sm-tree-arrow`,{expanded:e.expanded}])},[...n[16]||=[a(`svg`,{viewBox:`0 0 24 24`,width:`10`,height:`10`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[a(`polyline`,{points:`9 18 15 12 9 6`})],-1)]],2)):(l(),C(`span`,Me)),e.kind===`dir`?(l(),C(`svg`,Ne,[a(`use`,{"xlink:href":`#${m(_e)(e.name)||`icon-folder`}`},null,8,Pe)])):(l(),C(`svg`,Fe,[a(`use`,{"xlink:href":`#${m(_e)(e.name)}`},null,8,Ie)])),a(`span`,{class:`sm-tree-name`,title:e.path},b(e.name),9,Le)],14,je))),128)):(l(),C(`div`,Re,b(m(w)(`@SRCMAP:暂无文件,请先开始分析`)),1))],512),[[v,M.value===`files`]]),x(a(`div`,ze,[Dt.value.length>0?(l(!0),C(s,{key:0},h(Dt.value,e=>(l(),C(`div`,{key:e.displayName,class:`sm-outline-group`},[a(`div`,{class:`sm-outline-group-header`,style:S({color:e.color})},[n[17]||=a(`svg`,{viewBox:`0 0 24 24`,width:`8`,height:`8`,fill:`currentColor`},[a(`circle`,{cx:`12`,cy:`12`,r:`6`})],-1),i(` `+b(e.displayName)+` `,1),a(`span`,Be,b(e.nodes.length),1)],4),(l(!0),C(s,null,h(e.nodes,t=>(l(),C(`div`,{key:t.id,class:g([`sm-outline-node`,{"sm-outline-node--active":j.value===t.id}]),onClick:e=>{j.value=t.id,t.file&&Lt(t.file)}},[a(`span`,{class:`sm-outline-dot`,style:S({background:e.color})},null,4),a(`span`,{class:`sm-outline-label`,title:t.file||t.label},b(t.label),9,He),t.description?(l(),C(`span`,Ue,b(t.description.length>18?t.description.slice(0,18)+`…`:t.description),1)):u(``,!0)],10,Ve))),128))]))),128)):(l(),C(`div`,We,b(m(w)(`@SRCMAP:暂无分析结果`)),1))],512),[[v,M.value===`outline`]])],4),[[v,R.value.files]]),x(a(`div`,{class:`sm-resizer sm-resizer-v`,onMousedown:n[6]||=e=>K(`files`,e)},null,544),[[v,R.value.files&&R.value.graph]]),x(a(`div`,Ge,[A.value?(l(),C(`div`,Ke,[a(`span`,qe,b(A.value.language),1),A.value.subsystems&&A.value.subsystems.length>1?(l(!0),C(s,{key:0},h(A.value.subsystems,e=>(l(),C(`span`,{key:e.name,class:`sm-subsystem-tag`,style:S({borderColor:e.color,color:e.color})},`● `+b(e.displayName||e.name),5))),128)):(l(!0),C(s,{key:1},h(A.value.techStack.slice(0,4),e=>(l(),C(`span`,{key:e,class:`sm-tech-tag`},b(e),1))),128)),a(`span`,Je,b(A.value.summary),1)])):u(``,!0),a(`div`,Ye,[a(`div`,Xe,[a(`button`,{class:`sm-layout-btn`,disabled:z.value||!A.value,onClick:Pt},[n[18]||=te(`<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1.8" data-v-71d3569e><rect x="3" y="3" width="7" height="7" rx="1" data-v-71d3569e></rect><rect x="14" y="3" width="7" height="7" rx="1" data-v-71d3569e></rect><rect x="3" y="14" width="7" height="7" rx="1" data-v-71d3569e></rect><rect x="14" y="14" width="7" height="7" rx="1" data-v-71d3569e></rect></svg>`,1),i(` `+b(z.value?m(w)(`@SRCMAP:布局中...`):m(w)(`@SRCMAP:优化布局`)),1)],8,Ze)]),d(m(ie),{class:`sm-vue-flow`,"default-viewport":{zoom:1},"min-zoom":.2,"max-zoom":4,"fit-view-on-init":``,onNodeClick:Rt},{"node-default":f(({data:e,label:t})=>[d(m(me),{type:`target`,position:m(se).Top},null,8,[`position`]),a(`div`,{class:`sm-fn-inner`,title:`${t}${e?.description?`
|
|
3
|
-
`+e.description:``}`},[a(`div`,$e,b(t),1),e?.description?(l(),C(`div`,et,b(e.description.length>22?e.description.slice(0,22)+`…`:e.description),1)):u(``,!0)],8,Qe),d(m(me),{type:`source`,position:m(se).Bottom},null,8,[`position`])]),default:f(()=>[d(m(ue),{variant:m(de).Dots,gap:20,size:1,"pattern-color":Et.value},null,8,[`variant`,`pattern-color`]),d(m(oe)),d(m(fe),{"node-color":`#3b82f6`,"mask-color":r.value===`dark`?`rgba(0,0,0,0.55)`:`rgba(15,23,42,0.06)`},null,8,[`mask-color`])]),_:1}),!A.value&&!Z.value?(l(),C(`div`,tt,[(l(),C(`svg`,nt,[...n[19]||=[a(`polygon`,{points:`3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21`},null,-1),a(`line`,{x1:`9`,y1:`3`,x2:`9`,y2:`18`},null,-1),a(`line`,{x1:`15`,y1:`6`,x2:`15`,y2:`21`},null,-1)]])),a(`p`,null,b(m(w)(`@SRCMAP:输入项目路径后点击开始分析`)),1)])):u(``,!0),Z.value&&!A.value?(l(),C(`div`,rt,[n[20]||=a(`span`,{class:`sm-spinner sm-spinner-lg`},null,-1),a(`p`,null,b(m(w)(`@SRCMAP:AI 正在分析项目结构...`)),1)])):u(``,!0)]),a(`div`,{class:`sm-resizer sm-resizer-h`,onMousedown:n[7]||=e=>K(`log`,e)},null,32),a(`div`,{class:`sm-log-panel`,style:S({height:H.value+`px`})},[a(`div`,it,[n[21]||=a(`svg`,{viewBox:`0 0 24 24`,width:`12`,height:`12`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[a(`polyline`,{points:`4 17 10 11 4 5`}),a(`line`,{x1:`12`,y1:`19`,x2:`20`,y2:`19`})],-1),i(` `+b(m(w)(`@SRCMAP:Agent 日志`))+` `,1),Z.value?(l(),C(`span`,at)):u(``,!0)]),a(`div`,ot,[(l(!0),C(s,null,h(k.value.slice(-50),e=>(l(),C(`div`,{key:e.id,class:g([`sm-log-entry`,`sm-log-entry--${e.type}`])},b(e.message),3))),128)),k.value.length===0?(l(),C(`div`,st,b(m(w)(`@SRCMAP:等待开始分析`)),1)):u(``,!0)],512)],4)],512),[[v,R.value.graph]]),x(a(`div`,{class:`sm-resizer sm-resizer-v`,onMousedown:n[8]||=e=>K(`source`,e)},null,544),[[v,R.value.graph&&R.value.source]]),x(a(`div`,{class:`sm-panel sm-panel-source`,style:S({width:V.value+`px`})},[a(`div`,ct,[n[22]||=a(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[a(`polyline`,{points:`16 18 22 12 16 6`}),a(`polyline`,{points:`8 6 2 12 8 18`})],-1),i(` `+b(P.value||m(w)(`@SRCMAP:源码面板`))+` `,1),Q.value?(l(),C(`span`,lt,b(Q.value.label),1)):u(``,!0)]),Q.value?(l(),C(`div`,ut,[a(`div`,dt,b(Q.value.label),1),a(`div`,ft,[i(b(Q.value.file),1),Q.value.line?(l(),C(`span`,pt,` :`+b(Q.value.line),1)):u(``,!0)]),Q.value.description?(l(),C(`div`,mt,b(Q.value.description),1)):u(``,!0)])):u(``,!0),a(`div`,ht,[F.value?(l(),C(`div`,gt,[...n[23]||=[a(`span`,{class:`sm-spinner`},null,-1)]])):u(``,!0),!F.value&&!N.value?(l(),C(`div`,_t,b(m(w)(`@SRCMAP:点击调用图中的节点查看源码`)),1)):u(``,!0),a(`div`,{ref_key:`monacoContainerRef`,ref:J,class:`sm-monaco-container`},null,512)])],4),[[v,R.value.source]])])])}}}),[[`__scopeId`,`data-v-71d3569e`]]);export{E as default};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import"./rolldown-runtime-CMxvf4Kt.js";import{B as e,Dn as t,Et as n,Fn as r,Gn as i,Hn as a,In as o,It as s,J as c,Jn as l,Jt as u,Ln as d,Mn as f,N as p,On as m,P as h,Pn as g,Pt as _,Qn as v,Rn as y,St as ee,Un as b,V as x,Xn as S,Yn as C,Yt as w,Zt as T,_t as E,ar as D,bt as te,cr as O,ct as ne,dr as k,dt as re,er as A,et as ie,fr as j,ht as ae,ir as oe,jn as M,kt as se,lr as N,lt as ce,mr as P,nn as F,or as I,pr as le,rr as ue,sr as L,tn as R,vt as z,xt as de,zn as B,zt as fe}from"./vendor-WSdfT3f8.js";import{n as V,t as H}from"./_plugin-vue_export-helper-CCKxXnb6.js";import{t as pe}from"./index-B9iw-Qvb.js";var U={key:0,class:`ai-split-status`},me={key:1,class:`ai-split-status is-error`},W={class:`ai-split-prompt-actions`},G={key:0,class:`ai-split-prompt-editor`},K={class:`ai-split-prompt-editor__actions`},q={key:1,class:`ai-split-pre`},he={class:`ai-split-subhead`},ge={class:`ai-split-pre`},J={key:0,class:`ai-split-tab-count`},_e={class:`ai-split-pre ai-split-pre--think`},ve={key:0,class:`ai-split-tab-count`},ye={class:`ai-split-pre`},be={key:0,class:`ai-split-tab-count`},xe={key:0,class:`ai-split-empty`},Se={key:1,class:`ai-split-list`},Ce={class:`ai-split-list__no`},we={class:`ai-split-list__body`},Y={class:`ai-split-list__title`},Te={class:`ai-split-list__desc`},Ee=H(i({__name:`AISplitDialog`,props:{modelValue:{type:Boolean},title:{},desc:{},taskId:{},promptId:{}},emits:[`update:modelValue`,`confirm`],setup(t,{emit:i}){let s=t,c=i,l=N(`idle`),u=N(`prompt`),p=N(``),m=N(``),g=N(``),_=N(``),ee=N(``),S=N([]),C=N(``),T=null,E=0,O=r(()=>g.value.length),ne=r(()=>_.value.length),j=r(()=>l.value===`result`&&S.value.length>0),M=r(()=>l.value===`running`);function se(){ce(),c(`update:modelValue`,!1)}function ce(){T&&=(T.abort(),null)}function F(){ce(),l.value=`idle`,u.value=`prompt`,p.value=``,m.value=``,g.value=``,_.value=``,ee.value=``,S.value=[],C.value=``}async function I(){if(!s.title.trim()){h.warning(V(`@WORKBENCH:请先填写任务标题`));return}F();let e=++E;l.value=`running`,u.value=`thinking`,T=new AbortController;let t=T;try{let n=await fetch(`/api/workbench/tasks/ai-split-subtasks`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`text/event-stream`},body:JSON.stringify({title:s.title,desc:s.desc,taskId:s.taskId||``,promptId:s.promptId||``}),signal:t.signal});if(e!==E)return;if(!n.ok||!n.body){let e=await n.text().catch(()=>``);throw Error(e||`HTTP ${n.status}`)}let r=n.body.getReader(),i=new TextDecoder(`utf-8`),a=``,o=null;for(;;){let{value:t,done:n}=await r.read();if(e!==E)return;if(n)break;a+=i.decode(t,{stream:!0});let s=a.split(`
|
|
2
|
-
`);a=s.pop()??``;for(let e of s){let t=e.trim();if(!t.startsWith(`data:`))continue;let n=t.slice(5).trim();if(!n)continue;let r;try{r=JSON.parse(n)}catch{continue}switch(r.type){case`meta`:p.value=r.prompt?.system||``,m.value=r.prompt?.user||``;break;case`thinking`:g.value+=String(r.delta||``);break;case`content`:_.value+=String(r.delta||``);break;case`done`:o=r;break;case`error`:throw Error(r.error||`AI 拆分失败`)}}}if(e!==E)return;if(!o)throw Error(V(`@WORKBENCH:AI 未返回结果`));S.value=Array.isArray(o.subtasks)?o.subtasks:[],ee.value=o.raw||_.value,g.value||=V(`@WORKBENCH:(当前模型未返回独立的思考内容,可切换到「原始结果」查看完整输出)`),l.value=`result`,u.value=S.value.length>0?`confirm`:`raw`}catch(n){if(console.log(`[AI Split] catch err:`,{name:n?.name,msg:n?.message,aborted:t.signal.aborted,nonce:{mine:e,cur:E}}),e!==E||n?.name===`AbortError`||t.signal.aborted)return;l.value=`error`,C.value=n?.message||String(n)}finally{e===E&&(T=null)}}function le(){j.value&&(c(`confirm`,S.value.slice()),c(`update:modelValue`,!1))}function ue(){I()}oe(()=>s.modelValue,e=>{e&&l.value===`idle`&&I()});let L=N(!1),R=N(``),z=N(!1);function de(){R.value=p.value,L.value=!0}async function fe(){if(!R.value.trim()){h.warning(V(`@WORKBENCH:指令内容不能为空`));return}z.value=!0;try{let e=await fetch(`/api/workbench/tasks/ai-subtask-instruction`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({instruction:R.value})}).then(e=>e.json());e.success?(h.success(V(`@WORKBENCH:已保存指令`)),L.value=!1,ue()):h.error(e.error||V(`@WORKBENCH:保存失败`))}catch(e){h.error(V(`@WORKBENCH:保存失败: `)+(e?.message||e))}finally{z.value=!1}}function H(){L.value=!1}return(r,i)=>{let s=te,h=re,ee=ae,T=e,E=x,oe=ie;return v(),d(oe,{"model-value":t.modelValue,"onUpdate:modelValue":i[2]||=e=>c(`update:modelValue`,e),title:k(V)(`@WORKBENCH:AI 拆分任务`),width:`800px`,"close-on-click-modal":!1,"close-on-press-escape":!M.value,"show-close":!M.value,onClose:se},{footer:D(()=>[l.value===`error`?(v(),d(h,{key:0,onClick:ue},{default:D(()=>[a(P(k(V)(`@WORKBENCH:重新生成`)),1)]),_:1})):y(``,!0),b(h,{onClick:se},{default:D(()=>[a(P(k(V)(`@WORKBENCH:取消`)),1)]),_:1}),b(h,{type:`primary`,disabled:!j.value,onClick:le},{default:D(()=>[a(P(k(V)(`@WORKBENCH:确认入库`))+` (`+P(S.value.length)+`) `,1)]),_:1},8,[`disabled`])]),default:D(()=>[M.value?(v(),B(`div`,U,[b(s,{class:`is-loading`},{default:D(()=>[b(k(w))]),_:1}),o(`span`,null,P(k(V)(`@WORKBENCH:AI 正在拆分…`)),1)])):l.value===`error`?(v(),B(`div`,me,[b(s,{color:`#f56c6c`},{default:D(()=>[b(k(n))]),_:1}),o(`span`,null,P(C.value),1)])):y(``,!0),b(E,{modelValue:u.value,"onUpdate:modelValue":i[1]||=e=>u.value=e,class:`ai-split-tabs`},{default:D(()=>[b(T,{label:k(V)(`@WORKBENCH:提示词`),name:`prompt`},{default:D(()=>[o(`div`,W,[b(h,{size:`small`,disabled:M.value,onClick:de},{default:D(()=>[a(P(k(V)(`@WORKBENCH:编辑指令`)),1)]),_:1},8,[`disabled`])]),L.value?(v(),B(`div`,G,[b(ee,{modelValue:R.value,"onUpdate:modelValue":i[0]||=e=>R.value=e,type:`textarea`,rows:12,placeholder:k(V)(`@WORKBENCH:指令内容`)},null,8,[`modelValue`,`placeholder`]),o(`div`,K,[b(h,{size:`small`,onClick:H},{default:D(()=>[a(P(k(V)(`@WORKBENCH:取消`)),1)]),_:1}),b(h,{size:`small`,type:`primary`,loading:z.value,onClick:fe},{default:D(()=>[a(P(k(V)(`@WORKBENCH:保存并重跑`)),1)]),_:1},8,[`loading`])])])):(v(),B(`pre`,q,P(p.value||`(等待 LLM 启动…)`),1)),o(`h4`,he,P(k(V)(`@WORKBENCH:用户提示词(拼装后)`)),1),o(`pre`,ge,P(m.value||`(等待 LLM 启动…)`),1)]),_:1},8,[`label`]),b(T,{name:`thinking`},{label:D(()=>[o(`span`,null,[a(P(k(V)(`@WORKBENCH:思考`))+` `,1),O.value>0?(v(),B(`span`,J,`(`+P(O.value)+`)`,1)):y(``,!0)])]),default:D(()=>[o(`pre`,_e,P(g.value||k(V)(`@WORKBENCH:(暂无输出)`)),1)]),_:1}),b(T,{name:`raw`},{label:D(()=>[o(`span`,null,[a(P(k(V)(`@WORKBENCH:原始结果`))+` `,1),ne.value>0?(v(),B(`span`,ve,`(`+P(ne.value)+`)`,1)):y(``,!0)])]),default:D(()=>[o(`pre`,ye,P(_.value||k(V)(`@WORKBENCH:(暂无输出)`)),1)]),_:1}),b(T,{name:`confirm`,disabled:S.value.length===0},{label:D(()=>[o(`span`,null,[a(P(k(V)(`@WORKBENCH:确认入库`))+` `,1),S.value.length>0?(v(),B(`span`,be,`(`+P(S.value.length)+`)`,1)):y(``,!0)])]),default:D(()=>[S.value.length===0?(v(),B(`div`,xe,P(k(V)(`@WORKBENCH:(暂无子任务)`)),1)):(v(),B(`ul`,Se,[(v(!0),B(f,null,A(S.value,(e,t)=>(v(),B(`li`,{key:t,class:`ai-split-list__item`},[o(`div`,Ce,P(t+1),1),o(`div`,we,[o(`div`,Y,P(e.title),1),o(`div`,Te,P(e.desc),1)])]))),128))]))]),_:1},8,[`disabled`])]),_:1},8,[`modelValue`])]),_:1},8,[`model-value`,`title`,`close-on-press-escape`,`show-close`])}}}),[[`__scopeId`,`data-v-15311727`]]),De={class:`wb-attachments__head`},Oe={class:`wb-attachments__label`},ke={class:`wb-attachments__count`},Ae=[`disabled`],je={key:0,class:`wb-attachments__paste-hint`},Me={key:1,class:`wb-attachments__list`},Ne=[`title`,`onClick`],Pe=[`src`,`alt`],Fe={key:1},Ie={class:`wb-attachment__meta`},Le=[`title`],Re={class:`wb-attachment__sub`},ze=[`onClick`,`title`],Be=i({__name:`AttachmentZone`,props:{attachments:{},isImage:{type:Function},humanSize:{type:Function},isUploading:{type:Boolean},isPasteHover:{type:Boolean},maxCount:{},onPick:{type:Function},onRemove:{type:Function}},emits:[`paste`,`drop`,`dragover`,`dragenter`,`dragleave`],setup(e,{emit:t}){let n=e,i=t,s=r(()=>n.attachments.filter(e=>n.isImage(e))),l=r(()=>s.value.map(e=>`/api/workbench/attachments/${e.id}/raw`)),u=N(0),p=N(!1);function m(e){if(n.isImage(e)){let t=s.value.findIndex(t=>t.id===e.id);u.value=t>=0?t:0,p.value=!0}else window.open(`/api/workbench/attachments/${e.id}/raw`,`_blank`,`noopener`)}function h(){p.value=!1}return(t,n)=>(v(),B(f,null,[o(`div`,{class:j([`wb-attachments`,{"is-paste-hover":e.isPasteHover}]),onPaste:n[1]||=e=>i(`paste`,e),onDrop:n[2]||=M(e=>i(`drop`,e),[`prevent`]),onDragover:n[3]||=M(e=>i(`dragover`,e),[`prevent`]),onDragenter:n[4]||=M(e=>i(`dragenter`,e),[`prevent`]),onDragleave:n[5]||=e=>i(`dragleave`,e)},[o(`div`,De,[o(`span`,Oe,[a(P(k(V)(`@WORKBENCH:附件`))+` `,1),o(`span`,ke,P(e.attachments.length)+` / `+P(e.maxCount),1)]),o(`button`,{class:`wb-attachments__add`,disabled:e.isUploading||e.attachments.length>=e.maxCount,onClick:n[0]||=(...t)=>e.onPick&&e.onPick(...t)},P(e.isUploading?k(V)(`@WORKBENCH:上传中…`):k(V)(`@WORKBENCH:添加附件`)),9,Ae)]),e.isPasteHover?(v(),B(`div`,je,P(k(V)(`@WORKBENCH:粘贴图片以快速添加`)),1)):y(``,!0),e.attachments.length>0?(v(),B(`ul`,Me,[(v(!0),B(f,null,A(e.attachments,t=>(v(),B(`li`,{key:t.id,class:`wb-attachment`},[o(`button`,{type:`button`,class:`wb-attachment__preview`,title:k(V)(`@WORKBENCH:点击预览`),onClick:e=>m(t)},[o(`div`,{class:j([`wb-attachment__icon`,{"wb-attachment__icon--img":e.isImage(t)}])},[e.isImage(t)?(v(),B(`img`,{key:0,src:`/api/workbench/attachments/${t.id}/raw`,alt:t.originalName,loading:`lazy`},null,8,Pe)):(v(),B(`span`,Fe,P(t.ext.toUpperCase()),1))],2),o(`div`,Ie,[o(`div`,{class:`wb-attachment__name`,title:t.originalName},P(t.originalName),9,Le),o(`div`,Re,P(e.humanSize(t.size))+` · `+P(t.mimeType),1)])],8,Ne),o(`button`,{class:`wb-attachment__del`,onClick:M(n=>e.onRemove(t),[`stop`]),title:k(V)(`@WORKBENCH:删除`)},`×`,8,ze)]))),128))])):y(``,!0)],34),(v(),d(g,{to:`body`},[p.value&&l.value.length>0?(v(),d(k(c),{key:0,"url-list":l.value,"initial-index":u.value,"hide-on-click-modal":!0,onClose:h},null,8,[`url-list`,`initial-index`])):y(``,!0)]))],64))}}),Ve={class:`workbench`},He={class:`wb-sidebar`},Ue={class:`wb-section`},We={class:`wb-section__head`},Ge={class:`wb-section__tag`},Ke={class:`wb-section__title`},qe={class:`wb-section__count`},Je={class:`wb-task-list`},Ye=[`data-icon`,`onClick`],Xe=[`data-icon`],Ze={class:`wb-task-item__body`},Qe=[`title`],$e={class:`wb-task-item__meta`},et=[`title`],tt={class:`wb-task-item__num`},nt=[`title`],rt={class:`wb-task-item__num`},it=[`title`],at=[`onClick`,`title`,`aria-label`],ot={key:0,class:`wb-empty`},st={class:`wb-empty__icon`},ct={class:`wb-empty__text`},lt={class:`wb-empty__hint`},ut={class:`wb-section`},dt={class:`wb-section__head`},ft={class:`wb-section__tag wb-section__tag--accent`},pt={class:`wb-section__title`},mt={class:`wb-section__count`},ht=[`title`,`aria-label`],gt={class:`wb-prompt-list`},_t={class:`wb-prompt-item__icon`},vt=[`onClick`,`title`],yt=[`onClick`,`title`,`aria-label`],bt={key:0,class:`wb-empty wb-empty--compact`},xt={class:`wb-split`},St={key:0,class:`wb-placeholder`},Ct={class:`wb-split__header`},wt=[`placeholder`],Tt={value:null},Et=[`value`],Dt=[`placeholder`],Ot={class:`wb-split__sub-header`},kt={class:`wb-split__sub-actions`},At={key:0,class:`wb-dirty-badge`},jt={class:`wb-sub-list`},Mt={key:0,class:`wb-sub-item__row wb-sub-item__row--compact`},Nt=[`title`],Pt=[`title`],Ft=[`title`,`aria-label`,`onClick`],It=[`title`,`aria-label`,`onClick`],Lt=[`title`,`aria-label`,`onClick`],Rt={class:`wb-sub-item__row`},zt=[`onUpdate:modelValue`,`placeholder`,`onPaste`],Bt=[`title`],Vt=[`title`],Ht=[`title`,`onClick`],Ut=[`title`,`aria-label`,`onClick`],Wt=[`title`,`aria-label`,`onClick`],Gt=[`onClick`],Kt=[`onUpdate:modelValue`,`placeholder`,`onPaste`],qt=[`open`],Jt={class:`wb-log-summary`},Yt={key:0},Xt={key:1},Zt={key:2},Qt={class:`wb-log-summary__meta`},$t={key:0,class:`wb-log-section`},en={class:`wb-log-section__summary`},tn={class:`wb-log-section__tag wb-log-section__tag--user`},nn={class:`wb-log-section__count`},rn={class:`wb-log-section__pre wb-log-section__pre--user`},an={key:1,class:`wb-log-section`},on={class:`wb-log-section__summary`},sn={class:`wb-log-section__tag wb-log-section__tag--think`},cn={class:`wb-log-section__count`},ln={class:`wb-log-section__pre wb-log-section__pre--think`},un={key:0,class:`wb-empty wb-empty--rich`},dn={class:`wb-empty__art`,"aria-hidden":`true`},fn={class:`wb-empty__title`},pn={class:`wb-empty__hint`},mn={class:`wb-empty__cta`},hn=[`disabled`],gn={style:{display:`flex`,gap:`8px`,"align-items":`center`}},_n=64*1024,vn=`.png,.jpg,.jpeg,.gif,.webp,.bmp,.svg,.pdf,.txt,.md,.csv,.json,.log`,yn=5*1024*1024,bn=H(i({__name:`WorkbenchView`,setup(e){let n=N([]),i=N([]),c=N([]),g=N(null),x=r(()=>i.value.find(e=>e.id===g.value)||null),w=N(new Map);function H(){let e=new Map;if(x.value)for(let t of x.value.subtasks)e.set(t.id,{id:t.id,title:t.title,desc:t.desc,promptOverride:t.promptOverride});w.value=e}let U=r(()=>{let e=new Set;if(!x.value)return e;for(let t of x.value.subtasks){let n=w.value.get(t.id);if(!n){e.add(t.id);continue}(n.title!==t.title||n.desc!==t.desc||n.promptOverride!==t.promptOverride)&&e.add(t.id)}return e}),me=r(()=>U.value.size>0),W=O({visible:!1,editing:null,name:``,content:``,aiLoading:!1}),G=O({visible:!1,text:``,loading:!1,saving:!1}),K=O({visible:!1,editing:null,title:``,desc:``,promptId:null}),q=null,he=pe();function ge(){he.setRunning(c.value.filter(e=>e.status===`running`).length)}function J(e){return c.value.find(t=>t.subId===e)||null}function _e(e,t){if(e===`hello`){c.value=t.jobs||[],ge();return}if(e===`job:update`){let e=t,n=c.value.findIndex(t=>t.id===e.id);n>=0?c.value[n]=e:c.value.push(e),ge()}if(e===`sub:update`){let e=i.value.find(e=>e.id===t.taskId);if(e){let n=e.subtasks.findIndex(e=>e.id===t.sub.id);n>=0&&(e.subtasks[n]=t.sub)}}if(e===`task:update`){let e=i.value.findIndex(e=>e.id===t.id);e>=0&&(i.value[e]=t)}e===`task:error`&&h.error(t.error||V(`@WORKBENCH:执行出错`))}let ve=[{key:`image`,patterns:[/图\s*片/,/上\s*传/,/upload/i,/image/i,/screenshot/i,/截图/,/附件/,/attachment/i]},{key:`icon`,patterns:[/图\s*标/,/icon/i,/svg/i]},{key:`test`,patterns:[/测\s*试/,/test/i,/spec/i,/\bqa\b/i]},{key:`ui`,patterns:[/ui\s*优\s*化/,/样\s*式/,/style/i,/css/i,/界面/,/视\s*觉/,/design/i]}];function ye(e){let t=(e||``).trim();if(!t)return`doc`;for(let e of ve)if(e.patterns.some(e=>e.test(t)))return e.key;return`doc`}let be={icon:L(s),image:L(R),test:L(fe),ui:L(T),doc:L(s),idea:L(T)};function xe(e){return be[ye(e.title)]}function Se(e){return Array.isArray(e.attachments)?e.attachments.length:0}function Ce(e){return Array.isArray(e.subtasks)?e.subtasks.length:0}async function we(){n.value=(await fetch(`/api/workbench/prompts`).then(e=>e.json()).catch(()=>({prompts:[]}))).prompts||[]}async function Y(){i.value=(await fetch(`/api/workbench/tasks`).then(e=>e.json()).catch(()=>({tasks:[]}))).tasks||[],!g.value&&i.value.length>0&&(g.value=i.value[0].id),H()}async function Te(){c.value=(await fetch(`/api/workbench/jobs`).then(e=>e.json()).catch(()=>({jobs:[]}))).jobs||[],ge()}function De(){q&&=(q.close(),null),q=new EventSource(`/api/workbench/events`),q.onmessage=e=>{try{let t=JSON.parse(e.data);_e(t.event,t.payload)}catch{}},q.onerror=()=>{q&&=(q.close(),null),setTimeout(De,3e3)}}function Oe(){W.editing=null,W.name=``,W.content=``,W.aiLoading=!1,W.visible=!0}function ke(e){W.editing=e,W.name=e.name,W.content=e.content,W.aiLoading=!1,W.visible=!0}async function Ae(){if(!W.aiLoading){W.aiLoading=!0;try{let e=await fetch(`/api/workbench/prompts/ai-generate`,{method:`POST`}).then(e=>e.json());e.success?(W.name=e.name||W.name,W.content=e.result||``,h.success(V(`@WORKBENCH:已生成,可继续编辑`))):h.error(e.error||V(`@WORKBENCH:生成失败`))}catch(e){h.error(V(`@WORKBENCH:网络错误: `)+(e&&e.message||e))}finally{W.aiLoading=!1}}}async function je(){G.visible=!0,G.loading=!0;try{let e=await fetch(`/api/workbench/prompts/ai-instruction`).then(e=>e.json());e.success?G.text=e.instruction||``:h.error(e.error||V(`@WORKBENCH:读取指令失败`))}catch(e){h.error(V(`@WORKBENCH:读取指令失败`)+`: `+(e&&e.message||e))}finally{G.loading=!1}}async function Me(){if(!G.text.trim()){h.warning(V(`@WORKBENCH:指令内容不能为空`));return}G.saving=!0;try{let e=await fetch(`/api/workbench/prompts/ai-instruction`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({instruction:G.text})}).then(e=>e.json());e.success?(h.success(V(`@WORKBENCH:已保存指令`)),G.visible=!1):h.error(e.error||V(`@WORKBENCH:保存失败`))}catch(e){h.error(V(`@WORKBENCH:网络错误: `)+(e&&e.message||e))}finally{G.saving=!1}}async function Ne(){if(!W.name.trim()||!W.content.trim()){h.warning(V(`@WORKBENCH:名称和内容不能为空`));return}let e={id:W.editing?.id,name:W.name.trim(),content:W.content},t=await fetch(`/api/workbench/prompts`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)}).then(e=>e.json());t.success?(h.success(V(`@WORKBENCH:已保存`)),W.visible=!1,we()):h.error(t.error||V(`@WORKBENCH:保存失败`))}async function Pe(e){await p.confirm(V(`@WORKBENCH:删除提示词「{name}」?`,{name:e.name}),V(`@WORKBENCH:确认`),{type:`warning`}),await fetch(`/api/workbench/prompts/${e.id}`,{method:`DELETE`}),we();for(let t of i.value)t.promptId===e.id&&(t.promptId=null,await fetch(`/api/workbench/tasks`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)}));Y()}function Fe(){K.editing=null,K.title=``,K.desc=``,K.promptId=null,K.visible=!0}async function Ie(){if(!K.title.trim()){h.warning(V(`@WORKBENCH:标题必填`));return}let e={id:K.editing?.id,title:K.title.trim(),desc:K.desc,promptId:K.promptId,subtasks:K.editing?.subtasks||[]},t=await fetch(`/api/workbench/tasks`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)}).then(e=>e.json());t.success?(h.success(V(`@WORKBENCH:已保存`)),K.visible=!1,Y(),g.value||=t.task.id):h.error(t.error||V(`@WORKBENCH:保存失败`))}async function Le(e){await p.confirm(V(`@WORKBENCH:删除任务「{title}」及其所有子任务?`,{title:e.title}),V(`@WORKBENCH:确认`),{type:`warning`}),await fetch(`/api/workbench/tasks/${e.id}`,{method:`DELETE`}),g.value===e.id&&(g.value=null),Y()}function Re(e){g.value!==e.id&&(g.value=e.id,H())}function ze(){if(!x.value)return;let e={id:`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`,title:V(`@WORKBENCH:新子任务`),desc:``,status:`todo`,promptOverride:``};x.value.subtasks.push(e),X(!1)}function bn(e){x.value&&(x.value.subtasks=x.value.subtasks.filter(t=>t.id!==e.id),X(!1))}async function X(e){if(!x.value)return!1;let t=await fetch(`/api/workbench/tasks`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(x.value)}).then(e=>e.json());return t.success?(e&&h.success(V(`@WORKBENCH:已保存拆分`)),await Y(),!0):(h.error(t.error||V(`@WORKBENCH:保存失败`)),!1)}async function xn(){await X(!0)}let Sn=N(!1);function Cn(){if(x.value){if(!(x.value.title||``).trim()){h.warning(V(`@WORKBENCH:请先填写任务标题`));return}Sn.value=!0}}function wn(e){if(x.value){for(let t of e)x.value.subtasks.push({id:`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`,title:t.title,desc:t.desc||``,status:`todo`,promptOverride:``,attachments:[]});h.success(V(`@WORKBENCH:已生成 {n} 个子任务,请审阅后保存`,{n:e.length}))}}async function Tn(e){if(!e.subtasks||e.subtasks.length===0){h.warning(V(`@WORKBENCH:请先拆分任务`));return}if(x.value&&x.value.id===e.id){let t=i.value.find(t=>t.id===e.id);if((!t||t.title!==x.value.title||t.desc!==x.value.desc||JSON.stringify(t.subtasks)!==JSON.stringify(x.value.subtasks)||t.promptId!==x.value.promptId)&&!await X(!1))return}let t=await fetch(`/api/workbench/tasks/${e.id}/run`,{method:`POST`}).then(e=>e.json());t.success?h.success(t.message||V(`@WORKBENCH:已加入执行队列`)):h.error(t.error||V(`@WORKBENCH:执行失败`))}function En(e){switch(e){case`todo`:return V(`@WORKBENCH:待执行`);case`running`:return V(`@WORKBENCH:执行中`);case`done`:return V(`@WORKBENCH:已完成`);case`error`:return V(`@WORKBENCH:出错`);case`cancelled`:return V(`@WORKBENCH:已取消`);case`pending`:return V(`@WORKBENCH:排队中`);default:return e}}function Dn(e){switch(e){case`running`:return`var(--color-primary)`;case`done`:return`#22c55e`;case`error`:return`#ef4444`;case`cancelled`:return`#9ca3af`;case`pending`:return`#f59e0b`;default:return`var(--text-tertiary)`}}let Z=N(new Set);function On(e){return e.status===`done`?!Z.value.has(e.id):!1}function kn(e){Z.value.has(e.id)?Z.value.delete(e.id):Z.value.add(e.id),Z.value=new Set(Z.value)}async function An(e){if(!x.value)return;let t=w.value.get(e.id);t&&w.value.set(e.id,{...t,status:`todo`}),e.status=`todo`,await X(!1)}async function jn(e){try{await p.confirm(V(`@WORKBENCH:确认停止执行?已输出的内容会保留。`),V(`@WORKBENCH:停止执行`),{confirmButtonText:V(`@WORKBENCH:停止`),cancelButtonText:V(`@WORKBENCH:取消`),type:`warning`})}catch{return}let t=await fetch(`/api/workbench/jobs/${e.id}/cancel`,{method:`POST`}).then(e=>e.json()).catch(e=>({success:!1,error:e?.message||String(e)}));t.success?h.success(V(`@WORKBENCH:已发送停止信号`)):h.error(t.error||V(`@WORKBENCH:停止失败`))}S(async()=>{await Promise.all([we(),Y(),Te()]),De()}),C(()=>{q&&=(q.close(),null)});let Mn=N({});function Nn(e){return t=>{Mn.value[e]=t}}oe(()=>c.value.map(e=>({id:e.id,len:(e.output||``).length})),e=>{e.forEach(({id:e})=>{let t=c.value.find(t=>t.id===e);if(!t||t.status!==`running`)return;let n=Mn.value[t.subId];n&&l(()=>{n.scrollTop=n.scrollHeight})})},{deep:!0,flush:`post`});function Pn(e){return e?e.length<=_n?e:`${V(`@WORKBENCH:…(前文已截断)`)}\n${e.slice(-65536)}`:``}let Fn=new Set([`image/png`,`image/jpeg`,`image/jpg`,`image/gif`,`image/webp`,`image/bmp`,`image/svg+xml`,`application/pdf`,`text/plain`,`text/markdown`,`text/x-markdown`,`text/csv`,`application/json`,`text/json`,`text/x-log`]),In=N({}),Q=N(null);function Ln(e){return!!In.value[e]}function Rn(e,t){if(e instanceof File)return e;let n=e.type||`application/octet-stream`;return new File([e],t,{type:n})}function zn(e){return e.kind===`task`?`task-${e.task?.id??``}`:`sub-${e.sub.id}`}function Bn(e){let t=e.kind===`task`?e.task?.attachments:e.sub.attachments;return Array.isArray(t)?t:[]}function Vn(e,t){e.kind===`task`?e.task&&(e.task.attachments=t):e.sub.attachments=t}function Hn(e){return e.kind===`task`?`/api/workbench/tasks/${e.task?.id??``}/attachments`:`/api/workbench/subtasks/${e.sub.id}/attachments`}function Un(e,t){return e.kind===`task`?`/api/workbench/tasks/${e.task?.id??``}/attachments/${t}`:`/api/workbench/subtasks/${e.sub.id}/attachments/${t}`}function $(e,t){if(!e.clipboardData)return;let n=Array.from(e.clipboardData.items).filter(e=>e.kind===`file`&&e.type.startsWith(`image/`));if(n.length>0){e.preventDefault();for(let e of n){let n=e.getAsFile();if(!n)continue;let r=(n.type.split(`/`)[1]||`png`).replace(`jpeg`,`jpg`);Gn(t,Rn(n,`paste-${new Date().toISOString().replace(/[:.]/g,`-`)}.${r}`))}return}let r=Array.from(e.clipboardData.items).filter(e=>e.kind===`file`&&!e.type.startsWith(`image/`));if(r.length>0){e.preventDefault();for(let e of r){let n=e.getAsFile();n&&Gn(t,Rn(n,n.name||`pasted-file`))}}}function Wn(e,t){Q.value=null,Array.from(e.dataTransfer?.files||[]).forEach(e=>Gn(t,e))}async function Gn(e,t){if(t.size>yn){h.error(`「${t.name}」${V(`@WORKBENCH:超过 5MB 限制`)}`);return}if(!Fn.has(t.type)&&!t.name.match(/\.(png|jpg|jpeg|gif|webp|bmp|svg|pdf|txt|md|markdown|csv|json|log)$/i)){h.error(`${V(`@WORKBENCH:不支持的文件类型`)}(${t.name})`);return}if(Bn(e).length>=9){h.error(V(`@WORKBENCH:单个任务最多 9 个附件`));return}let n=zn(e);In.value[n]=!0;try{let n=await fetch(Hn(e),{method:`POST`,headers:{"Content-Type":`application/octet-stream`,"X-Original-Name":t.name,"X-Mime-Type":t.type||`application/octet-stream`},body:t}).then(e=>e.json());if(n.success){let r=Bn(e);r.push(n.attachment),Vn(e,r),h.success(`${V(`@WORKBENCH:已添加:`)}${t.name}`)}else h.error(n.error||V(`@WORKBENCH:上传失败`))}catch(e){h.error(V(`@WORKBENCH:上传失败`)+`:`+(e&&e.message||e))}finally{In.value[n]=!1}}async function Kn(e,t){try{await p.confirm(V(`@WORKBENCH:删除附件「{name}」?`,{name:t.originalName}),V(`@WORKBENCH:确认`),{type:`warning`})}catch{return}let n=await fetch(Un(e,t.id),{method:`DELETE`}).then(e=>e.json());n.success?(Vn(e,Bn(e).filter(e=>e.id!==t.id)),h.success(V(`@WORKBENCH:已删除`))):h.error(n.error||V(`@WORKBENCH:删除失败`))}function qn(e){let t=document.createElement(`input`);t.type=`file`,t.accept=vn,t.multiple=!0,t.onchange=()=>{Array.from(t.files||[]).forEach(t=>Gn(e,t))},t.click()}let Jn=new Set([`png`,`jpg`,`jpeg`,`gif`,`webp`,`bmp`,`svg`]);function Yn(e){return Jn.has(String(e.ext||``).toLowerCase())}function Xn(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(2)} MB`}return(e,r)=>{let s=te,c=re,l=ae,p=z,h=E,S=ie,C=ne,w=ce;return v(),B(`div`,Ve,[o(`aside`,He,[o(`section`,Ue,[o(`header`,We,[o(`span`,Ge,P(k(V)(`@WORKBENCH:任务`)),1),o(`h3`,Ke,P(k(V)(`@WORKBENCH:任务列表`)),1),o(`span`,qe,P(i.value.length),1)]),o(`button`,{class:`wb-new-btn`,onClick:Fe},[b(s,{class:`wb-new-btn__icon`},{default:D(()=>[b(k(F))]),_:1}),o(`span`,null,P(k(V)(`@WORKBENCH:新建任务`)),1),r[23]||=o(`span`,{class:`wb-new-btn__shortcut`},`N`,-1)]),o(`ul`,Je,[(v(!0),B(f,null,A(i.value,e=>(v(),B(`li`,{key:e.id,class:j([`wb-task-item`,{active:e.id===g.value,"has-attachment":Se(e)>0}]),"data-icon":ye(e.title),onClick:t=>Re(e)},[o(`div`,{class:`wb-task-item__avatar`,"data-icon":ye(e.title)},[b(s,null,{default:D(()=>[(v(),d(ue(xe(e))))]),_:2},1024)],8,Xe),o(`div`,Ze,[o(`div`,{class:`wb-task-item__title`,title:e.title},P(e.title||k(V)(`@WORKBENCH:未命名任务`)),9,Qe),o(`div`,$e,[o(`span`,{class:`wb-task-item__meta-item`,title:k(V)(`@WORKBENCH:个子任务`)},[b(s,{class:`wb-task-item__meta-icon`},{default:D(()=>[b(k(u))]),_:1}),o(`span`,tt,P(Ce(e)),1)],8,et),Se(e)>0?(v(),B(`span`,{key:0,class:`wb-task-item__meta-item`,title:k(V)(`@WORKBENCH:附件`)},[b(s,{class:`wb-task-item__meta-icon`},{default:D(()=>[b(k(R))]),_:1}),o(`span`,rt,P(Se(e)),1)],8,nt)):y(``,!0),e.promptId?(v(),B(`span`,{key:1,class:`wb-task-item__meta-item wb-task-item__meta-item--accent`,title:k(V)(`@WORKBENCH:已绑定预置提示词`)},[b(s,{class:`wb-task-item__meta-icon`},{default:D(()=>[b(k(T))]),_:1})],8,it)):y(``,!0)])]),o(`button`,{class:`wb-task-item__del`,onClick:M(t=>Le(e),[`stop`]),title:k(V)(`@WORKBENCH:删除`),"aria-label":k(V)(`@WORKBENCH:删除`)},[b(s,null,{default:D(()=>[b(k(se))]),_:1})],8,at)],10,Ye))),128)),i.value.length===0?(v(),B(`li`,ot,[o(`div`,st,[b(s,null,{default:D(()=>[b(k(_))]),_:1})]),o(`div`,ct,P(k(V)(`@WORKBENCH:暂无任务`)),1),o(`div`,lt,P(k(V)(`@WORKBENCH:点击上方按钮新建`)),1)])):y(``,!0)])]),o(`section`,ut,[o(`header`,dt,[o(`span`,ft,P(k(V)(`@WORKBENCH:提示`)),1),o(`h3`,pt,P(k(V)(`@WORKBENCH:预置提示词`)),1),o(`span`,mt,P(n.value.length),1),o(`button`,{class:`wb-section__action`,onClick:Oe,title:k(V)(`@WORKBENCH:新建提示词`),"aria-label":k(V)(`@WORKBENCH:新建提示词`)},[b(s,null,{default:D(()=>[b(k(F))]),_:1})],8,ht)]),o(`ul`,gt,[(v(!0),B(f,null,A(n.value,e=>(v(),B(`li`,{key:e.id,class:`wb-prompt-item`},[o(`div`,_t,[b(s,null,{default:D(()=>[b(k(T))]),_:1})]),o(`span`,{class:`wb-prompt-item__name`,onClick:t=>ke(e),title:e.content},P(e.name),9,vt),o(`button`,{class:`wb-prompt-item__del`,onClick:t=>Pe(e),title:k(V)(`@WORKBENCH:删除`),"aria-label":k(V)(`@WORKBENCH:删除`)},[b(s,null,{default:D(()=>[b(k(se))]),_:1})],8,yt)]))),128)),n.value.length===0?(v(),B(`li`,bt,P(k(V)(`@WORKBENCH:暂无提示词`)),1)):y(``,!0)])])]),o(`section`,xt,[x.value?(v(),B(f,{key:1},[o(`div`,Ct,[I(o(`input`,{class:`wb-input wb-input--title`,"onUpdate:modelValue":r[0]||=e=>x.value.title=e,placeholder:k(V)(`@WORKBENCH:任务标题`)},null,8,wt),[[m,x.value.title]]),I(o(`select`,{class:`wb-select`,"onUpdate:modelValue":r[1]||=e=>x.value.promptId=e},[o(`option`,Tt,P(k(V)(`@WORKBENCH:不绑定预置提示词`)),1),(v(!0),B(f,null,A(n.value,e=>(v(),B(`option`,{key:e.id,value:e.id},P(e.name),9,Et))),128))],512),[[t,x.value.promptId]]),b(c,{type:`info`,plain:``,disabled:!x.value.title||!x.value.title.trim(),onClick:Cn},{default:D(()=>[a(P(k(V)(`@WORKBENCH:AI 拆分`)),1)]),_:1},8,[`disabled`]),b(c,{type:`primary`,loading:!1,onClick:r[2]||=e=>Tn(x.value)},{default:D(()=>[a(P(k(V)(`@WORKBENCH:执行任务`)),1)]),_:1})]),I(o(`textarea`,{class:`wb-textarea`,"onUpdate:modelValue":r[3]||=e=>x.value.desc=e,placeholder:k(V)(`@WORKBENCH:任务描述(可选)`),rows:`3`,onPaste:r[4]||=e=>$(e,{kind:`task`,task:x.value})},null,40,Dt),[[m,x.value.desc]]),b(Be,{attachments:x.value.attachments||[],"is-image":Yn,"human-size":Xn,"is-uploading":Ln(`task-`+x.value.id),"is-paste-hover":Q.value===`task-`+x.value.id,"max-count":9,"on-pick":()=>qn({kind:`task`,task:x.value}),"on-remove":e=>Kn({kind:`task`,task:x.value},e),onPaste:r[5]||=e=>$(e,{kind:`task`,task:x.value}),onDrop:r[6]||=M(e=>Wn(e,{kind:`task`,task:x.value}),[`prevent`]),onDragover:r[7]||=M(e=>Q.value=`task-`+x.value.id,[`prevent`]),onDragenter:r[8]||=M(e=>Q.value=`task-`+x.value.id,[`prevent`]),onDragleave:r[9]||=e=>Q.value=Q.value===`task-`+x.value.id?null:Q.value},null,8,[`attachments`,`is-uploading`,`is-paste-hover`,`on-pick`,`on-remove`]),o(`div`,Ot,[o(`h4`,null,P(k(V)(`@WORKBENCH:子任务拆分`)),1),o(`div`,kt,[b(c,{size:`small`,plain:``,icon:k(F),disabled:x.value.subtasks.length===0,onClick:ze},{default:D(()=>[a(P(k(V)(`@WORKBENCH:添加子任务`)),1)]),_:1},8,[`icon`,`disabled`]),b(c,{size:`small`,type:me.value?`primary`:`default`,disabled:x.value.subtasks.length===0,onClick:xn},{default:D(()=>[a(P(k(V)(`@WORKBENCH:保存拆分`))+` `,1),me.value?(v(),B(`span`,At,P(U.value.size),1)):y(``,!0)]),_:1},8,[`type`,`disabled`])])]),o(`ul`,jt,[(v(!0),B(f,null,A(x.value.subtasks,e=>(v(),B(`li`,{key:e.id,class:j([`wb-sub-item`,{"is-dirty":U.value.has(e.id),"is-running":e.status===`running`,"is-done":e.status===`done`,"is-collapsed":On(e)}])},[On(e)?(v(),B(`div`,Mt,[o(`span`,{class:`wb-sub-item__status`,style:le({background:Dn(e.status)})},P(En(e.status)),5),o(`span`,{class:`wb-sub-item__title-compact`,title:e.title},P(e.title||k(V)(`@WORKBENCH:未命名子任务`)),9,Nt),J(e.id)?(v(),B(`span`,{key:0,class:`wb-sub-item__pid`,title:k(V)(`@WORKBENCH:进程ID`)},` PID: `+P(J(e.id)?.pid),9,Pt)):y(``,!0),o(`button`,{class:`wb-sub-item__toggle`,title:k(V)(`@WORKBENCH:展开`),"aria-label":k(V)(`@WORKBENCH:展开`),onClick:t=>kn(e)},[b(s,null,{default:D(()=>[b(k(de))]),_:1})],8,Ft),o(`button`,{class:`wb-sub-item__undo`,title:k(V)(`@WORKBENCH:取消完成`),"aria-label":k(V)(`@WORKBENCH:取消完成`),onClick:t=>An(e)},P(k(V)(`@WORKBENCH:取消完成`)),9,It),o(`button`,{class:`wb-sub-item__del`,title:k(V)(`@WORKBENCH:删除`),"aria-label":k(V)(`@WORKBENCH:删除`),onClick:t=>bn(e)},`×`,8,Lt)])):(v(),B(f,{key:1},[o(`div`,Rt,[o(`span`,{class:`wb-sub-item__status`,style:le({background:Dn(e.status)})},P(En(e.status)),5),I(o(`input`,{class:`wb-input`,"onUpdate:modelValue":t=>e.title=t,placeholder:k(V)(`@WORKBENCH:子任务标题`),onPaste:t=>$(t,{kind:`sub`,task:x.value,sub:e})},null,40,zt),[[m,e.title]]),U.value.has(e.id)?(v(),B(`span`,{key:0,class:`wb-sub-item__dirty`,title:k(V)(`@WORKBENCH:有未保存的更改`)},P(k(V)(`@WORKBENCH:未保存`)),9,Bt)):y(``,!0),J(e.id)?(v(),B(`span`,{key:1,class:`wb-sub-item__pid`,title:k(V)(`@WORKBENCH:进程ID`)},` PID: `+P(J(e.id)?.pid),9,Vt)):y(``,!0),J(e.id)&&(J(e.id)?.status===`running`||J(e.id)?.status===`pending`)?(v(),B(`button`,{key:2,class:`wb-sub-item__stop`,title:k(V)(`@WORKBENCH:停止执行`),onClick:t=>jn(J(e.id))},P(k(V)(`@WORKBENCH:停止`)),9,Ht)):y(``,!0),e.status===`done`?(v(),B(`button`,{key:3,class:`wb-sub-item__toggle`,title:k(V)(`@WORKBENCH:收起`),"aria-label":k(V)(`@WORKBENCH:收起`),onClick:t=>kn(e)},[b(s,null,{default:D(()=>[b(k(ee))]),_:1})],8,Ut)):y(``,!0),e.status===`done`?(v(),B(`button`,{key:4,class:`wb-sub-item__undo`,title:k(V)(`@WORKBENCH:取消完成`),"aria-label":k(V)(`@WORKBENCH:取消完成`),onClick:t=>An(e)},P(k(V)(`@WORKBENCH:取消完成`)),9,Wt)):y(``,!0),o(`button`,{class:`wb-sub-item__del`,onClick:t=>bn(e)},`×`,8,Gt)]),I(o(`textarea`,{class:`wb-textarea wb-textarea--sm`,"onUpdate:modelValue":t=>e.desc=t,placeholder:k(V)(`@WORKBENCH:子任务描述 / 独立提示词覆盖`),rows:`2`,onPaste:t=>$(t,{kind:`sub`,task:x.value,sub:e})},null,40,Kt),[[m,e.desc]]),J(e.id)?(v(),B(`details`,{key:0,class:`wb-log-details`,open:J(e.id)?.status===`running`||J(e.id)?.status===`pending`},[o(`summary`,Jt,[J(e.id)?.status===`running`?(v(),B(`span`,Yt,`● `+P(k(V)(`@WORKBENCH:正在执行…`)),1)):J(e.id)?.status===`pending`?(v(),B(`span`,Xt,P(k(V)(`@WORKBENCH:排队中…`)),1)):(v(),B(`span`,Zt,P(k(V)(`@WORKBENCH:查看执行日志`)),1)),o(`span`,Qt,P((J(e.id)?.output||``).length)+` `+P(k(V)(`@WORKBENCH:字符`)),1)]),J(e.id)?.prompt?(v(),B(`details`,$t,[o(`summary`,en,[o(`span`,tn,P(k(V)(`@WORKBENCH:用户提示词`)),1),o(`span`,nn,P((J(e.id)?.prompt||``).length)+` `+P(k(V)(`@WORKBENCH:字符`)),1)]),o(`pre`,rn,P(J(e.id)?.prompt),1)])):y(``,!0),J(e.id)?.thinking?(v(),B(`details`,an,[o(`summary`,on,[o(`span`,sn,P(k(V)(`@WORKBENCH:Claude 思考`)),1),o(`span`,cn,P((J(e.id)?.thinking||``).length)+` `+P(k(V)(`@WORKBENCH:字符`)),1)]),o(`pre`,ln,P(J(e.id)?.thinking),1)])):y(``,!0),o(`pre`,{ref_for:!0,ref:Nn(e.id),class:`wb-log-pre`},P(Pn(J(e.id)?.output)||k(V)(`@WORKBENCH:(暂无输出)`)),513)],8,qt)):y(``,!0),b(Be,{attachments:e.attachments||[],"is-image":Yn,"human-size":Xn,"is-uploading":Ln(`sub-`+e.id),"is-paste-hover":Q.value===e.id,"max-count":9,"on-pick":()=>qn({kind:`sub`,task:x.value,sub:e}),"on-remove":t=>Kn({kind:`sub`,task:x.value,sub:e},t),onPaste:t=>$(t,{kind:`sub`,task:x.value,sub:e}),onDrop:M(t=>Wn(t,{kind:`sub`,task:x.value,sub:e}),[`prevent`]),onDragover:M(t=>Q.value=e.id,[`prevent`]),onDragenter:M(t=>Q.value=e.id,[`prevent`]),onDragleave:t=>Q.value=Q.value===e.id?null:Q.value},null,8,[`attachments`,`is-uploading`,`is-paste-hover`,`on-pick`,`on-remove`,`onPaste`,`onDrop`,`onDragover`,`onDragenter`,`onDragleave`])],64))],2))),128)),x.value.subtasks.length===0?(v(),B(`li`,un,[o(`div`,dn,[b(s,null,{default:D(()=>[b(k(u))]),_:1})]),o(`div`,fn,P(k(V)(`@WORKBENCH:拆分任务,逐项执行`)),1),o(`div`,pn,P(k(V)(`@WORKBENCH:手动添加子任务,或让 AI 帮你自动拆分`)),1),o(`div`,mn,[b(c,{type:`primary`,size:`small`,icon:k(F),onClick:ze},{default:D(()=>[a(P(k(V)(`@WORKBENCH:添加子任务`)),1)]),_:1},8,[`icon`]),o(`button`,{type:`button`,class:`wb-empty__link`,disabled:!x.value.title||!x.value.title.trim(),onClick:Cn},P(k(V)(`@WORKBENCH:用 AI 自动拆分`)),9,hn)])])):y(``,!0)])],64)):(v(),B(`div`,St,[o(`p`,null,P(k(V)(`@WORKBENCH:左侧选择任务,或新建一个任务开始`)),1)]))]),b(S,{modelValue:W.visible,"onUpdate:modelValue":r[13]||=e=>W.visible=e,title:W.editing?k(V)(`@WORKBENCH:编辑提示词`):k(V)(`@WORKBENCH:新建提示词`),width:`640px`},{footer:D(()=>[b(c,{onClick:r[12]||=e=>W.visible=!1},{default:D(()=>[a(P(k(V)(`@WORKBENCH:取消`)),1)]),_:1}),b(c,{type:`primary`,onClick:Ne},{default:D(()=>[a(P(k(V)(`@WORKBENCH:保存`)),1)]),_:1})]),default:D(()=>[b(h,{"label-position":`top`},{default:D(()=>[b(p,{label:k(V)(`@WORKBENCH:名称`)},{default:D(()=>[b(l,{modelValue:W.name,"onUpdate:modelValue":r[10]||=e=>W.name=e,placeholder:k(V)(`@WORKBENCH:如:代码审查 / 写测试`)},null,8,[`modelValue`,`placeholder`])]),_:1},8,[`label`]),b(p,null,{default:D(()=>[o(`div`,gn,[b(c,{type:`primary`,plain:``,loading:W.aiLoading,onClick:Ae},{default:D(()=>[a(P(k(V)(`@WORKBENCH:AI 生成项目架构说明`)),1)]),_:1},8,[`loading`]),b(c,{onClick:je},{default:D(()=>[a(P(k(V)(`@WORKBENCH:编辑指令`)),1)]),_:1})])]),_:1}),b(p,{label:k(V)(`@WORKBENCH:内容`)},{default:D(()=>[b(l,{modelValue:W.content,"onUpdate:modelValue":r[11]||=e=>W.content=e,type:`textarea`,rows:10,placeholder:k(V)(`@WORKBENCH:可用变量:`)+`{{task.title}} {{task.desc}} {{sub.title}} {{sub.desc}} {{repo.path}} {{branch}}`},null,8,[`modelValue`,`placeholder`])]),_:1},8,[`label`])]),_:1})]),_:1},8,[`modelValue`,`title`]),b(S,{modelValue:G.visible,"onUpdate:modelValue":r[16]||=e=>G.visible=e,title:k(V)(`@WORKBENCH:编辑生成指令`),width:`720px`,"append-to-body":``},{footer:D(()=>[b(c,{onClick:r[15]||=e=>G.visible=!1},{default:D(()=>[a(P(k(V)(`@WORKBENCH:取消`)),1)]),_:1}),b(c,{type:`primary`,loading:G.saving,onClick:Me},{default:D(()=>[a(P(k(V)(`@WORKBENCH:保存`)),1)]),_:1},8,[`loading`])]),default:D(()=>[b(h,{"label-position":`top`},{default:D(()=>[b(p,{label:k(V)(`@WORKBENCH:指令内容`)},{default:D(()=>[b(l,{modelValue:G.text,"onUpdate:modelValue":r[14]||=e=>G.text=e,type:`textarea`,rows:18,placeholder:k(V)(`@WORKBENCH:指令内容`)},null,8,[`modelValue`,`placeholder`])]),_:1},8,[`label`])]),_:1})]),_:1},8,[`modelValue`,`title`]),b(S,{modelValue:K.visible,"onUpdate:modelValue":r[21]||=e=>K.visible=e,title:K.editing?k(V)(`@WORKBENCH:编辑任务`):k(V)(`@WORKBENCH:新建任务`),width:`560px`},{footer:D(()=>[b(c,{onClick:r[20]||=e=>K.visible=!1},{default:D(()=>[a(P(k(V)(`@WORKBENCH:取消`)),1)]),_:1}),b(c,{type:`primary`,onClick:Ie},{default:D(()=>[a(P(k(V)(`@WORKBENCH:保存`)),1)]),_:1})]),default:D(()=>[b(h,{"label-position":`top`},{default:D(()=>[b(p,{label:k(V)(`@WORKBENCH:标题`)},{default:D(()=>[b(l,{modelValue:K.title,"onUpdate:modelValue":r[17]||=e=>K.title=e},null,8,[`modelValue`])]),_:1},8,[`label`]),b(p,{label:k(V)(`@WORKBENCH:描述`)},{default:D(()=>[b(l,{modelValue:K.desc,"onUpdate:modelValue":r[18]||=e=>K.desc=e,type:`textarea`,rows:3},null,8,[`modelValue`])]),_:1},8,[`label`]),b(p,{label:k(V)(`@WORKBENCH:预置提示词`)},{default:D(()=>[b(w,{modelValue:K.promptId,"onUpdate:modelValue":r[19]||=e=>K.promptId=e,clearable:``,placeholder:k(V)(`@WORKBENCH:不绑定`),style:{width:`100%`}},{default:D(()=>[(v(!0),B(f,null,A(n.value,e=>(v(),d(C,{key:e.id,label:e.name,value:e.id},null,8,[`label`,`value`]))),128))]),_:1},8,[`modelValue`,`placeholder`])]),_:1},8,[`label`])]),_:1})]),_:1},8,[`modelValue`,`title`]),x.value?(v(),d(Ee,{key:0,modelValue:Sn.value,"onUpdate:modelValue":r[22]||=e=>Sn.value=e,title:x.value.title,desc:x.value.desc,"task-id":x.value.id,"prompt-id":x.value.promptId,onConfirm:wn},null,8,[`modelValue`,`title`,`desc`,`task-id`,`prompt-id`])):y(``,!0)])}}}),[[`__scopeId`,`data-v-eb771b47`]]);export{bn as default};
|