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,3 +1,4 @@
1
+ import { eventElement } from '/js/dom.js';
1
2
  // The Docs tab: a written account of the repo.
2
3
  //
3
4
  // Three layers, in order of how much they cost. The overview page and the
@@ -9,7 +10,6 @@
9
10
  //
10
11
  // The static sentences live in `/shared/analyzer/docs.js` — pure, and tested.
11
12
  // What is here is the DOM: tabs, streaming, and the wiring between them.
12
-
13
13
  import { parseFolderDoc, fileFactsLine, fileStaticDoc, folderStaticDoc, docFileRow } from '/shared/analyzer/docs.js';
14
14
  import { explainOverview } from '/shared/analyzer/explainLocal.js';
15
15
  import { scanIndex } from '/shared/analyzer/graph.js';
@@ -20,100 +20,89 @@ import { streamExplain } from './api.js';
20
20
  import { findNode, countFiles } from './tree.js';
21
21
  import { readRepoFile, gatherFileContext } from './repoFiles.js';
22
22
  import * as llm from './llm.js';
23
-
24
23
  let host = null;
25
24
  let hooks = {
26
- onToast: () => {},
27
- onOpenSettings: () => {},
28
- onOpenFile: () => {},
29
- repoOverview: () => '',
25
+ onToast: (..._args) => { },
26
+ onOpenSettings: (..._args) => { },
27
+ onOpenFile: (..._args) => { },
28
+ repoOverview: () => '',
30
29
  };
31
-
32
30
  export function initDocs(options) {
33
- host = options.host;
34
- hooks = { ...hooks, ...options };
31
+ host = options.host;
32
+ hooks = { ...hooks, ...options };
35
33
  }
36
-
37
34
  // Facts about one file or folder, bound to the loaded repo. Every prompt and
38
35
  // every static sentence on this page goes through these two, so the doc tab and
39
36
  // the folder pass can never describe the same file differently.
40
37
  const factsLine = (path) => fileFactsLine(path, state.scan, state.facts);
41
38
  const staticDoc = (path) => fileStaticDoc(path, state.scan, state.facts);
42
39
  const fileRow = (path) => docFileRow(path, state.scan, state.facts);
43
-
44
40
  // ---- the page ---------------------------------------------------------------
45
-
46
41
  // One-time document build per scan; AI briefs update rows in place afterwards.
