pkgviz 0.7.7 → 0.8.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 (367) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +2 -1
  3. package/.next/build-manifest.json +15 -24
  4. package/.next/fallback-build-manifest.json +15 -0
  5. package/.next/images-manifest.json +14 -3
  6. package/.next/next-minimal-server.js.nft.json +1 -1
  7. package/.next/next-server.js.nft.json +1 -1
  8. package/.next/prerender-manifest.json +52 -16
  9. package/.next/required-server-files.js +349 -0
  10. package/.next/required-server-files.json +106 -75
  11. package/.next/routes-manifest.json +16 -6
  12. package/.next/server/app/_global-error/page/app-paths-manifest.json +3 -0
  13. package/.next/server/app/_global-error/page/build-manifest.json +20 -0
  14. package/.next/server/app/_global-error/page/next-font-manifest.json +6 -0
  15. package/.next/server/app/_global-error/page/server-reference-manifest.json +4 -0
  16. package/.next/server/app/_global-error/page.js +9 -0
  17. package/.next/server/app/_global-error/page.js.map +5 -0
  18. package/.next/server/app/_global-error/page.js.nft.json +1 -0
  19. package/.next/server/app/_global-error/page_client-reference-manifest.js +3 -0
  20. package/.next/server/app/_global-error.html +1 -0
  21. package/.next/server/app/_global-error.meta +22 -0
  22. package/.next/server/app/_global-error.rsc +15 -0
  23. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +20 -0
  24. package/.next/server/app/_global-error.segments/_full.segment.rsc +15 -0
  25. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
  26. package/.next/server/app/_not-found/page/app-paths-manifest.json +3 -0
  27. package/.next/server/app/_not-found/page/build-manifest.json +20 -0
  28. package/.next/server/app/_not-found/page/next-font-manifest.json +11 -0
  29. package/.next/server/app/_not-found/page/react-loadable-manifest.json +1 -0
  30. package/.next/server/app/_not-found/page/server-reference-manifest.json +4 -0
  31. package/.next/server/app/_not-found/page.js +13 -2
  32. package/.next/server/app/_not-found/page.js.map +5 -0
  33. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  34. package/.next/server/app/_not-found/page_client-reference-manifest.js +3 -1
  35. package/.next/server/app/_not-found.html +1 -1
  36. package/.next/server/app/_not-found.meta +19 -0
  37. package/.next/server/app/_not-found.rsc +18 -19
  38. package/.next/server/app/_not-found.segments/_full.segment.rsc +18 -0
  39. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +24 -0
  40. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -0
  41. package/.next/server/app/favicon.ico/route/app-paths-manifest.json +3 -0
  42. package/.next/server/app/favicon.ico/route/build-manifest.json +12 -0
  43. package/.next/server/app/favicon.ico/route.js +6 -1
  44. package/.next/server/app/favicon.ico/route.js.map +5 -0
  45. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  46. package/.next/server/app/page/app-paths-manifest.json +3 -0
  47. package/.next/server/app/page/build-manifest.json +20 -0
  48. package/.next/server/app/page/next-font-manifest.json +11 -0
  49. package/.next/server/app/page/react-loadable-manifest.json +1 -0
  50. package/.next/server/app/page/server-reference-manifest.json +93 -0
  51. package/.next/server/app/page.js +16 -13
  52. package/.next/server/app/page.js.map +5 -0
  53. package/.next/server/app/page.js.nft.json +1 -1
  54. package/.next/server/app/page_client-reference-manifest.js +3 -1
  55. package/.next/server/app-paths-manifest.json +2 -1
  56. package/.next/server/chunks/[externals]__0l8ei7u._.js +3 -0
  57. package/.next/server/chunks/[externals]__0l8ei7u._.js.map +1 -0
  58. package/.next/server/chunks/[root-of-the-server]__1to-yfd._.js +160 -0
  59. package/.next/server/chunks/[root-of-the-server]__1to-yfd._.js.map +1 -0
  60. package/.next/server/chunks/[turbopack]_runtime.js +807 -0
  61. package/.next/server/chunks/[turbopack]_runtime.js.map +10 -0
  62. package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_0g2jjls.js +3 -0
  63. package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_0g2jjls.js.map +1 -0
  64. package/.next/server/chunks/ssr/[root-of-the-server]__0agsrep._.js +28 -0
  65. package/.next/server/chunks/ssr/[root-of-the-server]__0agsrep._.js.map +1 -0
  66. package/.next/server/chunks/ssr/[root-of-the-server]__0f_e7c8._.js +33 -0
  67. package/.next/server/chunks/ssr/[root-of-the-server]__0f_e7c8._.js.map +1 -0
  68. package/.next/server/chunks/ssr/[root-of-the-server]__0yqn68e._.js +3 -0
  69. package/.next/server/chunks/ssr/[root-of-the-server]__0yqn68e._.js.map +1 -0
  70. package/.next/server/chunks/ssr/[root-of-the-server]__112mz-6._.js +14 -0
  71. package/.next/server/chunks/ssr/[root-of-the-server]__112mz-6._.js.map +1 -0
  72. package/.next/server/chunks/ssr/[root-of-the-server]__11_76b7._.js +3 -0
  73. package/.next/server/chunks/ssr/[root-of-the-server]__11_76b7._.js.map +1 -0
  74. package/.next/server/chunks/ssr/[root-of-the-server]__12ovu0c._.js +3 -0
  75. package/.next/server/chunks/ssr/[root-of-the-server]__12ovu0c._.js.map +1 -0
  76. package/.next/server/chunks/ssr/[root-of-the-server]__16pmw__._.js +3 -0
  77. package/.next/server/chunks/ssr/[root-of-the-server]__16pmw__._.js.map +1 -0
  78. package/.next/server/chunks/ssr/[root-of-the-server]__1f2jx51._.js +33 -0
  79. package/.next/server/chunks/ssr/[root-of-the-server]__1f2jx51._.js.map +1 -0
  80. package/.next/server/chunks/ssr/[root-of-the-server]__1rn09vt._.js +3 -0
  81. package/.next/server/chunks/ssr/[root-of-the-server]__1rn09vt._.js.map +1 -0
  82. package/.next/server/chunks/ssr/[root-of-the-server]__20e4gkp._.js +3 -0
  83. package/.next/server/chunks/ssr/[root-of-the-server]__20e4gkp._.js.map +1 -0
  84. package/.next/server/chunks/ssr/[turbopack]_runtime.js +807 -0
  85. package/.next/server/chunks/ssr/[turbopack]_runtime.js.map +10 -0
  86. package/.next/server/chunks/ssr/_1acy_2s._.js +158 -0
  87. package/.next/server/chunks/ssr/_1acy_2s._.js.map +1 -0
  88. package/.next/server/chunks/ssr/_1rh2tf9._.js +367 -0
  89. package/.next/server/chunks/ssr/_1rh2tf9._.js.map +1 -0
  90. package/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0zi5s8-.js +3 -0
  91. package/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0zi5s8-.js.map +1 -0
  92. package/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_0pt47yr.js +3 -0
  93. package/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_0pt47yr.js.map +1 -0
  94. package/.next/server/chunks/ssr/node_modules_01xbj_9._.js +33 -0
  95. package/.next/server/chunks/ssr/node_modules_01xbj_9._.js.map +1 -0
  96. package/.next/server/chunks/ssr/node_modules_next_dist_1c5ky4f._.js +3 -0
  97. package/.next/server/chunks/ssr/node_modules_next_dist_1c5ky4f._.js.map +1 -0
  98. package/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +6 -0
  99. package/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js.map +1 -0
  100. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0wpq8j3._.js +3 -0
  101. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0wpq8j3._.js.map +1 -0
  102. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0symwr9.js +3 -0
  103. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0symwr9.js.map +1 -0
  104. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0q-w892.js +3 -0
  105. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0q-w892.js.map +1 -0
  106. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0l_sp0x.js +3 -0
  107. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0l_sp0x.js.map +1 -0
  108. package/.next/server/chunks/ssr/src_app_loading_tsx_01g_-m0._.js +3 -0
  109. package/.next/server/chunks/ssr/src_app_loading_tsx_01g_-m0._.js.map +1 -0
  110. package/.next/server/middleware-build-manifest.js +24 -1
  111. package/.next/server/middleware-manifest.json +2 -2
  112. package/.next/server/next-font-manifest.js +1 -1
  113. package/.next/server/next-font-manifest.json +15 -1
  114. package/.next/server/pages/404.html +1 -1
  115. package/.next/server/pages/500.html +1 -1
  116. package/.next/server/pages-manifest.json +2 -4
  117. package/.next/server/prefetch-hints.json +25 -0
  118. package/.next/server/server-reference-manifest.js +1 -1
  119. package/.next/server/server-reference-manifest.json +94 -1
  120. package/.next/static/chunks/19mx3mg6lkumu.js +1 -0
  121. package/.next/static/chunks/1las_6j17xa9p.js +1 -0
  122. package/.next/static/chunks/1xk6snnh6e5_j.css +3 -0
  123. package/.next/static/chunks/2-rtuqsgzmno4.js +22 -0
  124. package/.next/static/chunks/27t_qfc-3_lzs.js +1 -0
  125. package/.next/static/chunks/2sswp23h6zh08.js +345 -0
  126. package/.next/static/chunks/310vm2bl3xxpt.js +31 -0
  127. package/.next/static/chunks/3fntmmi971322.js +1 -0
  128. package/.next/static/chunks/turbopack-1b6uko1cu_h42.js +1 -0
  129. package/.next/static/media/4fa387ec64143e14-s.2tuy5pz7dlieh.woff2 +0 -0
  130. package/.next/static/media/53b9e256198e5412-s.390ncx5urfkfu.woff2 +0 -0
  131. package/.next/static/media/5ce348bf30bf5439-s.31988l_ccedte.woff2 +0 -0
  132. package/.next/static/media/6306c77e7c8268e4-s.2dbetqa9o8jxf.woff2 +0 -0
  133. package/.next/static/media/7178b3e590c64307-s.21jp631_3pja2.woff2 +0 -0
  134. package/.next/static/media/797e433ab948586e-s.p.0r6juujl39pe6.woff2 +0 -0
  135. package/.next/static/media/7d817b4c03b0c5f1-s.1uyisp29ctx0d.woff2 +0 -0
  136. package/.next/static/media/8a480f0b521d4e75-s.1qq4vpdcun5oj.woff2 +0 -0
  137. package/.next/static/media/bbc41e54d2fcbd21-s.1rgnod-3esatf.woff2 +0 -0
  138. package/.next/static/media/caa3a2e1cccd8315-s.p.0wgildi0cnwt9.woff2 +0 -0
  139. package/.next/static/media/favicon.2246eezznj2ab.ico +0 -0
  140. package/.next/static/media/fef07dbb0973bf53-s.3p2_lha1f2xer.woff2 +0 -0
  141. package/.next/static/rjdzHqzPw2GCNhDbjcu0-/_buildManifest.js +11 -0
  142. package/.next/static/rjdzHqzPw2GCNhDbjcu0-/_clientMiddlewareManifest.js +1 -0
  143. package/README.md +176 -4
  144. package/bin/pkgviz.ts +57 -29
  145. package/package.json +57 -36
  146. package/src/app/actions/audit.actions.ts +7 -49
  147. package/src/app/globals.css +0 -2
  148. package/src/app/layout.tsx +3 -2
  149. package/src/app/loading.tsx +8 -0
  150. package/src/app/page.tsx +16 -2
  151. package/src/app/utils/buildGraph.ts +99 -100
  152. package/src/app/utils/getParsedFileStructure.ts +128 -54
  153. package/src/app/utils/inspectParserLanguageAsync.ts +39 -0
  154. package/src/app/utils/markCyclicPackages.ts +45 -185
  155. package/src/app/utils/parser/cpp/parseCppFile.ts +68 -86
  156. package/src/app/utils/parser/delphi/parseFile.ts +36 -120
  157. package/src/app/utils/parser/java/findEntryPoint.ts +2 -1
  158. package/src/app/utils/parser/java/parseJavaFile.ts +33 -45
  159. package/src/app/utils/parser/kotlin/parseFile.ts +34 -87
  160. package/src/app/utils/parser/python/parseFile.ts +30 -100
  161. package/src/app/utils/parser/typescript/parseFile.ts +17 -52
  162. package/src/app/utils/selectParserLanguage.ts +39 -0
  163. package/src/cli/formatAuditRuleFailures.ts +21 -0
  164. package/src/cli/runAuditAsync.ts +41 -0
  165. package/src/components/Breadcrumb.tsx +5 -4
  166. package/src/components/ExportPanel.tsx +53 -0
  167. package/src/components/Header.tsx +1 -0
  168. package/src/components/Loader.tsx +8 -2
  169. package/src/components/ProjectLoadError.tsx +15 -0
  170. package/src/components/SettingsPanel.tsx +184 -0
  171. package/src/components/Switch.tsx +9 -17
  172. package/src/components/ThemeToggle.tsx +2 -1
  173. package/src/components/ToggleSwitch.tsx +36 -0
  174. package/src/components/sidebar/Sidebar.tsx +14 -0
  175. package/src/components/sidebar/SidebarBadge.tsx +25 -0
  176. package/src/components/sidebar/SidebarRow.tsx +14 -0
  177. package/src/components/sidebar/SidebarSection.tsx +16 -0
  178. package/src/components/sidebar/SidebarTabs.tsx +58 -0
  179. package/src/contexts/SettingsContext.tsx +19 -20
  180. package/src/features/audit/adapters/inbound/react/AuditRuleList.tsx +76 -0
  181. package/src/features/audit/adapters/inbound/react/AuditRulePanel.tsx +30 -0
  182. package/src/features/audit/adapters/inbound/react/CycleInspector.tsx +116 -0
  183. package/src/features/audit/adapters/inbound/react/CyclicDependenciesRuleDetails.tsx +124 -0
  184. package/src/features/audit/adapters/inbound/react/useCycleSelection.ts +47 -0
  185. package/src/features/audit/application/use-cases/createAuditFromSnapshot.ts +28 -0
  186. package/src/features/audit/application/use-cases/resolveSelectedCycleIds.ts +18 -0
  187. package/src/features/audit/composition/createAuditAsync.ts +11 -0
  188. package/src/features/audit/domain/evaluateAuditRules.ts +42 -0
  189. package/src/features/audit/domain/hasBlockingAuditRuleFailure.ts +6 -0
  190. package/src/features/audit/domain/resolveAuditConfiguration.ts +27 -0
  191. package/src/features/audit/utils/cycleVisualization.ts +86 -0
  192. package/src/features/dependency-analysis/adapters/outbound/dependency-graph/analyzeDependencyImportsAsync.ts +172 -0
  193. package/src/features/dependency-analysis/adapters/outbound/dependency-graph/analyzeTypeScriptImportsAsync.ts +6 -0
  194. package/src/features/graph-view/adapters/inbound/react/DependencyGraphView.tsx +184 -0
  195. package/src/features/graph-view/adapters/inbound/react/GraphZoomControls.tsx +48 -0
  196. package/src/features/graph-view/adapters/inbound/react/applyGraphInteractionPresentation.ts +35 -0
  197. package/src/features/graph-view/adapters/inbound/react/createGraphViewModel.ts +240 -0
  198. package/src/features/graph-view/adapters/inbound/react/createGraphViewStyles.ts +37 -0
  199. package/src/features/graph-view/adapters/inbound/react/useGraphFocus.ts +104 -0
  200. package/src/features/graph-view/adapters/inbound/react/useGraphInteractions.ts +50 -0
  201. package/src/features/graph-view/adapters/inbound/react/useGraphProjection.ts +71 -0
  202. package/src/features/graph-view/adapters/inbound/react/useGraphViewport.ts +48 -0
  203. package/src/features/graph-view/domain/getDependencyHighlightClasses.ts +39 -0
  204. package/src/features/graph-view/domain/reduceGraphInteraction.ts +23 -0
  205. package/src/features/graph-view/utils/createCompoundOpacityIndex.ts +38 -0
  206. package/src/features/graph-view/utils/projectVisibleGraph.ts +94 -0
  207. package/src/features/graph-view/utils/readNodeDefinitionId.ts +7 -0
  208. package/src/features/graph-view/utils/removeEmptyStructuralNodes.ts +27 -0
  209. package/src/features/project-analysis/adapters/outbound/filesystem/readProjectSnapshotAsync.ts +12 -0
  210. package/src/features/project-analysis/application/use-cases/createProjectSnapshotReader.ts +29 -0
  211. package/src/features/project-analysis/composition/loadProjectOverviewAsync.ts +14 -0
  212. package/src/features/project-analysis/composition/loadProjectSnapshotAsync.ts +12 -0
  213. package/src/features/project-tree/adapters/inbound/react/ProjectTreePanel.tsx +52 -0
  214. package/src/features/project-tree/adapters/inbound/react/useProjectTreeExpansion.ts +46 -0
  215. package/src/features/project-tree/application/use-cases/buildProjectTree.ts +61 -0
  216. package/src/features/project-tree/application/use-cases/resolveProjectTreeNavigation.ts +13 -0
  217. package/src/features/project-tree/utils/findProjectTreeNode.ts +14 -0
  218. package/src/features/project-tree/utils/findProjectTreeNodeByGraphPackage.ts +21 -0
  219. package/src/features/project-tree/utils/getProjectTreeAncestorIds.ts +26 -0
  220. package/src/features/settings/utils/readSettingsEnvironment.ts +53 -0
  221. package/src/i18n/en.ts +21 -2
  222. package/src/i18n/i18n.ts +2 -3
  223. package/src/layouts/breadthfirst/style.ts +1 -0
  224. package/src/layouts/circle/style.ts +1 -0
  225. package/src/layouts/concentric/style.ts +1 -0
  226. package/src/layouts/elk/layout.ts +1 -3
  227. package/src/layouts/elk/style.ts +1 -0
  228. package/src/layouts/getWeightBuckets.ts +3 -1
  229. package/src/layouts/grid/style.ts +1 -0
  230. package/src/layouts/index.ts +0 -4
  231. package/src/layouts/style.ts +172 -25
  232. package/src/screens/home/Home.tsx +73 -25
  233. package/src/screens/home/HomeGraph.tsx +43 -0
  234. package/src/screens/home/HomeSidebar.tsx +129 -0
  235. package/src/shared/types/index.ts +0 -9
  236. package/src/shared/utils/getProjectName.ts +4 -1
  237. package/src/shared/utils/parseProjectPath.ts +1 -0
  238. package/src/shared/utils/toPosix.ts +1 -1
  239. package/src/types/audit.ts +76 -0
  240. package/src/types/auditVisualization.ts +22 -0
  241. package/src/types/graphInteraction.ts +9 -0
  242. package/src/types/parserSelection.ts +11 -0
  243. package/src/types/projectAnalysis.ts +20 -0
  244. package/src/types/projectAnalysisActionResult.ts +2 -0
  245. package/src/types/projectTree.ts +7 -0
  246. package/src/utils/filter/filterByPackagePrefix.ts +25 -7
  247. package/src/utils/filter/filterEmptyPackages.ts +28 -29
  248. package/src/utils/filter/filterSubPackagesFromDepth.ts +20 -17
  249. package/src/utils/filter/filterVendorPackages.ts +1 -1
  250. package/src/utils/filter/isIntrinsicGraphNode.ts +8 -0
  251. package/src/utils/filter/toggleCompoundNodes.ts +41 -31
  252. package/src/utils/runProjectAnalysisActionAsync.ts +14 -0
  253. package/tsconfig.json +11 -3
  254. package/.next/app-build-manifest.json +0 -29
  255. package/.next/cache/.previewinfo +0 -1
  256. package/.next/cache/.rscinfo +0 -1
  257. package/.next/cache/.tsbuildinfo +0 -1
  258. package/.next/cache/chrome-devtools-workspace-uuid +0 -1
  259. package/.next/cache/eslint/.cache_6t30dc +0 -1
  260. package/.next/cache/webpack/client-production/0.pack +0 -4
  261. package/.next/cache/webpack/client-production/1.pack +0 -0
  262. package/.next/cache/webpack/client-production/10.pack +0 -0
  263. package/.next/cache/webpack/client-production/11.pack +0 -0
  264. package/.next/cache/webpack/client-production/12.pack +0 -0
  265. package/.next/cache/webpack/client-production/13.pack +0 -0
  266. package/.next/cache/webpack/client-production/14.pack +0 -0
  267. package/.next/cache/webpack/client-production/15.pack +0 -0
  268. package/.next/cache/webpack/client-production/16.pack +0 -0
  269. package/.next/cache/webpack/client-production/2.pack +0 -0
  270. package/.next/cache/webpack/client-production/3.pack +0 -0
  271. package/.next/cache/webpack/client-production/4.pack +0 -0
  272. package/.next/cache/webpack/client-production/5.pack +0 -0
  273. package/.next/cache/webpack/client-production/6.pack +0 -0
  274. package/.next/cache/webpack/client-production/7.pack +0 -0
  275. package/.next/cache/webpack/client-production/8.pack +0 -0
  276. package/.next/cache/webpack/client-production/9.pack +0 -0
  277. package/.next/cache/webpack/client-production/index.pack +0 -0
  278. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  279. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  280. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  281. package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  282. package/.next/cache/webpack/server-production/0.pack +0 -0
  283. package/.next/cache/webpack/server-production/1.pack +0 -0
  284. package/.next/cache/webpack/server-production/10.pack +0 -4
  285. package/.next/cache/webpack/server-production/11.pack +0 -0
  286. package/.next/cache/webpack/server-production/12.pack +0 -0
  287. package/.next/cache/webpack/server-production/13.pack +0 -0
  288. package/.next/cache/webpack/server-production/14.pack +0 -0
  289. package/.next/cache/webpack/server-production/15.pack +0 -0
  290. package/.next/cache/webpack/server-production/16.pack +0 -0
  291. package/.next/cache/webpack/server-production/17.pack +0 -0
  292. package/.next/cache/webpack/server-production/18.pack +0 -4
  293. package/.next/cache/webpack/server-production/2.pack +0 -0
  294. package/.next/cache/webpack/server-production/3.pack +0 -0
  295. package/.next/cache/webpack/server-production/4.pack +0 -0
  296. package/.next/cache/webpack/server-production/5.pack +0 -0
  297. package/.next/cache/webpack/server-production/6.pack +0 -0
  298. package/.next/cache/webpack/server-production/7.pack +0 -0
  299. package/.next/cache/webpack/server-production/8.pack +0 -0
  300. package/.next/cache/webpack/server-production/9.pack +0 -0
  301. package/.next/cache/webpack/server-production/index.pack +0 -0
  302. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  303. package/.next/diagnostics/build-diagnostics.json +0 -6
  304. package/.next/diagnostics/framework.json +0 -1
  305. package/.next/server/app/index.html +0 -1
  306. package/.next/server/app/index.meta +0 -7
  307. package/.next/server/app/index.rsc +0 -20
  308. package/.next/server/chunks/548.js +0 -6
  309. package/.next/server/chunks/610.js +0 -7
  310. package/.next/server/chunks/985.js +0 -22
  311. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  312. package/.next/server/pages/_app.js +0 -1
  313. package/.next/server/pages/_app.js.nft.json +0 -1
  314. package/.next/server/pages/_document.js +0 -1
  315. package/.next/server/pages/_document.js.nft.json +0 -1
  316. package/.next/server/pages/_error.js +0 -19
  317. package/.next/server/pages/_error.js.nft.json +0 -1
  318. package/.next/server/webpack-runtime.js +0 -1
  319. package/.next/static/_YC2p40ZiYT2Gcw-h_tjC/_buildManifest.js +0 -1
  320. package/.next/static/chunks/4bd1b696-cf72ae8a39fa05aa.js +0 -1
  321. package/.next/static/chunks/680-55ad17895c8fb70b.js +0 -1
  322. package/.next/static/chunks/90542734-29e255269788f1bd.js +0 -1
  323. package/.next/static/chunks/91dbc596-b9cdccd3f5cba95d.js +0 -1
  324. package/.next/static/chunks/964-b440f7af22c41ca5.js +0 -1
  325. package/.next/static/chunks/app/_not-found/page-11e4fea8e290f304.js +0 -1
  326. package/.next/static/chunks/app/layout-8aae7fba5ed8afb3.js +0 -1
  327. package/.next/static/chunks/app/page-91450b1230da8bca.js +0 -1
  328. package/.next/static/chunks/framework-bc35dade6ff9684e.js +0 -1
  329. package/.next/static/chunks/main-9e86d333c1e02dd1.js +0 -1
  330. package/.next/static/chunks/main-app-69a9ff75ff3465ca.js +0 -1
  331. package/.next/static/chunks/pages/_app-0a0020ddd67f79cf.js +0 -1
  332. package/.next/static/chunks/pages/_error-03529f2c21436739.js +0 -1
  333. package/.next/static/chunks/webpack-7adfafc806738767.js +0 -1
  334. package/.next/static/css/ec31779168107a11.css +0 -3
  335. package/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  336. package/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  337. package/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  338. package/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  339. package/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  340. package/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  341. package/.next/trace +0 -2
  342. package/.next/types/app/layout.ts +0 -84
  343. package/.next/types/app/page.ts +0 -84
  344. package/.next/types/cache-life.d.ts +0 -141
  345. package/.next/types/package.json +0 -1
  346. package/src/app/actions/graph.actions.ts +0 -25
  347. package/src/app/utils/parser/cpp/extractCppPackageFromImport.ts +0 -18
  348. package/src/app/utils/parser/delphi/extractPackageFromImport.ts +0 -21
  349. package/src/app/utils/parser/java/extractJavaPackageFromImport.ts +0 -39
  350. package/src/app/utils/parser/java/getIntrinsicPackagesRecursive.ts +0 -33
  351. package/src/app/utils/parser/kotlin/extractPackageFromImport.ts +0 -19
  352. package/src/app/utils/parser/python/extractPythonPackageFromImport.ts +0 -18
  353. package/src/app/utils/parser/typescript/extractTypeScriptPackageFromImport.ts +0 -18
  354. package/src/components/Cytoscape.tsx +0 -23
  355. package/src/components/Setting.tsx +0 -17
  356. package/src/components/Settings.tsx +0 -189
  357. package/src/components/ZoomInput.tsx +0 -94
  358. package/src/components/useCytoscape.ts +0 -343
  359. package/src/layouts/getLayoutStyle.ts +0 -19
  360. package/src/shared/utils/detectLanguage.ts +0 -255
  361. package/src/shared/utils/getJsonAsync.ts +0 -13
  362. package/src/shared/utils/parseEnv.ts +0 -91
  363. package/src/store/useLocalStorage.ts +0 -29
  364. package/src/utils/hasChildren.ts +0 -7
  365. /package/.next/{react-loadable-manifest.json → server/app/_global-error/page/react-loadable-manifest.json} +0 -0
  366. /package/.next/static/chunks/{polyfills-42372ed130431b0a.js → 0cz1d0mv5g_q7.js} +0 -0
  367. /package/.next/static/{_YC2p40ZiYT2Gcw-h_tjC → rjdzHqzPw2GCNhDbjcu0-}/_ssgManifest.js +0 -0
