diff --git a/README.md b/README.md index 93d5ef35..f259159a 100644 --- a/README.md +++ b/README.md @@ -50,16 +50,16 @@ determinstic tool. `napi` aims to support all major programming languages. Here is the current status: -| Language/Framework | Status | Related Issues | -| ------------------ | -------------- | --------------------------------------------------- | -| Python | ✅ Supported | [#28](https://github.com/nanoapi-io/napi/issues/28) | -| C# | ✅ Supported | [#31](https://github.com/nanoapi-io/napi/issues/31) | -| PHP | 🚧 In Progress | [#30](https://github.com/nanoapi-io/napi/issues/30) | -| Java | 🚧 In Progress | [#32](https://github.com/nanoapi-io/napi/issues/32) | -| C | 🚧 In Progress | Not Tracked Yet | -| C++ | 🚧 In Progress | Not Tracked Yet | -| JavaScript | 🚧 In Progress | Not Tracked Yet | -| TypeScript | 🚧 In Progress | Not Tracked Yet | +| Language/Framework | Status | +| ------------------ | -------------- | +| Python | ✅ Supported | +| C# | ✅ Supported | +| C | ✅ In Progress | +| Java | 🚧 In Progress | +| C++ | 🚧 In Progress | +| PHP | 🚧 In Progress | +| JavaScript | 🚧 In Progress | +| TypeScript | 🚧 In Progress | For the latest updates, visit our [project board](/projects). @@ -104,17 +104,30 @@ Initialize the project. This step is required before running any other command. This will create a .napirc configuration file in the project root, storing paths and settings necessary for further commands. -### `napi audit view` +### `napi manifest generate` -Scan and audit your codebase for potential improvements, vulnerabilities, and -maintainability issues. This command opens the NanoAPI UI in your default -browser, providing a clear overview of what areas of your code would benefit -most from refactoring or cleanup. +Generate a manifest of your codebase that captures its structure, dependencies, +and relationships. This command analyzes your code and writes the manifest data +to the configured `napi_out` directory in your project. -> **Important**: Run napi audit view periodically, especially before major -> refactoring efforts, to ensure your code is in good shape. It will soon also -> be possible to integrate that command into CI/CD workflows to catch -> architectural-level-quality issues early. +### `napi manifest view` + +Open the NanoAPI UI in your default browser to visualize and explore the +manifest generated by `napi manifest generate`. This provides an interactive +view of your codebase's architecture, helping you identify areas for potential +improvements and refactoring. + +### `napi extract` + +Extract specific symbols (functions, classes, etc.) from your codebase into +separate files. Use the format `--symbol file|symbol` where file is the path +relative to your project root and symbol is the name to extract. The UI can +generate these commands for convenient copy-pasting when browsing your code. + +> **Important**: Run `napi manifest generate` whenever you make significant +> changes to your codebase to ensure your manifest stays up-to-date. The +> manifest data can be integrated into CI/CD workflows to track architectural +> changes over time. ## CI/CD Integration diff --git a/deno.lock b/deno.lock index 982a30db..191b6246 100644 --- a/deno.lock +++ b/deno.lock @@ -23,18 +23,15 @@ "jsr:@std/path@^1.1.0": "1.1.0", "jsr:@std/testing@^1.0.12": "1.0.13", "npm:@deno/vite-plugin@^1.0.4": "1.0.4_vite@6.3.5__picomatch@4.0.2_@types+node@22.15.15", - "npm:@inquirer/prompts@*": "7.5.3_@types+node@22.15.15", "npm:@inquirer/prompts@^7.5.1": "7.5.3_@types+node@22.15.15", - "npm:@radix-ui/react-dialog@^1.1.13": "1.1.14_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", - "npm:@radix-ui/react-dropdown-menu@^2.1.14": "2.1.15_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", - "npm:@radix-ui/react-label@^2.1.6": "2.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", - "npm:@radix-ui/react-scroll-area@^1.2.8": "1.2.9_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", - "npm:@radix-ui/react-separator@^1.1.6": "1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", - "npm:@radix-ui/react-slider@^1.3.4": "1.3.5_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", - "npm:@radix-ui/react-slot@^1.2.2": "1.2.3_@types+react@19.1.6_react@19.1.0", - "npm:@radix-ui/react-toast@^1.2.13": "1.2.14_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", - "npm:@radix-ui/react-tooltip@^1.2.6": "1.2.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", - "npm:@radix-ui/themes@^3.2.1": "3.2.1_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-dialog@^1.1.14": "1.1.14_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-dropdown-menu@^2.1.15": "2.1.15_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-label@^2.1.7": "2.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-scroll-area@^1.2.9": "1.2.9_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-separator@^1.1.7": "1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-slider@^1.3.5": "1.3.5_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:@radix-ui/react-slot@^1.2.3": "1.2.3_@types+react@19.1.6_react@19.1.0", + "npm:@radix-ui/react-tooltip@^1.2.7": "1.2.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", "npm:@tailwindcss/vite@^4.1.5": "4.1.8_vite@6.3.5__picomatch@4.0.2_@types+node@22.15.15", "npm:@types/cytoscape-fcose@^2.2.4": "2.2.4", "npm:@types/node@*": "22.15.15", @@ -47,30 +44,25 @@ "npm:clsx@^2.1.1": "2.1.1", "npm:cytoscape-fcose@^2.2.0": "2.2.0_cytoscape@3.31.1", "npm:cytoscape@3.31.1": "3.31.1", - "npm:glob@*": "11.0.2", "npm:glob@^11.0.2": "11.0.2", - "npm:lucide-react@0.508": "0.508.0_react@19.1.0", + "npm:lucide-react@0.511": "0.511.0_react@19.1.0", + "npm:next-themes@~0.4.6": "0.4.6_react@19.1.0_react-dom@19.1.0__react@19.1.0", "npm:path-to-regexp@^6.3.0": "6.3.0", "npm:react-dom@^19.1.0": "19.1.0_react@19.1.0", "npm:react-resizable-panels@^3.0.1": "3.0.2_react@19.1.0_react-dom@19.1.0__react@19.1.0", "npm:react-router@^7.5.3": "7.6.1_react@19.1.0_react-dom@19.1.0__react@19.1.0", "npm:react@^19.1.0": "19.1.0", - "npm:tailwind-merge@^3.2.0": "3.3.0", + "npm:sonner@^2.0.4": "2.0.4_react@19.1.0_react-dom@19.1.0__react@19.1.0", + "npm:tailwind-merge@^3.3.0": "3.3.0", "npm:tailwindcss@^4.1.5": "4.1.8", - "npm:tree-sitter-c-sharp@*": "0.23.1_tree-sitter@0.22.4", "npm:tree-sitter-c-sharp@~0.23.1": "0.23.1_tree-sitter@0.22.4", - "npm:tree-sitter-c@*": "0.23.6_tree-sitter@0.22.4", "npm:tree-sitter-c@0.23.6": "0.23.6_tree-sitter@0.22.4", - "npm:tree-sitter-python@*": "0.23.6_tree-sitter@0.22.4", "npm:tree-sitter-python@~0.23.6": "0.23.6_tree-sitter@0.22.4", - "npm:tree-sitter@*": "0.22.4", "npm:tree-sitter@~0.22.4": "0.22.4", - "npm:tw-animate-css@^1.2.9": "1.3.3", + "npm:tw-animate-css@^1.3.3": "1.3.3", "npm:vite@*": "6.3.5_picomatch@4.0.2_@types+node@22.15.15", "npm:vite@^6.3.5": "6.3.5_picomatch@4.0.2_@types+node@22.15.15", - "npm:yargs@*": "17.7.2", "npm:yargs@^17.7.2": "17.7.2", - "npm:zod@*": "3.25.46", "npm:zod@^3.24.4": "3.25.46" }, "jsr": { @@ -812,70 +804,12 @@ "@tybys/wasm-util" ] }, - "@radix-ui/colors@3.0.0": { - "integrity": "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==" - }, "@radix-ui/number@1.1.1": { "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==" }, "@radix-ui/primitive@1.1.2": { "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==" }, - "@radix-ui/react-accessible-icon@1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==", - "dependencies": [ - "@radix-ui/react-visually-hidden", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-accordion@1.2.11_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-collapsible", - "@radix-ui/react-collection", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-direction", - "@radix-ui/react-id", - "@radix-ui/react-primitive", - "@radix-ui/react-use-controllable-state", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-alert-dialog@1.1.14_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-dialog", - "@radix-ui/react-primitive", - "@radix-ui/react-slot", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, "@radix-ui/react-arrow@1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "dependencies": [ @@ -890,80 +824,6 @@ "@types/react-dom" ] }, - "@radix-ui/react-aspect-ratio@1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==", - "dependencies": [ - "@radix-ui/react-primitive", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-avatar@1.1.10_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", - "dependencies": [ - "@radix-ui/react-context", - "@radix-ui/react-primitive", - "@radix-ui/react-use-callback-ref", - "@radix-ui/react-use-is-hydrated", - "@radix-ui/react-use-layout-effect", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-checkbox@1.3.2_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-presence", - "@radix-ui/react-primitive", - "@radix-ui/react-use-controllable-state", - "@radix-ui/react-use-previous", - "@radix-ui/react-use-size", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-collapsible@1.1.11_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-id", - "@radix-ui/react-presence", - "@radix-ui/react-primitive", - "@radix-ui/react-use-controllable-state", - "@radix-ui/react-use-layout-effect", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, "@radix-ui/react-collection@1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "dependencies": [ @@ -991,25 +851,6 @@ "@types/react" ] }, - "@radix-ui/react-context-menu@2.2.15_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-UsQUMjcYTsBjTSXw0P3GO0werEQvUY2plgRQuKoCTtkNr45q1DiL51j4m7gxhABzZ0BadoXNsIbg7F3KwiUBbw==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-context", - "@radix-ui/react-menu", - "@radix-ui/react-primitive", - "@radix-ui/react-use-callback-ref", - "@radix-ui/react-use-controllable-state", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, "@radix-ui/react-context@1.1.2_@types+react@19.1.6_react@19.1.0": { "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "dependencies": [ @@ -1027,393 +868,44 @@ "@radix-ui/react-compose-refs", "@radix-ui/react-context", "@radix-ui/react-dismissable-layer", - "@radix-ui/react-focus-guards", - "@radix-ui/react-focus-scope", - "@radix-ui/react-id", - "@radix-ui/react-portal", - "@radix-ui/react-presence", - "@radix-ui/react-primitive", - "@radix-ui/react-slot", - "@radix-ui/react-use-controllable-state", - "@types/react", - "@types/react-dom", - "aria-hidden", - "react", - "react-dom", - "react-remove-scroll" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-direction@1.1.1_@types+react@19.1.6_react@19.1.0": { - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "dependencies": [ - "@types/react", - "react" - ], - "optionalPeers": [ - "@types/react" - ] - }, - "@radix-ui/react-dismissable-layer@1.1.10_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-primitive", - "@radix-ui/react-use-callback-ref", - "@radix-ui/react-use-escape-keydown", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-dropdown-menu@2.1.15_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-id", - "@radix-ui/react-menu", - "@radix-ui/react-primitive", - "@radix-ui/react-use-controllable-state", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-focus-guards@1.1.2_@types+react@19.1.6_react@19.1.0": { - "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", - "dependencies": [ - "@types/react", - "react" - ], - "optionalPeers": [ - "@types/react" - ] - }, - "@radix-ui/react-focus-scope@1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", - "dependencies": [ - "@radix-ui/react-compose-refs", - "@radix-ui/react-primitive", - "@radix-ui/react-use-callback-ref", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-form@0.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-IXLKFnaYvFg/KkeV5QfOX7tRnwHXp127koOFUjLWMTrRv5Rny3DQcAtIFFeA/Cli4HHM8DuJCXAUsgnFVJndlw==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-id", - "@radix-ui/react-label", - "@radix-ui/react-primitive", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-hover-card@1.1.14_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-CPYZ24Mhirm+g6D8jArmLzjYu4Eyg3TTUHswR26QgzXBHBe64BO/RHOJKzmF/Dxb4y4f9PKyJdwm/O/AhNkb+Q==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-dismissable-layer", - "@radix-ui/react-popper", - "@radix-ui/react-portal", - "@radix-ui/react-presence", - "@radix-ui/react-primitive", - "@radix-ui/react-use-controllable-state", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-id@1.1.1_@types+react@19.1.6_react@19.1.0": { - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "dependencies": [ - "@radix-ui/react-use-layout-effect", - "@types/react", - "react" - ], - "optionalPeers": [ - "@types/react" - ] - }, - "@radix-ui/react-label@2.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", - "dependencies": [ - "@radix-ui/react-primitive", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-menu@2.1.15_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-collection", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-direction", - "@radix-ui/react-dismissable-layer", - "@radix-ui/react-focus-guards", - "@radix-ui/react-focus-scope", - "@radix-ui/react-id", - "@radix-ui/react-popper", - "@radix-ui/react-portal", - "@radix-ui/react-presence", - "@radix-ui/react-primitive", - "@radix-ui/react-roving-focus", - "@radix-ui/react-slot", - "@radix-ui/react-use-callback-ref", - "@types/react", - "@types/react-dom", - "aria-hidden", - "react", - "react-dom", - "react-remove-scroll" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-menubar@1.1.15_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-Z71C7LGD+YDYo3TV81paUs8f3Zbmkvg6VLRQpKYfzioOE6n7fOhA3ApK/V/2Odolxjoc4ENk8AYCjohCNayd5A==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-collection", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-direction", - "@radix-ui/react-id", - "@radix-ui/react-menu", - "@radix-ui/react-primitive", - "@radix-ui/react-roving-focus", - "@radix-ui/react-use-controllable-state", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-navigation-menu@1.2.13_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-WG8wWfDiJlSF5hELjwfjSGOXcBR/ZMhBFCGYe8vERpC39CQYZeq1PQ2kaYHdye3V95d06H89KGMsVCIE4LWo3g==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-collection", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-direction", - "@radix-ui/react-dismissable-layer", - "@radix-ui/react-id", - "@radix-ui/react-presence", - "@radix-ui/react-primitive", - "@radix-ui/react-use-callback-ref", - "@radix-ui/react-use-controllable-state", - "@radix-ui/react-use-layout-effect", - "@radix-ui/react-use-previous", - "@radix-ui/react-visually-hidden", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-one-time-password-field@0.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-w1vm7AGI8tNXVovOK7TYQHrAGpRF7qQL+ENpT1a743De5Zmay2RbWGKAiYDKIyIuqptns+znCKwNztE2xl1n0Q==", - "dependencies": [ - "@radix-ui/number", - "@radix-ui/primitive", - "@radix-ui/react-collection", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-direction", - "@radix-ui/react-primitive", - "@radix-ui/react-roving-focus", - "@radix-ui/react-use-controllable-state", - "@radix-ui/react-use-effect-event", - "@radix-ui/react-use-is-hydrated", - "@radix-ui/react-use-layout-effect", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-password-toggle-field@0.1.2_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-F90uYnlBsLPU1UbSLciLsWQmk8+hdWa6SFw4GXaIdNWxFxI5ITKVdAG64f+Twaa9ic6xE7pqxPyUmodrGjT4pQ==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-id", - "@radix-ui/react-primitive", - "@radix-ui/react-use-controllable-state", - "@radix-ui/react-use-effect-event", - "@radix-ui/react-use-is-hydrated", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-popover@1.1.14_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-dismissable-layer", - "@radix-ui/react-focus-guards", - "@radix-ui/react-focus-scope", - "@radix-ui/react-id", - "@radix-ui/react-popper", - "@radix-ui/react-portal", - "@radix-ui/react-presence", - "@radix-ui/react-primitive", - "@radix-ui/react-slot", - "@radix-ui/react-use-controllable-state", - "@types/react", - "@types/react-dom", - "aria-hidden", - "react", - "react-dom", - "react-remove-scroll" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-popper@1.2.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", - "dependencies": [ - "@floating-ui/react-dom", - "@radix-ui/react-arrow", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-primitive", - "@radix-ui/react-use-callback-ref", - "@radix-ui/react-use-layout-effect", - "@radix-ui/react-use-rect", - "@radix-ui/react-use-size", - "@radix-ui/rect", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-portal@1.1.9_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "dependencies": [ - "@radix-ui/react-primitive", - "@radix-ui/react-use-layout-effect", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, - "@radix-ui/react-presence@1.1.4_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", - "dependencies": [ - "@radix-ui/react-compose-refs", - "@radix-ui/react-use-layout-effect", + "@radix-ui/react-focus-guards", + "@radix-ui/react-focus-scope", + "@radix-ui/react-id", + "@radix-ui/react-portal", + "@radix-ui/react-presence", + "@radix-ui/react-primitive", + "@radix-ui/react-slot", + "@radix-ui/react-use-controllable-state", "@types/react", "@types/react-dom", + "aria-hidden", "react", - "react-dom" + "react-dom", + "react-remove-scroll" ], "optionalPeers": [ "@types/react", "@types/react-dom" ] }, - "@radix-ui/react-primitive@2.1.3_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "@radix-ui/react-direction@1.1.1_@types+react@19.1.6_react@19.1.0": { + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", "dependencies": [ - "@radix-ui/react-slot", "@types/react", - "@types/react-dom", - "react", - "react-dom" + "react" ], "optionalPeers": [ - "@types/react", - "@types/react-dom" + "@types/react" ] }, - "@radix-ui/react-progress@1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", + "@radix-ui/react-dismissable-layer@1.1.10_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", "dependencies": [ - "@radix-ui/react-context", + "@radix-ui/primitive", + "@radix-ui/react-compose-refs", "@radix-ui/react-primitive", + "@radix-ui/react-use-callback-ref", + "@radix-ui/react-use-escape-keydown", "@types/react", "@types/react-dom", "react", @@ -1424,19 +916,16 @@ "@types/react-dom" ] }, - "@radix-ui/react-radio-group@1.3.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g==", + "@radix-ui/react-dropdown-menu@2.1.15_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", "dependencies": [ "@radix-ui/primitive", "@radix-ui/react-compose-refs", "@radix-ui/react-context", - "@radix-ui/react-direction", - "@radix-ui/react-presence", + "@radix-ui/react-id", + "@radix-ui/react-menu", "@radix-ui/react-primitive", - "@radix-ui/react-roving-focus", "@radix-ui/react-use-controllable-state", - "@radix-ui/react-use-previous", - "@radix-ui/react-use-size", "@types/react", "@types/react-dom", "react", @@ -1447,18 +936,22 @@ "@types/react-dom" ] }, - "@radix-ui/react-roving-focus@1.1.10_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", + "@radix-ui/react-focus-guards@1.1.2_@types+react@19.1.6_react@19.1.0": { + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-focus-scope@1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-collection", "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-direction", - "@radix-ui/react-id", "@radix-ui/react-primitive", "@radix-ui/react-use-callback-ref", - "@radix-ui/react-use-controllable-state", "@types/react", "@types/react-dom", "react", @@ -1469,19 +962,22 @@ "@types/react-dom" ] }, - "@radix-ui/react-scroll-area@1.2.9_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==", + "@radix-ui/react-id@1.1.1_@types+react@19.1.6_react@19.1.0": { + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "dependencies": [ - "@radix-ui/number", - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-direction", - "@radix-ui/react-presence", - "@radix-ui/react-primitive", - "@radix-ui/react-use-callback-ref", "@radix-ui/react-use-layout-effect", "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@radix-ui/react-label@2.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", + "dependencies": [ + "@radix-ui/react-primitive", + "@types/react", "@types/react-dom", "react", "react-dom" @@ -1491,10 +987,9 @@ "@types/react-dom" ] }, - "@radix-ui/react-select@2.2.5_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", + "@radix-ui/react-menu@2.1.15_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", "dependencies": [ - "@radix-ui/number", "@radix-ui/primitive", "@radix-ui/react-collection", "@radix-ui/react-compose-refs", @@ -1506,13 +1001,11 @@ "@radix-ui/react-id", "@radix-ui/react-popper", "@radix-ui/react-portal", + "@radix-ui/react-presence", "@radix-ui/react-primitive", + "@radix-ui/react-roving-focus", "@radix-ui/react-slot", "@radix-ui/react-use-callback-ref", - "@radix-ui/react-use-controllable-state", - "@radix-ui/react-use-layout-effect", - "@radix-ui/react-use-previous", - "@radix-ui/react-visually-hidden", "@types/react", "@types/react-dom", "aria-hidden", @@ -1525,10 +1018,19 @@ "@types/react-dom" ] }, - "@radix-ui/react-separator@1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", + "@radix-ui/react-popper@1.2.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", "dependencies": [ + "@floating-ui/react-dom", + "@radix-ui/react-arrow", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", "@radix-ui/react-primitive", + "@radix-ui/react-use-callback-ref", + "@radix-ui/react-use-layout-effect", + "@radix-ui/react-use-rect", + "@radix-ui/react-use-size", + "@radix-ui/rect", "@types/react", "@types/react-dom", "react", @@ -1539,20 +1041,11 @@ "@types/react-dom" ] }, - "@radix-ui/react-slider@1.3.5_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==", + "@radix-ui/react-portal@1.1.9_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "dependencies": [ - "@radix-ui/number", - "@radix-ui/primitive", - "@radix-ui/react-collection", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-direction", "@radix-ui/react-primitive", - "@radix-ui/react-use-controllable-state", "@radix-ui/react-use-layout-effect", - "@radix-ui/react-use-previous", - "@radix-ui/react-use-size", "@types/react", "@types/react-dom", "react", @@ -1563,27 +1056,25 @@ "@types/react-dom" ] }, - "@radix-ui/react-slot@1.2.3_@types+react@19.1.6_react@19.1.0": { - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "@radix-ui/react-presence@1.1.4_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", "dependencies": [ "@radix-ui/react-compose-refs", + "@radix-ui/react-use-layout-effect", "@types/react", - "react" + "@types/react-dom", + "react", + "react-dom" ], "optionalPeers": [ - "@types/react" + "@types/react", + "@types/react-dom" ] }, - "@radix-ui/react-switch@1.2.5_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==", + "@radix-ui/react-primitive@2.1.3_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-primitive", - "@radix-ui/react-use-controllable-state", - "@radix-ui/react-use-previous", - "@radix-ui/react-use-size", + "@radix-ui/react-slot", "@types/react", "@types/react-dom", "react", @@ -1594,16 +1085,17 @@ "@types/react-dom" ] }, - "@radix-ui/react-tabs@1.1.12_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", + "@radix-ui/react-roving-focus@1.1.10_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", "dependencies": [ "@radix-ui/primitive", + "@radix-ui/react-collection", + "@radix-ui/react-compose-refs", "@radix-ui/react-context", "@radix-ui/react-direction", "@radix-ui/react-id", - "@radix-ui/react-presence", "@radix-ui/react-primitive", - "@radix-ui/react-roving-focus", + "@radix-ui/react-use-callback-ref", "@radix-ui/react-use-controllable-state", "@types/react", "@types/react-dom", @@ -1615,21 +1107,18 @@ "@types/react-dom" ] }, - "@radix-ui/react-toast@1.2.14_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg==", + "@radix-ui/react-scroll-area@1.2.9_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==", "dependencies": [ + "@radix-ui/number", "@radix-ui/primitive", - "@radix-ui/react-collection", "@radix-ui/react-compose-refs", "@radix-ui/react-context", - "@radix-ui/react-dismissable-layer", - "@radix-ui/react-portal", + "@radix-ui/react-direction", "@radix-ui/react-presence", "@radix-ui/react-primitive", "@radix-ui/react-use-callback-ref", - "@radix-ui/react-use-controllable-state", "@radix-ui/react-use-layout-effect", - "@radix-ui/react-visually-hidden", "@types/react", "@types/react-dom", "react", @@ -1640,16 +1129,10 @@ "@types/react-dom" ] }, - "@radix-ui/react-toggle-group@1.1.10_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ==", + "@radix-ui/react-separator@1.1.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-context", - "@radix-ui/react-direction", "@radix-ui/react-primitive", - "@radix-ui/react-roving-focus", - "@radix-ui/react-toggle", - "@radix-ui/react-use-controllable-state", "@types/react", "@types/react-dom", "react", @@ -1660,12 +1143,20 @@ "@types/react-dom" ] }, - "@radix-ui/react-toggle@1.1.9_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA==", + "@radix-ui/react-slider@1.3.5_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==", "dependencies": [ + "@radix-ui/number", "@radix-ui/primitive", + "@radix-ui/react-collection", + "@radix-ui/react-compose-refs", + "@radix-ui/react-context", + "@radix-ui/react-direction", "@radix-ui/react-primitive", "@radix-ui/react-use-controllable-state", + "@radix-ui/react-use-layout-effect", + "@radix-ui/react-use-previous", + "@radix-ui/react-use-size", "@types/react", "@types/react-dom", "react", @@ -1676,24 +1167,15 @@ "@types/react-dom" ] }, - "@radix-ui/react-toolbar@1.1.10_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-jiwQsduEL++M4YBIurjSa+voD86OIytCod0/dbIxFZDLD8NfO1//keXYMfsW8BPcfqwoNjt+y06XcJqAb4KR7A==", + "@radix-ui/react-slot@1.2.3_@types+react@19.1.6_react@19.1.0": { + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-context", - "@radix-ui/react-direction", - "@radix-ui/react-primitive", - "@radix-ui/react-roving-focus", - "@radix-ui/react-separator", - "@radix-ui/react-toggle-group", + "@radix-ui/react-compose-refs", "@types/react", - "@types/react-dom", - "react", - "react-dom" + "react" ], "optionalPeers": [ - "@types/react", - "@types/react-dom" + "@types/react" ] }, "@radix-ui/react-tooltip@1.2.7_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { @@ -1765,17 +1247,6 @@ "@types/react" ] }, - "@radix-ui/react-use-is-hydrated@0.1.0_@types+react@19.1.6_react@19.1.0": { - "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", - "dependencies": [ - "@types/react", - "react", - "use-sync-external-store" - ], - "optionalPeers": [ - "@types/react" - ] - }, "@radix-ui/react-use-layout-effect@1.1.1_@types+react@19.1.6_react@19.1.0": { "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "dependencies": [ @@ -1835,23 +1306,6 @@ "@radix-ui/rect@1.1.1": { "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==" }, - "@radix-ui/themes@3.2.1_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-WJL2YKAGItkunwm3O4cLTFKCGJTfAfF6Hmq7f5bCo1ggqC9qJQ/wfg/25AAN72aoEM1yqXZQ+pslsw48AFR0Xg==", - "dependencies": [ - "@radix-ui/colors", - "@types/react", - "@types/react-dom", - "classnames", - "radix-ui", - "react", - "react-dom", - "react-remove-scroll-bar" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, "@rolldown/pluginutils@1.0.0-beta.9": { "integrity": "sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==" }, @@ -2243,9 +1697,6 @@ "clsx" ] }, - "classnames@2.5.1": { - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" - }, "cli-width@4.1.0": { "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==" }, @@ -2537,8 +1988,8 @@ "yallist@3.1.1" ] }, - "lucide-react@0.508.0_react@19.1.0": { - "integrity": "sha512-gcP16PnexqtOFrTtv98kVsGzTfnbPekzZiQfByi2S89xfk7E/4uKE1USZqccIp58v42LqkO7MuwpCqshwSrJCg==", + "lucide-react@0.511.0_react@19.1.0": { + "integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==", "dependencies": [ "react" ] @@ -2578,6 +2029,13 @@ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "bin": true }, + "next-themes@0.4.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "dependencies": [ + "react", + "react-dom" + ] + }, "node-addon-api@8.3.1": { "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==" }, @@ -2621,74 +2079,6 @@ "source-map-js" ] }, - "radix-ui@1.4.2_@types+react@19.1.6_@types+react-dom@19.1.5__@types+react@19.1.6_react@19.1.0_react-dom@19.1.0__react@19.1.0": { - "integrity": "sha512-fT/3YFPJzf2WUpqDoQi005GS8EpCi+53VhcLaHUj5fwkPYiZAjk1mSxFvbMA8Uq71L03n+WysuYC+mlKkXxt/Q==", - "dependencies": [ - "@radix-ui/primitive", - "@radix-ui/react-accessible-icon", - "@radix-ui/react-accordion", - "@radix-ui/react-alert-dialog", - "@radix-ui/react-arrow", - "@radix-ui/react-aspect-ratio", - "@radix-ui/react-avatar", - "@radix-ui/react-checkbox", - "@radix-ui/react-collapsible", - "@radix-ui/react-collection", - "@radix-ui/react-compose-refs", - "@radix-ui/react-context", - "@radix-ui/react-context-menu", - "@radix-ui/react-dialog", - "@radix-ui/react-direction", - "@radix-ui/react-dismissable-layer", - "@radix-ui/react-dropdown-menu", - "@radix-ui/react-focus-guards", - "@radix-ui/react-focus-scope", - "@radix-ui/react-form", - "@radix-ui/react-hover-card", - "@radix-ui/react-label", - "@radix-ui/react-menu", - "@radix-ui/react-menubar", - "@radix-ui/react-navigation-menu", - "@radix-ui/react-one-time-password-field", - "@radix-ui/react-password-toggle-field", - "@radix-ui/react-popover", - "@radix-ui/react-popper", - "@radix-ui/react-portal", - "@radix-ui/react-presence", - "@radix-ui/react-primitive", - "@radix-ui/react-progress", - "@radix-ui/react-radio-group", - "@radix-ui/react-roving-focus", - "@radix-ui/react-scroll-area", - "@radix-ui/react-select", - "@radix-ui/react-separator", - "@radix-ui/react-slider", - "@radix-ui/react-slot", - "@radix-ui/react-switch", - "@radix-ui/react-tabs", - "@radix-ui/react-toast", - "@radix-ui/react-toggle", - "@radix-ui/react-toggle-group", - "@radix-ui/react-toolbar", - "@radix-ui/react-tooltip", - "@radix-ui/react-use-callback-ref", - "@radix-ui/react-use-controllable-state", - "@radix-ui/react-use-effect-event", - "@radix-ui/react-use-escape-keydown", - "@radix-ui/react-use-is-hydrated", - "@radix-ui/react-use-layout-effect", - "@radix-ui/react-use-size", - "@radix-ui/react-visually-hidden", - "@types/react", - "@types/react-dom", - "react", - "react-dom" - ], - "optionalPeers": [ - "@types/react", - "@types/react-dom" - ] - }, "react-dom@19.1.0_react@19.1.0": { "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "dependencies": [ @@ -2818,6 +2208,13 @@ "signal-exit@4.1.0": { "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" }, + "sonner@2.0.4_react@19.1.0_react-dom@19.1.0__react@19.1.0": { + "integrity": "sha512-fUOGFwhM9/t05VqjKeDv0+t6QZPByMkbFFs6IFsgRQKCBh/1d3HUAC5sYy80Q05+vDKdwSOG/zUPBc8PPpbDjw==", + "dependencies": [ + "react", + "react-dom" + ] + }, "source-map-js@1.2.1": { "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" }, @@ -2970,12 +2367,6 @@ "@types/react" ] }, - "use-sync-external-store@1.5.0_react@19.1.0": { - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", - "dependencies": [ - "react" - ] - }, "vite@6.3.5_picomatch@4.0.2": { "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dependencies": [ @@ -3081,16 +2472,14 @@ "packages/app": { "dependencies": [ "npm:@deno/vite-plugin@^1.0.4", - "npm:@radix-ui/react-dialog@^1.1.13", - "npm:@radix-ui/react-dropdown-menu@^2.1.14", - "npm:@radix-ui/react-label@^2.1.6", - "npm:@radix-ui/react-scroll-area@^1.2.8", - "npm:@radix-ui/react-separator@^1.1.6", - "npm:@radix-ui/react-slider@^1.3.4", - "npm:@radix-ui/react-slot@^1.2.2", - "npm:@radix-ui/react-toast@^1.2.13", - "npm:@radix-ui/react-tooltip@^1.2.6", - "npm:@radix-ui/themes@^3.2.1", + "npm:@radix-ui/react-dialog@^1.1.14", + "npm:@radix-ui/react-dropdown-menu@^2.1.15", + "npm:@radix-ui/react-label@^2.1.7", + "npm:@radix-ui/react-scroll-area@^1.2.9", + "npm:@radix-ui/react-separator@^1.1.7", + "npm:@radix-ui/react-slider@^1.3.5", + "npm:@radix-ui/react-slot@^1.2.3", + "npm:@radix-ui/react-tooltip@^1.2.7", "npm:@tailwindcss/vite@^4.1.5", "npm:@types/cytoscape-fcose@^2.2.4", "npm:@types/react-dom@^19.1.3", @@ -3101,14 +2490,16 @@ "npm:clsx@^2.1.1", "npm:cytoscape-fcose@^2.2.0", "npm:cytoscape@3.31.1", - "npm:lucide-react@0.508", + "npm:lucide-react@0.511", + "npm:next-themes@~0.4.6", "npm:react-dom@^19.1.0", "npm:react-resizable-panels@^3.0.1", "npm:react-router@^7.5.3", "npm:react@^19.1.0", - "npm:tailwind-merge@^3.2.0", + "npm:sonner@^2.0.4", + "npm:tailwind-merge@^3.3.0", "npm:tailwindcss@^4.1.5", - "npm:tw-animate-css@^1.2.9", + "npm:tw-animate-css@^1.3.3", "npm:vite@^6.3.5" ] }, diff --git a/packages/app/deno.json b/packages/app/deno.json index 12845a22..d550dbf6 100644 --- a/packages/app/deno.json +++ b/packages/app/deno.json @@ -5,16 +5,14 @@ "imports": { "@napi/shared": "../shared/src/index.ts", "@deno/vite-plugin": "npm:@deno/vite-plugin@^1.0.4", - "@radix-ui/react-dialog": "npm:@radix-ui/react-dialog@^1.1.13", - "@radix-ui/react-dropdown-menu": "npm:@radix-ui/react-dropdown-menu@^2.1.14", - "@radix-ui/react-label": "npm:@radix-ui/react-label@^2.1.6", - "@radix-ui/react-scroll-area": "npm:@radix-ui/react-scroll-area@^1.2.8", - "@radix-ui/react-separator": "npm:@radix-ui/react-separator@^1.1.6", - "@radix-ui/react-slider": "npm:@radix-ui/react-slider@^1.3.4", - "@radix-ui/react-slot": "npm:@radix-ui/react-slot@^1.2.2", - "@radix-ui/react-toast": "npm:@radix-ui/react-toast@^1.2.13", - "@radix-ui/react-tooltip": "npm:@radix-ui/react-tooltip@^1.2.6", - "@radix-ui/themes": "npm:@radix-ui/themes@^3.2.1", + "@radix-ui/react-dialog": "npm:@radix-ui/react-dialog@^1.1.14", + "@radix-ui/react-dropdown-menu": "npm:@radix-ui/react-dropdown-menu@^2.1.15", + "@radix-ui/react-label": "npm:@radix-ui/react-label@^2.1.7", + "@radix-ui/react-scroll-area": "npm:@radix-ui/react-scroll-area@^1.2.9", + "@radix-ui/react-separator": "npm:@radix-ui/react-separator@^1.1.7", + "@radix-ui/react-slider": "npm:@radix-ui/react-slider@^1.3.5", + "@radix-ui/react-slot": "npm:@radix-ui/react-slot@^1.2.3", + "@radix-ui/react-tooltip": "npm:@radix-ui/react-tooltip@^1.2.7", "@tailwindcss/vite": "npm:@tailwindcss/vite@^4.1.5", "@types/cytoscape-fcose": "npm:@types/cytoscape-fcose@^2.2.4", "@types/react": "npm:@types/react@^19.1.3", @@ -24,15 +22,17 @@ "class-variance-authority": "npm:class-variance-authority@^0.7.1", "clsx": "npm:clsx@^2.1.1", "cytoscape-fcose": "npm:cytoscape-fcose@^2.2.0", - "lucide-react": "npm:lucide-react@^0.508.0", + "lucide-react": "npm:lucide-react@^0.511.0", + "next-themes": "npm:next-themes@^0.4.6", "react": "npm:react@^19.1.0", "react-dom": "npm:react-dom@^19.1.0", "react-router": "npm:react-router@^7.5.3", "react-resizable-panels": "npm:react-resizable-panels@^3.0.1", "cytoscape": "npm:cytoscape@3.31.1", - "tailwind-merge": "npm:tailwind-merge@^3.2.0", + "sonner": "npm:sonner@^2.0.4", + "tailwind-merge": "npm:tailwind-merge@^3.3.0", "tailwindcss": "npm:tailwindcss@^4.1.5", - "tw-animate-css": "npm:tw-animate-css@^1.2.9", + "tw-animate-css": "npm:tw-animate-css@^1.3.3", "vite": "npm:vite@^6.3.5" }, "tasks": { diff --git a/packages/app/src/components/DependencyVisualizer/DependencyVisualizer.tsx b/packages/app/src/components/DependencyVisualizer/DependencyVisualizer.tsx new file mode 100644 index 00000000..2ae59c0f --- /dev/null +++ b/packages/app/src/components/DependencyVisualizer/DependencyVisualizer.tsx @@ -0,0 +1,150 @@ +import { useState } from "react"; +import { useSearchParams } from "react-router"; +import type { AuditManifest, DependencyManifest } from "@napi/shared"; +import { SidebarProvider, SidebarTrigger } from "../shadcn/Sidebar.tsx"; +import { Button } from "../shadcn/Button.tsx"; +import { Moon, Sun } from "lucide-react"; +import { useTheme } from "../../contexts/ThemeProvider.tsx"; +import { FileExplorerSidebar } from "./components/FileExplorerSidebar.tsx"; +import BreadcrumbNav from "./components/BreadcrumNav.tsx"; +import ProjectVisualizer from "./visualizers/ProjectVisualizer.tsx"; +import FileVisualizer from "./visualizers/FileVisualizer.tsx"; +import SymbolVisualizer from "./visualizers/SymbolVisualizer.tsx"; + +export interface VisualizerContext { + dependencyManifest: DependencyManifest; + auditManifest: AuditManifest; + highlightedCytoscapeRef: { + filePath: string; + symbolId: string | undefined; + } | undefined; +} + +export default function DependencyVisualizer(props: { + theme: string; + dependencyManifest: DependencyManifest; + auditManifest: AuditManifest; +}) { + const [searchParams] = useSearchParams(); + + const { setTheme } = useTheme(); + + const [highlightedCytoscapeRef, setHighlightedCytoscapeRef] = useState< + { + filePath: string; + symbolId: string | undefined; + } | undefined + >(undefined); + + return ( + + { + if (!node.fileId) return; + const newRef = { + filePath: node.fileId, + symbolId: node.symbolId, + }; + // If the new ref is the same as the current ref, we un set it (unhighlight) + if ( + highlightedCytoscapeRef?.filePath === newRef.filePath && + highlightedCytoscapeRef?.symbolId === newRef.symbolId + ) { + setHighlightedCytoscapeRef(undefined); + } else { + setHighlightedCytoscapeRef(newRef); + } + }} + toDetails={(node) => { + if (node.symbolId && node.fileId) { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.set("fileId", node.fileId); + newSearchParams.set("instanceId", node.symbolId); + return `?${newSearchParams.toString()}`; + } else if (node.fileId) { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.set("fileId", node.fileId); + newSearchParams.delete("instanceId"); + return `?${newSearchParams.toString()}`; + } else { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.delete("fileId"); + newSearchParams.delete("instanceId"); + return `?${newSearchParams.toString()}`; + } + }} + /> +
+
+
+ + { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.delete("fileId"); + newSearchParams.delete("instanceId"); + return `?${newSearchParams.toString()}`; + }} + fileId={searchParams.get("fileId")} + toFileIdLink={(fileId) => { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.set("fileId", fileId); + newSearchParams.delete("instanceId"); + return `?${newSearchParams.toString()}`; + }} + instanceId={searchParams.get("instanceId")} + toInstanceIdLink={(fileId, instanceId) => { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.set("fileId", fileId); + newSearchParams.set("instanceId", instanceId); + return `?${newSearchParams.toString()}`; + }} + /> +
+ +
+
+ {searchParams.get("fileId") && searchParams.get("instanceId") + ? ( + + ) + : searchParams.get("fileId") + ? ( + + ) + : ( + + )} +
+
+
+ ); +} diff --git a/packages/app/src/components/DependencyVisualizer/components/BreadcrumNav.tsx b/packages/app/src/components/DependencyVisualizer/components/BreadcrumNav.tsx new file mode 100644 index 00000000..15ca9875 --- /dev/null +++ b/packages/app/src/components/DependencyVisualizer/components/BreadcrumNav.tsx @@ -0,0 +1,57 @@ +import { Link } from "react-router"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbSeparator, +} from "../../shadcn/Breadcrumb.tsx"; + +export default function BreadcrumbNav(props: { + toProjectLink: () => string; + fileId: string | null; + toFileIdLink: (fileId: string) => string; + instanceId: string | null; + toInstanceIdLink: (fileId: string, instanceId: string) => string; +}) { + return ( + + + + + Project + + + {props.fileId && ( + <> + + + + + {props.fileId} + + + + {props.instanceId && ( + <> + + + + + {props.instanceId} + + + + + )} + + )} + + + ); +} diff --git a/packages/app/src/components/DisplayNameWithTootip.tsx b/packages/app/src/components/DependencyVisualizer/components/DisplayNameWithTootip.tsx similarity index 89% rename from packages/app/src/components/DisplayNameWithTootip.tsx rename to packages/app/src/components/DependencyVisualizer/components/DisplayNameWithTootip.tsx index d6e974e8..fa8c36d0 100644 --- a/packages/app/src/components/DisplayNameWithTootip.tsx +++ b/packages/app/src/components/DependencyVisualizer/components/DisplayNameWithTootip.tsx @@ -1,4 +1,8 @@ -import { Tooltip, TooltipContent, TooltipTrigger } from "./shadcn/Tooltip.tsx"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "../../shadcn/Tooltip.tsx"; export default function DisplayNameWithTooltip(props: { name: string; diff --git a/packages/app/src/components/DependencyVisualizer/components/FileExplorerSidebar.tsx b/packages/app/src/components/DependencyVisualizer/components/FileExplorerSidebar.tsx new file mode 100644 index 00000000..f432a8e0 --- /dev/null +++ b/packages/app/src/components/DependencyVisualizer/components/FileExplorerSidebar.tsx @@ -0,0 +1,370 @@ +import { useEffect, useState } from "react"; +import { Link } from "react-router"; +import type { AuditManifest, DependencyManifest } from "@napi/shared"; +import { + Sidebar, + SidebarContent, + SidebarGroup, + SidebarHeader, + SidebarRail, +} from "../../shadcn/Sidebar.tsx"; +import { Button } from "../../shadcn/Button.tsx"; +import { Input } from "../../shadcn/Input.tsx"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "../../shadcn/Tooltip.tsx"; +import { + ChevronDown, + ChevronRight, + Code, + File, + FolderClosed, + FolderOpen, + ScanEye, + SearchCode, +} from "lucide-react"; +import { ScrollArea, ScrollBar } from "../../shadcn/Scrollarea.tsx"; +import DisplayNameWithTooltip from "./DisplayNameWithTootip.tsx"; + +export interface ExplorerNodeData { + id: string; + displayName: string; + fileId?: string; + symbolId?: string; + children: Map; +} + +export function FileExplorerSidebar(props: { + dependencyManifest: DependencyManifest; + auditManifest: AuditManifest; + onHighlightInCytoscape: (node: ExplorerNodeData) => void; + toDetails: (node: ExplorerNodeData) => string; +}) { + const [search, setSearch] = useState(""); + + const [explorerTree, setExplorerTree] = useState(); + + // Build the explorer tree when the dependency manifest changes + useEffect(() => { + const tree = buildExplorerTree(props.dependencyManifest, search); + setExplorerTree(tree); + }, [props.dependencyManifest, search]); + + function buildExplorerTree( + dependencyManifest: DependencyManifest, + search: string, + ): ExplorerNodeData | undefined { + const getExplorerNodeId = (filePath: string, instanceId?: string) => { + if (instanceId) { + return `${filePath}#${instanceId}`; + } + return filePath; + }; + + const root: ExplorerNodeData = { + id: "root", + displayName: "Project", + children: new Map(), + }; + + // Filter function to check if a string matches the search term + const matchesSearch = (text: string): boolean => { + if (!search) return true; + return text.toLowerCase().includes(search.toLowerCase()); + }; + + // Track if any nodes match the search to avoid empty results + let hasMatchingNodes = false; + + for (const fileDependencyManifest of Object.values(dependencyManifest)) { + const filePath = fileDependencyManifest.filePath; + const fileName = filePath.split("/").pop() || ""; + const fileMatchesSearch = matchesSearch(fileName); + + // Check if any symbols match the search + const matchingSymbols = Object.keys(fileDependencyManifest.symbols) + .filter( + (symbolId) => matchesSearch(symbolId), + ); + + // Skip this file if neither the file nor any symbols match the search + if (search && !fileMatchesSearch && matchingSymbols.length === 0) { + continue; + } + + hasMatchingNodes = true; + + const parts = filePath.split("/"); + let currentNode: ExplorerNodeData = root; + for (const part of parts) { + const id = getExplorerNodeId(part); + if (!currentNode.children.has(id)) { + currentNode.children.set(id, { + id: id, + displayName: part, + children: new Map(), + }); + } + currentNode = currentNode.children.get(id)!; + } + currentNode.fileId = getExplorerNodeId(filePath); + + // Only add symbols that match the search or if no search is provided + for (const instanceId of Object.keys(fileDependencyManifest.symbols)) { + if (!search || matchesSearch(instanceId)) { + const id = getExplorerNodeId(filePath, instanceId); + currentNode.children.set(id, { + id: id, + displayName: instanceId, + fileId: filePath, + symbolId: instanceId, + children: new Map(), + }); + } + } + } + + // If no nodes match the search, return an empty tree + if (search && !hasMatchingNodes) { + return undefined; + } + + const flattenTree = (node: ExplorerNodeData): ExplorerNodeData => { + // First recursively flatten all children + if (node.children.size > 0) { + const flattenedChildren = new Map(); + for (const [id, child] of node.children) { + const flattenedChild = flattenTree(child); + flattenedChildren.set(id, flattenedChild); + } + node.children = flattenedChildren; + } + + // Then check if this node has exactly one child that can be merged + while (node.children.size === 1) { + const childEntry = Array.from(node.children.entries())[0]; + const child = childEntry[1]; + + // Skip if the child has symbols (is a leaf node) + if (child.fileId) { + break; + } + + // Merge child's name into parent + node.displayName = `${node.displayName}/${child.displayName}`; + // Update the parent's id to child's id + node.id = child.id; + // Replace parent's children with child's children + node.children = child.children; + } + + return node; + }; + + // Flatten nodes that have only one child + return flattenTree(root); + } + + return ( + + + + logo +
NanoAPI
+ +
+ + + + + + + setSearch(e.target.value)} + placeholder="Search" + /> + + +
+ Search for a file or symbol. +
+ The search will find partial matches in both symbol names and + file paths. +
+
+
+ +
+ {!explorerTree + ? ( +
+ No Matching files found +
+ ) + : ( + + )} +
+ +
+
+
+ +
+ ); +} + +function ExplorerNode(props: { + node: ExplorerNodeData; + level: number; + onHighlightInCytoscape: (node: ExplorerNodeData) => void; + toDetails: (node: ExplorerNodeData) => string; +}) { + const [showChildren, setShowChildren] = useState(false); + + const type: "folder" | "file" | "symbol" = props.node.symbolId + ? "symbol" + : props.node.fileId + ? "file" + : "folder"; + + return ( +
+ {(() => { + switch (type) { + case "folder": + return ( + + ); + case "file": + return ( +
+
+ +
+
+ + + + + + Highlight in graph + + + + + + + + View graph for this file + + +
+
+ ); + case "symbol": + return ( +
+
+ + +
+
+ + + + + + Highlight in graph + + + + + + + + View graph for this symbol + + +
+
+ ); + } + })()} + {showChildren && + Array.from(props.node.children.values()).map((child) => ( + + ))} +
+ ); +} diff --git a/packages/app/src/components/DependencyVisualizer/components/SymbolExtractionDialog.tsx b/packages/app/src/components/DependencyVisualizer/components/SymbolExtractionDialog.tsx new file mode 100644 index 00000000..fbe5bc57 --- /dev/null +++ b/packages/app/src/components/DependencyVisualizer/components/SymbolExtractionDialog.tsx @@ -0,0 +1,107 @@ +import { useState } from "react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "../../shadcn/Dialog.tsx"; +import { Button } from "../../shadcn/Button.tsx"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "../../shadcn/Card.tsx"; +import { Alert, AlertDescription, AlertTitle } from "../../shadcn/Alert.tsx"; +import { Separator } from "../../shadcn/Separator.tsx"; +import { Code, Copy, Info, Terminal } from "lucide-react"; +import { ScrollArea } from "../../shadcn/Scrollarea.tsx"; +import { toast } from "sonner"; + +export default function SymbolExtractionDialog(props: { + children: React.ReactNode; + filePath: string; + symbolIds: string[]; +}) { + const [open, setOpen] = useState(false); + const [copied, setCopied] = useState(false); + + const generateCommand = () => { + const symbolOptions = props.symbolIds.map((symbolId) => { + return `--symbol="${props.filePath}|${symbolId}"`; + }); + return `napi extract ${symbolOptions.join(" ")}`; + }; + + const copyToClipboard = async () => { + try { + await navigator.clipboard.writeText(generateCommand()); + setCopied(true); + toast.info("Copied to clipboard"); + } catch (err) { + console.error("Failed to copy to clipboard:", err); + } + }; + + return ( + + + {props.children} + + + + + + + Extract Symbol(s) via CLI + + + Use the NAPI CLI to extract symbols from your program. + + + +
+ + + + Prerequisites + + +
+ Ensure you have a NAPI manifest generated locally: +
+ + napi manifest generate + +
+
+ + + + + + +
+ + Command +
+ +
+
+ + + {generateCommand()} + + +
+
+
+
+
+ ); +} diff --git a/packages/app/src/components/contextMenu/FileContextMenu.tsx b/packages/app/src/components/DependencyVisualizer/components/contextMenu/FileContextMenu.tsx similarity index 74% rename from packages/app/src/components/contextMenu/FileContextMenu.tsx rename to packages/app/src/components/DependencyVisualizer/components/contextMenu/FileContextMenu.tsx index a9c013fa..05a5910e 100644 --- a/packages/app/src/components/contextMenu/FileContextMenu.tsx +++ b/packages/app/src/components/DependencyVisualizer/components/contextMenu/FileContextMenu.tsx @@ -1,4 +1,4 @@ -import { Link } from "react-router"; +import { Link, useSearchParams } from "react-router"; import type { FileDependencyManifest } from "@napi/shared"; import { DropdownMenu, @@ -7,7 +7,7 @@ import { DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, -} from "../shadcn/Dropdownmenu.tsx"; +} from "../../../shadcn/Dropdownmenu.tsx"; import { PanelRight, SearchCode } from "lucide-react"; import DisplayNameWithTooltip from "../DisplayNameWithTootip.tsx"; @@ -19,6 +19,15 @@ export default function FileContextMenu(props: { onClose: () => void; onOpenDetails: (filePath: string) => void; }) { + const [searchParams] = useSearchParams(); + + function getToFileLink(filePath: string) { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.set("fileId", filePath); + newSearchParams.delete("instanceId"); + return `?${newSearchParams.toString()}`; + } + return (
+ onClick={() => { props.context?.fileDependencyManifest && - props.onOpenDetails( - props.context?.fileDependencyManifest.filePath, - )} + props.onOpenDetails( + props.context?.fileDependencyManifest.filePath, + ); + props.onClose(); + }} >
@@ -55,11 +66,9 @@ export default function FileContextMenu(props: {
diff --git a/packages/app/src/components/contextMenu/SymbolContextMenu.tsx b/packages/app/src/components/DependencyVisualizer/components/contextMenu/SymbolContextMenu.tsx similarity index 72% rename from packages/app/src/components/contextMenu/SymbolContextMenu.tsx rename to packages/app/src/components/DependencyVisualizer/components/contextMenu/SymbolContextMenu.tsx index e3277ddb..9d3f934e 100644 --- a/packages/app/src/components/contextMenu/SymbolContextMenu.tsx +++ b/packages/app/src/components/DependencyVisualizer/components/contextMenu/SymbolContextMenu.tsx @@ -1,4 +1,4 @@ -import { Link } from "react-router"; +import { Link, useSearchParams } from "react-router"; import type { FileDependencyManifest, SymbolDependencyManifest, @@ -10,7 +10,7 @@ import { DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, -} from "../shadcn/Dropdownmenu.tsx"; +} from "../../../shadcn/Dropdownmenu.tsx"; import { PanelRight, SearchCode } from "lucide-react"; import DisplayNameWithTooltip from "../DisplayNameWithTootip.tsx"; @@ -23,6 +23,15 @@ export default function SymbolContextMenu(props: { onClose: () => void; onOpenDetails: (filePath: string, symbolId: string) => void; }) { + const [searchParams] = useSearchParams(); + + function getToSymbolLink(filePath: string, symbolId: string) { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.set("fileId", filePath); + newSearchParams.set("instanceId", symbolId); + return `?${newSearchParams.toString()}`; + } + return (
+ onClick={() => { props.context?.fileDependencyManifest && - props.onOpenDetails( - props.context?.fileDependencyManifest.filePath, - props.context?.symbolDependencyManifest.id, - )} + props.onOpenDetails( + props.context?.fileDependencyManifest.filePath, + props.context?.symbolDependencyManifest.id, + ); + props.onClose(); + }} >
@@ -61,15 +72,10 @@ export default function SymbolContextMenu(props: {
diff --git a/packages/app/src/components/controls/ControlExtensions/FiltersExtension.tsx b/packages/app/src/components/DependencyVisualizer/components/controls/ControlExtensions/FiltersExtension.tsx similarity index 96% rename from packages/app/src/components/controls/ControlExtensions/FiltersExtension.tsx rename to packages/app/src/components/DependencyVisualizer/components/controls/ControlExtensions/FiltersExtension.tsx index f68f9cce..a2986275 100644 --- a/packages/app/src/components/controls/ControlExtensions/FiltersExtension.tsx +++ b/packages/app/src/components/DependencyVisualizer/components/controls/ControlExtensions/FiltersExtension.tsx @@ -3,7 +3,7 @@ import { Tooltip, TooltipContent, TooltipTrigger, -} from "../../shadcn/Tooltip.tsx"; +} from "../../../../shadcn/Tooltip.tsx"; import { DropdownMenu, DropdownMenuCheckboxItem, @@ -11,8 +11,8 @@ import { DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, -} from "../../shadcn/Dropdownmenu.tsx"; -import { Button } from "../../shadcn/Button.tsx"; +} from "../../../../shadcn/Dropdownmenu.tsx"; +import { Button } from "../../../../shadcn/Button.tsx"; import { Funnel } from "lucide-react"; export default function FiltersExtension(props: { diff --git a/packages/app/src/components/controls/ControlExtensions/GraphDepthExtension.tsx b/packages/app/src/components/DependencyVisualizer/components/controls/ControlExtensions/GraphDepthExtension.tsx similarity index 91% rename from packages/app/src/components/controls/ControlExtensions/GraphDepthExtension.tsx rename to packages/app/src/components/DependencyVisualizer/components/controls/ControlExtensions/GraphDepthExtension.tsx index 19a71bd5..4d9c124c 100644 --- a/packages/app/src/components/controls/ControlExtensions/GraphDepthExtension.tsx +++ b/packages/app/src/components/DependencyVisualizer/components/controls/ControlExtensions/GraphDepthExtension.tsx @@ -3,17 +3,17 @@ import { Tooltip, TooltipContent, TooltipTrigger, -} from "../../shadcn/Tooltip.tsx"; +} from "../../../../shadcn/Tooltip.tsx"; import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, -} from "../../shadcn/Dropdownmenu.tsx"; -import { Button } from "../../shadcn/Button.tsx"; +} from "../../../../shadcn/Dropdownmenu.tsx"; +import { Button } from "../../../../shadcn/Button.tsx"; import { Settings2 } from "lucide-react"; -import { Slider } from "../../shadcn/Slider.tsx"; -import { Input } from "../../shadcn/Input.tsx"; -import { Label } from "../../shadcn/Label.tsx"; +import { Slider } from "../../../../shadcn/Slider.tsx"; +import { Input } from "../../../../shadcn/Input.tsx"; +import { Label } from "../../../../shadcn/Label.tsx"; export default function GraphDepthExtension(props: { busy: boolean; diff --git a/packages/app/src/components/controls/ControlExtensions/MetricsExtension.tsx b/packages/app/src/components/DependencyVisualizer/components/controls/ControlExtensions/MetricsExtension.tsx similarity index 94% rename from packages/app/src/components/controls/ControlExtensions/MetricsExtension.tsx rename to packages/app/src/components/DependencyVisualizer/components/controls/ControlExtensions/MetricsExtension.tsx index 5fa2d339..677df98e 100644 --- a/packages/app/src/components/controls/ControlExtensions/MetricsExtension.tsx +++ b/packages/app/src/components/DependencyVisualizer/components/controls/ControlExtensions/MetricsExtension.tsx @@ -12,14 +12,14 @@ import { Tooltip, TooltipContent, TooltipTrigger, -} from "../../shadcn/Tooltip.tsx"; -import { Button } from "../../shadcn/Button.tsx"; +} from "../../../../shadcn/Tooltip.tsx"; +import { Button } from "../../../../shadcn/Button.tsx"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from "../../shadcn/Dropdownmenu.tsx"; +} from "../../../../shadcn/Dropdownmenu.tsx"; // Extension for the controls in the project view export default function MetricsExtension(props: { diff --git a/packages/app/src/components/controls/Controls.tsx b/packages/app/src/components/DependencyVisualizer/components/controls/Controls.tsx similarity index 94% rename from packages/app/src/components/controls/Controls.tsx rename to packages/app/src/components/DependencyVisualizer/components/controls/Controls.tsx index ffdbbea0..b52fcf0c 100644 --- a/packages/app/src/components/controls/Controls.tsx +++ b/packages/app/src/components/DependencyVisualizer/components/controls/Controls.tsx @@ -1,6 +1,10 @@ import type { ReactNode } from "react"; -import { Button } from "../shadcn/Button.tsx"; -import { Tooltip, TooltipContent, TooltipTrigger } from "../shadcn/Tooltip.tsx"; +import { Button } from "../../../shadcn/Button.tsx"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "../../../shadcn/Tooltip.tsx"; import type { Core } from "cytoscape"; import { Focus, Network, ZoomIn, ZoomOut } from "lucide-react"; diff --git a/packages/app/src/components/detailsPanes/alertBadge.tsx b/packages/app/src/components/DependencyVisualizer/components/detailsPanes/AlertBadge.tsx similarity index 100% rename from packages/app/src/components/detailsPanes/alertBadge.tsx rename to packages/app/src/components/DependencyVisualizer/components/detailsPanes/AlertBadge.tsx diff --git a/packages/app/src/components/detailsPanes/FileDetailsPane.tsx b/packages/app/src/components/DependencyVisualizer/components/detailsPanes/FileDetailsPane.tsx similarity index 74% rename from packages/app/src/components/detailsPanes/FileDetailsPane.tsx rename to packages/app/src/components/DependencyVisualizer/components/detailsPanes/FileDetailsPane.tsx index 9fa24ec2..bbe735bd 100644 --- a/packages/app/src/components/detailsPanes/FileDetailsPane.tsx +++ b/packages/app/src/components/DependencyVisualizer/components/detailsPanes/FileDetailsPane.tsx @@ -4,16 +4,21 @@ import { SheetContent, SheetHeader, SheetTitle, - SheetTrigger, -} from "../shadcn/Sheet.tsx"; -import { Card, CardContent, CardHeader, CardTitle } from "../shadcn/Card.tsx"; +} from "../../../shadcn/Sheet.tsx"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "../../../shadcn/Card.tsx"; import { Code, File, Pickaxe, SearchCode } from "lucide-react"; -import { ScrollArea } from "../shadcn/Scrollarea.tsx"; -import { Button } from "../shadcn/Button.tsx"; -import { Link } from "react-router"; +import { ScrollArea } from "../../../shadcn/Scrollarea.tsx"; +import { Button } from "../../../shadcn/Button.tsx"; +import { Link, useSearchParams } from "react-router"; import DisplayNameWithTooltip from "../DisplayNameWithTootip.tsx"; -import Metrics from "./metrics.tsx"; -import AlertBadge from "./alertBadge.tsx"; +import Metrics from "./Metrics.tsx"; +import AlertBadge from "./AlertBadge.tsx"; +import SymbolExtractionDialog from "../SymbolExtractionDialog.tsx"; export default function FileDetailsPane(props: { context: { @@ -21,28 +26,14 @@ export default function FileDetailsPane(props: { fileAuditManifest: FileAuditManifest; } | undefined; onClose: () => void; - onAddSymbolsForExtraction: (filePath: string, symbolIds: string[]) => void; }) { - function markAllSymbolsForExtraction() { - if (!props.context?.fileDependencyManifest) { - return; - } + const [searchParams] = useSearchParams(); - props.onAddSymbolsForExtraction( - props.context?.fileDependencyManifest.filePath, - Object.keys(props.context.fileDependencyManifest.symbols), - ); - } - - function markSymbolForExtraction(symbolId: string) { - if (!props.context?.fileDependencyManifest) { - return; - } - - props.onAddSymbolsForExtraction( - props.context.fileDependencyManifest.filePath, - [symbolId], - ); + function getToFileLink(filePath: string) { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.set("fileId", filePath); + newSearchParams.delete("instanceId"); + return `?${newSearchParams.toString()}`; } return ( @@ -51,8 +42,7 @@ export default function FileDetailsPane(props: { open={props.context !== undefined} onOpenChange={() => props.onClose()} > - - +
@@ -70,24 +60,25 @@ export default function FileDetailsPane(props: { onClick={props.onClose} > View graph for this file - + +
@@ -161,7 +152,7 @@ export default function FileDetailsPane(props: { - + + void; - onAddSymbolsForExtraction: (filePath: string, symbolIds: string[]) => void; }) { - function markSymbolForExtraction(symbolId: string) { - if (!props.context?.fileDependencyManifest) { - return; - } + const [searchParams] = useSearchParams(); - props.onAddSymbolsForExtraction( - props.context.fileDependencyManifest.filePath, - [symbolId], - ); + function getToSymbolLink(filePath: string, symbolId: string) { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.set("fileId", filePath); + newSearchParams.set("instanceId", symbolId); + return `?${newSearchParams.toString()}`; } return ( @@ -48,7 +51,10 @@ export default function SymbolDetailsPane(props: { onOpenChange={() => props.onClose()} > - +
@@ -74,32 +80,24 @@ export default function SymbolDetailsPane(props: { onClick={props.onClose} > View graph for this symbol - + +
); } diff --git a/packages/app/src/pages/audit/file/instance/index.tsx b/packages/app/src/components/DependencyVisualizer/visualizers/SymbolVisualizer.tsx similarity index 74% rename from packages/app/src/pages/audit/file/instance/index.tsx rename to packages/app/src/components/DependencyVisualizer/visualizers/SymbolVisualizer.tsx index 6526ea95..8a5099c1 100644 --- a/packages/app/src/pages/audit/file/instance/index.tsx +++ b/packages/app/src/components/DependencyVisualizer/visualizers/SymbolVisualizer.tsx @@ -1,35 +1,31 @@ import { useEffect, useRef, useState } from "react"; -import Controls from "../../../../components/controls/Controls.tsx"; -import GraphDepthExtension from "../../../../components/controls/ControlExtensions/GraphDepthExtension.tsx"; -import SymbolContextMenu from "../../../../components/contextMenu/SymbolContextMenu.tsx"; -import { - useNavigate, - useOutletContext, - useParams, - useSearchParams, -} from "react-router"; -import type { AuditContext } from "../../base.tsx"; -import SymbolDetailsPane from "../../../../components/detailsPanes/SymbolDetailsPane.tsx"; -import { useTheme } from "../../../../contexts/ThemeProvider.tsx"; +import Controls from "../components/controls/Controls.tsx"; +import GraphDepthExtension from "../components/controls/ControlExtensions/GraphDepthExtension.tsx"; +import SymbolContextMenu from "../components/contextMenu/SymbolContextMenu.tsx"; +import { useNavigate, useSearchParams } from "react-router"; +import type { VisualizerContext } from "../DependencyVisualizer.tsx"; +import SymbolDetailsPane from "../components/detailsPanes/SymbolDetailsPane.tsx"; +import { useTheme } from "../../../contexts/ThemeProvider.tsx"; import type { FileAuditManifest, FileDependencyManifest, SymbolAuditManifest, SymbolDependencyManifest, } from "@napi/shared"; -import { SymbolDependencyVisualizer } from "../../../../helpers/cytoscape/symbolDependencyVisualizer/index.ts"; - -export default function AuditInstancePage() { +import { SymbolDependencyVisualizer } from "../cytoscape/symbolDependencyVisualizer/index.ts"; + +export default function SymbolVisualizer( + props: VisualizerContext & { + fileId: string; + instanceId: string; + }, +) { const navigate = useNavigate(); const { theme } = useTheme(); - const params = useParams<{ file: string; instance: string }>(); - const [searchParams, setSearchParams] = useSearchParams(); - const context = useOutletContext(); - const containerRef = useRef(null); const [busy, setBusy] = useState(true); const [symbolVisualizer, setSymbolVisualizer] = useState< @@ -81,18 +77,18 @@ export default function AuditInstancePage() { useEffect(() => { setBusy(true); - if (!params.file || !params.instance) { + if (!props.fileId || !props.instanceId) { return; } const symbolVisualizer = new SymbolDependencyVisualizer( containerRef.current as HTMLElement, - params.file, - params.instance, + props.fileId, + props.instanceId, dependencyDepth, dependentDepth, - context.dependencyManifest, - context.auditManifest, + props.dependencyManifest, + props.auditManifest, { theme: theme, onAfterNodeRightClick: (value: { @@ -101,7 +97,7 @@ export default function AuditInstancePage() { symbolId: string; }) => { const fileDependencyManifest = - context.dependencyManifest[value.filePath]; + props.dependencyManifest[value.filePath]; const symbolDependencyManifest = fileDependencyManifest.symbols[value.symbolId]; setContextMenu({ @@ -118,7 +114,7 @@ export default function AuditInstancePage() { symbolId, ); const urlEncodedSymbolName = - `/audit/${urlEncodedFileName}/${urlEncodedSymbolId}`; + `/${urlEncodedFileName}/${urlEncodedSymbolId}`; navigate(urlEncodedSymbolName); }, @@ -135,23 +131,23 @@ export default function AuditInstancePage() { setSymbolVisualizer(undefined); }; }, [ - context.dependencyManifest, - context.auditManifest, - params.file, - params.instance, + props.dependencyManifest, + props.auditManifest, + props.fileId, + props.instanceId, dependencyDepth, dependentDepth, ]); useEffect(() => { if (symbolVisualizer) { - if (context.highlightedCytoscapeRef) { - symbolVisualizer.highlightNode(context.highlightedCytoscapeRef); + if (props.highlightedCytoscapeRef) { + symbolVisualizer.highlightNode(props.highlightedCytoscapeRef); } else { symbolVisualizer.unhighlightNodes(); } } - }, [context.highlightedCytoscapeRef]); + }, [props.highlightedCytoscapeRef]); // Hook to update the theme in the graph useEffect(() => { @@ -169,12 +165,12 @@ export default function AuditInstancePage() {
symbolVisualizer?.layoutGraph(symbolVisualizer.cy)} > setContextMenu(undefined)} onOpenDetails={(filePath, symbolId) => { - const fileDependencyManifest = context.dependencyManifest[filePath]; + const fileDependencyManifest = props.dependencyManifest[filePath]; const symbolDependencyManifest = fileDependencyManifest.symbols[symbolId]; - const fileAuditManifest = context.auditManifest[filePath]; + const fileAuditManifest = props.auditManifest[filePath]; const symbolAuditManifest = fileAuditManifest.symbols[symbolId]; setDetailsPane({ fileDependencyManifest, @@ -208,9 +204,6 @@ export default function AuditInstancePage() { setDetailsPane(undefined)} - onAddSymbolsForExtraction={(filePath, symbolIds) => { - context.onAddSymbolsForExtraction(filePath, symbolIds); - }} />
); diff --git a/packages/app/src/components/shadcn/Alert.tsx b/packages/app/src/components/shadcn/Alert.tsx index f80fec6d..9ef9269b 100644 --- a/packages/app/src/components/shadcn/Alert.tsx +++ b/packages/app/src/components/shadcn/Alert.tsx @@ -1,16 +1,16 @@ -import * as React from "react"; +import type * as React from "react"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "../../lib/utils.ts"; const alertVariants = cva( - "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", { variants: { variant: { - default: "bg-background text-foreground", + default: "bg-card text-card-foreground", destructive: - "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", }, }, defaultVariants: { @@ -19,41 +19,48 @@ const alertVariants = cva( }, ); -const Alert = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes & VariantProps ->(({ className, variant, ...props }, ref) => ( -
-)); -Alert.displayName = "Alert"; +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ); +} -const AlertTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -AlertTitle.displayName = "AlertTitle"; +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} -const AlertDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -AlertDescription.displayName = "AlertDescription"; +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} export { Alert, AlertDescription, AlertTitle }; diff --git a/packages/app/src/components/shadcn/Breadcrumb.tsx b/packages/app/src/components/shadcn/Breadcrumb.tsx index 05f4a7f6..3de86be9 100644 --- a/packages/app/src/components/shadcn/Breadcrumb.tsx +++ b/packages/app/src/components/shadcn/Breadcrumb.tsx @@ -1,108 +1,102 @@ -import * as React from "react"; +import type * as React from "react"; import { Slot } from "@radix-ui/react-slot"; import { ChevronRight, MoreHorizontal } from "lucide-react"; import { cn } from "../../lib/utils.ts"; -const Breadcrumb = React.forwardRef< - HTMLElement, - React.ComponentPropsWithoutRef<"nav"> & { - separator?: React.ReactNode; - } ->(({ ...props }, ref) =>