47
42
  export function renderDocs() {
48
- if (state.docs.rendered) return;
49
- state.docs.rendered = true;
50
-
51
- const tabsEl = document.createElement('div');
52
- tabsEl.className = 'doc-tabs';
53
- const wrapEl = document.createElement('div');
54
- const scrollEl = document.createElement('div');
55
- scrollEl.className = 'doc-scroll';
56
- scrollEl.append(tabsEl, wrapEl);
57
-
58
- // Answers stream into this rail, above the bar, only once there's something
59
- // to show.
60
- const railEl = document.createElement('div');
61
- railEl.className = 'doc-qa-rail';
62
- railEl.hidden = true;
63
-
64
- // The sticky bottom bar: questions about whatever doc is on screen, with
65
- // clear/close once a conversation is underway.
66
- const barEl = document.createElement('form');
67
- barEl.className = 'doc-askbar';
68
- barEl.innerHTML = `
43
+ if (state.docs.rendered)
44
+ return;
45
+ state.docs.rendered = true;
46
+ const tabsEl = document.createElement('div');
47
+ tabsEl.className = 'doc-tabs';
48
+ const wrapEl = document.createElement('div');
49
+ const scrollEl = document.createElement('div');
50
+ scrollEl.className = 'doc-scroll';
51
+ scrollEl.append(tabsEl, wrapEl);
52
+ // Answers stream into this rail, above the bar, only once there's something
53
+ // to show.
54
+ const railEl = document.createElement('div');
55
+ railEl.className = 'doc-qa-rail';
56
+ railEl.hidden = true;
57
+ // The sticky bottom bar: questions about whatever doc is on screen, with
58
+ // clear/close once a conversation is underway.
59
+ const barEl = document.createElement('form');
60
+ barEl.className = 'doc-askbar';
61
+ barEl.innerHTML = `
69
62
  <input type="text" id="docAskInput" class="text-input" placeholder="Ask me anything about this doc…" spellcheck="false" autocomplete="off">
70
63
  <button type="submit" class="btn btn-ink">Ask</button>`;
71
- const clearBtn = document.createElement('button');
72
- clearBtn.type = 'button';
73
- clearBtn.className = 'btn btn-sm btn-ghost doc-clear';
74
- clearBtn.textContent = 'clear';
75
- clearBtn.title = 'Clear the conversation';
76
- clearBtn.hidden = true;
77
- const closeBtn = document.createElement('button');
78
- closeBtn.type = 'button';
79
- closeBtn.className = 'btn btn-sm btn-ghost doc-close';
80
- closeBtn.textContent = 'close';
81
- closeBtn.title = 'Close the conversation';
82
- closeBtn.hidden = true;
83
- clearBtn.addEventListener('click', () => { railEl.innerHTML = ''; });
84
- closeBtn.addEventListener('click', () => {
85
- railEl.hidden = true;
64
+ const clearBtn = document.createElement('button');
65
+ clearBtn.type = 'button';
66
+ clearBtn.className = 'btn btn-sm btn-ghost doc-clear';
67
+ clearBtn.textContent = 'clear';
68
+ clearBtn.title = 'Clear the conversation';
86
69
  clearBtn.hidden = true;
70
+ const closeBtn = document.createElement('button');
71
+ closeBtn.type = 'button';
72
+ closeBtn.className = 'btn btn-sm btn-ghost doc-close';
73
+ closeBtn.textContent = 'close';
74
+ closeBtn.title = 'Close the conversation';
87
75
  closeBtn.hidden = true;
88
- });
89
- barEl.append(clearBtn, closeBtn);
90
- barEl.addEventListener('submit', (event) => {
91
- event.preventDefault();
92
- askAboutRepo();
93
- });
94
-
95
- host.innerHTML = '';
96
- host.append(scrollEl, railEl, barEl);
97
- state.docs.tabsEl = tabsEl;
98
- state.docs.wrapEl = wrapEl;
99
- state.docs.railEl = railEl;
100
- state.docs.bar = barEl;
101
- state.docs.pageEl = buildOverviewPage();
102
-
103
- renderDocStrip();
104
- showActiveDoc();
105
- loadReadme();
76
+ clearBtn.addEventListener('click', () => { railEl.innerHTML = ''; });
77
+ closeBtn.addEventListener('click', () => {
78
+ railEl.hidden = true;
79
+ clearBtn.hidden = true;
80
+ closeBtn.hidden = true;
81
+ });
82
+ barEl.append(clearBtn, closeBtn);
83
+ barEl.addEventListener('submit', (event) => {
84
+ event.preventDefault();
85
+ askAboutRepo();
86
+ });
87
+ host.innerHTML = '';
88
+ host.append(scrollEl, railEl, barEl);
89
+ state.docs.tabsEl = tabsEl;
90
+ state.docs.wrapEl = wrapEl;
91
+ state.docs.railEl = railEl;
92
+ state.docs.bar = barEl;
93
+ state.docs.pageEl = buildOverviewPage();
94
+ renderDocStrip();
95
+ showActiveDoc();
96
+ loadReadme();
106
97
  }
107
-
108
98
  // The landing tab: repo header, topic index, README, the documentation zone
109
99
  // (filled on demand by the AI), and the question box at the bottom.
110
100
  function buildOverviewPage() {
111
- const { scan, facts, manifest } = state;
112
- const root = state.treeData;
113
-
114
- const page = document.createElement('div');
115
- page.className = 'doc-page';
116
- page.innerHTML = `
101
+ const { scan, facts, manifest } = state;
102
+ const root = state.treeData;
103
+ const page = document.createElement('div');
104
+ page.className = 'doc-page';
105
+ page.innerHTML = `
117
106
  <div id="doc-top">
118
107
  <h1 class="doc-title">${escapeHtml(scan.name)}</h1>
119
108
  <p class="doc-sub">${escapeHtml(scan.root)}</p>
@@ -130,219 +119,217 @@ function buildOverviewPage() {
130
119
  <button class="btn btn-ink" id="docGenBtn">Generate documentation</button>
131
120
  </div>
132
121
  `;
133
-
134
- buildToc(page, root);
135
-
136
- page.addEventListener('click', (event) => {
137
- const b = event.target.closest('[data-doc-open]');
138
- if (!b) return;
139
- const raw = b.dataset.docOpen;
140
- const i = raw.indexOf(':');
141
- openDocTab(raw.slice(0, i), raw.slice(i + 1));
142
- });
143
- page.querySelector('#docGenBtn').addEventListener('click', writeDocsWithAI);
144
-
145
- return page;
122
+ buildToc(page, root);
123
+ page.addEventListener('click', (event) => {
124
+ const b = eventElement(event)?.closest('[data-doc-open]');
125
+ if (!b)
126
+ return;
127
+ const raw = b.dataset.docOpen;
128
+ const i = raw.indexOf(':');
129
+ openDocTab(raw.slice(0, i), raw.slice(i + 1));
130
+ });
131
+ page.querySelector('#docGenBtn').addEventListener('click', writeDocsWithAI);
132
+ return page;
146
133
  }
147
-
148
134
  // The contents index: page anchors up top, then every folder and file as a
149
135
  // topic — clicking any of them opens its detailed AI tab.