@@ -1,9 +1,15 @@
1
- import React from 'react';
2
1
  import { LucideLoader } from 'lucide-react';
2
+ import React from 'react';
3
3
 
4
+ /*** Renders the loading indicator. */
4
5
  export default function Loader() {
5
6
  return (
6
- <div data-testid="loader" className="h-full flex items-center justify-center">
7
+ <div
8
+ data-testid="loader"
9
+ role="status"
10
+ aria-label="Loading"
11
+ className="flex min-h-0 min-w-0 flex-1 items-center justify-center"
12
+ >
7
13
  <LucideLoader />
8
14
  </div>
9
15
  );
@@ -0,0 +1,15 @@
1
+ interface ProjectLoadErrorProps {
2
+ readonly message: string;
3
+ }
4
+
5
+ /*** Renders a persistent project-loading error without taking down the surrounding PKGViz UI. */
6
+ export default function ProjectLoadError({ message }: ProjectLoadErrorProps) {
7
+ return (
8
+ <div role="alert" className="flex flex-1 items-center justify-center p-6">
9
+ <div className="max-w-xl rounded-md border border-red-300 bg-red-50 p-4 dark:border-red-900 dark:bg-red-950">
10
+ <p className="text-sm font-medium text-red-800 dark:text-red-200">Unable to load project</p>
11
+ <p className="mt-1 break-all text-xs text-red-700 dark:text-red-300">{message}</p>
12
+ </div>
13
+ </div>
14
+ );
15
+ }
@@ -0,0 +1,184 @@
1
+ 'use client';
2
+ import { ChevronDownIcon } from 'lucide-react';
3
+ import { Select, Slider } from 'radix-ui';
4
+ import React from 'react';
5
+
6
+ import { SidebarRow } from '@/components/sidebar/SidebarRow';
7
+ import { SidebarSection } from '@/components/sidebar/SidebarSection';
8
+ import Switch from '@/components/Switch';
9
+ import { useSettings } from '@/contexts/SettingsContext';
10
+ import { t } from '@/i18n/i18n';
11
+
12
+ /*** Renders the persistent first-rank graph controls below secondary sidebar tools. */
13
+ export function SettingsPanel() {
14
+ return (
15
+ <>
16
+ <FilterSettings />
17
+ <SubPackageDepthSettings />
18
+ <LayoutSettings />
19
+ <LayoutSpacingSettings />
20
+ </>
21
+ );
22
+ }
23
+
24
+ /*** Renders package-visibility settings. */
25
+ function FilterSettings() {
26
+ const {
27
+ showCompoundNodes,
28
+ showVendorPackages,
29
+ toggleShowCompoundNodes,
30
+ toggleShowVendorPackages,
31
+ } = useSettings();
32
+
33
+ return (
34
+ <SidebarSection title={t('settings.filter')}>
35
+ <SidebarRow>
36
+ <Switch
37
+ id="switch-show-vendor-packages"
38
+ label={t('settings.showVendorPackages')}
39
+ onToggle={toggleShowVendorPackages}
40
+ value={showVendorPackages}
41
+ />
42
+ </SidebarRow>
43
+ <SidebarRow>
44
+ <Switch
45
+ id="switch-show-compound-nodes"
46
+ label={t('settings.showCompoundNodes')}
47
+ onToggle={toggleShowCompoundNodes}
48
+ value={showCompoundNodes}
49
+ />
50
+ </SidebarRow>
51
+ </SidebarSection>
52
+ );
53
+ }
54
+
55
+ /*** Renders the visible subpackage-depth control. */
56
+ function SubPackageDepthSettings() {
57
+ const { maxSubPackageDepth, setSubPackageDepth, subPackageDepth } = useSettings();
58
+
59
+ return (
60
+ <SidebarSection title={t('settings.subPackageDepth') + ': ' + subPackageDepth}>
61
+ <SidebarRow>
62
+ <SettingsSlider
63
+ ariaLabel={t('settings.subPackageDepth')}
64
+ max={maxSubPackageDepth}
65
+ min={1}
66
+ step={1}
67
+ value={subPackageDepth}
68
+ onValueChange={setSubPackageDepth}
69
+ />
70
+ </SidebarRow>
71
+ </SidebarSection>
72
+ );
73
+ }
74
+
75
+ /*** Renders the active Cytoscape layout selector. */
76
+ function LayoutSettings() {
77
+ const { cytoscapeLayout, setCytoscapeLayout } = useSettings();
78
+
79
+ /*** Lets the Select close immediately before starting a potentially expensive graph layout. */
80
+ const selectLayout = (layout: string) => {
81
+ if (
82
+ layout !== 'breadthfirst' &&
83
+ layout !== 'circle' &&
84
+ layout !== 'concentric' &&
85
+ layout !== 'elk' &&
86
+ layout !== 'grid'
87
+ ) {
88
+ return;
89
+ }
90
+
91
+ React.startTransition(() => setCytoscapeLayout(layout));
92
+ };
93
+
94
+ return (
95
+ <SidebarSection title={t('settings.layout')}>
96
+ <SidebarRow>
97
+ <Select.Root value={cytoscapeLayout} onValueChange={selectLayout}>
98
+ <Select.Trigger
99
+ aria-label={t('cytoscapeLayout')}
100
+ className="inline-flex h-9 w-full items-center justify-between rounded-md border px-3"
101
+ >
102
+ <Select.Value className="text-foreground" />
103
+ <Select.Icon>
104
+ <ChevronDownIcon size={16} />
105
+ </Select.Icon>
106
+ </Select.Trigger>
107
+ <Select.Portal>
108
+ <Select.Content
109
+ position="popper"
110
+ side="bottom"
111
+ align="start"
112
+ sideOffset={6}
113
+ className="z-50 min-w-(--radix-select-trigger-width) rounded-md border bg-white shadow-md dark:bg-neutral-900"
114
+ >
115
+ <Select.Viewport className="p-1">
116
+ <Select.Group>
117
+ {['breadthfirst', 'circle', 'concentric', 'elk', 'grid'].map(layout => (
118
+ <Select.Item
119
+ key={layout}
120
+ value={layout}
121
+ textValue={t(layout)}
122
+ className="rounded px-2 py-1.5 hover:bg-neutral-100 dark:hover:bg-neutral-800"
123
+ >
124
+ <Select.ItemText>{t(layout)}</Select.ItemText>
125
+ </Select.Item>
126
+ ))}
127
+ </Select.Group>
128
+ </Select.Viewport>
129
+ </Select.Content>
130
+ </Select.Portal>
131
+ </Select.Root>
132
+ </SidebarRow>
133
+ </SidebarSection>
134
+ );
135
+ }
136
+
137
+ /*** Renders the active layout-spacing control. */
138
+ function LayoutSpacingSettings() {
139
+ const { cytoscapeLayoutSpacing, setCytoscapeLayoutSpacing } = useSettings();
140
+
141
+ return (
142
+ <SidebarSection title={t('settings.layoutSpacing') + ': ' + cytoscapeLayoutSpacing.toFixed(2)}>
143
+ <SidebarRow>
144
+ <SettingsSlider
145
+ ariaLabel={t('settings.layoutSpacing')}
146
+ max={1}
147
+ min={0.1}
148
+ step={0.01}
149
+ value={cytoscapeLayoutSpacing}
150
+ onValueChange={setCytoscapeLayoutSpacing}
151
+ />
152
+ </SidebarRow>
153
+ </SidebarSection>
154
+ );
155
+ }
156
+
157
+ /*** Renders the shared slider shape used by numeric sidebar settings. */
158
+ function SettingsSlider({ ariaLabel, max, min, onValueChange, step, value }: SettingsSliderProps) {
159
+ return (
160
+ <Slider.Root
161
+ min={min}
162
+ max={max}
163
+ step={step}
164
+ value={[value]}
165
+ onValueChange={([nextValue]) => onValueChange(nextValue)}
166
+ aria-label={ariaLabel}
167
+ className="relative flex h-5 w-full touch-none select-none items-center"
168
+ >
169
+ <Slider.Track className="relative h-1.5 grow rounded-full bg-neutral-200 dark:bg-neutral-800">
170
+ <Slider.Range className="absolute h-1.5 rounded-full bg-blue-600 dark:bg-blue-400" />
171
+ </Slider.Track>
172
+ <Slider.Thumb className="block h-4 w-4 rounded-full border border-neutral-300 bg-white shadow focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-neutral-600 dark:bg-neutral-100" />
173
+ </Slider.Root>
174
+ );
175
+ }
176
+
177
+ interface SettingsSliderProps {
178
+ readonly ariaLabel: string;
179
+ readonly max: number;
180
+ readonly min: number;
181
+ readonly onValueChange: (value: number) => void;
182
+ readonly step: number;
183
+ readonly value: number;
184
+ }
@@ -1,25 +1,17 @@
1
1
  'use client';
2
2
  import React from 'react';
3
- import { Switch } from 'radix-ui';
4
3
 
4
+ import { ToggleSwitch } from '@/components/ToggleSwitch';
5
+
6
+ /*** Renders a labeled settings switch. */
5
7
  export default function RadixSwitch({ id, label, value, onToggle }: ISwitch) {
6
8
  return (
7
- <form>
8
- <div className="flex items-center justify-between h-[20px]">
9
- <label className="pr-[15px] leading-none text-foreground whitespace-nowrap" htmlFor={id}>
10
- {label}
11
- </label>
12
- <Switch.Root
13
- className="relative h-[18px] w-[42px] cursor-default rounded-full bg-neutral-200 dark:bg-neutral-800 outline-none data-[state=checked]:bg-neutral-200 dark:data-[state=checked]:bg-gray-700"
14
- defaultChecked={value}
15
- id={id}
16
- checked={value}
17
- onCheckedChange={onToggle}
18
- >
19
- <Switch.Thumb className="block size-[16px] translate-x-0.5 rounded-full bg-neutral-500 dark:bg-neutral-500 transition-transform duration-100 will-change-transform data-[state=checked]:bg-blue-500 dark:data-[state=checked]:bg-white data-[state=checked]:translate-x-[24px]" />
20
- </Switch.Root>
21
- </div>
22
- </form>
9
+ <div className="flex h-[20px] items-center justify-between">
10
+ <label className="pr-[15px] leading-none whitespace-nowrap text-foreground" htmlFor={id}>
11
+ {label}
12
+ </label>
13
+ <ToggleSwitch ariaLabel={label} id={id} onToggle={onToggle} value={value} />
14
+ </div>
23
15
  );
24
16
  }
25
17
 
@@ -1,7 +1,8 @@
1
1
  'use client';
2
- import { useEffect, useState } from 'react';
3
2
  import { useTheme } from 'next-themes';
3
+ import { useEffect, useState } from 'react';
4
4
 
5
+ /*** Renders the light and dark theme toggle. */
5
6
  export default function ThemeToggle() {
6
7
  const { theme, setTheme, resolvedTheme } = useTheme();
7
8
  const [mounted, setMounted] = useState(false);
@@ -0,0 +1,36 @@
1
+ 'use client';
2
+ import { Switch } from 'radix-ui';
3
+ import React from 'react';
4
+
5
+ /*** Renders a switch control without imposing row or label layout. */
6
+ export function ToggleSwitch({ ariaLabel, checkedColor, id, onToggle, value }: ToggleSwitchProps) {
7
+ return (
8
+ <Switch.Root
9
+ aria-label={ariaLabel}
10
+ className="relative h-[18px] w-[42px] shrink-0 cursor-pointer rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-neutral-200 dark:bg-neutral-800 dark:data-[state=checked]:bg-gray-700"
11
+ id={id}
12
+ checked={value}
13
+ onCheckedChange={onToggle}
14
+ >
15
+ {value && checkedColor ? (
16
+ <span
17
+ aria-hidden="true"
18
+ className="pointer-events-none absolute inset-0 rounded-full opacity-25"
19
+ style={{ backgroundColor: checkedColor }}
20
+ />
21
+ ) : null}
22
+ <Switch.Thumb
23
+ className="relative block size-[16px] translate-x-0.5 rounded-full bg-neutral-500 transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[24px] data-[state=checked]:bg-blue-500 dark:bg-neutral-500 dark:data-[state=checked]:bg-white"
24
+ style={value && checkedColor ? { backgroundColor: checkedColor } : undefined}
25
+ />
26
+ </Switch.Root>
27
+ );
28
+ }
29
+
30
+ interface ToggleSwitchProps {
31
+ readonly ariaLabel: string;
32
+ readonly checkedColor?: string;
33
+ readonly id: string;
34
+ readonly value: boolean;
35
+ readonly onToggle: () => void;
36
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+
3
+ /*** Renders the fixed-width application sidebar shell. */
4
+ export function Sidebar({ children }: SidebarProps) {
5
+ return (
6
+ <aside className="flex min-h-0 w-[18rem] min-w-[18rem] max-w-[18rem] shrink-0 self-stretch flex-col overflow-hidden border-r border-r-neutral-200 bg-neutral-100 md:pt-14 dark:border-r-neutral-800 dark:bg-neutral-950">
7
+ {children}
8
+ </aside>
9
+ );
10
+ }
11
+
12
+ interface SidebarProps {
13
+ readonly children: React.ReactNode;
14
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+
3
+ /*** Renders a compact circular count badge for sidebar findings and secondary tabs. */
4
+ export function SidebarBadge({ count, tone = 'neutral' }: SidebarBadgeProps) {
5
+ const toneClass =
6
+ tone === 'danger'
7
+ ? 'border-red-300 bg-red-50 text-red-700 dark:border-red-900 dark:bg-red-950 dark:text-red-300'
8
+ : 'border-neutral-300 text-neutral-700 dark:border-neutral-700 dark:text-neutral-200';
9
+
10
+ return (
11
+ <span
12
+ className={
13
+ 'inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full border px-1 text-[10px] font-semibold leading-none ' +
14
+ toneClass
15
+ }
16
+ >
17
+ {count}
18
+ </span>
19
+ );
20
+ }
21
+
22
+ interface SidebarBadgeProps {
23
+ readonly count: number;
24
+ readonly tone?: 'danger' | 'neutral';
25
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+
3
+ /*** Renders one consistently styled row inside an application-sidebar section. */
4
+ export function SidebarRow({ children }: SidebarRowProps) {
5
+ return (
6
+ <div className="ml-3 border-b border-b-neutral-200 bg-white px-3 py-2 dark:border-b-neutral-800 dark:bg-neutral-900">
7
+ {children}
8
+ </div>
9
+ );
10
+ }
11
+
12
+ interface SidebarRowProps {
13
+ readonly children: React.ReactNode;
14
+ }
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+
3
+ /*** Renders a non-collapsible application-sidebar category. */
4
+ export function SidebarSection({ children, title }: SidebarSectionProps) {
5
+ return (
6
+ <section>
7
+ <h3 className="mx-6 mt-6 mb-2 text-sm font-bold">{title}</h3>
8
+ {children}
9
+ </section>
10
+ );
11
+ }
12
+
13
+ interface SidebarSectionProps {
14
+ readonly children: React.ReactNode;
15
+ readonly title: React.ReactNode;
16
+ }
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+ import { Tabs } from 'radix-ui';
3
+ import React from 'react';
4
+
5
+ import { SidebarBadge } from '@/components/sidebar/SidebarBadge';
6
+
7
+ /*** Renders reusable secondary sidebar views above the persistent graph settings. */
8
+ export function SidebarTabs({ ariaLabel, onValueChange, tabs, value }: SidebarTabsProps) {
9
+ return (
10
+ <Tabs.Root
11
+ value={value ?? ''}
12
+ onValueChange={onValueChange}
13
+ className="flex min-h-0 flex-1 flex-col overflow-hidden"
14
+ >
15
+ <Tabs.List
16
+ aria-label={ariaLabel}
17
+ className="ml-3 flex shrink-0 border-b border-neutral-200 dark:border-neutral-800"
18
+ >
19
+ {tabs.map(tab => (
20
+ <Tabs.Trigger
21
+ key={tab.id}
22
+ value={tab.id}
23
+ disabled={tab.disabled}
24
+ className="flex flex-1 items-center justify-center gap-1.5 border-b-2 border-transparent px-3 py-2 text-xs text-neutral-500 disabled:cursor-default disabled:opacity-40 data-[state=active]:border-blue-600 data-[state=active]:text-neutral-900 dark:text-neutral-400 dark:data-[state=active]:border-blue-400 dark:data-[state=active]:text-neutral-100"
25
+ >
26
+ <span>{tab.label}</span>
27
+ {tab.badge ? <SidebarBadge count={tab.badge} tone={tab.badgeTone} /> : null}
28
+ </Tabs.Trigger>
29
+ ))}
30
+ </Tabs.List>
31
+ {tabs.map(tab => (
32
+ <Tabs.Content
33
+ key={tab.id}
34
+ value={tab.id}
35
+ className="min-h-0 flex-1 overflow-y-auto border-b border-neutral-200 pb-3 outline-none dark:border-neutral-800"
36
+ >
37
+ {tab.content}
38
+ </Tabs.Content>
39
+ ))}
40
+ </Tabs.Root>
41
+ );
42
+ }
43
+
44
+ interface SidebarTab {
45
+ readonly badge?: number;
46
+ readonly badgeTone?: 'danger' | 'neutral';
47
+ readonly content: React.ReactNode;
48
+ readonly disabled?: boolean;
49
+ readonly id: string;
50
+ readonly label: React.ReactNode;
51
+ }
52
+
53
+ interface SidebarTabsProps {
54
+ readonly ariaLabel: string;
55
+ readonly onValueChange: (value: string) => void;
56
+ readonly tabs: readonly SidebarTab[];
57
+ readonly value: string | null;
58
+ }
@@ -1,46 +1,40 @@
1
+ import { useLocalStorage } from 'ankh-hooks/store';
1
2
  import type { LayoutOptions } from 'cytoscape';
3
+ import React, { createContext, type PropsWithChildren, use } from 'react';
2
4
 
3
- import React, { createContext, use, type PropsWithChildren } from 'react';
4
- import {
5
- getCytoscapeLayout,
6
- getCytoscapeLayoutSpacing,
7
- getShowCompoundNodes,
8
- getShowVendorPackages,
9
- getSubPackageDepth,
10
- } from '@/shared/utils/parseEnv';
11
- import { useLocalStorage } from '@/store/useLocalStorage';
5
+ import { readSettingsEnvironment } from '@/features/settings/utils/readSettingsEnvironment';
12
6
 
13
7
  // Settings context
14
8
  const SettingsContext = createContext<ISettingsContext | null>(null);
15
9
 
16
- // Settings provider
10
+ /*** Provides persisted graph settings to the application. */
17
11
  export const SettingsProvider = ({ children }: PropsWithChildren) => {
18
- const [maxSubPackageDepth, setMaxSubPackageDepth] = useLocalStorage<number>(
19
- 'maxSubPackageDepth',
20
- 1
21
- );
12
+ const environment = readSettingsEnvironment();
13
+ const [maxSubPackageDepth, setMaxSubPackageDepth] = useMaxSubPackageDepthSetting();
22
14
  const [showCompoundNodes, setShowCompoundNodes] = useLocalStorage<boolean>(
23
15
  'showCompoundNodes',
24
- getShowCompoundNodes()
16
+ environment.showCompoundNodes
25
17
  );
26
18
  const [showVendorPackages, setShowVendorPackages] = useLocalStorage<boolean>(
27
19
  'showVendorPackages',
28
- getShowVendorPackages()
20
+ environment.showVendorPackages
29
21
  );
30
22
  const [subPackageDepth, setSubPackageDepth] = useLocalStorage<number>(
31
23
  'subPackageDepth',
32
- getSubPackageDepth() || 1
24
+ environment.subPackageDepth
33
25
  );
34
26
  const [cytoscapeLayout, setCytoscapeLayout] = useLocalStorage<LayoutOptions['name']>(
35
27
  'cytoscapeLayout',
36
- getCytoscapeLayout()
28
+ environment.cytoscapeLayout
37
29
  );
38
30
  const [cytoscapeLayoutSpacing, setCytoscapeLayoutSpacing] = useLocalStorage<number>(
39
31
  'cytoscapeLayoutSpacing',
40
- getCytoscapeLayoutSpacing()
32
+ environment.cytoscapeLayoutSpacing
41
33
  );
42
34
 
35
+ /*** Toggles vendor package visibility. */
43
36
  const toggleShowVendorPackages = () => setShowVendorPackages(prev => !prev);
37
+ /*** Toggles compound node visibility. */
44
38
  const toggleShowCompoundNodes = () => setShowCompoundNodes(prev => !prev);
45
39
 
46
40
  return (
@@ -65,7 +59,12 @@ export const SettingsProvider = ({ children }: PropsWithChildren) => {
65
59
  );
66
60
  };
67
61
 
68
- // Hook to read/update settings
62
+ /*** Owns the persisted maximum package-depth state. */
63
+ function useMaxSubPackageDepthSetting() {
64
+ return useLocalStorage<number>('maxSubPackageDepth', 1);
65
+ }
66
+
67
+ /*** Returns the current graph settings context. */
69
68
  export function useSettings() {
70
69
  const context = use(SettingsContext);
71
70
  if (!context) throw new Error('useSettings() must be used within a SettingsProvider');
@@ -0,0 +1,76 @@
1
+ 'use client';
2
+ import React from 'react';
3
+
4
+ import { SidebarRow } from '@/components/sidebar/SidebarRow';
5
+ import { SidebarSection } from '@/components/sidebar/SidebarSection';
6
+ import { CyclicDependenciesRuleDetails } from '@/features/audit/adapters/inbound/react/CyclicDependenciesRuleDetails';
7
+ import type { Audit, AuditRuleResult } from '@/types/audit';
8
+ import type { CycleInspection, CycleSelection } from '@/types/auditVisualization';
9
+
10
+ /*** Renders only violated audit rules in the active Rules sidebar tab. */
11
+ export function AuditRuleList({
12
+ evaluation,
13
+ cycleSelection,
14
+ inspectedCycleId,
15
+ onCycleInspectionChange,
16
+ }: AuditRuleListProps) {
17
+ return (
18
+ <>
19
+ {evaluation.rules
20
+ .filter(rule => rule.status === 'failed')
21
+ .map(rule => (
22
+ <RuleDetails
23
+ key={rule.id}
24
+ evaluation={evaluation}
25
+ rule={rule}
26
+ cycleSelection={cycleSelection}
27
+ inspectedCycleId={inspectedCycleId}
28
+ onCycleInspectionChange={onCycleInspectionChange}
29
+ />
30
+ ))}
31
+ </>
32
+ );
33
+ }
34
+
35
+ /*** Dispatches one violated rule to its dedicated sidebar renderer. */
36
+ function RuleDetails({
37
+ evaluation,
38
+ cycleSelection,
39
+ inspectedCycleId,
40
+ onCycleInspectionChange,
41
+ rule,
42
+ }: RuleDetailsProps) {
43
+ if (rule.id === 'cyclic-dependencies') {
44
+ return (
45
+ <CyclicDependenciesRuleDetails
46
+ cycles={evaluation.cyclicPackages}
47
+ cycleSelection={cycleSelection}
48
+ inspectedCycleId={inspectedCycleId}
49
+ onCycleInspectionChange={onCycleInspectionChange}
50
+ />
51
+ );
52
+ }
53
+
54
+ return (
55
+ <SidebarSection title={rule.id}>
56
+ {rule.details.map((detail, index) => (
57
+ <SidebarRow key={detail + ':' + index}>
58
+ <code className="block truncate text-[11px]" title={detail}>
59
+ {detail}
60
+ </code>
61
+ </SidebarRow>
62
+ ))}
63
+ </SidebarSection>
64
+ );
65
+ }
66
+
67
+ interface AuditRuleListProps {
68
+ readonly inspectedCycleId?: string | null;
69
+ readonly evaluation: Audit['evaluation'];
70
+ readonly cycleSelection: CycleSelection;
71
+ readonly onCycleInspectionChange: (inspection: CycleInspection | null) => void;
72
+ }
73
+
74
+ interface RuleDetailsProps extends AuditRuleListProps {
75
+ readonly rule: AuditRuleResult;
76
+ }
@@ -0,0 +1,30 @@
1
+ 'use client';
2
+ import React from 'react';
3
+
4
+ import { AuditRuleList } from '@/features/audit/adapters/inbound/react/AuditRuleList';
5
+ import type { Audit } from '@/types/audit';
6
+ import type { CycleInspection, CycleSelection } from '@/types/auditVisualization';
7
+
8
+ /*** Renders violated audit rules while the Rules sidebar tab is active. */
9
+ export function AuditRulePanel({
10
+ evaluation,
11
+ cycleSelection,
12
+ inspectedCycleId,
13
+ onCycleInspectionChange,
14
+ }: AuditRulePanelProps) {
15
+ return (
16
+ <AuditRuleList
17
+ evaluation={evaluation}
18
+ cycleSelection={cycleSelection}
19
+ inspectedCycleId={inspectedCycleId}
20
+ onCycleInspectionChange={onCycleInspectionChange}
21
+ />
22
+ );
23
+ }
24
+
25
+ interface AuditRulePanelProps {
26
+ readonly inspectedCycleId?: string | null;
27
+ readonly evaluation: Audit['evaluation'];
28
+ readonly cycleSelection: CycleSelection;
29
+ readonly onCycleInspectionChange: (inspection: CycleInspection | null) => void;
30
+ }