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.
Files changed (35) hide show
  1. package/README.md +8 -4
  2. package/package.json +1 -1
  3. package/scripts/README_COLOR_CONVERTER.md +196 -196
  4. package/scripts/README_FONTSIZE_CONVERTER.md +278 -278
  5. package/scripts/README_SPACING_CONVERTER.md +126 -126
  6. package/scripts/README_STYLE_VARS.md +180 -180
  7. package/scripts/verify-file-search.mjs +281 -0
  8. package/src/ui/public/assets/EditorView-CSUfBR5z.js +0 -0
  9. package/src/ui/public/assets/EditorView-Dvg7sMyU.css +1 -0
  10. package/src/ui/public/assets/SourceMapView-CPsyB1rP.js +3 -0
  11. package/src/ui/public/assets/WorkbenchView-BBDJOY3C.js +6 -0
  12. package/src/ui/public/assets/WorkbenchView-CA91BJc8.css +1 -0
  13. package/src/ui/public/assets/{_plugin-vue_export-helper-Dw3U5p9d.js → _plugin-vue_export-helper-rFkdzaHd.js} +3 -3
  14. package/src/ui/public/assets/{css.worker-CvXBzhp8.js → css.worker-Wv5dxAWO.js} +1 -1
  15. package/src/ui/public/assets/{html.worker-BO6WuOEO.js → html.worker-CQP8QQsS.js} +1 -1
  16. package/src/ui/public/assets/index-CLVL3Inb.js +66 -0
  17. package/src/ui/public/assets/index-ChSExFC3.css +1 -0
  18. package/src/ui/public/assets/{json.worker-BkJRGcCJ.js → json.worker-DzV-CpCQ.js} +1 -1
  19. package/src/ui/public/assets/{ts.worker-B0J26iPs.js → ts.worker-Dth06zuC.js} +15 -15
  20. package/src/ui/public/assets/{vendor-C30huq-U.js → vendor-CDAfMQIV.js} +240 -247
  21. package/src/ui/public/assets/vendor-CIeR0bF6.css +1 -0
  22. package/src/ui/public/favicon.svg +75 -75
  23. package/src/ui/public/index.html +23 -23
  24. package/src/ui/public/logo.svg +74 -74
  25. package/src/ui/server/routes/workbench.js +299 -160
  26. package/src/ui/public/assets/EditorView-BLK-Sohi.js +0 -0
  27. package/src/ui/public/assets/EditorView-DEd8QuPp.css +0 -1
  28. package/src/ui/public/assets/SourceMapView-CMJnpleg.js +0 -3
  29. package/src/ui/public/assets/WorkbenchView-CPcGSyVM.css +0 -1
  30. package/src/ui/public/assets/WorkbenchView-CTB3QNSR.js +0 -6
  31. package/src/ui/public/assets/index-CV4VteDa.css +0 -1
  32. package/src/ui/public/assets/index-DyR_trSU.js +0 -66
  33. package/src/ui/public/assets/vendor-Bq2rS2vY.css +0 -1
  34. /package/src/ui/public/assets/{editor.worker-Cn2oRESe.js → editor.worker-Bd9IXS8d.js} +0 -0
  35. /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
