zen-gitsync 2.13.6 → 2.13.8
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 +8 -4
- package/package.json +1 -1
- package/scripts/README_COLOR_CONVERTER.md +196 -196
- package/scripts/README_FONTSIZE_CONVERTER.md +278 -278
- package/scripts/README_SPACING_CONVERTER.md +126 -126
- package/scripts/README_STYLE_VARS.md +180 -180
- package/scripts/verify-file-search.mjs +281 -0
- package/src/ui/public/assets/EditorView-CSUfBR5z.js +0 -0
- package/src/ui/public/assets/EditorView-Dvg7sMyU.css +1 -0
- package/src/ui/public/assets/SourceMapView-CPsyB1rP.js +3 -0
- package/src/ui/public/assets/WorkbenchView-BBDJOY3C.js +6 -0
- package/src/ui/public/assets/WorkbenchView-CA91BJc8.css +1 -0
- package/src/ui/public/assets/{_plugin-vue_export-helper-Dw3U5p9d.js → _plugin-vue_export-helper-rFkdzaHd.js} +3 -3
- package/src/ui/public/assets/{css.worker-CvXBzhp8.js → css.worker-Wv5dxAWO.js} +1 -1
- package/src/ui/public/assets/{html.worker-BO6WuOEO.js → html.worker-CQP8QQsS.js} +1 -1
- package/src/ui/public/assets/index-CLVL3Inb.js +66 -0
- package/src/ui/public/assets/index-ChSExFC3.css +1 -0
- package/src/ui/public/assets/{json.worker-BkJRGcCJ.js → json.worker-DzV-CpCQ.js} +1 -1
- package/src/ui/public/assets/{ts.worker-B0J26iPs.js → ts.worker-Dth06zuC.js} +15 -15
- package/src/ui/public/assets/{vendor-C30huq-U.js → vendor-CDAfMQIV.js} +240 -247
- package/src/ui/public/assets/vendor-CIeR0bF6.css +1 -0
- package/src/ui/public/favicon.svg +75 -75
- package/src/ui/public/index.html +23 -23
- package/src/ui/public/logo.svg +74 -74
- package/src/ui/server/routes/workbench.js +299 -160
- package/src/ui/public/assets/EditorView-BLK-Sohi.js +0 -0
- package/src/ui/public/assets/EditorView-DEd8QuPp.css +0 -1
- package/src/ui/public/assets/SourceMapView-CMJnpleg.js +0 -3
- package/src/ui/public/assets/WorkbenchView-CPcGSyVM.css +0 -1
- package/src/ui/public/assets/WorkbenchView-CTB3QNSR.js +0 -6
- package/src/ui/public/assets/index-CV4VteDa.css +0 -1
- package/src/ui/public/assets/index-DyR_trSU.js +0 -66
- package/src/ui/public/assets/vendor-Bq2rS2vY.css +0 -1
- /package/src/ui/public/assets/{editor.worker-Cn2oRESe.js → editor.worker-Bd9IXS8d.js} +0 -0
- /package/src/ui/public/assets/{rolldown-runtime-CMxvf4Kt.js → rolldown-runtime-BM3Ffeng.js} +0 -0
|
@@ -942,187 +942,209 @@ async function runTaskQueue(task, repoPath, branch) {
|
|
|
942
942
|
const priorOutputs = []
|
|
943
943
|
for (const sub of task.subtasks) {
|
|
944
944
|
if (sub.status === 'done') continue;
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
945
|
+
await runSingleSubtask(task, sub, repoPath, branch, priorOutputs)
|
|
946
|
+
}
|
|
947
|
+
// 写回 tasks.json
|
|
948
|
+
await persistTaskAfterRun(task)
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* 执行单个子任务。被 runTaskQueue(整批)和"单 sub 执行"endpoint 共用。
|
|
953
|
+
*
|
|
954
|
+
* @param {object} task 主任务对象
|
|
955
|
+
* @param {object} sub 要跑的子任务
|
|
956
|
+
* @param {string} repoPath 仓库路径
|
|
957
|
+
* @param {string} branch 分支名(可空)
|
|
958
|
+
* @param {Array<{title:string,output:string}>} priorOutputs
|
|
959
|
+
* 前序 done 子任务的输出摘要(in-place 追加)。用于把同一任务下
|
|
960
|
+
* 前面已完成的 sub 产物拼到当前 sub 的 prompt 头部,让 Claude
|
|
961
|
+
* 知道上下文。单独跑一个 sub 时,这个数组里只会有"前面 done 的 sub"。
|
|
962
|
+
*/
|
|
963
|
+
async function runSingleSubtask(task, sub, repoPath, branch, priorOutputs) {
|
|
964
|
+
const MAX_PREV_OUTPUT_CHARS = 2000
|
|
965
|
+
const promptTemplate = sub.promptOverride || (task.promptId
|
|
966
|
+
? (await readJson(PROMPTS_FILE, { prompts: [] })).prompts.find(p => p.id === task.promptId)?.content
|
|
967
|
+
: null) || '';
|
|
968
|
+
const ctx = {
|
|
969
|
+
task: { title: task.title, desc: task.desc || '' },
|
|
970
|
+
sub: { title: sub.title, desc: sub.desc || '' },
|
|
971
|
+
repo: { path: repoPath || '' },
|
|
972
|
+
branch: branch || ''
|
|
973
|
+
};
|
|
974
|
+
const interpolated = interpolate(promptTemplate, ctx);
|
|
975
|
+
const parts = [interpolated, sub.title, sub.desc].filter(s => s && s.trim());
|
|
976
|
+
let prompt = parts.join('\n\n');
|
|
977
|
+
|
|
978
|
+
// ── 前序上下文:把前几个 done 子任务的输出摘要拼到 prompt 头部 ──
|
|
979
|
+
if (priorOutputs && priorOutputs.length > 0) {
|
|
980
|
+
const prevBlock = priorOutputs.map((p, i) => {
|
|
981
|
+
const text = (p.output || '').slice(0, MAX_PREV_OUTPUT_CHARS)
|
|
982
|
+
const truncated = (p.output || '').length > MAX_PREV_OUTPUT_CHARS ? '\n…(前文已截断)' : ''
|
|
983
|
+
return `### [${i + 1}] ${p.title}\n${text}${truncated}`
|
|
984
|
+
}).join('\n\n')
|
|
985
|
+
prompt = `以下是同一任务下已经完成的前序子任务输出(仅作上下文参考,请基于这些结论继续当前子任务,无需重复执行它们):
|
|
966
986
|
|
|
967
987
|
${prevBlock}
|
|
968
988
|
|
|
969
989
|
---
|
|
970
990
|
|
|
971
991
|
${prompt}`
|
|
972
|
-
|
|
992
|
+
}
|
|
973
993
|
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
}
|
|
994
|
+
// ── 附件:合并 sub.attachments + task.attachments 后拼到 prompt 末尾 ──
|
|
995
|
+
// claude -p 字符串模式会扫描 prompt 中出现的本地文件路径并自动
|
|
996
|
+
// 识别为附件(图片 / PDF / 文本均可)。
|
|
997
|
+
// 主任务附件对所有 sub 都可见;子任务自己的附件只对该 sub 可见。
|
|
998
|
+
const allAttachments = [
|
|
999
|
+
...(Array.isArray(task.attachments) ? task.attachments : []),
|
|
1000
|
+
...(Array.isArray(sub.attachments) ? sub.attachments : [])
|
|
1001
|
+
];
|
|
1002
|
+
if (allAttachments.length > 0) {
|
|
1003
|
+
const lines = allAttachments
|
|
1004
|
+
.filter(a => a && a.absolutePath)
|
|
1005
|
+
.map((a, i) => ` ${i + 1}. [${a.mimeType || 'application/octet-stream'}] ${a.absolutePath}`);
|
|
1006
|
+
if (lines.length > 0) {
|
|
1007
|
+
prompt += `\n\n---\n本任务包含 ${lines.length} 个附件(请按文件路径读取,不要让用户重新提供):\n${lines.join('\n')}\n---`;
|
|
989
1008
|
}
|
|
1009
|
+
}
|
|
990
1010
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1011
|
+
const jobId = genId();
|
|
1012
|
+
const job = {
|
|
1013
|
+
id: jobId,
|
|
1014
|
+
taskId: task.id,
|
|
1015
|
+
subId: sub.id,
|
|
1016
|
+
title: `${task.title} / ${sub.title}`,
|
|
1017
|
+
status: 'pending',
|
|
1018
|
+
prompt
|
|
1019
|
+
};
|
|
1020
|
+
jobs.set(jobId, job);
|
|
1021
|
+
sub.status = 'running';
|
|
1022
|
+
publish('sub:update', { taskId: task.id, sub });
|
|
1023
|
+
publish('job:update', job);
|
|
1024
|
+
|
|
1025
|
+
try {
|
|
1026
|
+
const { pid, child } = await launchClaudeInNewWindow(repoPath || process.cwd(), prompt);
|
|
1027
|
+
job.pid = pid;
|
|
1028
|
+
// 保存 child 引用,供 cancel 接口调用 kill
|
|
1029
|
+
job.child = child;
|
|
1030
|
+
job.startedAt = nowIso();
|
|
1031
|
+
job.status = 'running';
|
|
1003
1032
|
publish('job:update', job);
|
|
1004
1033
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1034
|
+
// 流式 NDJSON 解析:把 stdout 当作 stream-json 协议处理
|
|
1035
|
+
// assistant.text → job.output (用户主要关心的内容)
|
|
1036
|
+
// assistant.thinking → job.thinking (折叠展示,让用户知道 Claude 在想)
|
|
1037
|
+
// 其他事件(init / tool_use / result 等)忽略,避免噪声
|
|
1038
|
+
// 解析失败的行原样进 output,便于排查协议异常。
|
|
1039
|
+
// thinking 几乎不做服务端截断:Claude reasoning 一般在 KB~几十 MB 之间,
|
|
1040
|
+
// 100MB 兜底只是为了防止内存爆炸。流式 publish 时改推增量 delta(仅新拼接的
|
|
1041
|
+
// 那部分),终态或重连时才随 job:update 全量同步,避免每帧重复广播整个累积文本。
|
|
1042
|
+
const MAX_OUTPUT = 100 * 1024 * 1024;
|
|
1043
|
+
const MAX_THINKING = 100 * 1024 * 1024;
|
|
1044
|
+
job.output = '';
|
|
1045
|
+
job.thinking = '';
|
|
1046
|
+
const lineBuf = { stdout: '', stderr: '' };
|
|
1047
|
+
|
|
1048
|
+
const parseLines = (channel, buf) => {
|
|
1049
|
+
const chunk = buf.toString('utf8');
|
|
1050
|
+
lineBuf[channel] += chunk;
|
|
1051
|
+
const lines = lineBuf[channel].split('\n');
|
|
1052
|
+
lineBuf[channel] = lines.pop() ?? ''; // 最后一段可能不完整,留给下次
|
|
1053
|
+
let pendingThinkingDelta = '';
|
|
1054
|
+
for (const line of lines) {
|
|
1055
|
+
const trimmed = line.trim();
|
|
1056
|
+
if (!trimmed) continue;
|
|
1057
|
+
if (channel === 'stderr' || !trimmed.startsWith('{')) {
|
|
1058
|
+
// 非 stream-json 行:原样塞进 output(兼容老版本 claude / 错误信息)
|
|
1059
|
+
const prevLen = job.output.length;
|
|
1060
|
+
job.output = (job.output + trimmed + '\n').slice(-MAX_OUTPUT);
|
|
1061
|
+
// output 也用 delta 推送,前端按"以 length 为锚追加"语义合并
|
|
1062
|
+
const delta = job.output.slice(prevLen);
|
|
1063
|
+
if (delta) publish('job:output-delta', { id: job.id, delta });
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
let evt;
|
|
1067
|
+
try { evt = JSON.parse(trimmed) } catch { continue }
|
|
1068
|
+
if (evt.type !== 'assistant') continue;
|
|
1069
|
+
const blocks = evt.message?.content;
|
|
1070
|
+
if (!Array.isArray(blocks)) continue;
|
|
1071
|
+
for (const b of blocks) {
|
|
1072
|
+
if (b.type === 'text' && typeof b.text === 'string') {
|
|
1039
1073
|
const prevLen = job.output.length;
|
|
1040
|
-
job.output = (job.output +
|
|
1041
|
-
// output 也用 delta 推送,前端按"以 length 为锚追加"语义合并
|
|
1074
|
+
job.output = (job.output + b.text).slice(-MAX_OUTPUT);
|
|
1042
1075
|
const delta = job.output.slice(prevLen);
|
|
1043
1076
|
if (delta) publish('job:output-delta', { id: job.id, delta });
|
|
1044
|
-
|
|
1077
|
+
} else if (b.type === 'thinking' && typeof b.thinking === 'string') {
|
|
1078
|
+
const prevLen = job.thinking.length;
|
|
1079
|
+
job.thinking = (job.thinking + b.thinking).slice(-MAX_THINKING);
|
|
1080
|
+
const delta = job.thinking.slice(prevLen);
|
|
1081
|
+
if (delta) pendingThinkingDelta += delta;
|
|
1045
1082
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
// 一批 NDJSON 处理完后统一发一次 thinking delta,避免高频小块 socket 占用
|
|
1086
|
+
if (pendingThinkingDelta) {
|
|
1087
|
+
publish('job:thinking-delta', { id: job.id, delta: pendingThinkingDelta });
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
if (child.stdout) child.stdout.on('data', (buf) => parseLines('stdout', buf));
|
|
1091
|
+
if (child.stderr) child.stderr.on('data', (buf) => parseLines('stderr', buf));
|
|
1092
|
+
|
|
1093
|
+
// 等待进程退出(detached 不阻塞主进程,用 polling /proc 兜底)
|
|
1094
|
+
await waitProcessExit(pid);
|
|
1095
|
+
const wasCancelled = cancelledJobs.has(jobId)
|
|
1096
|
+
if (wasCancelled) cancelledJobs.delete(jobId)
|
|
1097
|
+
// 进程退出时 stdout 可能残留最后一段未换行的 NDJSON,flush 一次
|
|
1098
|
+
// flush 内部也按 delta 推送,保持与流式阶段一致
|
|
1099
|
+
if (lineBuf.stdout.trim()) {
|
|
1100
|
+
const outPrev = job.output.length;
|
|
1101
|
+
const thinkPrev = job.thinking.length;
|
|
1102
|
+
try {
|
|
1103
|
+
const evt = JSON.parse(lineBuf.stdout.trim())
|
|
1104
|
+
if (evt.type === 'assistant' && Array.isArray(evt.message?.content)) {
|
|
1105
|
+
for (const b of evt.message.content) {
|
|
1052
1106
|
if (b.type === 'text' && typeof b.text === 'string') {
|
|
1053
|
-
|
|
1054
|
-
job.output = (job.output + b.text).slice(-MAX_OUTPUT);
|
|
1055
|
-
const delta = job.output.slice(prevLen);
|
|
1056
|
-
if (delta) publish('job:output-delta', { id: job.id, delta });
|
|
1107
|
+
job.output = (job.output + b.text).slice(-MAX_OUTPUT)
|
|
1057
1108
|
} else if (b.type === 'thinking' && typeof b.thinking === 'string') {
|
|
1058
|
-
|
|
1059
|
-
job.thinking = (job.thinking + b.thinking).slice(-MAX_THINKING);
|
|
1060
|
-
const delta = job.thinking.slice(prevLen);
|
|
1061
|
-
if (delta) pendingThinkingDelta += delta;
|
|
1109
|
+
job.thinking = (job.thinking + b.thinking).slice(-MAX_THINKING)
|
|
1062
1110
|
}
|
|
1063
1111
|
}
|
|
1064
1112
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
};
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
const evt = JSON.parse(lineBuf.stdout.trim())
|
|
1084
|
-
if (evt.type === 'assistant' && Array.isArray(evt.message?.content)) {
|
|
1085
|
-
for (const b of evt.message.content) {
|
|
1086
|
-
if (b.type === 'text' && typeof b.text === 'string') {
|
|
1087
|
-
job.output = (job.output + b.text).slice(-MAX_OUTPUT)
|
|
1088
|
-
} else if (b.type === 'thinking' && typeof b.thinking === 'string') {
|
|
1089
|
-
job.thinking = (job.thinking + b.thinking).slice(-MAX_THINKING)
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
} catch { /* 不是 JSON,忽略 */ }
|
|
1094
|
-
const outDelta = job.output.slice(outPrev);
|
|
1095
|
-
if (outDelta) publish('job:output-delta', { id: job.id, delta: outDelta });
|
|
1096
|
-
const thinkDelta = job.thinking.slice(thinkPrev);
|
|
1097
|
-
if (thinkDelta) publish('job:thinking-delta', { id: job.id, delta: thinkDelta });
|
|
1098
|
-
}
|
|
1099
|
-
job.endedAt = nowIso();
|
|
1100
|
-
if (wasCancelled) {
|
|
1101
|
-
job.exitCode = 130; // 128 + SIGINT(2),约定俗成的"用户取消"退出码
|
|
1102
|
-
job.status = 'cancelled';
|
|
1103
|
-
job.error = '用户已停止执行';
|
|
1104
|
-
// sub 不改状态——cancelled 是 job 维度,同 task 后续 sub 仍可继续执行
|
|
1105
|
-
} else {
|
|
1106
|
-
job.exitCode = 0;
|
|
1107
|
-
job.status = 'done';
|
|
1108
|
-
sub.status = 'done';
|
|
1109
|
-
// 把这个 sub 的输出累积到前序上下文,喂给下一个 sub
|
|
1110
|
-
priorOutputs.push({ title: sub.title, output: job.output || '' })
|
|
1111
|
-
}
|
|
1112
|
-
} catch (err) {
|
|
1113
|
-
job.error = err && err.message ? err.message : String(err);
|
|
1114
|
-
job.status = 'error';
|
|
1115
|
-
sub.status = 'error';
|
|
1116
|
-
} finally {
|
|
1117
|
-
// 移除 child 引用——避免后续被 SSE 序列化到前端
|
|
1118
|
-
delete job.child
|
|
1119
|
-
publish('job:update', job);
|
|
1120
|
-
publish('sub:update', { taskId: task.id, sub });
|
|
1121
|
-
// 终态:fire-and-forget 同步落盘,确保 done/cancelled/error 都立即归档
|
|
1122
|
-
flushJobsSaveNow().catch(err => console.warn('[workbench] jobs save failed:', err.message))
|
|
1113
|
+
} catch { /* 不是 JSON,忽略 */ }
|
|
1114
|
+
const outDelta = job.output.slice(outPrev);
|
|
1115
|
+
if (outDelta) publish('job:output-delta', { id: job.id, delta: outDelta });
|
|
1116
|
+
const thinkDelta = job.thinking.slice(thinkPrev);
|
|
1117
|
+
if (thinkDelta) publish('job:thinking-delta', { id: job.id, delta: thinkDelta });
|
|
1118
|
+
}
|
|
1119
|
+
job.endedAt = nowIso();
|
|
1120
|
+
if (wasCancelled) {
|
|
1121
|
+
job.exitCode = 130; // 128 + SIGINT(2),约定俗成的"用户取消"退出码
|
|
1122
|
+
job.status = 'cancelled';
|
|
1123
|
+
job.error = '用户已停止执行';
|
|
1124
|
+
// sub 不改状态——cancelled 是 job 维度,同 task 后续 sub 仍可继续执行
|
|
1125
|
+
} else {
|
|
1126
|
+
job.exitCode = 0;
|
|
1127
|
+
job.status = 'done';
|
|
1128
|
+
sub.status = 'done';
|
|
1129
|
+
// 把这个 sub 的输出累积到前序上下文,喂给下一个 sub
|
|
1130
|
+
if (priorOutputs) priorOutputs.push({ title: sub.title, output: job.output || '' })
|
|
1123
1131
|
}
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
job.error = err && err.message ? err.message : String(err);
|
|
1134
|
+
job.status = 'error';
|
|
1135
|
+
sub.status = 'error';
|
|
1136
|
+
} finally {
|
|
1137
|
+
// 移除 child 引用——避免后续被 SSE 序列化到前端
|
|
1138
|
+
delete job.child
|
|
1139
|
+
publish('job:update', job);
|
|
1140
|
+
publish('sub:update', { taskId: task.id, sub });
|
|
1141
|
+
// 终态:fire-and-forget 同步落盘,确保 done/cancelled/error 都立即归档
|
|
1142
|
+
flushJobsSaveNow().catch(err => console.warn('[workbench] jobs save failed:', err.message))
|
|
1124
1143
|
}
|
|
1125
|
-
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/** 把 task.subtasks 写回 tasks.json,并广播 task:update。runTaskQueue 和"单 sub 执行"共用。 */
|
|
1147
|
+
async function persistTaskAfterRun(task) {
|
|
1126
1148
|
const data = await readJson(TASKS_FILE, { tasks: [] });
|
|
1127
1149
|
const t = data.tasks.find(x => x.id === task.id);
|
|
1128
1150
|
if (t) {
|
|
@@ -1133,6 +1155,31 @@ ${prompt}`
|
|
|
1133
1155
|
}
|
|
1134
1156
|
}
|
|
1135
1157
|
|
|
1158
|
+
/**
|
|
1159
|
+
* 构建单 sub 执行时的 priorOutputs:把同一 task 下"排在当前 sub 之前"且已 done 的
|
|
1160
|
+
* 子任务输出摘要收集起来。这样单独跑一个 sub 时,它也能拿到前序上下文。
|
|
1161
|
+
*/
|
|
1162
|
+
async function collectPriorOutputs(task, targetSub) {
|
|
1163
|
+
const MAX_PREV_OUTPUT_CHARS = 2000
|
|
1164
|
+
const prior = []
|
|
1165
|
+
const targetIdx = task.subtasks.findIndex(s => s.id === targetSub.id)
|
|
1166
|
+
if (targetIdx < 0) return prior
|
|
1167
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
1168
|
+
const s = task.subtasks[i]
|
|
1169
|
+
if (s.status !== 'done') continue
|
|
1170
|
+
// 从 jobs 列表里找最近一个属于这个 sub 且 status=done 的 job,
|
|
1171
|
+
// 取其 output 作为"前序上下文摘要"
|
|
1172
|
+
const job = snapshotJobs()
|
|
1173
|
+
.filter(j => j.subId === s.id && j.status === 'done')
|
|
1174
|
+
.sort((a, b) => (b.endedAt || '').localeCompare(a.endedAt || ''))[0]
|
|
1175
|
+
if (!job) continue
|
|
1176
|
+
const text = (job.output || '').slice(0, MAX_PREV_OUTPUT_CHARS)
|
|
1177
|
+
const truncated = (job.output || '').length > MAX_PREV_OUTPUT_CHARS ? '\n…(前文已截断)' : ''
|
|
1178
|
+
prior.push({ title: s.title, output: text + truncated })
|
|
1179
|
+
}
|
|
1180
|
+
return prior
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1136
1183
|
function waitProcessExit(pid) {
|
|
1137
1184
|
return new Promise(resolve => {
|
|
1138
1185
|
let exited = false;
|
|
@@ -1694,8 +1741,11 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
|
|
|
1694
1741
|
|
|
1695
1742
|
app.post('/api/workbench/tasks', async (req, res) => {
|
|
1696
1743
|
try {
|
|
1697
|
-
const { id, title, desc, promptId, subtasks } = req.body || {};
|
|
1744
|
+
const { id, title, desc, promptId, subtasks, type: rawType, simpleOverride } = req.body || {};
|
|
1698
1745
|
if (!title) return res.status(400).json({ success: false, error: 'title 必填' });
|
|
1746
|
+
// type 归一化:仅接受 'simple' | 'complex',缺省/未知一律按 complex
|
|
1747
|
+
const taskType = rawType === 'simple' ? 'simple' : 'complex';
|
|
1748
|
+
const safeOverride = typeof simpleOverride === 'string' ? simpleOverride.slice(0, 8000) : '';
|
|
1699
1749
|
const data = await readJson(TASKS_FILE, { tasks: [] });
|
|
1700
1750
|
const tasks = data.tasks || [];
|
|
1701
1751
|
const now = nowIso();
|
|
@@ -1709,6 +1759,8 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
|
|
|
1709
1759
|
title,
|
|
1710
1760
|
desc: desc || '',
|
|
1711
1761
|
promptId: promptId || null,
|
|
1762
|
+
type: taskType,
|
|
1763
|
+
simpleOverride: taskType === 'simple' ? safeOverride : '',
|
|
1712
1764
|
subtasks: Array.isArray(subtasks) ? subtasks.map(s => ({
|
|
1713
1765
|
id: s.id || genId(),
|
|
1714
1766
|
title: s.title || '',
|
|
@@ -1737,6 +1789,8 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
|
|
|
1737
1789
|
title,
|
|
1738
1790
|
desc: desc || '',
|
|
1739
1791
|
promptId: promptId || null,
|
|
1792
|
+
type: taskType,
|
|
1793
|
+
simpleOverride: taskType === 'simple' ? safeOverride : '',
|
|
1740
1794
|
projectPath: currentProjectPath || '',
|
|
1741
1795
|
subtasks: Array.isArray(subtasks) ? subtasks.map(s => ({
|
|
1742
1796
|
id: s.id || genId(),
|
|
@@ -1789,6 +1843,91 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
|
|
|
1789
1843
|
}
|
|
1790
1844
|
});
|
|
1791
1845
|
|
|
1846
|
+
// ── 执行简单任务(无子任务直接跑) ──────────────────────────────────
|
|
1847
|
+
// POST /api/workbench/tasks/:id/run-simple
|
|
1848
|
+
// 行为:
|
|
1849
|
+
// - 适用于 type==='simple' 的任务,无需拆分子任务
|
|
1850
|
+
// - 在内存里合成一个虚拟 sub{title=task.title, desc=task.desc,
|
|
1851
|
+
// promptOverride=task.simpleOverride, status='todo'},
|
|
1852
|
+
// 复用 runSingleSubtask(同一套 prompt 拼装、附件、job 跟踪、取消、落盘)
|
|
1853
|
+
// - 不会修改 task.subtasks 持久化结构
|
|
1854
|
+
app.post('/api/workbench/tasks/:id/run-simple', async (req, res) => {
|
|
1855
|
+
try {
|
|
1856
|
+
const data = await readJson(TASKS_FILE, { tasks: [] });
|
|
1857
|
+
const task = (data.tasks || []).find(t => t.id === req.params.id);
|
|
1858
|
+
if (!task) return res.status(404).json({ success: false, error: '任务不存在' });
|
|
1859
|
+
if (task.type !== 'simple') {
|
|
1860
|
+
return res.status(400).json({ success: false, error: '该任务不是简单任务,请使用普通执行接口' });
|
|
1861
|
+
}
|
|
1862
|
+
const repoPath = typeof getCurrentProjectPath === 'function' ? getCurrentProjectPath() : '';
|
|
1863
|
+
// 虚拟 sub:不写回 tasks.json,只用一次
|
|
1864
|
+
const virtualSub = {
|
|
1865
|
+
id: `${task.id}__simple`,
|
|
1866
|
+
title: task.title,
|
|
1867
|
+
desc: task.desc || '',
|
|
1868
|
+
status: 'todo',
|
|
1869
|
+
promptOverride: task.simpleOverride || '',
|
|
1870
|
+
attachments: Array.isArray(task.attachments) ? task.attachments : []
|
|
1871
|
+
};
|
|
1872
|
+
// 简单任务本身没有 subtasks 列表,直接调 runSingleSubtask(不再走 runTaskQueue 循环)
|
|
1873
|
+
res.json({ success: true, message: '已开始执行简单任务' });
|
|
1874
|
+
runSingleSubtask(task, virtualSub, repoPath, '', []).catch(err => {
|
|
1875
|
+
publish('task:error', { taskId: task.id, error: err.message });
|
|
1876
|
+
});
|
|
1877
|
+
} catch (err) {
|
|
1878
|
+
res.status(500).json({ success: false, error: err.message });
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
|
|
1882
|
+
// ── 执行单个子任务 ────────────────────────────────────────────────────
|
|
1883
|
+
// POST /api/workbench/subtasks/:id/run
|
|
1884
|
+
// 行为:
|
|
1885
|
+
// - 仅执行指定 id 的这一个 sub,不再遍历 task 下其他 sub
|
|
1886
|
+
// - 拼 prompt 时会把"前面已 done 的 sub"输出摘要作为前序上下文塞进去,
|
|
1887
|
+
// 跟整批执行的语义保持一致
|
|
1888
|
+
// - 整批"执行任务"队列和单 sub 执行共用 runSingleSubtask,所以日志/状态
|
|
1889
|
+
// /取消/落盘逻辑完全一致
|
|
1890
|
+
// 限制:
|
|
1891
|
+
// - 该 sub 处于 running/pending 时拒绝(并发跑同一个 sub 会出现状态混乱)
|
|
1892
|
+
app.post('/api/workbench/subtasks/:id/run', async (req, res) => {
|
|
1893
|
+
try {
|
|
1894
|
+
const data = await readJson(TASKS_FILE, { tasks: [] });
|
|
1895
|
+
const subId = req.params.id;
|
|
1896
|
+
let foundTask = null;
|
|
1897
|
+
let foundSub = null;
|
|
1898
|
+
for (const t of (data.tasks || [])) {
|
|
1899
|
+
if (!Array.isArray(t.subtasks)) continue;
|
|
1900
|
+
const s = t.subtasks.find(x => x.id === subId);
|
|
1901
|
+
if (s) { foundTask = t; foundSub = s; break; }
|
|
1902
|
+
}
|
|
1903
|
+
if (!foundTask || !foundSub) {
|
|
1904
|
+
return res.status(404).json({ success: false, error: '子任务不存在' });
|
|
1905
|
+
}
|
|
1906
|
+
if (foundSub.status === 'running') {
|
|
1907
|
+
return res.status(400).json({ success: false, error: '该子任务正在执行中' });
|
|
1908
|
+
}
|
|
1909
|
+
// 兜底:即便磁盘状态是 todo,如果磁盘里还没归档的 job 还在跑(进程被孤儿),也拦一下
|
|
1910
|
+
const liveJob = snapshotJobs().find(j => j.subId === subId && (j.status === 'running' || j.status === 'pending'));
|
|
1911
|
+
if (liveJob) {
|
|
1912
|
+
return res.status(400).json({ success: false, error: '该子任务已有正在执行的 job' });
|
|
1913
|
+
}
|
|
1914
|
+
const repoPath = typeof getCurrentProjectPath === 'function' ? getCurrentProjectPath() : '';
|
|
1915
|
+
// 异步执行,立即返回
|
|
1916
|
+
res.json({ success: true, message: `已开始执行子任务:${foundSub.title || subId}` });
|
|
1917
|
+
(async () => {
|
|
1918
|
+
try {
|
|
1919
|
+
const priorOutputs = await collectPriorOutputs(foundTask, foundSub);
|
|
1920
|
+
await runSingleSubtask(foundTask, foundSub, repoPath, '', priorOutputs);
|
|
1921
|
+
await persistTaskAfterRun(foundTask);
|
|
1922
|
+
} catch (err) {
|
|
1923
|
+
publish('task:error', { taskId: foundTask.id, error: err.message });
|
|
1924
|
+
}
|
|
1925
|
+
})();
|
|
1926
|
+
} catch (err) {
|
|
1927
|
+
res.status(500).json({ success: false, error: err.message });
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1930
|
+
|
|
1792
1931
|
// ── 进程状态查询(兜底,SSE 断了也能拉) ────────────────────────────
|
|
1793
1932
|
app.get('/api/workbench/jobs', (_req, res) => {
|
|
1794
1933
|
res.json({ success: true, jobs: snapshotJobs() });
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.md-preview[data-v-d602e5a5]{color:inherit;word-break:break-word;padding:20px 24px;font-size:14px;line-height:1.6}.md-preview[data-v-d602e5a5] h1,.md-preview[data-v-d602e5a5] h2,.md-preview[data-v-d602e5a5] h3,.md-preview[data-v-d602e5a5] h4,.md-preview[data-v-d602e5a5] h5,.md-preview[data-v-d602e5a5] h6{border-bottom:1px solid var(--border-color,#d0d7de);margin-top:24px;margin-bottom:16px;padding-bottom:.3em;font-weight:600}.md-preview[data-v-d602e5a5] h1{font-size:1.8em}.md-preview[data-v-d602e5a5] h2{font-size:1.4em}.md-preview[data-v-d602e5a5] h3{font-size:1.2em}.md-preview[data-v-d602e5a5] p{margin:0 0 14px}.md-preview[data-v-d602e5a5] a{color:var(--link-color,#0969da);text-decoration:none}.md-preview[data-v-d602e5a5] a:hover{text-decoration:underline}.md-preview[data-v-d602e5a5] code{background:var(--code-bg,#f6f8fa);border-radius:4px;padding:2px 5px;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:87%}.md-preview[data-v-d602e5a5] pre{background:var(--code-bg,#f6f8fa);border-radius:6px;margin:0 0 16px;padding:14px 16px;overflow:auto}.md-preview[data-v-d602e5a5] pre code{background:0 0;padding:0;font-size:100%}.md-preview[data-v-d602e5a5] blockquote{border-left:3px solid var(--border-color,#d0d7de);color:var(--text-secondary,#656d76);margin:0 0 16px;padding:0 16px}.md-preview[data-v-d602e5a5] img{border-radius:4px;max-width:100%}.md-preview[data-v-d602e5a5] hr{border:none;border-top:1px solid var(--border-color,#d0d7de);margin:24px 0}.md-preview[data-v-d602e5a5] table{border-collapse:collapse;width:100%;margin-bottom:16px}.md-preview[data-v-d602e5a5] th,.md-preview[data-v-d602e5a5] td{border:1px solid var(--border-color,#d0d7de);padding:6px 13px}.md-preview[data-v-d602e5a5] thead tr{background:var(--code-bg,#f6f8fa)}.md-preview[data-v-d602e5a5] ul,.md-preview[data-v-d602e5a5] ol{margin-bottom:16px;padding-left:2em}.md-preview[data-v-d602e5a5] li{margin:4px 0}.md-segment[data-v-d602e5a5]{display:block}.md-mindmap[data-v-d602e5a5]{border:1px solid var(--border-color,#d0d7de);background:var(--bg-panel,#fff);border-radius:8px;margin:18px 0 28px;overflow:hidden}.md-mindmap-title[data-v-d602e5a5]{color:var(--text-secondary,#656d76);background:var(--code-bg,#f6f8fa);border-bottom:1px solid var(--border-color,#d0d7de);letter-spacing:.5px;padding:8px 12px;font-size:12px;font-weight:600}.md-mindmap-canvas[data-v-d602e5a5]{width:100%;height:480px;display:block}.mindmap-preview[data-v-5988cf1a]{background:var(--bg-container,#fff);flex-direction:column;width:100%;height:100%;display:flex}.mindmap-preview-header[data-v-5988cf1a]{border-bottom:1px solid var(--border-color,#d0d7de);background:var(--bg-panel,#f6f8fa);align-items:center;gap:12px;padding:8px 14px;font-size:12px;display:flex}.mindmap-preview-title[data-v-5988cf1a]{color:var(--text-primary,#1f2328);letter-spacing:.5px;font-weight:600}.mindmap-preview-hint[data-v-5988cf1a]{color:var(--text-secondary,#656d76);font-size:11px}.mindmap-preview-canvas[data-v-5988cf1a]{flex:1;width:100%;min-height:0}.mindmap-preview-canvas[data-v-5988cf1a] .zm-mindmap,.mindmap-preview-canvas[data-v-5988cf1a]>*{width:100%;height:100%}.editor-view[data-v-165ffaa9]{background:var(--bg-container);border:1px solid var(--border-color);width:100%;height:100%;box-shadow:var(--shadow-sm);border-radius:0;display:flex;overflow:hidden}.editor-sidebar[data-v-165ffaa9]{border-right:1px solid var(--border-color);background:var(--bg-panel);flex-direction:column;flex-shrink:0;display:flex;overflow:hidden}.sidebar-header[data-v-165ffaa9]{border-bottom:1px solid var(--border-color);flex-shrink:0;justify-content:space-between;align-items:center;padding:8px 10px 6px;display:flex}.sidebar-title[data-v-165ffaa9]{letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary);-webkit-user-select:none;user-select:none;font-size:11px;font-weight:700}.sidebar-action-btn[data-v-165ffaa9]{cursor:pointer;color:var(--text-tertiary);border-radius:var(--radius-base);background:0 0;border:none;align-items:center;padding:3px;display:flex}.sidebar-action-btn[data-v-165ffaa9]:hover{color:var(--text-primary);background:var(--bg-hover)}.sidebar-tree[data-v-165ffaa9]{flex:1;padding:4px 0;overflow:hidden auto}.tree-node[data-v-165ffaa9]{cursor:pointer;-webkit-user-select:none;user-select:none;height:24px;color:var(--text-primary);white-space:nowrap;border-radius:4px;align-items:center;gap:4px;padding-right:8px;font-size:13px;transition:background .1s;display:flex;overflow:hidden}.tree-node[data-v-165ffaa9]:hover{background:var(--bg-hover)}.tree-node--active[data-v-165ffaa9]{color:var(--color-primary);background:#3b82f61f}.tree-node--selected[data-v-165ffaa9]{outline-offset:-1px;background:#63b3ed26;outline:1px solid #63b3ed59}.tree-arrow[data-v-165ffaa9]{color:var(--text-tertiary);flex-shrink:0;align-items:center;width:12px;transition:transform .15s;display:flex}.tree-arrow.expanded[data-v-165ffaa9]{transform:rotate(90deg)}.tree-arrow-spacer[data-v-165ffaa9]{flex-shrink:0;width:12px}.tree-icon[data-v-165ffaa9]{flex-shrink:0;align-items:center;font-size:14px;line-height:1;display:flex}.tree-icon.mit-icon[data-v-165ffaa9]{fill:currentColor;vertical-align:middle;width:14px;height:14px;display:inline-block}.tree-icon--dir[data-v-165ffaa9]{color:#e8b84b}.tree-name[data-v-165ffaa9]{text-overflow:ellipsis;flex:1;min-width:0;font-size:13px;overflow:hidden}.tree-loading[data-v-165ffaa9]{border:1.5px solid var(--color-primary);border-top-color:#0000;border-radius:50%;flex-shrink:0;width:10px;height:10px;animation:.8s linear infinite spin-165ffaa9;display:inline-block}.tree-empty[data-v-165ffaa9]{color:var(--text-tertiary);text-align:center;padding:24px 12px;font-size:12px}.sidebar-loading[data-v-165ffaa9]{flex:1;justify-content:center;align-items:center;display:flex}.spin-icon[data-v-165ffaa9]{border:2px solid var(--color-primary);border-top-color:#0000;border-radius:50%;width:20px;height:20px;animation:.8s linear infinite spin-165ffaa9;display:inline-block}@keyframes spin-165ffaa9{to{transform:rotate(360deg)}}.editor-resizer[data-v-165ffaa9]{cursor:col-resize;z-index:2;background:0 0;flex-shrink:0;width:6px;transition:background .15s;position:relative}.editor-resizer[data-v-165ffaa9]:after{content:"";background:var(--color-gray-300);border-radius:2px;width:2px;height:32px;transition:background .15s,height .15s;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.editor-resizer[data-v-165ffaa9]:hover{background:#3b82f60f}.editor-resizer[data-v-165ffaa9]:hover:after{background:var(--color-primary);height:48px;box-shadow:0 0 8px #3b82f666}.editor-main[data-v-165ffaa9]{flex-direction:column;flex:1;display:flex;overflow:hidden}.editor-tabs[data-v-165ffaa9]{border-bottom:1px solid var(--border-color);background:var(--bg-panel);scrollbar-width:none;flex-shrink:0;display:flex;overflow:auto hidden}.editor-tabs[data-v-165ffaa9]::-webkit-scrollbar{display:none}.editor-tab[data-v-165ffaa9]{border-right:1px solid var(--border-color);cursor:pointer;height:34px;color:var(--text-secondary);-webkit-user-select:none;user-select:none;flex-shrink:0;align-items:center;gap:6px;min-width:80px;max-width:200px;padding:0 12px;font-size:12.5px;transition:background .1s,color .1s;display:flex}.editor-tab[data-v-165ffaa9]:hover{background:var(--bg-hover);color:var(--text-primary)}.editor-tab.active[data-v-165ffaa9]{background:var(--bg-container);color:var(--text-primary);border-bottom:2px solid var(--color-primary)}.tab-name[data-v-165ffaa9]{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.tab-dirty-dot[data-v-165ffaa9]{background:var(--color-warning);border-radius:50%;flex-shrink:0;width:6px;height:6px}.tab-close[data-v-165ffaa9]{cursor:pointer;color:var(--text-tertiary);opacity:0;background:0 0;border:none;border-radius:3px;flex-shrink:0;align-items:center;padding:2px;transition:opacity .1s,background .1s;display:flex}.editor-tab:hover .tab-close[data-v-165ffaa9],.editor-tab.active .tab-close[data-v-165ffaa9]{opacity:1}.tab-close[data-v-165ffaa9]:hover{background:var(--bg-hover);color:var(--color-danger)}.editor-empty[data-v-165ffaa9]{color:var(--text-tertiary);-webkit-user-select:none;user-select:none;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:10px;font-size:13px;display:flex}.editor-empty p[data-v-165ffaa9]{margin:0}.editor-empty-hint[data-v-165ffaa9]{opacity:.6;font-size:11px}.monaco-container[data-v-165ffaa9]{flex:1;min-width:0;overflow:hidden}.monaco-container.hidden[data-v-165ffaa9]{display:none}.image-tab-placeholder[data-v-165ffaa9]{justify-content:center;align-items:center;gap:var(--spacing-sm);color:var(--text-secondary);background:var(--bg-container);text-align:center;padding:var(--spacing-lg);flex-direction:column;flex:1;display:flex}.image-tab-placeholder-title[data-v-165ffaa9]{font-size:var(--font-size-md);font-weight:var(--font-weight-medium);color:var(--text-primary);word-break:break-all;margin:0}.image-tab-placeholder-hint[data-v-165ffaa9]{font-size:var(--font-size-sm);color:var(--text-tertiary);margin:0}.editor-body[data-v-165ffaa9]{flex:1;min-height:0;display:flex;overflow:hidden}.preview-resizer[data-v-165ffaa9]{cursor:col-resize;z-index:2;background:0 0;flex-shrink:0;order:99;width:6px;margin-left:auto;transition:background .15s;position:relative}.preview-resizer[data-v-165ffaa9]:after{content:"";background:var(--color-gray-300);border-radius:2px;width:2px;height:32px;transition:background .15s,height .15s;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.preview-resizer[data-v-165ffaa9]:hover{background:#3b82f60f}.preview-resizer[data-v-165ffaa9]:hover:after{background:var(--color-primary);height:48px;box-shadow:0 0 8px #3b82f666}.preview-panel[data-v-165ffaa9]{border-left:1px solid var(--border-color);background:var(--bg-panel);flex-direction:column;flex:1 1 0;min-width:200px;display:flex;overflow:hidden}.preview-header[data-v-165ffaa9]{border-bottom:1px solid var(--border-color);background:var(--bg-panel);flex-shrink:0;align-items:center;gap:6px;height:34px;padding:0 10px;display:flex}.preview-title[data-v-165ffaa9]{letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary);-webkit-user-select:none;user-select:none;font-size:11px;font-weight:700}.preview-ext-badge[data-v-165ffaa9]{color:var(--color-primary);letter-spacing:.04em;background:#3b82f626;border-radius:3px;padding:1px 5px;font-size:10px;font-weight:600}.preview-header-spacer[data-v-165ffaa9]{flex:1}.preview-close-btn[data-v-165ffaa9]{cursor:pointer;color:var(--text-tertiary);border-radius:var(--radius-base);background:0 0;border:none;align-items:center;padding:3px;transition:color .1s,background .1s;display:flex}.preview-close-btn[data-v-165ffaa9]:hover{color:var(--color-danger);background:var(--bg-hover)}.preview-body[data-v-165ffaa9]{flex-direction:column;flex:1;display:flex;overflow:hidden}.preview-iframe[data-v-165ffaa9]{background:0 0;border:none;flex:1;width:100%;height:100%}.preview-markdown[data-v-165ffaa9]{background:var(--bg-container,#fff);width:100%;height:100%;color:var(--text-primary,#1f2328);flex:1;overflow:auto}.preview-mindmap[data-v-165ffaa9]{background:var(--bg-container,#fff);flex-direction:column;flex:1;width:100%;height:100%;display:flex;overflow:hidden}.mindmap-toggle-btn.disabled[data-v-165ffaa9]{opacity:.45;cursor:not-allowed}.mindmap-toggle-btn.active[data-v-165ffaa9]{color:#fff;background:linear-gradient(#6366f1 0%,#4f46e5 100%);border-color:#4f46e5}.preview-image-wrap[data-v-165ffaa9]{background:var(--bg-container);flex:1;justify-content:center;align-items:center;padding:16px;display:flex;overflow:auto}.preview-image-wrap img[data-v-165ffaa9]{object-fit:contain;border-radius:4px;max-width:100%;max-height:100%;box-shadow:0 2px 12px #0003}.editor-tabs-spacer[data-v-165ffaa9]{flex:1}.preview-toggle-btn[data-v-165ffaa9]{cursor:pointer;height:34px;color:var(--text-secondary);white-space:nowrap;border:none;border-left:1px solid var(--border-color);background:0 0;flex-shrink:0;align-items:center;gap:5px;padding:0 10px;font-size:12px;transition:background .1s,color .1s;display:flex}.preview-toggle-btn[data-v-165ffaa9]:hover{background:var(--bg-hover);color:var(--text-primary)}.preview-toggle-btn.active[data-v-165ffaa9]{color:var(--color-primary);background:#3b82f614}.sidebar-actions[data-v-165ffaa9]{align-items:center;gap:2px;display:flex}.tree-inline-input-row[data-v-165ffaa9]{cursor:default;background:var(--bg-hover)}.tree-inline-input[data-v-165ffaa9]{background:var(--bg-container);border:1px solid var(--color-primary);min-width:0;height:20px;color:var(--text-primary);border-radius:3px;outline:none;flex:1;padding:0 5px;font-family:inherit;font-size:12.5px}.ctx-menu[data-v-165ffaa9]{z-index:9999;background:var(--bg-panel);border:1px solid var(--border-color);-webkit-user-select:none;user-select:none;border-radius:6px;min-width:160px;padding:4px;position:fixed;box-shadow:0 8px 24px #00000040}.ctx-menu-item[data-v-165ffaa9]{cursor:pointer;width:100%;color:var(--text-primary);text-align:left;background:0 0;border:none;border-radius:4px;align-items:center;gap:8px;padding:6px 10px;font-size:12.5px;transition:background .1s;display:flex}.ctx-menu-item[data-v-165ffaa9]:hover{background:var(--bg-hover)}.ctx-menu-item--danger[data-v-165ffaa9]{color:var(--color-danger)}.ctx-menu-item--danger[data-v-165ffaa9]:hover{background:#ef44441a}.ctx-menu-sep[data-v-165ffaa9]{background:var(--border-color);height:1px;margin:4px 2px}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{o as e}from"./rolldown-runtime-CMxvf4Kt.js";import{An as t,Bn as n,Gn as r,Ln as i,Mn as a,P as o,Pn as s,Qn as c,Rn as l,Un as ee,Vn as u,Wn as d,Xn as f,Zn as te,_ as ne,_t as re,c as p,cr as m,d as h,dr as g,er as _,f as ie,fr as ae,g as oe,gr as v,h as se,hr as y,jn as b,m as ce,mr as x,nr as S,or as le,p as ue,pr as C,qn as de,sr as w,t as fe,v as pe,y as me}from"./vendor-C30huq-U.js";import{n as T,t as E}from"./_plugin-vue_export-helper-Dw3U5p9d.js";import{a as he,r as ge}from"./index-DyR_trSU.js";var _e=e(h(),1),ve={class:`source-map-view`},ye={class:`sm-toolbar`},be={class:`sm-toolbar-left`},xe={class:`sm-title`},Se={class:`sm-toolbar-center`},Ce=[`placeholder`,`disabled`],we=[`disabled`],Te={class:`sm-toolbar-right`},Ee={class:`sm-body`},De={class:`sm-panel-header sm-panel-header--tabs`},Oe={key:0,class:`sm-badge`},ke={class:`sm-panel-body sm-file-tree`},Ae=[`onClick`],je={key:1,class:`sm-tree-arrow-spacer`},Me={key:2,class:`sm-tree-icon mit-icon`,"aria-hidden":`true`},Ne=[`xlink:href`],Pe={key:3,class:`sm-tree-icon mit-icon`,"aria-hidden":`true`},Fe=[`xlink:href`],Ie=[`title`],Le={key:1,class:`sm-tree-empty`},Re={class:`sm-panel-body sm-outline-body`},ze={class:`sm-badge`,style:{"margin-left":`auto`}},Be=[`onClick`],Ve=[`title`],He={key:0,class:`sm-outline-desc`},Ue={key:1,class:`sm-tree-empty`},We={class:`sm-panel sm-panel-graph`},Ge={key:0,class:`sm-project-info`},Ke={class:`sm-lang-badge`},qe={class:`sm-summary-text`},Je={class:`sm-graph-container`},Ye={class:`sm-layout-btn-wrap`},Xe=[`disabled`],Ze=[`title`],Qe={class:`sm-fn-label`},$e={key:0,class:`sm-fn-desc`},et={key:0,class:`sm-graph-empty`},tt={viewBox:`0 0 24 24`,width:`48`,height:`48`,fill:`none`,stroke:`currentColor`,"stroke-width":`1`,style:{opacity:`0.3`}},nt={key:1,class:`sm-graph-loading`},rt={class:`sm-log-header`},it={key:0,class:`sm-log-indicator`},at={class:`sm-log-body`,ref:`logBodyRef`},ot={key:0,class:`sm-empty`},st={class:`sm-panel-header`},ct={key:0,class:`sm-badge sm-badge-amber`},lt={key:0,class:`sm-node-detail`},ut={class:`sm-node-name`},dt={class:`sm-node-file`},ft={key:0},pt={key:0,class:`sm-node-desc`},mt={class:`sm-panel-body sm-source-body`},ht={key:0,class:`sm-source-overlay`},gt={key:1,class:`sm-source-overlay sm-source-placeholder`},D=E(de({__name:`SourceMapView`,setup(e){let h=g(document.documentElement.getAttribute(`data-theme`)===`dark`?`dark`:`light`);function de(){h.value=document.documentElement.getAttribute(`data-theme`)===`dark`?`dark`:`light`}let E=null,D=he(),O=g(D.currentDirectory||``),k=g(`idle`),A=g([]),_t=0,j=g(null),M=g(null),N=g(`files`),P=g(``),F=g(``),I=g(!1),L=g([]),R=g(new Set),z=g({files:!0,graph:!0,source:!0}),B=g(!1),V=g(220),H=g(360),U=g(140),W=null,G=0,vt=0,K=0;function q(e,t){W=e,G=t.clientX,vt=t.clientY,K=e===`files`?V.value:e===`source`?H.value:U.value,document.addEventListener(`mousemove`,yt),document.addEventListener(`mouseup`,J),t.preventDefault()}function yt(e){W&&(W===`files`?V.value=Math.max(120,Math.min(480,K+e.clientX-G)):W===`source`?H.value=Math.max(200,Math.min(700,K+G-e.clientX)):U.value=Math.max(60,Math.min(500,K+vt-e.clientY)))}function J(){W=null,document.removeEventListener(`mousemove`,yt),document.removeEventListener(`mouseup`,J)}let Y=g(null),X=ae(null),{fitView:bt,setNodes:xt,setEdges:St,getNodes:Ct,getEdges:wt,updateNodeInternals:Tt}=me(),Z=i(()=>k.value===`scanning`||k.value===`analyzing`),Et=i(()=>h.value===`dark`?`#334155`:`#cbd5e1`),Q=i(()=>j.value?.nodes.find(e=>e.id===M.value)??null),Dt=i(()=>{if(!j.value)return[];let e=new Map;for(let t of j.value.nodes){let n=t.subsystem??`__default__`;if(!e.has(n)){let r=j.value.subsystems?.find(e=>e.name===n);e.set(n,{displayName:r?.displayName||t.subsystem||T(`@SRCMAP:默认`),color:t.subsystemColor||kt[0],nodes:[]})}e.get(n).nodes.push(t)}return[...e.entries()].map(([,e])=>e)});function $(e,t=`info`){A.value.push({id:++_t,message:e,type:t,timestamp:Date.now()}),A.value.length>200&&A.value.splice(0,A.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(){!Y.value||X.value||(X.value=p.create(Y.value,{value:``,language:`plaintext`,theme:h.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}}))}le(h,e=>{X.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,l=a*600+n*220-(o-1)*220/2,ee=t*110,u=At(e);i.push({id:e.id,type:`default`,position:{x:l,y:ee},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:se.ArrowClosed}}))}}async function Pt(){let e=Ct.value,t=wt.value;if(e.length!==0){B.value=!0;try{await f(),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 _e.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)}),_e.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}xt(e.map(e=>({...e,position:r.get(e.id)??e.position}))),await f(),bt({padding:.18})}finally{B.value=!1}}}async function Ft(){if(!O.value.trim()){o.warning(T(`@SRCMAP:请先输入项目路径`));return}if(!Z.value){A.value=[],j.value=null,M.value=null,P.value=``,F.value=``,xt([]),St([]),k.value=`scanning`,$(T(`@SRCMAP:开始分析项目...`),`info`);try{let e=await fetch(`/api/code-analysis/analyze`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:O.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=``;k.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){$(`${T(`@SRCMAP:分析失败`)}: ${e.message}`,`error`),k.value=`error`}}}function It(e,t){if(e===`log`)$(t.message,t.type||`info`);else if(e===`files`)L.value=Ot(t.files||[]),L.value.forEach(e=>{e.kind===`dir`&&R.value.add(e.path)});else if(e===`result`){j.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(j.value.nodes,j.value.edges);xt(e),St(n),f(()=>Pt())}else e===`done`&&(t.error?($(`${T(`@SRCMAP:分析失败`)}: ${t.error}`,`error`),k.value=`error`):k.value=`done`)}async function Lt(e){if(!(!e||I.value)&&F.value!==e){I.value=!0,F.value=e,P.value=``;try{let t=await(await fetch(`/api/code-analysis/file-content?path=${encodeURIComponent(O.value)}&file=${encodeURIComponent(e)}`)).json();if(t.error)throw Error(t.error);P.value=t.content||``}catch(e){P.value=`// ${T(`@SRCMAP:加载失败`)}: ${e.message}`}finally{I.value=!1}}}function Rt(e){let t=e.node.data;M.value=t.id,t.file&&Lt(t.file)}function zt(e){R.value.has(e)?R.value.delete(e):R.value.add(e)}let Bt=i(()=>{let e=[];function t(n,r){for(let i of n){let n=R.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(L.value,0),e});return le(()=>D.currentDirectory,e=>{e&&!O.value&&(O.value=e)}),le([P,F],([e,t])=>{let n=X.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})}),c(()=>{Mt(),!O.value&&D.currentDirectory&&(O.value=D.currentDirectory),E=new MutationObserver(()=>de()),E.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-theme`]})}),te(()=>{X.value?.getModel()?.dispose(),X.value?.dispose(),J(),E?.disconnect(),E=null}),(e,i)=>{let o=re;return _(),u(`div`,ve,[l(`div`,ye,[l(`div`,be,[i[9]||=l(`svg`,{viewBox:`0 0 24 24`,width:`18`,height:`18`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`,class:`sm-icon-map`},[l(`polygon`,{points:`3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21`}),l(`line`,{x1:`9`,y1:`3`,x2:`9`,y2:`18`}),l(`line`,{x1:`15`,y1:`6`,x2:`15`,y2:`21`})],-1),l(`span`,xe,v(C(T)(`@SRCMAP:源码地图`)),1)]),l(`div`,Se,[m(l(`input`,{"onUpdate:modelValue":i[0]||=e=>O.value=e,class:`sm-path-input`,placeholder:C(T)(`@SRCMAP:输入项目目录路径`),disabled:Z.value,onKeydown:a(Ft,[`enter`])},null,40,Ce),[[t,O.value]]),l(`button`,{class:`sm-btn sm-btn-primary`,disabled:Z.value,onClick:Ft},[Z.value?(_(),u(s,{key:0},[i[10]||=l(`span`,{class:`sm-spinner`},null,-1),d(` `+v(C(T)(`@SRCMAP:分析中...`)),1)],64)):(_(),u(s,{key:1},[d(v(k.value===`done`?C(T)(`@SRCMAP:重新分析`):C(T)(`@SRCMAP:开始分析`)),1)],64))],8,we)]),l(`div`,Te,[r(o,{content:C(T)(`@SRCMAP:文件列表`),placement:`bottom`},{default:w(()=>[l(`button`,{class:x([`sm-panel-btn`,{active:z.value.files}]),onClick:i[1]||=e=>z.value.files=!z.value.files},[...i[11]||=[l(`svg`,{viewBox:`0 0 24 24`,width:`16`,height:`16`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[l(`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`]),r(o,{content:C(T)(`@SRCMAP:调用图`),placement:`bottom`},{default:w(()=>[l(`button`,{class:x([`sm-panel-btn`,{active:z.value.graph}]),onClick:i[2]||=e=>z.value.graph=!z.value.graph},[...i[12]||=[l(`svg`,{viewBox:`0 0 24 24`,width:`16`,height:`16`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[l(`circle`,{cx:`12`,cy:`12`,r:`3`}),l(`circle`,{cx:`3`,cy:`6`,r:`2`}),l(`circle`,{cx:`21`,cy:`6`,r:`2`}),l(`circle`,{cx:`3`,cy:`18`,r:`2`}),l(`circle`,{cx:`21`,cy:`18`,r:`2`}),l(`line`,{x1:`5`,y1:`6`,x2:`9`,y2:`11`}),l(`line`,{x1:`19`,y1:`6`,x2:`15`,y2:`11`}),l(`line`,{x1:`5`,y1:`18`,x2:`9`,y2:`13`}),l(`line`,{x1:`19`,y1:`18`,x2:`15`,y2:`13`})],-1)]],2)]),_:1},8,[`content`]),r(o,{content:C(T)(`@SRCMAP:源码面板`),placement:`bottom`},{default:w(()=>[l(`button`,{class:x([`sm-panel-btn`,{active:z.value.source}]),onClick:i[3]||=e=>z.value.source=!z.value.source},[...i[13]||=[l(`svg`,{viewBox:`0 0 24 24`,width:`16`,height:`16`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[l(`polyline`,{points:`16 18 22 12 16 6`}),l(`polyline`,{points:`8 6 2 12 8 18`})],-1)]],2)]),_:1},8,[`content`])])]),l(`div`,Ee,[m(l(`div`,{class:`sm-panel sm-panel-files`,style:y({width:V.value+`px`})},[l(`div`,De,[l(`button`,{class:x([`sm-tab-btn`,{active:N.value===`files`}]),onClick:i[4]||=e=>N.value=`files`},[i[14]||=l(`svg`,{viewBox:`0 0 24 24`,width:`12`,height:`12`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[l(`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),d(` `+v(C(T)(`@SRCMAP:文件列表`))+` `,1),j.value?(_(),u(`span`,Oe,v(j.value.allFiles.length),1)):n(``,!0)],2),l(`button`,{class:x([`sm-tab-btn`,{active:N.value===`outline`}]),onClick:i[5]||=e=>N.value=`outline`},[i[15]||=ee(`<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),d(` `+v(C(T)(`@SRCMAP:大纲`)),1)],2)]),m(l(`div`,ke,[Bt.value.length>0?(_(!0),u(s,{key:0},S(Bt.value,e=>(_(),u(`div`,{key:e.path,class:x([`sm-tree-node`,{"sm-tree-node--dir":e.kind===`dir`,"sm-tree-node--active":e.kind===`file`&&F.value===e.path}]),style:y({paddingLeft:10+e.depth*14+`px`}),onClick:t=>e.kind===`dir`?zt(e.path):Lt(e.path)},[e.kind===`dir`?(_(),u(`span`,{key:0,class:x([`sm-tree-arrow`,{expanded:e.expanded}])},[...i[16]||=[l(`svg`,{viewBox:`0 0 24 24`,width:`10`,height:`10`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[l(`polyline`,{points:`9 18 15 12 9 6`})],-1)]],2)):(_(),u(`span`,je)),e.kind===`dir`?(_(),u(`svg`,Me,[l(`use`,{"xlink:href":`#${C(ge)(e.name)||`icon-folder`}`},null,8,Ne)])):(_(),u(`svg`,Pe,[l(`use`,{"xlink:href":`#${C(ge)(e.name)}`},null,8,Fe)])),l(`span`,{class:`sm-tree-name`,title:e.path},v(e.name),9,Ie)],14,Ae))),128)):(_(),u(`div`,Le,v(C(T)(`@SRCMAP:暂无文件,请先开始分析`)),1))],512),[[b,N.value===`files`]]),m(l(`div`,Re,[Dt.value.length>0?(_(!0),u(s,{key:0},S(Dt.value,e=>(_(),u(`div`,{key:e.displayName,class:`sm-outline-group`},[l(`div`,{class:`sm-outline-group-header`,style:y({color:e.color})},[i[17]||=l(`svg`,{viewBox:`0 0 24 24`,width:`8`,height:`8`,fill:`currentColor`},[l(`circle`,{cx:`12`,cy:`12`,r:`6`})],-1),d(` `+v(e.displayName)+` `,1),l(`span`,ze,v(e.nodes.length),1)],4),(_(!0),u(s,null,S(e.nodes,t=>(_(),u(`div`,{key:t.id,class:x([`sm-outline-node`,{"sm-outline-node--active":M.value===t.id}]),onClick:e=>{M.value=t.id,t.file&&Lt(t.file)}},[l(`span`,{class:`sm-outline-dot`,style:y({background:e.color})},null,4),l(`span`,{class:`sm-outline-label`,title:t.file||t.label},v(t.label),9,Ve),t.description?(_(),u(`span`,He,v(t.description.length>18?t.description.slice(0,18)+`…`:t.description),1)):n(``,!0)],10,Be))),128))]))),128)):(_(),u(`div`,Ue,v(C(T)(`@SRCMAP:暂无分析结果`)),1))],512),[[b,N.value===`outline`]])],4),[[b,z.value.files]]),m(l(`div`,{class:`sm-resizer sm-resizer-v`,onMousedown:i[6]||=e=>q(`files`,e)},null,544),[[b,z.value.files&&z.value.graph]]),m(l(`div`,We,[j.value?(_(),u(`div`,Ge,[l(`span`,Ke,v(j.value.language),1),j.value.subsystems&&j.value.subsystems.length>1?(_(!0),u(s,{key:0},S(j.value.subsystems,e=>(_(),u(`span`,{key:e.name,class:`sm-subsystem-tag`,style:y({borderColor:e.color,color:e.color})},`● `+v(e.displayName||e.name),5))),128)):(_(!0),u(s,{key:1},S(j.value.techStack.slice(0,4),e=>(_(),u(`span`,{key:e,class:`sm-tech-tag`},v(e),1))),128)),l(`span`,qe,v(j.value.summary),1)])):n(``,!0),l(`div`,Je,[l(`div`,Ye,[l(`button`,{class:`sm-layout-btn`,disabled:B.value||!j.value,onClick:Pt},[i[18]||=ee(`<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),d(` `+v(B.value?C(T)(`@SRCMAP:布局中...`):C(T)(`@SRCMAP:优化布局`)),1)],8,Xe)]),r(C(ne),{class:`sm-vue-flow`,"default-viewport":{zoom:1},"min-zoom":.2,"max-zoom":4,"fit-view-on-init":``,onNodeClick:Rt},{"node-default":w(({data:e,label:t})=>[r(C(pe),{type:`target`,position:C(oe).Top},null,8,[`position`]),l(`div`,{class:`sm-fn-inner`,title:`${t}${e?.description?`
|
|
3
|
-
`+e.description:``}`},[l(`div`,Qe,v(t),1),e?.description?(_(),u(`div`,$e,v(e.description.length>22?e.description.slice(0,22)+`…`:e.description),1)):n(``,!0)],8,Ze),r(C(pe),{type:`source`,position:C(oe).Bottom},null,8,[`position`])]),default:w(()=>[r(C(ce),{variant:C(ue).Dots,gap:20,size:1,"pattern-color":Et.value},null,8,[`variant`,`pattern-color`]),r(C(ie)),r(C(fe),{"node-color":`#3b82f6`,"mask-color":h.value===`dark`?`rgba(0,0,0,0.55)`:`rgba(15,23,42,0.06)`},null,8,[`mask-color`])]),_:1}),!j.value&&!Z.value?(_(),u(`div`,et,[(_(),u(`svg`,tt,[...i[19]||=[l(`polygon`,{points:`3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21`},null,-1),l(`line`,{x1:`9`,y1:`3`,x2:`9`,y2:`18`},null,-1),l(`line`,{x1:`15`,y1:`6`,x2:`15`,y2:`21`},null,-1)]])),l(`p`,null,v(C(T)(`@SRCMAP:输入项目路径后点击开始分析`)),1)])):n(``,!0),Z.value&&!j.value?(_(),u(`div`,nt,[i[20]||=l(`span`,{class:`sm-spinner sm-spinner-lg`},null,-1),l(`p`,null,v(C(T)(`@SRCMAP:AI 正在分析项目结构...`)),1)])):n(``,!0)]),l(`div`,{class:`sm-resizer sm-resizer-h`,onMousedown:i[7]||=e=>q(`log`,e)},null,32),l(`div`,{class:`sm-log-panel`,style:y({height:U.value+`px`})},[l(`div`,rt,[i[21]||=l(`svg`,{viewBox:`0 0 24 24`,width:`12`,height:`12`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[l(`polyline`,{points:`4 17 10 11 4 5`}),l(`line`,{x1:`12`,y1:`19`,x2:`20`,y2:`19`})],-1),d(` `+v(C(T)(`@SRCMAP:Agent 日志`))+` `,1),Z.value?(_(),u(`span`,it)):n(``,!0)]),l(`div`,at,[(_(!0),u(s,null,S(A.value.slice(-50),e=>(_(),u(`div`,{key:e.id,class:x([`sm-log-entry`,`sm-log-entry--${e.type}`])},v(e.message),3))),128)),A.value.length===0?(_(),u(`div`,ot,v(C(T)(`@SRCMAP:等待开始分析`)),1)):n(``,!0)],512)],4)],512),[[b,z.value.graph]]),m(l(`div`,{class:`sm-resizer sm-resizer-v`,onMousedown:i[8]||=e=>q(`source`,e)},null,544),[[b,z.value.graph&&z.value.source]]),m(l(`div`,{class:`sm-panel sm-panel-source`,style:y({width:H.value+`px`})},[l(`div`,st,[i[22]||=l(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[l(`polyline`,{points:`16 18 22 12 16 6`}),l(`polyline`,{points:`8 6 2 12 8 18`})],-1),d(` `+v(F.value||C(T)(`@SRCMAP:源码面板`))+` `,1),Q.value?(_(),u(`span`,ct,v(Q.value.label),1)):n(``,!0)]),Q.value?(_(),u(`div`,lt,[l(`div`,ut,v(Q.value.label),1),l(`div`,dt,[d(v(Q.value.file),1),Q.value.line?(_(),u(`span`,ft,` :`+v(Q.value.line),1)):n(``,!0)]),Q.value.description?(_(),u(`div`,pt,v(Q.value.description),1)):n(``,!0)])):n(``,!0),l(`div`,mt,[I.value?(_(),u(`div`,ht,[...i[23]||=[l(`span`,{class:`sm-spinner`},null,-1)]])):n(``,!0),!I.value&&!P.value?(_(),u(`div`,gt,v(C(T)(`@SRCMAP:点击调用图中的节点查看源码`)),1)):n(``,!0),l(`div`,{ref_key:`monacoContainerRef`,ref:Y,class:`sm-monaco-container`},null,512)])],4),[[b,z.value.source]])])])}}}),[[`__scopeId`,`data-v-71d3569e`]]);export{D as default};
|