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.
Files changed (448) hide show
  1. package/README.md +174 -46
  2. package/bin/build-if-source.js +23 -0
  3. package/cli/commands.d.ts +26 -0
  4. package/cli/commands.js +836 -811
  5. package/cli/commands.js.map +1 -0
  6. package/cli/explorer/advanced.d.ts +14 -0
  7. package/cli/explorer/advanced.js +321 -329
  8. package/cli/explorer/advanced.js.map +1 -0
  9. package/cli/explorer/app.d.ts +1 -0
  10. package/cli/explorer/app.js +384 -313
  11. package/cli/explorer/app.js.map +1 -0
  12. package/cli/explorer/assistant.d.ts +9 -0
  13. package/cli/explorer/assistant.js +58 -0
  14. package/cli/explorer/assistant.js.map +1 -0
  15. package/cli/explorer/commands.d.ts +17 -0
  16. package/cli/explorer/commands.js +368 -302
  17. package/cli/explorer/commands.js.map +1 -0
  18. package/cli/explorer/featureViews.d.ts +124 -0
  19. package/cli/explorer/featureViews.js +179 -0
  20. package/cli/explorer/featureViews.js.map +1 -0
  21. package/cli/explorer/github.d.ts +21 -0
  22. package/cli/explorer/github.js +42 -44
  23. package/cli/explorer/github.js.map +1 -0
  24. package/cli/explorer/graphs.d.ts +7 -0
  25. package/cli/explorer/graphs.js +90 -95
  26. package/cli/explorer/graphs.js.map +1 -0
  27. package/cli/explorer/picker.d.ts +22 -0
  28. package/cli/explorer/picker.js +150 -0
  29. package/cli/explorer/picker.js.map +1 -0
  30. package/cli/explorer/report.d.ts +85 -0
  31. package/cli/explorer/report.js +60 -0
  32. package/cli/explorer/report.js.map +1 -0
  33. package/cli/explorer/session.d.ts +449 -0
  34. package/cli/explorer/session.js +143 -144
  35. package/cli/explorer/session.js.map +1 -0
  36. package/cli/explorer/views.d.ts +19 -0
  37. package/cli/explorer/views.js +465 -482
  38. package/cli/explorer/views.js.map +1 -0
  39. package/cli/explorer/wrap.d.ts +1 -0
  40. package/cli/explorer/wrap.js +28 -44
  41. package/cli/explorer/wrap.js.map +1 -0
  42. package/cli/main.d.ts +1 -0
  43. package/cli/main.js +214 -161
  44. package/cli/main.js.map +1 -0
  45. package/cli/prompt.d.ts +5 -0
  46. package/cli/prompt.js +84 -80
  47. package/cli/prompt.js.map +1 -0
  48. package/cli/scanOptions.d.ts +2 -0
  49. package/cli/scanOptions.js +11 -0
  50. package/cli/scanOptions.js.map +1 -0
  51. package/cli/ui.d.ts +33 -0
  52. package/cli/ui.js +35 -44
  53. package/cli/ui.js.map +1 -0
  54. package/cli/wizard.d.ts +187 -0
  55. package/cli/wizard.js +304 -284
  56. package/cli/wizard.js.map +1 -0
  57. package/package.json +15 -4
  58. package/public/app.d.ts +1 -0
  59. package/public/app.js +1057 -1009
  60. package/public/app.js.map +1 -0
  61. package/public/browser.d.ts +68 -0
  62. package/public/browser.js +2 -0
  63. package/public/browser.js.map +1 -0
  64. package/public/index.html +30 -13
  65. package/public/js/about.d.ts +6 -0
  66. package/public/js/about.js +152 -172
  67. package/public/js/about.js.map +1 -0
  68. package/public/js/aiDraft.d.ts +3 -0
  69. package/public/js/aiDraft.js +116 -124
  70. package/public/js/aiDraft.js.map +1 -0
  71. package/public/js/analysisPanel.d.ts +7 -0
  72. package/public/js/analysisPanel.js +166 -166
  73. package/public/js/analysisPanel.js.map +1 -0
  74. package/public/js/analysisReport.d.ts +38 -0
  75. package/public/js/analysisReport.js +105 -110
  76. package/public/js/analysisReport.js.map +1 -0
  77. package/public/js/api.d.ts +26 -0
  78. package/public/js/api.js +176 -167
  79. package/public/js/api.js.map +1 -0
  80. package/public/js/atlas.d.ts +6 -0
  81. package/public/js/atlas.js +50 -58
  82. package/public/js/atlas.js.map +1 -0
  83. package/public/js/blameView.d.ts +2 -0
  84. package/public/js/blameView.js +36 -37
  85. package/public/js/blameView.js.map +1 -0
  86. package/public/js/codeTab.d.ts +3 -0
  87. package/public/js/codeTab.js +175 -180
  88. package/public/js/codeTab.js.map +1 -0
  89. package/public/js/codeViewer.d.ts +5 -0
  90. package/public/js/codeViewer.js +162 -150
  91. package/public/js/codeViewer.js.map +1 -0
  92. package/public/js/components/FileChip.d.ts +9 -0
  93. package/public/js/components/FileChip.js +45 -49
  94. package/public/js/components/FileChip.js.map +1 -0
  95. package/public/js/components/MetricSparkline.d.ts +7 -0
  96. package/public/js/components/MetricSparkline.js +35 -44
  97. package/public/js/components/MetricSparkline.js.map +1 -0
  98. package/public/js/components/RiskBadge.d.ts +9 -0
  99. package/public/js/components/RiskBadge.js +55 -63
  100. package/public/js/components/RiskBadge.js.map +1 -0
  101. package/public/js/deepAnalysisView.d.ts +99 -0
  102. package/public/js/deepAnalysisView.js +282 -302
  103. package/public/js/deepAnalysisView.js.map +1 -0
  104. package/public/js/diagramPane.d.ts +8 -0
  105. package/public/js/diagramPane.js +133 -135
  106. package/public/js/diagramPane.js.map +1 -0
  107. package/public/js/diffView.d.ts +6 -0
  108. package/public/js/diffView.js +222 -209
  109. package/public/js/diffView.js.map +1 -0
  110. package/public/js/docsView.d.ts +2 -0
  111. package/public/js/docsView.js +441 -442
  112. package/public/js/docsView.js.map +1 -0
  113. package/public/js/dom.d.ts +189 -0
  114. package/public/js/dom.js +5 -0
  115. package/public/js/dom.js.map +1 -0
  116. package/public/js/fileSourceBrowser.d.ts +5 -0
  117. package/public/js/fileSourceBrowser.js +33 -41
  118. package/public/js/fileSourceBrowser.js.map +1 -0
  119. package/public/js/flameGraph.d.ts +5 -0
  120. package/public/js/flameGraph.js +62 -72
  121. package/public/js/flameGraph.js.map +1 -0
  122. package/public/js/forceGraph.d.ts +10 -0
  123. package/public/js/forceGraph.js +581 -556
  124. package/public/js/forceGraph.js.map +1 -0
  125. package/public/js/heatmap.d.ts +4 -0
  126. package/public/js/heatmap.js +142 -164
  127. package/public/js/heatmap.js.map +1 -0
  128. package/public/js/highlight.d.ts +2 -0
  129. package/public/js/highlight.js +116 -107
  130. package/public/js/highlight.js.map +1 -0
  131. package/public/js/html.d.ts +2 -0
  132. package/public/js/html.js +14 -44
  133. package/public/js/html.js.map +1 -0
  134. package/public/js/insightsView.d.ts +5 -0
  135. package/public/js/insightsView.js +93 -111
  136. package/public/js/insightsView.js.map +1 -0
  137. package/public/js/inspector.d.ts +37 -0
  138. package/public/js/inspector.js +416 -445
  139. package/public/js/inspector.js.map +1 -0
  140. package/public/js/llm.d.ts +113 -0
  141. package/public/js/llm.js +233 -272
  142. package/public/js/llm.js.map +1 -0
  143. package/public/js/mapView.d.ts +3 -0
  144. package/public/js/mapView.js +121 -120
  145. package/public/js/mapView.js.map +1 -0
  146. package/public/js/markdown.d.ts +3 -0
  147. package/public/js/markdown.js +84 -91
  148. package/public/js/markdown.js.map +1 -0
  149. package/public/js/mindmap.d.ts +32 -0
  150. package/public/js/mindmap.js +244 -259
  151. package/public/js/mindmap.js.map +1 -0
  152. package/public/js/mobilePanels.d.ts +5 -0
  153. package/public/js/mobilePanels.js +50 -0
  154. package/public/js/mobilePanels.js.map +1 -0
  155. package/public/js/repoFiles.d.ts +12 -0
  156. package/public/js/repoFiles.js +28 -27
  157. package/public/js/repoFiles.js.map +1 -0
  158. package/public/js/reviewView.d.ts +28 -0
  159. package/public/js/reviewView.js +256 -0
  160. package/public/js/reviewView.js.map +1 -0
  161. package/public/js/sbomView.d.ts +3 -0
  162. package/public/js/sbomView.js +59 -67
  163. package/public/js/sbomView.js.map +1 -0
  164. package/public/js/scanCache.d.ts +16 -0
  165. package/public/js/scanCache.js +67 -65
  166. package/public/js/scanCache.js.map +1 -0
  167. package/public/js/search.d.ts +7 -0
  168. package/public/js/search.js +868 -800
  169. package/public/js/search.js.map +1 -0
  170. package/public/js/searchLocal.d.ts +42 -0
  171. package/public/js/searchLocal.js +87 -0
  172. package/public/js/searchLocal.js.map +1 -0
  173. package/public/js/serverSettings.d.ts +5 -0
  174. package/public/js/serverSettings.js +153 -164
  175. package/public/js/serverSettings.js.map +1 -0
  176. package/public/js/state.d.ts +798 -0
  177. package/public/js/state.js +114 -122
  178. package/public/js/state.js.map +1 -0
  179. package/public/js/tour.d.ts +6 -0
  180. package/public/js/tour.js +5 -7
  181. package/public/js/tour.js.map +1 -0
  182. package/public/js/transitions.d.ts +2 -0
  183. package/public/js/transitions.js +9 -10
  184. package/public/js/transitions.js.map +1 -0
  185. package/public/js/tree.d.ts +12 -0
  186. package/public/js/tree.js +157 -154
  187. package/public/js/tree.js.map +1 -0
  188. package/public/js/workflowsView.d.ts +3 -0
  189. package/public/js/workflowsView.js +15 -22
  190. package/public/js/workflowsView.js.map +1 -0
  191. package/public/search.css +495 -0
  192. package/public/styles.css +163 -346
  193. package/public/workbench.css +166 -0
  194. package/server/apiAuth.d.ts +3 -0
  195. package/server/apiAuth.js +18 -20
  196. package/server/apiAuth.js.map +1 -0
  197. package/server/apiDiff.d.ts +2 -0
  198. package/server/apiDiff.js +20 -18
  199. package/server/apiDiff.js.map +1 -0
  200. package/server/apiDocs.d.ts +2 -0
  201. package/server/apiDocs.js +38 -40
  202. package/server/apiDocs.js.map +1 -0
  203. package/server/apiFile.d.ts +1 -0
  204. package/server/apiFile.js +14 -17
  205. package/server/apiFile.js.map +1 -0
  206. package/server/apiGitBlame.d.ts +2 -0
  207. package/server/apiGitBlame.js +50 -51
  208. package/server/apiGitBlame.js.map +1 -0
  209. package/server/apiMcp.d.ts +4 -0
  210. package/server/apiMcp.js +64 -66
  211. package/server/apiMcp.js.map +1 -0
  212. package/server/apiReview.d.ts +2 -0
  213. package/server/apiReview.js +45 -0
  214. package/server/apiReview.js.map +1 -0
  215. package/server/apiScan.d.ts +5 -0
  216. package/server/apiScan.js +90 -71
  217. package/server/apiScan.js.map +1 -0
  218. package/server/apiSearch.d.ts +33 -0
  219. package/server/apiSearch.js +184 -200
  220. package/server/apiSearch.js.map +1 -0
  221. package/server/apiSettings.d.ts +4 -0
  222. package/server/apiSettings.js +84 -85
  223. package/server/apiSettings.js.map +1 -0
  224. package/server/apiTools.d.ts +3 -0
  225. package/server/apiTools.js +55 -56
  226. package/server/apiTools.js.map +1 -0
  227. package/server/auth.d.ts +5 -0
  228. package/server/auth.js +54 -51
  229. package/server/auth.js.map +1 -0
  230. package/server/config.d.ts +42 -0
  231. package/server/config.js +189 -194
  232. package/server/config.js.map +1 -0
  233. package/server/daemon.d.ts +12 -0
  234. package/server/daemon.js +124 -114
  235. package/server/daemon.js.map +1 -0
  236. package/server/fileSourceNode.d.ts +2 -0
  237. package/server/fileSourceNode.js +34 -36
  238. package/server/fileSourceNode.js.map +1 -0
  239. package/server/gitClone.d.ts +9 -0
  240. package/server/gitClone.js +59 -61
  241. package/server/gitClone.js.map +1 -0
  242. package/server/gitDiff.d.ts +36 -0
  243. package/server/gitDiff.js +271 -169
  244. package/server/gitDiff.js.map +1 -0
  245. package/server/gitHistory.d.ts +12 -0
  246. package/server/gitHistory.js +80 -80
  247. package/server/gitHistory.js.map +1 -0
  248. package/server/gitRemote.d.ts +12 -0
  249. package/server/gitRemote.js +37 -38
  250. package/server/gitRemote.js.map +1 -0
  251. package/server/htmlText.d.ts +4 -0
  252. package/server/htmlText.js +16 -23
  253. package/server/htmlText.js.map +1 -0
  254. package/server/http.d.ts +5 -0
  255. package/server/http.js +27 -30
  256. package/server/http.js.map +1 -0
  257. package/server/httpGuards.d.ts +2 -0
  258. package/server/httpGuards.js +47 -43
  259. package/server/httpGuards.js.map +1 -0
  260. package/server/https.d.ts +34 -0
  261. package/server/https.js +121 -111
  262. package/server/https.js.map +1 -0
  263. package/server/index.d.ts +40 -0
  264. package/server/index.js +139 -147
  265. package/server/index.js.map +1 -0
  266. package/server/layout.d.ts +26 -0
  267. package/server/layout.js +59 -64
  268. package/server/layout.js.map +1 -0
  269. package/server/llmProxy.d.ts +3 -0
  270. package/server/llmProxy.js +137 -135
  271. package/server/llmProxy.js.map +1 -0
  272. package/server/logger.d.ts +38 -0
  273. package/server/logger.js +198 -211
  274. package/server/logger.js.map +1 -0
  275. package/server/mcp/analysis.d.ts +29 -0
  276. package/server/mcp/analysis.js +99 -120
  277. package/server/mcp/analysis.js.map +1 -0
  278. package/server/mcp/http.d.ts +9 -0
  279. package/server/mcp/http.js +128 -137
  280. package/server/mcp/http.js.map +1 -0
  281. package/server/mcp/runner.d.ts +68 -0
  282. package/server/mcp/runner.js +208 -218
  283. package/server/mcp/runner.js.map +1 -0
  284. package/server/mcp/server.d.ts +29 -0
  285. package/server/mcp/server.js +166 -179
  286. package/server/mcp/server.js.map +1 -0
  287. package/server/mcp/standalone.d.ts +1 -0
  288. package/server/mcp/standalone.js +8 -10
  289. package/server/mcp/standalone.js.map +1 -0
  290. package/server/mcp/tools.d.ts +2020 -0
  291. package/server/mcp/tools.js +523 -574
  292. package/server/mcp/tools.js.map +1 -0
  293. package/server/paths.d.ts +3 -0
  294. package/server/paths.js +17 -15
  295. package/server/paths.js.map +1 -0
  296. package/server/pidfile.d.ts +9 -0
  297. package/server/pidfile.js +43 -45
  298. package/server/pidfile.js.map +1 -0
  299. package/server/review.d.ts +5 -0
  300. package/server/review.js +35 -0
  301. package/server/review.js.map +1 -0
  302. package/server/router.d.ts +33 -0
  303. package/server/router.js +191 -197
  304. package/server/router.js.map +1 -0
  305. package/server/scanRequest.d.ts +3 -0
  306. package/server/scanRequest.js +36 -0
  307. package/server/scanRequest.js.map +1 -0
  308. package/server/searchIndex.d.ts +17 -0
  309. package/server/searchIndex.js +83 -80
  310. package/server/searchIndex.js.map +1 -0
  311. package/server/sessions.d.ts +14 -0
  312. package/server/sessions.js +62 -70
  313. package/server/sessions.js.map +1 -0
  314. package/server/startup.d.ts +118 -0
  315. package/server/startup.js +143 -148
  316. package/server/startup.js.map +1 -0
  317. package/server/static.d.ts +6 -0
  318. package/server/static.js +28 -31
  319. package/server/static.js.map +1 -0
  320. package/server/tools/install.d.ts +4 -0
  321. package/server/tools/install.js +176 -185
  322. package/server/tools/install.js.map +1 -0
  323. package/server/tools/parse.d.ts +8 -0
  324. package/server/tools/parse.js +112 -114
  325. package/server/tools/parse.js.map +1 -0
  326. package/server/tools/platform.d.ts +9 -0
  327. package/server/tools/platform.js +49 -47
  328. package/server/tools/platform.js.map +1 -0
  329. package/server/tools/registry.d.ts +137 -0
  330. package/server/tools/registry.js +173 -172
  331. package/server/tools/registry.js.map +1 -0
  332. package/server/tools/scan.d.ts +34 -0
  333. package/server/tools/scan.js +89 -98
  334. package/server/tools/scan.js.map +1 -0
  335. package/server/tools.d.ts +34 -0
  336. package/server/tools.js +94 -107
  337. package/server/tools.js.map +1 -0
  338. package/server/tunnel.d.ts +29 -0
  339. package/server/tunnel.js +61 -66
  340. package/server/tunnel.js.map +1 -0
  341. package/shared/analyzer/docs.d.ts +16 -0
  342. package/shared/analyzer/docs.js +99 -93
  343. package/shared/analyzer/docs.js.map +1 -0
  344. package/shared/analyzer/explainLocal.d.ts +4 -0
  345. package/shared/analyzer/explainLocal.js +122 -142
  346. package/shared/analyzer/explainLocal.js.map +1 -0
  347. package/shared/analyzer/github.d.ts +37 -0
  348. package/shared/analyzer/github.js +41 -43
  349. package/shared/analyzer/github.js.map +1 -0
  350. package/shared/analyzer/graph.d.ts +41 -0
  351. package/shared/analyzer/graph.js +393 -400
  352. package/shared/analyzer/graph.js.map +1 -0
  353. package/shared/analyzer/health.d.ts +33 -0
  354. package/shared/analyzer/health.js +197 -193
  355. package/shared/analyzer/health.js.map +1 -0
  356. package/shared/analyzer/history.d.ts +29 -0
  357. package/shared/analyzer/history.js +110 -111
  358. package/shared/analyzer/history.js.map +1 -0
  359. package/shared/analyzer/jsonConfig.d.ts +2 -0
  360. package/shared/analyzer/jsonConfig.js +61 -0
  361. package/shared/analyzer/jsonConfig.js.map +1 -0
  362. package/shared/analyzer/languages/csharp.d.ts +26 -0
  363. package/shared/analyzer/languages/csharp.js +83 -88
  364. package/shared/analyzer/languages/csharp.js.map +1 -0
  365. package/shared/analyzer/languages/generic.d.ts +23 -0
  366. package/shared/analyzer/languages/generic.js +94 -105
  367. package/shared/analyzer/languages/generic.js.map +1 -0
  368. package/shared/analyzer/languages/go.d.ts +24 -0
  369. package/shared/analyzer/languages/go.js +45 -54
  370. package/shared/analyzer/languages/go.js.map +1 -0
  371. package/shared/analyzer/languages/index.d.ts +6 -0
  372. package/shared/analyzer/languages/index.js +19 -20
  373. package/shared/analyzer/languages/index.js.map +1 -0
  374. package/shared/analyzer/languages/java.d.ts +20 -0
  375. package/shared/analyzer/languages/java.js +73 -78
  376. package/shared/analyzer/languages/java.js.map +1 -0
  377. package/shared/analyzer/languages/javascript.d.ts +12 -0
  378. package/shared/analyzer/languages/javascript.js +258 -209
  379. package/shared/analyzer/languages/javascript.js.map +1 -0
  380. package/shared/analyzer/languages/python.d.ts +23 -0
  381. package/shared/analyzer/languages/python.js +92 -100
  382. package/shared/analyzer/languages/python.js.map +1 -0
  383. package/shared/analyzer/languages/rust.d.ts +17 -0
  384. package/shared/analyzer/languages/rust.js +96 -98
  385. package/shared/analyzer/languages/rust.js.map +1 -0
  386. package/shared/analyzer/languages/typescript.d.ts +11 -0
  387. package/shared/analyzer/languages/typescript.js +117 -116
  388. package/shared/analyzer/languages/typescript.js.map +1 -0
  389. package/shared/analyzer/licenses.d.ts +18 -0
  390. package/shared/analyzer/licenses.js +130 -133
  391. package/shared/analyzer/licenses.js.map +1 -0
  392. package/shared/analyzer/metrics.d.ts +20 -0
  393. package/shared/analyzer/metrics.js +112 -104
  394. package/shared/analyzer/metrics.js.map +1 -0
  395. package/shared/analyzer/pathUtil.d.ts +10 -0
  396. package/shared/analyzer/pathUtil.js +29 -35
  397. package/shared/analyzer/pathUtil.js.map +1 -0
  398. package/shared/analyzer/patterns.d.ts +11 -0
  399. package/shared/analyzer/patterns.js +231 -253
  400. package/shared/analyzer/patterns.js.map +1 -0
  401. package/shared/analyzer/scan.d.ts +62 -0
  402. package/shared/analyzer/scan.js +451 -442
  403. package/shared/analyzer/scan.js.map +1 -0
  404. package/shared/analyzer/scanControl.d.ts +11 -0
  405. package/shared/analyzer/scanControl.js +62 -0
  406. package/shared/analyzer/scanControl.js.map +1 -0
  407. package/shared/analyzer/security.d.ts +18 -0
  408. package/shared/analyzer/security.js +159 -159
  409. package/shared/analyzer/security.js.map +1 -0
  410. package/shared/analyzer/services.d.ts +5 -0
  411. package/shared/analyzer/services.js +141 -138
  412. package/shared/analyzer/services.js.map +1 -0
  413. package/shared/analyzer/stack.d.ts +6 -0
  414. package/shared/analyzer/stack.js +134 -141
  415. package/shared/analyzer/stack.js.map +1 -0
  416. package/shared/analyzer/tour.d.ts +2 -0
  417. package/shared/analyzer/tour.js +45 -53
  418. package/shared/analyzer/tour.js.map +1 -0
  419. package/shared/analyzer/util.d.ts +8 -0
  420. package/shared/analyzer/util.js +130 -76
  421. package/shared/analyzer/util.js.map +1 -0
  422. package/shared/analyzer/workflows.d.ts +8 -0
  423. package/shared/analyzer/workflows.js +139 -134
  424. package/shared/analyzer/workflows.js.map +1 -0
  425. package/shared/contracts.d.ts +250 -0
  426. package/shared/contracts.js +2 -0
  427. package/shared/contracts.js.map +1 -0
  428. package/shared/diagram/aiFacts.d.ts +35 -0
  429. package/shared/diagram/aiFacts.js +102 -105
  430. package/shared/diagram/aiFacts.js.map +1 -0
  431. package/shared/diagram/aiMermaid.d.ts +5 -0
  432. package/shared/diagram/aiMermaid.js +46 -45
  433. package/shared/diagram/aiMermaid.js.map +1 -0
  434. package/shared/diagram/atlas.d.ts +16 -0
  435. package/shared/diagram/atlas.js +51 -60
  436. package/shared/diagram/atlas.js.map +1 -0
  437. package/shared/diagram/mermaid.d.ts +43 -0
  438. package/shared/diagram/mermaid.js +348 -370
  439. package/shared/diagram/mermaid.js.map +1 -0
  440. package/shared/review/contracts.d.ts +95 -0
  441. package/shared/review/contracts.js +2 -0
  442. package/shared/review/contracts.js.map +1 -0
  443. package/shared/review/review.d.ts +10 -0
  444. package/shared/review/review.js +184 -0
  445. package/shared/review/review.js.map +1 -0
  446. package/shared/search/query.d.ts +32 -0
  447. package/shared/search/query.js +395 -388
  448. package/shared/search/query.js.map +1 -0
