diff --git a/scripts/annotate-self-report.mjs b/scripts/annotate-self-report.mjs new file mode 100644 index 0000000..71c103b --- /dev/null +++ b/scripts/annotate-self-report.mjs @@ -0,0 +1,118 @@ +// 人工为 self-report 的「分组(文件/类)」与「流程」写入一句话摘要, +// 写进 CodeFile.summary / Container.summary / Flow.summary,供 UI 展示。 +// +// 匹配用稳定标识(文件路径 / 类名 / 流程名),不依赖会随代码改动漂移的行号, +// 因此重新生成报告后可直接重跑本脚本恢复标注。 +// 用法: node scripts/annotate-self-report.mjs + +import { readFileSync, writeFileSync, copyFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const target = resolve("web/public/self-report.json"); +const mirror = resolve("self-report.json"); + +// 文件分组摘要(key = CodeFile.path) +const fileSummaries = { + "src/scanner.ts": "递归扫描项目目录,按扩展名收集源文件并识别语言", + "src/types.ts": "标准化代码报告的核心类型定义(Schema 真相源)", + "src/externals.ts": "聚合函数交互,派生外部系统(数据库 / 服务)清单", + "src/flows.ts": "从入口点遍历调用图,自动生成调用链流程", + "src/analyzer.ts": "基于 TS 编译器 API 的解析核心:构建函数层级与调用边", + "src/cli.ts": "命令行入口:解析参数、运行分析、写出报告 JSON", + "src/overview.ts": "从报告推导「概览」确定层数据(接口 / 入口 / 外部系统 / 热点)", + "src/overview-cli.ts": "概览推导的命令行运行器,打印可读的项目地图", + "web/src/model/report.ts": "前端侧报告类型定义(与 CLI Schema 对齐)", + "web/src/model/reportRepository.ts": "报告数据源抽象,HTTP 加载报告 JSON", + "web/src/model/graphModel.ts": "将 Flow 转换为与渲染无关的「中性图」结构", + "web/src/services/dagreLayout.ts": "用 Dagre 计算图的自动布局坐标", + "web/src/model/overview.ts": "前端侧概览推导(与 CLI overview 同构)", + "web/src/services/sourceService.ts": "源码绑定:向开发服务器请求真实源码片段", + "web/src/services/languageMap.ts": "文件扩展名到语法高亮语言的映射", + "web/src/viewmodel/GraphViewModel.ts": "图视图 ViewModel:编排加载 / 选择 / 布局 / 搜索 / 源码", + "web/src/view/vmContext.ts": "提供 GraphViewModel 的 React Context", + "web/src/view/components/FlowNode.tsx": "自定义流程图节点:显示函数名 / 归属 / 交互徽标", + "web/src/view/components/FlowGraph.tsx": "React Flow 画布容器,渲染当前流程图", + "web/src/view/components/FlowList.tsx": "左侧流程列表面板", + "web/src/view/components/GroupList.tsx": "最左侧分组列表:按类 / 文件过滤流程", + "web/src/view/components/NodeDetailPanel.tsx": "右侧节点详情:位置 / 归属 / 交互 / 所在流程", + "web/src/view/components/SearchBar.tsx": "顶部搜索栏,检索流程与函数", + "web/src/view/components/OverviewPage.tsx": "概览落地页视图", + "web/src/view/components/CodePanel.tsx": "底部源码面板:语法高亮显示选中函数实现", + "web/src/view/components/FlowBanner.tsx": "画布顶部横幅:显示当前流程名与说明", + "web/src/view/App.tsx": "应用主组件:编排头部与各面板布局", + "web/src/main.tsx": "前端入口:构建数据源与 ViewModel 并挂载 App", + "web/vite.config.ts": "Vite 配置 + 开发期源码服务中间件", +}; + +// 类分组摘要(key = Container.name) +const containerSummaries = { + HttpReportRepository: "通过 HTTP 从 URL 加载报告 JSON 的仓库实现", + GraphViewModel: "图视图 ViewModel:持有全部可观察状态与命令", +}; + +// 流程摘要(key = Flow.name) +const flowSummaries = { + " (web/src/main.tsx)": "前端启动:构建报告数据源与 ViewModel,挂载 React 应用", + "visit (src/analyzer.ts)": "AST 遍历核心:递归访问语法树,登记函数 / 容器并建立调用边", + "observer() 回调@24 (web/src/view/components/OverviewPage.tsx)": "渲染概览落地页:项目信息、接口分组、外部系统、热点", + "observer() 回调@13 (web/src/view/components/NodeDetailPanel.tsx)": "渲染右侧节点详情:位置、归属、交互、所在流程", + "observer() 回调@7 (web/src/view/components/CodePanel.tsx)": "渲染底部源码面板:随选中节点显示语法高亮源码", + "observer() 回调@11 (web/src/view/App.tsx)": "渲染应用骨架:头部、搜索、各面板布局", + "GraphViewModel.positionedGraph (web/src/viewmodel/GraphViewModel.ts)": "计算当前流程的带坐标中性图(含 Dagre 自动布局)", + "observer() 回调@16 (web/src/view/components/FlowGraph.tsx)": "把中性图适配为 React Flow 节点 / 边并渲染画布", + "observer() 回调@4 (web/src/view/components/FlowList.tsx)": "渲染流程列表并处理流程选择", + "observer() 回调@4 (web/src/view/components/GroupList.tsx)": "渲染分组列表,按类 / 文件过滤流程", + "observer() 回调@6 (web/src/view/components/SearchBar.tsx)": "渲染搜索框与结果下拉", + "匿名函数@26 (web/src/view/components/SearchBar.tsx)": "搜索输入变更处理回调", + "map() 回调@72 (src/flows.ts)": "生成流程步骤时的映射回调", + "inProject (src/analyzer.ts)": "判断文件是否属于目标项目,过滤外部依赖", + "map() 回调@229 (src/overview.ts)": "概览推导中的流程映射回调", + "map() 回调@44 (web/src/model/graphModel.ts)": "将流程步骤映射为图节点", + "map() 回调@205 (web/src/model/overview.ts)": "前端概览推导中的映射回调", + "GraphViewModel.overview (web/src/viewmodel/GraphViewModel.ts)": "从报告推导概览确定层数据的计算属性", + "GraphViewModel.groups (web/src/viewmodel/GraphViewModel.ts)": "派生分组列表的计算属性", + "GraphViewModel.visibleFlows (web/src/viewmodel/GraphViewModel.ts)": "按当前分组过滤后的流程列表", + "GraphViewModel.selectedFuncOwner (web/src/viewmodel/GraphViewModel.ts)": "计算选中函数的归属(类 / 文件)及其说明", + "reaction() 回调@79 (web/src/viewmodel/GraphViewModel.ts)": "监听选中节点变化,自动拉取对应源码", + "memo() 回调@40 (web/src/view/components/FlowNode.tsx)": "自定义节点渲染(memo 回调)", + "匿名函数@41 (web/src/view/components/SearchBar.tsx)": "搜索结果项点击处理", + "observer() 回调@4 (web/src/view/components/FlowBanner.tsx)": "渲染当前流程横幅:流程名与一句话说明", + " (src/cli.ts)": "CLI 主流程:解析参数、运行分析、写出报告", + " (src/overview-cli.ts)": "概览 CLI 主流程:读取报告并打印项目地图", + " (web/vite.config.ts)": "Vite 配置与源码服务中间件初始化", +}; + +const report = JSON.parse(readFileSync(target, "utf-8")); + +let f = 0; +for (const file of report.files) { + if (fileSummaries[file.path]) { + file.summary = fileSummaries[file.path]; + f++; + } +} +let c = 0; +for (const cont of report.containers) { + if (containerSummaries[cont.name]) { + cont.summary = containerSummaries[cont.name]; + c++; + } +} +let fl = 0; +const unmatched = []; +for (const flow of report.flows) { + if (flowSummaries[flow.name]) { + flow.summary = flowSummaries[flow.name]; + flow.provenance = "manual"; + fl++; + } else { + unmatched.push(flow.name); + } +} + +writeFileSync(target, JSON.stringify(report, null, 2), "utf-8"); +copyFileSync(target, mirror); + +console.log(`已标注: 文件 ${f}/${report.files.length} · 类 ${c}/${report.containers.length} · 流程 ${fl}/${report.flows.length}`); +if (unmatched.length) console.log(`未匹配流程: ${unmatched.join(" | ")}`); +console.log(`写入: ${target}\n镜像: ${mirror}`); diff --git a/self-report.json b/self-report.json index c66a8bb..047d003 100644 --- a/self-report.json +++ b/self-report.json @@ -4,20 +4,46 @@ "name": "code-visualize", "root": "E:/code-visualize", "languages": [ + "javascript", "tsx", "typescript" ], - "generatedAt": "2026-07-22T03:03:33.188Z", + "generatedAt": "2026-07-22T09:34:53.239Z", "generator": "code-visualize-cli@0.1.0" }, "stats": { - "fileCount": 21, + "fileCount": 28, "containerCount": 2, - "functionCount": 94, - "edgeCount": 110, - "loc": 2212 + "functionCount": 119, + "edgeCount": 135, + "loc": 3018 }, "modules": [ + { + "id": "module:scripts", + "kind": "module", + "name": "scripts", + "provenance": "ast", + "path": "scripts", + "childModuleIds": [], + "fileIds": [ + "file:scripts/annotate-self-report.mjs" + ], + "parentId": "module:." + }, + { + "id": "module:.", + "kind": "module", + "name": "code-visualize", + "provenance": "ast", + "path": ".", + "childModuleIds": [ + "module:scripts", + "module:src", + "module:web" + ], + "fileIds": [] + }, { "id": "module:src", "kind": "module", @@ -31,22 +57,12 @@ "file:src/externals.ts", "file:src/flows.ts", "file:src/analyzer.ts", - "file:src/cli.ts" + "file:src/cli.ts", + "file:src/overview.ts", + "file:src/overview-cli.ts" ], "parentId": "module:." }, - { - "id": "module:.", - "kind": "module", - "name": "code-visualize", - "provenance": "ast", - "path": ".", - "childModuleIds": [ - "module:src", - "module:web" - ], - "fileIds": [] - }, { "id": "module:web/src/model", "kind": "module", @@ -100,7 +116,9 @@ "path": "web/src/services", "childModuleIds": [], "fileIds": [ - "file:web/src/services/dagreLayout.ts" + "file:web/src/services/dagreLayout.ts", + "file:web/src/services/sourceService.ts", + "file:web/src/services/languageMap.ts" ], "parentId": "module:web/src" }, @@ -144,12 +162,33 @@ "file:web/src/view/components/FlowList.tsx", "file:web/src/view/components/GroupList.tsx", "file:web/src/view/components/NodeDetailPanel.tsx", - "file:web/src/view/components/SearchBar.tsx" + "file:web/src/view/components/SearchBar.tsx", + "file:web/src/view/components/CodePanel.tsx", + "file:web/src/view/components/FlowBanner.tsx" ], "parentId": "module:web/src/view" } ], "files": [ + { + "id": "file:scripts/annotate-self-report.mjs", + "kind": "file", + "name": "annotate-self-report.mjs", + "location": { + "file": "scripts/annotate-self-report.mjs", + "startLine": 1, + "endLine": 119 + }, + "language": "javascript", + "provenance": "ast", + "moduleId": "module:scripts", + "path": "scripts/annotate-self-report.mjs", + "loc": 119, + "containerIds": [], + "functionIds": [ + "func:scripts/annotate-self-report.mjs#@0" + ] + }, { "id": "file:src/scanner.ts", "kind": "file", @@ -170,7 +209,8 @@ "func:src/scanner.ts#collectSourceFiles@24", "func:src/scanner.ts#walk@27", "func:src/scanner.ts#languageOf@52" - ] + ], + "summary": "递归扫描项目目录,按扩展名收集源文件并识别语言" }, { "id": "file:src/types.ts", @@ -187,7 +227,8 @@ "path": "src/types.ts", "loc": 205, "containerIds": [], - "functionIds": [] + "functionIds": [], + "summary": "标准化代码报告的核心类型定义(Schema 真相源)" }, { "id": "file:src/externals.ts", @@ -206,7 +247,8 @@ "containerIds": [], "functionIds": [ "func:src/externals.ts#deriveExternalSystems@4" - ] + ], + "summary": "聚合函数交互,派生外部系统(数据库 / 服务)清单" }, { "id": "file:src/flows.ts", @@ -227,7 +269,8 @@ "func:src/flows.ts#generateCallFlows@16", "func:src/flows.ts#stepIdOf@70", "func:src/flows.ts#map() 回调@72@72" - ] + ], + "summary": "从入口点遍历调用图,自动生成调用链流程" }, { "id": "file:src/analyzer.ts", @@ -273,7 +316,8 @@ "func:src/analyzer.ts#analyzeRelations@487", "func:src/analyzer.ts#walk@492", "func:src/analyzer.ts#resolveInternalImport@548" - ] + ], + "summary": "基于 TS 编译器 API 的解析核心:构建函数层级与调用边" }, { "id": "file:src/cli.ts", @@ -294,7 +338,56 @@ "func:src/cli.ts#parseArgs@13", "func:src/cli.ts#main@40", "func:src/cli.ts#@0" - ] + ], + "summary": "命令行入口:解析参数、运行分析、写出报告 JSON" + }, + { + "id": "file:src/overview.ts", + "kind": "file", + "name": "overview.ts", + "location": { + "file": "src/overview.ts", + "startLine": 1, + "endLine": 262 + }, + "language": "typescript", + "provenance": "ast", + "moduleId": "module:src", + "path": "src/overview.ts", + "loc": 262, + "containerIds": [], + "functionIds": [ + "func:src/overview.ts#apiInOf@79", + "func:src/overview.ts#groupKeyFromPath@83", + "func:src/overview.ts#reachableFuncs@92", + "func:src/overview.ts#deriveOverview@109", + "func:src/overview.ts#analyzeEntry@140", + "func:src/overview.ts#map() 回调@219@219", + "func:src/overview.ts#map() 回调@229@229" + ], + "summary": "从报告推导「概览」确定层数据(接口 / 入口 / 外部系统 / 热点)" + }, + { + "id": "file:src/overview-cli.ts", + "kind": "file", + "name": "overview-cli.ts", + "location": { + "file": "src/overview-cli.ts", + "startLine": 1, + "endLine": 87 + }, + "language": "typescript", + "provenance": "ast", + "moduleId": "module:src", + "path": "src/overview-cli.ts", + "loc": 87, + "containerIds": [], + "functionIds": [ + "func:src/overview-cli.ts#main@9", + "func:src/overview-cli.ts#flag@16", + "func:src/overview-cli.ts#@0" + ], + "summary": "概览推导的命令行运行器,打印可读的项目地图" }, { "id": "file:web/src/model/report.ts", @@ -311,7 +404,8 @@ "path": "web/src/model/report.ts", "loc": 194, "containerIds": [], - "functionIds": [] + "functionIds": [], + "summary": "前端侧报告类型定义(与 CLI Schema 对齐)" }, { "id": "file:web/src/model/reportRepository.ts", @@ -330,7 +424,8 @@ "containerIds": [ "container:web/src/model/reportRepository.ts#HttpReportRepository@16" ], - "functionIds": [] + "functionIds": [], + "summary": "报告数据源抽象,HTTP 加载报告 JSON" }, { "id": "file:web/src/model/graphModel.ts", @@ -352,7 +447,8 @@ "func:web/src/model/graphModel.ts#qualifierOf@24", "func:web/src/model/graphModel.ts#flowToGraph@43", "func:web/src/model/graphModel.ts#map() 回调@44@44" - ] + ], + "summary": "将 Flow 转换为与渲染无关的「中性图」结构" }, { "id": "file:web/src/services/dagreLayout.ts", @@ -371,7 +467,28 @@ "containerIds": [], "functionIds": [ "func:web/src/services/dagreLayout.ts#computeLayout@31" - ] + ], + "summary": "用 Dagre 计算图的自动布局坐标" + }, + { + "id": "file:web/src/services/sourceService.ts", + "kind": "file", + "name": "sourceService.ts", + "location": { + "file": "web/src/services/sourceService.ts", + "startLine": 1, + "endLine": 33 + }, + "language": "typescript", + "provenance": "ast", + "moduleId": "module:web/src/services", + "path": "web/src/services/sourceService.ts", + "loc": 33, + "containerIds": [], + "functionIds": [ + "func:web/src/services/sourceService.ts#fetchSource@19" + ], + "summary": "源码绑定:向开发服务器请求真实源码片段" }, { "id": "file:web/src/viewmodel/GraphViewModel.ts", @@ -380,21 +497,24 @@ "location": { "file": "web/src/viewmodel/GraphViewModel.ts", "startLine": 1, - "endLine": 290 + "endLine": 381 }, "language": "typescript", "provenance": "ast", "moduleId": "module:web/src/viewmodel", "path": "web/src/viewmodel/GraphViewModel.ts", - "loc": 290, + "loc": 381, "containerIds": [ - "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" ], "functionIds": [ - "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", - "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@149@149", - "func:web/src/viewmodel/GraphViewModel.ts#map() 回调@186@186" - ] + "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@203@203", + "func:web/src/viewmodel/GraphViewModel.ts#map() 回调@240@240", + "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@368@368" + ], + "summary": "图视图 ViewModel:编排加载 / 选择 / 布局 / 搜索 / 源码" }, { "id": "file:web/src/view/vmContext.ts", @@ -414,7 +534,8 @@ "functionIds": [ "func:web/src/view/vmContext.ts#useGraphVM@6", "func:web/src/view/vmContext.ts#@0" - ] + ], + "summary": "提供 GraphViewModel 的 React Context" }, { "id": "file:web/src/view/components/FlowNode.tsx", @@ -435,7 +556,8 @@ "func:web/src/view/components/FlowNode.tsx#interactionBadges@12", "func:web/src/view/components/FlowNode.tsx#memo() 回调@40@40", "func:web/src/view/components/FlowNode.tsx#@0" - ] + ], + "summary": "自定义流程图节点:显示函数名 / 归属 / 交互徽标" }, { "id": "file:web/src/view/components/FlowGraph.tsx", @@ -444,18 +566,19 @@ "location": { "file": "web/src/view/components/FlowGraph.tsx", "startLine": 1, - "endLine": 73 + "endLine": 71 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src/view/components", "path": "web/src/view/components/FlowGraph.tsx", - "loc": 73, + "loc": 71, "containerIds": [], "functionIds": [ - "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "func:web/src/view/components/FlowGraph.tsx#@0" - ] + ], + "summary": "React Flow 画布容器,渲染当前流程图" }, { "id": "file:web/src/view/components/FlowList.tsx", @@ -464,18 +587,19 @@ "location": { "file": "web/src/view/components/FlowList.tsx", "startLine": 1, - "endLine": 26 + "endLine": 39 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src/view/components", "path": "web/src/view/components/FlowList.tsx", - "loc": 26, + "loc": 39, "containerIds": [], "functionIds": [ "func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", "func:web/src/view/components/FlowList.tsx#@0" - ] + ], + "summary": "左侧流程列表面板" }, { "id": "file:web/src/view/components/GroupList.tsx", @@ -495,7 +619,8 @@ "functionIds": [ "func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", "func:web/src/view/components/GroupList.tsx#@0" - ] + ], + "summary": "最左侧分组列表:按类 / 文件过滤流程" }, { "id": "file:web/src/view/components/NodeDetailPanel.tsx", @@ -504,19 +629,20 @@ "location": { "file": "web/src/view/components/NodeDetailPanel.tsx", "startLine": 1, - "endLine": 86 + "endLine": 99 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src/view/components", "path": "web/src/view/components/NodeDetailPanel.tsx", - "loc": 86, + "loc": 99, "containerIds": [], "functionIds": [ "func:web/src/view/components/NodeDetailPanel.tsx#describeInteraction@5", "func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13", "func:web/src/view/components/NodeDetailPanel.tsx#@0" - ] + ], + "summary": "右侧节点详情:位置 / 归属 / 交互 / 所在流程" }, { "id": "file:web/src/view/components/SearchBar.tsx", @@ -539,7 +665,70 @@ "func:web/src/view/components/SearchBar.tsx#匿名函数@26@26", "func:web/src/view/components/SearchBar.tsx#匿名函数@41@41", "func:web/src/view/components/SearchBar.tsx#@0" - ] + ], + "summary": "顶部搜索栏,检索流程与函数" + }, + { + "id": "file:web/src/services/languageMap.ts", + "kind": "file", + "name": "languageMap.ts", + "location": { + "file": "web/src/services/languageMap.ts", + "startLine": 1, + "endLine": 49 + }, + "language": "typescript", + "provenance": "ast", + "moduleId": "module:web/src/services", + "path": "web/src/services/languageMap.ts", + "loc": 49, + "containerIds": [], + "functionIds": [ + "func:web/src/services/languageMap.ts#languageOf@42" + ], + "summary": "文件扩展名到语法高亮语言的映射" + }, + { + "id": "file:web/src/view/components/CodePanel.tsx", + "kind": "file", + "name": "CodePanel.tsx", + "location": { + "file": "web/src/view/components/CodePanel.tsx", + "startLine": 1, + "endLine": 67 + }, + "language": "tsx", + "provenance": "ast", + "moduleId": "module:web/src/view/components", + "path": "web/src/view/components/CodePanel.tsx", + "loc": 67, + "containerIds": [], + "functionIds": [ + "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "func:web/src/view/components/CodePanel.tsx#@0" + ], + "summary": "底部源码面板:语法高亮显示选中函数实现" + }, + { + "id": "file:web/src/view/components/FlowBanner.tsx", + "kind": "file", + "name": "FlowBanner.tsx", + "location": { + "file": "web/src/view/components/FlowBanner.tsx", + "startLine": 1, + "endLine": 17 + }, + "language": "tsx", + "provenance": "ast", + "moduleId": "module:web/src/view/components", + "path": "web/src/view/components/FlowBanner.tsx", + "loc": 17, + "containerIds": [], + "functionIds": [ + "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "func:web/src/view/components/FlowBanner.tsx#@0" + ], + "summary": "画布顶部横幅:显示当前流程名与说明" }, { "id": "file:web/src/view/App.tsx", @@ -548,18 +737,19 @@ "location": { "file": "web/src/view/App.tsx", "startLine": 1, - "endLine": 55 + "endLine": 61 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src/view", "path": "web/src/view/App.tsx", - "loc": 55, + "loc": 61, "containerIds": [], "functionIds": [ - "func:web/src/view/App.tsx#observer() 回调@9@9", + "func:web/src/view/App.tsx#observer() 回调@11@11", "func:web/src/view/App.tsx#@0" - ] + ], + "summary": "应用主组件:编排头部与各面板布局" }, { "id": "file:web/src/main.tsx", @@ -568,17 +758,18 @@ "location": { "file": "web/src/main.tsx", "startLine": 1, - "endLine": 26 + "endLine": 27 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src", "path": "web/src/main.tsx", - "loc": 26, + "loc": 27, "containerIds": [], "functionIds": [ "func:web/src/main.tsx#@0" - ] + ], + "summary": "前端入口:构建数据源与 ViewModel 并挂载 App" }, { "id": "file:web/vite.config.ts", @@ -587,17 +778,20 @@ "location": { "file": "web/vite.config.ts", "startLine": 1, - "endLine": 11 + "endLine": 61 }, "language": "typescript", "provenance": "ast", "moduleId": "module:web", "path": "web/vite.config.ts", - "loc": 11, + "loc": 61, "containerIds": [], "functionIds": [ + "func:web/vite.config.ts#sourceServer@11", + "func:web/vite.config.ts#use() 回调@15@15", "func:web/vite.config.ts#@0" - ] + ], + "summary": "Vite 配置 + 开发期源码服务中间件" } ], "containers": [ @@ -621,17 +815,18 @@ "func:web/src/model/reportRepository.ts#HttpReportRepository.constructor@17", "func:web/src/model/reportRepository.ts#HttpReportRepository.load@19" ], - "fieldIds": [] + "fieldIds": [], + "summary": "通过 HTTP 从 URL 加载报告 JSON 的仓库实现" }, { - "id": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "id": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "kind": "container", "name": "GraphViewModel", "qualifiedName": "GraphViewModel", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 43, - "endLine": 289, + "startLine": 52, + "endLine": 380, "startCol": 1, "endCol": 2 }, @@ -640,42 +835,49 @@ "fileId": "file:web/src/viewmodel/GraphViewModel.ts", "moduleId": "module:web/src/viewmodel", "functionIds": [ - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.constructor@58", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.sources@66", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentSource@70", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flows@160", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentFlow@165", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFunc@194", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flowsForSelectedFunc@202", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcIdsInFlows@225", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchResults@240", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286" + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.constructor@69", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.sources@85", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentSource@89", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroup@153", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flows@214", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentFlow@219", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFunc@248", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flowsForSelectedFunc@256", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcIdsInFlows@279", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchResults@294", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346" ], "fieldIds": [ - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loading@44", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.error@45", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.report@46", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedSourceId@48", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroupId@49", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFlowId@50", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedNodeId@51", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchQuery@52", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcById@54", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.containerById@55", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.fileById@56" - ] + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loading@53", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.error@54", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.report@55", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedSourceId@57", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroupId@58", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFlowId@59", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedNodeId@60", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchQuery@61", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.codeState@62", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.codePanelCollapsed@63", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcById@65", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.containerById@66", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.fileById@67" + ], + "summary": "图视图 ViewModel:持有全部可观察状态与命令" } ], "functions": [ @@ -1841,6 +2043,251 @@ "returnType": "void", "loc": 29 }, + { + "id": "func:src/overview.ts#apiInOf@79", + "kind": "function", + "name": "apiInOf", + "qualifiedName": "apiInOf", + "location": { + "file": "src/overview.ts", + "startLine": 79, + "endLine": 81, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "apiInOf(fn: Func): Interaction | undefined", + "params": [ + { + "name": "fn", + "typeRef": "Func", + "optional": false + } + ], + "returnType": "Interaction | undefined", + "loc": 3 + }, + { + "id": "func:src/overview.ts#groupKeyFromPath@83", + "kind": "function", + "name": "groupKeyFromPath", + "qualifiedName": "groupKeyFromPath", + "location": { + "file": "src/overview.ts", + "startLine": 83, + "endLine": 89, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "groupKeyFromPath(path: string): string", + "params": [ + { + "name": "path", + "typeRef": "string", + "optional": false + } + ], + "returnType": "string", + "loc": 7 + }, + { + "id": "func:src/overview.ts#reachableFuncs@92", + "kind": "function", + "name": "reachableFuncs", + "qualifiedName": "reachableFuncs", + "location": { + "file": "src/overview.ts", + "startLine": 92, + "endLine": 105, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "reachableFuncs(startId: string, adjacency: Map, limit): Set", + "params": [ + { + "name": "startId", + "typeRef": "string", + "optional": false + }, + { + "name": "adjacency", + "typeRef": "Map", + "optional": false + }, + { + "name": "limit", + "optional": true, + "defaultValue": "2000" + } + ], + "returnType": "Set", + "loc": 14 + }, + { + "id": "func:src/overview.ts#deriveOverview@109", + "kind": "function", + "name": "deriveOverview", + "qualifiedName": "deriveOverview", + "location": { + "file": "src/overview.ts", + "startLine": 109, + "endLine": 261, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "deriveOverview(report: Report): Overview", + "params": [ + { + "name": "report", + "typeRef": "Report", + "optional": false + } + ], + "returnType": "Overview", + "loc": 153 + }, + { + "id": "func:src/overview.ts#analyzeEntry@140", + "kind": "function", + "name": "analyzeEntry", + "qualifiedName": "analyzeEntry", + "location": { + "file": "src/overview.ts", + "startLine": 140, + "endLine": 155, + "startCol": 9, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "analyzeEntry(fn: Func)", + "params": [ + { + "name": "fn", + "typeRef": "Func", + "optional": false + } + ], + "loc": 16 + }, + { + "id": "func:src/overview.ts#map() 回调@219@219", + "kind": "function", + "name": "map() 回调@219", + "qualifiedName": "map() 回调@219", + "location": { + "file": "src/overview.ts", + "startLine": 219, + "endLine": 222, + "startCol": 10, + "endCol": 6 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "map() 回调@219([funcId, count])", + "params": [ + { + "name": "[funcId, count]", + "optional": false + } + ], + "loc": 4 + }, + { + "id": "func:src/overview.ts#map() 回调@229@229", + "kind": "function", + "name": "map() 回调@229", + "qualifiedName": "map() 回调@229", + "location": { + "file": "src/overview.ts", + "startLine": 229, + "endLine": 239, + "startCol": 10, + "endCol": 6 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "map() 回调@229(fl)", + "params": [ + { + "name": "fl", + "optional": false + } + ], + "loc": 11 + }, + { + "id": "func:src/overview-cli.ts#main@9", + "kind": "function", + "name": "main", + "qualifiedName": "main", + "location": { + "file": "src/overview-cli.ts", + "startLine": 9, + "endLine": 84, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview-cli.ts", + "moduleId": "module:src", + "signature": "main()", + "params": [], + "loc": 76 + }, + { + "id": "func:src/overview-cli.ts#flag@16", + "kind": "function", + "name": "flag", + "qualifiedName": "flag", + "location": { + "file": "src/overview-cli.ts", + "startLine": 16, + "endLine": 21, + "startCol": 9, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview-cli.ts", + "moduleId": "module:src", + "signature": "flag(db: boolean, svc: boolean)", + "params": [ + { + "name": "db", + "typeRef": "boolean", + "optional": false + }, + { + "name": "svc", + "typeRef": "boolean", + "optional": false + } + ], + "loc": 6 + }, { "id": "func:web/src/model/reportRepository.ts#HttpReportRepository.constructor@17", "kind": "function", @@ -2055,14 +2502,62 @@ "loc": 32 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "id": "func:web/src/services/sourceService.ts#fetchSource@19", + "kind": "function", + "name": "fetchSource", + "qualifiedName": "fetchSource", + "location": { + "file": "web/src/services/sourceService.ts", + "startLine": 19, + "endLine": 32, + "startCol": 14, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/services/sourceService.ts", + "moduleId": "module:web/src/services", + "signature": "fetchSource(root, file, startLine, endLine)", + "params": [ + { + "name": "root", + "optional": false + }, + { + "name": "file", + "optional": false + }, + { + "name": "startLine", + "optional": false + }, + { + "name": "endLine", + "optional": false + } + ], + "loc": 14, + "interactions": [ + { + "id": "ix:func:web/src/services/sourceService.ts#fetchSource@19:api:out:GET:`/__source?${params.toString()}`", + "kind": "api", + "direction": "out", + "provenance": "resolved", + "protocol": "http", + "method": "GET", + "path": "`/__source?${params.toString()}`" + } + ] + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "kind": "function", "name": "baseName", "qualifiedName": "baseName", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 34, - "endLine": 37, + "startLine": 43, + "endLine": 46, "startCol": 1, "endCol": 2 }, @@ -2082,23 +2577,23 @@ "loc": 4 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.constructor@58", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.constructor@69", "kind": "function", "name": "constructor", "qualifiedName": "GraphViewModel.constructor", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 58, - "endLine": 64, + "startLine": 69, + "endLine": 83, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", - "signature": "constructor(sourceList: ReportSource[], makeRepository: (url: string) => ReportRepository)", + "signature": "constructor(sourceList: ReportSource[], makeRepository: (url: string) => ReportRepository, loadSource: LoadSource)", "params": [ { "name": "sourceList", @@ -2109,26 +2604,31 @@ "name": "makeRepository", "typeRef": "(url: string) => ReportRepository", "optional": false + }, + { + "name": "loadSource", + "typeRef": "LoadSource", + "optional": false } ], - "loc": 7 + "loc": 15 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.sources@66", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.sources@85", "kind": "function", "name": "sources", "qualifiedName": "GraphViewModel.sources", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 66, - "endLine": 68, + "startLine": 85, + "endLine": 87, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "sources(): ReportSource[]", "params": [], @@ -2136,21 +2636,21 @@ "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentSource@70", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentSource@89", "kind": "function", "name": "currentSource", "qualifiedName": "GraphViewModel.currentSource", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 70, - "endLine": 72, + "startLine": 89, + "endLine": 91, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "currentSource(): ReportSource | null", "params": [], @@ -2158,21 +2658,21 @@ "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "kind": "function", "name": "selectReport", "qualifiedName": "GraphViewModel.selectReport", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 74, - "endLine": 83, + "startLine": 93, + "endLine": 102, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "selectReport(sourceId: string): void", "params": [ @@ -2186,21 +2686,43 @@ "loc": 10 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", "kind": "function", - "name": "groupOfFlow", - "qualifiedName": "GraphViewModel.groupOfFlow", + "name": "toggleCodePanel", + "qualifiedName": "GraphViewModel.toggleCodePanel", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 86, - "endLine": 97, + "startLine": 104, + "endLine": 106, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "toggleCodePanel(): void", + "params": [], + "returnType": "void", + "loc": 3 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", + "kind": "function", + "name": "groupOfFlow", + "qualifiedName": "GraphViewModel.groupOfFlow", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 109, + "endLine": 132, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "groupOfFlow(flow: Flow): FlowGroup", "params": [ @@ -2211,24 +2733,24 @@ } ], "returnType": "FlowGroup", - "loc": 12 + "loc": 24 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "kind": "function", "name": "groups", "qualifiedName": "GraphViewModel.groups", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 100, - "endLine": 109, + "startLine": 135, + "endLine": 144, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "groups(): FlowGroup[]", "params": [], @@ -2236,21 +2758,21 @@ "loc": 10 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "kind": "function", "name": "visibleFlows", "qualifiedName": "GraphViewModel.visibleFlows", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 112, - "endLine": 115, + "startLine": 147, + "endLine": 150, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "visibleFlows(): Flow[]", "params": [], @@ -2258,21 +2780,65 @@ "loc": 4 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroup@153", "kind": "function", - "name": "selectGroup", - "qualifiedName": "GraphViewModel.selectGroup", + "name": "selectedGroup", + "qualifiedName": "GraphViewModel.selectedGroup", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 117, - "endLine": 124, + "startLine": 153, + "endLine": 156, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "selectedGroup(): FlowGroup | null", + "params": [], + "returnType": "FlowGroup | null", + "loc": 4 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "kind": "function", + "name": "selectedFuncOwner", + "qualifiedName": "GraphViewModel.selectedFuncOwner", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 159, + "endLine": 169, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "selectedFuncOwner(): { name: string; kind: \"class\" | \"file\"; summary?: string } | null", + "params": [], + "returnType": "{ name: string; kind: \"class\" | \"file\"; summary?: string } | null", + "loc": 11 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", + "kind": "function", + "name": "selectGroup", + "qualifiedName": "GraphViewModel.selectGroup", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 171, + "endLine": 178, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "selectGroup(groupId: string | null): void", "params": [ @@ -2286,21 +2852,21 @@ "loc": 8 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "kind": "function", "name": "load", "qualifiedName": "GraphViewModel.load", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 126, - "endLine": 157, + "startLine": 180, + "endLine": 211, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "load(): Promise", "params": [], @@ -2308,123 +2874,13 @@ "loc": 32 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flows@160", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flows@214", "kind": "function", "name": "flows", "qualifiedName": "GraphViewModel.flows", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 160, - "endLine": 163, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "flows(): Flow[]", - "params": [], - "returnType": "Flow[]", - "loc": 4 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentFlow@165", - "kind": "function", - "name": "currentFlow", - "qualifiedName": "GraphViewModel.currentFlow", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 165, - "endLine": 168, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "currentFlow(): Flow | null", - "params": [], - "returnType": "Flow | null", - "loc": 4 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", - "kind": "function", - "name": "positionedGraph", - "qualifiedName": "GraphViewModel.positionedGraph", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 171, - "endLine": 192, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "positionedGraph(): PositionedGraph", - "params": [], - "returnType": "PositionedGraph", - "loc": 22 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFunc@194", - "kind": "function", - "name": "selectedFunc", - "qualifiedName": "GraphViewModel.selectedFunc", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 194, - "endLine": 199, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "selectedFunc(): Func | null", - "params": [], - "returnType": "Func | null", - "loc": 6 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flowsForSelectedFunc@202", - "kind": "function", - "name": "flowsForSelectedFunc", - "qualifiedName": "GraphViewModel.flowsForSelectedFunc", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 202, - "endLine": 208, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "flowsForSelectedFunc(): Flow[]", - "params": [], - "returnType": "Flow[]", - "loc": 7 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", - "kind": "function", - "name": "openFlowForFunc", - "qualifiedName": "GraphViewModel.openFlowForFunc", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 211, + "startLine": 214, "endLine": 217, "startCol": 3, "endCol": 4 @@ -2432,7 +2888,117 @@ "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "flows(): Flow[]", + "params": [], + "returnType": "Flow[]", + "loc": 4 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentFlow@219", + "kind": "function", + "name": "currentFlow", + "qualifiedName": "GraphViewModel.currentFlow", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 219, + "endLine": 222, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "currentFlow(): Flow | null", + "params": [], + "returnType": "Flow | null", + "loc": 4 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "kind": "function", + "name": "positionedGraph", + "qualifiedName": "GraphViewModel.positionedGraph", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 225, + "endLine": 246, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "positionedGraph(): PositionedGraph", + "params": [], + "returnType": "PositionedGraph", + "loc": 22 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFunc@248", + "kind": "function", + "name": "selectedFunc", + "qualifiedName": "GraphViewModel.selectedFunc", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 248, + "endLine": 253, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "selectedFunc(): Func | null", + "params": [], + "returnType": "Func | null", + "loc": 6 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flowsForSelectedFunc@256", + "kind": "function", + "name": "flowsForSelectedFunc", + "qualifiedName": "GraphViewModel.flowsForSelectedFunc", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 256, + "endLine": 262, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "flowsForSelectedFunc(): Flow[]", + "params": [], + "returnType": "Flow[]", + "loc": 7 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", + "kind": "function", + "name": "openFlowForFunc", + "qualifiedName": "GraphViewModel.openFlowForFunc", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 265, + "endLine": 271, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "openFlowForFunc(flowId: string, funcId: string): void", "params": [ @@ -2451,21 +3017,21 @@ "loc": 7 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "kind": "function", "name": "setSearchQuery", "qualifiedName": "GraphViewModel.setSearchQuery", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 221, - "endLine": 223, + "startLine": 275, + "endLine": 277, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "setSearchQuery(q: string): void", "params": [ @@ -2479,21 +3045,21 @@ "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcIdsInFlows@225", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcIdsInFlows@279", "kind": "function", "name": "funcIdsInFlows", "qualifiedName": "GraphViewModel.funcIdsInFlows", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 225, - "endLine": 231, + "startLine": 279, + "endLine": 285, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "funcIdsInFlows(): Set", "params": [], @@ -2501,21 +3067,21 @@ "loc": 7 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "kind": "function", "name": "biggestFlowForFunc", "qualifiedName": "GraphViewModel.biggestFlowForFunc", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 233, - "endLine": 238, + "startLine": 287, + "endLine": 292, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "biggestFlowForFunc(funcId: string): Flow | null", "params": [ @@ -2529,21 +3095,21 @@ "loc": 6 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchResults@240", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchResults@294", "kind": "function", "name": "searchResults", "qualifiedName": "GraphViewModel.searchResults", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 240, - "endLine": 266, + "startLine": 294, + "endLine": 320, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "searchResults(): SearchResult[]", "params": [], @@ -2551,21 +3117,21 @@ "loc": 27 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "kind": "function", "name": "activateSearchResult", "qualifiedName": "GraphViewModel.activateSearchResult", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 268, - "endLine": 278, + "startLine": 322, + "endLine": 332, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "activateSearchResult(result: SearchResult): void", "params": [ @@ -2579,21 +3145,21 @@ "loc": 11 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "kind": "function", "name": "selectFlow", "qualifiedName": "GraphViewModel.selectFlow", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 280, - "endLine": 284, + "startLine": 334, + "endLine": 338, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "selectFlow(flowId: string): void", "params": [ @@ -2607,21 +3173,21 @@ "loc": 5 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "kind": "function", "name": "selectNode", "qualifiedName": "GraphViewModel.selectNode", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 286, - "endLine": 288, + "startLine": 340, + "endLine": 342, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "selectNode(nodeId: string | null): void", "params": [ @@ -2635,14 +3201,56 @@ "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@149@149", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", "kind": "function", - "name": "runInAction() 回调@149", - "qualifiedName": "runInAction() 回调@149", + "name": "loadSelectedFuncCode", + "qualifiedName": "GraphViewModel.loadSelectedFuncCode", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 149, - "endLine": 151, + "startLine": 346, + "endLine": 379, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "loadSelectedFuncCode(): Promise", + "params": [], + "returnType": "Promise", + "loc": 34 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "kind": "function", + "name": "reaction() 回调@79", + "qualifiedName": "reaction() 回调@79", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 79, + "endLine": 81, + "startCol": 7, + "endCol": 8 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "moduleId": "module:web/src/viewmodel", + "signature": "reaction() 回调@79()", + "params": [], + "loc": 3 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@203@203", + "kind": "function", + "name": "runInAction() 回调@203", + "qualifiedName": "runInAction() 回调@203", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 203, + "endLine": 205, "startCol": 19, "endCol": 8 }, @@ -2650,19 +3258,19 @@ "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", "moduleId": "module:web/src/viewmodel", - "signature": "runInAction() 回调@149()", + "signature": "runInAction() 回调@203()", "params": [], "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#map() 回调@186@186", + "id": "func:web/src/viewmodel/GraphViewModel.ts#map() 回调@240@240", "kind": "function", - "name": "map() 回调@186", - "qualifiedName": "map() 回调@186", + "name": "map() 回调@240", + "qualifiedName": "map() 回调@240", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 186, - "endLine": 189, + "startLine": 240, + "endLine": 243, "startCol": 53, "endCol": 6 }, @@ -2670,7 +3278,7 @@ "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", "moduleId": "module:web/src/viewmodel", - "signature": "map() 回调@186(n)", + "signature": "map() 回调@240(n)", "params": [ { "name": "n", @@ -2679,6 +3287,26 @@ ], "loc": 4 }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@368@368", + "kind": "function", + "name": "runInAction() 回调@368", + "qualifiedName": "runInAction() 回调@368", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 368, + "endLine": 377, + "startCol": 19, + "endCol": 8 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "moduleId": "module:web/src/viewmodel", + "signature": "runInAction() 回调@368()", + "params": [], + "loc": 10 + }, { "id": "func:web/src/view/vmContext.ts#useGraphVM@6", "kind": "function", @@ -2754,14 +3382,14 @@ "loc": 27 }, { - "id": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "id": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "kind": "function", - "name": "observer() 回调@17", - "qualifiedName": "observer() 回调@17", + "name": "observer() 回调@16", + "qualifiedName": "observer() 回调@16", "location": { "file": "web/src/view/components/FlowGraph.tsx", - "startLine": 17, - "endLine": 72, + "startLine": 16, + "endLine": 70, "startCol": 35, "endCol": 2 }, @@ -2769,9 +3397,9 @@ "provenance": "ast", "fileId": "file:web/src/view/components/FlowGraph.tsx", "moduleId": "module:web/src/view/components", - "signature": "observer() 回调@17()", + "signature": "observer() 回调@16()", "params": [], - "loc": 56 + "loc": 55 }, { "id": "func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", @@ -2781,7 +3409,7 @@ "location": { "file": "web/src/view/components/FlowList.tsx", "startLine": 4, - "endLine": 25, + "endLine": 38, "startCol": 34, "endCol": 2 }, @@ -2791,7 +3419,7 @@ "moduleId": "module:web/src/view/components", "signature": "observer() 回调@4()", "params": [], - "loc": 22 + "loc": 35 }, { "id": "func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", @@ -2848,7 +3476,7 @@ "location": { "file": "web/src/view/components/NodeDetailPanel.tsx", "startLine": 13, - "endLine": 85, + "endLine": 98, "startCol": 41, "endCol": 2 }, @@ -2858,7 +3486,7 @@ "moduleId": "module:web/src/view/components", "signature": "observer() 回调@13()", "params": [], - "loc": 73 + "loc": 86 }, { "id": "func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6", @@ -2958,14 +3586,81 @@ "loc": 4 }, { - "id": "func:web/src/view/App.tsx#observer() 回调@9@9", + "id": "func:web/src/services/languageMap.ts#languageOf@42", "kind": "function", - "name": "observer() 回调@9", - "qualifiedName": "observer() 回调@9", + "name": "languageOf", + "qualifiedName": "languageOf", + "location": { + "file": "web/src/services/languageMap.ts", + "startLine": 42, + "endLine": 48, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/services/languageMap.ts", + "moduleId": "module:web/src/services", + "signature": "languageOf(file: string | undefined): string", + "params": [ + { + "name": "file", + "typeRef": "string | undefined", + "optional": false + } + ], + "returnType": "string", + "loc": 7 + }, + { + "id": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "kind": "function", + "name": "observer() 回调@7", + "qualifiedName": "observer() 回调@7", + "location": { + "file": "web/src/view/components/CodePanel.tsx", + "startLine": 7, + "endLine": 66, + "startCol": 35, + "endCol": 2 + }, + "language": "tsx", + "provenance": "ast", + "fileId": "file:web/src/view/components/CodePanel.tsx", + "moduleId": "module:web/src/view/components", + "signature": "observer() 回调@7()", + "params": [], + "loc": 60 + }, + { + "id": "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "kind": "function", + "name": "observer() 回调@4", + "qualifiedName": "observer() 回调@4", + "location": { + "file": "web/src/view/components/FlowBanner.tsx", + "startLine": 4, + "endLine": 16, + "startCol": 36, + "endCol": 2 + }, + "language": "tsx", + "provenance": "ast", + "fileId": "file:web/src/view/components/FlowBanner.tsx", + "moduleId": "module:web/src/view/components", + "signature": "observer() 回调@4()", + "params": [], + "loc": 13 + }, + { + "id": "func:web/src/view/App.tsx#observer() 回调@11@11", + "kind": "function", + "name": "observer() 回调@11", + "qualifiedName": "observer() 回调@11", "location": { "file": "web/src/view/App.tsx", - "startLine": 9, - "endLine": 54, + "startLine": 11, + "endLine": 60, "startCol": 29, "endCol": 2 }, @@ -2973,9 +3668,76 @@ "provenance": "ast", "fileId": "file:web/src/view/App.tsx", "moduleId": "module:web/src/view", - "signature": "observer() 回调@9()", + "signature": "observer() 回调@11()", "params": [], - "loc": 46 + "loc": 50 + }, + { + "id": "func:web/vite.config.ts#sourceServer@11", + "kind": "function", + "name": "sourceServer", + "qualifiedName": "sourceServer", + "location": { + "file": "web/vite.config.ts", + "startLine": 11, + "endLine": 52, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/vite.config.ts", + "moduleId": "module:web", + "signature": "sourceServer(): Plugin", + "params": [], + "returnType": "Plugin", + "loc": 42 + }, + { + "id": "func:web/vite.config.ts#use() 回调@15@15", + "kind": "function", + "name": "use() 回调@15", + "qualifiedName": "use() 回调@15", + "location": { + "file": "web/vite.config.ts", + "startLine": 15, + "endLine": 49, + "startCol": 43, + "endCol": 8 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/vite.config.ts", + "moduleId": "module:web", + "signature": "use() 回调@15(req, res)", + "params": [ + { + "name": "req", + "optional": false + }, + { + "name": "res", + "optional": false + } + ], + "loc": 35 + }, + { + "id": "func:scripts/annotate-self-report.mjs#@0", + "kind": "function", + "name": "", + "qualifiedName": "", + "location": { + "file": "scripts/annotate-self-report.mjs", + "startLine": 1, + "endLine": 119 + }, + "language": "javascript", + "provenance": "ast", + "fileId": "file:scripts/annotate-self-report.mjs", + "moduleId": "module:scripts", + "params": [], + "summary": "模块顶层代码(副作用/初始化)" }, { "id": "func:src/cli.ts#@0", @@ -2994,6 +3756,23 @@ "params": [], "summary": "模块顶层代码(副作用/初始化)" }, + { + "id": "func:src/overview-cli.ts#@0", + "kind": "function", + "name": "", + "qualifiedName": "", + "location": { + "file": "src/overview-cli.ts", + "startLine": 1, + "endLine": 87 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview-cli.ts", + "moduleId": "module:src", + "params": [], + "summary": "模块顶层代码(副作用/初始化)" + }, { "id": "func:web/src/view/vmContext.ts#@0", "kind": "function", @@ -3036,7 +3815,7 @@ "location": { "file": "web/src/view/components/FlowGraph.tsx", "startLine": 1, - "endLine": 73 + "endLine": 71 }, "language": "tsx", "provenance": "ast", @@ -3053,7 +3832,7 @@ "location": { "file": "web/src/view/components/FlowList.tsx", "startLine": 1, - "endLine": 26 + "endLine": 39 }, "language": "tsx", "provenance": "ast", @@ -3087,7 +3866,7 @@ "location": { "file": "web/src/view/components/NodeDetailPanel.tsx", "startLine": 1, - "endLine": 86 + "endLine": 99 }, "language": "tsx", "provenance": "ast", @@ -3113,6 +3892,40 @@ "params": [], "summary": "模块顶层代码(副作用/初始化)" }, + { + "id": "func:web/src/view/components/CodePanel.tsx#@0", + "kind": "function", + "name": "", + "qualifiedName": "", + "location": { + "file": "web/src/view/components/CodePanel.tsx", + "startLine": 1, + "endLine": 67 + }, + "language": "tsx", + "provenance": "ast", + "fileId": "file:web/src/view/components/CodePanel.tsx", + "moduleId": "module:web/src/view/components", + "params": [], + "summary": "模块顶层代码(副作用/初始化)" + }, + { + "id": "func:web/src/view/components/FlowBanner.tsx#@0", + "kind": "function", + "name": "", + "qualifiedName": "", + "location": { + "file": "web/src/view/components/FlowBanner.tsx", + "startLine": 1, + "endLine": 17 + }, + "language": "tsx", + "provenance": "ast", + "fileId": "file:web/src/view/components/FlowBanner.tsx", + "moduleId": "module:web/src/view/components", + "params": [], + "summary": "模块顶层代码(副作用/初始化)" + }, { "id": "func:web/src/view/App.tsx#@0", "kind": "function", @@ -3121,7 +3934,7 @@ "location": { "file": "web/src/view/App.tsx", "startLine": 1, - "endLine": 55 + "endLine": 61 }, "language": "tsx", "provenance": "ast", @@ -3138,7 +3951,7 @@ "location": { "file": "web/src/main.tsx", "startLine": 1, - "endLine": 26 + "endLine": 27 }, "language": "tsx", "provenance": "ast", @@ -3156,7 +3969,7 @@ "location": { "file": "web/vite.config.ts", "startLine": 1, - "endLine": 11 + "endLine": 61 }, "language": "typescript", "provenance": "ast", @@ -3168,186 +3981,219 @@ ], "fields": [ { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loading@44", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loading@53", "kind": "field", "name": "loading", "qualifiedName": "GraphViewModel.loading", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 44, - "endLine": 44, + "startLine": 53, + "endLine": 53, "startCol": 3, "endCol": 19 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.error@45", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.error@54", "kind": "field", "name": "error", "qualifiedName": "GraphViewModel.error", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 45, - "endLine": 45, + "startLine": 54, + "endLine": 54, "startCol": 3, "endCol": 31 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.report@46", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.report@55", "kind": "field", "name": "report", "qualifiedName": "GraphViewModel.report", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 46, - "endLine": 46, + "startLine": 55, + "endLine": 55, "startCol": 3, "endCol": 32 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "Report | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedSourceId@48", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedSourceId@57", "kind": "field", "name": "selectedSourceId", "qualifiedName": "GraphViewModel.selectedSourceId", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 48, - "endLine": 48, + "startLine": 57, + "endLine": 57, "startCol": 3, "endCol": 42 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroupId@49", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroupId@58", "kind": "field", "name": "selectedGroupId", "qualifiedName": "GraphViewModel.selectedGroupId", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 49, - "endLine": 49, + "startLine": 58, + "endLine": 58, "startCol": 3, "endCol": 41 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFlowId@50", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFlowId@59", "kind": "field", "name": "selectedFlowId", "qualifiedName": "GraphViewModel.selectedFlowId", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 50, - "endLine": 50, + "startLine": 59, + "endLine": 59, "startCol": 3, "endCol": 40 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedNodeId@51", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedNodeId@60", "kind": "field", "name": "selectedNodeId", "qualifiedName": "GraphViewModel.selectedNodeId", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 51, - "endLine": 51, + "startLine": 60, + "endLine": 60, "startCol": 3, "endCol": 40 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchQuery@52", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchQuery@61", "kind": "field", "name": "searchQuery", "qualifiedName": "GraphViewModel.searchQuery", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 52, - "endLine": 52, + "startLine": 61, + "endLine": 61, "startCol": 3, "endCol": 20 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcById@54", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.codeState@62", + "kind": "field", + "name": "codeState", + "qualifiedName": "GraphViewModel.codeState", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 62, + "endLine": 62, + "startCol": 3, + "endCol": 87 + }, + "language": "typescript", + "provenance": "ast", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "typeRef": "CodeState" + }, + { + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.codePanelCollapsed@63", + "kind": "field", + "name": "codePanelCollapsed", + "qualifiedName": "GraphViewModel.codePanelCollapsed", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 63, + "endLine": 63, + "startCol": 3, + "endCol": 30 + }, + "language": "typescript", + "provenance": "ast", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" + }, + { + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcById@65", "kind": "field", "name": "funcById", "qualifiedName": "GraphViewModel.funcById", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 54, - "endLine": 54, + "startLine": 65, + "endLine": 65, "startCol": 3, "endCol": 46 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.containerById@55", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.containerById@66", "kind": "field", "name": "containerById", "qualifiedName": "GraphViewModel.containerById", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 55, - "endLine": 55, + "startLine": 66, + "endLine": 66, "startCol": 3, "endCol": 56 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.fileById@56", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.fileById@67", "kind": "field", "name": "fileById", "qualifiedName": "GraphViewModel.fileById", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 56, - "endLine": 56, + "startLine": 67, + "endLine": 67, "startCol": 3, "endCol": 50 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" } ], "edges": [ @@ -3736,6 +4582,83 @@ "targetId": "func:src/cli.ts#main@40", "provenance": "resolved" }, + { + "id": "edge:import:file:src/overview.ts=>file:src/types.ts", + "kind": "import", + "sourceId": "file:src/overview.ts", + "targetId": "file:src/types.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:src/overview.ts#analyzeEntry@140=>func:src/overview.ts#reachableFuncs@92", + "kind": "call", + "sourceId": "func:src/overview.ts#analyzeEntry@140", + "targetId": "func:src/overview.ts#reachableFuncs@92", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#apiInOf@79", + "kind": "call", + "sourceId": "func:src/overview.ts#deriveOverview@109", + "targetId": "func:src/overview.ts#apiInOf@79", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#analyzeEntry@140", + "kind": "call", + "sourceId": "func:src/overview.ts#deriveOverview@109", + "targetId": "func:src/overview.ts#analyzeEntry@140", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#groupKeyFromPath@83", + "kind": "call", + "sourceId": "func:src/overview.ts#deriveOverview@109", + "targetId": "func:src/overview.ts#groupKeyFromPath@83", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview.ts#map() 回调@229@229=>func:src/overview.ts#analyzeEntry@140", + "kind": "call", + "sourceId": "func:src/overview.ts#map() 回调@229@229", + "targetId": "func:src/overview.ts#analyzeEntry@140", + "provenance": "resolved" + }, + { + "id": "edge:import:file:src/overview-cli.ts=>file:src/overview.ts", + "kind": "import", + "sourceId": "file:src/overview-cli.ts", + "targetId": "file:src/overview.ts", + "provenance": "ast" + }, + { + "id": "edge:import:file:src/overview-cli.ts=>file:src/types.ts", + "kind": "import", + "sourceId": "file:src/overview-cli.ts", + "targetId": "file:src/types.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:src/overview-cli.ts#main@9=>func:src/overview.ts#deriveOverview@109", + "kind": "call", + "sourceId": "func:src/overview-cli.ts#main@9", + "targetId": "func:src/overview.ts#deriveOverview@109", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview-cli.ts#main@9=>func:src/overview-cli.ts#flag@16", + "kind": "call", + "sourceId": "func:src/overview-cli.ts#main@9", + "targetId": "func:src/overview-cli.ts#flag@16", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview-cli.ts#@0=>func:src/overview-cli.ts#main@9", + "kind": "call", + "sourceId": "func:src/overview-cli.ts#@0", + "targetId": "func:src/overview-cli.ts#main@9", + "provenance": "resolved" + }, { "id": "edge:import:file:web/src/model/reportRepository.ts=>file:web/src/model/report.ts", "kind": "import", @@ -3793,59 +4716,80 @@ "provenance": "ast" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "edge:import:file:web/src/viewmodel/GraphViewModel.ts=>file:web/src/services/sourceService.ts", + "kind": "import", + "sourceId": "file:web/src/viewmodel/GraphViewModel.ts", + "targetId": "file:web/src/services/sourceService.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86=>func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171=>func:web/src/model/graphModel.ts#flowToGraph@43", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "kind": "call", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225=>func:web/src/model/graphModel.ts#flowToGraph@43", + "kind": "call", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", "targetId": "func:web/src/model/graphModel.ts#flowToGraph@43", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171=>func:web/src/services/dagreLayout.ts#computeLayout@31", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225=>func:web/src/services/dagreLayout.ts#computeLayout@31", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", "targetId": "func:web/src/services/dagreLayout.ts#computeLayout@31", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "provenance": "resolved" }, { @@ -3884,17 +4828,17 @@ "provenance": "ast" }, { - "id": "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17=>func:web/src/view/vmContext.ts#useGraphVM@6", + "id": "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16=>func:web/src/view/vmContext.ts#useGraphVM@6", "kind": "call", - "sourceId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "sourceId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "targetId": "func:web/src/view/vmContext.ts#useGraphVM@6", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "id": "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "kind": "call", - "sourceId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "sourceId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "provenance": "resolved" }, { @@ -3912,10 +4856,10 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "id": "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "kind": "call", "sourceId": "func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "provenance": "resolved" }, { @@ -3933,10 +4877,10 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "id": "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", "kind": "call", "sourceId": "func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", "provenance": "resolved" }, { @@ -3968,10 +4912,10 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "kind": "call", "sourceId": "func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "provenance": "resolved" }, { @@ -3996,17 +4940,17 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "id": "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "kind": "call", "sourceId": "func:web/src/view/components/SearchBar.tsx#activate@12", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "kind": "call", "sourceId": "func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "provenance": "resolved" }, { @@ -4017,10 +4961,10 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "kind": "call", "sourceId": "func:web/src/view/components/SearchBar.tsx#匿名函数@26@26", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "provenance": "resolved" }, { @@ -4030,6 +4974,55 @@ "targetId": "func:web/src/view/components/SearchBar.tsx#activate@12", "provenance": "resolved" }, + { + "id": "edge:import:file:web/src/view/components/CodePanel.tsx=>file:web/src/view/vmContext.ts", + "kind": "import", + "sourceId": "file:web/src/view/components/CodePanel.tsx", + "targetId": "file:web/src/view/vmContext.ts", + "provenance": "ast" + }, + { + "id": "edge:import:file:web/src/view/components/CodePanel.tsx=>file:web/src/services/languageMap.ts", + "kind": "import", + "sourceId": "file:web/src/view/components/CodePanel.tsx", + "targetId": "file:web/src/services/languageMap.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/view/vmContext.ts#useGraphVM@6", + "kind": "call", + "sourceId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "targetId": "func:web/src/view/vmContext.ts#useGraphVM@6", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/services/languageMap.ts#languageOf@42", + "kind": "call", + "sourceId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "targetId": "func:web/src/services/languageMap.ts#languageOf@42", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "kind": "call", + "sourceId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "provenance": "resolved" + }, + { + "id": "edge:import:file:web/src/view/components/FlowBanner.tsx=>file:web/src/view/vmContext.ts", + "kind": "import", + "sourceId": "file:web/src/view/components/FlowBanner.tsx", + "targetId": "file:web/src/view/vmContext.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4=>func:web/src/view/vmContext.ts#useGraphVM@6", + "kind": "call", + "sourceId": "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "targetId": "func:web/src/view/vmContext.ts#useGraphVM@6", + "provenance": "resolved" + }, { "id": "edge:import:file:web/src/view/App.tsx=>file:web/src/view/vmContext.ts", "kind": "import", @@ -4073,17 +5066,31 @@ "provenance": "ast" }, { - "id": "edge:call:func:web/src/view/App.tsx#observer() 回调@9@9=>func:web/src/view/vmContext.ts#useGraphVM@6", + "id": "edge:import:file:web/src/view/App.tsx=>file:web/src/view/components/CodePanel.tsx", + "kind": "import", + "sourceId": "file:web/src/view/App.tsx", + "targetId": "file:web/src/view/components/CodePanel.tsx", + "provenance": "ast" + }, + { + "id": "edge:import:file:web/src/view/App.tsx=>file:web/src/view/components/FlowBanner.tsx", + "kind": "import", + "sourceId": "file:web/src/view/App.tsx", + "targetId": "file:web/src/view/components/FlowBanner.tsx", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/view/App.tsx#observer() 回调@11@11=>func:web/src/view/vmContext.ts#useGraphVM@6", "kind": "call", - "sourceId": "func:web/src/view/App.tsx#observer() 回调@9@9", + "sourceId": "func:web/src/view/App.tsx#observer() 回调@11@11", "targetId": "func:web/src/view/vmContext.ts#useGraphVM@6", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/App.tsx#observer() 回调@9@9=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "id": "edge:call:func:web/src/view/App.tsx#observer() 回调@11@11=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "kind": "call", - "sourceId": "func:web/src/view/App.tsx#observer() 回调@9@9", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "sourceId": "func:web/src/view/App.tsx#observer() 回调@11@11", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "provenance": "resolved" }, { @@ -4115,10 +5122,24 @@ "provenance": "ast" }, { - "id": "edge:call:func:web/src/main.tsx#@0=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "edge:import:file:web/src/main.tsx=>file:web/src/services/sourceService.ts", + "kind": "import", + "sourceId": "file:web/src/main.tsx", + "targetId": "file:web/src/services/sourceService.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/main.tsx#@0=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "kind": "call", "sourceId": "func:web/src/main.tsx#@0", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/vite.config.ts#@0=>func:web/vite.config.ts#sourceServer@11", + "kind": "call", + "sourceId": "func:web/vite.config.ts#@0", + "targetId": "func:web/vite.config.ts#sourceServer@11", "provenance": "resolved" } ], @@ -4127,7 +5148,7 @@ "id": "flow:call:func:web/src/main.tsx#@0", "name": " (web/src/main.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/main.tsx#@0", "steps": [ { @@ -4136,27 +5157,27 @@ "nodeId": "func:web/src/main.tsx#@0", "title": "", "nextStepIds": [ - "step:flow:call:func:web/src/main.tsx#@0:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126" + "step:flow:call:func:web/src/main.tsx#@0:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180" ] }, { - "id": "step:flow:call:func:web/src/main.tsx#@0:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "step:flow:call:func:web/src/main.tsx#@0:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "order": 1, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "title": "GraphViewModel.load", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/main.tsx#@0=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126" + "edge:call:func:web/src/main.tsx#@0=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180" ], - "summary": "以 为入口的调用链,覆盖 2 个函数" + "summary": "前端启动:构建报告数据源与 ViewModel,挂载 React 应用" }, { "id": "flow:call:func:src/analyzer.ts#visit@245", "name": "visit (src/analyzer.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:src/analyzer.ts#visit@245", "steps": [ { @@ -4289,13 +5310,13 @@ "edge:call:func:src/analyzer.ts#visit@245=>func:src/analyzer.ts#containsCall@173", "edge:call:func:src/analyzer.ts#visit@245=>func:src/analyzer.ts#inlineName@202" ], - "summary": "以 visit 为入口的调用链,覆盖 13 个函数" + "summary": "AST 遍历核心:递归访问语法树,登记函数 / 容器并建立调用边" }, { "id": "flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13", "name": "observer() 回调@13 (web/src/view/components/NodeDetailPanel.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13", "steps": [ { @@ -4306,7 +5327,7 @@ "nextStepIds": [ "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/view/vmContext.ts#useGraphVM@6", "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/view/components/NodeDetailPanel.tsx#describeInteraction@5", - "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211" + "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265" ] }, { @@ -4324,9 +5345,9 @@ "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "order": 3, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "title": "GraphViewModel.openFlowForFunc", "nextStepIds": [] } @@ -4334,36 +5355,83 @@ "edgeIds": [ "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/view/vmContext.ts#useGraphVM@6", "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/view/components/NodeDetailPanel.tsx#describeInteraction@5", - "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211" + "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265" ], - "summary": "以 observer() 回调@13 为入口的调用链,覆盖 4 个函数" + "summary": "渲染右侧节点详情:位置、归属、交互、所在流程" }, { - "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", - "name": "GraphViewModel.positionedGraph (web/src/viewmodel/GraphViewModel.ts)", + "id": "flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "name": "observer() 回调@7 (web/src/view/components/CodePanel.tsx)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", + "provenance": "manual", + "entryNodeId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", "steps": [ { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", + "id": "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", "order": 0, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", - "title": "GraphViewModel.positionedGraph", + "nodeId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "title": "observer() 回调@7", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/model/graphModel.ts#flowToGraph@43", - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/services/dagreLayout.ts#computeLayout@31" + "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/view/vmContext.ts#useGraphVM@6", + "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/services/languageMap.ts#languageOf@42", + "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/model/graphModel.ts#flowToGraph@43", + "id": "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/view/vmContext.ts#useGraphVM@6", + "order": 1, + "nodeId": "func:web/src/view/vmContext.ts#useGraphVM@6", + "title": "useGraphVM", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/services/languageMap.ts#languageOf@42", + "order": 2, + "nodeId": "func:web/src/services/languageMap.ts#languageOf@42", + "title": "languageOf", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "order": 3, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "title": "GraphViewModel.toggleCodePanel", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/view/vmContext.ts#useGraphVM@6", + "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/services/languageMap.ts#languageOf@42", + "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104" + ], + "summary": "渲染底部源码面板:随选中节点显示语法高亮源码" + }, + { + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "name": "GraphViewModel.positionedGraph (web/src/viewmodel/GraphViewModel.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "steps": [ + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "order": 0, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "title": "GraphViewModel.positionedGraph", + "nextStepIds": [ + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/model/graphModel.ts#flowToGraph@43", + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/services/dagreLayout.ts#computeLayout@31" + ] + }, + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/model/graphModel.ts#flowToGraph@43", "order": 1, "nodeId": "func:web/src/model/graphModel.ts#flowToGraph@43", "title": "flowToGraph", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/services/dagreLayout.ts#computeLayout@31", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/services/dagreLayout.ts#computeLayout@31", "order": 2, "nodeId": "func:web/src/services/dagreLayout.ts#computeLayout@31", "title": "computeLayout", @@ -4371,54 +5439,54 @@ } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171=>func:web/src/model/graphModel.ts#flowToGraph@43", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171=>func:web/src/services/dagreLayout.ts#computeLayout@31" + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225=>func:web/src/model/graphModel.ts#flowToGraph@43", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225=>func:web/src/services/dagreLayout.ts#computeLayout@31" ], - "summary": "以 GraphViewModel.positionedGraph 为入口的调用链,覆盖 3 个函数" + "summary": "计算当前流程的带坐标中性图(含 Dagre 自动布局)" }, { - "id": "flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", - "name": "observer() 回调@17 (web/src/view/components/FlowGraph.tsx)", + "id": "flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", + "name": "observer() 回调@16 (web/src/view/components/FlowGraph.tsx)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "provenance": "manual", + "entryNodeId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "steps": [ { - "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "order": 0, - "nodeId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", - "title": "observer() 回调@17", + "nodeId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", + "title": "observer() 回调@16", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286" + "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/view/vmContext.ts#useGraphVM@6", + "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340" ] }, { - "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/view/vmContext.ts#useGraphVM@6", + "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/view/vmContext.ts#useGraphVM@6", "order": 1, "nodeId": "func:web/src/view/vmContext.ts#useGraphVM@6", "title": "useGraphVM", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "title": "GraphViewModel.selectNode", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286" + "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16=>func:web/src/view/vmContext.ts#useGraphVM@6", + "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340" ], - "summary": "以 observer() 回调@17 为入口的调用链,覆盖 3 个函数" + "summary": "把中性图适配为 React Flow 节点 / 边并渲染画布" }, { "id": "flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", "name": "observer() 回调@4 (web/src/view/components/FlowList.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", "steps": [ { @@ -4428,7 +5496,7 @@ "title": "observer() 回调@4", "nextStepIds": [ "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280" + "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334" ] }, { @@ -4439,24 +5507,24 @@ "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "id": "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "title": "GraphViewModel.selectFlow", "nextStepIds": [] } ], "edgeIds": [ "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280" + "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334" ], - "summary": "以 observer() 回调@4 为入口的调用链,覆盖 3 个函数" + "summary": "渲染流程列表并处理流程选择" }, { "id": "flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", "name": "observer() 回调@4 (web/src/view/components/GroupList.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", "steps": [ { @@ -4466,7 +5534,7 @@ "title": "observer() 回调@4", "nextStepIds": [ "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117" + "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171" ] }, { @@ -4477,24 +5545,24 @@ "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "id": "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", "title": "GraphViewModel.selectGroup", "nextStepIds": [] } ], "edgeIds": [ "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117" + "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171" ], - "summary": "以 observer() 回调@4 为入口的调用链,覆盖 3 个函数" + "summary": "渲染分组列表,按类 / 文件过滤流程" }, { "id": "flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6", "name": "observer() 回调@6 (web/src/view/components/SearchBar.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6", "steps": [ { @@ -4504,7 +5572,7 @@ "title": "observer() 回调@6", "nextStepIds": [ "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275" ] }, { @@ -4515,24 +5583,24 @@ "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "title": "GraphViewModel.setSearchQuery", "nextStepIds": [] } ], "edgeIds": [ "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221" + "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275" ], - "summary": "以 observer() 回调@6 为入口的调用链,覆盖 3 个函数" + "summary": "渲染搜索框与结果下拉" }, { "id": "flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26", "name": "匿名函数@26 (web/src/view/components/SearchBar.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/SearchBar.tsx#匿名函数@26@26", "steps": [ { @@ -4542,7 +5610,7 @@ "title": "匿名函数@26", "nextStepIds": [ "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/view/components/SearchBar.tsx#activate@12", - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275" ] }, { @@ -4551,103 +5619,103 @@ "nodeId": "func:web/src/view/components/SearchBar.tsx#activate@12", "title": "activate", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322" ] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "title": "GraphViewModel.setSearchQuery", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "order": 3, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "title": "GraphViewModel.activateSearchResult", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265" ] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "order": 4, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "title": "GraphViewModel.biggestFlowForFunc", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "order": 5, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "title": "GraphViewModel.openFlowForFunc", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", - "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", + "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/view/components/SearchBar.tsx#activate@12", - "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221" + "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275" ], - "summary": "以 匿名函数@26 为入口的调用链,覆盖 6 个函数" + "summary": "搜索输入变更处理回调" }, { - "id": "flow:call:func:web/src/view/App.tsx#observer() 回调@9@9", - "name": "observer() 回调@9 (web/src/view/App.tsx)", + "id": "flow:call:func:web/src/view/App.tsx#observer() 回调@11@11", + "name": "observer() 回调@11 (web/src/view/App.tsx)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/view/App.tsx#observer() 回调@9@9", + "provenance": "manual", + "entryNodeId": "func:web/src/view/App.tsx#observer() 回调@11@11", "steps": [ { - "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/view/App.tsx#observer() 回调@9@9", + "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/view/App.tsx#observer() 回调@11@11", "order": 0, - "nodeId": "func:web/src/view/App.tsx#observer() 回调@9@9", - "title": "observer() 回调@9", + "nodeId": "func:web/src/view/App.tsx#observer() 回调@11@11", + "title": "observer() 回调@11", "nextStepIds": [ - "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74" + "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/view/vmContext.ts#useGraphVM@6", + "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93" ] }, { - "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/view/vmContext.ts#useGraphVM@6", + "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/view/vmContext.ts#useGraphVM@6", "order": 1, "nodeId": "func:web/src/view/vmContext.ts#useGraphVM@6", "title": "useGraphVM", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "title": "GraphViewModel.selectReport", "nextStepIds": [ - "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126" + "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180" ] }, { - "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "order": 3, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "title": "GraphViewModel.load", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", - "edge:call:func:web/src/view/App.tsx#observer() 回调@9@9=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/App.tsx#observer() 回调@9@9=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74" + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", + "edge:call:func:web/src/view/App.tsx#observer() 回调@11@11=>func:web/src/view/vmContext.ts#useGraphVM@6", + "edge:call:func:web/src/view/App.tsx#observer() 回调@11@11=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93" ], - "summary": "以 observer() 回调@9 为入口的调用链,覆盖 4 个函数" + "summary": "渲染应用骨架:头部、搜索、各面板布局" }, { "id": "flow:call:func:src/flows.ts#map() 回调@72@72", "name": "map() 回调@72 (src/flows.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:src/flows.ts#map() 回调@72@72", "steps": [ { @@ -4670,13 +5738,13 @@ "edgeIds": [ "edge:call:func:src/flows.ts#map() 回调@72@72=>func:src/flows.ts#stepIdOf@70" ], - "summary": "以 map() 回调@72 为入口的调用链,覆盖 2 个函数" + "summary": "生成流程步骤时的映射回调" }, { "id": "flow:call:func:src/analyzer.ts#inProject@65", "name": "inProject (src/analyzer.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:src/analyzer.ts#inProject@65", "steps": [ { @@ -4709,13 +5777,52 @@ "edge:call:func:src/analyzer.ts#relOf@64=>func:src/scanner.ts#toPosix@19", "edge:call:func:src/analyzer.ts#inProject@65=>func:src/analyzer.ts#relOf@64" ], - "summary": "以 inProject 为入口的调用链,覆盖 3 个函数" + "summary": "判断文件是否属于目标项目,过滤外部依赖" + }, + { + "id": "flow:call:func:src/overview.ts#map() 回调@229@229", + "name": "map() 回调@229 (src/overview.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:src/overview.ts#map() 回调@229@229", + "steps": [ + { + "id": "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#map() 回调@229@229", + "order": 0, + "nodeId": "func:src/overview.ts#map() 回调@229@229", + "title": "map() 回调@229", + "nextStepIds": [ + "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#analyzeEntry@140" + ] + }, + { + "id": "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#analyzeEntry@140", + "order": 1, + "nodeId": "func:src/overview.ts#analyzeEntry@140", + "title": "analyzeEntry", + "nextStepIds": [ + "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#reachableFuncs@92" + ] + }, + { + "id": "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#reachableFuncs@92", + "order": 2, + "nodeId": "func:src/overview.ts#reachableFuncs@92", + "title": "reachableFuncs", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:src/overview.ts#analyzeEntry@140=>func:src/overview.ts#reachableFuncs@92", + "edge:call:func:src/overview.ts#map() 回调@229@229=>func:src/overview.ts#analyzeEntry@140" + ], + "summary": "概览推导中的流程映射回调" }, { "id": "flow:call:func:web/src/model/graphModel.ts#map() 回调@44@44", "name": "map() 回调@44 (web/src/model/graphModel.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/model/graphModel.ts#map() 回调@44@44", "steps": [ { @@ -4748,91 +5855,149 @@ "edge:call:func:web/src/model/graphModel.ts#qualifierOf@24=>func:web/src/model/graphModel.ts#baseName@19", "edge:call:func:web/src/model/graphModel.ts#map() 回调@44@44=>func:web/src/model/graphModel.ts#qualifierOf@24" ], - "summary": "以 map() 回调@44 为入口的调用链,覆盖 3 个函数" + "summary": "将流程步骤映射为图节点" }, { - "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "name": "GraphViewModel.groups (web/src/viewmodel/GraphViewModel.ts)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "steps": [ { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "order": 0, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "title": "GraphViewModel.groups", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86" + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "order": 1, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "title": "GraphViewModel.groupOfFlow", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#baseName@34" + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#baseName@43" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "title": "baseName", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86=>func:web/src/viewmodel/GraphViewModel.ts#baseName@34", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86" + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109" ], - "summary": "以 GraphViewModel.groups 为入口的调用链,覆盖 3 个函数" + "summary": "派生分组列表的计算属性" }, { - "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "name": "GraphViewModel.visibleFlows (web/src/viewmodel/GraphViewModel.ts)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "steps": [ { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "order": 0, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "title": "GraphViewModel.visibleFlows", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86" + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "order": 1, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "title": "GraphViewModel.groupOfFlow", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#baseName@34" + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#baseName@43" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "title": "baseName", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86=>func:web/src/viewmodel/GraphViewModel.ts#baseName@34", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86" + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109" ], - "summary": "以 GraphViewModel.visibleFlows 为入口的调用链,覆盖 3 个函数" + "summary": "按当前分组过滤后的流程列表" + }, + { + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "name": "GraphViewModel.selectedFuncOwner (web/src/viewmodel/GraphViewModel.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "steps": [ + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "order": 0, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "title": "GraphViewModel.selectedFuncOwner", + "nextStepIds": [ + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159:func:web/src/viewmodel/GraphViewModel.ts#baseName@43" + ] + }, + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159:func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "order": 1, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "title": "baseName", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43" + ], + "summary": "计算选中函数的归属(类 / 文件)及其说明" + }, + { + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "name": "reaction() 回调@79 (web/src/viewmodel/GraphViewModel.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "steps": [ + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "order": 0, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "title": "reaction() 回调@79", + "nextStepIds": [ + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346" + ] + }, + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", + "order": 1, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", + "title": "GraphViewModel.loadSelectedFuncCode", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346" + ], + "summary": "监听选中节点变化,自动拉取对应源码" }, { "id": "flow:call:func:web/src/view/components/FlowNode.tsx#memo() 回调@40@40", "name": "memo() 回调@40 (web/src/view/components/FlowNode.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/FlowNode.tsx#memo() 回调@40@40", "steps": [ { @@ -4855,13 +6020,13 @@ "edgeIds": [ "edge:call:func:web/src/view/components/FlowNode.tsx#memo() 回调@40@40=>func:web/src/view/components/FlowNode.tsx#interactionBadges@12" ], - "summary": "以 memo() 回调@40 为入口的调用链,覆盖 2 个函数" + "summary": "自定义节点渲染(memo 回调)" }, { "id": "flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41", "name": "匿名函数@41 (web/src/view/components/SearchBar.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/SearchBar.tsx#匿名函数@41@41", "steps": [ { @@ -4879,47 +6044,76 @@ "nodeId": "func:web/src/view/components/SearchBar.tsx#activate@12", "title": "activate", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322" ] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "title": "GraphViewModel.activateSearchResult", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265" ] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "order": 3, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "title": "GraphViewModel.biggestFlowForFunc", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "order": 4, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "title": "GraphViewModel.openFlowForFunc", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", - "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", + "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41=>func:web/src/view/components/SearchBar.tsx#activate@12" ], - "summary": "以 匿名函数@41 为入口的调用链,覆盖 5 个函数" + "summary": "搜索结果项点击处理" + }, + { + "id": "flow:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "name": "observer() 回调@4 (web/src/view/components/FlowBanner.tsx)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "steps": [ + { + "id": "step:flow:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "order": 0, + "nodeId": "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "title": "observer() 回调@4", + "nextStepIds": [ + "step:flow:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4:func:web/src/view/vmContext.ts#useGraphVM@6" + ] + }, + { + "id": "step:flow:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4:func:web/src/view/vmContext.ts#useGraphVM@6", + "order": 1, + "nodeId": "func:web/src/view/vmContext.ts#useGraphVM@6", + "title": "useGraphVM", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4=>func:web/src/view/vmContext.ts#useGraphVM@6" + ], + "summary": "渲染当前流程横幅:流程名与一句话说明" }, { "id": "flow:call:func:src/cli.ts#@0", "name": " (src/cli.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:src/cli.ts#@0", "steps": [ { @@ -5183,7 +6377,122 @@ "edge:call:func:src/cli.ts#main@40=>func:src/analyzer.ts#analyzeProject@32", "edge:call:func:src/cli.ts#@0=>func:src/cli.ts#main@40" ], - "summary": "以 为入口的调用链,覆盖 26 个函数" + "summary": "CLI 主流程:解析参数、运行分析、写出报告" + }, + { + "id": "flow:call:func:src/overview-cli.ts#@0", + "name": " (src/overview-cli.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:src/overview-cli.ts#@0", + "steps": [ + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#@0", + "order": 0, + "nodeId": "func:src/overview-cli.ts#@0", + "title": "", + "nextStepIds": [ + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#main@9" + ] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#main@9", + "order": 1, + "nodeId": "func:src/overview-cli.ts#main@9", + "title": "main", + "nextStepIds": [ + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#deriveOverview@109", + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#flag@16" + ] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#deriveOverview@109", + "order": 2, + "nodeId": "func:src/overview.ts#deriveOverview@109", + "title": "deriveOverview", + "nextStepIds": [ + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#apiInOf@79", + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#analyzeEntry@140", + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#groupKeyFromPath@83" + ] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#flag@16", + "order": 3, + "nodeId": "func:src/overview-cli.ts#flag@16", + "title": "flag", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#apiInOf@79", + "order": 4, + "nodeId": "func:src/overview.ts#apiInOf@79", + "title": "apiInOf", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#analyzeEntry@140", + "order": 5, + "nodeId": "func:src/overview.ts#analyzeEntry@140", + "title": "analyzeEntry", + "nextStepIds": [ + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#reachableFuncs@92" + ] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#groupKeyFromPath@83", + "order": 6, + "nodeId": "func:src/overview.ts#groupKeyFromPath@83", + "title": "groupKeyFromPath", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#reachableFuncs@92", + "order": 7, + "nodeId": "func:src/overview.ts#reachableFuncs@92", + "title": "reachableFuncs", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:src/overview.ts#analyzeEntry@140=>func:src/overview.ts#reachableFuncs@92", + "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#apiInOf@79", + "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#analyzeEntry@140", + "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#groupKeyFromPath@83", + "edge:call:func:src/overview-cli.ts#main@9=>func:src/overview.ts#deriveOverview@109", + "edge:call:func:src/overview-cli.ts#main@9=>func:src/overview-cli.ts#flag@16", + "edge:call:func:src/overview-cli.ts#@0=>func:src/overview-cli.ts#main@9" + ], + "summary": "概览 CLI 主流程:读取报告并打印项目地图" + }, + { + "id": "flow:call:func:web/vite.config.ts#@0", + "name": " (web/vite.config.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/vite.config.ts#@0", + "steps": [ + { + "id": "step:flow:call:func:web/vite.config.ts#@0:func:web/vite.config.ts#@0", + "order": 0, + "nodeId": "func:web/vite.config.ts#@0", + "title": "", + "nextStepIds": [ + "step:flow:call:func:web/vite.config.ts#@0:func:web/vite.config.ts#sourceServer@11" + ] + }, + { + "id": "step:flow:call:func:web/vite.config.ts#@0:func:web/vite.config.ts#sourceServer@11", + "order": 1, + "nodeId": "func:web/vite.config.ts#sourceServer@11", + "title": "sourceServer", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/vite.config.ts#@0=>func:web/vite.config.ts#sourceServer@11" + ], + "summary": "Vite 配置与源码服务中间件初始化" } ], "externalSystems": [] diff --git a/src/overview-cli.ts b/src/overview-cli.ts new file mode 100644 index 0000000..8f3ebc6 --- /dev/null +++ b/src/overview-cli.ts @@ -0,0 +1,86 @@ +// 用法: npx tsx src/overview-cli.ts +// 读取一份报告,推导「确定层」落地页数据并打印成可读文本地图。 + +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { deriveOverview } from "./overview"; +import type { Report } from "./types"; + +function main() { + const arg = process.argv[2] ?? "sample-report.json"; + const file = resolve(process.cwd(), arg); + const report = JSON.parse(readFileSync(file, "utf-8")) as Report; + const ov = deriveOverview(report); + + const L: string[] = []; + const flag = (db: boolean, svc: boolean) => { + const t: string[] = []; + if (db) t.push("DB"); + if (svc) t.push("外部服务"); + return t.length ? " [" + t.join(" · ") + "]" : ""; + }; + + L.push("========================================================"); + L.push(" 项目概览 · " + ov.project.name); + L.push("========================================================"); + L.push("技术栈 : " + ov.project.languages.join(", ")); + L.push( + "规模 : " + + ov.project.fileCount + + " 文件 / " + + ov.project.functionCount + + " 函数 / " + + ov.project.edgeCount + + " 关系边" + + (ov.project.loc ? " / " + ov.project.loc + " 行" : "") + ); + L.push("入口 : 共 " + ov.entrypoints.total + " 个(HTTP " + ov.entrypoints.httpCount + " · 其他 " + ov.entrypoints.others.length + ")"); + + L.push(""); + L.push("──────────── 一、对外接口(按业务域分组) ────────────"); + for (const g of ov.entrypoints.httpGroups) { + L.push(""); + L.push("[" + g.key + "] (" + g.endpoints.length + " 个接口)"); + for (const ep of g.endpoints) { + const size = ep.flowSize != null ? " ~" + ep.flowSize + "步" : ""; + L.push(" " + ep.method.padEnd(6) + ep.path + size + flag(ep.touchesDb, ep.callsService)); + } + } + + if (ov.entrypoints.others.length) { + L.push(""); + L.push("──────────── 二、其他入口(定时/事件/内部) ────────────"); + for (const o of ov.entrypoints.others) { + const size = o.flowSize != null ? " ~" + o.flowSize + "步" : ""; + L.push(" " + o.funcName + " (" + (o.file ?? "") + ")" + size + flag(o.touchesDb, o.callsService)); + } + } + + L.push(""); + L.push("──────────── 三、外部系统 ────────────"); + for (const db of ov.externals.databases) { + L.push(" [库] " + db.name + (db.engine ? " (" + db.engine + ")" : "") + " · " + db.tableCount + " 张表"); + L.push(" " + db.tables.join(", ")); + } + for (const s of ov.externals.services) { + L.push(" [服务] " + s.name + (s.protocol ? " (" + s.protocol + ")" : "") + " · " + s.endpointCount + " 个端点"); + for (const e of s.endpoints) L.push(" " + e); + } + + L.push(""); + L.push("──────────── 四、重点 · 被调用最多的函数 ────────────"); + for (const r of ov.hotspots.mostCalled) { + L.push(" " + String(r.fanIn).padStart(3) + "× " + r.funcName + " (" + (r.file ?? "") + ")"); + } + + L.push(""); + L.push("──────────── 五、重点 · 最大的调用链 ────────────"); + for (const f of ov.hotspots.largestFlows) { + L.push(" " + String(f.size).padStart(3) + "步 " + f.name + flag(f.touchesDb, f.callsService)); + } + + L.push(""); + process.stdout.write(L.join("\n") + "\n"); +} + +main(); diff --git a/src/overview.ts b/src/overview.ts new file mode 100644 index 0000000..1f8ff3d --- /dev/null +++ b/src/overview.ts @@ -0,0 +1,261 @@ +// 落地页「确定层」推导:只用 report JSON 里已确定的结构, +// 不依赖任何 AI / 人工,产出一张“项目地图”。 + +import type { Report, Func, Flow, Edge, ExternalSystem, Interaction } from "./types"; + +export interface OverviewProjectInfo { + name: string; + languages: string[]; + fileCount: number; + functionCount: number; + edgeCount: number; + loc?: number; +} + +export interface HttpEndpoint { + method: string; + path: string; + funcId: string; + funcName: string; + file?: string; + flowId?: string; + flowSize?: number; // 该入口调用链覆盖的函数数 + touchesDb: boolean; // 调用链是否触达数据库 + callsService: boolean; // 调用链是否发起外部服务请求 +} + +export interface EndpointGroup { + key: string; // 业务域,如 auth / tasks + endpoints: HttpEndpoint[]; +} + +export interface OtherEntry { + funcId: string; + funcName: string; + file?: string; + flowId?: string; + flowSize?: number; + touchesDb: boolean; + callsService: boolean; +} + +export interface ExternalSummary { + databases: { name: string; engine?: string; tableCount: number; tables: string[] }[]; + services: { name: string; protocol?: string; endpointCount: number; endpoints: string[] }[]; +} + +export interface FuncRank { + funcId: string; + funcName: string; + file?: string; + fanIn: number; // 被多少处调用 +} + +export interface FlowRank { + flowId: string; + name: string; + size: number; // 覆盖函数数 + touchesDb: boolean; + callsService: boolean; +} + +export interface Overview { + project: OverviewProjectInfo; + entrypoints: { + total: number; + httpGroups: EndpointGroup[]; + httpCount: number; + others: OtherEntry[]; + }; + externals: ExternalSummary; + hotspots: { + mostCalled: FuncRank[]; + largestFlows: FlowRank[]; + }; +} + +// ---------- 工具 ---------- + +function apiInOf(fn: Func): Interaction | undefined { + return (fn.interactions ?? []).find((it) => it.kind === "api" && it.direction === "in"); +} + +function groupKeyFromPath(path: string): string { + // /api/auth/sso-login -> auth ; /api/tasks/:id -> tasks ; /health -> health + const segs = path.split("/").filter(Boolean); + if (segs.length === 0) return "(root)"; + const idx = segs[0] === "api" ? 1 : 0; + return segs[idx] ?? segs[0]; +} + +// 从某入口函数出发,沿调用/触发边可达的所有函数 id(有界 BFS) +function reachableFuncs(startId: string, adjacency: Map, limit = 2000): Set { + const seen = new Set([startId]); + const queue = [startId]; + while (queue.length && seen.size < limit) { + const cur = queue.shift()!; + for (const next of adjacency.get(cur) ?? []) { + if (!seen.has(next)) { + seen.add(next); + queue.push(next); + } + } + } + return seen; +} + +// ---------- 主推导 ---------- + +export function deriveOverview(report: Report): Overview { + const funcById = new Map(); + for (const fn of report.functions) funcById.set(fn.id, fn); + + // 调用/触发邻接表(用于可达性 & fan-in) + const adjacency = new Map(); + const fanIn = new Map(); + for (const e of report.edges as Edge[]) { + if (e.kind === "call" || e.kind === "trigger") { + if (!adjacency.has(e.sourceId)) adjacency.set(e.sourceId, []); + adjacency.get(e.sourceId)!.push(e.targetId); + fanIn.set(e.targetId, (fanIn.get(e.targetId) ?? 0) + 1); + } + } + + // 入口函数 -> 对应的 call flow(用 entryNodeId 关联) + const flowByEntry = new Map(); + for (const fl of report.flows) { + if (fl.entryNodeId) flowByEntry.set(fl.entryNodeId, fl); + } + + // 判定一个函数集合是否触达 db / 外部服务 + const dbFuncIds = new Set(); + const svcFuncIds = new Set(); + for (const fn of report.functions) { + for (const it of fn.interactions ?? []) { + if (it.kind === "db") dbFuncIds.add(fn.id); + if (it.kind === "api" && it.direction === "out") svcFuncIds.add(fn.id); + } + } + + const analyzeEntry = (fn: Func) => { + const flow = flowByEntry.get(fn.id); + const reach = reachableFuncs(fn.id, adjacency); + let touchesDb = false; + let callsService = false; + for (const id of reach) { + if (dbFuncIds.has(id)) touchesDb = true; + if (svcFuncIds.has(id)) callsService = true; + } + return { + flowId: flow?.id, + flowSize: flow ? flow.steps.length : reach.size, + touchesDb, + callsService, + }; + }; + + // 分类入口 + const httpEndpoints: HttpEndpoint[] = []; + const others: OtherEntry[] = []; + for (const fn of report.functions) { + if (!fn.isEntry) continue; + const api = apiInOf(fn); + const info = analyzeEntry(fn); + if (api && api.kind === "api") { + httpEndpoints.push({ + method: api.method ?? "", + path: api.path ?? "", + funcId: fn.id, + funcName: fn.name, + file: fn.location?.file, + ...info, + }); + } else { + others.push({ + funcId: fn.id, + funcName: fn.name, + file: fn.location?.file, + ...info, + }); + } + } + + // HTTP 分组 + const groupMap = new Map(); + for (const ep of httpEndpoints) { + const key = groupKeyFromPath(ep.path); + if (!groupMap.has(key)) groupMap.set(key, []); + groupMap.get(key)!.push(ep); + } + const httpGroups: EndpointGroup[] = [...groupMap.entries()] + .map(([key, endpoints]) => ({ + key, + endpoints: endpoints.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method)), + })) + .sort((a, b) => b.endpoints.length - a.endpoints.length || a.key.localeCompare(b.key)); + + // 外部系统汇总 + const externals: ExternalSummary = { databases: [], services: [] }; + for (const sys of (report.externalSystems ?? []) as ExternalSystem[]) { + if (sys.kind === "database") { + externals.databases.push({ + name: sys.name, + engine: sys.engine, + tableCount: sys.tables.length, + tables: sys.tables, + }); + } else { + externals.services.push({ + name: sys.name, + protocol: sys.protocol, + endpointCount: sys.endpoints.length, + endpoints: sys.endpoints, + }); + } + } + + // 热点:被调用最多的函数 + const mostCalled: FuncRank[] = [...fanIn.entries()] + .map(([funcId, count]) => { + const fn = funcById.get(funcId); + return { funcId, funcName: fn?.name ?? funcId, file: fn?.location?.file, fanIn: count }; + }) + .filter((r) => !!funcById.get(r.funcId)) + .sort((a, b) => b.fanIn - a.fanIn) + .slice(0, 10); + + // 热点:最大的调用链 + const largestFlows: FlowRank[] = report.flows + .map((fl) => { + const entry = fl.entryNodeId ? funcById.get(fl.entryNodeId) : undefined; + const info = entry ? analyzeEntry(entry) : { touchesDb: false, callsService: false }; + return { + flowId: fl.id, + name: fl.name, + size: fl.steps.length, + touchesDb: info.touchesDb, + callsService: info.callsService, + }; + }) + .sort((a, b) => b.size - a.size) + .slice(0, 10); + + return { + project: { + name: report.project.name, + languages: report.project.languages, + fileCount: report.stats.fileCount, + functionCount: report.stats.functionCount, + edgeCount: report.stats.edgeCount, + loc: report.stats.loc, + }, + entrypoints: { + total: httpEndpoints.length + others.length, + httpGroups, + httpCount: httpEndpoints.length, + others: others.sort((a, b) => (b.flowSize ?? 0) - (a.flowSize ?? 0)), + }, + externals, + hotspots: { mostCalled, largestFlows }, + }; +} diff --git a/web/index.html b/web/index.html index b1c46fc..bd16f5f 100644 --- a/web/index.html +++ b/web/index.html @@ -4,6 +4,12 @@ 代码分析可视化 + + +
diff --git a/web/package-lock.json b/web/package-lock.json index dc357e7..d3741bf 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -9,11 +9,13 @@ "version": "0.1.0", "dependencies": { "@dagrejs/dagre": "^1.1.4", + "@types/react-syntax-highlighter": "^15.5.13", "@xyflow/react": "^12.3.5", "mobx": "^6.13.5", "mobx-react-lite": "^4.0.7", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-syntax-highlighter": "^16.1.1" }, "devDependencies": { "@types/react": "^19.0.0", @@ -257,6 +259,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", @@ -1273,11 +1284,25 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/prismjs": { + "version": "1.26.6", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", + "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", - "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -1293,6 +1318,21 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-syntax-highlighter": { + "version": "15.5.13", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz", + "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-react": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", @@ -1424,12 +1464,52 @@ ], "license": "CC-BY-4.0" }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/classcat": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", "license": "MIT" }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1441,7 +1521,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, "license": "MIT" }, "node_modules/d3-color": { @@ -1567,6 +1646,19 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.395", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.395.tgz", @@ -1626,6 +1718,19 @@ "node": ">=6" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1644,6 +1749,14 @@ } } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1669,6 +1782,95 @@ "node": ">=6.9.0" } }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", + "license": "CC0-1.0" + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1702,6 +1904,20 @@ "node": ">=6" } }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "license": "MIT", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -1783,6 +1999,31 @@ "node": ">=18" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1832,6 +2073,25 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/react": { "version": "19.2.8", "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", @@ -1863,6 +2123,42 @@ "node": ">=0.10.0" } }, + "node_modules/react-syntax-highlighter": { + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.1.tgz", + "integrity": "sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "highlight.js": "^10.4.1", + "highlightjs-vue": "^1.0.0", + "lowlight": "^1.17.0", + "prismjs": "^1.30.0", + "refractor": "^5.0.0" + }, + "engines": { + "node": ">= 16.20.2" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, + "node_modules/refractor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", + "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/prismjs": "^1.0.0", + "hastscript": "^9.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/rollup": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", @@ -1934,6 +2230,16 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", diff --git a/web/package.json b/web/package.json index ffb2480..5549688 100644 --- a/web/package.json +++ b/web/package.json @@ -10,11 +10,13 @@ }, "dependencies": { "@dagrejs/dagre": "^1.1.4", + "@types/react-syntax-highlighter": "^15.5.13", "@xyflow/react": "^12.3.5", "mobx": "^6.13.5", "mobx-react-lite": "^4.0.7", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-syntax-highlighter": "^16.1.1" }, "devDependencies": { "@types/react": "^19.0.0", diff --git a/web/public/self-report.json b/web/public/self-report.json index c66a8bb..047d003 100644 --- a/web/public/self-report.json +++ b/web/public/self-report.json @@ -4,20 +4,46 @@ "name": "code-visualize", "root": "E:/code-visualize", "languages": [ + "javascript", "tsx", "typescript" ], - "generatedAt": "2026-07-22T03:03:33.188Z", + "generatedAt": "2026-07-22T09:34:53.239Z", "generator": "code-visualize-cli@0.1.0" }, "stats": { - "fileCount": 21, + "fileCount": 28, "containerCount": 2, - "functionCount": 94, - "edgeCount": 110, - "loc": 2212 + "functionCount": 119, + "edgeCount": 135, + "loc": 3018 }, "modules": [ + { + "id": "module:scripts", + "kind": "module", + "name": "scripts", + "provenance": "ast", + "path": "scripts", + "childModuleIds": [], + "fileIds": [ + "file:scripts/annotate-self-report.mjs" + ], + "parentId": "module:." + }, + { + "id": "module:.", + "kind": "module", + "name": "code-visualize", + "provenance": "ast", + "path": ".", + "childModuleIds": [ + "module:scripts", + "module:src", + "module:web" + ], + "fileIds": [] + }, { "id": "module:src", "kind": "module", @@ -31,22 +57,12 @@ "file:src/externals.ts", "file:src/flows.ts", "file:src/analyzer.ts", - "file:src/cli.ts" + "file:src/cli.ts", + "file:src/overview.ts", + "file:src/overview-cli.ts" ], "parentId": "module:." }, - { - "id": "module:.", - "kind": "module", - "name": "code-visualize", - "provenance": "ast", - "path": ".", - "childModuleIds": [ - "module:src", - "module:web" - ], - "fileIds": [] - }, { "id": "module:web/src/model", "kind": "module", @@ -100,7 +116,9 @@ "path": "web/src/services", "childModuleIds": [], "fileIds": [ - "file:web/src/services/dagreLayout.ts" + "file:web/src/services/dagreLayout.ts", + "file:web/src/services/sourceService.ts", + "file:web/src/services/languageMap.ts" ], "parentId": "module:web/src" }, @@ -144,12 +162,33 @@ "file:web/src/view/components/FlowList.tsx", "file:web/src/view/components/GroupList.tsx", "file:web/src/view/components/NodeDetailPanel.tsx", - "file:web/src/view/components/SearchBar.tsx" + "file:web/src/view/components/SearchBar.tsx", + "file:web/src/view/components/CodePanel.tsx", + "file:web/src/view/components/FlowBanner.tsx" ], "parentId": "module:web/src/view" } ], "files": [ + { + "id": "file:scripts/annotate-self-report.mjs", + "kind": "file", + "name": "annotate-self-report.mjs", + "location": { + "file": "scripts/annotate-self-report.mjs", + "startLine": 1, + "endLine": 119 + }, + "language": "javascript", + "provenance": "ast", + "moduleId": "module:scripts", + "path": "scripts/annotate-self-report.mjs", + "loc": 119, + "containerIds": [], + "functionIds": [ + "func:scripts/annotate-self-report.mjs#@0" + ] + }, { "id": "file:src/scanner.ts", "kind": "file", @@ -170,7 +209,8 @@ "func:src/scanner.ts#collectSourceFiles@24", "func:src/scanner.ts#walk@27", "func:src/scanner.ts#languageOf@52" - ] + ], + "summary": "递归扫描项目目录,按扩展名收集源文件并识别语言" }, { "id": "file:src/types.ts", @@ -187,7 +227,8 @@ "path": "src/types.ts", "loc": 205, "containerIds": [], - "functionIds": [] + "functionIds": [], + "summary": "标准化代码报告的核心类型定义(Schema 真相源)" }, { "id": "file:src/externals.ts", @@ -206,7 +247,8 @@ "containerIds": [], "functionIds": [ "func:src/externals.ts#deriveExternalSystems@4" - ] + ], + "summary": "聚合函数交互,派生外部系统(数据库 / 服务)清单" }, { "id": "file:src/flows.ts", @@ -227,7 +269,8 @@ "func:src/flows.ts#generateCallFlows@16", "func:src/flows.ts#stepIdOf@70", "func:src/flows.ts#map() 回调@72@72" - ] + ], + "summary": "从入口点遍历调用图,自动生成调用链流程" }, { "id": "file:src/analyzer.ts", @@ -273,7 +316,8 @@ "func:src/analyzer.ts#analyzeRelations@487", "func:src/analyzer.ts#walk@492", "func:src/analyzer.ts#resolveInternalImport@548" - ] + ], + "summary": "基于 TS 编译器 API 的解析核心:构建函数层级与调用边" }, { "id": "file:src/cli.ts", @@ -294,7 +338,56 @@ "func:src/cli.ts#parseArgs@13", "func:src/cli.ts#main@40", "func:src/cli.ts#@0" - ] + ], + "summary": "命令行入口:解析参数、运行分析、写出报告 JSON" + }, + { + "id": "file:src/overview.ts", + "kind": "file", + "name": "overview.ts", + "location": { + "file": "src/overview.ts", + "startLine": 1, + "endLine": 262 + }, + "language": "typescript", + "provenance": "ast", + "moduleId": "module:src", + "path": "src/overview.ts", + "loc": 262, + "containerIds": [], + "functionIds": [ + "func:src/overview.ts#apiInOf@79", + "func:src/overview.ts#groupKeyFromPath@83", + "func:src/overview.ts#reachableFuncs@92", + "func:src/overview.ts#deriveOverview@109", + "func:src/overview.ts#analyzeEntry@140", + "func:src/overview.ts#map() 回调@219@219", + "func:src/overview.ts#map() 回调@229@229" + ], + "summary": "从报告推导「概览」确定层数据(接口 / 入口 / 外部系统 / 热点)" + }, + { + "id": "file:src/overview-cli.ts", + "kind": "file", + "name": "overview-cli.ts", + "location": { + "file": "src/overview-cli.ts", + "startLine": 1, + "endLine": 87 + }, + "language": "typescript", + "provenance": "ast", + "moduleId": "module:src", + "path": "src/overview-cli.ts", + "loc": 87, + "containerIds": [], + "functionIds": [ + "func:src/overview-cli.ts#main@9", + "func:src/overview-cli.ts#flag@16", + "func:src/overview-cli.ts#@0" + ], + "summary": "概览推导的命令行运行器,打印可读的项目地图" }, { "id": "file:web/src/model/report.ts", @@ -311,7 +404,8 @@ "path": "web/src/model/report.ts", "loc": 194, "containerIds": [], - "functionIds": [] + "functionIds": [], + "summary": "前端侧报告类型定义(与 CLI Schema 对齐)" }, { "id": "file:web/src/model/reportRepository.ts", @@ -330,7 +424,8 @@ "containerIds": [ "container:web/src/model/reportRepository.ts#HttpReportRepository@16" ], - "functionIds": [] + "functionIds": [], + "summary": "报告数据源抽象,HTTP 加载报告 JSON" }, { "id": "file:web/src/model/graphModel.ts", @@ -352,7 +447,8 @@ "func:web/src/model/graphModel.ts#qualifierOf@24", "func:web/src/model/graphModel.ts#flowToGraph@43", "func:web/src/model/graphModel.ts#map() 回调@44@44" - ] + ], + "summary": "将 Flow 转换为与渲染无关的「中性图」结构" }, { "id": "file:web/src/services/dagreLayout.ts", @@ -371,7 +467,28 @@ "containerIds": [], "functionIds": [ "func:web/src/services/dagreLayout.ts#computeLayout@31" - ] + ], + "summary": "用 Dagre 计算图的自动布局坐标" + }, + { + "id": "file:web/src/services/sourceService.ts", + "kind": "file", + "name": "sourceService.ts", + "location": { + "file": "web/src/services/sourceService.ts", + "startLine": 1, + "endLine": 33 + }, + "language": "typescript", + "provenance": "ast", + "moduleId": "module:web/src/services", + "path": "web/src/services/sourceService.ts", + "loc": 33, + "containerIds": [], + "functionIds": [ + "func:web/src/services/sourceService.ts#fetchSource@19" + ], + "summary": "源码绑定:向开发服务器请求真实源码片段" }, { "id": "file:web/src/viewmodel/GraphViewModel.ts", @@ -380,21 +497,24 @@ "location": { "file": "web/src/viewmodel/GraphViewModel.ts", "startLine": 1, - "endLine": 290 + "endLine": 381 }, "language": "typescript", "provenance": "ast", "moduleId": "module:web/src/viewmodel", "path": "web/src/viewmodel/GraphViewModel.ts", - "loc": 290, + "loc": 381, "containerIds": [ - "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" ], "functionIds": [ - "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", - "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@149@149", - "func:web/src/viewmodel/GraphViewModel.ts#map() 回调@186@186" - ] + "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@203@203", + "func:web/src/viewmodel/GraphViewModel.ts#map() 回调@240@240", + "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@368@368" + ], + "summary": "图视图 ViewModel:编排加载 / 选择 / 布局 / 搜索 / 源码" }, { "id": "file:web/src/view/vmContext.ts", @@ -414,7 +534,8 @@ "functionIds": [ "func:web/src/view/vmContext.ts#useGraphVM@6", "func:web/src/view/vmContext.ts#@0" - ] + ], + "summary": "提供 GraphViewModel 的 React Context" }, { "id": "file:web/src/view/components/FlowNode.tsx", @@ -435,7 +556,8 @@ "func:web/src/view/components/FlowNode.tsx#interactionBadges@12", "func:web/src/view/components/FlowNode.tsx#memo() 回调@40@40", "func:web/src/view/components/FlowNode.tsx#@0" - ] + ], + "summary": "自定义流程图节点:显示函数名 / 归属 / 交互徽标" }, { "id": "file:web/src/view/components/FlowGraph.tsx", @@ -444,18 +566,19 @@ "location": { "file": "web/src/view/components/FlowGraph.tsx", "startLine": 1, - "endLine": 73 + "endLine": 71 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src/view/components", "path": "web/src/view/components/FlowGraph.tsx", - "loc": 73, + "loc": 71, "containerIds": [], "functionIds": [ - "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "func:web/src/view/components/FlowGraph.tsx#@0" - ] + ], + "summary": "React Flow 画布容器,渲染当前流程图" }, { "id": "file:web/src/view/components/FlowList.tsx", @@ -464,18 +587,19 @@ "location": { "file": "web/src/view/components/FlowList.tsx", "startLine": 1, - "endLine": 26 + "endLine": 39 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src/view/components", "path": "web/src/view/components/FlowList.tsx", - "loc": 26, + "loc": 39, "containerIds": [], "functionIds": [ "func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", "func:web/src/view/components/FlowList.tsx#@0" - ] + ], + "summary": "左侧流程列表面板" }, { "id": "file:web/src/view/components/GroupList.tsx", @@ -495,7 +619,8 @@ "functionIds": [ "func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", "func:web/src/view/components/GroupList.tsx#@0" - ] + ], + "summary": "最左侧分组列表:按类 / 文件过滤流程" }, { "id": "file:web/src/view/components/NodeDetailPanel.tsx", @@ -504,19 +629,20 @@ "location": { "file": "web/src/view/components/NodeDetailPanel.tsx", "startLine": 1, - "endLine": 86 + "endLine": 99 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src/view/components", "path": "web/src/view/components/NodeDetailPanel.tsx", - "loc": 86, + "loc": 99, "containerIds": [], "functionIds": [ "func:web/src/view/components/NodeDetailPanel.tsx#describeInteraction@5", "func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13", "func:web/src/view/components/NodeDetailPanel.tsx#@0" - ] + ], + "summary": "右侧节点详情:位置 / 归属 / 交互 / 所在流程" }, { "id": "file:web/src/view/components/SearchBar.tsx", @@ -539,7 +665,70 @@ "func:web/src/view/components/SearchBar.tsx#匿名函数@26@26", "func:web/src/view/components/SearchBar.tsx#匿名函数@41@41", "func:web/src/view/components/SearchBar.tsx#@0" - ] + ], + "summary": "顶部搜索栏,检索流程与函数" + }, + { + "id": "file:web/src/services/languageMap.ts", + "kind": "file", + "name": "languageMap.ts", + "location": { + "file": "web/src/services/languageMap.ts", + "startLine": 1, + "endLine": 49 + }, + "language": "typescript", + "provenance": "ast", + "moduleId": "module:web/src/services", + "path": "web/src/services/languageMap.ts", + "loc": 49, + "containerIds": [], + "functionIds": [ + "func:web/src/services/languageMap.ts#languageOf@42" + ], + "summary": "文件扩展名到语法高亮语言的映射" + }, + { + "id": "file:web/src/view/components/CodePanel.tsx", + "kind": "file", + "name": "CodePanel.tsx", + "location": { + "file": "web/src/view/components/CodePanel.tsx", + "startLine": 1, + "endLine": 67 + }, + "language": "tsx", + "provenance": "ast", + "moduleId": "module:web/src/view/components", + "path": "web/src/view/components/CodePanel.tsx", + "loc": 67, + "containerIds": [], + "functionIds": [ + "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "func:web/src/view/components/CodePanel.tsx#@0" + ], + "summary": "底部源码面板:语法高亮显示选中函数实现" + }, + { + "id": "file:web/src/view/components/FlowBanner.tsx", + "kind": "file", + "name": "FlowBanner.tsx", + "location": { + "file": "web/src/view/components/FlowBanner.tsx", + "startLine": 1, + "endLine": 17 + }, + "language": "tsx", + "provenance": "ast", + "moduleId": "module:web/src/view/components", + "path": "web/src/view/components/FlowBanner.tsx", + "loc": 17, + "containerIds": [], + "functionIds": [ + "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "func:web/src/view/components/FlowBanner.tsx#@0" + ], + "summary": "画布顶部横幅:显示当前流程名与说明" }, { "id": "file:web/src/view/App.tsx", @@ -548,18 +737,19 @@ "location": { "file": "web/src/view/App.tsx", "startLine": 1, - "endLine": 55 + "endLine": 61 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src/view", "path": "web/src/view/App.tsx", - "loc": 55, + "loc": 61, "containerIds": [], "functionIds": [ - "func:web/src/view/App.tsx#observer() 回调@9@9", + "func:web/src/view/App.tsx#observer() 回调@11@11", "func:web/src/view/App.tsx#@0" - ] + ], + "summary": "应用主组件:编排头部与各面板布局" }, { "id": "file:web/src/main.tsx", @@ -568,17 +758,18 @@ "location": { "file": "web/src/main.tsx", "startLine": 1, - "endLine": 26 + "endLine": 27 }, "language": "tsx", "provenance": "ast", "moduleId": "module:web/src", "path": "web/src/main.tsx", - "loc": 26, + "loc": 27, "containerIds": [], "functionIds": [ "func:web/src/main.tsx#@0" - ] + ], + "summary": "前端入口:构建数据源与 ViewModel 并挂载 App" }, { "id": "file:web/vite.config.ts", @@ -587,17 +778,20 @@ "location": { "file": "web/vite.config.ts", "startLine": 1, - "endLine": 11 + "endLine": 61 }, "language": "typescript", "provenance": "ast", "moduleId": "module:web", "path": "web/vite.config.ts", - "loc": 11, + "loc": 61, "containerIds": [], "functionIds": [ + "func:web/vite.config.ts#sourceServer@11", + "func:web/vite.config.ts#use() 回调@15@15", "func:web/vite.config.ts#@0" - ] + ], + "summary": "Vite 配置 + 开发期源码服务中间件" } ], "containers": [ @@ -621,17 +815,18 @@ "func:web/src/model/reportRepository.ts#HttpReportRepository.constructor@17", "func:web/src/model/reportRepository.ts#HttpReportRepository.load@19" ], - "fieldIds": [] + "fieldIds": [], + "summary": "通过 HTTP 从 URL 加载报告 JSON 的仓库实现" }, { - "id": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "id": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "kind": "container", "name": "GraphViewModel", "qualifiedName": "GraphViewModel", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 43, - "endLine": 289, + "startLine": 52, + "endLine": 380, "startCol": 1, "endCol": 2 }, @@ -640,42 +835,49 @@ "fileId": "file:web/src/viewmodel/GraphViewModel.ts", "moduleId": "module:web/src/viewmodel", "functionIds": [ - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.constructor@58", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.sources@66", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentSource@70", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flows@160", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentFlow@165", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFunc@194", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flowsForSelectedFunc@202", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcIdsInFlows@225", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchResults@240", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", - "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286" + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.constructor@69", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.sources@85", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentSource@89", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroup@153", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flows@214", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentFlow@219", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFunc@248", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flowsForSelectedFunc@256", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcIdsInFlows@279", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchResults@294", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", + "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346" ], "fieldIds": [ - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loading@44", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.error@45", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.report@46", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedSourceId@48", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroupId@49", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFlowId@50", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedNodeId@51", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchQuery@52", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcById@54", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.containerById@55", - "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.fileById@56" - ] + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loading@53", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.error@54", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.report@55", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedSourceId@57", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroupId@58", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFlowId@59", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedNodeId@60", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchQuery@61", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.codeState@62", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.codePanelCollapsed@63", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcById@65", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.containerById@66", + "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.fileById@67" + ], + "summary": "图视图 ViewModel:持有全部可观察状态与命令" } ], "functions": [ @@ -1841,6 +2043,251 @@ "returnType": "void", "loc": 29 }, + { + "id": "func:src/overview.ts#apiInOf@79", + "kind": "function", + "name": "apiInOf", + "qualifiedName": "apiInOf", + "location": { + "file": "src/overview.ts", + "startLine": 79, + "endLine": 81, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "apiInOf(fn: Func): Interaction | undefined", + "params": [ + { + "name": "fn", + "typeRef": "Func", + "optional": false + } + ], + "returnType": "Interaction | undefined", + "loc": 3 + }, + { + "id": "func:src/overview.ts#groupKeyFromPath@83", + "kind": "function", + "name": "groupKeyFromPath", + "qualifiedName": "groupKeyFromPath", + "location": { + "file": "src/overview.ts", + "startLine": 83, + "endLine": 89, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "groupKeyFromPath(path: string): string", + "params": [ + { + "name": "path", + "typeRef": "string", + "optional": false + } + ], + "returnType": "string", + "loc": 7 + }, + { + "id": "func:src/overview.ts#reachableFuncs@92", + "kind": "function", + "name": "reachableFuncs", + "qualifiedName": "reachableFuncs", + "location": { + "file": "src/overview.ts", + "startLine": 92, + "endLine": 105, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "reachableFuncs(startId: string, adjacency: Map, limit): Set", + "params": [ + { + "name": "startId", + "typeRef": "string", + "optional": false + }, + { + "name": "adjacency", + "typeRef": "Map", + "optional": false + }, + { + "name": "limit", + "optional": true, + "defaultValue": "2000" + } + ], + "returnType": "Set", + "loc": 14 + }, + { + "id": "func:src/overview.ts#deriveOverview@109", + "kind": "function", + "name": "deriveOverview", + "qualifiedName": "deriveOverview", + "location": { + "file": "src/overview.ts", + "startLine": 109, + "endLine": 261, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "deriveOverview(report: Report): Overview", + "params": [ + { + "name": "report", + "typeRef": "Report", + "optional": false + } + ], + "returnType": "Overview", + "loc": 153 + }, + { + "id": "func:src/overview.ts#analyzeEntry@140", + "kind": "function", + "name": "analyzeEntry", + "qualifiedName": "analyzeEntry", + "location": { + "file": "src/overview.ts", + "startLine": 140, + "endLine": 155, + "startCol": 9, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "analyzeEntry(fn: Func)", + "params": [ + { + "name": "fn", + "typeRef": "Func", + "optional": false + } + ], + "loc": 16 + }, + { + "id": "func:src/overview.ts#map() 回调@219@219", + "kind": "function", + "name": "map() 回调@219", + "qualifiedName": "map() 回调@219", + "location": { + "file": "src/overview.ts", + "startLine": 219, + "endLine": 222, + "startCol": 10, + "endCol": 6 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "map() 回调@219([funcId, count])", + "params": [ + { + "name": "[funcId, count]", + "optional": false + } + ], + "loc": 4 + }, + { + "id": "func:src/overview.ts#map() 回调@229@229", + "kind": "function", + "name": "map() 回调@229", + "qualifiedName": "map() 回调@229", + "location": { + "file": "src/overview.ts", + "startLine": 229, + "endLine": 239, + "startCol": 10, + "endCol": 6 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview.ts", + "moduleId": "module:src", + "signature": "map() 回调@229(fl)", + "params": [ + { + "name": "fl", + "optional": false + } + ], + "loc": 11 + }, + { + "id": "func:src/overview-cli.ts#main@9", + "kind": "function", + "name": "main", + "qualifiedName": "main", + "location": { + "file": "src/overview-cli.ts", + "startLine": 9, + "endLine": 84, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview-cli.ts", + "moduleId": "module:src", + "signature": "main()", + "params": [], + "loc": 76 + }, + { + "id": "func:src/overview-cli.ts#flag@16", + "kind": "function", + "name": "flag", + "qualifiedName": "flag", + "location": { + "file": "src/overview-cli.ts", + "startLine": 16, + "endLine": 21, + "startCol": 9, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview-cli.ts", + "moduleId": "module:src", + "signature": "flag(db: boolean, svc: boolean)", + "params": [ + { + "name": "db", + "typeRef": "boolean", + "optional": false + }, + { + "name": "svc", + "typeRef": "boolean", + "optional": false + } + ], + "loc": 6 + }, { "id": "func:web/src/model/reportRepository.ts#HttpReportRepository.constructor@17", "kind": "function", @@ -2055,14 +2502,62 @@ "loc": 32 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "id": "func:web/src/services/sourceService.ts#fetchSource@19", + "kind": "function", + "name": "fetchSource", + "qualifiedName": "fetchSource", + "location": { + "file": "web/src/services/sourceService.ts", + "startLine": 19, + "endLine": 32, + "startCol": 14, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/services/sourceService.ts", + "moduleId": "module:web/src/services", + "signature": "fetchSource(root, file, startLine, endLine)", + "params": [ + { + "name": "root", + "optional": false + }, + { + "name": "file", + "optional": false + }, + { + "name": "startLine", + "optional": false + }, + { + "name": "endLine", + "optional": false + } + ], + "loc": 14, + "interactions": [ + { + "id": "ix:func:web/src/services/sourceService.ts#fetchSource@19:api:out:GET:`/__source?${params.toString()}`", + "kind": "api", + "direction": "out", + "provenance": "resolved", + "protocol": "http", + "method": "GET", + "path": "`/__source?${params.toString()}`" + } + ] + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "kind": "function", "name": "baseName", "qualifiedName": "baseName", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 34, - "endLine": 37, + "startLine": 43, + "endLine": 46, "startCol": 1, "endCol": 2 }, @@ -2082,23 +2577,23 @@ "loc": 4 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.constructor@58", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.constructor@69", "kind": "function", "name": "constructor", "qualifiedName": "GraphViewModel.constructor", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 58, - "endLine": 64, + "startLine": 69, + "endLine": 83, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", - "signature": "constructor(sourceList: ReportSource[], makeRepository: (url: string) => ReportRepository)", + "signature": "constructor(sourceList: ReportSource[], makeRepository: (url: string) => ReportRepository, loadSource: LoadSource)", "params": [ { "name": "sourceList", @@ -2109,26 +2604,31 @@ "name": "makeRepository", "typeRef": "(url: string) => ReportRepository", "optional": false + }, + { + "name": "loadSource", + "typeRef": "LoadSource", + "optional": false } ], - "loc": 7 + "loc": 15 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.sources@66", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.sources@85", "kind": "function", "name": "sources", "qualifiedName": "GraphViewModel.sources", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 66, - "endLine": 68, + "startLine": 85, + "endLine": 87, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "sources(): ReportSource[]", "params": [], @@ -2136,21 +2636,21 @@ "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentSource@70", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentSource@89", "kind": "function", "name": "currentSource", "qualifiedName": "GraphViewModel.currentSource", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 70, - "endLine": 72, + "startLine": 89, + "endLine": 91, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "currentSource(): ReportSource | null", "params": [], @@ -2158,21 +2658,21 @@ "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "kind": "function", "name": "selectReport", "qualifiedName": "GraphViewModel.selectReport", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 74, - "endLine": 83, + "startLine": 93, + "endLine": 102, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "selectReport(sourceId: string): void", "params": [ @@ -2186,21 +2686,43 @@ "loc": 10 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", "kind": "function", - "name": "groupOfFlow", - "qualifiedName": "GraphViewModel.groupOfFlow", + "name": "toggleCodePanel", + "qualifiedName": "GraphViewModel.toggleCodePanel", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 86, - "endLine": 97, + "startLine": 104, + "endLine": 106, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "toggleCodePanel(): void", + "params": [], + "returnType": "void", + "loc": 3 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", + "kind": "function", + "name": "groupOfFlow", + "qualifiedName": "GraphViewModel.groupOfFlow", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 109, + "endLine": 132, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "groupOfFlow(flow: Flow): FlowGroup", "params": [ @@ -2211,24 +2733,24 @@ } ], "returnType": "FlowGroup", - "loc": 12 + "loc": 24 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "kind": "function", "name": "groups", "qualifiedName": "GraphViewModel.groups", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 100, - "endLine": 109, + "startLine": 135, + "endLine": 144, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "groups(): FlowGroup[]", "params": [], @@ -2236,21 +2758,21 @@ "loc": 10 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "kind": "function", "name": "visibleFlows", "qualifiedName": "GraphViewModel.visibleFlows", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 112, - "endLine": 115, + "startLine": 147, + "endLine": 150, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "visibleFlows(): Flow[]", "params": [], @@ -2258,21 +2780,65 @@ "loc": 4 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroup@153", "kind": "function", - "name": "selectGroup", - "qualifiedName": "GraphViewModel.selectGroup", + "name": "selectedGroup", + "qualifiedName": "GraphViewModel.selectedGroup", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 117, - "endLine": 124, + "startLine": 153, + "endLine": 156, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "selectedGroup(): FlowGroup | null", + "params": [], + "returnType": "FlowGroup | null", + "loc": 4 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "kind": "function", + "name": "selectedFuncOwner", + "qualifiedName": "GraphViewModel.selectedFuncOwner", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 159, + "endLine": 169, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "selectedFuncOwner(): { name: string; kind: \"class\" | \"file\"; summary?: string } | null", + "params": [], + "returnType": "{ name: string; kind: \"class\" | \"file\"; summary?: string } | null", + "loc": 11 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", + "kind": "function", + "name": "selectGroup", + "qualifiedName": "GraphViewModel.selectGroup", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 171, + "endLine": 178, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "selectGroup(groupId: string | null): void", "params": [ @@ -2286,21 +2852,21 @@ "loc": 8 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "kind": "function", "name": "load", "qualifiedName": "GraphViewModel.load", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 126, - "endLine": 157, + "startLine": 180, + "endLine": 211, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "load(): Promise", "params": [], @@ -2308,123 +2874,13 @@ "loc": 32 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flows@160", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flows@214", "kind": "function", "name": "flows", "qualifiedName": "GraphViewModel.flows", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 160, - "endLine": 163, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "flows(): Flow[]", - "params": [], - "returnType": "Flow[]", - "loc": 4 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentFlow@165", - "kind": "function", - "name": "currentFlow", - "qualifiedName": "GraphViewModel.currentFlow", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 165, - "endLine": 168, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "currentFlow(): Flow | null", - "params": [], - "returnType": "Flow | null", - "loc": 4 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", - "kind": "function", - "name": "positionedGraph", - "qualifiedName": "GraphViewModel.positionedGraph", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 171, - "endLine": 192, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "positionedGraph(): PositionedGraph", - "params": [], - "returnType": "PositionedGraph", - "loc": 22 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFunc@194", - "kind": "function", - "name": "selectedFunc", - "qualifiedName": "GraphViewModel.selectedFunc", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 194, - "endLine": 199, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "selectedFunc(): Func | null", - "params": [], - "returnType": "Func | null", - "loc": 6 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flowsForSelectedFunc@202", - "kind": "function", - "name": "flowsForSelectedFunc", - "qualifiedName": "GraphViewModel.flowsForSelectedFunc", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 202, - "endLine": 208, - "startCol": 3, - "endCol": 4 - }, - "language": "typescript", - "provenance": "ast", - "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", - "moduleId": "module:web/src/viewmodel", - "signature": "flowsForSelectedFunc(): Flow[]", - "params": [], - "returnType": "Flow[]", - "loc": 7 - }, - { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", - "kind": "function", - "name": "openFlowForFunc", - "qualifiedName": "GraphViewModel.openFlowForFunc", - "location": { - "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 211, + "startLine": 214, "endLine": 217, "startCol": 3, "endCol": 4 @@ -2432,7 +2888,117 @@ "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "flows(): Flow[]", + "params": [], + "returnType": "Flow[]", + "loc": 4 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.currentFlow@219", + "kind": "function", + "name": "currentFlow", + "qualifiedName": "GraphViewModel.currentFlow", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 219, + "endLine": 222, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "currentFlow(): Flow | null", + "params": [], + "returnType": "Flow | null", + "loc": 4 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "kind": "function", + "name": "positionedGraph", + "qualifiedName": "GraphViewModel.positionedGraph", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 225, + "endLine": 246, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "positionedGraph(): PositionedGraph", + "params": [], + "returnType": "PositionedGraph", + "loc": 22 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFunc@248", + "kind": "function", + "name": "selectedFunc", + "qualifiedName": "GraphViewModel.selectedFunc", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 248, + "endLine": 253, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "selectedFunc(): Func | null", + "params": [], + "returnType": "Func | null", + "loc": 6 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.flowsForSelectedFunc@256", + "kind": "function", + "name": "flowsForSelectedFunc", + "qualifiedName": "GraphViewModel.flowsForSelectedFunc", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 256, + "endLine": 262, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "flowsForSelectedFunc(): Flow[]", + "params": [], + "returnType": "Flow[]", + "loc": 7 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", + "kind": "function", + "name": "openFlowForFunc", + "qualifiedName": "GraphViewModel.openFlowForFunc", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 265, + "endLine": 271, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "openFlowForFunc(flowId: string, funcId: string): void", "params": [ @@ -2451,21 +3017,21 @@ "loc": 7 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "kind": "function", "name": "setSearchQuery", "qualifiedName": "GraphViewModel.setSearchQuery", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 221, - "endLine": 223, + "startLine": 275, + "endLine": 277, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "setSearchQuery(q: string): void", "params": [ @@ -2479,21 +3045,21 @@ "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcIdsInFlows@225", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcIdsInFlows@279", "kind": "function", "name": "funcIdsInFlows", "qualifiedName": "GraphViewModel.funcIdsInFlows", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 225, - "endLine": 231, + "startLine": 279, + "endLine": 285, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "funcIdsInFlows(): Set", "params": [], @@ -2501,21 +3067,21 @@ "loc": 7 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "kind": "function", "name": "biggestFlowForFunc", "qualifiedName": "GraphViewModel.biggestFlowForFunc", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 233, - "endLine": 238, + "startLine": 287, + "endLine": 292, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "biggestFlowForFunc(funcId: string): Flow | null", "params": [ @@ -2529,21 +3095,21 @@ "loc": 6 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchResults@240", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchResults@294", "kind": "function", "name": "searchResults", "qualifiedName": "GraphViewModel.searchResults", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 240, - "endLine": 266, + "startLine": 294, + "endLine": 320, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "searchResults(): SearchResult[]", "params": [], @@ -2551,21 +3117,21 @@ "loc": 27 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "kind": "function", "name": "activateSearchResult", "qualifiedName": "GraphViewModel.activateSearchResult", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 268, - "endLine": 278, + "startLine": 322, + "endLine": 332, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "activateSearchResult(result: SearchResult): void", "params": [ @@ -2579,21 +3145,21 @@ "loc": 11 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "kind": "function", "name": "selectFlow", "qualifiedName": "GraphViewModel.selectFlow", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 280, - "endLine": 284, + "startLine": 334, + "endLine": 338, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "selectFlow(flowId: string): void", "params": [ @@ -2607,21 +3173,21 @@ "loc": 5 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "kind": "function", "name": "selectNode", "qualifiedName": "GraphViewModel.selectNode", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 286, - "endLine": 288, + "startLine": 340, + "endLine": 342, "startCol": 3, "endCol": 4 }, "language": "typescript", "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "moduleId": "module:web/src/viewmodel", "signature": "selectNode(nodeId: string | null): void", "params": [ @@ -2635,14 +3201,56 @@ "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@149@149", + "id": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", "kind": "function", - "name": "runInAction() 回调@149", - "qualifiedName": "runInAction() 回调@149", + "name": "loadSelectedFuncCode", + "qualifiedName": "GraphViewModel.loadSelectedFuncCode", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 149, - "endLine": 151, + "startLine": 346, + "endLine": 379, + "startCol": 3, + "endCol": 4 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "moduleId": "module:web/src/viewmodel", + "signature": "loadSelectedFuncCode(): Promise", + "params": [], + "returnType": "Promise", + "loc": 34 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "kind": "function", + "name": "reaction() 回调@79", + "qualifiedName": "reaction() 回调@79", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 79, + "endLine": 81, + "startCol": 7, + "endCol": 8 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "moduleId": "module:web/src/viewmodel", + "signature": "reaction() 回调@79()", + "params": [], + "loc": 3 + }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@203@203", + "kind": "function", + "name": "runInAction() 回调@203", + "qualifiedName": "runInAction() 回调@203", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 203, + "endLine": 205, "startCol": 19, "endCol": 8 }, @@ -2650,19 +3258,19 @@ "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", "moduleId": "module:web/src/viewmodel", - "signature": "runInAction() 回调@149()", + "signature": "runInAction() 回调@203()", "params": [], "loc": 3 }, { - "id": "func:web/src/viewmodel/GraphViewModel.ts#map() 回调@186@186", + "id": "func:web/src/viewmodel/GraphViewModel.ts#map() 回调@240@240", "kind": "function", - "name": "map() 回调@186", - "qualifiedName": "map() 回调@186", + "name": "map() 回调@240", + "qualifiedName": "map() 回调@240", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 186, - "endLine": 189, + "startLine": 240, + "endLine": 243, "startCol": 53, "endCol": 6 }, @@ -2670,7 +3278,7 @@ "provenance": "ast", "fileId": "file:web/src/viewmodel/GraphViewModel.ts", "moduleId": "module:web/src/viewmodel", - "signature": "map() 回调@186(n)", + "signature": "map() 回调@240(n)", "params": [ { "name": "n", @@ -2679,6 +3287,26 @@ ], "loc": 4 }, + { + "id": "func:web/src/viewmodel/GraphViewModel.ts#runInAction() 回调@368@368", + "kind": "function", + "name": "runInAction() 回调@368", + "qualifiedName": "runInAction() 回调@368", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 368, + "endLine": 377, + "startCol": 19, + "endCol": 8 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/viewmodel/GraphViewModel.ts", + "moduleId": "module:web/src/viewmodel", + "signature": "runInAction() 回调@368()", + "params": [], + "loc": 10 + }, { "id": "func:web/src/view/vmContext.ts#useGraphVM@6", "kind": "function", @@ -2754,14 +3382,14 @@ "loc": 27 }, { - "id": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "id": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "kind": "function", - "name": "observer() 回调@17", - "qualifiedName": "observer() 回调@17", + "name": "observer() 回调@16", + "qualifiedName": "observer() 回调@16", "location": { "file": "web/src/view/components/FlowGraph.tsx", - "startLine": 17, - "endLine": 72, + "startLine": 16, + "endLine": 70, "startCol": 35, "endCol": 2 }, @@ -2769,9 +3397,9 @@ "provenance": "ast", "fileId": "file:web/src/view/components/FlowGraph.tsx", "moduleId": "module:web/src/view/components", - "signature": "observer() 回调@17()", + "signature": "observer() 回调@16()", "params": [], - "loc": 56 + "loc": 55 }, { "id": "func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", @@ -2781,7 +3409,7 @@ "location": { "file": "web/src/view/components/FlowList.tsx", "startLine": 4, - "endLine": 25, + "endLine": 38, "startCol": 34, "endCol": 2 }, @@ -2791,7 +3419,7 @@ "moduleId": "module:web/src/view/components", "signature": "observer() 回调@4()", "params": [], - "loc": 22 + "loc": 35 }, { "id": "func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", @@ -2848,7 +3476,7 @@ "location": { "file": "web/src/view/components/NodeDetailPanel.tsx", "startLine": 13, - "endLine": 85, + "endLine": 98, "startCol": 41, "endCol": 2 }, @@ -2858,7 +3486,7 @@ "moduleId": "module:web/src/view/components", "signature": "observer() 回调@13()", "params": [], - "loc": 73 + "loc": 86 }, { "id": "func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6", @@ -2958,14 +3586,81 @@ "loc": 4 }, { - "id": "func:web/src/view/App.tsx#observer() 回调@9@9", + "id": "func:web/src/services/languageMap.ts#languageOf@42", "kind": "function", - "name": "observer() 回调@9", - "qualifiedName": "observer() 回调@9", + "name": "languageOf", + "qualifiedName": "languageOf", + "location": { + "file": "web/src/services/languageMap.ts", + "startLine": 42, + "endLine": 48, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/src/services/languageMap.ts", + "moduleId": "module:web/src/services", + "signature": "languageOf(file: string | undefined): string", + "params": [ + { + "name": "file", + "typeRef": "string | undefined", + "optional": false + } + ], + "returnType": "string", + "loc": 7 + }, + { + "id": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "kind": "function", + "name": "observer() 回调@7", + "qualifiedName": "observer() 回调@7", + "location": { + "file": "web/src/view/components/CodePanel.tsx", + "startLine": 7, + "endLine": 66, + "startCol": 35, + "endCol": 2 + }, + "language": "tsx", + "provenance": "ast", + "fileId": "file:web/src/view/components/CodePanel.tsx", + "moduleId": "module:web/src/view/components", + "signature": "observer() 回调@7()", + "params": [], + "loc": 60 + }, + { + "id": "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "kind": "function", + "name": "observer() 回调@4", + "qualifiedName": "observer() 回调@4", + "location": { + "file": "web/src/view/components/FlowBanner.tsx", + "startLine": 4, + "endLine": 16, + "startCol": 36, + "endCol": 2 + }, + "language": "tsx", + "provenance": "ast", + "fileId": "file:web/src/view/components/FlowBanner.tsx", + "moduleId": "module:web/src/view/components", + "signature": "observer() 回调@4()", + "params": [], + "loc": 13 + }, + { + "id": "func:web/src/view/App.tsx#observer() 回调@11@11", + "kind": "function", + "name": "observer() 回调@11", + "qualifiedName": "observer() 回调@11", "location": { "file": "web/src/view/App.tsx", - "startLine": 9, - "endLine": 54, + "startLine": 11, + "endLine": 60, "startCol": 29, "endCol": 2 }, @@ -2973,9 +3668,76 @@ "provenance": "ast", "fileId": "file:web/src/view/App.tsx", "moduleId": "module:web/src/view", - "signature": "observer() 回调@9()", + "signature": "observer() 回调@11()", "params": [], - "loc": 46 + "loc": 50 + }, + { + "id": "func:web/vite.config.ts#sourceServer@11", + "kind": "function", + "name": "sourceServer", + "qualifiedName": "sourceServer", + "location": { + "file": "web/vite.config.ts", + "startLine": 11, + "endLine": 52, + "startCol": 1, + "endCol": 2 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/vite.config.ts", + "moduleId": "module:web", + "signature": "sourceServer(): Plugin", + "params": [], + "returnType": "Plugin", + "loc": 42 + }, + { + "id": "func:web/vite.config.ts#use() 回调@15@15", + "kind": "function", + "name": "use() 回调@15", + "qualifiedName": "use() 回调@15", + "location": { + "file": "web/vite.config.ts", + "startLine": 15, + "endLine": 49, + "startCol": 43, + "endCol": 8 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:web/vite.config.ts", + "moduleId": "module:web", + "signature": "use() 回调@15(req, res)", + "params": [ + { + "name": "req", + "optional": false + }, + { + "name": "res", + "optional": false + } + ], + "loc": 35 + }, + { + "id": "func:scripts/annotate-self-report.mjs#@0", + "kind": "function", + "name": "", + "qualifiedName": "", + "location": { + "file": "scripts/annotate-self-report.mjs", + "startLine": 1, + "endLine": 119 + }, + "language": "javascript", + "provenance": "ast", + "fileId": "file:scripts/annotate-self-report.mjs", + "moduleId": "module:scripts", + "params": [], + "summary": "模块顶层代码(副作用/初始化)" }, { "id": "func:src/cli.ts#@0", @@ -2994,6 +3756,23 @@ "params": [], "summary": "模块顶层代码(副作用/初始化)" }, + { + "id": "func:src/overview-cli.ts#@0", + "kind": "function", + "name": "", + "qualifiedName": "", + "location": { + "file": "src/overview-cli.ts", + "startLine": 1, + "endLine": 87 + }, + "language": "typescript", + "provenance": "ast", + "fileId": "file:src/overview-cli.ts", + "moduleId": "module:src", + "params": [], + "summary": "模块顶层代码(副作用/初始化)" + }, { "id": "func:web/src/view/vmContext.ts#@0", "kind": "function", @@ -3036,7 +3815,7 @@ "location": { "file": "web/src/view/components/FlowGraph.tsx", "startLine": 1, - "endLine": 73 + "endLine": 71 }, "language": "tsx", "provenance": "ast", @@ -3053,7 +3832,7 @@ "location": { "file": "web/src/view/components/FlowList.tsx", "startLine": 1, - "endLine": 26 + "endLine": 39 }, "language": "tsx", "provenance": "ast", @@ -3087,7 +3866,7 @@ "location": { "file": "web/src/view/components/NodeDetailPanel.tsx", "startLine": 1, - "endLine": 86 + "endLine": 99 }, "language": "tsx", "provenance": "ast", @@ -3113,6 +3892,40 @@ "params": [], "summary": "模块顶层代码(副作用/初始化)" }, + { + "id": "func:web/src/view/components/CodePanel.tsx#@0", + "kind": "function", + "name": "", + "qualifiedName": "", + "location": { + "file": "web/src/view/components/CodePanel.tsx", + "startLine": 1, + "endLine": 67 + }, + "language": "tsx", + "provenance": "ast", + "fileId": "file:web/src/view/components/CodePanel.tsx", + "moduleId": "module:web/src/view/components", + "params": [], + "summary": "模块顶层代码(副作用/初始化)" + }, + { + "id": "func:web/src/view/components/FlowBanner.tsx#@0", + "kind": "function", + "name": "", + "qualifiedName": "", + "location": { + "file": "web/src/view/components/FlowBanner.tsx", + "startLine": 1, + "endLine": 17 + }, + "language": "tsx", + "provenance": "ast", + "fileId": "file:web/src/view/components/FlowBanner.tsx", + "moduleId": "module:web/src/view/components", + "params": [], + "summary": "模块顶层代码(副作用/初始化)" + }, { "id": "func:web/src/view/App.tsx#@0", "kind": "function", @@ -3121,7 +3934,7 @@ "location": { "file": "web/src/view/App.tsx", "startLine": 1, - "endLine": 55 + "endLine": 61 }, "language": "tsx", "provenance": "ast", @@ -3138,7 +3951,7 @@ "location": { "file": "web/src/main.tsx", "startLine": 1, - "endLine": 26 + "endLine": 27 }, "language": "tsx", "provenance": "ast", @@ -3156,7 +3969,7 @@ "location": { "file": "web/vite.config.ts", "startLine": 1, - "endLine": 11 + "endLine": 61 }, "language": "typescript", "provenance": "ast", @@ -3168,186 +3981,219 @@ ], "fields": [ { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loading@44", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loading@53", "kind": "field", "name": "loading", "qualifiedName": "GraphViewModel.loading", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 44, - "endLine": 44, + "startLine": 53, + "endLine": 53, "startCol": 3, "endCol": 19 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.error@45", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.error@54", "kind": "field", "name": "error", "qualifiedName": "GraphViewModel.error", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 45, - "endLine": 45, + "startLine": 54, + "endLine": 54, "startCol": 3, "endCol": 31 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.report@46", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.report@55", "kind": "field", "name": "report", "qualifiedName": "GraphViewModel.report", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 46, - "endLine": 46, + "startLine": 55, + "endLine": 55, "startCol": 3, "endCol": 32 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "Report | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedSourceId@48", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedSourceId@57", "kind": "field", "name": "selectedSourceId", "qualifiedName": "GraphViewModel.selectedSourceId", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 48, - "endLine": 48, + "startLine": 57, + "endLine": 57, "startCol": 3, "endCol": 42 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroupId@49", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedGroupId@58", "kind": "field", "name": "selectedGroupId", "qualifiedName": "GraphViewModel.selectedGroupId", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 49, - "endLine": 49, + "startLine": 58, + "endLine": 58, "startCol": 3, "endCol": 41 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFlowId@50", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFlowId@59", "kind": "field", "name": "selectedFlowId", "qualifiedName": "GraphViewModel.selectedFlowId", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 50, - "endLine": 50, + "startLine": 59, + "endLine": 59, "startCol": 3, "endCol": 40 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedNodeId@51", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedNodeId@60", "kind": "field", "name": "selectedNodeId", "qualifiedName": "GraphViewModel.selectedNodeId", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 51, - "endLine": 51, + "startLine": 60, + "endLine": 60, "startCol": 3, "endCol": 40 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", "typeRef": "string | null" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchQuery@52", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.searchQuery@61", "kind": "field", "name": "searchQuery", "qualifiedName": "GraphViewModel.searchQuery", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 52, - "endLine": 52, + "startLine": 61, + "endLine": 61, "startCol": 3, "endCol": 20 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcById@54", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.codeState@62", + "kind": "field", + "name": "codeState", + "qualifiedName": "GraphViewModel.codeState", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 62, + "endLine": 62, + "startCol": 3, + "endCol": 87 + }, + "language": "typescript", + "provenance": "ast", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52", + "typeRef": "CodeState" + }, + { + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.codePanelCollapsed@63", + "kind": "field", + "name": "codePanelCollapsed", + "qualifiedName": "GraphViewModel.codePanelCollapsed", + "location": { + "file": "web/src/viewmodel/GraphViewModel.ts", + "startLine": 63, + "endLine": 63, + "startCol": 3, + "endCol": 30 + }, + "language": "typescript", + "provenance": "ast", + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" + }, + { + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.funcById@65", "kind": "field", "name": "funcById", "qualifiedName": "GraphViewModel.funcById", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 54, - "endLine": 54, + "startLine": 65, + "endLine": 65, "startCol": 3, "endCol": 46 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.containerById@55", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.containerById@66", "kind": "field", "name": "containerById", "qualifiedName": "GraphViewModel.containerById", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 55, - "endLine": 55, + "startLine": 66, + "endLine": 66, "startCol": 3, "endCol": 56 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" }, { - "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.fileById@56", + "id": "field:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.fileById@67", "kind": "field", "name": "fileById", "qualifiedName": "GraphViewModel.fileById", "location": { "file": "web/src/viewmodel/GraphViewModel.ts", - "startLine": 56, - "endLine": 56, + "startLine": 67, + "endLine": 67, "startCol": 3, "endCol": 50 }, "language": "typescript", "provenance": "ast", - "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@43" + "ownerId": "container:web/src/viewmodel/GraphViewModel.ts#GraphViewModel@52" } ], "edges": [ @@ -3736,6 +4582,83 @@ "targetId": "func:src/cli.ts#main@40", "provenance": "resolved" }, + { + "id": "edge:import:file:src/overview.ts=>file:src/types.ts", + "kind": "import", + "sourceId": "file:src/overview.ts", + "targetId": "file:src/types.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:src/overview.ts#analyzeEntry@140=>func:src/overview.ts#reachableFuncs@92", + "kind": "call", + "sourceId": "func:src/overview.ts#analyzeEntry@140", + "targetId": "func:src/overview.ts#reachableFuncs@92", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#apiInOf@79", + "kind": "call", + "sourceId": "func:src/overview.ts#deriveOverview@109", + "targetId": "func:src/overview.ts#apiInOf@79", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#analyzeEntry@140", + "kind": "call", + "sourceId": "func:src/overview.ts#deriveOverview@109", + "targetId": "func:src/overview.ts#analyzeEntry@140", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#groupKeyFromPath@83", + "kind": "call", + "sourceId": "func:src/overview.ts#deriveOverview@109", + "targetId": "func:src/overview.ts#groupKeyFromPath@83", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview.ts#map() 回调@229@229=>func:src/overview.ts#analyzeEntry@140", + "kind": "call", + "sourceId": "func:src/overview.ts#map() 回调@229@229", + "targetId": "func:src/overview.ts#analyzeEntry@140", + "provenance": "resolved" + }, + { + "id": "edge:import:file:src/overview-cli.ts=>file:src/overview.ts", + "kind": "import", + "sourceId": "file:src/overview-cli.ts", + "targetId": "file:src/overview.ts", + "provenance": "ast" + }, + { + "id": "edge:import:file:src/overview-cli.ts=>file:src/types.ts", + "kind": "import", + "sourceId": "file:src/overview-cli.ts", + "targetId": "file:src/types.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:src/overview-cli.ts#main@9=>func:src/overview.ts#deriveOverview@109", + "kind": "call", + "sourceId": "func:src/overview-cli.ts#main@9", + "targetId": "func:src/overview.ts#deriveOverview@109", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview-cli.ts#main@9=>func:src/overview-cli.ts#flag@16", + "kind": "call", + "sourceId": "func:src/overview-cli.ts#main@9", + "targetId": "func:src/overview-cli.ts#flag@16", + "provenance": "resolved" + }, + { + "id": "edge:call:func:src/overview-cli.ts#@0=>func:src/overview-cli.ts#main@9", + "kind": "call", + "sourceId": "func:src/overview-cli.ts#@0", + "targetId": "func:src/overview-cli.ts#main@9", + "provenance": "resolved" + }, { "id": "edge:import:file:web/src/model/reportRepository.ts=>file:web/src/model/report.ts", "kind": "import", @@ -3793,59 +4716,80 @@ "provenance": "ast" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "edge:import:file:web/src/viewmodel/GraphViewModel.ts=>file:web/src/services/sourceService.ts", + "kind": "import", + "sourceId": "file:web/src/viewmodel/GraphViewModel.ts", + "targetId": "file:web/src/services/sourceService.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86=>func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171=>func:web/src/model/graphModel.ts#flowToGraph@43", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "kind": "call", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225=>func:web/src/model/graphModel.ts#flowToGraph@43", + "kind": "call", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", "targetId": "func:web/src/model/graphModel.ts#flowToGraph@43", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171=>func:web/src/services/dagreLayout.ts#computeLayout@31", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225=>func:web/src/services/dagreLayout.ts#computeLayout@31", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", "targetId": "func:web/src/services/dagreLayout.ts#computeLayout@31", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "kind": "call", - "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "sourceId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "provenance": "resolved" }, { @@ -3884,17 +4828,17 @@ "provenance": "ast" }, { - "id": "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17=>func:web/src/view/vmContext.ts#useGraphVM@6", + "id": "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16=>func:web/src/view/vmContext.ts#useGraphVM@6", "kind": "call", - "sourceId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "sourceId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "targetId": "func:web/src/view/vmContext.ts#useGraphVM@6", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "id": "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "kind": "call", - "sourceId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "sourceId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "provenance": "resolved" }, { @@ -3912,10 +4856,10 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "id": "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "kind": "call", "sourceId": "func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "provenance": "resolved" }, { @@ -3933,10 +4877,10 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "id": "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", "kind": "call", "sourceId": "func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", "provenance": "resolved" }, { @@ -3968,10 +4912,10 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "kind": "call", "sourceId": "func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "provenance": "resolved" }, { @@ -3996,17 +4940,17 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "id": "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "kind": "call", "sourceId": "func:web/src/view/components/SearchBar.tsx#activate@12", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "kind": "call", "sourceId": "func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "provenance": "resolved" }, { @@ -4017,10 +4961,10 @@ "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "kind": "call", "sourceId": "func:web/src/view/components/SearchBar.tsx#匿名函数@26@26", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "provenance": "resolved" }, { @@ -4030,6 +4974,55 @@ "targetId": "func:web/src/view/components/SearchBar.tsx#activate@12", "provenance": "resolved" }, + { + "id": "edge:import:file:web/src/view/components/CodePanel.tsx=>file:web/src/view/vmContext.ts", + "kind": "import", + "sourceId": "file:web/src/view/components/CodePanel.tsx", + "targetId": "file:web/src/view/vmContext.ts", + "provenance": "ast" + }, + { + "id": "edge:import:file:web/src/view/components/CodePanel.tsx=>file:web/src/services/languageMap.ts", + "kind": "import", + "sourceId": "file:web/src/view/components/CodePanel.tsx", + "targetId": "file:web/src/services/languageMap.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/view/vmContext.ts#useGraphVM@6", + "kind": "call", + "sourceId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "targetId": "func:web/src/view/vmContext.ts#useGraphVM@6", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/services/languageMap.ts#languageOf@42", + "kind": "call", + "sourceId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "targetId": "func:web/src/services/languageMap.ts#languageOf@42", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "kind": "call", + "sourceId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "provenance": "resolved" + }, + { + "id": "edge:import:file:web/src/view/components/FlowBanner.tsx=>file:web/src/view/vmContext.ts", + "kind": "import", + "sourceId": "file:web/src/view/components/FlowBanner.tsx", + "targetId": "file:web/src/view/vmContext.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4=>func:web/src/view/vmContext.ts#useGraphVM@6", + "kind": "call", + "sourceId": "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "targetId": "func:web/src/view/vmContext.ts#useGraphVM@6", + "provenance": "resolved" + }, { "id": "edge:import:file:web/src/view/App.tsx=>file:web/src/view/vmContext.ts", "kind": "import", @@ -4073,17 +5066,31 @@ "provenance": "ast" }, { - "id": "edge:call:func:web/src/view/App.tsx#observer() 回调@9@9=>func:web/src/view/vmContext.ts#useGraphVM@6", + "id": "edge:import:file:web/src/view/App.tsx=>file:web/src/view/components/CodePanel.tsx", + "kind": "import", + "sourceId": "file:web/src/view/App.tsx", + "targetId": "file:web/src/view/components/CodePanel.tsx", + "provenance": "ast" + }, + { + "id": "edge:import:file:web/src/view/App.tsx=>file:web/src/view/components/FlowBanner.tsx", + "kind": "import", + "sourceId": "file:web/src/view/App.tsx", + "targetId": "file:web/src/view/components/FlowBanner.tsx", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/view/App.tsx#observer() 回调@11@11=>func:web/src/view/vmContext.ts#useGraphVM@6", "kind": "call", - "sourceId": "func:web/src/view/App.tsx#observer() 回调@9@9", + "sourceId": "func:web/src/view/App.tsx#observer() 回调@11@11", "targetId": "func:web/src/view/vmContext.ts#useGraphVM@6", "provenance": "resolved" }, { - "id": "edge:call:func:web/src/view/App.tsx#observer() 回调@9@9=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "id": "edge:call:func:web/src/view/App.tsx#observer() 回调@11@11=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "kind": "call", - "sourceId": "func:web/src/view/App.tsx#observer() 回调@9@9", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "sourceId": "func:web/src/view/App.tsx#observer() 回调@11@11", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "provenance": "resolved" }, { @@ -4115,10 +5122,24 @@ "provenance": "ast" }, { - "id": "edge:call:func:web/src/main.tsx#@0=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "edge:import:file:web/src/main.tsx=>file:web/src/services/sourceService.ts", + "kind": "import", + "sourceId": "file:web/src/main.tsx", + "targetId": "file:web/src/services/sourceService.ts", + "provenance": "ast" + }, + { + "id": "edge:call:func:web/src/main.tsx#@0=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "kind": "call", "sourceId": "func:web/src/main.tsx#@0", - "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "targetId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", + "provenance": "resolved" + }, + { + "id": "edge:call:func:web/vite.config.ts#@0=>func:web/vite.config.ts#sourceServer@11", + "kind": "call", + "sourceId": "func:web/vite.config.ts#@0", + "targetId": "func:web/vite.config.ts#sourceServer@11", "provenance": "resolved" } ], @@ -4127,7 +5148,7 @@ "id": "flow:call:func:web/src/main.tsx#@0", "name": " (web/src/main.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/main.tsx#@0", "steps": [ { @@ -4136,27 +5157,27 @@ "nodeId": "func:web/src/main.tsx#@0", "title": "", "nextStepIds": [ - "step:flow:call:func:web/src/main.tsx#@0:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126" + "step:flow:call:func:web/src/main.tsx#@0:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180" ] }, { - "id": "step:flow:call:func:web/src/main.tsx#@0:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "step:flow:call:func:web/src/main.tsx#@0:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "order": 1, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "title": "GraphViewModel.load", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/main.tsx#@0=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126" + "edge:call:func:web/src/main.tsx#@0=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180" ], - "summary": "以 为入口的调用链,覆盖 2 个函数" + "summary": "前端启动:构建报告数据源与 ViewModel,挂载 React 应用" }, { "id": "flow:call:func:src/analyzer.ts#visit@245", "name": "visit (src/analyzer.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:src/analyzer.ts#visit@245", "steps": [ { @@ -4289,13 +5310,13 @@ "edge:call:func:src/analyzer.ts#visit@245=>func:src/analyzer.ts#containsCall@173", "edge:call:func:src/analyzer.ts#visit@245=>func:src/analyzer.ts#inlineName@202" ], - "summary": "以 visit 为入口的调用链,覆盖 13 个函数" + "summary": "AST 遍历核心:递归访问语法树,登记函数 / 容器并建立调用边" }, { "id": "flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13", "name": "observer() 回调@13 (web/src/view/components/NodeDetailPanel.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13", "steps": [ { @@ -4306,7 +5327,7 @@ "nextStepIds": [ "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/view/vmContext.ts#useGraphVM@6", "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/view/components/NodeDetailPanel.tsx#describeInteraction@5", - "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211" + "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265" ] }, { @@ -4324,9 +5345,9 @@ "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "step:flow:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "order": 3, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "title": "GraphViewModel.openFlowForFunc", "nextStepIds": [] } @@ -4334,36 +5355,83 @@ "edgeIds": [ "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/view/vmContext.ts#useGraphVM@6", "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/view/components/NodeDetailPanel.tsx#describeInteraction@5", - "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211" + "edge:call:func:web/src/view/components/NodeDetailPanel.tsx#observer() 回调@13@13=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265" ], - "summary": "以 observer() 回调@13 为入口的调用链,覆盖 4 个函数" + "summary": "渲染右侧节点详情:位置、归属、交互、所在流程" }, { - "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", - "name": "GraphViewModel.positionedGraph (web/src/viewmodel/GraphViewModel.ts)", + "id": "flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "name": "observer() 回调@7 (web/src/view/components/CodePanel.tsx)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", + "provenance": "manual", + "entryNodeId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", "steps": [ { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", + "id": "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", "order": 0, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171", - "title": "GraphViewModel.positionedGraph", + "nodeId": "func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7", + "title": "observer() 回调@7", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/model/graphModel.ts#flowToGraph@43", - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/services/dagreLayout.ts#computeLayout@31" + "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/view/vmContext.ts#useGraphVM@6", + "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/services/languageMap.ts#languageOf@42", + "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/model/graphModel.ts#flowToGraph@43", + "id": "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/view/vmContext.ts#useGraphVM@6", + "order": 1, + "nodeId": "func:web/src/view/vmContext.ts#useGraphVM@6", + "title": "useGraphVM", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/services/languageMap.ts#languageOf@42", + "order": 2, + "nodeId": "func:web/src/services/languageMap.ts#languageOf@42", + "title": "languageOf", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "order": 3, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104", + "title": "GraphViewModel.toggleCodePanel", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/view/vmContext.ts#useGraphVM@6", + "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/services/languageMap.ts#languageOf@42", + "edge:call:func:web/src/view/components/CodePanel.tsx#observer() 回调@7@7=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.toggleCodePanel@104" + ], + "summary": "渲染底部源码面板:随选中节点显示语法高亮源码" + }, + { + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "name": "GraphViewModel.positionedGraph (web/src/viewmodel/GraphViewModel.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "steps": [ + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "order": 0, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225", + "title": "GraphViewModel.positionedGraph", + "nextStepIds": [ + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/model/graphModel.ts#flowToGraph@43", + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/services/dagreLayout.ts#computeLayout@31" + ] + }, + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/model/graphModel.ts#flowToGraph@43", "order": 1, "nodeId": "func:web/src/model/graphModel.ts#flowToGraph@43", "title": "flowToGraph", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171:func:web/src/services/dagreLayout.ts#computeLayout@31", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225:func:web/src/services/dagreLayout.ts#computeLayout@31", "order": 2, "nodeId": "func:web/src/services/dagreLayout.ts#computeLayout@31", "title": "computeLayout", @@ -4371,54 +5439,54 @@ } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171=>func:web/src/model/graphModel.ts#flowToGraph@43", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@171=>func:web/src/services/dagreLayout.ts#computeLayout@31" + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225=>func:web/src/model/graphModel.ts#flowToGraph@43", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.positionedGraph@225=>func:web/src/services/dagreLayout.ts#computeLayout@31" ], - "summary": "以 GraphViewModel.positionedGraph 为入口的调用链,覆盖 3 个函数" + "summary": "计算当前流程的带坐标中性图(含 Dagre 自动布局)" }, { - "id": "flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", - "name": "observer() 回调@17 (web/src/view/components/FlowGraph.tsx)", + "id": "flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", + "name": "observer() 回调@16 (web/src/view/components/FlowGraph.tsx)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "provenance": "manual", + "entryNodeId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "steps": [ { - "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", + "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", "order": 0, - "nodeId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17", - "title": "observer() 回调@17", + "nodeId": "func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16", + "title": "observer() 回调@16", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286" + "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/view/vmContext.ts#useGraphVM@6", + "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340" ] }, { - "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/view/vmContext.ts#useGraphVM@6", + "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/view/vmContext.ts#useGraphVM@6", "order": 1, "nodeId": "func:web/src/view/vmContext.ts#useGraphVM@6", "title": "useGraphVM", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "id": "step:flow:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340", "title": "GraphViewModel.selectNode", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@17@17=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@286" + "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16=>func:web/src/view/vmContext.ts#useGraphVM@6", + "edge:call:func:web/src/view/components/FlowGraph.tsx#observer() 回调@16@16=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectNode@340" ], - "summary": "以 observer() 回调@17 为入口的调用链,覆盖 3 个函数" + "summary": "把中性图适配为 React Flow 节点 / 边并渲染画布" }, { "id": "flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", "name": "observer() 回调@4 (web/src/view/components/FlowList.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/FlowList.tsx#observer() 回调@4@4", "steps": [ { @@ -4428,7 +5496,7 @@ "title": "observer() 回调@4", "nextStepIds": [ "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280" + "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334" ] }, { @@ -4439,24 +5507,24 @@ "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "id": "step:flow:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334", "title": "GraphViewModel.selectFlow", "nextStepIds": [] } ], "edgeIds": [ "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@280" + "edge:call:func:web/src/view/components/FlowList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectFlow@334" ], - "summary": "以 observer() 回调@4 为入口的调用链,覆盖 3 个函数" + "summary": "渲染流程列表并处理流程选择" }, { "id": "flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", "name": "observer() 回调@4 (web/src/view/components/GroupList.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/GroupList.tsx#observer() 回调@4@4", "steps": [ { @@ -4466,7 +5534,7 @@ "title": "observer() 回调@4", "nextStepIds": [ "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117" + "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171" ] }, { @@ -4477,24 +5545,24 @@ "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "id": "step:flow:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171", "title": "GraphViewModel.selectGroup", "nextStepIds": [] } ], "edgeIds": [ "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@117" + "edge:call:func:web/src/view/components/GroupList.tsx#observer() 回调@4@4=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectGroup@171" ], - "summary": "以 observer() 回调@4 为入口的调用链,覆盖 3 个函数" + "summary": "渲染分组列表,按类 / 文件过滤流程" }, { "id": "flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6", "name": "observer() 回调@6 (web/src/view/components/SearchBar.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6", "steps": [ { @@ -4504,7 +5572,7 @@ "title": "observer() 回调@6", "nextStepIds": [ "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275" ] }, { @@ -4515,24 +5583,24 @@ "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "title": "GraphViewModel.setSearchQuery", "nextStepIds": [] } ], "edgeIds": [ "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221" + "edge:call:func:web/src/view/components/SearchBar.tsx#observer() 回调@6@6=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275" ], - "summary": "以 observer() 回调@6 为入口的调用链,覆盖 3 个函数" + "summary": "渲染搜索框与结果下拉" }, { "id": "flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26", "name": "匿名函数@26 (web/src/view/components/SearchBar.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/SearchBar.tsx#匿名函数@26@26", "steps": [ { @@ -4542,7 +5610,7 @@ "title": "匿名函数@26", "nextStepIds": [ "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/view/components/SearchBar.tsx#activate@12", - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275" ] }, { @@ -4551,103 +5619,103 @@ "nodeId": "func:web/src/view/components/SearchBar.tsx#activate@12", "title": "activate", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322" ] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275", "title": "GraphViewModel.setSearchQuery", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "order": 3, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "title": "GraphViewModel.activateSearchResult", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265" ] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "order": 4, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "title": "GraphViewModel.biggestFlowForFunc", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "order": 5, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "title": "GraphViewModel.openFlowForFunc", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", - "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", + "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/view/components/SearchBar.tsx#activate@12", - "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@221" + "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@26@26=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.setSearchQuery@275" ], - "summary": "以 匿名函数@26 为入口的调用链,覆盖 6 个函数" + "summary": "搜索输入变更处理回调" }, { - "id": "flow:call:func:web/src/view/App.tsx#observer() 回调@9@9", - "name": "observer() 回调@9 (web/src/view/App.tsx)", + "id": "flow:call:func:web/src/view/App.tsx#observer() 回调@11@11", + "name": "observer() 回调@11 (web/src/view/App.tsx)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/view/App.tsx#observer() 回调@9@9", + "provenance": "manual", + "entryNodeId": "func:web/src/view/App.tsx#observer() 回调@11@11", "steps": [ { - "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/view/App.tsx#observer() 回调@9@9", + "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/view/App.tsx#observer() 回调@11@11", "order": 0, - "nodeId": "func:web/src/view/App.tsx#observer() 回调@9@9", - "title": "observer() 回调@9", + "nodeId": "func:web/src/view/App.tsx#observer() 回调@11@11", + "title": "observer() 回调@11", "nextStepIds": [ - "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/view/vmContext.ts#useGraphVM@6", - "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74" + "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/view/vmContext.ts#useGraphVM@6", + "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93" ] }, { - "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/view/vmContext.ts#useGraphVM@6", + "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/view/vmContext.ts#useGraphVM@6", "order": 1, "nodeId": "func:web/src/view/vmContext.ts#useGraphVM@6", "title": "useGraphVM", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93", "title": "GraphViewModel.selectReport", "nextStepIds": [ - "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126" + "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180" ] }, { - "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@9@9:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "id": "step:flow:call:func:web/src/view/App.tsx#observer() 回调@11@11:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "order": 3, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", "title": "GraphViewModel.load", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@126", - "edge:call:func:web/src/view/App.tsx#observer() 回调@9@9=>func:web/src/view/vmContext.ts#useGraphVM@6", - "edge:call:func:web/src/view/App.tsx#observer() 回调@9@9=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@74" + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.load@180", + "edge:call:func:web/src/view/App.tsx#observer() 回调@11@11=>func:web/src/view/vmContext.ts#useGraphVM@6", + "edge:call:func:web/src/view/App.tsx#observer() 回调@11@11=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectReport@93" ], - "summary": "以 observer() 回调@9 为入口的调用链,覆盖 4 个函数" + "summary": "渲染应用骨架:头部、搜索、各面板布局" }, { "id": "flow:call:func:src/flows.ts#map() 回调@72@72", "name": "map() 回调@72 (src/flows.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:src/flows.ts#map() 回调@72@72", "steps": [ { @@ -4670,13 +5738,13 @@ "edgeIds": [ "edge:call:func:src/flows.ts#map() 回调@72@72=>func:src/flows.ts#stepIdOf@70" ], - "summary": "以 map() 回调@72 为入口的调用链,覆盖 2 个函数" + "summary": "生成流程步骤时的映射回调" }, { "id": "flow:call:func:src/analyzer.ts#inProject@65", "name": "inProject (src/analyzer.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:src/analyzer.ts#inProject@65", "steps": [ { @@ -4709,13 +5777,52 @@ "edge:call:func:src/analyzer.ts#relOf@64=>func:src/scanner.ts#toPosix@19", "edge:call:func:src/analyzer.ts#inProject@65=>func:src/analyzer.ts#relOf@64" ], - "summary": "以 inProject 为入口的调用链,覆盖 3 个函数" + "summary": "判断文件是否属于目标项目,过滤外部依赖" + }, + { + "id": "flow:call:func:src/overview.ts#map() 回调@229@229", + "name": "map() 回调@229 (src/overview.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:src/overview.ts#map() 回调@229@229", + "steps": [ + { + "id": "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#map() 回调@229@229", + "order": 0, + "nodeId": "func:src/overview.ts#map() 回调@229@229", + "title": "map() 回调@229", + "nextStepIds": [ + "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#analyzeEntry@140" + ] + }, + { + "id": "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#analyzeEntry@140", + "order": 1, + "nodeId": "func:src/overview.ts#analyzeEntry@140", + "title": "analyzeEntry", + "nextStepIds": [ + "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#reachableFuncs@92" + ] + }, + { + "id": "step:flow:call:func:src/overview.ts#map() 回调@229@229:func:src/overview.ts#reachableFuncs@92", + "order": 2, + "nodeId": "func:src/overview.ts#reachableFuncs@92", + "title": "reachableFuncs", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:src/overview.ts#analyzeEntry@140=>func:src/overview.ts#reachableFuncs@92", + "edge:call:func:src/overview.ts#map() 回调@229@229=>func:src/overview.ts#analyzeEntry@140" + ], + "summary": "概览推导中的流程映射回调" }, { "id": "flow:call:func:web/src/model/graphModel.ts#map() 回调@44@44", "name": "map() 回调@44 (web/src/model/graphModel.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/model/graphModel.ts#map() 回调@44@44", "steps": [ { @@ -4748,91 +5855,149 @@ "edge:call:func:web/src/model/graphModel.ts#qualifierOf@24=>func:web/src/model/graphModel.ts#baseName@19", "edge:call:func:web/src/model/graphModel.ts#map() 回调@44@44=>func:web/src/model/graphModel.ts#qualifierOf@24" ], - "summary": "以 map() 回调@44 为入口的调用链,覆盖 3 个函数" + "summary": "将流程步骤映射为图节点" }, { - "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "name": "GraphViewModel.groups (web/src/viewmodel/GraphViewModel.ts)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "steps": [ { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "order": 0, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135", "title": "GraphViewModel.groups", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86" + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "order": 1, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "title": "GraphViewModel.groupOfFlow", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#baseName@34" + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#baseName@43" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100:func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135:func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "title": "baseName", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86=>func:web/src/viewmodel/GraphViewModel.ts#baseName@34", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@100=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86" + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groups@135=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109" ], - "summary": "以 GraphViewModel.groups 为入口的调用链,覆盖 3 个函数" + "summary": "派生分组列表的计算属性" }, { - "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "name": "GraphViewModel.visibleFlows (web/src/viewmodel/GraphViewModel.ts)", "flowType": "call", - "provenance": "resolved", - "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "steps": [ { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "order": 0, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147", "title": "GraphViewModel.visibleFlows", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86" + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "order": 1, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109", "title": "GraphViewModel.groupOfFlow", "nextStepIds": [ - "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#baseName@34" + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#baseName@43" ] }, { - "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112:func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147:func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@34", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", "title": "baseName", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86=>func:web/src/viewmodel/GraphViewModel.ts#baseName@34", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@112=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@86" + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.visibleFlows@147=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.groupOfFlow@109" ], - "summary": "以 GraphViewModel.visibleFlows 为入口的调用链,覆盖 3 个函数" + "summary": "按当前分组过滤后的流程列表" + }, + { + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "name": "GraphViewModel.selectedFuncOwner (web/src/viewmodel/GraphViewModel.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "steps": [ + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "order": 0, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159", + "title": "GraphViewModel.selectedFuncOwner", + "nextStepIds": [ + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159:func:web/src/viewmodel/GraphViewModel.ts#baseName@43" + ] + }, + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159:func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "order": 1, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#baseName@43", + "title": "baseName", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.selectedFuncOwner@159=>func:web/src/viewmodel/GraphViewModel.ts#baseName@43" + ], + "summary": "计算选中函数的归属(类 / 文件)及其说明" + }, + { + "id": "flow:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "name": "reaction() 回调@79 (web/src/viewmodel/GraphViewModel.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "steps": [ + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "order": 0, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79", + "title": "reaction() 回调@79", + "nextStepIds": [ + "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346" + ] + }, + { + "id": "step:flow:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", + "order": 1, + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346", + "title": "GraphViewModel.loadSelectedFuncCode", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#reaction() 回调@79@79=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.loadSelectedFuncCode@346" + ], + "summary": "监听选中节点变化,自动拉取对应源码" }, { "id": "flow:call:func:web/src/view/components/FlowNode.tsx#memo() 回调@40@40", "name": "memo() 回调@40 (web/src/view/components/FlowNode.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/FlowNode.tsx#memo() 回调@40@40", "steps": [ { @@ -4855,13 +6020,13 @@ "edgeIds": [ "edge:call:func:web/src/view/components/FlowNode.tsx#memo() 回调@40@40=>func:web/src/view/components/FlowNode.tsx#interactionBadges@12" ], - "summary": "以 memo() 回调@40 为入口的调用链,覆盖 2 个函数" + "summary": "自定义节点渲染(memo 回调)" }, { "id": "flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41", "name": "匿名函数@41 (web/src/view/components/SearchBar.tsx)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:web/src/view/components/SearchBar.tsx#匿名函数@41@41", "steps": [ { @@ -4879,47 +6044,76 @@ "nodeId": "func:web/src/view/components/SearchBar.tsx#activate@12", "title": "activate", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322" ] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "order": 2, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "title": "GraphViewModel.activateSearchResult", "nextStepIds": [ - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211" + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265" ] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "order": 3, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", "title": "GraphViewModel.biggestFlowForFunc", "nextStepIds": [] }, { - "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "id": "step:flow:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "order": 4, - "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", + "nodeId": "func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", "title": "GraphViewModel.openFlowForFunc", "nextStepIds": [] } ], "edgeIds": [ - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@233", - "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@211", - "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@268", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.biggestFlowForFunc@287", + "edge:call:func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.openFlowForFunc@265", + "edge:call:func:web/src/view/components/SearchBar.tsx#activate@12=>func:web/src/viewmodel/GraphViewModel.ts#GraphViewModel.activateSearchResult@322", "edge:call:func:web/src/view/components/SearchBar.tsx#匿名函数@41@41=>func:web/src/view/components/SearchBar.tsx#activate@12" ], - "summary": "以 匿名函数@41 为入口的调用链,覆盖 5 个函数" + "summary": "搜索结果项点击处理" + }, + { + "id": "flow:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "name": "observer() 回调@4 (web/src/view/components/FlowBanner.tsx)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "steps": [ + { + "id": "step:flow:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "order": 0, + "nodeId": "func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4", + "title": "observer() 回调@4", + "nextStepIds": [ + "step:flow:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4:func:web/src/view/vmContext.ts#useGraphVM@6" + ] + }, + { + "id": "step:flow:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4:func:web/src/view/vmContext.ts#useGraphVM@6", + "order": 1, + "nodeId": "func:web/src/view/vmContext.ts#useGraphVM@6", + "title": "useGraphVM", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/src/view/components/FlowBanner.tsx#observer() 回调@4@4=>func:web/src/view/vmContext.ts#useGraphVM@6" + ], + "summary": "渲染当前流程横幅:流程名与一句话说明" }, { "id": "flow:call:func:src/cli.ts#@0", "name": " (src/cli.ts)", "flowType": "call", - "provenance": "resolved", + "provenance": "manual", "entryNodeId": "func:src/cli.ts#@0", "steps": [ { @@ -5183,7 +6377,122 @@ "edge:call:func:src/cli.ts#main@40=>func:src/analyzer.ts#analyzeProject@32", "edge:call:func:src/cli.ts#@0=>func:src/cli.ts#main@40" ], - "summary": "以 为入口的调用链,覆盖 26 个函数" + "summary": "CLI 主流程:解析参数、运行分析、写出报告" + }, + { + "id": "flow:call:func:src/overview-cli.ts#@0", + "name": " (src/overview-cli.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:src/overview-cli.ts#@0", + "steps": [ + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#@0", + "order": 0, + "nodeId": "func:src/overview-cli.ts#@0", + "title": "", + "nextStepIds": [ + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#main@9" + ] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#main@9", + "order": 1, + "nodeId": "func:src/overview-cli.ts#main@9", + "title": "main", + "nextStepIds": [ + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#deriveOverview@109", + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#flag@16" + ] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#deriveOverview@109", + "order": 2, + "nodeId": "func:src/overview.ts#deriveOverview@109", + "title": "deriveOverview", + "nextStepIds": [ + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#apiInOf@79", + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#analyzeEntry@140", + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#groupKeyFromPath@83" + ] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview-cli.ts#flag@16", + "order": 3, + "nodeId": "func:src/overview-cli.ts#flag@16", + "title": "flag", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#apiInOf@79", + "order": 4, + "nodeId": "func:src/overview.ts#apiInOf@79", + "title": "apiInOf", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#analyzeEntry@140", + "order": 5, + "nodeId": "func:src/overview.ts#analyzeEntry@140", + "title": "analyzeEntry", + "nextStepIds": [ + "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#reachableFuncs@92" + ] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#groupKeyFromPath@83", + "order": 6, + "nodeId": "func:src/overview.ts#groupKeyFromPath@83", + "title": "groupKeyFromPath", + "nextStepIds": [] + }, + { + "id": "step:flow:call:func:src/overview-cli.ts#@0:func:src/overview.ts#reachableFuncs@92", + "order": 7, + "nodeId": "func:src/overview.ts#reachableFuncs@92", + "title": "reachableFuncs", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:src/overview.ts#analyzeEntry@140=>func:src/overview.ts#reachableFuncs@92", + "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#apiInOf@79", + "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#analyzeEntry@140", + "edge:call:func:src/overview.ts#deriveOverview@109=>func:src/overview.ts#groupKeyFromPath@83", + "edge:call:func:src/overview-cli.ts#main@9=>func:src/overview.ts#deriveOverview@109", + "edge:call:func:src/overview-cli.ts#main@9=>func:src/overview-cli.ts#flag@16", + "edge:call:func:src/overview-cli.ts#@0=>func:src/overview-cli.ts#main@9" + ], + "summary": "概览 CLI 主流程:读取报告并打印项目地图" + }, + { + "id": "flow:call:func:web/vite.config.ts#@0", + "name": " (web/vite.config.ts)", + "flowType": "call", + "provenance": "manual", + "entryNodeId": "func:web/vite.config.ts#@0", + "steps": [ + { + "id": "step:flow:call:func:web/vite.config.ts#@0:func:web/vite.config.ts#@0", + "order": 0, + "nodeId": "func:web/vite.config.ts#@0", + "title": "", + "nextStepIds": [ + "step:flow:call:func:web/vite.config.ts#@0:func:web/vite.config.ts#sourceServer@11" + ] + }, + { + "id": "step:flow:call:func:web/vite.config.ts#@0:func:web/vite.config.ts#sourceServer@11", + "order": 1, + "nodeId": "func:web/vite.config.ts#sourceServer@11", + "title": "sourceServer", + "nextStepIds": [] + } + ], + "edgeIds": [ + "edge:call:func:web/vite.config.ts#@0=>func:web/vite.config.ts#sourceServer@11" + ], + "summary": "Vite 配置与源码服务中间件初始化" } ], "externalSystems": [] diff --git a/web/src/main.tsx b/web/src/main.tsx index fb253ee..7649a4c 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -6,6 +6,7 @@ import { App } from "./view/App"; import { VMContext } from "./view/vmContext"; import { GraphViewModel } from "./viewmodel/GraphViewModel"; import { HttpReportRepository, type ReportSource } from "./model/reportRepository"; +import { fetchSource } from "./services/sourceService"; // 可切换的报告数据源;新增项目只需往这里加一条 const sources: ReportSource[] = [ @@ -13,7 +14,7 @@ const sources: ReportSource[] = [ { id: "self", name: "code-visualize(本工具)", url: "/self-report.json" }, ]; -const viewModel = new GraphViewModel(sources, (url) => new HttpReportRepository(url)); +const viewModel = new GraphViewModel(sources, (url) => new HttpReportRepository(url), fetchSource); void viewModel.load(); ReactDOM.createRoot(document.getElementById("root")!).render( diff --git a/web/src/services/dagreLayout.ts b/web/src/services/dagreLayout.ts index 657dbfd..6e7c12c 100644 --- a/web/src/services/dagreLayout.ts +++ b/web/src/services/dagreLayout.ts @@ -60,3 +60,63 @@ export function computeLayout( } return result; } + +export interface ClusterRect extends Position { + id: string; + width: number; + height: number; +} + +export interface ClusteredLayout { + positions: Map; + clusters: ClusterRect[]; +} + +/** + * 带「簇(背景大框)」的分层布局:把同组节点聚在一起,返回节点坐标与每个簇的包围矩形。 + * 基于 dagre 复合图(compound graph)实现,dagre 会尽量避免不同簇交叉。 + */ +export function computeClusteredLayout( + nodes: LayoutNodeInput[], + edges: LayoutEdgeInput[], + nodeToGroup: Map, + options: LayoutOptions = {} +): ClusteredLayout { + const { direction = "LR", nodeSep = 40, rankSep = 90 } = options; + + const g = new dagre.graphlib.Graph({ compound: true }); + g.setGraph({ rankdir: direction, nodesep: nodeSep, ranksep: rankSep, marginx: 24, marginy: 24 }); + g.setDefaultEdgeLabel(() => ({})); + + const groupIds = new Set(nodeToGroup.values()); + for (const gid of groupIds) { + // 簇(父节点)用带内边距的空标签,dagre 会据子节点算出包围盒 + g.setNode(gid, { clusterLabelPos: "top", paddingTop: 26, paddingBottom: 16, paddingLeft: 16, paddingRight: 16 }); + } + + for (const n of nodes) { + g.setNode(n.id, { width: n.width, height: n.height }); + const gid = nodeToGroup.get(n.id); + if (gid) g.setParent(n.id, gid); + } + for (const e of edges) { + if (e.source !== e.target) g.setEdge(e.source, e.target); + } + + dagre.layout(g); + + const positions = new Map(); + for (const n of nodes) { + const node = g.node(n.id); + positions.set(n.id, node ? { x: node.x - n.width / 2, y: node.y - n.height / 2 } : { x: 0, y: 0 }); + } + + const clusters: ClusterRect[] = []; + for (const gid of groupIds) { + const c = g.node(gid) as { x: number; y: number; width: number; height: number } | undefined; + if (!c || !c.width) continue; + clusters.push({ id: gid, x: c.x - c.width / 2, y: c.y - c.height / 2, width: c.width, height: c.height }); + } + + return { positions, clusters }; +} diff --git a/web/src/services/languageMap.ts b/web/src/services/languageMap.ts new file mode 100644 index 0000000..c707915 --- /dev/null +++ b/web/src/services/languageMap.ts @@ -0,0 +1,48 @@ +// 文件扩展名 → Prism 语法高亮语言标识 + +const EXT_TO_LANG: Record = { + ts: "typescript", + tsx: "tsx", + mts: "typescript", + cts: "typescript", + js: "javascript", + jsx: "jsx", + mjs: "javascript", + cjs: "javascript", + json: "json", + py: "python", + go: "go", + java: "java", + kt: "kotlin", + rs: "rust", + rb: "ruby", + php: "php", + cs: "csharp", + cpp: "cpp", + cc: "cpp", + c: "c", + h: "c", + hpp: "cpp", + swift: "swift", + scala: "scala", + sh: "bash", + bash: "bash", + sql: "sql", + css: "css", + scss: "scss", + less: "less", + html: "markup", + xml: "markup", + vue: "markup", + yaml: "yaml", + yml: "yaml", + md: "markdown", +}; + +export function languageOf(file: string | undefined): string { + if (!file) return "text"; + const idx = file.lastIndexOf("."); + if (idx < 0) return "text"; + const ext = file.slice(idx + 1).toLowerCase(); + return EXT_TO_LANG[ext] ?? "text"; +} diff --git a/web/src/services/sourceService.ts b/web/src/services/sourceService.ts new file mode 100644 index 0000000..52ecb18 --- /dev/null +++ b/web/src/services/sourceService.ts @@ -0,0 +1,32 @@ +// 源码绑定:按报告里的 location(项目根 + 相对路径 + 行号区间) +// 向开发服务器请求真实源码片段。保持报告精简、内容永远与代码库一致。 + +export interface CodeSnippet { + file: string; + startLine: number; + endLine: number; + code: string; +} + +export type LoadSource = ( + root: string, + file: string, + startLine: number, + endLine: number +) => Promise; + +/** 通过 Vite 开发中间件 /__source 读取源码片段 */ +export const fetchSource: LoadSource = async (root, file, startLine, endLine) => { + const params = new URLSearchParams({ + root, + file, + start: String(startLine), + end: String(endLine), + }); + const res = await fetch(`/__source?${params.toString()}`); + if (!res.ok) { + const text = await res.text().catch(() => ""); + throw new Error(`HTTP ${res.status}${text ? ` · ${text}` : ""}`); + } + return (await res.json()) as CodeSnippet; +}; diff --git a/web/src/styles.css b/web/src/styles.css index 2d973bf..8cffbac 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -1,3 +1,8 @@ +:root { + --font-mono: "JetBrains Mono", "Cascadia Code", "SF Mono", ui-monospace, + "Menlo", "Consolas", monospace; +} + * { box-sizing: border-box; } @@ -53,6 +58,25 @@ body { flex: 0 0 auto; } +.app__groupmode { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 12px; + color: #cbd5e1; + flex: 0 0 auto; +} + +.app__groupmode select { + background: #1e293b; + color: #e2e8f0; + border: 1px solid #334155; + border-radius: 6px; + padding: 3px 8px; + font-size: 12.5px; + cursor: pointer; +} + /* ---------- 搜索栏 ---------- */ .search { position: relative; @@ -171,10 +195,70 @@ body { color: #dc2626; } +.app__center { + flex: 1 1 auto; + min-width: 0; + display: flex; + flex-direction: column; +} + .app__canvas { flex: 1 1 auto; min-width: 0; - height: 100%; + min-height: 0; +} + +/* ---------- 当前流程横幅 ---------- */ +.flowbanner { + flex: 0 0 auto; + padding: 8px 16px; + background: #fff; + border-bottom: 1px solid #e2e8f0; +} + +.flowbanner__title { + font-size: 13.5px; + font-weight: 700; + color: #0f172a; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.flowbanner__summary { + font-size: 12px; + color: #475569; + margin-top: 2px; + line-height: 1.4; +} + +.flowbanner__summary--empty { + color: #cbd5e1; + font-style: italic; +} + +/* ---------- 流程列表:分组说明 ---------- */ +.flowlist__groupinfo { + flex: 0 0 auto; + padding: 8px 12px 10px; + background: #f8fafc; + border-bottom: 1px solid #eef2f6; +} + +.flowlist__groupname { + display: flex; + align-items: center; + gap: 6px; + font-size: 12.5px; + font-weight: 600; + color: #334155; +} + +.flowlist__groupsummary { + font-size: 11.5px; + color: #64748b; + line-height: 1.45; + margin-top: 4px; } /* ---------- 侧栏面板 ---------- */ @@ -191,12 +275,12 @@ body { } .panel--left { - width: 280px; + width: 420px; border-right: 1px solid #e2e8f0; } .panel--right { - width: 320px; + width: 380px; border-left: 1px solid #e2e8f0; } @@ -225,10 +309,10 @@ body { border: 1px solid transparent; background: transparent; border-radius: 8px; - padding: 8px 10px; + padding: 9px 11px; cursor: pointer; - font-size: 12.5px; - color: #475569; + font-size: 13.5px; + color: #1e293b; } .flow-item:hover { @@ -245,6 +329,7 @@ body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + font-weight: 500; } .flow-item__count { @@ -439,7 +524,7 @@ body { } .detail__mono { - font-family: Consolas, "Courier New", monospace; + font-family: var(--font-mono); font-size: 12px; color: #334155; word-break: break-all; @@ -452,6 +537,16 @@ body { padding: 6px 8px; } +.detail__owner-summary { + font-size: 12px; + color: #475569; + line-height: 1.5; + background: #f8fafc; + border: 1px solid #eef2f6; + border-radius: 6px; + padding: 6px 8px; +} + .detail__ix { list-style: none; margin: 0; @@ -545,3 +640,166 @@ body { margin: auto; color: #94a3b8; } + +/* ---------- 背景分组大框 ---------- */ +.group-frame { + width: 100%; + height: 100%; + border: 1.5px dashed; + border-radius: 12px; + pointer-events: none; +} + +.group-frame__label { + position: absolute; + top: 5px; + left: 12px; + font-size: 11.5px; + font-weight: 700; + font-family: var(--font-mono); + opacity: 0.85; + pointer-events: none; +} + +/* ---------- 底部代码面板(亮色主题) ---------- */ +.codepanel { + flex: 0 0 auto; + height: 300px; + min-height: 120px; + display: flex; + flex-direction: column; + border-top: 1px solid #e2e8f0; + background: #ffffff; +} + +.codepanel--collapsed { + height: auto; + min-height: 0; +} + +.codepanel--collapsed .codepanel__body { + display: none; +} + +.codepanel__header { + flex: 0 0 auto; + display: flex; + align-items: center; + gap: 10px; + padding: 6px 14px; + background: #f8fafc; + border-bottom: 1px solid #e2e8f0; + font-size: 12px; + cursor: pointer; + user-select: none; +} + +.codepanel__header:hover { + background: #eef2f6; +} + +.codepanel__toggle { + flex: 0 0 auto; + width: 20px; + height: 20px; + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid #cbd5e1; + background: #fff; + color: #334155; + font-size: 15px; + font-weight: 700; + line-height: 1; + border-radius: 5px; +} + +.codepanel__header:hover .codepanel__toggle { + background: #e2e8f0; + color: #0f172a; +} + +.codepanel__title { + font-weight: 700; + color: #334155; +} + +.codepanel__loc { + font-family: var(--font-mono); + color: #2563eb; +} + +.codepanel__lang { + font-size: 10.5px; + font-weight: 700; + color: #6d28d9; + background: #ede9fe; + border-radius: 5px; + padding: 1px 7px; + text-transform: uppercase; + letter-spacing: 0.3px; +} + +.codepanel__sig { + color: #94a3b8; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-family: var(--font-mono); +} + +.codepanel__body { + flex: 1 1 auto; + min-height: 0; + overflow: auto; +} + +.codepanel__empty { + padding: 16px; + color: #94a3b8; + font-size: 12.5px; +} + +/* ---------- 源码视图 ---------- */ +.code { + margin: 0; + background: #ffffff; + padding: 8px 0; + font-family: var(--font-mono); + font-size: 12.5px; + line-height: 1.55; +} + +.code code { + display: block; + min-width: max-content; +} + +.code__line { + display: flex; + white-space: pre; +} + +.code__line:hover { + background: #f1f5f9; +} + +.code__ln { + flex: 0 0 auto; + width: 46px; + padding: 0 12px 0 0; + text-align: right; + color: #cbd5e1; + user-select: none; +} + +.code__text { + flex: 1 1 auto; + padding: 0 12px 0 12px; + color: #1e293b; + border-left: 1px solid #eef2f6; +} + +.code__error { + color: #dc2626; +} diff --git a/web/src/view/App.tsx b/web/src/view/App.tsx index 5a3ed36..c8587b1 100644 --- a/web/src/view/App.tsx +++ b/web/src/view/App.tsx @@ -5,6 +5,8 @@ import { FlowList } from "./components/FlowList"; import { GroupList } from "./components/GroupList"; import { NodeDetailPanel } from "./components/NodeDetailPanel"; import { SearchBar } from "./components/SearchBar"; +import { CodePanel } from "./components/CodePanel"; +import { FlowBanner } from "./components/FlowBanner"; export const App = observer(function App() { const vm = useGraphVM(); @@ -27,6 +29,19 @@ export const App = observer(function App() { {vm.report ? : null} + {vm.report ? ( + + ) : null} {vm.report ? (
文件 {vm.report.stats.fileCount} · 函数 {vm.report.stats.functionCount} · 边{" "} @@ -42,9 +57,13 @@ export const App = observer(function App() { <> -
- -
+
+ +
+ +
+ +
) : null} diff --git a/web/src/view/components/CodePanel.tsx b/web/src/view/components/CodePanel.tsx new file mode 100644 index 0000000..6925741 --- /dev/null +++ b/web/src/view/components/CodePanel.tsx @@ -0,0 +1,66 @@ +import { observer } from "mobx-react-lite"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { oneLight } from "react-syntax-highlighter/dist/esm/styles/prism"; +import { useGraphVM } from "../vmContext"; +import { languageOf } from "../../services/languageMap"; + +export const CodePanel = observer(function CodePanel() { + const vm = useGraphVM(); + const fn = vm.selectedFunc; + const { loading, error, snippet } = vm.codeState; + + const location = fn?.location ? `${fn.location.file}:${fn.location.startLine}` : ""; + const language = languageOf(fn?.location?.file); + const collapsed = vm.codePanelCollapsed; + + return ( +
+
vm.toggleCodePanel()} + title={collapsed ? "展开源码" : "隐藏源码"} + > + {collapsed ? "+" : "−"} + 源码 + {fn ? {location} : null} + {fn ? {language} : null} + {fn?.signature ? {fn.signature} : null} +
+
+ {!fn ? ( +
选中流程图中的节点,这里显示对应源码实现
+ ) : loading ? ( +
加载源码中…
+ ) : error ? ( +
无法加载源码: {error}
+ ) : !snippet || !snippet.code ? ( +
无源码
+ ) : ( + + {snippet.code} + + )} +
+
+ ); +}); diff --git a/web/src/view/components/FlowBanner.tsx b/web/src/view/components/FlowBanner.tsx new file mode 100644 index 0000000..fc47708 --- /dev/null +++ b/web/src/view/components/FlowBanner.tsx @@ -0,0 +1,16 @@ +import { observer } from "mobx-react-lite"; +import { useGraphVM } from "../vmContext"; + +export const FlowBanner = observer(function FlowBanner() { + const vm = useGraphVM(); + const flow = vm.currentFlow; + if (!flow) return null; + + return ( +
+
+ {flow.name} +
+
+ ); +}); diff --git a/web/src/view/components/FlowGraph.tsx b/web/src/view/components/FlowGraph.tsx index 68ca50a..a196a96 100644 --- a/web/src/view/components/FlowGraph.tsx +++ b/web/src/view/components/FlowGraph.tsx @@ -4,32 +4,62 @@ import { ReactFlow, Background, Controls, - MiniMap, MarkerType, type Node, type Edge, } from "@xyflow/react"; import { useGraphVM } from "../vmContext"; import { FlowNode, type FlowNodeData } from "./FlowNode"; +import { GroupFrameNode, type GroupFrameData } from "./GroupFrameNode"; -const nodeTypes = { flowNode: FlowNode }; +const nodeTypes = { flowNode: FlowNode, groupFrame: GroupFrameNode }; + +// 为分组(目录)稳定地分配一个颜色 +function colorForGroup(id: string): string { + let h = 0; + for (let i = 0; i < id.length; i++) h = (h * 31 + id.charCodeAt(i)) % 360; + return `hsl(${h}, 55%, 45%)`; +} + +function lastSegments(dir: string, n = 2): string { + const parts = dir.split("/").filter(Boolean); + return parts.slice(-n).join("/") || dir; +} + +// 文件分组时(键形如 src/cli.ts)只显示文件名,目录分组时显示末两级 +function frameLabel(id: string): string { + return /\.[a-z0-9]+$/i.test(id) ? id.split("/").pop() || id : lastSegments(id); +} export const FlowGraph = observer(function FlowGraph() { const vm = useGraphVM(); - const { nodes, edges } = vm.positionedGraph; + const { nodes, edges, clusters } = vm.positionedGraph; const selectedNodeId = vm.selectedNodeId; - const rfNodes: Node[] = useMemo( - () => - nodes.map((n) => ({ - id: n.id, - type: "flowNode", - position: { x: n.x, y: n.y }, - selected: n.id === selectedNodeId, - data: { label: n.label, qualifier: n.qualifier, isEntry: n.isEntry, interactions: n.interactions }, - })), - [nodes, selectedNodeId] - ); + const rfNodes: Node[] = useMemo(() => { + const frames: Node[] = clusters.map((c) => ({ + id: `frame:${c.id}`, + type: "groupFrame", + position: { x: c.x, y: c.y }, + data: { label: frameLabel(c.id), color: colorForGroup(c.id) }, + style: { width: c.width, height: c.height }, + selectable: false, + draggable: false, + focusable: false, + zIndex: 0, + })); + + const flowNodes: Node[] = nodes.map((n) => ({ + id: n.id, + type: "flowNode", + position: { x: n.x, y: n.y }, + selected: n.id === selectedNodeId, + data: { label: n.label, qualifier: n.qualifier, isEntry: n.isEntry, interactions: n.interactions }, + zIndex: 1, + })); + + return [...frames, ...flowNodes]; + }, [nodes, clusters, selectedNodeId]); const rfEdges: Edge[] = useMemo( () => @@ -61,12 +91,14 @@ export const FlowGraph = observer(function FlowGraph() { minZoom={0.1} maxZoom={2} proOptions={{ hideAttribution: true }} - onNodeClick={(_, node) => vm.selectNode(node.id)} + onNodeClick={(_, node) => { + if (node.id.startsWith("frame:")) return; + vm.selectNode(node.id); + }} onPaneClick={() => vm.selectNode(null)} > - ); }); diff --git a/web/src/view/components/FlowList.tsx b/web/src/view/components/FlowList.tsx index ac7b85b..7986e1f 100644 --- a/web/src/view/components/FlowList.tsx +++ b/web/src/view/components/FlowList.tsx @@ -4,18 +4,31 @@ import { useGraphVM } from "../vmContext"; export const FlowList = observer(function FlowList() { const vm = useGraphVM(); + const group = vm.selectedGroup; + return (
+ {vm.selectedFuncOwner ? ( +
+
+ 归属{vm.selectedFuncOwner.kind === "class" ? "类" : "文件"} · {vm.selectedFuncOwner.name} +
+ {vm.selectedFuncOwner.summary ? ( +
{vm.selectedFuncOwner.summary}
+ ) : ( +
暂无说明
+ )} +
+ ) : null} + {fn.signature ? (
签名
diff --git a/web/src/viewmodel/GraphViewModel.ts b/web/src/viewmodel/GraphViewModel.ts index 2170aa3..609bc92 100644 --- a/web/src/viewmodel/GraphViewModel.ts +++ b/web/src/viewmodel/GraphViewModel.ts @@ -1,8 +1,16 @@ -import { makeAutoObservable, runInAction } from "mobx"; +import { makeAutoObservable, reaction, runInAction } from "mobx"; import type { Report, Flow, Func, Container, CodeFile } from "../model/report"; import type { ReportRepository, ReportSource } from "../model/reportRepository"; import { flowToGraph, type GraphEdge, type GraphNode } from "../model/graphModel"; -import { computeLayout, type Position } from "../services/dagreLayout"; +import { computeClusteredLayout, type ClusterRect, type Position } from "../services/dagreLayout"; +import type { CodeSnippet, LoadSource } from "../services/sourceService"; + +export interface CodeState { + funcId: string | null; + loading: boolean; + error: string | null; + snippet: CodeSnippet | null; +} export const NODE_WIDTH = 220; export const NODE_HEIGHT = 68; @@ -15,6 +23,7 @@ export interface PositionedNode extends GraphNode, Position { export interface PositionedGraph { nodes: PositionedNode[]; edges: GraphEdge[]; + clusters: ClusterRect[]; } export interface FlowGroup { @@ -22,8 +31,11 @@ export interface FlowGroup { name: string; kind: "class" | "file"; count: number; + summary?: string; } +export type GroupMode = "none" | "file" | "dir"; + export interface SearchResult { id: string; kind: "flow" | "func"; @@ -50,6 +62,9 @@ export class GraphViewModel { selectedFlowId: string | null = null; selectedNodeId: string | null = null; searchQuery = ""; + codeState: CodeState = { funcId: null, loading: false, error: null, snippet: null }; + codePanelCollapsed = false; + groupMode: GroupMode = "dir"; private funcById = new Map(); private containerById = new Map(); @@ -57,10 +72,18 @@ export class GraphViewModel { constructor( private readonly sourceList: ReportSource[], - private readonly makeRepository: (url: string) => ReportRepository + private readonly makeRepository: (url: string) => ReportRepository, + private readonly loadSource: LoadSource ) { this.selectedSourceId = sourceList[0]?.id ?? null; makeAutoObservable(this, {}, { autoBind: true }); + // 选中节点变化时,自动拉取对应源码 + reaction( + () => this.selectedFunc?.id ?? null, + () => { + void this.loadSelectedFuncCode(); + } + ); } get sources(): ReportSource[] { @@ -82,16 +105,36 @@ export class GraphViewModel { void this.load(); } + toggleCodePanel(): void { + this.codePanelCollapsed = !this.codePanelCollapsed; + } + + setGroupMode(mode: GroupMode): void { + this.groupMode = mode; + } + /** 计算一条流程入口所属的分组(方法→类,自由函数→文件) */ private groupOfFlow(flow: Flow): FlowGroup { const entry = flow.entryNodeId ? this.funcById.get(flow.entryNodeId) : undefined; if (entry?.ownerId) { const container = this.containerById.get(entry.ownerId); - return { id: `class:${entry.ownerId}`, name: container?.name ?? "未知类", kind: "class", count: 0 }; + return { + id: `class:${entry.ownerId}`, + name: container?.name ?? "未知类", + kind: "class", + count: 0, + summary: container?.summary, + }; } const file = entry ? this.fileById.get(entry.fileId) : undefined; if (file) { - return { id: `file:${file.id}`, name: baseName(file.path), kind: "file", count: 0 }; + return { + id: `file:${file.id}`, + name: baseName(file.path), + kind: "file", + count: 0, + summary: file.summary, + }; } return { id: "ungrouped", name: "未归类", kind: "file", count: 0 }; } @@ -114,6 +157,25 @@ export class GraphViewModel { return this.flows.filter((f) => this.groupOfFlow(f).id === this.selectedGroupId); } + /** 当前选中的分组(含说明),未选则为 null */ + get selectedGroup(): FlowGroup | null { + if (!this.selectedGroupId) return null; + return this.groups.find((g) => g.id === this.selectedGroupId) ?? null; + } + + /** 选中函数的归属(所属类或文件)及其说明 */ + get selectedFuncOwner(): { name: string; kind: "class" | "file"; summary?: string } | null { + const fn = this.selectedFunc; + if (!fn) return null; + if (fn.ownerId) { + const c = this.containerById.get(fn.ownerId); + if (c) return { name: c.name, kind: "class", summary: c.summary }; + } + const f = this.fileById.get(fn.fileId); + if (f) return { name: baseName(f.path), kind: "file", summary: f.summary }; + return null; + } + selectGroup(groupId: string | null): void { this.selectedGroupId = groupId; const visible = this.visibleFlows; @@ -170,16 +232,27 @@ export class GraphViewModel { /** 计算好坐标的中性图,供 View 适配到具体渲染库 */ get positionedGraph(): PositionedGraph { const flow = this.currentFlow; - if (!flow) return { nodes: [], edges: [] }; + if (!flow) return { nodes: [], edges: [], clusters: [] }; const graph = flowToGraph(flow, { funcById: this.funcById, containerById: this.containerById, fileById: this.fileById, }); - const positions = computeLayout( + + // 按当前分组模式把节点分组,作为背景大框 + const nodeToGroup = new Map(); + if (this.groupMode !== "none") { + for (const n of graph.nodes) { + const key = n.funcId ? this.groupKeyOfFunc(n.funcId) : undefined; + if (key) nodeToGroup.set(n.id, key); + } + } + + const { positions, clusters } = computeClusteredLayout( graph.nodes.map((n) => ({ id: n.id, width: NODE_WIDTH, height: NODE_HEIGHT })), graph.edges.map((e) => ({ source: e.source, target: e.target })), + nodeToGroup, { direction: "LR" } ); @@ -188,7 +261,17 @@ export class GraphViewModel { return { ...n, x: pos.x, y: pos.y, width: NODE_WIDTH, height: NODE_HEIGHT }; }); - return { nodes, edges: graph.edges }; + return { nodes, edges: graph.edges, clusters }; + } + + /** 按当前分组模式取分组键:文件用完整路径,目录用其所在目录 */ + private groupKeyOfFunc(funcId: string): string | undefined { + const fn = this.funcById.get(funcId); + const file = fn?.location?.file ?? (fn ? this.fileById.get(fn.fileId)?.path : undefined); + if (!file) return undefined; + if (this.groupMode === "file") return file; + const idx = file.lastIndexOf("/"); + return idx < 0 ? "(根目录)" : file.slice(0, idx); } get selectedFunc(): Func | null { @@ -286,4 +369,41 @@ export class GraphViewModel { selectNode(nodeId: string | null): void { this.selectedNodeId = nodeId; } + + // ---------- 源码绑定 ---------- + + async loadSelectedFuncCode(): Promise { + const fn = this.selectedFunc; + const root = this.report?.project.root; + if (!fn || !fn.location || !root) { + runInAction(() => { + this.codeState = { funcId: fn?.id ?? null, loading: false, error: null, snippet: null }; + }); + return; + } + const funcId = fn.id; + const { file, startLine, endLine } = fn.location; + runInAction(() => { + this.codeState = { funcId, loading: true, error: null, snippet: null }; + }); + try { + const snippet = await this.loadSource(root, file, startLine, endLine); + runInAction(() => { + if (this.selectedFunc?.id === funcId) { + this.codeState = { funcId, loading: false, error: null, snippet }; + } + }); + } catch (e) { + runInAction(() => { + if (this.selectedFunc?.id === funcId) { + this.codeState = { + funcId, + loading: false, + error: e instanceof Error ? e.message : String(e), + snippet: null, + }; + } + }); + } + } } diff --git a/web/vite.config.ts b/web/vite.config.ts index 995aaa3..edf38a6 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -1,8 +1,58 @@ -import { defineConfig } from "vite"; +import { defineConfig, type Plugin } from "vite"; import react from "@vitejs/plugin-react"; +import { readFileSync } from "node:fs"; +import { resolve, sep } from "node:path"; + +/** + * 开发期源码服务:GET /__source?root=&file=&start=&end= + * 按报告里的 location 从磁盘读取真实源码片段,实现「报告 → 代码库」绑定。 + * 仅用于本地开发;做了基本的路径穿越防护(结果路径必须落在 root 内)。 + */ +function sourceServer(): Plugin { + return { + name: "code-visualize-source-server", + configureServer(server) { + server.middlewares.use("/__source", (req, res) => { + try { + const url = new URL(req.url ?? "", "http://localhost"); + const root = url.searchParams.get("root") ?? ""; + const file = url.searchParams.get("file") ?? ""; + const start = Number(url.searchParams.get("start") ?? "1"); + const end = Number(url.searchParams.get("end") ?? String(start)); + + if (!root || !file) { + res.statusCode = 400; + res.end("missing root/file"); + return; + } + + const rootResolved = resolve(root); + const target = resolve(rootResolved, file); + if (target !== rootResolved && !target.startsWith(rootResolved + sep)) { + res.statusCode = 403; + res.end("path escapes root"); + return; + } + + const content = readFileSync(target, "utf-8"); + const allLines = content.split(/\r?\n/); + const from = Math.max(1, start); + const to = Math.min(allLines.length, Math.max(from, end)); + const code = allLines.slice(from - 1, to).join("\n"); + + res.setHeader("Content-Type", "application/json; charset=utf-8"); + res.end(JSON.stringify({ file, startLine: from, endLine: to, code })); + } catch (e) { + res.statusCode = 404; + res.end(e instanceof Error ? e.message : "read error"); + } + }); + }, + }; +} export default defineConfig({ - plugins: [react()], + plugins: [react(), sourceServer()], server: { port: 5300, open: true,