codebase-onboarder 0.6.0 → 1.0.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 +284 -53
- package/bin/build-if-source.js +23 -0
- package/bin/onboarder.js +0 -0
- package/cli/agent/command.d.ts +71 -0
- package/cli/agent/command.js +177 -0
- package/cli/agent/command.js.map +1 -0
- package/cli/agent/config.d.ts +31 -0
- package/cli/agent/config.js +98 -0
- package/cli/agent/config.js.map +1 -0
- package/cli/agent/contracts.d.ts +77 -0
- package/cli/agent/contracts.js +2 -0
- package/cli/agent/contracts.js.map +1 -0
- package/cli/agent/files.d.ts +19 -0
- package/cli/agent/files.js +109 -0
- package/cli/agent/files.js.map +1 -0
- package/cli/agent/gitSource.d.ts +18 -0
- package/cli/agent/gitSource.js +69 -0
- package/cli/agent/gitSource.js.map +1 -0
- package/cli/agent/github.d.ts +21 -0
- package/cli/agent/github.js +94 -0
- package/cli/agent/github.js.map +1 -0
- package/cli/agent/mcp.d.ts +15 -0
- package/cli/agent/mcp.js +78 -0
- package/cli/agent/mcp.js.map +1 -0
- package/cli/agent/process.d.ts +18 -0
- package/cli/agent/process.js +118 -0
- package/cli/agent/process.js.map +1 -0
- package/cli/agent/protocol.d.ts +3 -0
- package/cli/agent/protocol.js +26 -0
- package/cli/agent/protocol.js.map +1 -0
- package/cli/agent/runner.d.ts +34 -0
- package/cli/agent/runner.js +152 -0
- package/cli/agent/runner.js.map +1 -0
- package/cli/agent/stdio.d.ts +1 -0
- package/cli/agent/stdio.js +62 -0
- package/cli/agent/stdio.js.map +1 -0
- package/cli/agent/tools.d.ts +7 -0
- package/cli/agent/tools.js +201 -0
- package/cli/agent/tools.js.map +1 -0
- package/cli/chat/app.d.ts +16 -0
- package/cli/chat/app.js +345 -0
- package/cli/chat/app.js.map +1 -0
- package/cli/chat/commands.d.ts +11 -0
- package/cli/chat/commands.js +120 -0
- package/cli/chat/commands.js.map +1 -0
- package/cli/chat/composer.d.ts +75 -0
- package/cli/chat/composer.js +467 -0
- package/cli/chat/composer.js.map +1 -0
- package/cli/chat/controller.d.ts +60 -0
- package/cli/chat/controller.js +393 -0
- package/cli/chat/controller.js.map +1 -0
- package/cli/chat/logo.d.ts +3 -0
- package/cli/chat/logo.js +14 -0
- package/cli/chat/logo.js.map +1 -0
- package/cli/chat/models.d.ts +18 -0
- package/cli/chat/models.js +88 -0
- package/cli/chat/models.js.map +1 -0
- package/cli/chat/profile.d.ts +5 -0
- package/cli/chat/profile.js +98 -0
- package/cli/chat/profile.js.map +1 -0
- package/cli/chat/render.d.ts +40 -0
- package/cli/chat/render.js +259 -0
- package/cli/chat/render.js.map +1 -0
- package/cli/chat/screen.d.ts +26 -0
- package/cli/chat/screen.js +117 -0
- package/cli/chat/screen.js.map +1 -0
- package/cli/chat/source.d.ts +29 -0
- package/cli/chat/source.js +116 -0
- package/cli/chat/source.js.map +1 -0
- package/cli/chat/store.d.ts +43 -0
- package/cli/chat/store.js +94 -0
- package/cli/chat/store.js.map +1 -0
- package/cli/chat/text.d.ts +4 -0
- package/cli/chat/text.js +27 -0
- package/cli/chat/text.js.map +1 -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 +321 -329
- package/cli/explorer/advanced.js.map +1 -0
- package/cli/explorer/app.d.ts +2 -0
- package/cli/explorer/app.js +404 -314
- 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 +373 -302
- 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 +42 -44
- package/cli/explorer/github.js.map +1 -0
- package/cli/explorer/graphs.d.ts +7 -0
- package/cli/explorer/graphs.js +90 -95
- 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 +143 -144
- package/cli/explorer/session.js.map +1 -0
- package/cli/explorer/views.d.ts +19 -0
- package/cli/explorer/views.js +465 -482
- package/cli/explorer/views.js.map +1 -0
- package/cli/explorer/wrap.d.ts +1 -0
- package/cli/explorer/wrap.js +28 -44
- package/cli/explorer/wrap.js.map +1 -0
- package/cli/main.d.ts +1 -0
- package/cli/main.js +251 -164
- 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 +35 -44
- 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/docs/HERMES_RESEARCH.md +51 -0
- package/package.json +16 -4
- package/public/app.d.ts +1 -0
- package/public/app.js +1133 -1011
- package/public/app.js.map +1 -0
- package/public/assets/onboarder-mark.svg +11 -0
- package/public/browser.d.ts +71 -0
- package/public/browser.js +2 -0
- package/public/browser.js.map +1 -0
- package/public/index.html +54 -20
- package/public/js/about.d.ts +6 -0
- package/public/js/about.js +152 -172
- package/public/js/about.js.map +1 -0
- package/public/js/aiDraft.d.ts +4 -0
- package/public/js/aiDraft.js +166 -125
- 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 +29 -0
- package/public/js/api.js +198 -166
- 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 +9 -0
- package/public/js/diagramPane.js +138 -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 +197 -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 +115 -0
- package/public/js/llm.js +246 -278
- 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 +29 -28
- 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 +188 -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 +37 -38
- package/server/gitRemote.js.map +1 -0
- package/server/hermesChat.d.ts +13 -0
- package/server/hermesChat.js +145 -0
- package/server/hermesChat.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 +6 -0
- package/server/http.js +27 -34
- 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 -64
- 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 +167 -180
- 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 +195 -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 +41 -43
- 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 +50 -46
- 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
package/public/js/inspector.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { eventElement } from '/js/dom.js';
|
|
1
2
|
// The inspector: everything we know about the thing you clicked, written up
|
|
2
3
|
// like margin notes. Explanations come from the static graph; the AI button
|
|
3
4
|
// hands off to the LLM when one is configured.
|
|
4
|
-
|
|
5
5
|
import { roleOf, scanIndex } from '/shared/analyzer/graph.js';
|
|
6
6
|
import { explainFile, explainFolder, explainOverview } from '/shared/analyzer/explainLocal.js';
|
|
7
7
|
import { baseName } from '/shared/analyzer/pathUtil.js';
|
|
@@ -9,527 +9,498 @@ import { languageLabel } from '/shared/analyzer/languages/index.js';
|
|
|
9
9
|
import { escapeHtml } from './html.js';
|
|
10
10
|
import { mdLite, mdInline } from './markdown.js';
|
|
11
11
|
import { renderAnalysisPanel } from './analysisPanel.js';
|
|
12
|
-
|
|
13
12
|
const el = {};
|
|
14
13
|
['inspectorEmpty', 'inspectorBody', 'inspTitle', 'inspRole', 'inspPath', 'inspStats', 'inspExplain', 'inspLists', 'aiExplainBtn', 'askInput', 'inspBack']
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
.forEach((id) => (el[id] = document.getElementById(id)));
|
|
17
15
|
let askAIHandler = null;
|
|
18
16
|
let clearFocusHandler = null;
|
|
19
17
|
let inspHooks = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
onOpenCode: (..._args) => { },
|
|
19
|
+
onDeepDive: (..._args) => { },
|
|
20
|
+
onToast: (..._args) => { },
|
|
23
21
|
};
|
|
24
|
-
|
|
25
22
|
export function initInspector(options = {}) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
23
|
+
const { onAskAI, onClearFocus, ...rest } = options;
|
|
24
|
+
askAIHandler = onAskAI;
|
|
25
|
+
clearFocusHandler = onClearFocus;
|
|
26
|
+
inspHooks = { ...inspHooks, ...rest };
|
|
27
|
+
el.aiExplainBtn.addEventListener('click', () => askAIHandler?.());
|
|
28
|
+
el.inspBack.addEventListener('click', () => clearFocusHandler?.());
|
|
29
|
+
el.inspectorBody.addEventListener('click', (e) => {
|
|
30
|
+
const btn = eventElement(e)?.closest('button[data-insp-action]');
|
|
31
|
+
if (!btn)
|
|
32
|
+
return;
|
|
33
|
+
const action = btn.dataset.inspAction;
|
|
34
|
+
const targetPath = btn.dataset.path || el.inspPath.textContent;
|
|
35
|
+
if (action === 'code') {
|
|
36
|
+
inspHooks.onOpenCode?.(targetPath);
|
|
37
|
+
}
|
|
38
|
+
else if (action === 'dive') {
|
|
39
|
+
inspHooks.onDeepDive?.(targetPath);
|
|
40
|
+
}
|
|
41
|
+
else if (action === 'copy') {
|
|
42
|
+
if (navigator.clipboard) {
|
|
43
|
+
navigator.clipboard.writeText(targetPath)
|
|
44
|
+
.then(() => inspHooks.onToast?.('Path copied to clipboard'))
|
|
45
|
+
.catch(() => inspHooks.onToast?.('Failed to copy path'));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
50
49
|
}
|
|
51
|
-
|
|
52
50
|
// Show/hide the small "‹ back" control that returns to the view's own panel.
|
|
53
51
|
function setBack(label) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
if (label) {
|
|
53
|
+
el.inspBack.hidden = false;
|
|
54
|
+
el.inspBack.textContent = '‹ ' + label;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
el.inspBack.hidden = true;
|
|
58
|
+
}
|
|
60
59
|
}
|
|
61
|
-
|
|
62
60
|
export function clearInspector() {
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
el.inspectorEmpty.hidden = false;
|
|
62
|
+
el.inspectorBody.hidden = true;
|
|
65
63
|
}
|
|
66
|
-
|
|
67
64
|
export function showFile(path, state) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const hist = history?.byPath?.[path];
|
|
82
|
-
|
|
83
|
-
const actionsHtml = `
|
|
65
|
+
const { scan, facts, history, backLabel } = state;
|
|
66
|
+
const file = scanIndex(scan).fileAt(path);
|
|
67
|
+
const role = roleOf(path, facts);
|
|
68
|
+
setBack(backLabel);
|
|
69
|
+
el.inspTitle.textContent = baseName(path);
|
|
70
|
+
el.inspRole.textContent = role;
|
|
71
|
+
el.inspRole.className = 'role-chip is-' + role;
|
|
72
|
+
el.inspPath.textContent = path;
|
|
73
|
+
el.askInput.placeholder = `Ask about ${baseName(path)}…`;
|
|
74
|
+
const fin = facts.fanIn[path] || 0;
|
|
75
|
+
const fout = facts.fanOut[path] || 0;
|
|
76
|
+
const hist = history?.byPath?.[path];
|
|
77
|
+
const actionsHtml = `
|
|
84
78
|
<div class="insp-actions" style="display:flex; gap:6px; margin: 6px 0 10px;">
|
|
85
79
|
<button class="btn btn-ghost btn-sm" data-insp-action="code" data-path="${escapeHtml(path)}" title="Open file in Code view">Code</button>
|
|
86
80
|
<button class="btn btn-ghost btn-sm" data-insp-action="dive" data-path="${escapeHtml(path)}" title="Open Deep Dive flowchart">Deep Dive →</button>
|
|
87
81
|
<button class="btn btn-ghost btn-sm" data-insp-action="copy" data-path="${escapeHtml(path)}" title="Copy relative path">Copy Path</button>
|
|
88
82
|
</div>
|
|
89
83
|
`;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
84
|
+
el.inspStats.innerHTML = [
|
|
85
|
+
chip(`<b>${fin}</b> dependents`),
|
|
86
|
+
chip(`<b>${fout}</b> imports`),
|
|
87
|
+
hist ? chip(`<b>${hist.churn}</b> commit${hist.churn === 1 ? '' : 's'}`) : '',
|
|
88
|
+
hist?.hotspot ? chip(`hotspot <b>${hist.hotspot}</b>`) : '',
|
|
89
|
+
hist?.solo && hist.churn >= 2 ? chip(`<b>solo</b> author`) : '',
|
|
90
|
+
file ? chip(`<b>${file.functions.length}</b> functions`) : '',
|
|
91
|
+
file ? chip(`<b>${(file.size / 1024).toFixed(1)}</b> kb`) : '',
|
|
92
|
+
file ? chip(langName(file.lang)) : '',
|
|
93
|
+
file && file.cognitive !== undefined ? chip(`<b>${file.cognitive}</b> cognitive cx`) : '',
|
|
94
|
+
file && file.maintainability !== undefined ? chip(`<b>${file.maintainability}</b> MI`) : '',
|
|
95
|
+
facts.communities && facts.communities[path] ? chip(`community <b>${facts.communities[path]}</b>`) : '',
|
|
96
|
+
].join('');
|
|
97
|
+
if (file && state && state.health) {
|
|
98
|
+
const riskData = state.health.perFile?.find(x => x.path === path);
|
|
99
|
+
if (riskData) {
|
|
100
|
+
el.inspStats.innerHTML += ` <risk-badge score="${riskData.risk}" style="vertical-align: middle; margin-left: 8px; --risk-size: 32px;"></risk-badge>`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
setExplain(actionsHtml + mdLite(explainFile(path, file, facts)), 'From the static analysis — no AI involved.');
|
|
104
|
+
el.inspLists.innerHTML = '';
|
|
105
|
+
addPathList('Imports', facts.importsOf[path] || [], scan);
|
|
106
|
+
addPathList('Imported by', facts.importers[path] || [], scan);
|
|
107
|
+
if (file?.exports.length) {
|
|
108
|
+
addList('Exports', file.exports.map((e) => ({ text: e.name, kind: e.kind })));
|
|
109
|
+
}
|
|
110
|
+
if (file?.functions.length) {
|
|
111
|
+
addList('Functions', file.functions.map((f) => ({ text: f.name + '()', kind: f.kind + ' · line ' + f.line })));
|
|
109
112
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
setExplain(actionsHtml + mdLite(explainFile(path, file, facts)), 'From the static analysis — no AI involved.');
|
|
113
|
-
|
|
114
|
-
el.inspLists.innerHTML = '';
|
|
115
|
-
addPathList('Imports', facts.importsOf[path] || [], scan);
|
|
116
|
-
addPathList('Imported by', facts.importers[path] || [], scan);
|
|
117
|
-
if (file?.exports.length) {
|
|
118
|
-
addList('Exports', file.exports.map((e) => ({ text: e.name, kind: e.kind })));
|
|
119
|
-
}
|
|
120
|
-
if (file?.functions.length) {
|
|
121
|
-
addList('Functions', file.functions.map((f) => ({ text: f.name + '()', kind: f.kind + ' · line ' + f.line })));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
el.inspectorEmpty.hidden = true;
|
|
125
|
-
el.inspectorBody.hidden = false;
|
|
113
|
+
el.inspectorEmpty.hidden = true;
|
|
114
|
+
el.inspectorBody.hidden = false;
|
|
126
115
|
}
|
|
127
|
-
|
|
128
116
|
export function showFolder(folder, { scan, facts, backLabel }) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
117
|
+
setBack(backLabel);
|
|
118
|
+
el.inspTitle.textContent = (folder || '(repo root)') + '/';
|
|
119
|
+
el.inspRole.textContent = 'folder';
|
|
120
|
+
el.inspRole.className = 'role-chip is-test';
|
|
121
|
+
el.inspPath.textContent = folder || '(the top of the repository)';
|
|
122
|
+
el.askInput.placeholder = folder ? `Ask about ${folder}/…` : 'Ask anything about this repo…';
|
|
123
|
+
const here = scanIndex(scan).filesIn(folder);
|
|
124
|
+
const count = here.length;
|
|
125
|
+
el.inspStats.innerHTML = chip(`<b>${count}</b> parsed files`);
|
|
126
|
+
setExplain(mdLite(explainFolder(folder, scan, facts)), 'From the static analysis — no AI involved.');
|
|
127
|
+
el.inspLists.innerHTML = '';
|
|
128
|
+
addPathList('Files here', here.map((f) => f.path), scan);
|
|
129
|
+
el.inspectorEmpty.hidden = true;
|
|
130
|
+
el.inspectorBody.hidden = false;
|
|
143
131
|
}
|
|
144
|
-
|
|
145
132
|
export function showOverview({ scan, facts, manifest, history }) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
133
|
+
setBack();
|
|
134
|
+
el.inspTitle.textContent = scan.name;
|
|
135
|
+
el.inspRole.textContent = 'repo';
|
|
136
|
+
el.inspRole.className = 'role-chip is-entry';
|
|
137
|
+
el.inspPath.textContent = scan.root;
|
|
138
|
+
el.askInput.placeholder = 'Ask anything about this repo…';
|
|
139
|
+
const conf = scan.stats.imports?.confidence;
|
|
140
|
+
el.inspStats.innerHTML = [
|
|
141
|
+
chip(`<b>${scan.stats.filesParsed}</b> files${scan.stats.truncated ? ' (partial)' : ''}`),
|
|
142
|
+
chip(`<b>${scan.stats.edgeCount}</b> connections`),
|
|
143
|
+
chip(`<b>${facts.entries.length}</b> entries`),
|
|
144
|
+
chip(`<b>${facts.hubs.length}</b> hubs`),
|
|
145
|
+
history?.available ? chip(`<b>${history.commitCount}</b> commits`) : '',
|
|
146
|
+
// How much of the import graph we actually placed. Worth a permanent slot:
|
|
147
|
+
// every other number on this page is downstream of it.
|
|
148
|
+
typeof conf === 'number' ? chip(`<b>${conf}%</b> of imports placed`) : '',
|
|
149
|
+
].join('');
|
|
150
|
+
setExplain(mdLite(explainOverview(scan, facts, manifest)), 'From the static analysis — no AI involved.');
|
|
151
|
+
el.inspLists.innerHTML = '';
|
|
152
|
+
addPathList('Entry points', facts.entries, scan);
|
|
153
|
+
addList('Load-bearing files', facts.hubs.slice(0, 8).map((h) => ({ text: h.path, kind: h.fanIn + ' dependents', path: h.path })), scan);
|
|
154
|
+
if (history?.available) {
|
|
155
|
+
if (history.perFile?.length) {
|
|
156
|
+
const topHotspots = history.perFile.filter((f) => f.hotspot > 10).slice(0, 6);
|
|
157
|
+
if (topHotspots.length) {
|
|
158
|
+
addList('Hotspots (churn × complexity)', topHotspots.map((f) => ({ text: f.path, kind: `hotspot ${f.hotspot} · ${f.churn} commits`, path: f.path })));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (history.coChanged?.length) {
|
|
162
|
+
addList('Co-changed pairs', history.coChanged.slice(0, 5).map((p) => ({ text: `${p.a} + ${p.b}`, kind: `${p.count} joint commits` })));
|
|
163
|
+
}
|
|
173
164
|
}
|
|
174
|
-
if (
|
|
175
|
-
|
|
165
|
+
if (scan.externals.length) {
|
|
166
|
+
addList('Outside packages', scan.externals.slice(0, 10).map((x) => ({ text: x.name, kind: 'used by ' + x.usedBy.length })));
|
|
176
167
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
addList('Outside packages', scan.externals.slice(0, 10).map((x) => ({ text: x.name, kind: 'used by ' + x.usedBy.length })));
|
|
180
|
-
}
|
|
181
|
-
el.inspectorEmpty.hidden = true;
|
|
182
|
-
el.inspectorBody.hidden = false;
|
|
168
|
+
el.inspectorEmpty.hidden = true;
|
|
169
|
+
el.inspectorBody.hidden = false;
|
|
183
170
|
}
|
|
184
|
-
|
|
185
171
|
// The Patterns view panel: written observations plus the folder-traffic grid.
|
|
186
172
|
export function showPatterns({ scan, patterns }) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
findings.map((f) => `
|
|
173
|
+
setBack();
|
|
174
|
+
const { layersInfo, findings, coupling } = patterns;
|
|
175
|
+
el.inspTitle.textContent = 'The shape of it';
|
|
176
|
+
el.inspRole.textContent = 'patterns';
|
|
177
|
+
el.inspRole.className = 'role-chip is-entry';
|
|
178
|
+
el.inspPath.textContent = `${scan.name} — ${layersInfo.layers.length} layers deep from the door`;
|
|
179
|
+
el.askInput.placeholder = 'Ask about the architecture…';
|
|
180
|
+
el.inspStats.innerHTML = [
|
|
181
|
+
chip(`<b>${layersInfo.layers.length}</b> layers`),
|
|
182
|
+
chip(`<b>${layersInfo.unreachable.length}</b> off the path`),
|
|
183
|
+
chip(`<b>${findings.length}</b> observations`),
|
|
184
|
+
].join('');
|
|
185
|
+
el.inspExplain.innerHTML =
|
|
186
|
+
findings.map((f) => `
|
|
202
187
|
<div class="finding is-${f.tone}">
|
|
203
188
|
<h4>${escapeHtml(f.title)}</h4>
|
|
204
189
|
<p>${mdInline(f.detail)}</p>
|
|
205
190
|
${f.paths && f.paths.length ? `<div style="margin-top:6px;display:flex;flex-wrap:wrap;gap:4px;">${f.paths.slice(0, 4).map((p) => `<button class="role-chip" data-goto="${escapeHtml(p)}" style="cursor:pointer;font-size:11px;">${escapeHtml(p)}</button>`).join('')}${f.paths.length > 4 ? `<span style="font-size:11px;color:var(--faint);align-self:center;">+${f.paths.length - 4} more</span>` : ''}</div>` : ''}
|
|
206
191
|
</div>`).join('') +
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
el.inspectorBody.hidden = false;
|
|
192
|
+
'<p class="explain-src">Read straight from the import graph — no AI involved.</p>';
|
|
193
|
+
el.inspLists.innerHTML = '';
|
|
194
|
+
if (coupling.folders.length) {
|
|
195
|
+
const h = document.createElement('h3');
|
|
196
|
+
h.className = 'inspector-h';
|
|
197
|
+
h.textContent = 'Folder traffic';
|
|
198
|
+
el.inspLists.appendChild(h);
|
|
199
|
+
el.inspLists.appendChild(heatGrid(coupling));
|
|
200
|
+
const note = document.createElement('p');
|
|
201
|
+
note.className = 'explain-src';
|
|
202
|
+
note.textContent = 'Rows pull in columns; darker means more.';
|
|
203
|
+
el.inspLists.appendChild(note);
|
|
204
|
+
}
|
|
205
|
+
el.inspectorEmpty.hidden = true;
|
|
206
|
+
el.inspectorBody.hidden = false;
|
|
223
207
|
}
|
|
224
|
-
|
|
225
208
|
// The Health view panel: the repo's grade and score with an itemized
|
|
226
209
|
// breakdown, a legend for the metrics, and the riskiest files — clickable.
|
|
227
210
|
export function showHealth({ scan, health }) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
211
|
+
setBack();
|
|
212
|
+
const t = health.totals;
|
|
213
|
+
el.inspTitle.textContent = 'Health';
|
|
214
|
+
el.inspRole.textContent = 'grade ' + health.grade;
|
|
215
|
+
el.inspRole.className = 'role-chip is-' + (health.score >= 70 ? 'entry' : health.score >= 60 ? 'hub' : 'cycle');
|
|
216
|
+
el.inspPath.textContent = `${scan.name} — ${health.score}/100`;
|
|
217
|
+
el.askInput.placeholder = 'Ask about the riskiest files…';
|
|
218
|
+
el.inspStats.innerHTML = [
|
|
219
|
+
chip(`<b>${health.score}</b> / 100`),
|
|
220
|
+
chip(`<b>${t.crit}</b> critical`),
|
|
221
|
+
chip(`<b>${t.high}</b> high risk`),
|
|
222
|
+
chip(`avg cx <b>${t.avgCx}</b>`),
|
|
223
|
+
].join('');
|
|
224
|
+
const top = health.perFile[0];
|
|
225
|
+
let note = `<b>${escapeHtml(scan.name)}</b> grades <b>${health.grade}</b> (${health.score}/100).`;
|
|
226
|
+
if (health.score >= 90)
|
|
227
|
+
note += ' Clean dependency shape — keep it that way.';
|
|
228
|
+
if (t.crit)
|
|
229
|
+
note += ` ${t.crit} file${t.crit === 1 ? ' sits' : 's sit'} in the critical band — change those with care.`;
|
|
230
|
+
else if (t.high)
|
|
231
|
+
note += ` ${t.high} file${t.high === 1 ? '' : 's'} in the high band.`;
|
|
232
|
+
if (top)
|
|
233
|
+
note += ` The single riskiest file is <b>${escapeHtml(top.name)}</b> (risk ${top.risk}${top.inCycle ? ', in a cycle' : ''}, ${top.fanIn} dependents, complexity ${top.complexity}).`;
|
|
234
|
+
const breakdownHtml = health.breakdown.length
|
|
235
|
+
? `<div class="score-bd"><h4>Why ${health.score}/100</h4><ul>`
|
|
236
|
+
+ health.breakdown.map((b) => `<li><span>${escapeHtml(b.label)}</span><b>${b.points}</b></li>`).join('')
|
|
237
|
+
+ '</ul></div>'
|
|
238
|
+
: '<p class="explain-src">A perfect run — nothing was deducted.</p>';
|
|
239
|
+
const legendHtml = `<div class="score-bd"><h4>How the risk score works</h4><ul class="legend">`
|
|
240
|
+
+ `<li><b>centrality</b> — how much of the repo flows through a file (PageRank)</li>`
|
|
241
|
+
+ (health.blastExact === false
|
|
242
|
+
? `<li><b>blast radius</b> — direct dependents only: this repo is over 2,500 files, where the transitive walk is too slow to run</li>`
|
|
243
|
+
: `<li><b>blast radius</b> — how many files break if it does</li>`)
|
|
244
|
+
+ `<li><b>complexity</b> — decision points; how hard it is to change safely</li>`
|
|
245
|
+
+ `<li><b>cycles</b> — circular dependencies, the sharpest edge</li>`
|
|
246
|
+
+ '</ul></div>';
|
|
247
|
+
el.inspExplain.innerHTML = `<p>${note}</p>` + breakdownHtml + legendHtml
|
|
248
|
+
+ '<p class="explain-src">Scored from centrality, blast radius, complexity and cycles — no AI involved.</p>';
|
|
249
|
+
el.inspLists.innerHTML = '';
|
|
250
|
+
addList('Riskiest files', health.perFile.map((f) => ({ text: f.name, kind: `risk ${f.risk} · blast ${f.blast} · cx ${f.complexity}`, path: f.path })), scan);
|
|
251
|
+
el.inspectorEmpty.hidden = true;
|
|
252
|
+
el.inspectorBody.hidden = false;
|
|
270
253
|
}
|
|
271
|
-
|
|
272
254
|
// The Security view panel: severity counts, a written summary, and the
|
|
273
255
|
// findings list — each one clickable into its file's deep-dive.
|
|
274
256
|
export function showSecurity({ scan, security }) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const rows = security.files.flatMap((f) => f.findings.slice(0, 6).map((x) => ({ text: `${f.name}:${x.line} — ${x.message}`, kind: x.severity, path: f.path })));
|
|
328
|
-
addList('Findings', rows, scan);
|
|
329
|
-
el.inspectorEmpty.hidden = true;
|
|
330
|
-
el.inspectorBody.hidden = false;
|
|
257
|
+
setBack();
|
|
258
|
+
const c = security.counts;
|
|
259
|
+
el.inspTitle.textContent = 'Security';
|
|
260
|
+
el.inspRole.textContent = 'grade ' + security.grade;
|
|
261
|
+
el.inspRole.className = 'role-chip is-' + (security.score >= 75 ? 'entry' : security.score >= 60 ? 'hub' : 'cycle');
|
|
262
|
+
el.inspPath.textContent = `${scan.name} — ${security.score}/100 · ${security.total} findings`;
|
|
263
|
+
el.askInput.placeholder = 'Ask about the security findings…';
|
|
264
|
+
el.inspStats.innerHTML = [
|
|
265
|
+
chip(`<b>${c.critical}</b> critical`),
|
|
266
|
+
chip(`<b>${c.high}</b> high`),
|
|
267
|
+
chip(`<b>${c.medium}</b> medium`),
|
|
268
|
+
chip(`<b>${c.low + c.info}</b> low/info`),
|
|
269
|
+
].join('');
|
|
270
|
+
let note = `<b>${escapeHtml(scan.name)}</b> grades <b>${security.grade}</b> for security (${security.score}/100) — ${security.total} finding${security.total === 1 ? '' : 's'} across ${security.files.length} file${security.files.length === 1 ? '' : 's'}.`;
|
|
271
|
+
if (!security.total)
|
|
272
|
+
note += ' The rule engine found nothing to flag — a clean bill.';
|
|
273
|
+
else {
|
|
274
|
+
const worst = security.files[0];
|
|
275
|
+
note += ` The loudest file is <b>${escapeHtml(worst.name)}</b> (${worst.count} finding${worst.count === 1 ? '' : 's'}, worst: ${worst.worst}).`;
|
|
276
|
+
if (c.critical)
|
|
277
|
+
note += ` ${c.critical} critical — fix those first.`;
|
|
278
|
+
else if (c.high)
|
|
279
|
+
note += ` ${c.high} high — worth a pass soon.`;
|
|
280
|
+
}
|
|
281
|
+
const sevLegend = `<div class="score-bd"><h4>What the severities mean</h4><ul class="legend">`
|
|
282
|
+
+ `<li><b class="sev-critical">critical</b> — secrets & keys committed; fix now</li>`
|
|
283
|
+
+ `<li><b class="sev-high">high</b> — injection & unsafe deserialization; sanitize, parameterize</li>`
|
|
284
|
+
+ `<li><b class="sev-medium">medium</b> — XSS sinks, weak crypto, open redirects, swallowed errors</li>`
|
|
285
|
+
+ `<li><b class="sev-low">low / info</b> — config, debug leftovers, style — tidy-up</li>`
|
|
286
|
+
+ '</ul></div>';
|
|
287
|
+
const catEntries = Object.entries(security.byCat).sort((a, b) => Number(b[1]) - Number(a[1]));
|
|
288
|
+
const catHtml = catEntries.length
|
|
289
|
+
? `<div class="score-bd"><h4>By category</h4><ul>`
|
|
290
|
+
+ catEntries.map(([cat, n]) => `<li><span>${escapeHtml(cat)}</span><b>${n}</b></li>`).join('')
|
|
291
|
+
+ '</ul></div>'
|
|
292
|
+
: '';
|
|
293
|
+
// Attribution: the security story should say which engine told it. Built-in
|
|
294
|
+
// findings carry no `source`; external ones carry the tool's name.
|
|
295
|
+
const externalCount = security.files.reduce((n, f) => n + f.findings.filter((x) => x.source === 'external').length, 0);
|
|
296
|
+
const sourceLine = externalCount
|
|
297
|
+
? `From the built-in rule engine plus ${externalCount} finding${externalCount === 1 ? '' : 's'} from external engines (see the list below).`
|
|
298
|
+
: 'From the built-in rule engine — no external scanner, no AI.';
|
|
299
|
+
el.inspExplain.innerHTML = `<p>${note}</p>` + catHtml + sevLegend
|
|
300
|
+
+ `<p class="explain-src">${sourceLine}</p>`;
|
|
301
|
+
// The deep-analysis panel: which engines are installed, and the button that
|
|
302
|
+
// runs them. Rendered on every pass so a finished run re-paints in place.
|
|
303
|
+
renderAnalysisPanel();
|
|
304
|
+
el.inspLists.innerHTML = '';
|
|
305
|
+
const rows = security.files.flatMap((f) => f.findings.slice(0, 6).map((x) => ({ text: `${f.name}:${x.line} — ${x.message}`, kind: x.severity, path: f.path })));
|
|
306
|
+
addList('Findings', rows, scan);
|
|
307
|
+
el.inspectorEmpty.hidden = true;
|
|
308
|
+
el.inspectorBody.hidden = false;
|
|
331
309
|
}
|
|
332
|
-
|
|
333
310
|
// The History view panel: what the git log says — churn, authorship, the
|
|
334
311
|
// hotspot cross-tab — with the top of each list clickable into the file's
|
|
335
312
|
// deep-dive. When there is no history it says why rather than showing zeros.
|
|
336
313
|
export function showHistory({ scan, history }) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
314
|
+
setBack();
|
|
315
|
+
el.inspTitle.textContent = 'History';
|
|
316
|
+
if (!history?.available) {
|
|
317
|
+
el.inspRole.textContent = 'unavailable';
|
|
318
|
+
el.inspRole.className = 'role-chip is-test';
|
|
319
|
+
el.inspPath.textContent = scan.name;
|
|
320
|
+
el.askInput.placeholder = 'Ask anything about this repo…';
|
|
321
|
+
el.inspStats.innerHTML = '';
|
|
322
|
+
el.inspExplain.innerHTML =
|
|
323
|
+
`<p>${escapeHtml(history?.reason || 'No history available.')}</p>`
|
|
324
|
+
+ '<p class="explain-src">History needs a checkout with commits — a git URL clone has one; a folder picked in the browser or a plain directory may not.</p>';
|
|
325
|
+
el.inspLists.innerHTML = '';
|
|
326
|
+
el.inspectorEmpty.hidden = true;
|
|
327
|
+
el.inspectorBody.hidden = false;
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const hotspots = history.perFile.filter((f) => f.hotspot > 0);
|
|
331
|
+
const top = hotspots[0];
|
|
332
|
+
el.inspRole.textContent = history.truncated
|
|
333
|
+
? `${history.commitCount} of ${history.totalCommits} commits`
|
|
334
|
+
: `${history.commitCount} commits`;
|
|
335
|
+
el.inspRole.className = 'role-chip is-entry';
|
|
336
|
+
el.inspPath.textContent = `${scan.name} — ${span(history.firstCommitAt, history.lastCommitAt)}`;
|
|
337
|
+
el.askInput.placeholder = 'Ask about the hotspots…';
|
|
338
|
+
el.inspStats.innerHTML = [
|
|
339
|
+
chip(`<b>${history.authors.length}</b> ${history.authors.length === 1 ? 'author' : 'authors'}`),
|
|
340
|
+
chip(`<b>${hotspots.length}</b> ${hotspots.length === 1 ? 'hotspot' : 'hotspots'}`),
|
|
341
|
+
history.soloFiles ? chip(`<b>${history.soloFiles}</b> solo-owned`) : '',
|
|
342
|
+
history.pathsGone ? chip(`<b>${history.pathsGone}</b> files gone`) : '',
|
|
343
|
+
].join('');
|
|
344
|
+
let note = `<b>${escapeHtml(scan.name)}</b> shows ${history.commitCount} commits by ${history.authors.length} ${history.authors.length === 1 ? 'person' : 'people'}, ${span(history.firstCommitAt, history.lastCommitAt)}.`;
|
|
345
|
+
if (top) {
|
|
346
|
+
note += ` The sharpest hotspot is <b>${escapeHtml(top.name)}</b> — touched ${top.churn} ${top.churn === 1 ? 'time' : 'times'} at complexity ${top.complexity}`;
|
|
347
|
+
note += top.solo ? ', always by the same person.' : ` by ${top.authors} ${top.authors === 1 ? 'person' : 'people'}.`;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
note += ' Nothing stands out — no file is both complex and frequently changed.';
|
|
351
|
+
}
|
|
352
|
+
if (history.truncated)
|
|
353
|
+
note += ` The window was capped at ${history.commitCount} of ${history.totalCommits} total commits.`;
|
|
354
|
+
const legendHtml = `<div class="score-bd"><h4>How the hotspot score works</h4><ul class="legend">`
|
|
355
|
+
+ `<li><b>churn</b> — how many commits have touched the file</li>`
|
|
356
|
+
+ `<li><b>complexity</b> — decision points, the same number Health uses</li>`
|
|
357
|
+
+ `<li><b>hotspot</b> — both at once, each against the repo's own worst: where change and difficulty meet</li>`
|
|
358
|
+
+ `<li><b>solo</b> — only one person has ever touched it; knowledge leaves with them</li>`
|
|
359
|
+
+ '</ul></div>';
|
|
360
|
+
el.inspExplain.innerHTML = `<p>${note}</p>` + legendHtml
|
|
361
|
+
+ '<p class="explain-src">From the git log — authors keyed by email, no AI involved.</p>';
|
|
348
362
|
el.inspLists.innerHTML = '';
|
|
363
|
+
addList('Hotspots', hotspots.map((f) => ({
|
|
364
|
+
text: f.name,
|
|
365
|
+
kind: `hotspot ${f.hotspot} · ${f.churn} commits · cx ${f.complexity}${f.solo ? ' · solo' : ''}`,
|
|
366
|
+
path: f.path,
|
|
367
|
+
})), scan);
|
|
368
|
+
if (history.coChanged?.length) {
|
|
369
|
+
addList('Change together, unconnected', history.coChanged.map((p) => ({
|
|
370
|
+
text: p.a.split('/').pop() + ' + ' + p.b.split('/').pop(),
|
|
371
|
+
kind: p.count + '× together',
|
|
372
|
+
path: p.a,
|
|
373
|
+
title: p.a + ' ↔ ' + p.b,
|
|
374
|
+
})), scan);
|
|
375
|
+
}
|
|
349
376
|
el.inspectorEmpty.hidden = true;
|
|
350
377
|
el.inspectorBody.hidden = false;
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const hotspots = history.perFile.filter((f) => f.hotspot > 0);
|
|
355
|
-
const top = hotspots[0];
|
|
356
|
-
el.inspRole.textContent = history.truncated
|
|
357
|
-
? `${history.commitCount} of ${history.totalCommits} commits`
|
|
358
|
-
: `${history.commitCount} commits`;
|
|
359
|
-
el.inspRole.className = 'role-chip is-entry';
|
|
360
|
-
el.inspPath.textContent = `${scan.name} — ${span(history.firstCommitAt, history.lastCommitAt)}`;
|
|
361
|
-
el.askInput.placeholder = 'Ask about the hotspots…';
|
|
362
|
-
el.inspStats.innerHTML = [
|
|
363
|
-
chip(`<b>${history.authors.length}</b> ${history.authors.length === 1 ? 'author' : 'authors'}`),
|
|
364
|
-
chip(`<b>${hotspots.length}</b> ${hotspots.length === 1 ? 'hotspot' : 'hotspots'}`),
|
|
365
|
-
history.soloFiles ? chip(`<b>${history.soloFiles}</b> solo-owned`) : '',
|
|
366
|
-
history.pathsGone ? chip(`<b>${history.pathsGone}</b> files gone`) : '',
|
|
367
|
-
].join('');
|
|
368
|
-
|
|
369
|
-
let note = `<b>${escapeHtml(scan.name)}</b> shows ${history.commitCount} commits by ${history.authors.length} ${history.authors.length === 1 ? 'person' : 'people'}, ${span(history.firstCommitAt, history.lastCommitAt)}.`;
|
|
370
|
-
if (top) {
|
|
371
|
-
note += ` The sharpest hotspot is <b>${escapeHtml(top.name)}</b> — touched ${top.churn} ${top.churn === 1 ? 'time' : 'times'} at complexity ${top.complexity}`;
|
|
372
|
-
note += top.solo ? ', always by the same person.' : ` by ${top.authors} ${top.authors === 1 ? 'person' : 'people'}.`;
|
|
373
|
-
} else {
|
|
374
|
-
note += ' Nothing stands out — no file is both complex and frequently changed.';
|
|
375
|
-
}
|
|
376
|
-
if (history.truncated) note += ` The window was capped at ${history.commitCount} of ${history.totalCommits} total commits.`;
|
|
377
|
-
|
|
378
|
-
const legendHtml = `<div class="score-bd"><h4>How the hotspot score works</h4><ul class="legend">`
|
|
379
|
-
+ `<li><b>churn</b> — how many commits have touched the file</li>`
|
|
380
|
-
+ `<li><b>complexity</b> — decision points, the same number Health uses</li>`
|
|
381
|
-
+ `<li><b>hotspot</b> — both at once, each against the repo's own worst: where change and difficulty meet</li>`
|
|
382
|
-
+ `<li><b>solo</b> — only one person has ever touched it; knowledge leaves with them</li>`
|
|
383
|
-
+ '</ul></div>';
|
|
384
|
-
|
|
385
|
-
el.inspExplain.innerHTML = `<p>${note}</p>` + legendHtml
|
|
386
|
-
+ '<p class="explain-src">From the git log — authors keyed by email, no AI involved.</p>';
|
|
387
|
-
el.inspLists.innerHTML = '';
|
|
388
|
-
addList('Hotspots', hotspots.map((f) => ({
|
|
389
|
-
text: f.name,
|
|
390
|
-
kind: `hotspot ${f.hotspot} · ${f.churn} commits · cx ${f.complexity}${f.solo ? ' · solo' : ''}`,
|
|
391
|
-
path: f.path,
|
|
392
|
-
})), scan);
|
|
393
|
-
if (history.coChanged?.length) {
|
|
394
|
-
addList('Change together, unconnected', history.coChanged.map((p) => ({
|
|
395
|
-
text: p.a.split('/').pop() + ' + ' + p.b.split('/').pop(),
|
|
396
|
-
kind: p.count + '× together',
|
|
397
|
-
path: p.a,
|
|
398
|
-
title: p.a + ' ↔ ' + p.b,
|
|
399
|
-
})), scan);
|
|
400
|
-
}
|
|
401
|
-
el.inspectorEmpty.hidden = true;
|
|
402
|
-
el.inspectorBody.hidden = false;
|
|
403
378
|
}
|
|
404
|
-
|
|
405
379
|
function span(firstIso, lastIso) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
380
|
+
const fmt = (iso) => {
|
|
381
|
+
if (!iso)
|
|
382
|
+
return '?';
|
|
383
|
+
const d = new Date(iso);
|
|
384
|
+
if (Number.isNaN(d.getTime()))
|
|
385
|
+
return iso.slice(0, 10);
|
|
386
|
+
return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
|
|
387
|
+
};
|
|
388
|
+
return fmt(firstIso) + ' → ' + fmt(lastIso);
|
|
413
389
|
}
|
|
414
|
-
|
|
415
390
|
function heatGrid(coupling) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
grid.appendChild(document.createElement('span'));
|
|
422
|
-
for (const c of folders) {
|
|
423
|
-
const label = document.createElement('span');
|
|
424
|
-
label.className = 'heat-col-label';
|
|
425
|
-
label.textContent = shortFolder(c);
|
|
426
|
-
label.title = c;
|
|
427
|
-
grid.appendChild(label);
|
|
428
|
-
}
|
|
429
|
-
for (const r of folders) {
|
|
430
|
-
const label = document.createElement('span');
|
|
431
|
-
label.className = 'heat-label';
|
|
432
|
-
label.textContent = shortFolder(r);
|
|
433
|
-
label.title = r;
|
|
434
|
-
grid.appendChild(label);
|
|
391
|
+
const { folders, counts, max } = coupling;
|
|
392
|
+
const grid = document.createElement('div');
|
|
393
|
+
grid.className = 'heat';
|
|
394
|
+
grid.style.gridTemplateColumns = `64px repeat(${folders.length}, 1fr)`;
|
|
395
|
+
grid.appendChild(document.createElement('span'));
|
|
435
396
|
for (const c of folders) {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
397
|
+
const label = document.createElement('span');
|
|
398
|
+
label.className = 'heat-col-label';
|
|
399
|
+
label.textContent = shortFolder(c);
|
|
400
|
+
label.title = c;
|
|
401
|
+
grid.appendChild(label);
|
|
402
|
+
}
|
|
403
|
+
for (const r of folders) {
|
|
404
|
+
const label = document.createElement('span');
|
|
405
|
+
label.className = 'heat-label';
|
|
406
|
+
label.textContent = shortFolder(r);
|
|
407
|
+
label.title = r;
|
|
408
|
+
grid.appendChild(label);
|
|
409
|
+
for (const c of folders) {
|
|
410
|
+
const cell = document.createElement('span');
|
|
411
|
+
cell.className = 'heat-cell';
|
|
412
|
+
const n = counts.get(r + '->' + c) || 0;
|
|
413
|
+
if (n > 0) {
|
|
414
|
+
const pct = Math.max(14, Math.round((n / max) * 86));
|
|
415
|
+
cell.style.background = `color-mix(in srgb, var(--accent) ${pct}%, transparent)`;
|
|
416
|
+
cell.textContent = n;
|
|
417
|
+
cell.title = `${r} → ${c}: ${n} ${n === 1 ? 'import' : 'imports'}`;
|
|
418
|
+
}
|
|
419
|
+
grid.appendChild(cell);
|
|
420
|
+
}
|
|
446
421
|
}
|
|
447
|
-
|
|
448
|
-
return grid;
|
|
422
|
+
return grid;
|
|
449
423
|
}
|
|
450
|
-
|
|
451
424
|
function shortFolder(name) {
|
|
452
|
-
|
|
453
|
-
|
|
425
|
+
const clean = name === '(root)' ? 'root' : name;
|
|
426
|
+
return clean.length > 9 ? clean.slice(0, 8) + '…' : clean;
|
|
454
427
|
}
|
|
455
|
-
|
|
456
428
|
// The AI flow reuses the same box: streaming text lands here. `headerHtml`
|
|
457
429
|
// pins a line above the answer — the question being answered, for instance.
|
|
458
430
|
export function setExplain(html, sourceNote) {
|
|
459
|
-
|
|
431
|
+
el.inspExplain.innerHTML = html + (sourceNote ? `<p class="explain-src">${escapeHtml(sourceNote)}</p>` : '');
|
|
460
432
|
}
|
|
461
|
-
|
|
462
433
|
export function explainStreamWriter(headerHtml = '') {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
434
|
+
let text = '';
|
|
435
|
+
el.inspExplain.innerHTML = headerHtml + '<p><span class="caret"></span></p>';
|
|
436
|
+
return {
|
|
437
|
+
push(delta) {
|
|
438
|
+
text += delta;
|
|
439
|
+
el.inspExplain.innerHTML = headerHtml + mdLite(text) + '<span class="caret"></span>';
|
|
440
|
+
},
|
|
441
|
+
finish(note) {
|
|
442
|
+
el.inspExplain.innerHTML = headerHtml + mdLite(text) + (note ? `<p class="explain-src">${escapeHtml(note)}</p>` : '');
|
|
443
|
+
},
|
|
444
|
+
fail(message) {
|
|
445
|
+
el.inspExplain.innerHTML = headerHtml + `<p style="color:var(--warn)">${escapeHtml(message)}</p>`;
|
|
446
|
+
},
|
|
447
|
+
};
|
|
477
448
|
}
|
|
478
|
-
|
|
479
449
|
// ---- helpers ---------------------------------------------------------------
|
|
480
|
-
|
|
481
450
|
function addPathList(title, paths, scan) {
|
|
482
|
-
|
|
483
|
-
|
|
451
|
+
if (!paths.length)
|
|
452
|
+
return;
|
|
453
|
+
addList(title, paths.map((p) => ({ text: p, path: p })), scan);
|
|
484
454
|
}
|
|
485
|
-
|
|
486
455
|
function addList(title, items, scan) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
456
|
+
if (!items || !items.length)
|
|
457
|
+
return;
|
|
458
|
+
const h = document.createElement('h3');
|
|
459
|
+
h.className = 'inspector-h';
|
|
460
|
+
h.textContent = title;
|
|
461
|
+
const ul = document.createElement('ul');
|
|
462
|
+
ul.className = 'link-list';
|
|
463
|
+
for (const item of items.slice(0, 14)) {
|
|
464
|
+
const li = document.createElement('li');
|
|
465
|
+
if (item.path) {
|
|
466
|
+
const b = document.createElement('button');
|
|
467
|
+
b.textContent = item.text;
|
|
468
|
+
b.dataset.goto = item.path;
|
|
469
|
+
if (item.title)
|
|
470
|
+
b.title = item.title;
|
|
471
|
+
li.appendChild(b);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
li.textContent = item.text;
|
|
475
|
+
}
|
|
476
|
+
if (item.kind) {
|
|
477
|
+
const k = document.createElement('span');
|
|
478
|
+
k.className = 'fn-kind';
|
|
479
|
+
k.textContent = item.kind;
|
|
480
|
+
li.appendChild(k);
|
|
481
|
+
}
|
|
482
|
+
ul.appendChild(li);
|
|
503
483
|
}
|
|
504
|
-
if (
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
484
|
+
if (items.length > 14) {
|
|
485
|
+
const more = document.createElement('li');
|
|
486
|
+
more.textContent = `…and ${items.length - 14} more`;
|
|
487
|
+
more.style.color = 'var(--faint)';
|
|
488
|
+
ul.appendChild(more);
|
|
509
489
|
}
|
|
510
|
-
|
|
511
|
-
}
|
|
512
|
-
if (items.length > 14) {
|
|
513
|
-
const more = document.createElement('li');
|
|
514
|
-
more.textContent = `…and ${items.length - 14} more`;
|
|
515
|
-
more.style.color = 'var(--faint)';
|
|
516
|
-
ul.appendChild(more);
|
|
517
|
-
}
|
|
518
|
-
el.inspLists.append(h, ul);
|
|
490
|
+
el.inspLists.append(h, ul);
|
|
519
491
|
}
|
|
520
|
-
|
|
521
492
|
// Clicking a path inside the inspector navigates to it (wired in app.js).
|
|
522
493
|
export function onInspectorNavigate(handler) {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
494
|
+
el.inspLists.addEventListener('click', (event) => {
|
|
495
|
+
const b = eventElement(event)?.closest('button[data-goto]');
|
|
496
|
+
if (b)
|
|
497
|
+
handler(b.dataset.goto);
|
|
498
|
+
});
|
|
527
499
|
}
|
|
528
|
-
|
|
529
500
|
function chip(html) {
|
|
530
|
-
|
|
501
|
+
return `<span class="stat-chip">${html}</span>`;
|
|
531
502
|
}
|
|
532
|
-
|
|
533
503
|
function langName(id) {
|
|
534
|
-
|
|
504
|
+
return languageLabel(id, { short: true });
|
|
535
505
|
}
|
|
506
|
+
//# sourceMappingURL=inspector.js.map
|