@tyroneross/navgator 0.2.0 → 0.2.1

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 (338) hide show
  1. package/dist/cli/index.js +1 -1
  2. package/package.json +1 -1
  3. package/web/.next/standalone/web/.next/BUILD_ID +1 -0
  4. package/web/.next/standalone/web/.next/app-path-routes-manifest.json +13 -0
  5. package/web/.next/standalone/web/.next/build-manifest.json +20 -0
  6. package/web/.next/standalone/web/.next/package.json +1 -0
  7. package/web/.next/standalone/web/.next/prerender-manifest.json +88 -0
  8. package/web/.next/standalone/web/.next/required-server-files.json +317 -0
  9. package/web/.next/standalone/web/.next/routes-manifest.json +110 -0
  10. package/web/.next/standalone/web/.next/server/app/_global-error/page/app-paths-manifest.json +3 -0
  11. package/web/.next/standalone/web/.next/server/app/_global-error/page/build-manifest.json +17 -0
  12. package/web/.next/standalone/web/.next/server/app/_global-error/page/next-font-manifest.json +6 -0
  13. package/web/.next/standalone/web/.next/server/app/_global-error/page/react-loadable-manifest.json +1 -0
  14. package/web/.next/standalone/web/.next/server/app/_global-error/page/server-reference-manifest.json +4 -0
  15. package/web/.next/standalone/web/.next/server/app/_global-error/page.js +10 -0
  16. package/web/.next/standalone/web/.next/server/app/_global-error/page.js.map +5 -0
  17. package/web/.next/standalone/web/.next/server/app/_global-error/page.js.nft.json +1 -0
  18. package/web/.next/standalone/web/.next/server/app/_global-error/page_client-reference-manifest.js +2 -0
  19. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -0
  20. package/web/.next/standalone/web/.next/server/app/_global-error.meta +15 -0
  21. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +12 -0
  22. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +5 -0
  23. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +12 -0
  24. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +7 -0
  25. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +4 -0
  26. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
  27. package/web/.next/standalone/web/.next/server/app/_not-found/page/app-paths-manifest.json +3 -0
  28. package/web/.next/standalone/web/.next/server/app/_not-found/page/build-manifest.json +17 -0
  29. package/web/.next/standalone/web/.next/server/app/_not-found/page/next-font-manifest.json +11 -0
  30. package/web/.next/standalone/web/.next/server/app/_not-found/page/react-loadable-manifest.json +1 -0
  31. package/web/.next/standalone/web/.next/server/app/_not-found/page/server-reference-manifest.json +4 -0
  32. package/web/.next/standalone/web/.next/server/app/_not-found/page.js +13 -0
  33. package/web/.next/standalone/web/.next/server/app/_not-found/page.js.map +5 -0
  34. package/web/.next/standalone/web/.next/server/app/_not-found/page.js.nft.json +1 -0
  35. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +2 -0
  36. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -0
  37. package/web/.next/standalone/web/.next/server/app/_not-found.meta +16 -0
  38. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +17 -0
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +17 -0
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +8 -0
  41. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +8 -0
  42. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -0
  43. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +4 -0
  44. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +3 -0
  45. package/web/.next/standalone/web/.next/server/app/api/components/route/app-paths-manifest.json +3 -0
  46. package/web/.next/standalone/web/.next/server/app/api/components/route/build-manifest.json +11 -0
  47. package/web/.next/standalone/web/.next/server/app/api/components/route/server-reference-manifest.json +4 -0
  48. package/web/.next/standalone/web/.next/server/app/api/components/route.js +6 -0
  49. package/web/.next/standalone/web/.next/server/app/api/components/route.js.map +5 -0
  50. package/web/.next/standalone/web/.next/server/app/api/components/route.js.nft.json +1 -0
  51. package/web/.next/standalone/web/.next/server/app/api/components/route_client-reference-manifest.js +2 -0
  52. package/web/.next/standalone/web/.next/server/app/api/connections/route/app-paths-manifest.json +3 -0
  53. package/web/.next/standalone/web/.next/server/app/api/connections/route/build-manifest.json +11 -0
  54. package/web/.next/standalone/web/.next/server/app/api/connections/route/server-reference-manifest.json +4 -0
  55. package/web/.next/standalone/web/.next/server/app/api/connections/route.js +6 -0
  56. package/web/.next/standalone/web/.next/server/app/api/connections/route.js.map +5 -0
  57. package/web/.next/standalone/web/.next/server/app/api/connections/route.js.nft.json +1 -0
  58. package/web/.next/standalone/web/.next/server/app/api/connections/route_client-reference-manifest.js +2 -0
  59. package/web/.next/standalone/web/.next/server/app/api/graph/route/app-paths-manifest.json +3 -0
  60. package/web/.next/standalone/web/.next/server/app/api/graph/route/build-manifest.json +11 -0
  61. package/web/.next/standalone/web/.next/server/app/api/graph/route/server-reference-manifest.json +4 -0
  62. package/web/.next/standalone/web/.next/server/app/api/graph/route.js +6 -0
  63. package/web/.next/standalone/web/.next/server/app/api/graph/route.js.map +5 -0
  64. package/web/.next/standalone/web/.next/server/app/api/graph/route.js.nft.json +1 -0
  65. package/web/.next/standalone/web/.next/server/app/api/graph/route_client-reference-manifest.js +2 -0
  66. package/web/.next/standalone/web/.next/server/app/api/projects/route/app-paths-manifest.json +3 -0
  67. package/web/.next/standalone/web/.next/server/app/api/projects/route/build-manifest.json +11 -0
  68. package/web/.next/standalone/web/.next/server/app/api/projects/route/server-reference-manifest.json +4 -0
  69. package/web/.next/standalone/web/.next/server/app/api/projects/route.js +6 -0
  70. package/web/.next/standalone/web/.next/server/app/api/projects/route.js.map +5 -0
  71. package/web/.next/standalone/web/.next/server/app/api/projects/route.js.nft.json +1 -0
  72. package/web/.next/standalone/web/.next/server/app/api/projects/route_client-reference-manifest.js +2 -0
  73. package/web/.next/standalone/web/.next/server/app/api/prompts/route/app-paths-manifest.json +3 -0
  74. package/web/.next/standalone/web/.next/server/app/api/prompts/route/build-manifest.json +11 -0
  75. package/web/.next/standalone/web/.next/server/app/api/prompts/route/server-reference-manifest.json +4 -0
  76. package/web/.next/standalone/web/.next/server/app/api/prompts/route.js +7 -0
  77. package/web/.next/standalone/web/.next/server/app/api/prompts/route.js.map +5 -0
  78. package/web/.next/standalone/web/.next/server/app/api/prompts/route.js.nft.json +1 -0
  79. package/web/.next/standalone/web/.next/server/app/api/prompts/route_client-reference-manifest.js +2 -0
  80. package/web/.next/standalone/web/.next/server/app/api/scan/route/app-paths-manifest.json +3 -0
  81. package/web/.next/standalone/web/.next/server/app/api/scan/route/build-manifest.json +11 -0
  82. package/web/.next/standalone/web/.next/server/app/api/scan/route/server-reference-manifest.json +4 -0
  83. package/web/.next/standalone/web/.next/server/app/api/scan/route.js +6 -0
  84. package/web/.next/standalone/web/.next/server/app/api/scan/route.js.map +5 -0
  85. package/web/.next/standalone/web/.next/server/app/api/scan/route.js.nft.json +1 -0
  86. package/web/.next/standalone/web/.next/server/app/api/scan/route_client-reference-manifest.js +2 -0
  87. package/web/.next/standalone/web/.next/server/app/api/settings/route/app-paths-manifest.json +3 -0
  88. package/web/.next/standalone/web/.next/server/app/api/settings/route/build-manifest.json +11 -0
  89. package/web/.next/standalone/web/.next/server/app/api/settings/route/server-reference-manifest.json +4 -0
  90. package/web/.next/standalone/web/.next/server/app/api/settings/route.js +6 -0
  91. package/web/.next/standalone/web/.next/server/app/api/settings/route.js.map +5 -0
  92. package/web/.next/standalone/web/.next/server/app/api/settings/route.js.nft.json +1 -0
  93. package/web/.next/standalone/web/.next/server/app/api/settings/route_client-reference-manifest.js +2 -0
  94. package/web/.next/standalone/web/.next/server/app/api/status/route/app-paths-manifest.json +3 -0
  95. package/web/.next/standalone/web/.next/server/app/api/status/route/build-manifest.json +11 -0
  96. package/web/.next/standalone/web/.next/server/app/api/status/route/server-reference-manifest.json +4 -0
  97. package/web/.next/standalone/web/.next/server/app/api/status/route.js +6 -0
  98. package/web/.next/standalone/web/.next/server/app/api/status/route.js.map +5 -0
  99. package/web/.next/standalone/web/.next/server/app/api/status/route.js.nft.json +1 -0
  100. package/web/.next/standalone/web/.next/server/app/api/status/route_client-reference-manifest.js +2 -0
  101. package/web/.next/standalone/web/.next/server/app/index.html +1 -0
  102. package/web/.next/standalone/web/.next/server/app/index.meta +14 -0
  103. package/web/.next/standalone/web/.next/server/app/index.rsc +23 -0
  104. package/web/.next/standalone/web/.next/server/app/index.segments/__PAGE__.segment.rsc +9 -0
  105. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +23 -0
  106. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +8 -0
  107. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +8 -0
  108. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +5 -0
  109. package/web/.next/standalone/web/.next/server/app/page/app-paths-manifest.json +3 -0
  110. package/web/.next/standalone/web/.next/server/app/page/build-manifest.json +17 -0
  111. package/web/.next/standalone/web/.next/server/app/page/next-font-manifest.json +11 -0
  112. package/web/.next/standalone/web/.next/server/app/page/react-loadable-manifest.json +1 -0
  113. package/web/.next/standalone/web/.next/server/app/page/server-reference-manifest.json +4 -0
  114. package/web/.next/standalone/web/.next/server/app/page.js +15 -0
  115. package/web/.next/standalone/web/.next/server/app/page.js.map +5 -0
  116. package/web/.next/standalone/web/.next/server/app/page.js.nft.json +1 -0
  117. package/web/.next/standalone/web/.next/server/app/page_client-reference-manifest.js +2 -0
  118. package/web/.next/standalone/web/.next/server/app-paths-manifest.json +13 -0
  119. package/web/.next/standalone/web/.next/server/chunks/2374f_next_dist_esm_build_templates_app-route_0bb4e66a.js +19 -0
  120. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__006b837d._.js +3 -0
  121. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__1ab91221._.js +3 -0
  122. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__2e09fec9._.js +3 -0
  123. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__38d0390f._.js +3 -0
  124. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__539ef30d._.js +3 -0
  125. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__594bcf20._.js +3 -0
  126. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__b888fadf._.js +3 -0
  127. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__f3450c22._.js +21 -0
  128. package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__fa2ec862._.js +3 -0
  129. package/web/.next/standalone/web/.next/server/chunks/[turbopack]_runtime.js +770 -0
  130. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_e6039567._.js +6 -0
  131. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_09351209._.js +6 -0
  132. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_222be7ae._.js +6 -0
  133. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_2346e1b3._.js +3 -0
  134. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_cec86455._.js +4 -0
  135. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_client_components_9c5d1a14._.js +3 -0
  136. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_client_components_builtin_forbidden_8eae0c85.js +3 -0
  137. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_client_components_builtin_global-error_81159d60.js +3 -0
  138. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_client_components_builtin_unauthorized_7d34a31c.js +3 -0
  139. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_e03afa0e._.js +3 -0
  140. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_esm_build_templates_app-page_2c8d71b9.js +4 -0
  141. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_next_dist_server_route-modules_app-page_vendored_ssr_react-dom_8910f04c.js +3 -0
  142. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__1a0663e6._.js +3 -0
  143. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__4306eafc._.js +3 -0
  144. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__44903626._.js +3 -0
  145. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__60278e3f._.js +3 -0
  146. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__61942f24._.js +4 -0
  147. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__8c45c3c9._.js +3 -0
  148. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9a2f110d._.js +3 -0
  149. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__ed07bd88._.js +3 -0
  150. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f2db61af._.js +3 -0
  151. package/web/.next/standalone/web/.next/server/chunks/ssr/[turbopack]_runtime.js +770 -0
  152. package/web/.next/standalone/web/.next/server/chunks/ssr/web_0103e631._.js +4 -0
  153. package/web/.next/standalone/web/.next/server/chunks/ssr/web_171de0df._.js +14 -0
  154. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app__global-error_page_actions_2a1e94d4.js +3 -0
  155. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app__not-found_page_actions_9eaa9845.js +3 -0
  156. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_page_actions_ec26bf28.js +3 -0
  157. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_components_route_actions_c88bf2a6.js +3 -0
  158. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_connections_route_actions_b1d5e95a.js +3 -0
  159. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_graph_route_actions_e2dd052c.js +3 -0
  160. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_projects_route_actions_3f671cbb.js +3 -0
  161. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_prompts_route_actions_85b56166.js +3 -0
  162. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_scan_route_actions_861cde8d.js +3 -0
  163. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_settings_route_actions_e19026ae.js +3 -0
  164. package/web/.next/standalone/web/.next/server/chunks/web__next-internal_server_app_api_status_route_actions_8b8c6c89.js +3 -0
  165. package/web/.next/standalone/web/.next/server/functions-config-manifest.json +4 -0
  166. package/web/.next/standalone/web/.next/server/middleware-build-manifest.js +21 -0
  167. package/web/.next/standalone/web/.next/server/middleware-manifest.json +6 -0
  168. package/web/.next/standalone/web/.next/server/next-font-manifest.js +1 -0
  169. package/web/.next/standalone/web/.next/server/next-font-manifest.json +15 -0
  170. package/web/.next/standalone/web/.next/server/pages/404.html +1 -0
  171. package/web/.next/standalone/web/.next/server/pages/500.html +2 -0
  172. package/web/.next/standalone/web/.next/server/pages-manifest.json +4 -0
  173. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -0
  174. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +5 -0
  175. package/web/.next/standalone/web/.next/static/WH0NvTGUEDs9QVUjcPJKx/_buildManifest.js +11 -0
  176. package/web/.next/standalone/web/.next/static/WH0NvTGUEDs9QVUjcPJKx/_clientMiddlewareManifest.json +1 -0
  177. package/web/.next/standalone/web/.next/static/WH0NvTGUEDs9QVUjcPJKx/_ssgManifest.js +1 -0
  178. package/web/.next/standalone/web/.next/static/chunks/062ae79751df2759.js +1 -0
  179. package/web/.next/standalone/web/.next/static/chunks/159889e17b2cf1f8.js +2 -0
  180. package/web/.next/standalone/web/.next/static/chunks/458d6f37339fc069.js +1 -0
  181. package/web/.next/standalone/web/.next/static/chunks/6d3d39425a878d7f.js +1 -0
  182. package/web/.next/standalone/web/.next/static/chunks/8a80e7184ad3a13f.css +2 -0
  183. package/web/.next/standalone/web/.next/static/chunks/a6dad97d9634a72d.js +1 -0
  184. package/web/.next/standalone/web/.next/static/chunks/c056475f5f4424b6.css +1 -0
  185. package/web/.next/standalone/web/.next/static/chunks/c57fee8cce8d7cb9.js +1 -0
  186. package/web/.next/standalone/web/.next/static/chunks/cb3513192b63e480.js +12 -0
  187. package/web/.next/standalone/web/.next/static/chunks/dd22b5f2beb2cc31.js +1 -0
  188. package/web/.next/standalone/web/.next/static/chunks/e0affeef0ddb9a97.js +4 -0
  189. package/web/.next/standalone/web/.next/static/chunks/f74a6859e1c4d5e3.js +1 -0
  190. package/web/.next/standalone/web/.next/static/chunks/turbopack-c0c89f9e6f0a38c4.js +3 -0
  191. package/web/.next/standalone/web/.next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  192. package/web/.next/standalone/web/.next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  193. package/web/.next/standalone/web/.next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  194. package/web/.next/standalone/web/.next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  195. package/web/.next/standalone/web/.next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  196. package/web/.next/standalone/web/.next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  197. package/web/.next/standalone/web/app/api/components/route.ts +252 -0
  198. package/web/.next/standalone/web/app/api/connections/route.ts +319 -0
  199. package/web/.next/standalone/web/app/api/graph/route.ts +235 -0
  200. package/web/.next/standalone/web/app/api/projects/route.ts +221 -0
  201. package/web/.next/standalone/web/app/api/prompts/route.ts +328 -0
  202. package/web/.next/standalone/web/app/api/scan/route.ts +108 -0
  203. package/web/.next/standalone/web/app/api/settings/route.ts +198 -0
  204. package/web/.next/standalone/web/app/api/status/route.ts +169 -0
  205. package/web/.next/standalone/web/app/globals.css +99 -0
  206. package/web/.next/standalone/web/app/layout.tsx +49 -0
  207. package/web/.next/standalone/web/app/page.tsx +94 -0
  208. package/web/.next/standalone/web/components/components-panel.tsx +240 -0
  209. package/web/.next/standalone/web/components/connections-panel.tsx +220 -0
  210. package/web/.next/standalone/web/components/diagram-view.tsx +735 -0
  211. package/web/.next/standalone/web/components/header.tsx +225 -0
  212. package/web/.next/standalone/web/components/impact-analysis.tsx +358 -0
  213. package/web/.next/standalone/web/components/llm-tracking-panel.tsx +1483 -0
  214. package/web/.next/standalone/web/components/settings-panel.tsx +671 -0
  215. package/web/.next/standalone/web/components/sidebar.tsx +74 -0
  216. package/web/.next/standalone/web/components/status-overview.tsx +326 -0
  217. package/web/.next/standalone/web/components/theme-provider.tsx +11 -0
  218. package/web/.next/standalone/web/components/ui/accordion.tsx +66 -0
  219. package/web/.next/standalone/web/components/ui/alert-dialog.tsx +157 -0
  220. package/web/.next/standalone/web/components/ui/alert.tsx +66 -0
  221. package/web/.next/standalone/web/components/ui/aspect-ratio.tsx +11 -0
  222. package/web/.next/standalone/web/components/ui/avatar.tsx +53 -0
  223. package/web/.next/standalone/web/components/ui/badge.tsx +46 -0
  224. package/web/.next/standalone/web/components/ui/breadcrumb.tsx +109 -0
  225. package/web/.next/standalone/web/components/ui/button-group.tsx +83 -0
  226. package/web/.next/standalone/web/components/ui/button.tsx +60 -0
  227. package/web/.next/standalone/web/components/ui/calendar.tsx +213 -0
  228. package/web/.next/standalone/web/components/ui/card.tsx +92 -0
  229. package/web/.next/standalone/web/components/ui/carousel.tsx +241 -0
  230. package/web/.next/standalone/web/components/ui/chart.tsx +353 -0
  231. package/web/.next/standalone/web/components/ui/checkbox.tsx +32 -0
  232. package/web/.next/standalone/web/components/ui/collapsible.tsx +33 -0
  233. package/web/.next/standalone/web/components/ui/command.tsx +184 -0
  234. package/web/.next/standalone/web/components/ui/context-menu.tsx +252 -0
  235. package/web/.next/standalone/web/components/ui/dialog.tsx +143 -0
  236. package/web/.next/standalone/web/components/ui/drawer.tsx +135 -0
  237. package/web/.next/standalone/web/components/ui/dropdown-menu.tsx +257 -0
  238. package/web/.next/standalone/web/components/ui/empty.tsx +104 -0
  239. package/web/.next/standalone/web/components/ui/field.tsx +244 -0
  240. package/web/.next/standalone/web/components/ui/form.tsx +167 -0
  241. package/web/.next/standalone/web/components/ui/hover-card.tsx +44 -0
  242. package/web/.next/standalone/web/components/ui/input-group.tsx +169 -0
  243. package/web/.next/standalone/web/components/ui/input-otp.tsx +77 -0
  244. package/web/.next/standalone/web/components/ui/input.tsx +21 -0
  245. package/web/.next/standalone/web/components/ui/item.tsx +193 -0
  246. package/web/.next/standalone/web/components/ui/kbd.tsx +28 -0
  247. package/web/.next/standalone/web/components/ui/label.tsx +24 -0
  248. package/web/.next/standalone/web/components/ui/menubar.tsx +276 -0
  249. package/web/.next/standalone/web/components/ui/navigation-menu.tsx +166 -0
  250. package/web/.next/standalone/web/components/ui/pagination.tsx +127 -0
  251. package/web/.next/standalone/web/components/ui/popover.tsx +48 -0
  252. package/web/.next/standalone/web/components/ui/progress.tsx +31 -0
  253. package/web/.next/standalone/web/components/ui/radio-group.tsx +45 -0
  254. package/web/.next/standalone/web/components/ui/resizable.tsx +56 -0
  255. package/web/.next/standalone/web/components/ui/scroll-area.tsx +58 -0
  256. package/web/.next/standalone/web/components/ui/select.tsx +185 -0
  257. package/web/.next/standalone/web/components/ui/separator.tsx +28 -0
  258. package/web/.next/standalone/web/components/ui/sheet.tsx +139 -0
  259. package/web/.next/standalone/web/components/ui/sidebar.tsx +726 -0
  260. package/web/.next/standalone/web/components/ui/skeleton.tsx +13 -0
  261. package/web/.next/standalone/web/components/ui/slider.tsx +63 -0
  262. package/web/.next/standalone/web/components/ui/sonner.tsx +25 -0
  263. package/web/.next/standalone/web/components/ui/spinner.tsx +16 -0
  264. package/web/.next/standalone/web/components/ui/switch.tsx +31 -0
  265. package/web/.next/standalone/web/components/ui/table.tsx +116 -0
  266. package/web/.next/standalone/web/components/ui/tabs.tsx +66 -0
  267. package/web/.next/standalone/web/components/ui/textarea.tsx +18 -0
  268. package/web/.next/standalone/web/components/ui/toast.tsx +129 -0
  269. package/web/.next/standalone/web/components/ui/toaster.tsx +35 -0
  270. package/web/.next/standalone/web/components/ui/toggle-group.tsx +73 -0
  271. package/web/.next/standalone/web/components/ui/toggle.tsx +47 -0
  272. package/web/.next/standalone/web/components/ui/tooltip.tsx +61 -0
  273. package/web/.next/standalone/web/components/ui/use-mobile.tsx +19 -0
  274. package/web/.next/standalone/web/components/ui/use-toast.ts +191 -0
  275. package/web/.next/standalone/web/components.json +21 -0
  276. package/web/.next/standalone/web/hooks/use-mobile.ts +19 -0
  277. package/web/.next/standalone/web/hooks/use-toast.ts +191 -0
  278. package/web/.next/standalone/web/lib/hooks/index.ts +8 -0
  279. package/web/.next/standalone/web/lib/hooks/use-components.ts +83 -0
  280. package/web/.next/standalone/web/lib/hooks/use-connections.ts +83 -0
  281. package/web/.next/standalone/web/lib/hooks/use-projects.ts +116 -0
  282. package/web/.next/standalone/web/lib/hooks/use-prompts.ts +122 -0
  283. package/web/.next/standalone/web/lib/hooks/use-settings.ts +135 -0
  284. package/web/.next/standalone/web/lib/hooks/use-status.ts +80 -0
  285. package/web/.next/standalone/web/lib/project-context.tsx +46 -0
  286. package/web/.next/standalone/web/lib/transform.ts +625 -0
  287. package/web/.next/standalone/web/lib/types.ts +198 -0
  288. package/web/.next/standalone/web/lib/utils.ts +6 -0
  289. package/web/.next/standalone/web/next.config.mjs +12 -0
  290. package/web/.next/standalone/web/package-lock.json +3987 -0
  291. package/web/.next/standalone/web/package.json +73 -0
  292. package/web/.next/standalone/web/pnpm-lock.yaml +5 -0
  293. package/web/.next/standalone/web/postcss.config.mjs +8 -0
  294. package/web/.next/standalone/web/public/apple-icon.png +0 -0
  295. package/web/.next/standalone/web/public/icon-dark-32x32.png +0 -0
  296. package/web/.next/standalone/web/public/icon-light-32x32.png +0 -0
  297. package/web/.next/standalone/web/public/icon.svg +26 -0
  298. package/web/.next/standalone/web/public/navgator-logo.png +0 -0
  299. package/web/.next/standalone/web/public/placeholder-logo.png +0 -0
  300. package/web/.next/standalone/web/public/placeholder-logo.svg +1 -0
  301. package/web/.next/standalone/web/public/placeholder-user.jpg +0 -0
  302. package/web/.next/standalone/web/public/placeholder.jpg +0 -0
  303. package/web/.next/standalone/web/public/placeholder.svg +1 -0
  304. package/web/.next/standalone/web/public/public/apple-icon.png +0 -0
  305. package/web/.next/standalone/web/public/public/icon-dark-32x32.png +0 -0
  306. package/web/.next/standalone/web/public/public/icon-light-32x32.png +0 -0
  307. package/web/.next/standalone/web/public/public/icon.svg +26 -0
  308. package/web/.next/standalone/web/public/public/navgator-logo.png +0 -0
  309. package/web/.next/standalone/web/public/public/placeholder-logo.png +0 -0
  310. package/web/.next/standalone/web/public/public/placeholder-logo.svg +1 -0
  311. package/web/.next/standalone/web/public/public/placeholder-user.jpg +0 -0
  312. package/web/.next/standalone/web/public/public/placeholder.jpg +0 -0
  313. package/web/.next/standalone/web/public/public/placeholder.svg +1 -0
  314. package/web/.next/standalone/web/server.js +38 -0
  315. package/web/.next/standalone/web/styles/globals.css +125 -0
  316. package/web/.next/standalone/web/tsconfig.json +41 -0
  317. package/web/.next/static/WH0NvTGUEDs9QVUjcPJKx/_buildManifest.js +11 -0
  318. package/web/.next/static/WH0NvTGUEDs9QVUjcPJKx/_clientMiddlewareManifest.json +1 -0
  319. package/web/.next/static/WH0NvTGUEDs9QVUjcPJKx/_ssgManifest.js +1 -0
  320. package/web/.next/static/chunks/062ae79751df2759.js +1 -0
  321. package/web/.next/static/chunks/159889e17b2cf1f8.js +2 -0
  322. package/web/.next/static/chunks/458d6f37339fc069.js +1 -0
  323. package/web/.next/static/chunks/6d3d39425a878d7f.js +1 -0
  324. package/web/.next/static/chunks/8a80e7184ad3a13f.css +2 -0
  325. package/web/.next/static/chunks/a6dad97d9634a72d.js +1 -0
  326. package/web/.next/static/chunks/c056475f5f4424b6.css +1 -0
  327. package/web/.next/static/chunks/c57fee8cce8d7cb9.js +1 -0
  328. package/web/.next/static/chunks/cb3513192b63e480.js +12 -0
  329. package/web/.next/static/chunks/dd22b5f2beb2cc31.js +1 -0
  330. package/web/.next/static/chunks/e0affeef0ddb9a97.js +4 -0
  331. package/web/.next/static/chunks/f74a6859e1c4d5e3.js +1 -0
  332. package/web/.next/static/chunks/turbopack-c0c89f9e6f0a38c4.js +3 -0
  333. package/web/.next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  334. package/web/.next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  335. package/web/.next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  336. package/web/.next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  337. package/web/.next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  338. package/web/.next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