- const promptTemplate = sub.promptOverride || (task.promptId
946
- ? (await readJson(PROMPTS_FILE, { prompts: [] })).prompts.find(p => p.id === task.promptId)?.content
947
- : null) || '';
948
- const ctx = {
949
- task: { title: task.title, desc: task.desc || '' },
950
- sub: { title: sub.title, desc: sub.desc || '' },
951
- repo: { path: repoPath || '' },
952
- branch: branch || ''
953
- };
954
- const interpolated = interpolate(promptTemplate, ctx);
955
- const parts = [interpolated, sub.title, sub.desc].filter(s => s && s.trim());
956
- let prompt = parts.join('\n\n');
957
-
958
- // ── 前序上下文:把前几个 done 子任务的输出摘要拼到 prompt 头部 ──
959
- if (priorOutputs.length > 0) {
960
- const prevBlock = priorOutputs.map((p, i) => {
961
- const text = (p.output || '').slice(0, MAX_PREV_OUTPUT_CHARS)
962
- const truncated = (p.output || '').length > MAX_PREV_OUTPUT_CHARS ? '\n…(前文已截断)' : ''
963
- return `### [${i + 1}] ${p.title}\n${text}${truncated}`
964
- }).join('\n\n')
965
- prompt = `以下是同一任务下已经完成的前序子任务输出(仅作上下文参考,请基于这些结论继续当前子任务,无需重复执行它们):
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
- // ── 附件:合并 sub.attachments + task.attachments 后拼到 prompt 末尾 ──
975
- // claude -p 字符串模式会扫描 prompt 中出现的本地文件路径并自动
976
- // 识别为附件(图片 / PDF / 文本均可)。
977
- // 主任务附件对所有 sub 都可见;子任务自己的附件只对该 sub 可见。
978
- const allAttachments = [
979
- ...(Array.isArray(task.attachments) ? task.attachments : []),
980
- ...(Array.isArray(sub.attachments) ? sub.attachments : [])
981
- ];
982
- if (allAttachments.length > 0) {
983
- const lines = allAttachments
984
- .filter(a => a && a.absolutePath)
985
- .map((a, i) => ` ${i + 1}. [${a.mimeType || 'application/octet-stream'}] ${a.absolutePath}`);
986
- if (lines.length > 0) {
987
- prompt += `\n\n---\n本任务包含 ${lines.length} 个附件(请按文件路径读取,不要让用户重新提供):\n${lines.join('\n')}\n---`;
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
- const jobId = genId();
992
- const job = {
993
- id: jobId,
994
- taskId: task.id,
995
- subId: sub.id,
996
- title: `${task.title} / ${sub.title}`,
997
- status: 'pending',
998
- prompt
999
- };
1000
- jobs.set(jobId, job);
1001
- sub.status = 'running';
1002
- publish('sub:update', { taskId: task.id, sub });
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
- try {
1006
- const { pid, child } = await launchClaudeInNewWindow(repoPath || process.cwd(), prompt);
1007
- job.pid = pid;
1008
- // 保存 child 引用,供 cancel 接口调用 kill
1009
- job.child = child;
1010
- job.startedAt = nowIso();
1011
- job.status = 'running';
1012
- publish('job:update', job);
1013
-
1014
- // 流式 NDJSON 解析:把 stdout 当作 stream-json 协议处理
1015
- // assistant.text → job.output (用户主要关心的内容)
1016
- // assistant.thinking → job.thinking (折叠展示,让用户知道 Claude 在想)
1017
- // 其他事件(init / tool_use / result 等)忽略,避免噪声
1018
- // 解析失败的行原样进 output,便于排查协议异常。
1019
- // thinking 几乎不做服务端截断:Claude reasoning 一般在 KB~几十 MB 之间,
1020
- // 100MB 兜底只是为了防止内存爆炸。流式 publish 时改推增量 delta(仅新拼接的
1021
- // 那部分),终态或重连时才随 job:update 全量同步,避免每帧重复广播整个累积文本。
1022
- const MAX_OUTPUT = 100 * 1024 * 1024;
1023
- const MAX_THINKING = 100 * 1024 * 1024;
1024
- job.output = '';
1025
- job.thinking = '';
1026
- const lineBuf = { stdout: '', stderr: '' };
1027
-
1028
- const parseLines = (channel, buf) => {
1029
- const chunk = buf.toString('utf8');
1030
- lineBuf[channel] += chunk;
1031
- const lines = lineBuf[channel].split('\n');
1032
- lineBuf[channel] = lines.pop() ?? ''; // 最后一段可能不完整,留给下次
1033
- let pendingThinkingDelta = '';
1034
- for (const line of lines) {
1035
- const trimmed = line.trim();
1036
- if (!trimmed) continue;
1037
- if (channel === 'stderr' || !trimmed.startsWith('{')) {
1038
- // stream-json 行:原样塞进 output(兼容老版本 claude / 错误信息)
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 + trimmed + '\n').slice(-MAX_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
- continue;
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
- let evt;
1047
- try { evt = JSON.parse(trimmed) } catch { continue }
1048
- if (evt.type !== 'assistant') continue;
1049
- const blocks = evt.message?.content;
1050
- if (!Array.isArray(blocks)) continue;
1051
- for (const b of blocks) {
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
- const prevLen = job.output.length;
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
- const prevLen = job.thinking.length;
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
- // 一批 NDJSON 处理完后统一发一次 thinking delta,避免高频小块 socket 占用
1066
- if (pendingThinkingDelta) {
1067
- publish('job:thinking-delta', { id: job.id, delta: pendingThinkingDelta });
1068
- }
1069
- };
1070
- if (child.stdout) child.stdout.on('data', (buf) => parseLines('stdout', buf));
1071
- if (child.stderr) child.stderr.on('data', (buf) => parseLines('stderr', buf));
1072
-
1073
- // 等待进程退出(detached 不阻塞主进程,用 polling /proc 兜底)
1074
- await waitProcessExit(pid);
1075
- const wasCancelled = cancelledJobs.has(jobId)
1076
- if (wasCancelled) cancelledJobs.delete(jobId)
1077
- // 进程退出时 stdout 可能残留最后一段未换行的 NDJSON,flush 一次
1078
- // flush 内部也按 delta 推送,保持与流式阶段一致
1079
- if (lineBuf.stdout.trim()) {
1080
- const outPrev = job.output.length;
1081
- const thinkPrev = job.thinking.length;
1082
- try {
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
- // 写回 tasks.json
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() });
@@ -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};