150
136
  function buildToc(page, root) {
151
- const toc = page.querySelector('#docToc');
152
- const bits = [
153
- `<span class="doc-toc-h">Get documentation about a topic</span>`,
154
- `<a href="#doc-top">Overview</a>`,
155
- `<a href="#doc-readme-h">In its own words</a>`,
156
- `<span class="doc-toc-group">Folders and files — click for the AI document</span>`,
157
- ];
158
- let folderCount = 0;
159
- const parsedSet = new Set(state.scan.files.map((f) => f.path));
160
- const walk = (node, depth) => {
161
- if (folderCount >= 30) return;
162
- folderCount++;
163
- const indent = Math.min(depth, 3);
164
- bits.push(`<button class="doc-toc-link doc-toc-folder doc-indent-${indent}" data-doc-open="folder:${escapeHtml(node.path)}">${escapeHtml(node.path ? node.name + '/' : '(repo root)')}</button>`);
165
- const files = node.files.filter((f) => parsedSet.has(f)).slice(0, 10);
166
- if (files.length) {
167
- const links = files
168
- .map((f) => `<button class="doc-toc-link" data-doc-open="file:${escapeHtml(f)}">${escapeHtml(f.split('/').pop())}</button>`)
169
- .join('<span class="doc-toc-sep"> · </span>');
170
- bits.push(`<span class="doc-toc-files doc-indent-${indent}">${links}${node.files.length > files.length ? ' …' : ''}</span>`);
171
- }
172
- for (const child of [...node.dirs.values()].sort((a, b) => a.name.localeCompare(b.name))) {
173
- walk(child, depth + 1);
137
+ const toc = page.querySelector('#docToc');
138
+ const bits = [
139
+ `<span class="doc-toc-h">Get documentation about a topic</span>`,
140
+ `<a href="#doc-top">Overview</a>`,
141
+ `<a href="#doc-readme-h">In its own words</a>`,
142
+ `<span class="doc-toc-group">Folders and files — click for the AI document</span>`,
143
+ ];
144
+ let folderCount = 0;
145
+ const parsedSet = new Set(state.scan.files.map((f) => f.path));
146
+ const walk = (node, depth) => {
147
+ if (folderCount >= 30)
148
+ return;
149
+ folderCount++;
150
+ const indent = Math.min(depth, 3);
151
+ bits.push(`<button class="doc-toc-link doc-toc-folder doc-indent-${indent}" data-doc-open="folder:${escapeHtml(node.path)}">${escapeHtml(node.path ? node.name + '/' : '(repo root)')}</button>`);
152
+ const files = node.files.filter((f) => parsedSet.has(f)).slice(0, 10);
153
+ if (files.length) {
154
+ const links = files
155
+ .map((f) => `<button class="doc-toc-link" data-doc-open="file:${escapeHtml(f)}">${escapeHtml(f.split('/').pop())}</button>`)
156
+ .join('<span class="doc-toc-sep"> · </span>');
157
+ bits.push(`<span class="doc-toc-files doc-indent-${indent}">${links}${node.files.length > files.length ? ' …' : ''}</span>`);
158
+ }
159
+ for (const child of [...node.dirs.values()].sort((a, b) => a.name.localeCompare(b.name))) {
160
+ walk(child, depth + 1);
161
+ }
162
+ };
163
+ if (root.files.length)
164
+ walk({ ...root, dirs: new Map() }, 0);
165
+ for (const child of [...root.dirs.values()].sort((a, b) => a.name.localeCompare(b.name))) {
166
+ walk(child, 0);
174
167
  }
175
- };
176
- if (root.files.length) walk({ ...root, dirs: new Map() }, 0);
177
- for (const child of [...root.dirs.values()].sort((a, b) => a.name.localeCompare(b.name))) {
178
- walk(child, 0);
179
- }
180
- toc.innerHTML = bits.join('');
168
+ toc.innerHTML = bits.join('');
181
169
  }
182
-
183
170
  async function loadReadme() {
184
- const box = host.querySelector('#docReadme');
185
- if (!box) return;
186
- for (const name of ['README.md', 'readme.md', 'Readme.md', 'README', 'README.txt']) {
187
- try {
188
- const text = await readRepoFile(name);
189
- if (text) {
190
- box.innerHTML = mdRender(text);
171
+ const box = host.querySelector('#docReadme');
172
+ if (!box)
191
173
  return;
192
- }
193
- } catch {
194
- /* try the next spelling */
174
+ for (const name of ['README.md', 'readme.md', 'Readme.md', 'README', 'README.txt']) {
175
+ try {
176
+ const text = await readRepoFile(name);
177
+ if (text) {
178
+ box.innerHTML = mdRender(text);
179
+ return;
180
+ }
181
+ }
182
+ catch {
183
+ /* try the next spelling */
184
+ }
195
185
  }
196
- }
197
- box.innerHTML = '<span class="doc-readme-none">This repo ships no README — the map and the patterns are all you get.</span>';
186
+ box.innerHTML = '<span class="doc-readme-none">This repo ships no README — the map and the patterns are all you get.</span>';
198
187
  }
199
-
200
188
  // ---- the sticky ask bar: questions scoped to the doc on screen -------------
201
-
202
189
  async function askAboutRepo() {
203
- const input = host.querySelector('#docAskInput');
204
- const question = input?.value.trim();
205
- if (!question) return hooks.onToast('Type a question first.');
206
- if (!llm.isConfigured()) {
207
- hooks.onOpenSettings();
208
- hooks.onToast('Add an endpoint and model first.');
209
- return;
210
- }
211
- input.value = '';
212
- const rail = state.docs.railEl;
213
- rail.hidden = false;
214
- // reveal the clear/close controls now that a conversation exists
215
- const bar = state.docs.bar;
216
- if (bar) {
217
- const c = bar.querySelector('.doc-clear');
218
- const x = bar.querySelector('.doc-close');
219
- if (c) c.hidden = false;
220
- if (x) x.hidden = false;
221
- }
222
- const settings = llm.getSettings();
223
-
224
- const card = document.createElement('div');
225
- card.className = 'doc-qa-card';
226
- card.innerHTML = `<p class="doc-qa-q">“${escapeHtml(question)}”</p><div class="doc-qa-a"><span class="caret"></span></div>`;
227
- rail.appendChild(card);
228
- card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
229
-
230
- // Scope the answer to whatever is on screen: an open file/folder doc tab
231
- // gets its facts (and source, for files) sent along; the overview answers
232
- // from the repo-wide picture.
233
- const active = state.docs.active || 'overview';
234
- let file = null;
235
- let scope = 'the repo overview';
236
- let overview = hooks.repoOverview() + ' Top-level folders: ' + [...state.treeData.dirs.keys()].join(', ') + '.';
237
- if (active.startsWith('file:')) {
238
- const path = active.slice(5);
239
- file = await gatherFileContext(path);
240
- scope = path;
241
- } else if (active.startsWith('folder:')) {
242
- scope = active.slice(7) + '/';
243
- overview += ` The user is currently reading the documentation for the folder "${active.slice(7)}".`;
244
- }
245
-
246
- const messages = llm.questionMessages({ repoName: state.scan.name, overview, question, file });
247
- let text = '';
248
- try {
249
- for await (const delta of streamExplain({ ...settings, messages, maxTokens: 1200 })) {
250
- text += delta;
251
- card.querySelector('.doc-qa-a').innerHTML = mdLite(text) + '<span class="caret"></span>';
190
+ const input = host.querySelector('#docAskInput');
191
+ const question = input?.value.trim();
192
+ if (!question)
193
+ return hooks.onToast('Type a question first.');
194
+ if (!llm.isConfigured()) {
195
+ hooks.onOpenSettings();
196
+ hooks.onToast('Add an endpoint and model first.');
197
+ return;
198
+ }
199
+ input.value = '';
200
+ const rail = state.docs.railEl;
201
+ rail.hidden = false;
202
+ // reveal the clear/close controls now that a conversation exists
203
+ const bar = state.docs.bar;
204
+ if (bar) {
205
+ const c = bar.querySelector('.doc-clear');
206
+ const x = bar.querySelector('.doc-close');
207
+ if (c)
208
+ c.hidden = false;
209
+ if (x)
210
+ x.hidden = false;
211
+ }
212
+ const settings = llm.getSettings();
213
+ const card = document.createElement('div');
214
+ card.className = 'doc-qa-card';
215
+ card.innerHTML = `<p class="doc-qa-q">“${escapeHtml(question)}”</p><div class="doc-qa-a"><span class="caret"></span></div>`;
216
+ rail.appendChild(card);
217
+ card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
218
+ // Scope the answer to whatever is on screen: an open file/folder doc tab
219
+ // gets its facts (and source, for files) sent along; the overview answers
220
+ // from the repo-wide picture.
221
+ const active = state.docs.active || 'overview';
222
+ let file = null;
223
+ let scope = 'the repo overview';
224
+ let overview = hooks.repoOverview() + ' Top-level folders: ' + [...state.treeData.dirs.keys()].join(', ') + '.';
225
+ if (active.startsWith('file:')) {
226
+ const path = active.slice(5);
227
+ file = await gatherFileContext(path);
228
+ scope = path;
229
+ }
230
+ else if (active.startsWith('folder:')) {
231
+ scope = active.slice(7) + '/';
232
+ overview += ` The user is currently reading the documentation for the folder "${active.slice(7)}".`;
233
+ }
234
+ const messages = llm.questionMessages({ repoName: state.scan.name, overview, question, file });
235
+ let text = '';
236
+ try {
237
+ for await (const delta of streamExplain({ ...settings, messages, maxTokens: 1200 })) {
238
+ text += delta;
239
+ card.querySelector('.doc-qa-a').innerHTML = mdLite(text) + '<span class="caret"></span>';
240
+ }
241
+ card.querySelector('.doc-qa-a').innerHTML =
242
+ mdLite(text) + `<p class="explain-src">Answered by ${settings.model}, looking at ${escapeHtml(scope)}.</p>`;
243
+ }
244
+ catch (err) {
245
+ card.querySelector('.doc-qa-a').innerHTML =
246
+ `<p style="color:var(--warn)">${escapeHtml(err.message)} — check the API key drawer, top right.</p>`;
252
247
  }
253
- card.querySelector('.doc-qa-a').innerHTML =
254
- mdLite(text) + `<p class="explain-src">Answered by ${settings.model}, looking at ${escapeHtml(scope)}.</p>`;
255
- } catch (err) {
256
- card.querySelector('.doc-qa-a').innerHTML =
257
- `<p style="color:var(--warn)">${escapeHtml(err.message)} — check the API key drawer, top right.</p>`;
258
- }
259
248
  }
260
-
261
249
  // ---- doc tabs: detailed AI documents per file/folder -----------------------
262
-
263
250
  function renderDocStrip() {
264
- const strip = state.docs.tabsEl;
265
- if (!strip) return;
266
- strip.innerHTML = '';
267
-
268
- const mk = (key, label, closable) => {
269
- const b = document.createElement('button');
270
- b.className = 'doc-tab' + (state.docs.active === key ? ' is-active' : '');
271
- const entry = state.docs.tabs.get(key);
272
- b.innerHTML = `<span>${escapeHtml(label)}</span>${entry?.status === 'writing' ? '<span class="doc-tab-spin">…</span>' : ''}`;
273
- b.addEventListener('click', () => {
274
- state.docs.active = key;
275
- renderDocStrip();
276
- showActiveDoc();
277
- });
278
- if (closable) {
279
- const x = document.createElement('span');
280
- x.className = 'doc-tab-x';
281
- x.textContent = '×';
282
- x.title = 'Close this tab';
283
- x.addEventListener('click', (event) => {
284
- event.stopPropagation();
285
- closeDocTab(key);
286
- });
287
- b.appendChild(x);
251
+ const strip = state.docs.tabsEl;
252
+ if (!strip)
253
+ return;
254
+ strip.innerHTML = '';
255
+ const mk = (key, label, closable) => {
256
+ const b = document.createElement('button');
257
+ b.className = 'doc-tab' + (state.docs.active === key ? ' is-active' : '');
258
+ const entry = state.docs.tabs.get(key);
259
+ b.innerHTML = `<span>${escapeHtml(label)}</span>${entry?.status === 'writing' ? '<span class="doc-tab-spin">…</span>' : ''}`;
260
+ b.addEventListener('click', () => {
261
+ state.docs.active = key;
262
+ renderDocStrip();
263
+ showActiveDoc();
264
+ });
265
+ if (closable) {
266
+ const x = document.createElement('span');
267
+ x.className = 'doc-tab-x';
268
+ x.textContent = '×';
269
+ x.title = 'Close this tab';
270
+ x.addEventListener('click', (event) => {
271
+ event.stopPropagation();
272
+ closeDocTab(key);
273
+ });
274
+ b.appendChild(x);
275
+ }
276
+ return b;
277
+ };
278
+ strip.appendChild(mk('overview', 'Overview', false));
279
+ for (const [key, entry] of state.docs.tabs) {
280
+ strip.appendChild(mk(key, entry.title, true));
288
281
  }
289
- return b;
290
- };
291
-
292
- strip.appendChild(mk('overview', 'Overview', false));
293
- for (const [key, entry] of state.docs.tabs) {
294
- strip.appendChild(mk(key, entry.title, true));
295
- }
296
282
  }
297
-
298
283
  function openDocTab(kind, path) {
299
- const key = kind + ':' + path;
300
- if (!state.docs.tabs.has(key)) {
301
- const title = (path ? path.split('/').pop() : '(root)') + (kind === 'folder' ? '/' : '');
302
- state.docs.tabs.set(key, { key, kind, path, title, status: 'idle', text: '', error: '' });
303
- }
304
- state.docs.active = key;
305
- renderDocStrip();
306
- showActiveDoc();
307
- const entry = state.docs.tabs.get(key);
308
- if (entry.status === 'idle') generateDocTab(entry);
284
+ const key = kind + ':' + path;
285
+ if (!state.docs.tabs.has(key)) {
286
+ const title = (path ? path.split('/').pop() : '(root)') + (kind === 'folder' ? '/' : '');
287
+ state.docs.tabs.set(key, { key, kind, path, title, status: 'idle', text: '', error: '' });
288
+ }
289
+ state.docs.active = key;
290
+ renderDocStrip();
291
+ showActiveDoc();
292
+ const entry = state.docs.tabs.get(key);
293
+ if (entry.status === 'idle')
294
+ generateDocTab(entry);
309
295
  }
310
-
311
296
  function closeDocTab(key) {
312
- state.docs.tabs.delete(key);
313
- if (state.docs.active === key) state.docs.active = 'overview';
314
- renderDocStrip();
315
- showActiveDoc();
297
+ state.docs.tabs.delete(key);
298
+ if (state.docs.active === key)
299
+ state.docs.active = 'overview';
300
+ renderDocStrip();
301
+ showActiveDoc();
316
302
  }
317
-
318
303
  function showActiveDoc() {
319
- const wrap = state.docs.wrapEl;
320
- if (!wrap) return;
321
- if (state.docs.active === 'overview') {
322
- wrap.replaceChildren(state.docs.pageEl);
323
- return;
324
- }
325
- const entry = state.docs.tabs.get(state.docs.active);
326
- if (entry) wrap.replaceChildren(buildPaneEl(entry));
304
+ const wrap = state.docs.wrapEl;
305
+ if (!wrap)
306
+ return;
307
+ if (state.docs.active === 'overview') {
308
+ wrap.replaceChildren(state.docs.pageEl);
309
+ return;
310
+ }
311
+ const entry = state.docs.tabs.get(state.docs.active);
312
+ if (entry)
313
+ wrap.replaceChildren(buildPaneEl(entry));
327
314
  }
328
-
329
315
  function buildPaneEl(entry) {
330
- const pane = document.createElement('div');
331
- pane.className = 'doc-page doc-detail';
332
-
333
- const chips = [];
334
- if (entry.kind === 'file') {
335
- const f = scanIndex(state.scan).fileAt(entry.path);
336
- const fin = state.facts.fanIn[entry.path] || 0;
337
- const fout = state.facts.fanOut[entry.path] || 0;
338
- chips.push(`<b>${fin}</b> dependents`, `<b>${fout}</b> imports`);
339
- if (f) chips.push(`<b>${f.functions.length}</b> functions`, `<b>${(f.size / 1024).toFixed(1)}</b> kb`);
340
- } else {
341
- const node = findNode(state.treeData, entry.path);
342
- if (node) chips.push(`<b>${node.files.length}</b> files`, `<b>${node.dirs.size}</b> subfolders`);
343
- }
344
-
345
- pane.innerHTML = `
316
+ const pane = document.createElement('div');
317
+ pane.className = 'doc-page doc-detail';
318
+ const chips = [];
319
+ if (entry.kind === 'file') {
320
+ const f = scanIndex(state.scan).fileAt(entry.path);
321
+ const fin = state.facts.fanIn[entry.path] || 0;
322
+ const fout = state.facts.fanOut[entry.path] || 0;
323
+ chips.push(`<b>${fin}</b> dependents`, `<b>${fout}</b> imports`);
324
+ if (f)
325
+ chips.push(`<b>${f.functions.length}</b> functions`, `<b>${(f.size / 1024).toFixed(1)}</b> kb`);
326
+ }
327
+ else {
328
+ const node = findNode(state.treeData, entry.path);
329
+ if (node)
330
+ chips.push(`<b>${node.files.length}</b> files`, `<b>${node.dirs.size}</b> subfolders`);
331
+ }
332
+ pane.innerHTML = `
346
333
  <h1 class="doc-title">${escapeHtml(entry.title)}</h1>
347
334
  <p class="doc-sub">${escapeHtml(entry.path || '(repo root)')}</p>
348
335
  <p class="doc-sub doc-detail-meta">${chips.join(' &nbsp;·&nbsp; ')}</p>
@@ -352,168 +339,172 @@ function buildPaneEl(entry) {
352
339
  </div>
353
340
  <div class="doc-detail-body"></div>
354
341
  `;
355
-
356
- const body = pane.querySelector('.doc-detail-body');
357
- if (entry.status === 'error') {
358
- body.innerHTML = `<p style="color:var(--warn)">${escapeHtml(entry.error)} — hit Regenerate to try again.</p>`;
359
- } else if (entry.status === 'idle' || (entry.status === 'writing' && !entry.text)) {
360
- // The static write-up is the floor: something true is on screen before the
361
- // model answers, and stays there if it never does.
362
- const fallback = entry.kind === 'file'
363
- ? staticDoc(entry.path)
364
- : folderStaticDoc(entry.path, state.scan, state.facts, findNode(state.treeData, entry.path)?.dirs.size || 0);
365
- body.innerHTML = `<p>${escapeHtml(fallback)}</p>`
366
- + '<p class="doc-readme-none">Gathering facts and writing<span class="caret"></span></p>';
367
- } else {
368
- body.innerHTML = mdRender(entry.text) + (entry.status === 'writing' ? '<span class="caret"></span>' : '');
369
- }
370
-
371
- pane.addEventListener('click', (event) => {
372
- if (event.target.closest('[data-doc-dd]')) hooks.onOpenFile(entry.path);
373
- if (event.target.closest('[data-doc-regen]')) {
374
- entry.status = 'idle';
375
- entry.text = '';
376
- generateDocTab(entry);
377
- showActiveDoc();
342
+ const body = pane.querySelector('.doc-detail-body');
343
+ if (entry.status === 'error') {
344
+ body.innerHTML = `<p style="color:var(--warn)">${escapeHtml(entry.error)} — hit Regenerate to try again.</p>`;
378
345
  }
379
- });
380
- return pane;
346
+ else if (entry.status === 'idle' || (entry.status === 'writing' && !entry.text)) {
347
+ // The static write-up is the floor: something true is on screen before the
348
+ // model answers, and stays there if it never does.
349
+ const fallback = entry.kind === 'file'
350
+ ? staticDoc(entry.path)
351
+ : folderStaticDoc(entry.path, state.scan, state.facts, findNode(state.treeData, entry.path)?.dirs.size || 0);
352
+ body.innerHTML = `<p>${escapeHtml(fallback)}</p>`
353
+ + '<p class="doc-readme-none">Gathering facts and writing<span class="caret"></span></p>';
354
+ }
355
+ else {
356
+ body.innerHTML = mdRender(entry.text) + (entry.status === 'writing' ? '<span class="caret"></span>' : '');
357
+ }
358
+ pane.addEventListener('click', (event) => {
359
+ if (eventElement(event)?.closest('[data-doc-dd]'))
360
+ hooks.onOpenFile(entry.path);
361
+ if (eventElement(event)?.closest('[data-doc-regen]')) {
362
+ entry.status = 'idle';
363
+ entry.text = '';
364
+ generateDocTab(entry);
365
+ showActiveDoc();
366
+ }
367
+ });
368
+ return pane;
381
369
  }
382
-
383
370
  let docRefreshTimer = null;
384
371
  function scheduleDocPaneRefresh(entry) {
385
- if (state.docs.active !== entry.key || docRefreshTimer) return;
386
- docRefreshTimer = setTimeout(() => {
387
- docRefreshTimer = null;
388
- if (state.docs.active === entry.key) showActiveDoc();
389
- }, 160);
372
+ if (state.docs.active !== entry.key || docRefreshTimer)
373
+ return;
374
+ docRefreshTimer = setTimeout(() => {
375
+ docRefreshTimer = null;
376
+ if (state.docs.active === entry.key)
377
+ showActiveDoc();
378
+ }, 160);
390
379
  }
391
-
392
380
  async function generateDocTab(entry) {
393
- if (!llm.isConfigured()) {
394
- entry.status = 'error';
395
- entry.error = 'No endpoint configured — open the API key drawer, top right.';
396
- showActiveDoc();
381
+ if (!llm.isConfigured()) {
382
+ entry.status = 'error';
383
+ entry.error = 'No endpoint configured — open the API key drawer, top right.';
384
+ showActiveDoc();
385
+ renderDocStrip();
386
+ hooks.onOpenSettings();
387
+ return;
388
+ }
389
+ const settings = llm.getSettings();
390
+ entry.status = 'writing';
391
+ entry.text = '';
392
+ entry.error = '';
397
393
  renderDocStrip();
398
- hooks.onOpenSettings();
399
- return;
400
- }
401
- const settings = llm.getSettings();
402
- entry.status = 'writing';
403
- entry.text = '';
404
- entry.error = '';
405
- renderDocStrip();
406
- try {
407
- let messages;
408
- if (entry.kind === 'file') {
409
- const ctx = await gatherFileContext(entry.path);
410
- messages = llm.fileDocMessages({ repoName: state.scan.name, overview: hooks.repoOverview(), file: ctx });
411
- } else {
412
- const node = findNode(state.treeData, entry.path);
413
- messages = llm.folderDocDetailedMessages({
414
- repoName: state.scan.name,
415
- folder: entry.path,
416
- subfolders: node ? [...node.dirs.keys()].map((n) => n + '/') : [],
417
- files: (node?.files || []).slice(0, 18).map(fileRow),
418
- });
394
+ try {
395
+ let messages;
396
+ if (entry.kind === 'file') {
397
+ const ctx = await gatherFileContext(entry.path);
398
+ messages = llm.fileDocMessages({ repoName: state.scan.name, overview: hooks.repoOverview(), file: ctx });
399
+ }
400
+ else {
401
+ const node = findNode(state.treeData, entry.path);
402
+ messages = llm.folderDocDetailedMessages({
403
+ repoName: state.scan.name,
404
+ folder: entry.path,
405
+ subfolders: node ? [...node.dirs.keys()].map((n) => n + '/') : [],
406
+ files: (node?.files || []).slice(0, 18).map(fileRow),
407
+ });
408
+ }
409
+ for await (const delta of streamExplain({ ...settings, messages, maxTokens: 1800 })) {
410
+ entry.text += delta;
411
+ scheduleDocPaneRefresh(entry);
412
+ }
413
+ entry.status = 'done';
419
414
  }
420
- for await (const delta of streamExplain({ ...settings, messages, maxTokens: 1800 })) {
421
- entry.text += delta;
422
- scheduleDocPaneRefresh(entry);
415
+ catch (err) {
416
+ entry.status = 'error';
417
+ entry.error = err.message;
423
418
  }
424
- entry.status = 'done';
425
- } catch (err) {
426
- entry.status = 'error';
427
- entry.error = err.message;
428
- }
429
- renderDocStrip();
430
- if (state.docs.active === entry.key) showActiveDoc();
419
+ renderDocStrip();
420
+ if (state.docs.active === entry.key)
421
+ showActiveDoc();
431
422
  }
432
-
433
423
  // ---- the folder-by-folder pass --------------------------------------------
434
-
435
424
  // Which folders get an AI brief, and with what file facts. Top-level folders
436
425
  // by size, capped — one call briefs a folder and everything directly in it.
437
426
  function docTargets() {
438
- const root = state.treeData;
439
- const targets = [];
440
- if (root.files.length) {
441
- targets.push({
442
- path: '',
443
- label: '(repo root)',
444
- subfolders: [...root.dirs.keys()].map((n) => n + '/'),
445
- files: root.files.slice(0, 18).map(fileRow),
446
- });
447
- }
448
- const topDirs = [...root.dirs.values()]
449
- .map((node) => ({ node, count: countFiles(node) }))
450
- .sort((a, b) => b.count - a.count)
451
- .slice(0, 8);
452
- for (const { node } of topDirs) {
453
- targets.push({
454
- path: node.path,
455
- label: node.path + '/',
456
- subfolders: [...node.dirs.keys()].map((n) => n + '/'),
457
- files: node.files.slice(0, 18).map(fileRow),
458
- });
459
- }
460
- return targets;
427
+ const root = state.treeData;
428
+ const targets = [];
429
+ if (root.files.length) {
430
+ targets.push({
431
+ path: '',
432
+ label: '(repo root)',
433
+ subfolders: [...root.dirs.keys()].map((n) => n + '/'),
434
+ files: root.files.slice(0, 18).map(fileRow),
435
+ });
436
+ }
437
+ const topDirs = [...root.dirs.values()]
438
+ .map((node) => ({ node, count: countFiles(node) }))
439
+ .sort((a, b) => b.count - a.count)
440
+ .slice(0, 8);
441
+ for (const { node } of topDirs) {
442
+ targets.push({
443
+ path: node.path,
444
+ label: node.path + '/',
445
+ subfolders: [...node.dirs.keys()].map((n) => n + '/'),
446
+ files: node.files.slice(0, 18).map(fileRow),
447
+ });
448
+ }
449
+ return targets;
461
450
  }
462
-
463
451
  async function writeDocsWithAI() {
464
- if (state.docs.writing) return;
465
- if (!llm.isConfigured()) {
466
- hooks.onOpenSettings();
467
- hooks.onToast('Add an endpoint and model first.');
468
- return;
469
- }
470
- const settings = llm.getSettings();
471
- const page = state.docs.pageEl;
472
- const zone = page?.querySelector('#docGenZone');
473
- const btn = page?.querySelector('#docGenBtn');
474
- const note = page?.querySelector('#docAiNote');
475
- const overviewEl = page?.querySelector('#docOverview');
476
- if (!zone) return;
477
-
478
- state.docs.writing = true;
479
- if (btn) {
480
- btn.disabled = true;
481
- btn.textContent = 'writing…';
482
- }
483
- zone.querySelectorAll('.doc-generated').forEach((el) => el.remove()); // regenerate = fresh page
484
- const empty = zone.querySelector('.doc-gen-empty');
485
- if (empty) empty.hidden = true;
486
-
487
- try {
488
- // 1. The repo-level overview, streamed in place.
489
- if (note) note.textContent = 'Writing the overview…';
490
- const readmeText = host.querySelector('#docReadme')?.innerText?.slice(0, 1500) || '';
491
- let out = '';
492
- if (overviewEl) overviewEl.innerHTML = '<span class="caret"></span>';
493
- const overviewMsgs = llm.repoDocMessages({
494
- repoName: state.scan.name,
495
- overview: hooks.repoOverview(),
496
- readmeExcerpt: readmeText,
497
- entries: state.facts.entries,
498
- hubs: state.facts.hubs,
499
- });
500
- for await (const delta of streamExplain({ ...settings, messages: overviewMsgs, maxTokens: 900 })) {
501
- out += delta;
502
- if (overviewEl) overviewEl.textContent = out;
452
+ if (state.docs.writing)
453
+ return;
454
+ if (!llm.isConfigured()) {
455
+ hooks.onOpenSettings();
456
+ hooks.onToast('Add an endpoint and model first.');
457
+ return;
503
458
  }
504
-
505
- // 2. Folder by folder: each section appears with static sentences first,
506
- // then the AI's documentation overwrites it as the answer lands.
507
- const targets = docTargets();
508
- let done = 0;
509
- for (const t of targets) {
510
- done++;
511
- if (note) note.textContent = `Writing documentation… ${done} of ${targets.length} — ${t.label}`;
512
-
513
- const node = findNode(state.treeData, t.path);
514
- const section = document.createElement('section');
515
- section.className = 'doc-folder doc-generated';
516
- section.innerHTML = `
459
+ const settings = llm.getSettings();
460
+ const page = state.docs.pageEl;
461
+ const zone = page?.querySelector('#docGenZone');
462
+ const btn = page?.querySelector('#docGenBtn');
463
+ const note = page?.querySelector('#docAiNote');
464
+ const overviewEl = page?.querySelector('#docOverview');
465
+ if (!zone)
466
+ return;
467
+ state.docs.writing = true;
468
+ if (btn) {
469
+ btn.disabled = true;
470
+ btn.textContent = 'writing…';
471
+ }
472
+ zone.querySelectorAll('.doc-generated').forEach((el) => el.remove()); // regenerate = fresh page
473
+ const empty = zone.querySelector('.doc-gen-empty');
474
+ if (empty)
475
+ empty.hidden = true;
476
+ try {
477
+ // 1. The repo-level overview, streamed in place.
478
+ if (note)
479
+ note.textContent = 'Writing the overview…';
480
+ const readmeText = host.querySelector('#docReadme')?.innerText?.slice(0, 1500) || '';
481
+ let out = '';
482
+ if (overviewEl)
483
+ overviewEl.innerHTML = '<span class="caret"></span>';
484
+ const overviewMsgs = llm.repoDocMessages({
485
+ repoName: state.scan.name,
486
+ overview: hooks.repoOverview(),
487
+ readmeExcerpt: readmeText,
488
+ entries: state.facts.entries,
489
+ hubs: state.facts.hubs,
490
+ });
491
+ for await (const delta of streamExplain({ ...settings, messages: overviewMsgs, maxTokens: 900 })) {
492
+ out += delta;
493
+ if (overviewEl)
494
+ overviewEl.textContent = out;
495
+ }
496
+ // 2. Folder by folder: each section appears with static sentences first,
497
+ // then the AI's documentation overwrites it as the answer lands.
498
+ const targets = docTargets();
499
+ let done = 0;
500
+ for (const t of targets) {
501
+ done++;
502
+ if (note)
503
+ note.textContent = `Writing documentation… ${done} of ${targets.length} — ${t.label}`;
504
+ const node = findNode(state.treeData, t.path);
505
+ const section = document.createElement('section');
506
+ section.className = 'doc-folder doc-generated';
507
+ section.innerHTML = `
517
508
  <h3 class="doc-folder-h"><button data-doc-open="folder:${escapeHtml(t.path)}">${escapeHtml(t.label)}</button></h3>
518
509
  <p class="doc-folder-brief"><span class="caret"></span></p>
519
510
  ${t.files.map((f) => `
@@ -525,42 +516,50 @@ async function writeDocsWithAI() {
525
516
  <p class="doc-entry-text" data-doc-file="${escapeHtml(f.path)}">${escapeHtml(staticDoc(f.path))}</p>
526
517
  </div>`).join('')}
527
518
  `;
528
- zone.appendChild(section);
529
- section.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
530
-
531
- let text = '';
532
- const msgs = llm.folderDocMessages({
533
- repoName: state.scan.name,
534
- folder: t.path,
535
- subfolders: t.subfolders,
536
- files: t.files,
537
- });
538
- for await (const delta of streamExplain({ ...settings, messages: msgs, maxTokens: 1600 })) {
539
- text += delta;
540
- }
541
- const parsed = parseFolderDoc(text);
542
- const folderP = section.querySelector('.doc-folder-brief');
543
- if (folderP) {
544
- folderP.textContent = parsed.folderBrief
545
- || folderStaticDoc(t.path, state.scan, state.facts, node?.dirs.size || 0);
546
- }
547
- for (const [name, brief] of parsed.files) {
548
- const row = t.files.find((f) => f.name === name);
549
- if (!row) continue;
550
- const el = section.querySelector(`[data-doc-file="${CSS.escape(row.path)}"]`);
551
- if (el) el.textContent = brief;
552
- }
519
+ zone.appendChild(section);
520
+ section.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
521
+ let text = '';
522
+ const msgs = llm.folderDocMessages({
523
+ repoName: state.scan.name,
524
+ folder: t.path,
525
+ subfolders: t.subfolders,
526
+ files: t.files,
527
+ });
528
+ for await (const delta of streamExplain({ ...settings, messages: msgs, maxTokens: 1600 })) {
529
+ text += delta;
530
+ }
531
+ const parsed = parseFolderDoc(text);
532
+ const folderP = section.querySelector('.doc-folder-brief');
533
+ if (folderP) {
534
+ folderP.textContent = parsed.folderBrief
535
+ || folderStaticDoc(t.path, state.scan, state.facts, node?.dirs.size || 0);
536
+ }
537
+ for (const [name, brief] of parsed.files) {
538
+ const row = t.files.find((f) => f.name === name);
539
+ if (!row)
540
+ continue;
541
+ const el = section.querySelector(`[data-doc-file="${CSS.escape(row.path)}"]`);
542
+ if (el)
543
+ el.textContent = brief;
544
+ }
545
+ }
546
+ if (note)
547
+ note.textContent = `Written by ${settings.model} from the real import graph. Press “Regenerate documentation” for a fresh take.`;
548
+ if (btn)
549
+ btn.textContent = 'Regenerate documentation';
553
550
  }
554
- if (note) note.textContent = `Written by ${settings.model} from the real import graph. Press “Regenerate documentation” for a fresh take.`;
555
- if (btn) btn.textContent = 'Regenerate documentation';
556
- } catch (err) {
557
- if (note) note.textContent = 'AI writing stopped: ' + err.message;
558
- hooks.onToast(err.message);
559
- } finally {
560
- state.docs.writing = false;
561
- if (btn) {
562
- btn.disabled = false;
563
- if (btn.textContent === 'writing…') btn.textContent = 'Generate documentation';
551
+ catch (err) {
552
+ if (note)
553
+ note.textContent = 'AI writing stopped: ' + err.message;
554
+ hooks.onToast(err.message);
555
+ }
556
+ finally {
557
+ state.docs.writing = false;
558
+ if (btn) {
559
+ btn.disabled = false;
560
+ if (btn.textContent === 'writing…')
561
+ btn.textContent = 'Generate documentation';
562
+ }
564
563
  }
565
- }
566
564
  }
565
+ //# sourceMappingURL=docsView.js.map