@@ -0,0 +1,735 @@
1
+ "use client"
2
+
3
+ import { useState, useCallback, useEffect, useMemo, useRef } from "react"
4
+ import {
5
+ Copy,
6
+ Check,
7
+ ZoomIn,
8
+ ZoomOut,
9
+ RotateCcw,
10
+ Loader2,
11
+ Info,
12
+ X,
13
+ ArrowRight,
14
+ Server,
15
+ } from "lucide-react"
16
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
17
+ import { Button } from "@/components/ui/button"
18
+ import { Badge } from "@/components/ui/badge"
19
+ import {
20
+ Select,
21
+ SelectContent,
22
+ SelectItem,
23
+ SelectTrigger,
24
+ SelectValue,
25
+ } from "@/components/ui/select"
26
+ import { useActiveProject } from "@/lib/project-context"
27
+
28
+ interface DiagramViewProps {
29
+ selectedComponent: string | null
30
+ }
31
+
32
+ interface GraphNode {
33
+ id: string
34
+ name: string
35
+ type: string
36
+ layer: string
37
+ version?: string
38
+ purpose?: string
39
+ configFiles?: string[]
40
+ tags?: string[]
41
+ connectsTo?: string[]
42
+ connectedFrom?: string[]
43
+ hostedBy?: string
44
+ hosts?: string[]
45
+ }
46
+
47
+ interface GraphEdge {
48
+ id: string
49
+ source: string
50
+ target: string
51
+ type: string
52
+ label?: string
53
+ }
54
+
55
+ interface GraphData {
56
+ nodes: GraphNode[]
57
+ edges: GraphEdge[]
58
+ metadata?: { component_count: number; connection_count: number }
59
+ }
60
+
61
+ // Demo data used when no real graph exists
62
+ const DEMO_NODES: GraphNode[] = [
63
+ { id: "nextjs", name: "Next.js", type: "framework", layer: "frontend" },
64
+ { id: "react", name: "React", type: "npm", layer: "frontend" },
65
+ { id: "payments", name: "payments.ts", type: "service", layer: "backend" },
66
+ { id: "users", name: "users.ts", type: "service", layer: "backend" },
67
+ { id: "postgres", name: "PostgreSQL", type: "database", layer: "database" },
68
+ { id: "redis", name: "Redis", type: "database", layer: "database" },
69
+ { id: "openai", name: "OpenAI", type: "llm", layer: "external" },
70
+ { id: "stripe", name: "Stripe", type: "service", layer: "external" },
71
+ ]
72
+
73
+ const DEMO_EDGES: GraphEdge[] = [
74
+ { id: "e1", source: "nextjs", target: "payments", type: "service-call", label: "API" },
75
+ { id: "e2", source: "nextjs", target: "users", type: "service-call", label: "API" },
76
+ { id: "e3", source: "payments", target: "postgres", type: "service-call", label: "query" },
77
+ { id: "e4", source: "payments", target: "stripe", type: "service-call", label: "call" },
78
+ { id: "e5", source: "users", target: "postgres", type: "service-call", label: "query" },
79
+ { id: "e6", source: "users", target: "redis", type: "service-call", label: "cache" },
80
+ { id: "e7", source: "users", target: "openai", type: "service-call", label: "call" },
81
+ ]
82
+
83
+ const LAYER_ORDER = ["frontend", "backend", "database", "queue", "infra", "external"]
84
+ const LAYER_LABELS: Record<string, string> = {
85
+ frontend: "Frontend",
86
+ backend: "Backend",
87
+ database: "Data Layer",
88
+ queue: "Queue / Workers",
89
+ infra: "Infrastructure",
90
+ external: "External Services",
91
+ }
92
+
93
+ const typeColors: Record<string, { bg: string; border: string; text: string }> = {
94
+ framework: { bg: "fill-chart-1/20", border: "stroke-chart-1", text: "fill-chart-1" },
95
+ npm: { bg: "fill-chart-1/10", border: "stroke-chart-1/50", text: "fill-foreground" },
96
+ service: { bg: "fill-chart-2/20", border: "stroke-chart-2", text: "fill-chart-2" },
97
+ database: { bg: "fill-chart-3/20", border: "stroke-chart-3", text: "fill-chart-3" },
98
+ queue: { bg: "fill-chart-4/20", border: "stroke-chart-4", text: "fill-chart-4" },
99
+ llm: { bg: "fill-warning/20", border: "stroke-warning", text: "fill-warning" },
100
+ infra: { bg: "fill-info/20", border: "stroke-info", text: "fill-info" },
101
+ }
102
+
103
+ function layoutGraph(graphNodes: GraphNode[], direction: "TB" | "LR") {
104
+ // Group nodes by layer
105
+ const byLayer = new Map<string, GraphNode[]>()
106
+ for (const node of graphNodes) {
107
+ const layer = node.layer || "backend"
108
+ if (!byLayer.has(layer)) byLayer.set(layer, [])
109
+ byLayer.get(layer)!.push(node)
110
+ }
111
+
112
+ const activeLayers = LAYER_ORDER.filter((l) => (byLayer.get(l)?.length || 0) > 0)
113
+
114
+ const NODE_W = 110
115
+ const NODE_H = 40
116
+ const H_GAP = 20
117
+ const LAYER_PAD = 40
118
+ const LAYER_GAP = 30
119
+
120
+ const layers: { id: string; label: string; x: number; y: number; width: number; height: number }[] = []
121
+ const positioned: { id: string; label: string; type: string; layer: string; x: number; y: number; node: GraphNode }[] = []
122
+
123
+ let currentY = 40
124
+ for (const layerId of activeLayers) {
125
+ const group = byLayer.get(layerId) || []
126
+ // Cap at 12 nodes per layer to keep diagram readable
127
+ const visible = group.slice(0, 12)
128
+ const rowWidth = visible.length * (NODE_W + H_GAP) - H_GAP
129
+ const layerWidth = Math.max(rowWidth + LAYER_PAD * 2, 200)
130
+
131
+ layers.push({
132
+ id: layerId,
133
+ label: LAYER_LABELS[layerId] || layerId,
134
+ x: 40,
135
+ y: currentY,
136
+ width: layerWidth,
137
+ height: NODE_H + LAYER_PAD + 20,
138
+ })
139
+
140
+ const startX = 40 + LAYER_PAD
141
+ visible.forEach((node, i) => {
142
+ positioned.push({
143
+ id: node.id,
144
+ label: node.name,
145
+ type: node.type,
146
+ layer: node.layer,
147
+ x: startX + i * (NODE_W + H_GAP),
148
+ y: currentY + 30,
149
+ node,
150
+ })
151
+ })
152
+
153
+ if (group.length > 12) {
154
+ positioned.push({
155
+ id: `${layerId}_overflow`,
156
+ label: `+${group.length - 12} more`,
157
+ type: "overflow",
158
+ layer: layerId,
159
+ x: startX + 12 * (NODE_W + H_GAP),
160
+ y: currentY + 30,
161
+ node: { id: `${layerId}_overflow`, name: `+${group.length - 12} more`, type: "overflow", layer: layerId },
162
+ })
163
+ }
164
+
165
+ currentY += NODE_H + LAYER_PAD + 20 + LAYER_GAP
166
+ }
167
+
168
+ const maxWidth = Math.max(...layers.map((l) => l.x + l.width), 600) + 40
169
+ const totalHeight = currentY + 20
170
+
171
+ return { layers, nodes: positioned, svgWidth: maxWidth, svgHeight: totalHeight }
172
+ }
173
+
174
+ // =============================================================================
175
+ // NODE DETAIL POPOVER
176
+ // =============================================================================
177
+
178
+ interface NodeDetailProps {
179
+ node: GraphNode
180
+ allNodes: GraphNode[]
181
+ edges: GraphEdge[]
182
+ position: { x: number; y: number }
183
+ onClose: () => void
184
+ }
185
+
186
+ function NodeDetail({ node, allNodes, edges, position, onClose }: NodeDetailProps) {
187
+ const nodeMap = useMemo(() => {
188
+ const m = new Map<string, GraphNode>()
189
+ for (const n of allNodes) m.set(n.id, n)
190
+ return m
191
+ }, [allNodes])
192
+
193
+ // Find connections
194
+ const outgoing = edges.filter((e) => e.source === node.id)
195
+ const incoming = edges.filter((e) => e.target === node.id)
196
+
197
+ // Find hosted services
198
+ const hostedServices = node.hosts?.map((id) => nodeMap.get(id)).filter(Boolean) || []
199
+ const hostedBy = node.hostedBy ? nodeMap.get(node.hostedBy) : null
200
+
201
+ return (
202
+ <div
203
+ className="absolute z-50 w-72 rounded-lg border border-border bg-card shadow-lg"
204
+ style={{ left: position.x, top: position.y }}
205
+ >
206
+ <div className="flex items-center justify-between border-b border-border px-4 py-3">
207
+ <div className="flex items-center gap-2">
208
+ <span className="font-mono text-sm font-medium text-foreground">{node.name}</span>
209
+ {node.version && (
210
+ <Badge variant="outline" className="text-xs">v{node.version}</Badge>
211
+ )}
212
+ </div>
213
+ <button onClick={onClose} className="text-muted-foreground hover:text-foreground">
214
+ <X className="h-4 w-4" />
215
+ </button>
216
+ </div>
217
+
218
+ <div className="space-y-3 px-4 py-3">
219
+ {/* Type & Layer */}
220
+ <div className="flex items-center gap-2">
221
+ <Badge variant="outline" className="text-xs capitalize">{node.type}</Badge>
222
+ <Badge variant="outline" className="text-xs capitalize">{node.layer}</Badge>
223
+ </div>
224
+
225
+ {/* Purpose */}
226
+ {node.purpose && (
227
+ <p className="text-xs text-muted-foreground">{node.purpose}</p>
228
+ )}
229
+
230
+ {/* Hosted By */}
231
+ {hostedBy && (
232
+ <div>
233
+ <p className="text-xs font-medium text-muted-foreground">Hosted on</p>
234
+ <div className="mt-1 flex items-center gap-1.5">
235
+ <Server className="h-3 w-3 text-info" />
236
+ <span className="text-xs text-foreground">{hostedBy.name}</span>
237
+ </div>
238
+ </div>
239
+ )}
240
+
241
+ {/* Hosts */}
242
+ {hostedServices.length > 0 && (
243
+ <div>
244
+ <p className="text-xs font-medium text-muted-foreground">Hosts</p>
245
+ <div className="mt-1 flex flex-wrap gap-1">
246
+ {hostedServices.map((s) => (
247
+ <Badge key={s!.id} variant="outline" className="text-xs">
248
+ {s!.name}
249
+ </Badge>
250
+ ))}
251
+ </div>
252
+ </div>
253
+ )}
254
+
255
+ {/* Connections */}
256
+ {outgoing.length > 0 && (
257
+ <div>
258
+ <p className="text-xs font-medium text-muted-foreground">Connects to ({outgoing.length})</p>
259
+ <div className="mt-1 space-y-1">
260
+ {outgoing.slice(0, 5).map((e) => {
261
+ const target = nodeMap.get(e.target)
262
+ return (
263
+ <div key={e.id} className="flex items-center gap-1.5 text-xs text-foreground">
264
+ <ArrowRight className="h-3 w-3 text-muted-foreground" />
265
+ <span>{target?.name || e.target}</span>
266
+ {e.label && <span className="text-muted-foreground">({e.label})</span>}
267
+ </div>
268
+ )
269
+ })}
270
+ {outgoing.length > 5 && (
271
+ <p className="text-xs text-muted-foreground">+{outgoing.length - 5} more</p>
272
+ )}
273
+ </div>
274
+ </div>
275
+ )}
276
+
277
+ {incoming.length > 0 && (
278
+ <div>
279
+ <p className="text-xs font-medium text-muted-foreground">Connected from ({incoming.length})</p>
280
+ <div className="mt-1 space-y-1">
281
+ {incoming.slice(0, 5).map((e) => {
282
+ const source = nodeMap.get(e.source)
283
+ return (
284
+ <div key={e.id} className="flex items-center gap-1.5 text-xs text-foreground">
285
+ <ArrowRight className="h-3 w-3 rotate-180 text-muted-foreground" />
286
+ <span>{source?.name || e.source}</span>
287
+ {e.label && <span className="text-muted-foreground">({e.label})</span>}
288
+ </div>
289
+ )
290
+ })}
291
+ {incoming.length > 5 && (
292
+ <p className="text-xs text-muted-foreground">+{incoming.length - 5} more</p>
293
+ )}
294
+ </div>
295
+ </div>
296
+ )}
297
+
298
+ {/* Config Files */}
299
+ {node.configFiles && node.configFiles.length > 0 && (
300
+ <div>
301
+ <p className="text-xs font-medium text-muted-foreground">Config files</p>
302
+ <div className="mt-1 space-y-0.5">
303
+ {node.configFiles.slice(0, 3).map((f, i) => (
304
+ <p key={i} className="truncate font-mono text-xs text-muted-foreground">{f.split("/").pop()}</p>
305
+ ))}
306
+ </div>
307
+ </div>
308
+ )}
309
+
310
+ {/* Tags */}
311
+ {node.tags && node.tags.length > 0 && (
312
+ <div className="flex flex-wrap gap-1">
313
+ {[...new Set(node.tags)].slice(0, 6).map((tag) => (
314
+ <span key={tag} className="rounded bg-secondary px-1.5 py-0.5 text-xs text-muted-foreground">
315
+ {tag}
316
+ </span>
317
+ ))}
318
+ </div>
319
+ )}
320
+ </div>
321
+ </div>
322
+ )
323
+ }
324
+
325
+ // =============================================================================
326
+ // MAIN DIAGRAM VIEW
327
+ // =============================================================================
328
+
329
+ export function DiagramView({ selectedComponent }: DiagramViewProps) {
330
+ const [copied, setCopied] = useState(false)
331
+ const [zoom, setZoom] = useState(1)
332
+ const [direction, setDirection] = useState<"TB" | "LR">("TB")
333
+ const [graphData, setGraphData] = useState<GraphData | null>(null)
334
+ const [isLoading, setIsLoading] = useState(true)
335
+ const [isDemo, setIsDemo] = useState(false)
336
+ const [showFlowArrows, setShowFlowArrows] = useState(true)
337
+ const [selectedNode, setSelectedNode] = useState<{ node: GraphNode; x: number; y: number } | null>(null)
338
+ const svgRef = useRef<SVGSVGElement>(null)
339
+ const { activeProjectPath } = useActiveProject()
340
+
341
+ useEffect(() => {
342
+ async function fetchGraph() {
343
+ setIsLoading(true)
344
+ try {
345
+ const params = new URLSearchParams()
346
+ if (activeProjectPath) params.set("path", activeProjectPath)
347
+ const res = await fetch(`/api/graph?${params}`)
348
+ const json = await res.json()
349
+ if (json.success && json.data && json.data.nodes?.length > 0) {
350
+ setGraphData(json.data)
351
+ setIsDemo(false)
352
+ } else {
353
+ setGraphData({ nodes: DEMO_NODES, edges: DEMO_EDGES })
354
+ setIsDemo(true)
355
+ }
356
+ } catch {
357
+ setGraphData({ nodes: DEMO_NODES, edges: DEMO_EDGES })
358
+ setIsDemo(true)
359
+ }
360
+ setIsLoading(false)
361
+ }
362
+ fetchGraph()
363
+ }, [activeProjectPath])
364
+
365
+ const currentNodes = graphData?.nodes || DEMO_NODES
366
+ const currentEdges = graphData?.edges || DEMO_EDGES
367
+
368
+ const layout = useMemo(
369
+ () => layoutGraph(currentNodes, direction),
370
+ [currentNodes, direction]
371
+ )
372
+
373
+ // Filter edges to those where both endpoints are positioned in the layout
374
+ const flowEdges = useMemo(() => {
375
+ const positionedIds = new Set(layout.nodes.map((n) => n.id))
376
+ return currentEdges.filter(
377
+ (e) => e.source !== e.target && positionedIds.has(e.source) && positionedIds.has(e.target)
378
+ )
379
+ }, [currentEdges, layout.nodes])
380
+
381
+ // Build hosting groups for visual nesting
382
+ const hostingGroups = useMemo(() => {
383
+ const groups: { host: GraphNode; hosted: GraphNode[] }[] = []
384
+ for (const node of currentNodes) {
385
+ if (node.hosts && node.hosts.length > 0) {
386
+ const hosted = node.hosts
387
+ .map((id) => currentNodes.find((n) => n.id === id))
388
+ .filter(Boolean) as GraphNode[]
389
+ if (hosted.length > 0) {
390
+ groups.push({ host: node, hosted })
391
+ }
392
+ }
393
+ }
394
+ return groups
395
+ }, [currentNodes])
396
+
397
+ const handleCopy = useCallback(() => {
398
+ let code = `flowchart ${direction}\n`
399
+ const byLayer = new Map<string, GraphNode[]>()
400
+ for (const n of currentNodes) {
401
+ const l = n.layer || "backend"
402
+ if (!byLayer.has(l)) byLayer.set(l, [])
403
+ byLayer.get(l)!.push(n)
404
+ }
405
+ for (const [layer, group] of byLayer) {
406
+ const label = LAYER_LABELS[layer] || layer
407
+ code += ` subgraph ${label}\n`
408
+ for (const n of group) {
409
+ const shape = n.type === "database" ? `[(${n.name})]` : `[${n.name}]`
410
+ code += ` ${n.id}${shape}\n`
411
+ }
412
+ code += ` end\n`
413
+ }
414
+ code += "\n"
415
+ for (const e of flowEdges) {
416
+ const label = e.label || e.type
417
+ code += ` ${e.source} -->|${label}| ${e.target}\n`
418
+ }
419
+ navigator.clipboard.writeText(code)
420
+ setCopied(true)
421
+ setTimeout(() => setCopied(false), 2000)
422
+ }, [currentNodes, flowEdges, direction])
423
+
424
+ const getNodePos = (nodeId: string) => {
425
+ const n = layout.nodes.find((n) => n.id === nodeId)
426
+ return n ? { x: n.x, y: n.y } : null
427
+ }
428
+
429
+ const handleNodeClick = (node: GraphNode, event: React.MouseEvent) => {
430
+ if (node.type === "overflow") return
431
+ const svgRect = svgRef.current?.getBoundingClientRect()
432
+ if (!svgRect) return
433
+
434
+ // Position popover near the click but within bounds
435
+ const x = event.clientX - svgRect.left + 10
436
+ const y = event.clientY - svgRect.top + 10
437
+
438
+ setSelectedNode({ node, x, y })
439
+ }
440
+
441
+ return (
442
+ <div className="space-y-6">
443
+ <div className="flex items-start justify-between">
444
+ <div>
445
+ <h1 className="text-2xl font-semibold text-foreground">Architecture Diagram</h1>
446
+ <p className="text-sm text-muted-foreground">
447
+ {isDemo ? "Demo data — run a scan to see your real architecture" : `${currentNodes.length} components, ${flowEdges.length} connections`}
448
+ </p>
449
+ </div>
450
+ </div>
451
+
452
+ {isDemo && (
453
+ <div className="flex items-center gap-2 rounded-lg border border-info/30 bg-info/10 p-3">
454
+ <Info className="h-4 w-4 text-info" />
455
+ <p className="text-sm text-info">
456
+ Showing demo data. Run <code className="rounded bg-info/20 px-1">navgator scan</code> to visualize your project.
457
+ </p>
458
+ </div>
459
+ )}
460
+
461
+ {/* Hosting Groups Summary */}
462
+ {hostingGroups.length > 0 && (
463
+ <div className="flex flex-wrap gap-3">
464
+ {hostingGroups.map((group) => (
465
+ <div key={group.host.id} className="flex items-center gap-2 rounded-lg border border-info/20 bg-info/5 px-3 py-1.5">
466
+ <Server className="h-3.5 w-3.5 text-info" />
467
+ <span className="text-xs font-medium text-foreground">{group.host.name}</span>
468
+ <span className="text-xs text-muted-foreground">hosts</span>
469
+ {group.hosted.map((h) => (
470
+ <Badge key={h.id} variant="outline" className="text-xs">{h.name}</Badge>
471
+ ))}
472
+ </div>
473
+ ))}
474
+ </div>
475
+ )}
476
+
477
+ {/* Controls */}
478
+ <div className="flex flex-wrap items-center gap-3">
479
+ <div className="flex items-center gap-1 rounded-lg border border-border bg-card p-1">
480
+ <Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={() => setZoom((z) => Math.max(0.5, z - 0.1))}>
481
+ <ZoomOut className="h-4 w-4" />
482
+ </Button>
483
+ <span className="w-12 text-center text-sm text-muted-foreground">{Math.round(zoom * 100)}%</span>
484
+ <Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={() => setZoom((z) => Math.min(2, z + 0.1))}>
485
+ <ZoomIn className="h-4 w-4" />
486
+ </Button>
487
+ <Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={() => setZoom(1)}>
488
+ <RotateCcw className="h-4 w-4" />
489
+ </Button>
490
+ </div>
491
+
492
+ <Select value={direction} onValueChange={(v) => setDirection(v as "TB" | "LR")}>
493
+ <SelectTrigger className="w-32 bg-card">
494
+ <SelectValue />
495
+ </SelectTrigger>
496
+ <SelectContent>
497
+ <SelectItem value="TB">Top to Bottom</SelectItem>
498
+ <SelectItem value="LR">Left to Right</SelectItem>
499
+ </SelectContent>
500
+ </Select>
501
+
502
+ <Button
503
+ variant={showFlowArrows ? "default" : "outline"}
504
+ size="sm"
505
+ className="gap-2"
506
+ onClick={() => setShowFlowArrows(!showFlowArrows)}
507
+ >
508
+ <ArrowRight className="h-4 w-4" />
509
+ Flow Arrows
510
+ </Button>
511
+
512
+ <div className="flex-1" />
513
+
514
+ <Button variant="outline" size="sm" className="gap-2 bg-transparent" onClick={handleCopy}>
515
+ {copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
516
+ {copied ? "Copied" : "Copy Mermaid"}
517
+ </Button>
518
+ </div>
519
+
520
+ {/* Diagram */}
521
+ <Card className="bg-card overflow-hidden">
522
+ <CardContent className="p-0">
523
+ <div className="relative overflow-auto bg-background/50" style={{ minHeight: 400 }}>
524
+ {isLoading ? (
525
+ <div className="flex h-96 items-center justify-center">
526
+ <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
527
+ </div>
528
+ ) : (
529
+ <>
530
+ <svg
531
+ ref={svgRef}
532
+ width={layout.svgWidth * zoom}
533
+ height={layout.svgHeight * zoom}
534
+ className="mx-auto"
535
+ >
536
+ <g transform={`scale(${zoom})`}>
537
+ {/* Layer backgrounds */}
538
+ {layout.layers.map((layer) => (
539
+ <g key={layer.id}>
540
+ <rect
541
+ x={layer.x}
542
+ y={layer.y}
543
+ width={layer.width}
544
+ height={layer.height}
545
+ rx={8}
546
+ className="fill-secondary/30 stroke-border/50"
547
+ strokeWidth={1}
548
+ strokeDasharray="4 2"
549
+ />
550
+ <text
551
+ x={layer.x + 12}
552
+ y={layer.y + 18}
553
+ className="fill-muted-foreground font-medium uppercase tracking-wider"
554
+ style={{ fontSize: 10 }}
555
+ >
556
+ {layer.label}
557
+ </text>
558
+ </g>
559
+ ))}
560
+
561
+ {/* Hosting group outlines */}
562
+ {hostingGroups.map((group) => {
563
+ const hostPos = layout.nodes.find((n) => n.id === group.host.id)
564
+ const hostedPositions = group.hosted
565
+ .map((h) => layout.nodes.find((n) => n.id === h.id))
566
+ .filter(Boolean)
567
+
568
+ if (!hostPos || hostedPositions.length === 0) return null
569
+
570
+ const allPositions = [hostPos, ...hostedPositions]
571
+ const minX = Math.min(...allPositions.map((p) => p!.x)) - 8
572
+ const minY = Math.min(...allPositions.map((p) => p!.y)) - 8
573
+ const maxX = Math.max(...allPositions.map((p) => p!.x)) + 118
574
+ const maxY = Math.max(...allPositions.map((p) => p!.y)) + 48
575
+
576
+ return (
577
+ <rect
578
+ key={`host-${group.host.id}`}
579
+ x={minX}
580
+ y={minY}
581
+ width={maxX - minX}
582
+ height={maxY - minY}
583
+ rx={10}
584
+ className="fill-none stroke-info/40"
585
+ strokeWidth={2}
586
+ strokeDasharray="6 3"
587
+ />
588
+ )
589
+ })}
590
+
591
+ {/* Flow Edges */}
592
+ {showFlowArrows && flowEdges.map((edge, idx) => {
593
+ const from = getNodePos(edge.source)
594
+ const to = getNodePos(edge.target)
595
+ if (!from || !to) return null
596
+
597
+ const x1 = from.x + 55
598
+ const y1 = from.y + 20
599
+ const x2 = to.x + 55
600
+ const y2 = to.y + 20
601
+
602
+ // Use curved path for better readability
603
+ const midY = (y1 + y2) / 2
604
+ const path = `M ${x1} ${y1} C ${x1} ${midY}, ${x2} ${midY}, ${x2} ${y2}`
605
+
606
+ return (
607
+ <g key={idx}>
608
+ <path
609
+ d={path}
610
+ className="stroke-border"
611
+ fill="none"
612
+ strokeWidth={1.5}
613
+ markerEnd="url(#arrowhead)"
614
+ />
615
+ {edge.label && (
616
+ <text
617
+ x={(x1 + x2) / 2}
618
+ y={midY - 4}
619
+ textAnchor="middle"
620
+ className="fill-muted-foreground"
621
+ style={{ fontSize: 9 }}
622
+ >
623
+ {edge.label}
624
+ </text>
625
+ )}
626
+ </g>
627
+ )
628
+ })}
629
+
630
+ <defs>
631
+ <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
632
+ <polygon points="0 0, 10 3.5, 0 7" className="fill-border" />
633
+ </marker>
634
+ </defs>
635
+
636
+ {/* Nodes */}
637
+ {layout.nodes.map((node) => {
638
+ const colors = typeColors[node.type] || typeColors.service || { bg: "fill-secondary", border: "stroke-border", text: "fill-foreground" }
639
+ const isHighlighted = selectedComponent?.toLowerCase() === node.label.toLowerCase()
640
+ const isSelected = selectedNode?.node.id === node.id
641
+ const hasHosting = node.node.hosts && node.node.hosts.length > 0
642
+
643
+ if (node.type === "overflow") {
644
+ return (
645
+ <g key={node.id}>
646
+ <text x={node.x + 55} y={node.y + 24} textAnchor="middle" className="fill-muted-foreground" style={{ fontSize: 11 }}>
647
+ {node.label}
648
+ </text>
649
+ </g>
650
+ )
651
+ }
652
+
653
+ return (
654
+ <g
655
+ key={node.id}
656
+ className="cursor-pointer"
657
+ onClick={(e) => handleNodeClick(node.node, e)}
658
+ >
659
+ <rect
660
+ x={node.x}
661
+ y={node.y}
662
+ width={110}
663
+ height={40}
664
+ rx={6}
665
+ className={`${colors.bg} ${colors.border} ${
666
+ isSelected ? "stroke-primary stroke-[2.5]" :
667
+ isHighlighted ? "stroke-primary stroke-2" : "stroke-1"
668
+ }`}
669
+ />
670
+ {/* Hosting indicator */}
671
+ {hasHosting && (
672
+ <circle
673
+ cx={node.x + 104}
674
+ cy={node.y + 6}
675
+ r={5}
676
+ className="fill-info stroke-card"
677
+ strokeWidth={1.5}
678
+ />
679
+ )}
680
+ <text
681
+ x={node.x + 55}
682
+ y={node.y + 24}
683
+ textAnchor="middle"
684
+ className={`${colors.text} font-medium`}
685
+ style={{ fontSize: 11 }}
686
+ >
687
+ {node.label.length > 14 ? node.label.slice(0, 12) + "…" : node.label}
688
+ </text>
689
+ </g>
690
+ )
691
+ })}
692
+ </g>
693
+ </svg>
694
+
695
+ {/* Node Detail Popover */}
696
+ {selectedNode && (
697
+ <NodeDetail
698
+ node={selectedNode.node}
699
+ allNodes={currentNodes}
700
+ edges={currentEdges}
701
+ position={{ x: selectedNode.x, y: selectedNode.y }}
702
+ onClose={() => setSelectedNode(null)}
703
+ />
704
+ )}
705
+ </>
706
+ )}
707
+ </div>
708
+ </CardContent>
709
+ </Card>
710
+
711
+ {/* Legend */}
712
+ <Card className="bg-card">
713
+ <CardHeader className="pb-2">
714
+ <CardTitle className="text-sm font-medium">Legend</CardTitle>
715
+ </CardHeader>
716
+ <CardContent>
717
+ <div className="flex flex-wrap gap-4">
718
+ {Object.entries(typeColors).map(([type, colors]) => (
719
+ <div key={type} className="flex items-center gap-2">
720
+ <div
721
+ className={`h-4 w-4 rounded border ${colors.bg.replace("fill-", "bg-")} ${colors.border.replace("stroke-", "border-")}`}
722
+ />
723
+ <span className="text-sm capitalize text-muted-foreground">{type}</span>
724
+ </div>
725
+ ))}
726
+ <div className="flex items-center gap-2">
727
+ <div className="h-4 w-4 rounded border-2 border-dashed border-info/40" />
728
+ <span className="text-sm text-muted-foreground">Hosting group</span>
729
+ </div>
730
+ </div>
731
+ </CardContent>
732
+ </Card>
733
+ </div>
734
+ )
735
+ }