codebase-onboarder 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -47
- package/bin/build-if-source.js +23 -0
- package/cli/commands.d.ts +26 -0
- package/cli/commands.js +836 -811
- package/cli/commands.js.map +1 -0
- package/cli/explorer/advanced.d.ts +14 -0
- package/cli/explorer/advanced.js +449 -0
- package/cli/explorer/advanced.js.map +1 -0
- package/cli/explorer/app.d.ts +1 -0
- package/cli/explorer/app.js +396 -229
- package/cli/explorer/app.js.map +1 -0
- package/cli/explorer/assistant.d.ts +9 -0
- package/cli/explorer/assistant.js +58 -0
- package/cli/explorer/assistant.js.map +1 -0
- package/cli/explorer/commands.d.ts +17 -0
- package/cli/explorer/commands.js +400 -162
- package/cli/explorer/commands.js.map +1 -0
- package/cli/explorer/featureViews.d.ts +124 -0
- package/cli/explorer/featureViews.js +179 -0
- package/cli/explorer/featureViews.js.map +1 -0
- package/cli/explorer/github.d.ts +21 -0
- package/cli/explorer/github.js +73 -0
- package/cli/explorer/github.js.map +1 -0
- package/cli/explorer/graphs.d.ts +7 -0
- package/cli/explorer/graphs.js +173 -0
- package/cli/explorer/graphs.js.map +1 -0
- package/cli/explorer/picker.d.ts +22 -0
- package/cli/explorer/picker.js +150 -0
- package/cli/explorer/picker.js.map +1 -0
- package/cli/explorer/report.d.ts +85 -0
- package/cli/explorer/report.js +60 -0
- package/cli/explorer/report.js.map +1 -0
- package/cli/explorer/session.d.ts +449 -0
- package/cli/explorer/session.js +184 -95
- package/cli/explorer/session.js.map +1 -0
- package/cli/explorer/views.d.ts +19 -0
- package/cli/explorer/views.js +496 -412
- package/cli/explorer/views.js.map +1 -0
- package/cli/explorer/wrap.d.ts +1 -0
- package/cli/explorer/wrap.js +33 -0
- package/cli/explorer/wrap.js.map +1 -0
- package/cli/main.d.ts +1 -0
- package/cli/main.js +235 -138
- package/cli/main.js.map +1 -0
- package/cli/prompt.d.ts +5 -0
- package/cli/prompt.js +84 -80
- package/cli/prompt.js.map +1 -0
- package/cli/scanOptions.d.ts +2 -0
- package/cli/scanOptions.js +11 -0
- package/cli/scanOptions.js.map +1 -0
- package/cli/ui.d.ts +33 -0
- package/cli/ui.js +55 -35
- package/cli/ui.js.map +1 -0
- package/cli/wizard.d.ts +187 -0
- package/cli/wizard.js +304 -284
- package/cli/wizard.js.map +1 -0
- package/package.json +15 -4
- package/public/app.d.ts +1 -0
- package/public/app.js +1057 -1009
- package/public/app.js.map +1 -0
- package/public/browser.d.ts +68 -0
- package/public/browser.js +2 -0
- package/public/browser.js.map +1 -0
- package/public/index.html +30 -13
- package/public/js/about.d.ts +6 -0
- package/public/js/about.js +165 -172
- package/public/js/about.js.map +1 -0
- package/public/js/aiDraft.d.ts +3 -0
- package/public/js/aiDraft.js +116 -124
- package/public/js/aiDraft.js.map +1 -0
- package/public/js/analysisPanel.d.ts +7 -0
- package/public/js/analysisPanel.js +166 -166
- package/public/js/analysisPanel.js.map +1 -0
- package/public/js/analysisReport.d.ts +38 -0
- package/public/js/analysisReport.js +105 -110
- package/public/js/analysisReport.js.map +1 -0
- package/public/js/api.d.ts +26 -0
- package/public/js/api.js +176 -167
- package/public/js/api.js.map +1 -0
- package/public/js/atlas.d.ts +6 -0
- package/public/js/atlas.js +50 -58
- package/public/js/atlas.js.map +1 -0
- package/public/js/blameView.d.ts +2 -0
- package/public/js/blameView.js +36 -37
- package/public/js/blameView.js.map +1 -0
- package/public/js/codeTab.d.ts +3 -0
- package/public/js/codeTab.js +175 -180
- package/public/js/codeTab.js.map +1 -0
- package/public/js/codeViewer.d.ts +5 -0
- package/public/js/codeViewer.js +162 -150
- package/public/js/codeViewer.js.map +1 -0
- package/public/js/components/FileChip.d.ts +9 -0
- package/public/js/components/FileChip.js +45 -49
- package/public/js/components/FileChip.js.map +1 -0
- package/public/js/components/MetricSparkline.d.ts +7 -0
- package/public/js/components/MetricSparkline.js +35 -44
- package/public/js/components/MetricSparkline.js.map +1 -0
- package/public/js/components/RiskBadge.d.ts +9 -0
- package/public/js/components/RiskBadge.js +55 -63
- package/public/js/components/RiskBadge.js.map +1 -0
- package/public/js/deepAnalysisView.d.ts +99 -0
- package/public/js/deepAnalysisView.js +282 -302
- package/public/js/deepAnalysisView.js.map +1 -0
- package/public/js/diagramPane.d.ts +8 -0
- package/public/js/diagramPane.js +133 -135
- package/public/js/diagramPane.js.map +1 -0
- package/public/js/diffView.d.ts +6 -0
- package/public/js/diffView.js +222 -209
- package/public/js/diffView.js.map +1 -0
- package/public/js/docsView.d.ts +2 -0
- package/public/js/docsView.js +441 -442
- package/public/js/docsView.js.map +1 -0
- package/public/js/dom.d.ts +189 -0
- package/public/js/dom.js +5 -0
- package/public/js/dom.js.map +1 -0
- package/public/js/fileSourceBrowser.d.ts +5 -0
- package/public/js/fileSourceBrowser.js +33 -41
- package/public/js/fileSourceBrowser.js.map +1 -0
- package/public/js/flameGraph.d.ts +5 -0
- package/public/js/flameGraph.js +62 -72
- package/public/js/flameGraph.js.map +1 -0
- package/public/js/forceGraph.d.ts +10 -0
- package/public/js/forceGraph.js +581 -556
- package/public/js/forceGraph.js.map +1 -0
- package/public/js/heatmap.d.ts +4 -0
- package/public/js/heatmap.js +142 -164
- package/public/js/heatmap.js.map +1 -0
- package/public/js/highlight.d.ts +2 -0
- package/public/js/highlight.js +116 -107
- package/public/js/highlight.js.map +1 -0
- package/public/js/html.d.ts +2 -0
- package/public/js/html.js +14 -44
- package/public/js/html.js.map +1 -0
- package/public/js/insightsView.d.ts +5 -0
- package/public/js/insightsView.js +93 -111
- package/public/js/insightsView.js.map +1 -0
- package/public/js/inspector.d.ts +37 -0
- package/public/js/inspector.js +416 -445
- package/public/js/inspector.js.map +1 -0
- package/public/js/llm.d.ts +113 -0
- package/public/js/llm.js +233 -272
- package/public/js/llm.js.map +1 -0
- package/public/js/mapView.d.ts +3 -0
- package/public/js/mapView.js +121 -120
- package/public/js/mapView.js.map +1 -0
- package/public/js/markdown.d.ts +3 -0
- package/public/js/markdown.js +84 -91
- package/public/js/markdown.js.map +1 -0
- package/public/js/mindmap.d.ts +32 -0
- package/public/js/mindmap.js +244 -259
- package/public/js/mindmap.js.map +1 -0
- package/public/js/mobilePanels.d.ts +5 -0
- package/public/js/mobilePanels.js +50 -0
- package/public/js/mobilePanels.js.map +1 -0
- package/public/js/repoFiles.d.ts +12 -0
- package/public/js/repoFiles.js +28 -27
- package/public/js/repoFiles.js.map +1 -0
- package/public/js/reviewView.d.ts +28 -0
- package/public/js/reviewView.js +256 -0
- package/public/js/reviewView.js.map +1 -0
- package/public/js/sbomView.d.ts +3 -0
- package/public/js/sbomView.js +59 -67
- package/public/js/sbomView.js.map +1 -0
- package/public/js/scanCache.d.ts +16 -0
- package/public/js/scanCache.js +67 -65
- package/public/js/scanCache.js.map +1 -0
- package/public/js/search.d.ts +7 -0
- package/public/js/search.js +868 -800
- package/public/js/search.js.map +1 -0
- package/public/js/searchLocal.d.ts +42 -0
- package/public/js/searchLocal.js +87 -0
- package/public/js/searchLocal.js.map +1 -0
- package/public/js/serverSettings.d.ts +5 -0
- package/public/js/serverSettings.js +153 -164
- package/public/js/serverSettings.js.map +1 -0
- package/public/js/state.d.ts +798 -0
- package/public/js/state.js +114 -122
- package/public/js/state.js.map +1 -0
- package/public/js/tour.d.ts +6 -0
- package/public/js/tour.js +5 -7
- package/public/js/tour.js.map +1 -0
- package/public/js/transitions.d.ts +2 -0
- package/public/js/transitions.js +9 -10
- package/public/js/transitions.js.map +1 -0
- package/public/js/tree.d.ts +12 -0
- package/public/js/tree.js +157 -154
- package/public/js/tree.js.map +1 -0
- package/public/js/workflowsView.d.ts +3 -0
- package/public/js/workflowsView.js +15 -22
- package/public/js/workflowsView.js.map +1 -0
- package/public/search.css +495 -0
- package/public/styles.css +163 -346
- package/public/workbench.css +166 -0
- package/server/apiAuth.d.ts +3 -0
- package/server/apiAuth.js +18 -20
- package/server/apiAuth.js.map +1 -0
- package/server/apiDiff.d.ts +2 -0
- package/server/apiDiff.js +20 -18
- package/server/apiDiff.js.map +1 -0
- package/server/apiDocs.d.ts +2 -0
- package/server/apiDocs.js +38 -40
- package/server/apiDocs.js.map +1 -0
- package/server/apiFile.d.ts +1 -0
- package/server/apiFile.js +14 -17
- package/server/apiFile.js.map +1 -0
- package/server/apiGitBlame.d.ts +2 -0
- package/server/apiGitBlame.js +50 -51
- package/server/apiGitBlame.js.map +1 -0
- package/server/apiMcp.d.ts +4 -0
- package/server/apiMcp.js +64 -66
- package/server/apiMcp.js.map +1 -0
- package/server/apiReview.d.ts +2 -0
- package/server/apiReview.js +45 -0
- package/server/apiReview.js.map +1 -0
- package/server/apiScan.d.ts +5 -0
- package/server/apiScan.js +90 -71
- package/server/apiScan.js.map +1 -0
- package/server/apiSearch.d.ts +33 -0
- package/server/apiSearch.js +184 -200
- package/server/apiSearch.js.map +1 -0
- package/server/apiSettings.d.ts +4 -0
- package/server/apiSettings.js +84 -85
- package/server/apiSettings.js.map +1 -0
- package/server/apiTools.d.ts +3 -0
- package/server/apiTools.js +55 -56
- package/server/apiTools.js.map +1 -0
- package/server/auth.d.ts +5 -0
- package/server/auth.js +54 -51
- package/server/auth.js.map +1 -0
- package/server/config.d.ts +42 -0
- package/server/config.js +189 -194
- package/server/config.js.map +1 -0
- package/server/daemon.d.ts +12 -0
- package/server/daemon.js +124 -114
- package/server/daemon.js.map +1 -0
- package/server/fileSourceNode.d.ts +2 -0
- package/server/fileSourceNode.js +34 -36
- package/server/fileSourceNode.js.map +1 -0
- package/server/gitClone.d.ts +9 -0
- package/server/gitClone.js +59 -61
- package/server/gitClone.js.map +1 -0
- package/server/gitDiff.d.ts +36 -0
- package/server/gitDiff.js +271 -169
- package/server/gitDiff.js.map +1 -0
- package/server/gitHistory.d.ts +12 -0
- package/server/gitHistory.js +80 -80
- package/server/gitHistory.js.map +1 -0
- package/server/gitRemote.d.ts +12 -0
- package/server/gitRemote.js +60 -0
- package/server/gitRemote.js.map +1 -0
- package/server/htmlText.d.ts +4 -0
- package/server/htmlText.js +16 -23
- package/server/htmlText.js.map +1 -0
- package/server/http.d.ts +5 -0
- package/server/http.js +27 -30
- package/server/http.js.map +1 -0
- package/server/httpGuards.d.ts +2 -0
- package/server/httpGuards.js +47 -43
- package/server/httpGuards.js.map +1 -0
- package/server/https.d.ts +34 -0
- package/server/https.js +121 -111
- package/server/https.js.map +1 -0
- package/server/index.d.ts +40 -0
- package/server/index.js +139 -147
- package/server/index.js.map +1 -0
- package/server/layout.d.ts +26 -0
- package/server/layout.js +59 -37
- package/server/layout.js.map +1 -0
- package/server/llmProxy.d.ts +3 -0
- package/server/llmProxy.js +137 -135
- package/server/llmProxy.js.map +1 -0
- package/server/logger.d.ts +38 -0
- package/server/logger.js +198 -211
- package/server/logger.js.map +1 -0
- package/server/mcp/analysis.d.ts +29 -0
- package/server/mcp/analysis.js +99 -120
- package/server/mcp/analysis.js.map +1 -0
- package/server/mcp/http.d.ts +9 -0
- package/server/mcp/http.js +128 -137
- package/server/mcp/http.js.map +1 -0
- package/server/mcp/runner.d.ts +68 -0
- package/server/mcp/runner.js +208 -218
- package/server/mcp/runner.js.map +1 -0
- package/server/mcp/server.d.ts +29 -0
- package/server/mcp/server.js +166 -179
- package/server/mcp/server.js.map +1 -0
- package/server/mcp/standalone.d.ts +1 -0
- package/server/mcp/standalone.js +8 -10
- package/server/mcp/standalone.js.map +1 -0
- package/server/mcp/tools.d.ts +2020 -0
- package/server/mcp/tools.js +523 -574
- package/server/mcp/tools.js.map +1 -0
- package/server/paths.d.ts +3 -0
- package/server/paths.js +17 -15
- package/server/paths.js.map +1 -0
- package/server/pidfile.d.ts +9 -0
- package/server/pidfile.js +43 -45
- package/server/pidfile.js.map +1 -0
- package/server/review.d.ts +5 -0
- package/server/review.js +35 -0
- package/server/review.js.map +1 -0
- package/server/router.d.ts +33 -0
- package/server/router.js +191 -197
- package/server/router.js.map +1 -0
- package/server/scanRequest.d.ts +3 -0
- package/server/scanRequest.js +36 -0
- package/server/scanRequest.js.map +1 -0
- package/server/searchIndex.d.ts +17 -0
- package/server/searchIndex.js +83 -80
- package/server/searchIndex.js.map +1 -0
- package/server/sessions.d.ts +14 -0
- package/server/sessions.js +62 -70
- package/server/sessions.js.map +1 -0
- package/server/startup.d.ts +118 -0
- package/server/startup.js +143 -148
- package/server/startup.js.map +1 -0
- package/server/static.d.ts +6 -0
- package/server/static.js +28 -31
- package/server/static.js.map +1 -0
- package/server/tools/install.d.ts +4 -0
- package/server/tools/install.js +176 -185
- package/server/tools/install.js.map +1 -0
- package/server/tools/parse.d.ts +8 -0
- package/server/tools/parse.js +112 -114
- package/server/tools/parse.js.map +1 -0
- package/server/tools/platform.d.ts +9 -0
- package/server/tools/platform.js +49 -47
- package/server/tools/platform.js.map +1 -0
- package/server/tools/registry.d.ts +137 -0
- package/server/tools/registry.js +173 -172
- package/server/tools/registry.js.map +1 -0
- package/server/tools/scan.d.ts +34 -0
- package/server/tools/scan.js +89 -98
- package/server/tools/scan.js.map +1 -0
- package/server/tools.d.ts +34 -0
- package/server/tools.js +94 -107
- package/server/tools.js.map +1 -0
- package/server/tunnel.d.ts +29 -0
- package/server/tunnel.js +61 -66
- package/server/tunnel.js.map +1 -0
- package/shared/analyzer/docs.d.ts +16 -0
- package/shared/analyzer/docs.js +99 -93
- package/shared/analyzer/docs.js.map +1 -0
- package/shared/analyzer/explainLocal.d.ts +4 -0
- package/shared/analyzer/explainLocal.js +122 -142
- package/shared/analyzer/explainLocal.js.map +1 -0
- package/shared/analyzer/github.d.ts +37 -0
- package/shared/analyzer/github.js +79 -0
- package/shared/analyzer/github.js.map +1 -0
- package/shared/analyzer/graph.d.ts +41 -0
- package/shared/analyzer/graph.js +393 -400
- package/shared/analyzer/graph.js.map +1 -0
- package/shared/analyzer/health.d.ts +33 -0
- package/shared/analyzer/health.js +197 -193
- package/shared/analyzer/health.js.map +1 -0
- package/shared/analyzer/history.d.ts +29 -0
- package/shared/analyzer/history.js +110 -111
- package/shared/analyzer/history.js.map +1 -0
- package/shared/analyzer/jsonConfig.d.ts +2 -0
- package/shared/analyzer/jsonConfig.js +61 -0
- package/shared/analyzer/jsonConfig.js.map +1 -0
- package/shared/analyzer/languages/csharp.d.ts +26 -0
- package/shared/analyzer/languages/csharp.js +83 -88
- package/shared/analyzer/languages/csharp.js.map +1 -0
- package/shared/analyzer/languages/generic.d.ts +23 -0
- package/shared/analyzer/languages/generic.js +94 -105
- package/shared/analyzer/languages/generic.js.map +1 -0
- package/shared/analyzer/languages/go.d.ts +24 -0
- package/shared/analyzer/languages/go.js +45 -54
- package/shared/analyzer/languages/go.js.map +1 -0
- package/shared/analyzer/languages/index.d.ts +6 -0
- package/shared/analyzer/languages/index.js +19 -20
- package/shared/analyzer/languages/index.js.map +1 -0
- package/shared/analyzer/languages/java.d.ts +20 -0
- package/shared/analyzer/languages/java.js +73 -78
- package/shared/analyzer/languages/java.js.map +1 -0
- package/shared/analyzer/languages/javascript.d.ts +12 -0
- package/shared/analyzer/languages/javascript.js +258 -209
- package/shared/analyzer/languages/javascript.js.map +1 -0
- package/shared/analyzer/languages/python.d.ts +23 -0
- package/shared/analyzer/languages/python.js +92 -100
- package/shared/analyzer/languages/python.js.map +1 -0
- package/shared/analyzer/languages/rust.d.ts +17 -0
- package/shared/analyzer/languages/rust.js +96 -98
- package/shared/analyzer/languages/rust.js.map +1 -0
- package/shared/analyzer/languages/typescript.d.ts +11 -0
- package/shared/analyzer/languages/typescript.js +117 -116
- package/shared/analyzer/languages/typescript.js.map +1 -0
- package/shared/analyzer/licenses.d.ts +18 -0
- package/shared/analyzer/licenses.js +130 -133
- package/shared/analyzer/licenses.js.map +1 -0
- package/shared/analyzer/metrics.d.ts +20 -0
- package/shared/analyzer/metrics.js +112 -104
- package/shared/analyzer/metrics.js.map +1 -0
- package/shared/analyzer/pathUtil.d.ts +10 -0
- package/shared/analyzer/pathUtil.js +29 -35
- package/shared/analyzer/pathUtil.js.map +1 -0
- package/shared/analyzer/patterns.d.ts +11 -0
- package/shared/analyzer/patterns.js +231 -253
- package/shared/analyzer/patterns.js.map +1 -0
- package/shared/analyzer/scan.d.ts +62 -0
- package/shared/analyzer/scan.js +451 -442
- package/shared/analyzer/scan.js.map +1 -0
- package/shared/analyzer/scanControl.d.ts +11 -0
- package/shared/analyzer/scanControl.js +62 -0
- package/shared/analyzer/scanControl.js.map +1 -0
- package/shared/analyzer/security.d.ts +18 -0
- package/shared/analyzer/security.js +159 -159
- package/shared/analyzer/security.js.map +1 -0
- package/shared/analyzer/services.d.ts +5 -0
- package/shared/analyzer/services.js +141 -138
- package/shared/analyzer/services.js.map +1 -0
- package/shared/analyzer/stack.d.ts +6 -0
- package/shared/analyzer/stack.js +134 -141
- package/shared/analyzer/stack.js.map +1 -0
- package/shared/analyzer/tour.d.ts +2 -0
- package/shared/analyzer/tour.js +45 -53
- package/shared/analyzer/tour.js.map +1 -0
- package/shared/analyzer/util.d.ts +8 -0
- package/shared/analyzer/util.js +130 -76
- package/shared/analyzer/util.js.map +1 -0
- package/shared/analyzer/workflows.d.ts +8 -0
- package/shared/analyzer/workflows.js +139 -134
- package/shared/analyzer/workflows.js.map +1 -0
- package/shared/contracts.d.ts +250 -0
- package/shared/contracts.js +2 -0
- package/shared/contracts.js.map +1 -0
- package/shared/diagram/aiFacts.d.ts +35 -0
- package/shared/diagram/aiFacts.js +102 -105
- package/shared/diagram/aiFacts.js.map +1 -0
- package/shared/diagram/aiMermaid.d.ts +5 -0
- package/shared/diagram/aiMermaid.js +46 -45
- package/shared/diagram/aiMermaid.js.map +1 -0
- package/shared/diagram/atlas.d.ts +16 -0
- package/shared/diagram/atlas.js +51 -60
- package/shared/diagram/atlas.js.map +1 -0
- package/shared/diagram/mermaid.d.ts +43 -0
- package/shared/diagram/mermaid.js +348 -370
- package/shared/diagram/mermaid.js.map +1 -0
- package/shared/review/contracts.d.ts +95 -0
- package/shared/review/contracts.js +2 -0
- package/shared/review/contracts.js.map +1 -0
- package/shared/review/review.d.ts +10 -0
- package/shared/review/review.js +184 -0
- package/shared/review/review.js.map +1 -0
- package/shared/search/query.d.ts +32 -0
- package/shared/search/query.js +395 -388
- package/shared/search/query.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/public/js/llm.ts"],"names":[],"mappings":"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","sourcesContent":["// LLM settings and prompt assembly. The key lives in localStorage and only\n// ever travels to the local server, which forwards it to whatever\n// OpenAI-compatible endpoint the user pointed at.\n\nconst STORE_KEY = 'onboarder.llm.v1';\n\n// No endpoint, model, or key ships with the repo. Anything saved in the\n// API-key drawer overrides these empty defaults (localStorage wins); without\n// a saved endpoint the app stays on the offline graph explanations.\nexport const DEFAULT_SETTINGS = {\n baseUrl: '',\n apiKey: '',\n model: '',\n};\n\nexport function getSettings() {\n try {\n return { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem(STORE_KEY) || '{}') };\n } catch {\n return { ...DEFAULT_SETTINGS };\n }\n}\n\nexport function saveSettings(settings) {\n localStorage.setItem(STORE_KEY, JSON.stringify({\n baseUrl: (settings.baseUrl || '').trim().replace(/\\/+$/, ''),\n apiKey: settings.apiKey || '',\n model: (settings.model || '').trim(),\n }));\n}\n\nexport function isConfigured() {\n const s = getSettings();\n return Boolean(s.baseUrl && s.model);\n}\n\n// Azure OpenAI speaks a slightly different dialect than the rest: api-key\n// header (handled server-side) and no `reasoning` request parameter.\nexport function isAzureHost(baseUrl) {\n return /\\.openai\\.azure\\.com|\\.cognitiveservices\\.azure\\.com/i.test(baseUrl || '');\n}\n\nconst SYSTEM_PROMPT = [\n 'You are a senior engineer walking a new teammate through a codebase they have never seen.',\n 'Write like a person, not a product page: plain sentences, concrete names, no hype, no bullet-point cosplay unless a list is genuinely clearer.',\n 'Keep it under 180 words. Refer to files and functions by their real names in backticks.',\n 'If something looks risky to change, say so plainly.',\n].join(' ');\n\nexport function fileMessages({ repoName, overview, path, role, fanIn, fanOut, importers, imports, functions, exports_, source }) {\n const context = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n '',\n `File: ${path} (role: ${role})`,\n `Imported by ${fanIn} files: ${importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${fanOut} files: ${imports.slice(0, 8).join(', ') || 'none'}`,\n functions?.length ? `Functions: ${functions.slice(0, 14).join(', ')}` : '',\n exports_?.length ? `Exports: ${exports_.slice(0, 10).join(', ')}` : '',\n ].filter(Boolean).join('\\n');\n\n const user = source\n ? `${context}\\n\\nHere is the source, possibly truncated:\\n\\`\\`\\`\\n${source.slice(0, 12000)}\\n\\`\\`\\`\\n\\nExplain what this file does, how it fits into the repo, and what breaks if it is deleted.`\n : `${context}\\n\\n(Source was not available.) Explain what this file most likely does, how it fits into the repo, and what breaks if it is deleted. Say what you are inferring versus what the graph proves.`;\n\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\nexport function overviewMessages({ repoName, overview, entries, hubs, services, cycles, orphans, languages }) {\n const user = [\n `Repository: ${repoName}`,\n `Languages: ${languages}`,\n overview ? `Static notes: ${overview}` : '',\n entries?.length ? `Entry points: ${entries.slice(0, 5).join(', ')}` : '',\n hubs?.length ? `Most-depended-on files: ${hubs.slice(0, 5).map((h) => h.path + ' (' + h.fanIn + ')').join(', ')}` : '',\n services?.length ? `Services: ${services.map((s) => s.name).join(', ')}` : '',\n cycles?.length ? `${cycles.length} circular dependencies` : '',\n orphans?.length ? `${orphans.length} files imported by nothing` : '',\n '',\n 'Give a new teammate the orientation you would give on their first day: what this thing is, where to start reading, and which files to treat with respect.',\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\n// Diagram generation prompts. The model gets compact graph facts and returns\n// a Mermaid flowchart with a caption; the theme is injected by us afterwards\n// so AI drafts look exactly like the hand-drawn ones.\n\nconst DIAGRAM_PROMPT = [\n 'You turn codebase facts into Mermaid flowcharts that teach.',\n 'Output format, exactly: first line `%% caption: <one honest sentence about what the diagram shows>`, then the flowchart source. Nothing else — no fences, no commentary, no sign-off.',\n 'Rules: start with `flowchart LR` or `flowchart TD` on its own line; use short alphanumeric ids (a1, b2); put every label in \"double quotes\"; at most 36 nodes; group with subgraphs where it helps; A --> B means A depends on B; no click, style or linkStyle statements.',\n 'Where you can infer what a group of files is FOR, name the cluster by its purpose instead of repeating folder names.',\n].join(' ');\n\nexport function diagramMessages({ repoName, kind, facts }) {\n const user = [\n `Repository: ${repoName}`,\n `Diagram to draw: ${kind}`,\n 'Facts:',\n facts.join('\\n'),\n '',\n `Draw the most useful ${kind} for someone who has never seen this codebase.`,\n ].join('\\n');\n\n return [\n { role: 'system', content: DIAGRAM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\n// ---- documentation prompts -------------------------------------------------\n\nconst DOC_SYSTEM = [\n 'You write documentation for a codebase as a senior engineer would: concrete, calm, specific.',\n 'Plain sentences. Real names. No hype, no filler, no \"this file plays a crucial role\".',\n].join(' ');\n\n// Detailed reference notes for one file or folder, opened as a tab in Docs.\nconst FILE_DOC_SYSTEM = [\n 'You write detailed reference notes for one file in a codebase, as a senior engineer would for a new teammate.',\n 'Concrete and specific: real names, real numbers, no filler. Answer in Markdown with exactly these sections, in this order:',\n '## Purpose',\n '## How it fits',\n '## Key parts',\n '## Handle with care',\n 'Keep \"Key parts\" a short bullet list of the functions or exports that matter. Under 350 words total. If a section honestly has nothing to say, one line saying so.',\n].join('\\n');\n\nconst FOLDER_DOC_SYSTEM = [\n 'You write detailed reference notes for one folder in a codebase, as a senior engineer would for a new teammate.',\n 'Concrete and specific: real names, no filler. Answer in Markdown with exactly these sections, in this order:',\n '## Purpose',\n '## How the parts cooperate',\n '## The files that matter',\n '## Handle with care',\n 'Under 300 words total. Say what you infer from names and import facts versus what you know.',\n].join('\\n');\n\nexport function fileDocMessages({ repoName, overview, file }) {\n const blocks = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n `File: ${file.path} (role: ${file.role})`,\n `Imported by ${file.fanIn} files: ${file.importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${file.fanOut} files: ${file.imports.slice(0, 8).join(', ') || 'none'}`,\n file.functions?.length ? `Functions: ${file.functions.slice(0, 14).join(', ')}` : '',\n file.exports_?.length ? `Exports: ${file.exports_.slice(0, 10).join(', ')}` : '',\n file.source\n ? `Source, possibly truncated:\\n\\`\\`\\`\\n${file.source.slice(0, 12000)}\\n\\`\\`\\``\n : '(Source unavailable — write from the graph facts and say what you are inferring.)',\n ].filter(Boolean);\n return [\n { role: 'system', content: FILE_DOC_SYSTEM },\n { role: 'user', content: blocks.join('\\n') },\n ];\n}\n\nexport function folderDocDetailedMessages({ repoName, folder, subfolders, files }) {\n const lines = files.map((f) =>\n `- ${f.name} — ${f.fanIn} dependents, pulls in ${f.fanOut}` +\n (f.role !== 'module' ? `, role: ${f.role}` : '') +\n (f.functions.length ? `, functions: ${f.functions.slice(0, 5).join(', ')}` : '')\n );\n const user = [\n `Repository: ${repoName}`,\n `Folder: ${folder || '(repo root)'}`,\n subfolders?.length ? `Subfolders: ${subfolders.join(', ')}` : '',\n 'Files with import-graph facts:',\n lines.join('\\n') || '(no files directly here)',\n ].filter(Boolean).join('\\n');\n return [\n { role: 'system', content: FOLDER_DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport function repoDocMessages({ repoName, overview, readmeExcerpt, entries, hubs }) {\n const user = [\n `Repository: ${repoName}`,\n overview ? `Static analysis notes: ${overview}` : '',\n entries?.length ? `Entry points: ${entries.slice(0, 5).join(', ')}` : '',\n hubs?.length ? `Most depended-on files: ${hubs.slice(0, 5).map((h) => h.path + ' (' + h.fanIn + ')').join(', ')}` : '',\n readmeExcerpt ? `The project's own README begins:\\n\"\"\"\\n${readmeExcerpt.slice(0, 1500)}\\n\"\"\"` : '(No README found.)',\n '',\n 'Write 3–4 sentences of high-level overview for a brand-new teammate: what this project is, how it is organized at the top level, and where to start reading. Plain prose only — no headings, no bullets.',\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport function folderDocMessages({ repoName, folder, subfolders, files }) {\n const fileLines = files.map((f) => {\n if (!f.parsed) return `- ${f.name} — not parsed (asset or data)`;\n const bits = [`${f.fanIn} dependents`, `pulls in ${f.fanOut}`];\n if (f.role !== 'module') bits.push('role: ' + f.role);\n if (f.functions.length) bits.push('functions: ' + f.functions.slice(0, 5).join(', '));\n return `- ${f.name} — ${bits.join(', ')}`;\n });\n\n const user = [\n `Repository: ${repoName}`,\n `Folder under review: ${folder || '(repo root)'}`,\n subfolders?.length ? `Subfolders inside it: ${subfolders.join(', ')}` : '',\n 'Files (with import-graph facts):',\n fileLines.join('\\n') || '(no files directly here)',\n '',\n 'Write complete documentation for this folder, in exactly this format, no preamble, no fences:',\n 'FOLDER: <3-5 sentences — what lives here, why it exists, how the parts cooperate, and what a newcomer should read first>',\n ...files.filter((f) => f.parsed).map((f) => `FILE: ${f.name}: <2-3 sentences — what this file is for, who it talks to, and what to handle with care>`),\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nconst QUESTION_PROMPT = [\n 'You are a senior engineer answering a teammate\\'s question about a codebase.',\n 'Answer directly and concretely, citing real file and function names in backticks.',\n 'If the answer is not in the graph facts or source you were given, say so plainly and name the file you would open next to find out.',\n 'Keep it under 200 words unless the question genuinely needs more.',\n].join(' ');\n\n// A free-form question. When the user has a file selected, its facts and\n// source ride along; otherwise the model reasons from the repo overview.\nexport function questionMessages({ repoName, overview, question, file }) {\n const blocks = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n ];\n\n if (file) {\n blocks.push(\n `The user is currently looking at: ${file.path} (role: ${file.role})`,\n `Imported by ${file.fanIn} files: ${file.importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${file.fanOut} files: ${file.imports.slice(0, 8).join(', ') || 'none'}`\n );\n if (file.functions?.length) blocks.push(`Functions: ${file.functions.slice(0, 14).join(', ')}`);\n if (file.exports_?.length) blocks.push(`Exports: ${file.exports_.slice(0, 10).join(', ')}`);\n if (file.source) {\n blocks.push(`Source of ${file.path}, possibly truncated:\\n\\`\\`\\`\\n${file.source.slice(0, 12000)}\\n\\`\\`\\``);\n } else {\n blocks.push('(Source for that file was not available — answer from the graph facts and say so.)');\n }\n }\n\n blocks.push(`Question: ${question}`);\n\n return [\n { role: 'system', content: QUESTION_PROMPT },\n { role: 'user', content: blocks.filter(Boolean).join('\\n') },\n ];\n}\n\n\n// Used by the About tab to summarize a detected framework/library: the model\n// reasons from the official docs excerpt (when fetched) plus the repo's own\n// context. docsText may be empty — the prompt says so and falls back to\n// general knowledge.\nexport function stackSummaryMessages({ repoName, item, docsText, repoNote }) {\n const system =\n 'You are a senior developer writing compact field notes for a codebase onboarding tool. Be specific and honest; distinguish between what the docs state and what you infer. No headings, no \"Sure\" / \"Absolutely\" openers, no filler.';\n const declared = item.version\n ? `The project declares ${item.name} ${item.version} (${item.category}, ${item.lang}${item.dev ? ', dev dependency' : ''}).`\n : `The project uses ${item.name} (${item.category}, ${item.lang}).`;\n const user = [\n `Repository: ${repoName}.`,\n declared,\n `Repo context: ${repoNote}`,\n docsText\n ? `Official docs excerpt:\\n${docsText}`\n : 'No official docs could be fetched for this dependency — rely on your own knowledge and say so in one short clause.',\n 'In 2-4 sentences: what this is, why the project likely chose it, and anything a new teammate should know about using it here.',\n ].join('\\n\\n');\n return [{ role: 'system', content: system }, { role: 'user', content: user }];\n}\n\n// The Deep Analysis report, read back by the model. Two jobs in one builder: a\n// report is either *explained* (no question — prioritize what to fix) or asked\n// about (\"is the eval finding actually reachable?\"). The engine list is part of\n// the context on purpose: what the engines could not see is often the real\n// answer, and a model that does not know a tool was missing will invent\n// confidence it has not earned.\nconst ANALYSIS_SYSTEM = [\n 'You are a senior engineer walking the owner of a codebase through a static-analysis report.',\n 'Be concrete and calm. Name real files, rules and line numbers in backticks.',\n 'Prioritize: what must be fixed now, what can wait, and what is noise. Say plainly when a finding looks like a false positive and why.',\n 'Never invent a finding that is not in the list you were given. If the list is empty, say so — and say what that does and does not prove.',\n 'If some engines did not run or are not installed, say what that leaves unchecked.',\n 'Keep it under 220 words unless the question genuinely needs more.',\n].join(' ');\n\nexport function analysisMessages({ repoName, grade, score, counts, engines, findings, question }) {\n const engineLines = (engines || []).map((e) => {\n if (e.ran) return `- ${e.label}: ran, ${e.findings} finding(s) in ${e.ms}ms`;\n if (e.failed) return `- ${e.label}: ran but failed — ${e.failure || 'no reason given'}`;\n if (e.available) return `- ${e.label}: installed but not run`;\n return `- ${e.label}: NOT INSTALLED (${e.reason || 'unknown'})`;\n });\n\n const findingLines = (findings || []).slice(0, 40).map(\n (f) => `- [${f.severity}] ${f.path}:${f.line} — ${f.message} (${f.tool} · ${f.rule})`,\n );\n\n const user = [\n `Repository: ${repoName}`,\n grade ? `Built-in security grade: ${grade} (${score}/100)` : '',\n counts\n ? `Findings: ${counts.total} total — ${counts.critical} critical, ${counts.high} high, ${counts.medium} medium, ${counts.low} low. ${counts.external} came from external engines, ${counts.builtin} from the built-in scanner.`\n : '',\n engineLines.length ? `Engines:\\n${engineLines.join('\\n')}` : '',\n findingLines.length\n ? `Findings, worst first:\\n${findingLines.join('\\n')}`\n : 'No findings were reported by anything.',\n question\n ? `Question: ${question}`\n : 'Explain this report: what matters most, what to fix first, and what the engines could not see.',\n ].filter(Boolean).join('\\n\\n');\n\n return [\n { role: 'system', content: ANALYSIS_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport async function streamTokens(container, text) {\n container.innerHTML = '';\n for (let i = 0; i < text.length; i++) {\n container.innerHTML += text[i];\n await new Promise(r => setTimeout(r, 10));\n }\n}\n\n"]}
|
package/public/js/mapView.js
CHANGED
|
@@ -10,150 +10,151 @@
|
|
|
10
10
|
// the reader is looking rather than jump. And clicking a file "peeks" — it fills
|
|
11
11
|
// the inspector and leaves the map where it is — while clicking a folder name
|
|
12
12
|
// navigates away to the Files view. Both are deliberate.
|
|
13
|
-
|
|
14
13
|
import { roleOf, factIndex } from '/shared/analyzer/graph.js';
|
|
15
14
|
import { pathFilter } from '/shared/analyzer/pathUtil.js';
|
|
16
15
|
import { buildSpecs, layoutMindMap, renderMindMap } from './mindmap.js';
|
|
17
16
|
import { pruneTree } from './tree.js';
|
|
18
17
|
import { state, focusFile, focusFolder } from './state.js';
|
|
19
|
-
|
|
20
18
|
// Opening every folder at once on a big repo lays out thousands of cells in one
|
|
21
19
|
// frame. Past this many files the button refuses and says why.
|
|
22
20
|
const OPEN_ALL_LIMIT = 700;
|
|
23
|
-
|
|
24
|
-
let host = null; // #canvasContent
|
|
21
|
+
let host = null; // #canvasContent
|
|
25
22
|
let toggleBtn = null; // #mmToggleBtn — "Open folders" / "Close all"
|
|
26
23
|
let hooks = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
onSyncInspector: (..._args) => { },
|
|
25
|
+
onRenderSidebar: (..._args) => { },
|
|
26
|
+
onOpenFile: (..._args) => { },
|
|
27
|
+
onOpenFolder: (..._args) => { },
|
|
28
|
+
onToast: (..._args) => { },
|
|
29
|
+
onFit: (..._args) => { },
|
|
33
30
|
};
|
|
34
|
-
|
|
35
31
|
export function initMap(options) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
32
|
+
host = options.host;
|
|
33
|
+
toggleBtn = options.toggleBtn;
|
|
34
|
+
hooks = { ...hooks, ...options };
|
|
35
|
+
toggleBtn.addEventListener('click', () => {
|
|
36
|
+
if (state.mm.expanded.size > 1) {
|
|
37
|
+
state.mm.expanded = new Set(['dir:']);
|
|
38
|
+
renderMap(true);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (state.scan.allFiles.length > OPEN_ALL_LIMIT) {
|
|
42
|
+
return hooks.onToast('Too many files to open at once — open folders as you go.');
|
|
43
|
+
}
|
|
44
|
+
const open = (node) => {
|
|
45
|
+
state.mm.expanded.add('dir:' + node.path);
|
|
46
|
+
for (const d of node.dirs.values())
|
|
47
|
+
open(d);
|
|
48
|
+
};
|
|
49
|
+
open(state.treeData);
|
|
50
|
+
renderMap(true);
|
|
51
|
+
hooks.onToast('Every folder is open. Files stay shut until you click them.');
|
|
52
|
+
});
|
|
57
53
|
}
|
|
58
|
-
|
|
59
54
|
// The filter as the map needs it: a folder whose own name matches keeps its whole
|
|
60
55
|
// branch, so searching for a folder shows you what is in it.
|
|
61
56
|
function visibleTree() {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
const include = pathFilter(state.filters);
|
|
58
|
+
const needle = state.filters.text.trim().toLowerCase();
|
|
59
|
+
const matchesFolder = needle ? (p) => p.toLowerCase().includes(needle) : null;
|
|
60
|
+
return pruneTree(state.treeData, include, matchesFolder);
|
|
66
61
|
}
|
|
67
|
-
|
|
68
62
|
export function renderMap(fit) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
63
|
+
const { scan, facts } = state;
|
|
64
|
+
const fileByPath = new Map(scan.files.map((f) => [f.path, f]));
|
|
65
|
+
const rootSpec = buildSpecs({
|
|
66
|
+
root: visibleTree(),
|
|
67
|
+
expanded: state.mm.expanded,
|
|
68
|
+
rootSummary: {
|
|
69
|
+
name: scan.name,
|
|
70
|
+
summary: `${scan.stats.filesParsed} code files · ${scan.stats.edgeCount} connections`,
|
|
71
|
+
},
|
|
72
|
+
folderInfo: (node) => {
|
|
73
|
+
let summary = `${node.files.length} ${node.files.length === 1 ? 'file' : 'files'}`;
|
|
74
|
+
if (node.dirs.size)
|
|
75
|
+
summary += ` · ${node.dirs.size} ${node.dirs.size === 1 ? 'folder' : 'folders'}`;
|
|
76
|
+
return { summary, cls: '' };
|
|
77
|
+
},
|
|
78
|
+
fileInfo: (path) => {
|
|
79
|
+
const f = fileByPath.get(path);
|
|
80
|
+
// A path with no scan entry was walked but never parsed — listed, greyed,
|
|
81
|
+
// and given no neighbours rather than left out of the map entirely.
|
|
82
|
+
if (!f)
|
|
83
|
+
return { parsed: false, summary: 'not parsed', neighbors: [] };
|
|
84
|
+
const fin = facts.fanIn[path] || 0;
|
|
85
|
+
const fout = facts.fanOut[path] || 0;
|
|
86
|
+
const role = roleOf(path, facts);
|
|
87
|
+
const cls = [
|
|
88
|
+
role === 'entry' ? 'is-entry' : '',
|
|
89
|
+
role === 'hub' ? 'is-hub' : '',
|
|
90
|
+
factIndex(facts).inCycle.has(path) ? 'is-cycle' : '',
|
|
91
|
+
].filter(Boolean).join(' ');
|
|
92
|
+
return {
|
|
93
|
+
parsed: true,
|
|
94
|
+
summary: fileSummary(role, fin, fout, f),
|
|
95
|
+
cls,
|
|
96
|
+
neighbors: [
|
|
97
|
+
...(facts.importsOf[path] || []).map((p) => ({ path: p, relation: 'pulled in' })),
|
|
98
|
+
...(facts.importers[path] || []).map((p) => ({ path: p, relation: 'leans on it' })),
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
const layout = layoutMindMap(rootSpec, state.mm.expanded);
|
|
104
|
+
renderMindMap(host, layout, state.mm.expanded, {
|
|
105
|
+
onToggle: (cell) => {
|
|
106
|
+
if (state.mm.expanded.has(cell.id))
|
|
107
|
+
state.mm.expanded.delete(cell.id);
|
|
108
|
+
else
|
|
109
|
+
state.mm.expanded.add(cell.id);
|
|
110
|
+
renderMap(false); // keep the pan position — the map grows around you
|
|
111
|
+
peek(cell);
|
|
112
|
+
},
|
|
113
|
+
onNavigate: (cell) => {
|
|
114
|
+
if (cell.navFolder)
|
|
115
|
+
hooks.onOpenFolder(cell.navFolder);
|
|
116
|
+
else if (cell.kind === 'ghost' || cell.kind === 'more') {
|
|
117
|
+
// A ghost stands for a file outside the current branch; there is nothing
|
|
118
|
+
// to expand, so the only sensible click is to go and open it.
|
|
119
|
+
if (cell.nav)
|
|
120
|
+
hooks.onOpenFile(cell.nav);
|
|
121
|
+
}
|
|
122
|
+
else if (cell.nav)
|
|
123
|
+
peek(cell);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
// One button, two duties: it says what a click will do next.
|
|
127
|
+
toggleBtn.textContent = state.mm.expanded.size > 1 ? 'Close all' : 'Open folders';
|
|
128
|
+
if (fit)
|
|
129
|
+
hooks.onFit();
|
|
130
130
|
}
|
|
131
|
-
|
|
132
131
|
// Fills the inspector for a cell without leaving the map.
|
|
133
132
|
function peek(cell) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
133
|
+
if (cell.kind === 'folder' || cell.kind === 'root') {
|
|
134
|
+
focusFolder(cell.id.slice(4)); // strip the `dir:` prefix
|
|
135
|
+
hooks.onSyncInspector();
|
|
136
|
+
}
|
|
137
|
+
else if (cell.nav) {
|
|
138
|
+
focusFile(cell.nav);
|
|
139
|
+
hooks.onSyncInspector();
|
|
140
|
+
hooks.onRenderSidebar();
|
|
141
|
+
}
|
|
142
142
|
}
|
|
143
|
-
|
|
144
143
|
// The line under a file's name. Each role gets the number that matters for it:
|
|
145
144
|
// an entry point is interesting for what it pulls in, a hub for what leans on it.
|
|
146
145
|
export function fileSummary(role, fanIn, fanOut, file) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
switch (role) {
|
|
147
|
+
case 'entry': return `entry point · pulls in ${fanOut}`;
|
|
148
|
+
case 'hub': return `hub · ${fanIn} dependents`;
|
|
149
|
+
case 'leaf': return `leaf · ${fanIn} dependent${fanIn === 1 ? '' : 's'}`;
|
|
150
|
+
case 'test': return 'test file';
|
|
151
|
+
case 'config': return 'configuration';
|
|
152
|
+
default: {
|
|
153
|
+
let s = `${fanOut} out · ${fanIn} in`;
|
|
154
|
+
if (file.functions.length)
|
|
155
|
+
s += ` · ${file.functions.length} fn`;
|
|
156
|
+
return s;
|
|
157
|
+
}
|
|
157
158
|
}
|
|
158
|
-
}
|
|
159
159
|
}
|
|
160
|
+
//# sourceMappingURL=mapView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapView.js","sourceRoot":"","sources":["../../src/public/js/mapView.ts"],"names":[],"mappings":"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","sourcesContent":["// The Map view: the repo as an expanding tree map rather than a flowchart.\n//\n// This is the landing view after a scan, and the only one that is not Mermaid.\n// Cells are laid out by mindmap.js from a spec built here — the spec is where the\n// graph's numbers become the one-line summaries under each name, which is the\n// whole value of the view and the reason this glue is worth its own module.\n//\n// Two behaviours worth knowing before changing anything. Expanding a folder\n// re-renders *without* re-fitting, because the map is meant to grow around where\n// the reader is looking rather than jump. And clicking a file \"peeks\" — it fills\n// the inspector and leaves the map where it is — while clicking a folder name\n// navigates away to the Files view. Both are deliberate.\n\nimport { roleOf, factIndex } from '/shared/analyzer/graph.js';\nimport { pathFilter } from '/shared/analyzer/pathUtil.js';\nimport { buildSpecs, layoutMindMap, renderMindMap } from './mindmap.js';\nimport { pruneTree } from './tree.js';\nimport { state, focusFile, focusFolder } from './state.js';\n\n// Opening every folder at once on a big repo lays out thousands of cells in one\n// frame. Past this many files the button refuses and says why.\nconst OPEN_ALL_LIMIT = 700;\n\nlet host = null; // #canvasContent\nlet toggleBtn = null; // #mmToggleBtn — \"Open folders\" / \"Close all\"\nlet hooks = {\n onSyncInspector: (..._args: unknown[]) => {},\n onRenderSidebar: (..._args: unknown[]) => {},\n onOpenFile: (..._args: unknown[]) => {},\n onOpenFolder: (..._args: unknown[]) => {},\n onToast: (..._args: unknown[]) => {},\n onFit: (..._args: unknown[]) => {},\n};\n\nexport function initMap(options) {\n host = options.host;\n toggleBtn = options.toggleBtn;\n hooks = { ...hooks, ...options };\n\n toggleBtn.addEventListener('click', () => {\n if (state.mm.expanded.size > 1) {\n state.mm.expanded = new Set(['dir:']);\n renderMap(true);\n return;\n }\n if (state.scan.allFiles.length > OPEN_ALL_LIMIT) {\n return hooks.onToast('Too many files to open at once — open folders as you go.');\n }\n const open = (node) => {\n state.mm.expanded.add('dir:' + node.path);\n for (const d of node.dirs.values()) open(d);\n };\n open(state.treeData);\n renderMap(true);\n hooks.onToast('Every folder is open. Files stay shut until you click them.');\n });\n}\n\n// The filter as the map needs it: a folder whose own name matches keeps its whole\n// branch, so searching for a folder shows you what is in it.\nfunction visibleTree() {\n const include = pathFilter(state.filters);\n const needle = state.filters.text.trim().toLowerCase();\n const matchesFolder = needle ? (p) => p.toLowerCase().includes(needle) : null;\n return pruneTree(state.treeData, include, matchesFolder);\n}\n\nexport function renderMap(fit) {\n const { scan, facts } = state;\n const fileByPath = new Map(scan.files.map((f) => [f.path, f]));\n\n const rootSpec = buildSpecs({\n root: visibleTree(),\n expanded: state.mm.expanded,\n rootSummary: {\n name: scan.name,\n summary: `${scan.stats.filesParsed} code files · ${scan.stats.edgeCount} connections`,\n },\n folderInfo: (node) => {\n let summary = `${node.files.length} ${node.files.length === 1 ? 'file' : 'files'}`;\n if (node.dirs.size) summary += ` · ${node.dirs.size} ${node.dirs.size === 1 ? 'folder' : 'folders'}`;\n return { summary, cls: '' };\n },\n fileInfo: (path) => {\n const f = fileByPath.get(path);\n // A path with no scan entry was walked but never parsed — listed, greyed,\n // and given no neighbours rather than left out of the map entirely.\n if (!f) return { parsed: false, summary: 'not parsed', neighbors: [] };\n const fin = facts.fanIn[path] || 0;\n const fout = facts.fanOut[path] || 0;\n const role = roleOf(path, facts);\n const cls = [\n role === 'entry' ? 'is-entry' : '',\n role === 'hub' ? 'is-hub' : '',\n factIndex(facts).inCycle.has(path) ? 'is-cycle' : '',\n ].filter(Boolean).join(' ');\n return {\n parsed: true,\n summary: fileSummary(role, fin, fout, f),\n cls,\n neighbors: [\n ...(facts.importsOf[path] || []).map((p) => ({ path: p, relation: 'pulled in' })),\n ...(facts.importers[path] || []).map((p) => ({ path: p, relation: 'leans on it' })),\n ],\n };\n },\n });\n\n const layout = layoutMindMap(rootSpec, state.mm.expanded);\n renderMindMap(host, layout, state.mm.expanded, {\n onToggle: (cell) => {\n if (state.mm.expanded.has(cell.id)) state.mm.expanded.delete(cell.id);\n else state.mm.expanded.add(cell.id);\n renderMap(false); // keep the pan position — the map grows around you\n peek(cell);\n },\n onNavigate: (cell) => {\n if (cell.navFolder) hooks.onOpenFolder(cell.navFolder);\n else if (cell.kind === 'ghost' || cell.kind === 'more') {\n // A ghost stands for a file outside the current branch; there is nothing\n // to expand, so the only sensible click is to go and open it.\n if (cell.nav) hooks.onOpenFile(cell.nav);\n } else if (cell.nav) peek(cell);\n },\n });\n\n // One button, two duties: it says what a click will do next.\n toggleBtn.textContent = state.mm.expanded.size > 1 ? 'Close all' : 'Open folders';\n if (fit) hooks.onFit();\n}\n\n// Fills the inspector for a cell without leaving the map.\nfunction peek(cell) {\n if (cell.kind === 'folder' || cell.kind === 'root') {\n focusFolder(cell.id.slice(4)); // strip the `dir:` prefix\n hooks.onSyncInspector();\n } else if (cell.nav) {\n focusFile(cell.nav);\n hooks.onSyncInspector();\n hooks.onRenderSidebar();\n }\n}\n\n// The line under a file's name. Each role gets the number that matters for it:\n// an entry point is interesting for what it pulls in, a hub for what leans on it.\nexport function fileSummary(role, fanIn, fanOut, file) {\n switch (role) {\n case 'entry': return `entry point · pulls in ${fanOut}`;\n case 'hub': return `hub · ${fanIn} dependents`;\n case 'leaf': return `leaf · ${fanIn} dependent${fanIn === 1 ? '' : 's'}`;\n case 'test': return 'test file';\n case 'config': return 'configuration';\n default: {\n let s = `${fanOut} out · ${fanIn} in`;\n if (file.functions.length) s += ` · ${file.functions.length} fn`;\n return s;\n }\n }\n}\n"]}
|
package/public/js/markdown.js
CHANGED
|
@@ -1,119 +1,112 @@
|
|
|
1
1
|
// A small, honest Markdown renderer for READMEs: headings to h3, bold,
|
|
2
2
|
// italic, inline code, fenced code blocks, lists, links, paragraphs. HTML is
|
|
3
3
|
// escaped before anything else — a README is untrusted input like any other.
|
|
4
|
-
|
|
5
4
|
import { escapeHtml } from './html.js';
|
|
6
|
-
|
|
7
5
|
export function mdRender(text) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
6
|
+
const lines = escapeHtml(String(text || '')).split('\n');
|
|
7
|
+
const html = [];
|
|
8
|
+
let para = [];
|
|
9
|
+
let list = [];
|
|
10
|
+
let inCode = false;
|
|
11
|
+
let code = [];
|
|
12
|
+
const flushPara = () => {
|
|
13
|
+
if (!para.length)
|
|
14
|
+
return;
|
|
15
|
+
html.push('<p>' + para.map(inline).join('<br>') + '</p>');
|
|
16
|
+
para = [];
|
|
17
|
+
};
|
|
18
|
+
const flushList = () => {
|
|
19
|
+
if (!list.length)
|
|
20
|
+
return;
|
|
21
|
+
html.push('<ul>' + list.map((item) => '<li>' + inline(item) + '</li>').join('') + '</ul>');
|
|
22
|
+
list = [];
|
|
23
|
+
};
|
|
24
|
+
const flushCode = () => {
|
|
25
|
+
html.push('<pre><code>' + code.join('\n') + '</code></pre>');
|
|
26
|
+
code = [];
|
|
27
|
+
};
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
if (/^```/.test(line.trim())) {
|
|
30
|
+
if (inCode) {
|
|
31
|
+
inCode = false;
|
|
32
|
+
flushCode();
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
flushPara();
|
|
36
|
+
flushList();
|
|
37
|
+
inCode = true;
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (inCode) {
|
|
42
|
+
code.push(line);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const heading = line.match(/^(#{1,3})\s+(.+)$/);
|
|
46
|
+
if (heading) {
|
|
47
|
+
flushPara();
|
|
48
|
+
flushList();
|
|
49
|
+
const level = heading[1].length;
|
|
50
|
+
html.push(`<h${level} class="doc-h">${inline(heading[2].trim())}</h${level}>`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const bullet = line.match(/^\s*(?:[-*]|\d+\.)\s+(.+)$/);
|
|
54
|
+
if (bullet) {
|
|
55
|
+
flushPara();
|
|
56
|
+
list.push(bullet[1].trim());
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (!line.trim()) {
|
|
60
|
+
flushPara();
|
|
61
|
+
flushList();
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
para.push(line.trim());
|
|
54
65
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!line.trim()) {
|
|
64
|
-
flushPara();
|
|
65
|
-
flushList();
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
para.push(line.trim());
|
|
69
|
-
}
|
|
70
|
-
if (inCode) flushCode();
|
|
71
|
-
flushPara();
|
|
72
|
-
flushList();
|
|
73
|
-
return html.join('\n');
|
|
66
|
+
if (inCode)
|
|
67
|
+
flushCode();
|
|
68
|
+
flushPara();
|
|
69
|
+
flushList();
|
|
70
|
+
return html.join('\n');
|
|
74
71
|
}
|
|
75
|
-
|
|
76
72
|
// Everything reaching here has already been through escapeHtml() in mdRender,
|
|
77
73
|
// so `text`, `label` and `url` hold no live markup and no bare quotes — which
|
|
78
74
|
// is what makes it safe to drop `url` straight into an attribute below.
|
|
79
75
|
// Do not add a caller that skips that step, and do not escape again here:
|
|
80
76
|
// double-escaping turns a legitimate `&` in a URL into `&amp;`.
|
|
81
77
|
function inline(text) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
return text
|
|
79
|
+
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
80
|
+
.replace(/\*([^*]+)\*/g, '<em>$1</em>')
|
|
81
|
+
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
|
82
|
+
.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (m, label, url) => {
|
|
83
|
+
// Only http(s) becomes a link — a README is free to write
|
|
84
|
+
// `[click](javascript:…)` and we are not obliged to honour it.
|
|
85
|
+
if (/^https?:\/\//i.test(url)) {
|
|
86
|
+
return `<a href="${url}" target="_blank" rel="noopener noreferrer">${label}</a>`;
|
|
87
|
+
}
|
|
88
|
+
return `<span class="doc-dead-link" title="${url}">${label}</span>`;
|
|
93
89
|
});
|
|
94
90
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
91
|
// Markdown-lite: for AI answers and the static explanations, which use only
|
|
98
92
|
// **bold** and `code`. Escape everything first, then restore those two — so a
|
|
99
93
|
// model that echoes markup back at us can't inject it. Lives here rather than
|
|
100
94
|
// beside its callers in the view layer because it is pure text work, and being
|
|
101
95
|
// pure is what makes it testable.
|
|
102
96
|
export function mdLite(text) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
return escapeHtml(String(text ?? ''))
|
|
98
|
+
.split(/\n\n+/)
|
|
99
|
+
.map((para) => `<p>${inlineMarks(para).replace(/\n/g, '<br>')}</p>`)
|
|
100
|
+
.join('');
|
|
107
101
|
}
|
|
108
|
-
|
|
109
102
|
// The same two marks without the paragraph wrapper, for card bodies and
|
|
110
103
|
// single-line captions.
|
|
111
104
|
export function mdInline(text) {
|
|
112
|
-
|
|
105
|
+
return inlineMarks(escapeHtml(String(text ?? '')));
|
|
113
106
|
}
|
|
114
|
-
|
|
115
107
|
function inlineMarks(safe) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
108
|
+
return safe
|
|
109
|
+
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
110
|
+
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
119
111
|
}
|
|
112
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/public/js/markdown.ts"],"names":[],"mappings":"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","sourcesContent":["// A small, honest Markdown renderer for READMEs: headings to h3, bold,\n// italic, inline code, fenced code blocks, lists, links, paragraphs. HTML is\n// escaped before anything else — a README is untrusted input like any other.\n\nimport { escapeHtml } from './html.js';\n\nexport function mdRender(text) {\n const lines = escapeHtml(String(text || '')).split('\\n');\n const html = [];\n let para = [];\n let list = [];\n let inCode = false;\n let code = [];\n\n const flushPara = () => {\n if (!para.length) return;\n html.push('<p>' + para.map(inline).join('<br>') + '</p>');\n para = [];\n };\n const flushList = () => {\n if (!list.length) return;\n html.push('<ul>' + list.map((item) => '<li>' + inline(item) + '</li>').join('') + '</ul>');\n list = [];\n };\n const flushCode = () => {\n html.push('<pre><code>' + code.join('\\n') + '</code></pre>');\n code = [];\n };\n\n for (const line of lines) {\n if (/^```/.test(line.trim())) {\n if (inCode) {\n inCode = false;\n flushCode();\n } else {\n flushPara();\n flushList();\n inCode = true;\n }\n continue;\n }\n if (inCode) {\n code.push(line);\n continue;\n }\n\n const heading = line.match(/^(#{1,3})\\s+(.+)$/);\n if (heading) {\n flushPara();\n flushList();\n const level = heading[1].length;\n html.push(`<h${level} class=\"doc-h\">${inline(heading[2].trim())}</h${level}>`);\n continue;\n }\n\n const bullet = line.match(/^\\s*(?:[-*]|\\d+\\.)\\s+(.+)$/);\n if (bullet) {\n flushPara();\n list.push(bullet[1].trim());\n continue;\n }\n\n if (!line.trim()) {\n flushPara();\n flushList();\n continue;\n }\n para.push(line.trim());\n }\n if (inCode) flushCode();\n flushPara();\n flushList();\n return html.join('\\n');\n}\n\n// Everything reaching here has already been through escapeHtml() in mdRender,\n// so `text`, `label` and `url` hold no live markup and no bare quotes — which\n// is what makes it safe to drop `url` straight into an attribute below.\n// Do not add a caller that skips that step, and do not escape again here:\n// double-escaping turns a legitimate `&` in a URL into `&amp;`.\nfunction inline(text) {\n return text\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/\\*([^*]+)\\*/g, '<em>$1</em>')\n .replace(/`([^`]+)`/g, '<code>$1</code>')\n .replace(/\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/g, (m, label, url) => {\n // Only http(s) becomes a link — a README is free to write\n // `[click](javascript:…)` and we are not obliged to honour it.\n if (/^https?:\\/\\//i.test(url)) {\n return `<a href=\"${url}\" target=\"_blank\" rel=\"noopener noreferrer\">${label}</a>`;\n }\n return `<span class=\"doc-dead-link\" title=\"${url}\">${label}</span>`;\n });\n}\n\n\n// Markdown-lite: for AI answers and the static explanations, which use only\n// **bold** and `code`. Escape everything first, then restore those two — so a\n// model that echoes markup back at us can't inject it. Lives here rather than\n// beside its callers in the view layer because it is pure text work, and being\n// pure is what makes it testable.\nexport function mdLite(text) {\n return escapeHtml(String(text ?? ''))\n .split(/\\n\\n+/)\n .map((para) => `<p>${inlineMarks(para).replace(/\\n/g, '<br>')}</p>`)\n .join('');\n}\n\n// The same two marks without the paragraph wrapper, for card bodies and\n// single-line captions.\nexport function mdInline(text) {\n return inlineMarks(escapeHtml(String(text ?? '')));\n}\n\nfunction inlineMarks(safe) {\n return safe\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/`([^`]+)`/g, '<code>$1</code>');\n}\n"]}
|