@@ -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
- if (!container) return;
14
- activeScanId = scanId;
15
-
16
- container.innerHTML = `
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
- // Wire controls
73
- const baseSelect = container.querySelector('#diffBaseSelect');
74
- const headSelect = container.querySelector('#diffHeadSelect');
75
- const runBtn = container.querySelector('#diffRunBtn');
76
- const unifiedBtn = container.querySelector('#diffModeUnified');
77
- const splitBtn = container.querySelector('#diffModeSplit');
78
-
79
- unifiedBtn?.addEventListener('click', () => {
80
- diffMode = 'unified';
81
- unifiedBtn.classList.add('is-active');
82
- splitBtn?.classList.remove('is-active');
83
- renderCurrentFileDiff(container);
84
- });
85
-
86
- splitBtn?.addEventListener('click', () => {
87
- diffMode = 'split';
88
- splitBtn.classList.add('is-active');
89
- unifiedBtn?.classList.remove('is-active');
90
- renderCurrentFileDiff(container);
91
- });
92
-
93
- runBtn?.addEventListener('click', () => {
94
- loadDiffData(container, scan, facts, baseSelect.value, headSelect.value);
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
- async function loadRefs(container, scanId) {
103
- if (!scanId) return;
104
- try {
105
- const res = await fetch('/api/diff/refs?scan=' + encodeURIComponent(scanId));
106
- if (res.ok) {
107
- currentRefs = await res.json();
108
- const baseSelect = container.querySelector('#diffBaseSelect');
109
- const headSelect = container.querySelector('#diffHeadSelect');
110
- if (baseSelect && currentRefs.branches?.length) {
111
- baseSelect.innerHTML = `
112
- <option value="HEAD~1">HEAD~1 (previous commit)</option>
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
- const listEl = container.querySelector('#diffFilesList');
126
- const summaryEl = container.querySelector('#diffSummaryChip');
127
- if (listEl) listEl.innerHTML = '<p class="diff-empty">Loading diff…</p>';
128
-
129
- try {
130
- const params = new URLSearchParams();
131
- if (activeScanId) params.set('scan', activeScanId);
132
- if (base) params.set('base', base);
133
- if (head) params.set('head', head);
134
-
135
- const res = await fetch('/api/diff?' + params.toString());
136
- if (!res.ok) {
137
- if (listEl) listEl.innerHTML = '<p class="diff-empty">No git repository or diff available for this target.</p>';
138
- return;
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
- currentDiff = await res.json();
142
- const files = currentDiff.files || [];
143
-
144
- if (summaryEl) {
145
- 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>`;
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
- const metricEl = container.querySelector('#blastMetric');
168
- const descEl = container.querySelector('#blastDesc');
169
- if (!metricEl || !descEl) return;
170
-
171
- if (!diffFiles.length) {
172
- metricEl.textContent = '0 files';
173
- descEl.textContent = 'No modified files to analyze.';
174
- return;
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
- const totalFiles = scan?.files?.length || 1;
193
- const impactCount = impacted.size;
194
- const pct = Math.round((impactCount / totalFiles) * 100);
195
-
196
- metricEl.innerHTML = `<b>${impactCount}</b> dependent files affected (${pct}%)`;
197
- descEl.innerHTML = impactCount > 0
198
- ? `Directly impacts <b>${impactCount}</b> downstream module${impactCount === 1 ? '' : 's'} across the import graph.`
199
- : `Self-contained change: no downstream imports affected.`;
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
- const listEl = container.querySelector('#diffFilesList');
204
- if (!listEl) return;
205
-
206
- listEl.innerHTML = files.map((f) => {
207
- const path = f.newPath || f.oldPath;
208
- const isSelected = path === selectedFile;
209
- return `
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
- }).join('');
220
-
221
- listEl.querySelectorAll('.diff-file-item').forEach((item) => {
222
- item.addEventListener('click', () => {
223
- selectedFile = item.dataset.path;
224
- listEl.querySelectorAll('.diff-file-item').forEach((el) => el.classList.remove('is-selected'));
225
- item.classList.add('is-selected');
226
- renderCurrentFileDiff(container);
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
- const mainEl = container.querySelector('#diffViewerMain');
233
- if (!mainEl || !currentDiff) return;
234
-
235
- const file = currentDiff.files?.find((f) => (f.newPath || f.oldPath) === selectedFile);
236
- if (!file) {
237
- mainEl.innerHTML = '<div class="diff-placeholder">Select a file to inspect diff.</div>';
238
- return;
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
- return renderUnifiedHunk(hunk);
246
- }).join('');
247
-
248
- mainEl.innerHTML = `
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
- let oldLine = hunk.oldStart;
263
- let newLine = hunk.newStart;
264
-
265
- const linesHtml = hunk.lines.map((line) => {
266
- let oldNum = '';
267
- let newNum = '';
268
-
269
- if (line.type === 'del') {
270
- oldNum = oldLine++;
271
- } else if (line.type === 'add') {
272
- newNum = newLine++;
273
- } else {
274
- oldNum = oldLine++;
275
- newNum = newLine++;
276
- }
277
-
278
- const sign = line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ');
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
- }).join('');
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
- let oldLine = hunk.oldStart;
300
- let newLine = hunk.newStart;
301
-
302
- const rows = [];
303
- for (let i = 0; i < hunk.lines.length; i++) {
304
- const line = hunk.lines[i];
305
- if (line.type === 'del') {
306
- const delNum = oldLine++;
307
- // Check if next is add
308
- const next = hunk.lines[i + 1];
309
- if (next && next.type === 'add') {
310
- const addNum = newLine++;
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
- i++;
318
- } else {
319
- rows.push(`
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
- } else if (line.type === 'add') {
327
- const addNum = newLine++;
328
- rows.push(`
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
- } else {
335
- const oNum = oldLine++;
336
- const nNum = newLine++;
337
- rows.push(`
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"]}
@@ -0,0 +1,2 @@
1
+ export declare function initDocs(options: any): void;
2
+ export declare function renderDocs(): void;