codebase-onboarder 0.6.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 +174 -46
- 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 +321 -329
- package/cli/explorer/advanced.js.map +1 -0
- package/cli/explorer/app.d.ts +1 -0
- package/cli/explorer/app.js +384 -313
- 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 +368 -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 +214 -161
- 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/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 +152 -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 +37 -38
- 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 -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 +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 +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 +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
package/public/js/diffView.js
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
// PR / Branch Diff & Blast Radius View.
|
|
2
2
|
// Renders side-by-side / unified git diffs and calculates downstream architectural blast radius.
|
|
3
|
-
|
|
4
3
|
import { escapeHtml } from './html.js';
|
|
5
|
-
|
|
6
4
|
let activeScanId = null;
|
|
7
5
|
let currentDiff = null;
|
|
8
6
|
let currentRefs = null;
|
|
9
7
|
let selectedFile = null;
|
|
8
|
+
let generation = 0;
|
|
9
|
+
let diffRequest = null;
|
|
10
|
+
let refsRequest = null;
|
|
11
|
+
export function stopDiff() { generation++; diffRequest?.abort(); refsRequest?.abort(); }
|
|
10
12
|
let diffMode = 'unified'; // 'unified' | 'split'
|
|
11
|
-
|
|
12
13
|
export async function renderDiffView(container, { scan, facts, scanId }) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
stopDiff();
|
|
15
|
+
const viewGeneration = generation;
|
|
16
|
+
currentDiff = null;
|
|
17
|
+
if (!container)
|
|
18
|
+
return;
|
|
19
|
+
activeScanId = scanId;
|
|
20
|
+
container.innerHTML = `
|
|
17
21
|
<div class="diff-layout">
|
|
18
22
|
<header class="diff-header">
|
|
19
23
|
<div class="diff-ref-selectors">
|
|
20
24
|
<div class="diff-select-group">
|
|
21
25
|
<span class="diff-select-label">Base:</span>
|
|
22
26
|
<select class="diff-select" id="diffBaseSelect">
|
|
23
|
-
<option value="HEAD~1">HEAD~1 (previous commit)</option>
|
|
24
27
|
<option value="HEAD">HEAD (committed)</option>
|
|
28
|
+
<option value="HEAD~1">HEAD~1 (previous commit)</option>
|
|
25
29
|
<option value="main">main</option>
|
|
26
30
|
</select>
|
|
27
31
|
</div>
|
|
@@ -68,145 +72,161 @@ export async function renderDiffView(container, { scan, facts, scanId }) {
|
|
|
68
72
|
</div>
|
|
69
73
|
</div>
|
|
70
74
|
`;
|
|
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
|
-
// Initial load
|
|
98
|
-
await loadRefs(container, scanId);
|
|
99
|
-
await loadDiffData(container, scan, facts, 'HEAD~1', '');
|
|
75
|
+
// Wire controls
|
|
76
|
+
const baseSelect = container.querySelector('#diffBaseSelect');
|
|
77
|
+
const headSelect = container.querySelector('#diffHeadSelect');
|
|
78
|
+
const runBtn = container.querySelector('#diffRunBtn');
|
|
79
|
+
const unifiedBtn = container.querySelector('#diffModeUnified');
|
|
80
|
+
const splitBtn = container.querySelector('#diffModeSplit');
|
|
81
|
+
unifiedBtn?.addEventListener('click', () => {
|
|
82
|
+
diffMode = 'unified';
|
|
83
|
+
unifiedBtn.classList.add('is-active');
|
|
84
|
+
splitBtn?.classList.remove('is-active');
|
|
85
|
+
renderCurrentFileDiff(container);
|
|
86
|
+
});
|
|
87
|
+
splitBtn?.addEventListener('click', () => {
|
|
88
|
+
diffMode = 'split';
|
|
89
|
+
splitBtn.classList.add('is-active');
|
|
90
|
+
unifiedBtn?.classList.remove('is-active');
|
|
91
|
+
renderCurrentFileDiff(container);
|
|
92
|
+
});
|
|
93
|
+
runBtn?.addEventListener('click', () => {
|
|
94
|
+
loadDiffData(container, scan, facts, baseSelect.value, headSelect.value);
|
|
95
|
+
});
|
|
96
|
+
// Initial load
|
|
97
|
+
await loadRefs(container, scanId, viewGeneration);
|
|
98
|
+
if (viewGeneration !== generation)
|
|
99
|
+
return;
|
|
100
|
+
await loadDiffData(container, scan, facts, 'HEAD', '');
|
|
100
101
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
102
|
+
async function loadRefs(container, scanId, viewGeneration) {
|
|
103
|
+
if (!scanId)
|
|
104
|
+
return;
|
|
105
|
+
try {
|
|
106
|
+
const controller = new AbortController();
|
|
107
|
+
refsRequest = controller;
|
|
108
|
+
const res = await fetch('/api/diff/refs?scan=' + encodeURIComponent(scanId), { signal: controller.signal });
|
|
109
|
+
if (res.ok) {
|
|
110
|
+
const data = await res.json();
|
|
111
|
+
if (viewGeneration !== generation)
|
|
112
|
+
return;
|
|
113
|
+
currentRefs = data;
|
|
114
|
+
const baseSelect = container.querySelector('#diffBaseSelect');
|
|
115
|
+
const headSelect = container.querySelector('#diffHeadSelect');
|
|
116
|
+
if (baseSelect && currentRefs.branches?.length) {
|
|
117
|
+
baseSelect.innerHTML = `
|
|
113
118
|
<option value="HEAD">HEAD</option>
|
|
119
|
+
<option value="HEAD~1">HEAD~1 (previous commit)</option>
|
|
114
120
|
${currentRefs.branches.map((b) => `<option value="${escapeHtml(b)}">${escapeHtml(b)}</option>`).join('')}
|
|
115
121
|
${(currentRefs.tags || []).map((t) => `<option value="${escapeHtml(t)}">tag: ${escapeHtml(t)}</option>`).join('')}
|
|
116
122
|
`;
|
|
117
|
-
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
/* fallback to defaults */
|
|
118
128
|
}
|
|
119
|
-
} catch (e) {
|
|
120
|
-
/* fallback to defaults */
|
|
121
|
-
}
|
|
122
129
|
}
|
|
123
|
-
|
|
124
130
|
async function loadDiffData(container, scan, facts, base, head) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
131
|
+
diffRequest?.abort();
|
|
132
|
+
const controller = new AbortController();
|
|
133
|
+
diffRequest = controller;
|
|
134
|
+
const viewGeneration = generation;
|
|
135
|
+
currentDiff = null;
|
|
136
|
+
selectedFile = null;
|
|
137
|
+
const mainEl = container.querySelector('#diffViewerMain');
|
|
138
|
+
if (mainEl)
|
|
139
|
+
mainEl.innerHTML = '<div class="diff-placeholder">Reading selected comparison…</div>';
|
|
140
|
+
const metric = container.querySelector('#blastMetric');
|
|
141
|
+
if (metric)
|
|
142
|
+
metric.textContent = '—';
|
|
143
|
+
const listEl = container.querySelector('#diffFilesList');
|
|
144
|
+
const summaryEl = container.querySelector('#diffSummaryChip');
|
|
145
|
+
if (listEl)
|
|
146
|
+
listEl.innerHTML = '<p class="diff-empty">Loading diff…</p>';
|
|
147
|
+
try {
|
|
148
|
+
const params = new URLSearchParams();
|
|
149
|
+
if (activeScanId)
|
|
150
|
+
params.set('scan', activeScanId);
|
|
151
|
+
if (base)
|
|
152
|
+
params.set('base', base);
|
|
153
|
+
if (head)
|
|
154
|
+
params.set('head', head);
|
|
155
|
+
const res = await fetch('/api/diff?' + params.toString(), { signal: controller.signal });
|
|
156
|
+
const data = await res.json();
|
|
157
|
+
if (controller.signal.aborted || viewGeneration !== generation)
|
|
158
|
+
return;
|
|
159
|
+
if (!res.ok)
|
|
160
|
+
throw new Error(data.error || 'Comparison unavailable.');
|
|
161
|
+
currentDiff = data;
|
|
162
|
+
const files = currentDiff.files || [];
|
|
163
|
+
if (summaryEl) {
|
|
164
|
+
summaryEl.innerHTML = `<b>${files.length}</b> files <span class="diff-add">+${currentDiff.stats?.additions || 0}</span> <span class="diff-del">-${currentDiff.stats?.deletions || 0}</span>`;
|
|
165
|
+
}
|
|
166
|
+
// Compute blast radius from modified files
|
|
167
|
+
computeAndRenderBlastRadius(container, files, scan, facts);
|
|
168
|
+
if (!files.length) {
|
|
169
|
+
if (listEl)
|
|
170
|
+
listEl.innerHTML = '<p class="diff-empty">No differences between selected references.</p>';
|
|
171
|
+
const mainEl = container.querySelector('#diffViewerMain');
|
|
172
|
+
if (mainEl)
|
|
173
|
+
mainEl.innerHTML = '<div class="diff-placeholder">No changes in this comparison. Untracked files are available in Review.</div>';
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
selectedFile = files[0].newPath || files[0].oldPath;
|
|
177
|
+
renderFilesList(container, files);
|
|
178
|
+
renderCurrentFileDiff(container);
|
|
139
179
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
180
|
+
catch (err) {
|
|
181
|
+
if (controller.signal.aborted || viewGeneration !== generation)
|
|
182
|
+
return;
|
|
183
|
+
if (summaryEl)
|
|
184
|
+
summaryEl.textContent = 'Unavailable';
|
|
185
|
+
if (mainEl)
|
|
186
|
+
mainEl.innerHTML = '<div class="diff-placeholder">Choose valid references and compare again.</div>';
|
|
187
|
+
if (listEl)
|
|
188
|
+
listEl.innerHTML = `<p class="diff-empty">Failed to load diff: ${escapeHtml(err.message)}</p>`;
|
|
146
189
|
}
|
|
147
|
-
|
|
148
|
-
// Compute blast radius from modified files
|
|
149
|
-
computeAndRenderBlastRadius(container, files, scan, facts);
|
|
150
|
-
|
|
151
|
-
if (!files.length) {
|
|
152
|
-
if (listEl) listEl.innerHTML = '<p class="diff-empty">No differences between selected references.</p>';
|
|
153
|
-
const mainEl = container.querySelector('#diffViewerMain');
|
|
154
|
-
if (mainEl) mainEl.innerHTML = '<div class="diff-placeholder">Working tree clean. No changes detected.</div>';
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
selectedFile = files[0].newPath || files[0].oldPath;
|
|
159
|
-
renderFilesList(container, files);
|
|
160
|
-
renderCurrentFileDiff(container);
|
|
161
|
-
} catch (err) {
|
|
162
|
-
if (listEl) listEl.innerHTML = `<p class="diff-empty">Failed to load diff: ${escapeHtml(err.message)}</p>`;
|
|
163
|
-
}
|
|
164
190
|
}
|
|
165
|
-
|
|
166
191
|
function computeAndRenderBlastRadius(container, diffFiles, scan, facts) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const modifiedPaths = new Set(diffFiles.map((f) => f.newPath || f.oldPath));
|
|
178
|
-
const impacted = new Set();
|
|
179
|
-
const queue = [...modifiedPaths];
|
|
180
|
-
|
|
181
|
-
while (queue.length) {
|
|
182
|
-
const curr = queue.shift();
|
|
183
|
-
const dependents = facts?.importers?.[curr] || [];
|
|
184
|
-
for (const dep of dependents) {
|
|
185
|
-
if (!impacted.has(dep) && !modifiedPaths.has(dep)) {
|
|
186
|
-
impacted.add(dep);
|
|
187
|
-
queue.push(dep);
|
|
188
|
-
}
|
|
192
|
+
const metricEl = container.querySelector('#blastMetric');
|
|
193
|
+
const descEl = container.querySelector('#blastDesc');
|
|
194
|
+
if (!metricEl || !descEl)
|
|
195
|
+
return;
|
|
196
|
+
if (!diffFiles.length) {
|
|
197
|
+
metricEl.textContent = '0 files';
|
|
198
|
+
descEl.textContent = 'No modified files to analyze.';
|
|
199
|
+
return;
|
|
189
200
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
201
|
+
const modifiedPaths = new Set(diffFiles.map((f) => f.newPath || f.oldPath));
|
|
202
|
+
const impacted = new Set();
|
|
203
|
+
const queue = [...modifiedPaths];
|
|
204
|
+
while (queue.length) {
|
|
205
|
+
const curr = queue.shift();
|
|
206
|
+
const dependents = facts?.importers?.[curr] || [];
|
|
207
|
+
for (const dep of dependents) {
|
|
208
|
+
if (!impacted.has(dep) && !modifiedPaths.has(dep)) {
|
|
209
|
+
impacted.add(dep);
|
|
210
|
+
queue.push(dep);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const totalFiles = scan?.files?.length || 1;
|
|
215
|
+
const impactCount = impacted.size;
|
|
216
|
+
const pct = Math.round((impactCount / totalFiles) * 100);
|
|
217
|
+
metricEl.innerHTML = `<b>${impactCount}</b> dependent files affected (${pct}%)`;
|
|
218
|
+
descEl.innerHTML = impactCount > 0
|
|
219
|
+
? `Directly impacts <b>${impactCount}</b> downstream module${impactCount === 1 ? '' : 's'} across the import graph.`
|
|
220
|
+
: `Self-contained change: no downstream imports affected.`;
|
|
200
221
|
}
|
|
201
|
-
|
|
202
222
|
function renderFilesList(container, files) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
223
|
+
const listEl = container.querySelector('#diffFilesList');
|
|
224
|
+
if (!listEl)
|
|
225
|
+
return;
|
|
226
|
+
listEl.innerHTML = files.map((f) => {
|
|
227
|
+
const path = f.newPath || f.oldPath;
|
|
228
|
+
const isSelected = path === selectedFile;
|
|
229
|
+
return `
|
|
210
230
|
<div class="diff-file-item ${isSelected ? 'is-selected' : ''}" data-path="${escapeHtml(path)}">
|
|
211
231
|
<span class="diff-file-badge status-${f.status}">${f.status.slice(0, 1).toUpperCase()}</span>
|
|
212
232
|
<span class="diff-file-path" title="${escapeHtml(path)}">${escapeHtml(path)}</span>
|
|
@@ -216,36 +236,32 @@ function renderFilesList(container, files) {
|
|
|
216
236
|
</span>
|
|
217
237
|
</div>
|
|
218
238
|
`;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
239
|
+
}).join('');
|
|
240
|
+
listEl.querySelectorAll('.diff-file-item').forEach((item) => {
|
|
241
|
+
item.addEventListener('click', () => {
|
|
242
|
+
selectedFile = item.dataset.path;
|
|
243
|
+
listEl.querySelectorAll('.diff-file-item').forEach((el) => el.classList.remove('is-selected'));
|
|
244
|
+
item.classList.add('is-selected');
|
|
245
|
+
renderCurrentFileDiff(container);
|
|
246
|
+
});
|
|
227
247
|
});
|
|
228
|
-
});
|
|
229
248
|
}
|
|
230
|
-
|
|
231
249
|
function renderCurrentFileDiff(container) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const hunksHtml = file.hunks.map((hunk) => {
|
|
242
|
-
if (diffMode === 'split') {
|
|
243
|
-
return renderSplitHunk(hunk);
|
|
250
|
+
const mainEl = container.querySelector('#diffViewerMain');
|
|
251
|
+
if (!mainEl || !currentDiff)
|
|
252
|
+
return;
|
|
253
|
+
const file = currentDiff.files?.find((f) => (f.newPath || f.oldPath) === selectedFile);
|
|
254
|
+
if (!file) {
|
|
255
|
+
mainEl.innerHTML = '<div class="diff-placeholder">Select a file to inspect diff.</div>';
|
|
256
|
+
return;
|
|
244
257
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
258
|
+
const hunksHtml = file.hunks.map((hunk) => {
|
|
259
|
+
if (diffMode === 'split') {
|
|
260
|
+
return renderSplitHunk(hunk);
|
|
261
|
+
}
|
|
262
|
+
return renderUnifiedHunk(hunk);
|
|
263
|
+
}).join('');
|
|
264
|
+
mainEl.innerHTML = `
|
|
249
265
|
<div class="diff-file-card">
|
|
250
266
|
<div class="diff-file-card-head">
|
|
251
267
|
<span class="diff-card-title">${escapeHtml(file.newPath || file.oldPath)}</span>
|
|
@@ -257,27 +273,24 @@ function renderCurrentFileDiff(container) {
|
|
|
257
273
|
</div>
|
|
258
274
|
`;
|
|
259
275
|
}
|
|
260
|
-
|
|
261
276
|
function renderUnifiedHunk(hunk) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return `
|
|
277
|
+
let oldLine = hunk.oldStart;
|
|
278
|
+
let newLine = hunk.newStart;
|
|
279
|
+
const linesHtml = hunk.lines.map((line) => {
|
|
280
|
+
let oldNum = '';
|
|
281
|
+
let newNum = '';
|
|
282
|
+
if (line.type === 'del') {
|
|
283
|
+
oldNum = oldLine++;
|
|
284
|
+
}
|
|
285
|
+
else if (line.type === 'add') {
|
|
286
|
+
newNum = newLine++;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
oldNum = oldLine++;
|
|
290
|
+
newNum = newLine++;
|
|
291
|
+
}
|
|
292
|
+
const sign = line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ');
|
|
293
|
+
return `
|
|
281
294
|
<div class="diff-line line-${line.type}">
|
|
282
295
|
<span class="diff-ln old-ln">${oldNum}</span>
|
|
283
296
|
<span class="diff-ln new-ln">${newNum}</span>
|
|
@@ -285,68 +298,68 @@ function renderUnifiedHunk(hunk) {
|
|
|
285
298
|
<span class="diff-code">${escapeHtml(line.text)}</span>
|
|
286
299
|
</div>
|
|
287
300
|
`;
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return `
|
|
301
|
+
}).join('');
|
|
302
|
+
return `
|
|
291
303
|
<div class="diff-hunk">
|
|
292
304
|
<div class="diff-hunk-header">${escapeHtml(hunk.header)}</div>
|
|
293
305
|
${linesHtml}
|
|
294
306
|
</div>
|
|
295
307
|
`;
|
|
296
308
|
}
|
|
297
|
-
|
|
298
309
|
function renderSplitHunk(hunk) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
rows.push(`
|
|
310
|
+
let oldLine = hunk.oldStart;
|
|
311
|
+
let newLine = hunk.newStart;
|
|
312
|
+
const rows = [];
|
|
313
|
+
for (let i = 0; i < hunk.lines.length; i++) {
|
|
314
|
+
const line = hunk.lines[i];
|
|
315
|
+
if (line.type === 'del') {
|
|
316
|
+
const delNum = oldLine++;
|
|
317
|
+
// Check if next is add
|
|
318
|
+
const next = hunk.lines[i + 1];
|
|
319
|
+
if (next && next.type === 'add') {
|
|
320
|
+
const addNum = newLine++;
|
|
321
|
+
rows.push(`
|
|
312
322
|
<div class="split-diff-row">
|
|
313
323
|
<div class="split-pane line-del"><span class="diff-ln">${delNum}</span><span class="diff-code">-${escapeHtml(line.text)}</span></div>
|
|
314
324
|
<div class="split-pane line-add"><span class="diff-ln">${addNum}</span><span class="diff-code">+${escapeHtml(next.text)}</span></div>
|
|
315
325
|
</div>
|
|
316
326
|
`);
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
327
|
+
i++;
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
rows.push(`
|
|
320
331
|
<div class="split-diff-row">
|
|
321
332
|
<div class="split-pane line-del"><span class="diff-ln">${delNum}</span><span class="diff-code">-${escapeHtml(line.text)}</span></div>
|
|
322
333
|
<div class="split-pane line-empty"></div>
|
|
323
334
|
</div>
|
|
324
335
|
`);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else if (line.type === 'add') {
|
|
339
|
+
const addNum = newLine++;
|
|
340
|
+
rows.push(`
|
|
329
341
|
<div class="split-diff-row">
|
|
330
342
|
<div class="split-pane line-empty"></div>
|
|
331
343
|
<div class="split-pane line-add"><span class="diff-ln">${addNum}</span><span class="diff-code">+${escapeHtml(line.text)}</span></div>
|
|
332
344
|
</div>
|
|
333
345
|
`);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
const oNum = oldLine++;
|
|
349
|
+
const nNum = newLine++;
|
|
350
|
+
rows.push(`
|
|
338
351
|
<div class="split-diff-row">
|
|
339
352
|
<div class="split-pane line-context"><span class="diff-ln">${oNum}</span><span class="diff-code"> ${escapeHtml(line.text)}</span></div>
|
|
340
353
|
<div class="split-pane line-context"><span class="diff-ln">${nNum}</span><span class="diff-code"> ${escapeHtml(line.text)}</span></div>
|
|
341
354
|
</div>
|
|
342
355
|
`);
|
|
356
|
+
}
|
|
343
357
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
return `
|
|
358
|
+
return `
|
|
347
359
|
<div class="diff-hunk">
|
|
348
360
|
<div class="diff-hunk-header">${escapeHtml(hunk.header)}</div>
|
|
349
361
|
<div class="split-diff-table">${rows.join('')}</div>
|
|
350
362
|
</div>
|
|
351
363
|
`;
|
|
352
364
|
}
|
|
365
|
+
//# sourceMappingURL=diffView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diffView.js","sourceRoot":"","sources":["../../src/public/js/diffView.ts"],"names":[],"mappings":"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","sourcesContent":["// PR / Branch Diff & Blast Radius View.\n// Renders side-by-side / unified git diffs and calculates downstream architectural blast radius.\n\nimport { escapeHtml } from './html.js';\n\nlet activeScanId = null;\nlet currentDiff = null;\nlet currentRefs = null;\nlet selectedFile = null;\nlet generation = 0;\nlet diffRequest: AbortController | null = null;\nlet refsRequest: AbortController | null = null;\nexport function stopDiff() { generation++; diffRequest?.abort(); refsRequest?.abort(); }\nlet diffMode = 'unified'; // 'unified' | 'split'\n\nexport async function renderDiffView(container, { scan, facts, scanId }) {\n stopDiff();\n const viewGeneration = generation;\n currentDiff = null;\n if (!container) return;\n activeScanId = scanId;\n\n container.innerHTML = `\n <div class=\"diff-layout\">\n <header class=\"diff-header\">\n <div class=\"diff-ref-selectors\">\n <div class=\"diff-select-group\">\n <span class=\"diff-select-label\">Base:</span>\n <select class=\"diff-select\" id=\"diffBaseSelect\">\n <option value=\"HEAD\">HEAD (committed)</option>\n <option value=\"HEAD~1\">HEAD~1 (previous commit)</option>\n <option value=\"main\">main</option>\n </select>\n </div>\n <span class=\"diff-arrow\">←</span>\n <div class=\"diff-select-group\">\n <span class=\"diff-select-label\">Compare (Head):</span>\n <select class=\"diff-select\" id=\"diffHeadSelect\">\n <option value=\"\">Working Tree (Uncommitted)</option>\n <option value=\"HEAD\">HEAD</option>\n </select>\n </div>\n <button class=\"btn btn-ink btn-sm\" id=\"diffRunBtn\">Compare</button>\n </div>\n <div class=\"diff-view-modes\">\n <button class=\"btn btn-ghost btn-sm is-active\" id=\"diffModeUnified\">Unified</button>\n <button class=\"btn btn-ghost btn-sm\" id=\"diffModeSplit\">Split</button>\n </div>\n </header>\n\n <div class=\"diff-body\">\n <!-- Sidebar: Changed Files & Blast Radius -->\n <aside class=\"diff-sidebar\">\n <div class=\"diff-sidebar-head\">\n <h4>Changed Files</h4>\n <span class=\"diff-stat-chip\" id=\"diffSummaryChip\">Loading…</span>\n </div>\n\n <!-- Blast Radius Impact Overview -->\n <div class=\"blast-radius-box\" id=\"blastRadiusBox\">\n <div class=\"br-title\">Downstream Blast Radius</div>\n <div class=\"br-metric\" id=\"blastMetric\">—</div>\n <div class=\"br-desc\" id=\"blastDesc\">Analyzing impacted dependent modules…</div>\n </div>\n\n <div class=\"diff-files-list\" id=\"diffFilesList\">\n <p class=\"diff-empty\">Fetching diff…</p>\n </div>\n </aside>\n\n <!-- Main: Diff Viewer -->\n <main class=\"diff-content\" id=\"diffViewerMain\">\n <div class=\"diff-placeholder\">Select a file to inspect diff hunks.</div>\n </main>\n </div>\n </div>\n `;\n\n // Wire controls\n const baseSelect = container.querySelector('#diffBaseSelect');\n const headSelect = container.querySelector('#diffHeadSelect');\n const runBtn = container.querySelector('#diffRunBtn');\n const unifiedBtn = container.querySelector('#diffModeUnified');\n const splitBtn = container.querySelector('#diffModeSplit');\n\n unifiedBtn?.addEventListener('click', () => {\n diffMode = 'unified';\n unifiedBtn.classList.add('is-active');\n splitBtn?.classList.remove('is-active');\n renderCurrentFileDiff(container);\n });\n\n splitBtn?.addEventListener('click', () => {\n diffMode = 'split';\n splitBtn.classList.add('is-active');\n unifiedBtn?.classList.remove('is-active');\n renderCurrentFileDiff(container);\n });\n\n runBtn?.addEventListener('click', () => {\n loadDiffData(container, scan, facts, baseSelect.value, headSelect.value);\n });\n\n // Initial load\n await loadRefs(container, scanId, viewGeneration);\n if (viewGeneration !== generation) return;\n await loadDiffData(container, scan, facts, 'HEAD', '');\n}\n\nasync function loadRefs(container, scanId, viewGeneration) {\n if (!scanId) return;\n try {\n const controller = new AbortController(); refsRequest = controller;\n const res = await fetch('/api/diff/refs?scan=' + encodeURIComponent(scanId), { signal: controller.signal });\n if (res.ok) {\n const data = await res.json();\n if (viewGeneration !== generation) return;\n currentRefs = data;\n const baseSelect = container.querySelector('#diffBaseSelect');\n const headSelect = container.querySelector('#diffHeadSelect');\n if (baseSelect && currentRefs.branches?.length) {\n baseSelect.innerHTML = `\n <option value=\"HEAD\">HEAD</option>\n <option value=\"HEAD~1\">HEAD~1 (previous commit)</option>\n ${currentRefs.branches.map((b) => `<option value=\"${escapeHtml(b)}\">${escapeHtml(b)}</option>`).join('')}\n ${(currentRefs.tags || []).map((t) => `<option value=\"${escapeHtml(t)}\">tag: ${escapeHtml(t)}</option>`).join('')}\n `;\n }\n }\n } catch (e) {\n /* fallback to defaults */\n }\n}\n\nasync function loadDiffData(container, scan, facts, base, head) {\n diffRequest?.abort();\n const controller = new AbortController(); diffRequest = controller;\n const viewGeneration = generation;\n currentDiff = null; selectedFile = null;\n const mainEl = container.querySelector('#diffViewerMain');\n if (mainEl) mainEl.innerHTML = '<div class=\"diff-placeholder\">Reading selected comparison…</div>';\n const metric = container.querySelector('#blastMetric'); if (metric) metric.textContent = '—';\n const listEl = container.querySelector('#diffFilesList');\n const summaryEl = container.querySelector('#diffSummaryChip');\n if (listEl) listEl.innerHTML = '<p class=\"diff-empty\">Loading diff…</p>';\n\n try {\n const params = new URLSearchParams();\n if (activeScanId) params.set('scan', activeScanId);\n if (base) params.set('base', base);\n if (head) params.set('head', head);\n\n const res = await fetch('/api/diff?' + params.toString(), { signal: controller.signal });\n const data = await res.json();\n if (controller.signal.aborted || viewGeneration !== generation) return;\n if (!res.ok) throw new Error(data.error || 'Comparison unavailable.');\n currentDiff = data;\n const files = currentDiff.files || [];\n\n if (summaryEl) {\n summaryEl.innerHTML = `<b>${files.length}</b> files <span class=\"diff-add\">+${currentDiff.stats?.additions || 0}</span> <span class=\"diff-del\">-${currentDiff.stats?.deletions || 0}</span>`;\n }\n\n // Compute blast radius from modified files\n computeAndRenderBlastRadius(container, files, scan, facts);\n\n if (!files.length) {\n if (listEl) listEl.innerHTML = '<p class=\"diff-empty\">No differences between selected references.</p>';\n const mainEl = container.querySelector('#diffViewerMain');\n if (mainEl) mainEl.innerHTML = '<div class=\"diff-placeholder\">No changes in this comparison. Untracked files are available in Review.</div>';\n return;\n }\n\n selectedFile = files[0].newPath || files[0].oldPath;\n renderFilesList(container, files);\n renderCurrentFileDiff(container);\n } catch (err) {\n if (controller.signal.aborted || viewGeneration !== generation) return;\n if (summaryEl) summaryEl.textContent = 'Unavailable';\n if (mainEl) mainEl.innerHTML = '<div class=\"diff-placeholder\">Choose valid references and compare again.</div>';\n if (listEl) listEl.innerHTML = `<p class=\"diff-empty\">Failed to load diff: ${escapeHtml(err.message)}</p>`;\n }\n}\n\nfunction computeAndRenderBlastRadius(container, diffFiles, scan, facts) {\n const metricEl = container.querySelector('#blastMetric');\n const descEl = container.querySelector('#blastDesc');\n if (!metricEl || !descEl) return;\n\n if (!diffFiles.length) {\n metricEl.textContent = '0 files';\n descEl.textContent = 'No modified files to analyze.';\n return;\n }\n\n const modifiedPaths = new Set<string>(diffFiles.map((f) => f.newPath || f.oldPath));\n const impacted = new Set();\n const queue = [...modifiedPaths];\n\n while (queue.length) {\n const curr = queue.shift();\n const dependents = facts?.importers?.[curr] || [];\n for (const dep of dependents) {\n if (!impacted.has(dep) && !modifiedPaths.has(dep)) {\n impacted.add(dep);\n queue.push(dep);\n }\n }\n }\n\n const totalFiles = scan?.files?.length || 1;\n const impactCount = impacted.size;\n const pct = Math.round((impactCount / totalFiles) * 100);\n\n metricEl.innerHTML = `<b>${impactCount}</b> dependent files affected (${pct}%)`;\n descEl.innerHTML = impactCount > 0\n ? `Directly impacts <b>${impactCount}</b> downstream module${impactCount === 1 ? '' : 's'} across the import graph.`\n : `Self-contained change: no downstream imports affected.`;\n}\n\nfunction renderFilesList(container, files) {\n const listEl = container.querySelector('#diffFilesList');\n if (!listEl) return;\n\n listEl.innerHTML = files.map((f) => {\n const path = f.newPath || f.oldPath;\n const isSelected = path === selectedFile;\n return `\n <div class=\"diff-file-item ${isSelected ? 'is-selected' : ''}\" data-path=\"${escapeHtml(path)}\">\n <span class=\"diff-file-badge status-${f.status}\">${f.status.slice(0, 1).toUpperCase()}</span>\n <span class=\"diff-file-path\" title=\"${escapeHtml(path)}\">${escapeHtml(path)}</span>\n <span class=\"diff-file-counts\">\n <span class=\"diff-add\">+${f.additions}</span>\n <span class=\"diff-del\">-${f.deletions}</span>\n </span>\n </div>\n `;\n }).join('');\n\n listEl.querySelectorAll('.diff-file-item').forEach((item) => {\n item.addEventListener('click', () => {\n selectedFile = item.dataset.path;\n listEl.querySelectorAll('.diff-file-item').forEach((el) => el.classList.remove('is-selected'));\n item.classList.add('is-selected');\n renderCurrentFileDiff(container);\n });\n });\n}\n\nfunction renderCurrentFileDiff(container) {\n const mainEl = container.querySelector('#diffViewerMain');\n if (!mainEl || !currentDiff) return;\n\n const file = currentDiff.files?.find((f) => (f.newPath || f.oldPath) === selectedFile);\n if (!file) {\n mainEl.innerHTML = '<div class=\"diff-placeholder\">Select a file to inspect diff.</div>';\n return;\n }\n\n const hunksHtml = file.hunks.map((hunk) => {\n if (diffMode === 'split') {\n return renderSplitHunk(hunk);\n }\n return renderUnifiedHunk(hunk);\n }).join('');\n\n mainEl.innerHTML = `\n <div class=\"diff-file-card\">\n <div class=\"diff-file-card-head\">\n <span class=\"diff-card-title\">${escapeHtml(file.newPath || file.oldPath)}</span>\n <span class=\"diff-card-meta\">${file.hunks.length} hunk${file.hunks.length === 1 ? '' : 's'} · +${file.additions} -${file.deletions}</span>\n </div>\n <div class=\"diff-hunks-body\">\n ${hunksHtml || '<div class=\"diff-empty-hunk\">Binary file or metadata change</div>'}\n </div>\n </div>\n `;\n}\n\nfunction renderUnifiedHunk(hunk) {\n let oldLine = hunk.oldStart;\n let newLine = hunk.newStart;\n\n const linesHtml = hunk.lines.map((line) => {\n let oldNum: string | number = '';\n let newNum: string | number = '';\n\n if (line.type === 'del') {\n oldNum = oldLine++;\n } else if (line.type === 'add') {\n newNum = newLine++;\n } else {\n oldNum = oldLine++;\n newNum = newLine++;\n }\n\n const sign = line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ');\n\n return `\n <div class=\"diff-line line-${line.type}\">\n <span class=\"diff-ln old-ln\">${oldNum}</span>\n <span class=\"diff-ln new-ln\">${newNum}</span>\n <span class=\"diff-sign\">${sign}</span>\n <span class=\"diff-code\">${escapeHtml(line.text)}</span>\n </div>\n `;\n }).join('');\n\n return `\n <div class=\"diff-hunk\">\n <div class=\"diff-hunk-header\">${escapeHtml(hunk.header)}</div>\n ${linesHtml}\n </div>\n `;\n}\n\nfunction renderSplitHunk(hunk) {\n let oldLine = hunk.oldStart;\n let newLine = hunk.newStart;\n\n const rows = [];\n for (let i = 0; i < hunk.lines.length; i++) {\n const line = hunk.lines[i];\n if (line.type === 'del') {\n const delNum = oldLine++;\n // Check if next is add\n const next = hunk.lines[i + 1];\n if (next && next.type === 'add') {\n const addNum = newLine++;\n rows.push(`\n <div class=\"split-diff-row\">\n <div class=\"split-pane line-del\"><span class=\"diff-ln\">${delNum}</span><span class=\"diff-code\">-${escapeHtml(line.text)}</span></div>\n <div class=\"split-pane line-add\"><span class=\"diff-ln\">${addNum}</span><span class=\"diff-code\">+${escapeHtml(next.text)}</span></div>\n </div>\n `);\n i++;\n } else {\n rows.push(`\n <div class=\"split-diff-row\">\n <div class=\"split-pane line-del\"><span class=\"diff-ln\">${delNum}</span><span class=\"diff-code\">-${escapeHtml(line.text)}</span></div>\n <div class=\"split-pane line-empty\"></div>\n </div>\n `);\n }\n } else if (line.type === 'add') {\n const addNum = newLine++;\n rows.push(`\n <div class=\"split-diff-row\">\n <div class=\"split-pane line-empty\"></div>\n <div class=\"split-pane line-add\"><span class=\"diff-ln\">${addNum}</span><span class=\"diff-code\">+${escapeHtml(line.text)}</span></div>\n </div>\n `);\n } else {\n const oNum = oldLine++;\n const nNum = newLine++;\n rows.push(`\n <div class=\"split-diff-row\">\n <div class=\"split-pane line-context\"><span class=\"diff-ln\">${oNum}</span><span class=\"diff-code\"> ${escapeHtml(line.text)}</span></div>\n <div class=\"split-pane line-context\"><span class=\"diff-ln\">${nNum}</span><span class=\"diff-code\"> ${escapeHtml(line.text)}</span></div>\n </div>\n `);\n }\n }\n\n return `\n <div class=\"diff-hunk\">\n <div class=\"diff-hunk-header\">${escapeHtml(hunk.header)}</div>\n <div class=\"split-diff-table\">${rows.join('')}</div>\n </div>\n `;\n}\n"]}
|