pkgviz 0.7.6 → 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/5HapMtlwNHlJNa0mJQNsK/_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/{5HapMtlwNHlJNa0mJQNsK → rjdzHqzPw2GCNhDbjcu0-}/_ssgManifest.js +0 -0
@@ -0,0 +1,6 @@
1
+ import { analyzeDependencyImportsAsync } from './analyzeDependencyImportsAsync';
2
+
3
+ /*** Returns PKGViz imports indexed by source file from the canonical TypeScript analyzer. */
4
+ export async function analyzeTypeScriptImportsAsync(projectRoot: string) {
5
+ return analyzeDependencyImportsAsync(projectRoot);
6
+ }
@@ -0,0 +1,184 @@
1
+ 'use client';
2
+
3
+ import { GraphView, type GraphViewElementEvent, type GraphViewLayoutName } from '@zora/graph-view';
4
+ import type { ElementsDefinition, LayoutOptions } from 'cytoscape';
5
+ import { useTheme } from 'next-themes';
6
+ import React, { useMemo } from 'react';
7
+
8
+ import { useSettings } from '@/contexts/SettingsContext';
9
+ import { createGraphViewModel } from '@/features/graph-view/adapters/inbound/react/createGraphViewModel';
10
+ import { createGraphViewStyles } from '@/features/graph-view/adapters/inbound/react/createGraphViewStyles';
11
+ import { GraphZoomControls } from '@/features/graph-view/adapters/inbound/react/GraphZoomControls';
12
+ import { useGraphFocus } from '@/features/graph-view/adapters/inbound/react/useGraphFocus';
13
+ import { useGraphInteractions } from '@/features/graph-view/adapters/inbound/react/useGraphInteractions';
14
+ import { useGraphProjection } from '@/features/graph-view/adapters/inbound/react/useGraphProjection';
15
+ import { useGraphViewport } from '@/features/graph-view/adapters/inbound/react/useGraphViewport';
16
+ import { LAYOUTS } from '@/layouts/constants';
17
+ import { getCanvasBg } from '@/layouts/style';
18
+ import type { CycleHighlight } from '@/types/auditVisualization';
19
+
20
+ /*** Renders PKGViz graph policy through the materialized ZORA GraphView runtime. */
21
+ export function DependencyGraphView(props: DependencyGraphViewProps) {
22
+ const settings = useSettings();
23
+ const { resolvedTheme } = useTheme();
24
+ const theme = resolvedTheme === 'dark' ? 'dark' : 'light';
25
+ const visibleElements = useGraphProjection({
26
+ currentPackage: props.currentPackage,
27
+ elements: props.packageGraph,
28
+ preservePackageScope: props.cycleHighlights.length > 0,
29
+ setCurrentPackage: props.setCurrentPackage,
30
+ setMaxSubPackageDepth: settings.setMaxSubPackageDepth,
31
+ setSubPackageDepth: settings.setSubPackageDepth,
32
+ showCompoundNodes: settings.showCompoundNodes,
33
+ showVendorPackages: settings.showVendorPackages,
34
+ subPackageDepth: settings.subPackageDepth,
35
+ });
36
+
37
+ useGraphFocus({
38
+ cycleHighlights: props.cycleHighlights,
39
+ currentPackage: props.currentPackage,
40
+ setCurrentPackage: props.setCurrentPackage,
41
+ setSubPackageDepth: settings.setSubPackageDepth,
42
+ subPackageDepth: settings.subPackageDepth,
43
+ visibleElements,
44
+ });
45
+
46
+ const presentation = useGraphViewPresentation({
47
+ cycleHighlights: props.cycleHighlights,
48
+ layout: settings.cytoscapeLayout,
49
+ packageGraph: props.packageGraph,
50
+ theme,
51
+ visibleElements,
52
+ });
53
+ if (presentation.model === null) return null;
54
+
55
+ return (
56
+ <DependencyGraphCanvas
57
+ {...props}
58
+ layout={presentation.layout}
59
+ layoutOptions={presentation.layoutOptions}
60
+ model={presentation.model}
61
+ onSpacingFactorChange={settings.setCytoscapeLayoutSpacing}
62
+ spacingFactor={settings.cytoscapeLayoutSpacing}
63
+ styles={presentation.styles}
64
+ theme={theme}
65
+ />
66
+ );
67
+ }
68
+
69
+ /***
70
+ * Memoizes GraphView model, style, and layout inputs from the active PKGViz projection.
71
+ * @performance
72
+ * Stable inputs avoid repeated projection work and let the owner distinguish presentation from
73
+ * topology/layout changes. Do not recreate these objects on unrelated renders or add compensating
74
+ * layout effects here. ZORA owns runtime reconciliation; memoization alone is not a correctness gate.
75
+ */
76
+ function useGraphViewPresentation(input: GraphViewPresentationInput) {
77
+ const model = useMemo(
78
+ () =>
79
+ input.visibleElements === null
80
+ ? null
81
+ : createGraphViewModel(input.packageGraph, input.visibleElements, input.cycleHighlights),
82
+ [input.cycleHighlights, input.packageGraph, input.visibleElements]
83
+ );
84
+ const styles = useMemo(
85
+ () =>
86
+ input.visibleElements === null
87
+ ? []
88
+ : createGraphViewStyles(input.visibleElements, input.theme, input.layout),
89
+ [input.layout, input.theme, input.visibleElements]
90
+ );
91
+ const layoutOptions = useMemo<Readonly<Record<string, unknown>>>(
92
+ () => ({ ...LAYOUTS[input.layout] }),
93
+ [input.layout]
94
+ );
95
+
96
+ return {
97
+ layout: readGraphViewLayout(input.layout),
98
+ layoutOptions,
99
+ model,
100
+ styles,
101
+ };
102
+ }
103
+
104
+ /*** Owns GraphView controller callbacks and renders the viewport plus zoom controls. */
105
+ function DependencyGraphCanvas(props: DependencyGraphCanvasProps) {
106
+ const viewport = useGraphViewport(props.layout);
107
+ const interactions = useGraphInteractions(props.model.nodes, props.model.edges);
108
+
109
+ /*** Handles structural graph navigation without touching the rendering engine. */
110
+ const handleNodeEvent = (event: GraphViewElementEvent) => {
111
+ interactions.handleNodeEvent(event);
112
+ if (event.type !== 'double-press' || !props.model.parentNodeIds.has(event.id)) return;
113
+ props.setCurrentPackage(event.id);
114
+ };
115
+
116
+ return (
117
+ <div className="flex min-h-0 min-w-0 flex-1 flex-col gap-2 overflow-hidden px-8">
118
+ <div className="relative min-h-0 flex-1 overflow-hidden">
119
+ <GraphView
120
+ edges={interactions.edges}
121
+ layout={props.layout}
122
+ layoutOptions={props.layoutOptions}
123
+ maxZoom={2}
124
+ minZoom={0.5}
125
+ minReadableLabelSize={24}
126
+ maxFitLabelSize={24}
127
+ nodes={interactions.nodes}
128
+ zoomMode="fit-relative"
129
+ sizeNodesToLabels
130
+ onLayoutComplete={viewport.handleLayoutComplete}
131
+ onNodeEvent={handleNodeEvent}
132
+ onReady={viewport.handleReady}
133
+ onViewportChange={viewport.handleViewportChange}
134
+ onSpacingFactorChange={props.onSpacingFactorChange}
135
+ spacingFactor={props.spacingFactor}
136
+ style={{ background: getCanvasBg(props.theme) }}
137
+ styleRules={props.styles}
138
+ />
139
+ {props.overlay}
140
+ </div>
141
+ <GraphZoomControls
142
+ controller={viewport.controller}
143
+ maxZoom={viewport.max}
144
+ minZoom={viewport.min}
145
+ zoom={viewport.zoom}
146
+ />
147
+ </div>
148
+ );
149
+ }
150
+
151
+ /*** Narrows persisted Cytoscape layout names to the layouts supported by ZORA GraphView. */
152
+ function readGraphViewLayout(layout: LayoutOptions['name']): GraphViewLayoutName {
153
+ if (layout === 'breadthfirst') return 'breadthfirst';
154
+ if (layout === 'circle') return 'circle';
155
+ if (layout === 'elk') return 'elk';
156
+ if (layout === 'grid') return 'grid';
157
+ return 'concentric';
158
+ }
159
+
160
+ interface DependencyGraphViewProps {
161
+ readonly currentPackage: string;
162
+ readonly packageGraph: ElementsDefinition;
163
+ readonly setCurrentPackage: (path: string) => void;
164
+ readonly cycleHighlights: readonly CycleHighlight[];
165
+ readonly overlay?: React.ReactNode;
166
+ }
167
+
168
+ interface GraphViewPresentationInput {
169
+ readonly cycleHighlights: readonly CycleHighlight[];
170
+ readonly layout: LayoutOptions['name'];
171
+ readonly packageGraph: ElementsDefinition;
172
+ readonly theme: 'dark' | 'light';
173
+ readonly visibleElements: ElementsDefinition | null;
174
+ }
175
+
176
+ interface DependencyGraphCanvasProps extends DependencyGraphViewProps {
177
+ readonly onSpacingFactorChange: (spacingFactor: number) => void;
178
+ readonly layout: GraphViewLayoutName;
179
+ readonly layoutOptions: Readonly<Record<string, unknown>>;
180
+ readonly model: NonNullable<ReturnType<typeof createGraphViewModel>>;
181
+ readonly spacingFactor: number;
182
+ readonly styles: ReturnType<typeof createGraphViewStyles>;
183
+ readonly theme: 'dark' | 'light';
184
+ }
@@ -0,0 +1,48 @@
1
+ 'use client';
2
+
3
+ import type { GraphViewController } from '@zora/graph-view';
4
+ import { CircleDotDashedIcon } from 'lucide-react';
5
+ import React, { type ChangeEvent } from 'react';
6
+
7
+ interface GraphZoomControlsProps {
8
+ readonly controller: GraphViewController | null;
9
+ readonly maxZoom: number;
10
+ readonly minZoom: number;
11
+ readonly zoom: number;
12
+ }
13
+
14
+ /*** Renders graph zoom controls against the engine-neutral ZORA viewport controller. */
15
+ export function GraphZoomControls({ controller, maxZoom, minZoom, zoom }: GraphZoomControlsProps) {
16
+ /*** Applies one slider zoom value through the GraphView controller. */
17
+ const handleSliderChange = (event: ChangeEvent<HTMLInputElement>) => {
18
+ if (controller === null) return;
19
+ const level = Number.parseFloat(event.target.value);
20
+ controller.setZoom(Math.min(maxZoom, Math.max(minZoom, level)));
21
+ };
22
+
23
+ return (
24
+ <div className="flex shrink-0 items-center justify-center gap-2 border-t border-t-gray-200 p-4 dark:border-t-gray-800">
25
+ <label htmlFor="zoom">Zoom:</label>
26
+ <button
27
+ type="button"
28
+ title="Fit graph and optimize spacing for readability"
29
+ aria-label="Fit graph and optimize spacing for readability"
30
+ onClick={() => controller?.fit({ optimizeSpacing: true })}
31
+ className="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800"
32
+ >
33
+ <CircleDotDashedIcon className="cursor-pointer" />
34
+ </button>
35
+ <input
36
+ id="zoom"
37
+ type="range"
38
+ min={minZoom}
39
+ max={maxZoom}
40
+ step="any"
41
+ value={zoom}
42
+ onChange={handleSliderChange}
43
+ className="w-64"
44
+ />
45
+ <span className="text-sm">{(zoom * 100).toFixed(0)}%</span>
46
+ </div>
47
+ );
48
+ }
@@ -0,0 +1,35 @@
1
+ import type { GraphViewEdge, GraphViewNode } from '@zora/graph-view';
2
+
3
+ import { getDependencyHighlightClasses } from '@/features/graph-view/domain/getDependencyHighlightClasses';
4
+ import type { GraphInteractionState } from '@/types/graphInteraction';
5
+
6
+ /***
7
+ * Projects PKGViz interaction classes into the public GraphView presentation contract.
8
+ * @performance Reuse prepared model data and styles; interaction never rebuilds package/cycle indexes.
9
+ * Always start from the base model so pointer exit removes transient classes and preserves audit data.
10
+ */
11
+ export function applyGraphInteractionPresentation(
12
+ nodes: readonly GraphViewNode[],
13
+ edges: readonly GraphViewEdge[],
14
+ state: GraphInteractionState
15
+ ) {
16
+ const activeIds = new Set(
17
+ state.hoveredNodeId === null ? state.selectedNodeIds : [state.hoveredNodeId]
18
+ );
19
+ if (activeIds.size === 0) return { nodes, edges };
20
+ const classes = getDependencyHighlightClasses(
21
+ nodes.map(node => node.id),
22
+ edges,
23
+ activeIds
24
+ );
25
+ return {
26
+ nodes: nodes.map((node, index) => ({
27
+ ...node,
28
+ classes: [node.classes, classes.nodeClasses.at(index)].filter(Boolean).join(' '),
29
+ })),
30
+ edges: edges.map((edge, index) => ({
31
+ ...edge,
32
+ classes: [edge.classes, classes.edgeClasses.at(index)].filter(Boolean).join(' '),
33
+ })),
34
+ };
35
+ }
@@ -0,0 +1,240 @@
1
+ import type { GraphViewEdge, GraphViewNode } from '@zora/graph-view';
2
+ import type { ElementsDefinition } from 'cytoscape';
3
+
4
+ import { createCompoundOpacityIndex } from '@/features/graph-view/utils/createCompoundOpacityIndex';
5
+ import { readNodeDefinitionId } from '@/features/graph-view/utils/readNodeDefinitionId';
6
+ import type { CycleHighlight } from '@/types/auditVisualization';
7
+
8
+ /***
9
+ * Projects PKGViz graph data into the engine-neutral ZORA GraphView contract.
10
+ * @performance
11
+ * Performance invariant: build package and cycle indexes once per projection, then use lookups
12
+ * in element conversion. Do not move full-graph or full-cycle scans into the node/edge callbacks.
13
+ * Recheck test/benchmarks/graphPresentation.ts and the README Performance evidence when changing
14
+ * this path; CPU model timings do not establish browser rendering or layout performance.
15
+ */
16
+ export function createGraphViewModel(
17
+ allElements: ElementsDefinition,
18
+ visibleElements: ElementsDefinition,
19
+ cycleHighlights: readonly CycleHighlight[]
20
+ ): GraphViewModel {
21
+ const visibleNodeIds = new Set(
22
+ visibleElements.nodes
23
+ .map(node => readNodeDefinitionId(node))
24
+ .filter((id): id is string => id !== null)
25
+ );
26
+ const parentById = createVisibleParentMap(visibleElements, visibleNodeIds);
27
+ const detachedNodeIds = getDetachedNodeIds(visibleElements, parentById);
28
+ const packageParents = indexPackageParents(allElements);
29
+ const parentNodeIds = new Set([...visibleNodeIds].filter(id => packageParents.has(id)));
30
+ const cycles = indexCyclePresentation(cycleHighlights);
31
+ const appearance = {
32
+ cycles: cycles.nodes,
33
+ opacity: createCompoundOpacityIndex(parentById, detachedNodeIds),
34
+ };
35
+
36
+ return {
37
+ edges: visibleElements.edges.flatMap(edge => createGraphViewEdge(edge, cycles.edges)),
38
+ nodes: visibleElements.nodes.flatMap(node =>
39
+ createGraphViewNode(node, parentById, detachedNodeIds, parentNodeIds, appearance)
40
+ ),
41
+ parentNodeIds,
42
+ };
43
+ }
44
+
45
+ interface GraphViewModel {
46
+ readonly edges: readonly GraphViewEdge[];
47
+ readonly nodes: readonly GraphViewNode[];
48
+ readonly parentNodeIds: ReadonlySet<string>;
49
+ }
50
+
51
+ /***
52
+ * Indexes package ancestry once instead of scanning the full graph for each visible package.
53
+ * @performance
54
+ * Replacing this set with nodes.some(...) per visible node restores quadratic work for large
55
+ * projections. Walk dotted boundaries, including missing intermediate packages, so hidden
56
+ * descendants remain navigable without confusing sibling prefixes such as a.b and a.bc.
57
+ * Mutation is confined to this fresh index; source elements are never modified.
58
+ */
59
+ function indexPackageParents(elements: ElementsDefinition): ReadonlySet<string> {
60
+ const parents = new Set<string>();
61
+ for (const node of elements.nodes) {
62
+ const id = readNodeDefinitionId(node);
63
+ if (id === null) continue;
64
+ for (
65
+ let boundary = id.lastIndexOf('.');
66
+ boundary >= 0;
67
+ boundary = id.lastIndexOf('.', boundary - 1)
68
+ ) {
69
+ parents.add(id.slice(0, boundary));
70
+ if (boundary === 0) break;
71
+ }
72
+ }
73
+ return parents;
74
+ }
75
+
76
+ /***
77
+ * Indexes cycle overlays with last-cycle precedence and the first matching directed-edge step.
78
+ * @performance
79
+ * Visit overlay membership once, not every cycle for every rendered element. The reverse edge
80
+ * traversal makes the first occurrence win within a cycle; later cycles still overwrite earlier
81
+ * ones. Keep directed source/target keys distinct. Model regression tests protect this precedence.
82
+ */
83
+ function indexCyclePresentation(highlights: readonly CycleHighlight[]) {
84
+ const nodes = new Map<string, string>();
85
+ const edges = new Map<string, Map<string, { readonly color: string; readonly step: number }>>();
86
+ for (const highlight of highlights) {
87
+ for (const id of highlight.cycle.packages) nodes.set(id, highlight.color);
88
+ highlight.cycle.edges.toReversed().forEach((edge, index) => {
89
+ const targets =
90
+ edges.get(edge.from) ??
91
+ new Map<string, { readonly color: string; readonly step: number }>();
92
+ targets.set(edge.to, { color: highlight.color, step: highlight.cycle.edges.length - index });
93
+ edges.set(edge.from, targets);
94
+ });
95
+ }
96
+ return { edges, nodes };
97
+ }
98
+
99
+ /*** Builds only compound-parent relations whose parent is present in the visible graph. */
100
+ function createVisibleParentMap(
101
+ elements: ElementsDefinition,
102
+ visibleNodeIds: ReadonlySet<string>
103
+ ): ReadonlyMap<string, string> {
104
+ return new Map(
105
+ elements.nodes.flatMap(node => {
106
+ const id = readNodeDefinitionId(node);
107
+ const parentId = readString(node.data.parent);
108
+ return id && parentId && visibleNodeIds.has(parentId) ? [[id, parentId] as const] : [];
109
+ })
110
+ );
111
+ }
112
+
113
+ /*** Detaches descendants participating in ancestor edges so every dependency has drawable endpoints. */
114
+ function getDetachedNodeIds(
115
+ elements: ElementsDefinition,
116
+ parentById: ReadonlyMap<string, string>
117
+ ): ReadonlySet<string> {
118
+ const detachedNodeIds = new Set<string>();
119
+
120
+ for (const edge of elements.edges) {
121
+ const source = readString(edge.data.source);
122
+ const target = readString(edge.data.target);
123
+ if (!source || !target) continue;
124
+
125
+ if (isAncestor(source, target, parentById)) detachedNodeIds.add(target);
126
+ else if (isAncestor(target, source, parentById)) detachedNodeIds.add(source);
127
+ }
128
+
129
+ return detachedNodeIds;
130
+ }
131
+
132
+ /*** Returns whether one visible node contains another through the compound-parent chain. */
133
+ function isAncestor(
134
+ possibleAncestor: string,
135
+ nodeId: string,
136
+ parentById: ReadonlyMap<string, string>
137
+ ): boolean {
138
+ const visited = new Set<string>();
139
+ let current = parentById.get(nodeId);
140
+
141
+ while (current !== undefined && !visited.has(current)) {
142
+ if (current === possibleAncestor) return true;
143
+ visited.add(current);
144
+ current = parentById.get(current);
145
+ }
146
+
147
+ return false;
148
+ }
149
+
150
+ /***
151
+ * Converts one visible node using prepared parent and cycle lookups.
152
+ * @performance
153
+ * Resolve labels once per model update. ZORA measures styled labels during reconciliation;
154
+ * do not restore character-count width estimates or per-frame consumer measurement callbacks.
155
+ */
156
+ function createGraphViewNode(
157
+ node: ElementsDefinition['nodes'][number],
158
+ parentById: ReadonlyMap<string, string>,
159
+ detachedNodeIds: ReadonlySet<string>,
160
+ parentNodeIds: ReadonlySet<string>,
161
+ appearance: {
162
+ readonly cycles: ReadonlyMap<string, string>;
163
+ readonly opacity: ReadonlyMap<string, number>;
164
+ }
165
+ ): GraphViewNode[] {
166
+ const id = readNodeDefinitionId(node);
167
+ if (id === null) return [];
168
+
169
+ const cycleColor = appearance.cycles.get(id);
170
+ const parentId = detachedNodeIds.has(id) ? undefined : parentById.get(id);
171
+ const label = readString(node.data.label) ?? readString(node.data.name) ?? id;
172
+
173
+ return [
174
+ {
175
+ id,
176
+ label,
177
+ parentId,
178
+ classes: appendClasses(
179
+ readClasses(node.classes),
180
+ parentNodeIds.has(id) ? 'isParent' : undefined,
181
+ cycleColor !== undefined ? 'auditCycle' : undefined
182
+ ),
183
+ data: {
184
+ ...node.data,
185
+ compoundFillOpacity: appearance.opacity.get(id) ?? 0.04,
186
+ parent: parentId,
187
+ ...(cycleColor !== undefined ? { auditCycleColor: cycleColor } : {}),
188
+ },
189
+ },
190
+ ];
191
+ }
192
+
193
+ /*** Convert one visible edge while projecting audit-cycle presentation metadata. */
194
+ function createGraphViewEdge(
195
+ edge: ElementsDefinition['edges'][number],
196
+ cycleEdges: ReturnType<typeof indexCyclePresentation>['edges']
197
+ ): GraphViewEdge[] {
198
+ const source = readString(edge.data.source);
199
+ const target = readString(edge.data.target);
200
+ if (!source || !target) return [];
201
+
202
+ const cycle = cycleEdges.get(source)?.get(target);
203
+ return [
204
+ {
205
+ id: readString(edge.data.id),
206
+ source,
207
+ target,
208
+ classes: appendClasses(readClasses(edge.classes), cycle ? 'auditCycle' : undefined),
209
+ data: {
210
+ ...edge.data,
211
+ ...(cycle
212
+ ? {
213
+ auditCycleColor: cycle.color,
214
+ auditCycleStep: String(cycle.step),
215
+ }
216
+ : {}),
217
+ },
218
+ },
219
+ ];
220
+ }
221
+
222
+ /*** Normalize Cytoscape class declarations into the GraphView string contract. */
223
+ function readClasses(value: unknown): string {
224
+ if (typeof value === 'string') return value;
225
+ if (!Array.isArray(value)) return '';
226
+ return value.filter((entry): entry is string => typeof entry === 'string').join(' ');
227
+ }
228
+
229
+ /*** Append optional classes without introducing duplicate whitespace. */
230
+ function appendClasses(base: string, ...classes: readonly (string | undefined)[]): string {
231
+ return [base, ...classes]
232
+ .filter(value => value && value.length > 0)
233
+ .join(' ')
234
+ .trim();
235
+ }
236
+
237
+ /*** Read one optional string data field at the adapter boundary. */
238
+ function readString(value: unknown): string | undefined {
239
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
240
+ }
@@ -0,0 +1,37 @@
1
+ import type { GraphViewStyleRule } from '@zora/graph-view';
2
+ import type { ElementsDefinition, LayoutOptions, StylesheetJson } from 'cytoscape';
3
+
4
+ import {
5
+ getBreadthfirstStyle,
6
+ getCircleStyle,
7
+ getConcentricStyle,
8
+ getElkStyle,
9
+ getGridStyle,
10
+ } from '@/layouts';
11
+ import { getStyle as getCommonStyle, type ThemeKey } from '@/layouts/style';
12
+
13
+ /*** Builds ZORA GraphView style rules from the existing PKGViz Cytoscape style policy. */
14
+ export function createGraphViewStyles(
15
+ elements: ElementsDefinition,
16
+ theme: ThemeKey,
17
+ layout: LayoutOptions['name']
18
+ ): readonly GraphViewStyleRule[] {
19
+ return [...getCommonStyle(elements, theme), ...getLayoutStyle(layout)].map(toGraphViewStyleRule);
20
+ }
21
+
22
+ /*** Converts one Cytoscape stylesheet rule into the GraphView presentation contract. */
23
+ function toGraphViewStyleRule(rule: StylesheetJson[number]): GraphViewStyleRule {
24
+ return {
25
+ selector: rule.selector,
26
+ style: { ...('style' in rule ? rule.style : rule.css) },
27
+ };
28
+ }
29
+
30
+ /*** Resolves the visual style extension owned by the active layout. */
31
+ function getLayoutStyle(layout: LayoutOptions['name']): StylesheetJson {
32
+ if (layout === 'breadthfirst') return getBreadthfirstStyle();
33
+ if (layout === 'circle') return getCircleStyle();
34
+ if (layout === 'elk') return getElkStyle();
35
+ if (layout === 'grid') return getGridStyle();
36
+ return getConcentricStyle();
37
+ }
@@ -0,0 +1,104 @@
1
+ 'use client';
2
+
3
+ import type { ElementsDefinition } from 'cytoscape';
4
+ import { useEffect, useRef } from 'react';
5
+
6
+ import { createCycleFocus } from '@/features/audit/utils/cycleVisualization';
7
+ import { readNodeDefinitionId } from '@/features/graph-view/utils/readNodeDefinitionId';
8
+ import type { CycleHighlight } from '@/types/auditVisualization';
9
+
10
+ /*** Expands package projection once when active cycles are not yet visible. */
11
+ export function useGraphFocus(input: UseGraphFocusInput) {
12
+ const handledCycleSignatureRef = useRef<string | null>(null);
13
+ const {
14
+ cycleHighlights,
15
+ currentPackage,
16
+ setCurrentPackage,
17
+ setSubPackageDepth,
18
+ subPackageDepth,
19
+ visibleElements,
20
+ } = input;
21
+
22
+ useEffect(
23
+ () =>
24
+ runCycleProjectionFocus(
25
+ {
26
+ cycleHighlights,
27
+ currentPackage,
28
+ setCurrentPackage,
29
+ setSubPackageDepth,
30
+ subPackageDepth,
31
+ visibleElements,
32
+ },
33
+ handledCycleSignatureRef
34
+ ),
35
+ [
36
+ cycleHighlights,
37
+ currentPackage,
38
+ setCurrentPackage,
39
+ setSubPackageDepth,
40
+ subPackageDepth,
41
+ visibleElements,
42
+ ]
43
+ );
44
+ }
45
+
46
+ /*** Runs one cycle-projection iteration without touching layout or viewport state. */
47
+ function runCycleProjectionFocus(
48
+ input: UseGraphFocusInput,
49
+ handledCycleSignatureRef: { current: string | null }
50
+ ) {
51
+ if (input.visibleElements === null) return;
52
+
53
+ if (input.cycleHighlights.length === 0) {
54
+ handledCycleSignatureRef.current = null;
55
+ return;
56
+ }
57
+
58
+ const signature = getCycleSignature(input.cycleHighlights);
59
+ if (handledCycleSignatureRef.current === signature) return;
60
+ if (ensureCycleProjection(input)) return;
61
+ handledCycleSignatureRef.current = signature;
62
+ }
63
+
64
+ /*** Returns a stable identity for the current active-cycle set. */
65
+ function getCycleSignature(highlights: readonly CycleHighlight[]): string {
66
+ return highlights
67
+ .map(highlight => highlight.id)
68
+ .sort()
69
+ .join('|');
70
+ }
71
+
72
+ /*** Expands only the projection dimensions required to expose every active cycle package. */
73
+ function ensureCycleProjection(input: UseGraphFocusInput): boolean {
74
+ if (input.visibleElements === null) return false;
75
+ const activePackageNames = [
76
+ ...new Set(input.cycleHighlights.flatMap(highlight => highlight.cycle.packages)),
77
+ ];
78
+ const visibleNodeIds = new Set(
79
+ input.visibleElements.nodes
80
+ .map(node => readNodeDefinitionId(node))
81
+ .filter((id): id is string => id !== null)
82
+ );
83
+ if (activePackageNames.every(packageName => visibleNodeIds.has(packageName))) return false;
84
+
85
+ const focus = createCycleFocus(input.cycleHighlights);
86
+ if (!focus) return false;
87
+
88
+ const normalizedCurrentPackage = input.currentPackage.replace(/\//g, '.');
89
+ const scopeChanged = focus.currentPackage !== normalizedCurrentPackage;
90
+ const depthChanged = focus.packageDepth > input.subPackageDepth;
91
+
92
+ if (scopeChanged) input.setCurrentPackage(focus.currentPackage);
93
+ if (depthChanged) input.setSubPackageDepth(focus.packageDepth);
94
+ return scopeChanged || depthChanged;
95
+ }
96
+
97
+ interface UseGraphFocusInput {
98
+ readonly cycleHighlights: readonly CycleHighlight[];
99
+ readonly currentPackage: string;
100
+ readonly setCurrentPackage: (path: string) => void;
101
+ readonly setSubPackageDepth: (depth: number) => void;
102
+ readonly subPackageDepth: number;
103
+ readonly visibleElements: ElementsDefinition | null;
104
+ }