diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..03c5b27 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,68 @@ +# CodeIntel Development Rules + +## Code Style + +### General +- NO emojis anywhere - not in code, comments, docs, or commit messages +- Prefer files under 200 lines. Larger files allowed when logically cohesive. +- Comments explain WHY, not WHAT. Keep them brief. +- No decorative headers or ASCII art +- Casual tone in comments - write like a human, not a robot + +### JSDoc Policy +- JSDoc allowed for public API functions and exported hooks +- Keep JSDoc minimal - document params and return types, not obvious behavior +- Approved files using JSDoc: `frontend/src/services/playground-api.ts`, `frontend/src/config/api.ts`, `frontend/src/hooks/useViewTransition.ts` + +### Large File Exceptions +These files exceed 200 lines but are approved due to logical cohesion: +- `backend/routes/playground.py` +- `backend/services/dna_extractor.py` +- `frontend/src/components/DependencyGraph/index.tsx` + +### Frontend (TypeScript/React) +- Package manager: **Bun only**. Never npm, never yarn. +- Always `bun install`, `bun run dev`, `bun run build` +- Never delete `bun.lock` or create `package-lock.json` +- Use shadcn/ui components over custom UI +- Tailwind for styling, no CSS files +- Functional components with hooks, no class components + +### Backend (Python) +- Python 3.11+ required +- Type hints on all function signatures +- Async/await for I/O operations +- PEP 8 style, max 120 char lines +- Use existing patterns from `services/` directory + +### Commits +- Format: `type: description` (e.g., `fix: remove broken link`) +- Types: feat, fix, docs, refactor, test, chore +- No emojis in commit messages +- Keep commits focused - one change per commit + +## Architecture + +### Project Structure +```plaintext +backend/ # FastAPI, Python 3.11+ +frontend/ # React 18, TypeScript, Vite, Bun +mcp-server/ # MCP protocol server +``` + +### API Versioning +- All endpoints use `/api/v1/` prefix +- Version config in `backend/config/api.py` + +### Key Services +- `indexer_optimized.py` - Code parsing and embedding +- `dependency_analyzer.py` - Import graph extraction +- `style_analyzer.py` - Convention detection +- `dna_extractor.py` - Architectural pattern extraction + +## What NOT to Do + +- Don't use npm (use Bun) +- Don't add emojis +- Don't write verbose comments +- Don't add "AI-looking" badges or decorations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 11f7191..6d16b96 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,15 +17,18 @@ pip install -r requirements.txt cp .env.example .env # Add your API keys to .env -# Set up frontend +# Set up frontend (MUST use Bun, not npm!) cd ../frontend -npm install +bun install # Run tests cd ../backend pytest tests/ -v ``` +> **Important:** The frontend uses Bun exclusively. Do NOT use npm or yarn. +> Install Bun: `curl -fsSL https://bun.sh/install | bash` + ## How to Contribute ### Reporting Bugs @@ -65,7 +68,7 @@ pytest tests/ -v - Use TypeScript strict mode - Prefer functional components - Use Tailwind for styling -- Run: `npm run build` to check for errors +- Run: `bun run build` to check for errors ## Testing diff --git a/DEFERRED.md b/DEFERRED.md new file mode 100644 index 0000000..273f724 --- /dev/null +++ b/DEFERRED.md @@ -0,0 +1,36 @@ +# Deferred Work + +Items intentionally skipped for future PRs. + +--- + +## PR: feature/docs-overhaul + +### 1. Extract shared EndpointHeader component +**Priority:** Low +**Effort:** 30 min +**Files affected:** +- `frontend/src/pages/api/APIRepositoriesPage.tsx` +- `frontend/src/pages/api/APIAnalysisPage.tsx` +- `frontend/src/pages/api/APISearchPage.tsx` +- `frontend/src/pages/api/APIOverviewPage.tsx` + +**What:** All 4 API doc pages duplicate the endpoint header markup (colored method badge + path in bordered container). APIRepositoriesPage already has a local `EndpointHeader` component - extract it to `@/components/docs` and reuse. + +**Saves:** ~40 lines of duplication + +**When to do:** Next time any of these pages need changes, or during a cleanup sprint. + +--- + +### 2. Consolidate navigation data source +**Priority:** Medium +**Effort:** 1-2 hours +**Files affected:** +- `frontend/src/components/docs/DocsSidebar.tsx` (navigation) +- `frontend/src/components/docs/DocsSearch.tsx` (docsPages) +- `frontend/src/components/docs/DocsLayout.tsx` (mobileNavigation) + +**What:** Same page list maintained in 3 places. Adding a page requires updating all 3. Extract single `docsNavigation` data source and derive variants from it. + +**When to do:** Before adding more doc pages, or if someone forgets to update all 3. diff --git a/Makefile b/Makefile index 3b0d88d..bf57580 100644 --- a/Makefile +++ b/Makefile @@ -5,16 +5,16 @@ # Default target - rebuild frontend f frontend: - @echo "🔄 Rebuilding frontend..." + @echo "Rebuilding frontend..." @docker compose build frontend @docker compose up -d frontend - @echo "✅ Done" + @echo "Done" b backend: - @echo "🔄 Rebuilding backend..." + @echo "Rebuilding backend..." @docker compose build backend @docker compose up -d backend - @echo "✅ Done" + @echo "Done" all: @docker compose build @@ -39,7 +39,7 @@ status ps: @docker compose ps clean: - @echo "⚠️ Full rebuild (slow)..." + @echo "Full rebuild (slow)..." @docker compose build --no-cache @docker compose up -d diff --git a/README.md b/README.md index c915d3a..4b6301e 100644 --- a/README.md +++ b/README.md @@ -123,9 +123,9 @@ python -m venv venv && source venv/bin/activate pip install -r requirements.txt python main.py -# Frontend (new terminal) +# Frontend (new terminal) - MUST use Bun! cd frontend -npm install && npm run dev +bun install && bun run dev ``` diff --git a/frontend/bun.lock b/frontend/bun.lock index ec4f6e0..82a3651 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -7,11 +7,13 @@ "dependencies": { "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-switch": "^1.2.6", @@ -23,6 +25,7 @@ "@types/react-syntax-highlighter": "^15.5.13", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "dagre": "^0.8.5", "framer-motion": "^12.29.0", "lucide-react": "^0.554.0", @@ -51,13 +54,13 @@ "packages": { "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], - "@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="], + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - "@babel/compat-data": ["@babel/compat-data@7.28.6", "", {}, "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg=="], + "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], - "@babel/core": ["@babel/core@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw=="], + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], - "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="], + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], @@ -77,7 +80,7 @@ "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], - "@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="], + "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], @@ -87,9 +90,9 @@ "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], - "@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="], + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], - "@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="], + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], @@ -143,11 +146,11 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], + "@floating-ui/core": ["@floating-ui/core@1.7.4", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg=="], - "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], + "@floating-ui/dom": ["@floating-ui/dom@1.7.5", "", { "dependencies": { "@floating-ui/core": "^1.7.4", "@floating-ui/utils": "^0.2.10" } }, "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg=="], - "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw=="], + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.7", "", { "dependencies": { "@floating-ui/dom": "^1.7.5" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg=="], "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], @@ -167,6 +170,8 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], @@ -215,6 +220,8 @@ "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g=="], "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], @@ -261,71 +268,71 @@ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.56.0", "", { "os": "android", "cpu": "arm" }, "sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.56.0", "", { "os": "android", "cpu": "arm64" }, "sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.56.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.56.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.56.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.56.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.56.0", "", { "os": "linux", "cpu": "arm" }, "sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.56.0", "", { "os": "linux", "cpu": "arm" }, "sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.56.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.56.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="], - "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA=="], + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.56.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="], - "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.56.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.56.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.56.0", "", { "os": "linux", "cpu": "x64" }, "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.56.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="], - "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.56.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.56.0", "", { "os": "none", "cpu": "arm64" }, "sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.56.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.56.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.56.0", "", { "os": "win32", "cpu": "x64" }, "sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.56.0", "", { "os": "win32", "cpu": "x64" }, "sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], - "@supabase/auth-js": ["@supabase/auth-js@2.91.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-3gFGMPuif2BOuAHXLAGsoOlDa64PROct1v7G94pMnvUAhh75u6+vnx4MYz1wyoyDBN5lCkJPGQNg5+RIgqxnpA=="], + "@supabase/auth-js": ["@supabase/auth-js@2.95.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-vD2YoS8E2iKIX0F7EwXTmqhUpaNsmbU6X2R0/NdFcs02oEfnHyNP/3M716f3wVJ2E5XHGiTFXki6lRckhJ0Thg=="], - "@supabase/functions-js": ["@supabase/functions-js@2.91.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-xKepd3HZ6K6rKibriehKggIegsoz+jjV67tikN51q/YQq3AlUAkjUMSnMrqs8t5LMlAi+a3dJU812acXanR0cw=="], + "@supabase/functions-js": ["@supabase/functions-js@2.95.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-uTuOAKzs9R/IovW1krO0ZbUHSJnsnyJElTXIRhjJTqymIVGcHzkAYnBCJqd7468Fs/Foz1BQ7Dv6DCl05lr7ig=="], - "@supabase/postgrest-js": ["@supabase/postgrest-js@2.91.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-UKumTC6SGHd65G/5Gj0V58u+SkUyiH4zEJ8OP2eb06+Tqnges1E/3Tl7lyq2qbcMP8nEyH/0M7m2bYjrn++haw=="], + "@supabase/postgrest-js": ["@supabase/postgrest-js@2.95.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-LTrRBqU1gOovxRm1vRXPItSMPBmEFqrfTqdPTRtzOILV4jPSueFz6pES5hpb4LRlkFwCPRmv3nQJ5N625V2Xrg=="], - "@supabase/realtime-js": ["@supabase/realtime-js@2.91.1", "", { "dependencies": { "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "tslib": "2.8.1", "ws": "^8.18.2" } }, "sha512-Y4rifuvzekFgd2hUfiEvcMoh/JU3s1hmpWYS7tNGL2QHuFfWg8a4w/qg5qoSMVDvgGRz6G4L6yB1FaQRTplENQ=="], + "@supabase/realtime-js": ["@supabase/realtime-js@2.95.3", "", { "dependencies": { "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "tslib": "2.8.1", "ws": "^8.18.2" } }, "sha512-D7EAtfU3w6BEUxDACjowWNJo/ZRo7sDIuhuOGKHIm9FHieGeoJV5R6GKTLtga/5l/6fDr2u+WcW/m8I9SYmaIw=="], - "@supabase/storage-js": ["@supabase/storage-js@2.91.1", "", { "dependencies": { "iceberg-js": "^0.8.1", "tslib": "2.8.1" } }, "sha512-hMJNT2tSleOrWwx4FmHTpihIA2PRDixAsWflECuQ4YDkeduBZGX5m2txnstMnteWW+H+mm+92WRRFLuidXqbfA=="], + "@supabase/storage-js": ["@supabase/storage-js@2.95.3", "", { "dependencies": { "iceberg-js": "^0.8.1", "tslib": "2.8.1" } }, "sha512-4GxkJiXI3HHWjxpC3sDx1BVrV87O0hfX+wvJdqGv67KeCu+g44SPnII8y0LL/Wr677jB7tpjAxKdtVWf+xhc9A=="], - "@supabase/supabase-js": ["@supabase/supabase-js@2.91.1", "", { "dependencies": { "@supabase/auth-js": "2.91.1", "@supabase/functions-js": "2.91.1", "@supabase/postgrest-js": "2.91.1", "@supabase/realtime-js": "2.91.1", "@supabase/storage-js": "2.91.1" } }, "sha512-57Fb4s5nfLn5ed2a1rPtl+LI1Wbtms8MS4qcUa0w6luaStBlFhmSeD2TLBgJWdMIupWRF6iFTH4QTrO2+pG/ZQ=="], + "@supabase/supabase-js": ["@supabase/supabase-js@2.95.3", "", { "dependencies": { "@supabase/auth-js": "2.95.3", "@supabase/functions-js": "2.95.3", "@supabase/postgrest-js": "2.95.3", "@supabase/realtime-js": "2.95.3", "@supabase/storage-js": "2.95.3" } }, "sha512-Fukw1cUTQ6xdLiHDJhKKPu6svEPaCEDvThqCne3OaQyZvuq2qjhJAd91kJu3PXLG18aooCgYBaB6qQz35hhABg=="], "@tanstack/query-core": ["@tanstack/query-core@5.90.20", "", {}, "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg=="], - "@tanstack/react-query": ["@tanstack/react-query@5.90.20", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw=="], + "@tanstack/react-query": ["@tanstack/react-query@5.90.21", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg=="], "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], @@ -405,15 +412,15 @@ "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], - "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], + "@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="], "@types/phoenix": ["@types/phoenix@1.6.7", "", {}, "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q=="], - "@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="], + "@types/prismjs": ["@types/prismjs@1.26.6", "", {}, "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw=="], "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], - "@types/react": ["@types/react@18.3.27", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w=="], + "@types/react": ["@types/react@18.3.28", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw=="], "@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="], @@ -433,9 +440,9 @@ "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], - "autoprefixer": ["autoprefixer@10.4.23", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001760", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": "bin/autoprefixer" }, "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA=="], + "autoprefixer": ["autoprefixer@10.4.24", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001766", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": "bin/autoprefixer" }, "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.9.17", "", { "bin": "dist/cli.js" }, "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": "dist/cli.js" }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], @@ -445,7 +452,7 @@ "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], - "caniuse-lite": ["caniuse-lite@1.0.30001766", "", {}, "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA=="], + "caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="], "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], @@ -461,6 +468,8 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], @@ -503,7 +512,7 @@ "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], - "electron-to-chromium": ["electron-to-chromium@1.5.278", "", {}, "sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw=="], + "electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="], "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": "bin/esbuild" }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], @@ -523,7 +532,7 @@ "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], - "framer-motion": ["framer-motion@12.29.0", "", { "dependencies": { "motion-dom": "^12.29.0", "motion-utils": "^12.27.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-1gEFGXHYV2BD42ZPTFmSU9buehppU+bCuOnHU0AD18DKh9j4DuTx47MvqY5ax+NNWRtK32qIcJf1UxKo1WwjWg=="], + "framer-motion": ["framer-motion@12.34.0", "", { "dependencies": { "motion-dom": "^12.34.0", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-+/H49owhzkzQyxtn7nZeF4kdH++I2FWrESQ184Zbcw5cEqNHYkE5yxWxcTLSj5lNx3NWdbIRy5FHqUvetD8FWg=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -593,9 +602,9 @@ "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - "motion-dom": ["motion-dom@12.29.0", "", { "dependencies": { "motion-utils": "^12.27.2" } }, "sha512-3eiz9bb32yvY8Q6XNM4AwkSOBPgU//EIKTZwsSWgA9uzbPBhZJeScCVcBuwwYVqhfamewpv7ZNmVKTGp5qnzkA=="], + "motion-dom": ["motion-dom@12.34.0", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-Lql3NuEcScRDxTAO6GgUsRHBZOWI/3fnMlkMcH5NftzcN37zJta+bpbMAV9px4Nj057TuvRooMK7QrzMCgtz6Q=="], - "motion-utils": ["motion-utils@12.27.2", "", {}, "sha512-B55gcoL85Mcdt2IEStY5EEAsrMSVE2sI14xQ/uAdPL+mfQxhKKFaEag9JmfxedJOR4vZpBGoPeC/Gm13I/4g5Q=="], + "motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -675,7 +684,7 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rollup": ["rollup@4.56.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.56.0", "@rollup/rollup-android-arm64": "4.56.0", "@rollup/rollup-darwin-arm64": "4.56.0", "@rollup/rollup-darwin-x64": "4.56.0", "@rollup/rollup-freebsd-arm64": "4.56.0", "@rollup/rollup-freebsd-x64": "4.56.0", "@rollup/rollup-linux-arm-gnueabihf": "4.56.0", "@rollup/rollup-linux-arm-musleabihf": "4.56.0", "@rollup/rollup-linux-arm64-gnu": "4.56.0", "@rollup/rollup-linux-arm64-musl": "4.56.0", "@rollup/rollup-linux-loong64-gnu": "4.56.0", "@rollup/rollup-linux-loong64-musl": "4.56.0", "@rollup/rollup-linux-ppc64-gnu": "4.56.0", "@rollup/rollup-linux-ppc64-musl": "4.56.0", "@rollup/rollup-linux-riscv64-gnu": "4.56.0", "@rollup/rollup-linux-riscv64-musl": "4.56.0", "@rollup/rollup-linux-s390x-gnu": "4.56.0", "@rollup/rollup-linux-x64-gnu": "4.56.0", "@rollup/rollup-linux-x64-musl": "4.56.0", "@rollup/rollup-openbsd-x64": "4.56.0", "@rollup/rollup-openharmony-arm64": "4.56.0", "@rollup/rollup-win32-arm64-msvc": "4.56.0", "@rollup/rollup-win32-ia32-msvc": "4.56.0", "@rollup/rollup-win32-x64-gnu": "4.56.0", "@rollup/rollup-win32-x64-msvc": "4.56.0", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg=="], + "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -768,5 +777,9 @@ "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "tinyglobby/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], } } diff --git a/frontend/package.json b/frontend/package.json index 68f5346..5c75245 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,11 +12,13 @@ "dependencies": { "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-switch": "^1.2.6", @@ -28,6 +30,7 @@ "@types/react-syntax-highlighter": "^15.5.13", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "dagre": "^0.8.5", "framer-motion": "^12.29.0", "lucide-react": "^0.554.0", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index dc68ffe..2996b73 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,7 +8,16 @@ import { LandingPage } from './pages/LandingPage'; import { Dashboard } from './components/Dashboard'; import { DocsHomePage } from './pages/DocsHomePage'; import { MCPSetupPage } from './pages/MCPSetupPage'; +import { MCPToolsPage } from './pages/MCPToolsPage'; +import { MCPExamplesPage } from './pages/MCPExamplesPage'; +import { QuickStartPage } from './pages/QuickStartPage'; +import { SemanticSearchPage, DependencyAnalysisPage, ImpactPredictionPage, CodeStyleAnalysisPage } from './pages/features'; +import { DockerSetupPage, SelfHostingPage } from './pages/deployment'; +import { APIOverviewPage, APIRepositoriesPage, APISearchPage, APIAnalysisPage } from './pages/api'; +import { ArchitecturePage } from './pages/ArchitecturePage'; +import { ContributingPage } from './pages/ContributingPage'; import { GitHubCallbackPage } from './pages/GitHubCallbackPage'; +import { ScrollToTop } from './components/ScrollToTop'; function ProtectedRoute({ children }: { children: React.ReactNode }) { const { user, loading } = useAuth(); @@ -66,9 +75,30 @@ function AppRoutes() { {/* Documentation Routes - Public, no auth required */} } /> + } /> } /> + } /> + } /> - {/* GitHub OAuth Callback - Protected, user must be logged in */} + {/* Feature pages */} + } /> + } /> + } /> + } /> + + {/* Deployment pages */} + } /> + } /> + + {/* API Reference pages */} + } /> + } /> + } /> + } /> + + {/* Contributing pages */} + } /> + } /> - {/* Placeholder routes for future docs pages */} - } /> - } /> - } /> - } /> - } /> {/* Fallback */} } /> @@ -101,6 +125,7 @@ export function App() { > + diff --git a/frontend/src/components/ScrollToTop.tsx b/frontend/src/components/ScrollToTop.tsx new file mode 100644 index 0000000..424e7fa --- /dev/null +++ b/frontend/src/components/ScrollToTop.tsx @@ -0,0 +1,12 @@ +import { useEffect } from 'react' +import { useLocation } from 'react-router-dom' + +export function ScrollToTop() { + const { pathname } = useLocation() + + useEffect(() => { + window.scrollTo(0, 0) + }, [pathname]) + + return null +} diff --git a/frontend/src/components/docs/DocsBadges.tsx b/frontend/src/components/docs/DocsBadges.tsx new file mode 100644 index 0000000..5e27355 --- /dev/null +++ b/frontend/src/components/docs/DocsBadges.tsx @@ -0,0 +1,59 @@ +import { Clock } from 'lucide-react' +import { cn } from '@/lib/utils' + +interface TimeEstimateProps { + minutes: number + className?: string +} + +export function TimeEstimate({ minutes, className }: TimeEstimateProps) { + const label = minutes === 1 ? '1 min read' : `${minutes} min read` + + return ( + + + {label} + + ) +} + +// Difficulty badge +type Difficulty = 'beginner' | 'intermediate' | 'advanced' + +interface DifficultyBadgeProps { + level: Difficulty + className?: string +} + +const difficultyConfig: Record = { + beginner: { + label: 'Beginner', + className: 'text-green-400 bg-green-500/10 border-green-500/30', + }, + intermediate: { + label: 'Intermediate', + className: 'text-amber-400 bg-amber-500/10 border-amber-500/30', + }, + advanced: { + label: 'Advanced', + className: 'text-red-400 bg-red-500/10 border-red-500/30', + }, +} + +export function DifficultyBadge({ level, className }: DifficultyBadgeProps) { + const config = difficultyConfig[level] + + return ( + + {config.label} + + ) +} diff --git a/frontend/src/components/docs/DocsBreadcrumb.tsx b/frontend/src/components/docs/DocsBreadcrumb.tsx new file mode 100644 index 0000000..d2afe41 --- /dev/null +++ b/frontend/src/components/docs/DocsBreadcrumb.tsx @@ -0,0 +1,73 @@ +import { Link, useLocation } from 'react-router-dom' +import { ChevronRight, Home } from 'lucide-react' + +interface BreadcrumbItem { + label: string + href: string +} + +// Map paths to readable labels +const pathLabels: Record = { + 'docs': 'Documentation', + 'quickstart': 'Quick Start', + 'mcp-setup': 'MCP Setup', + 'mcp-tools': 'MCP Tools', + 'mcp-examples': 'Examples', + 'features': 'Features', + 'search': 'Semantic Search', + 'dependencies': 'Dependencies', + 'impact': 'Impact Analysis', + 'style': 'Code Style', + 'deployment': 'Deployment', + 'docker': 'Docker', + 'self-host': 'Self-Hosting', + 'api': 'API Reference', + 'authentication': 'Authentication', + 'repositories': 'Repositories', + 'architecture': 'Architecture', + 'contributing': 'Contributing', +} + +export function DocsBreadcrumb() { + const location = useLocation() + const pathSegments = location.pathname.split('/').filter(Boolean) + + // Build breadcrumb items + const items: BreadcrumbItem[] = pathSegments.map((segment, index) => { + const href = '/' + pathSegments.slice(0, index + 1).join('/') + const label = pathLabels[segment] || segment.charAt(0).toUpperCase() + segment.slice(1) + return { label, href } + }) + + // Don't show breadcrumb on docs home + if (pathSegments.length <= 1) { + return null + } + + return ( + + ) +} diff --git a/frontend/src/components/docs/DocsCallout.tsx b/frontend/src/components/docs/DocsCallout.tsx new file mode 100644 index 0000000..6fba506 --- /dev/null +++ b/frontend/src/components/docs/DocsCallout.tsx @@ -0,0 +1,69 @@ +import { cn } from '@/lib/utils' +import { AlertCircle, AlertTriangle, Info, Lightbulb, CheckCircle } from 'lucide-react' + +type CalloutType = 'info' | 'warning' | 'tip' | 'danger' | 'success' + +interface DocsCalloutProps { + type?: CalloutType + title?: string + children: React.ReactNode +} + +const calloutConfig: Record = { + info: { + icon: , + containerClass: 'bg-blue-500/10 border-blue-500/30', + titleClass: 'text-blue-400', + }, + warning: { + icon: , + containerClass: 'bg-amber-500/10 border-amber-500/30', + titleClass: 'text-amber-400', + }, + tip: { + icon: , + containerClass: 'bg-green-500/10 border-green-500/30', + titleClass: 'text-green-400', + }, + danger: { + icon: , + containerClass: 'bg-red-500/10 border-red-500/30', + titleClass: 'text-red-400', + }, + success: { + icon: , + containerClass: 'bg-emerald-500/10 border-emerald-500/30', + titleClass: 'text-emerald-400', + }, +} + +export function DocsCallout({ type = 'info', title, children }: DocsCalloutProps) { + const config = calloutConfig[type] + + return ( +
+
+ + {config.icon} + +
+ {title && ( +

+ {title} +

+ )} +
+ {children} +
+
+
+
+ ) +} diff --git a/frontend/src/components/docs/DocsCards.tsx b/frontend/src/components/docs/DocsCards.tsx new file mode 100644 index 0000000..10b0d7a --- /dev/null +++ b/frontend/src/components/docs/DocsCards.tsx @@ -0,0 +1,53 @@ +import { Link } from 'react-router-dom' +import { cn } from '@/lib/utils' + +interface DocsCardProps { + title: string + description: string + href: string + icon?: React.ReactNode + badge?: string +} + +export function DocsCard({ title, description, href, icon, badge }: DocsCardProps) { + return ( + + {badge && ( + + {badge} + + )} + +
+ {icon && ( +
+ {icon} +
+ )} +
+

+ {title} +

+

+ {description} +

+
+
+ + ) +} + +export function DocsCardGrid({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ) +} diff --git a/frontend/src/components/docs/DocsCodeBlock.tsx b/frontend/src/components/docs/DocsCodeBlock.tsx new file mode 100644 index 0000000..c6d6277 --- /dev/null +++ b/frontend/src/components/docs/DocsCodeBlock.tsx @@ -0,0 +1,177 @@ +import { useState } from 'react' +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' +import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism' +import { Check, Copy } from 'lucide-react' +import { cn } from '@/lib/utils' +import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs' + +interface CodeBlockProps { + children: string + language?: string + filename?: string + showLineNumbers?: boolean + highlightLines?: number[] +} + +export function DocsCodeBlock({ + children, + language = 'bash', + filename, + showLineNumbers = false, + highlightLines = [] +}: CodeBlockProps) { + const [copied, setCopied] = useState(false) + + const copyToClipboard = async () => { + try { + await navigator.clipboard.writeText(children.trim()) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } catch { + // Clipboard API failed, silently ignore + } + } + + return ( +
+ {filename && ( +
+ {filename} + {language} +
+ )} + +
+ 0} + lineProps={(lineNumber) => ({ + style: { + backgroundColor: highlightLines.includes(lineNumber) + ? 'rgba(59, 130, 246, 0.1)' + : 'transparent', + display: 'block', + width: '100%', + }, + })} + customStyle={{ + margin: 0, + padding: '1rem', + background: 'transparent', + fontSize: '0.875rem', + }} + > + {children.trim()} + + + +
+
+ ) +} + +// Multi-language code block with tabs +interface CodeTabsProps { + tabs: { + label: string + language: string + code: string + }[] + filename?: string +} + +export function DocsCodeTabs({ tabs, filename }: CodeTabsProps) { + const [copied, setCopied] = useState(false) + const [activeTab, setActiveTab] = useState(tabs[0]?.label || '') + + const copyToClipboard = async () => { + try { + const activeCode = tabs.find(t => t.label === activeTab)?.code || '' + await navigator.clipboard.writeText(activeCode.trim()) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } catch { + // Clipboard API failed, silently ignore + } + } + + return ( +
+ +
+ + {tabs.map((tab) => ( + + {tab.label} + + ))} + + + {filename && ( + {filename} + )} +
+ + {tabs.map((tab) => ( + +
+ + {tab.code.trim()} + +
+
+ ))} +
+ + +
+ ) +} diff --git a/frontend/src/components/docs/DocsLayout.tsx b/frontend/src/components/docs/DocsLayout.tsx index 3f14be3..7878bc9 100644 --- a/frontend/src/components/docs/DocsLayout.tsx +++ b/frontend/src/components/docs/DocsLayout.tsx @@ -1,18 +1,194 @@ +import { useState } from 'react' +import { Link, useLocation } from 'react-router-dom' import { DocsSidebar } from './DocsSidebar' +import { DocsBreadcrumb } from './DocsBreadcrumb' +import { DocsTableOfContents, TOCItem } from './DocsTableOfContents' +import { DocsSearch } from './DocsSearch' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet' +import { Menu, BookOpen } from 'lucide-react' +import { cn } from '@/lib/utils' interface DocsLayoutProps { children: React.ReactNode + toc?: TOCItem[] + showToc?: boolean } -export function DocsLayout({ children }: DocsLayoutProps) { +export function DocsLayout({ children, toc = [], showToc = true }: DocsLayoutProps) { + const [mobileMenuOpen, setMobileMenuOpen] = useState(false) + return ( -
- -
-
- {children} +
+ {/* Mobile header */} +
+
+ + + + + + setMobileMenuOpen(false)} /> + + + + +
+ +
+ Docs + +
+ +
+
-
+ + +
+ {/* Desktop sidebar */} + + + {/* Main content */} +
+
+
+ {/* Article content */} +
+ +
+ {children} +
+
+ + {/* Table of contents - desktop only */} + {showToc && toc.length > 0 && ( + + )} +
+
+
+
) } + +// Mobile sidebar content +function MobileSidebar({ onNavigate }: { onNavigate: () => void }) { + return ( + +
+ +
+ +
+
+

OpenCodeIntel

+

Documentation

+
+ + + +
+
+ ) +} + +// Mobile navigation items +const mobileNavigation = [ + { + title: 'Getting Started', + items: [ + { title: 'Introduction', href: '/docs' }, + { title: 'Quick Start', href: '/docs/quickstart' }, + ], + }, + { + title: 'MCP Integration', + items: [ + { title: 'MCP Setup Guide', href: '/docs/mcp-setup' }, + { title: 'Tools Reference', href: '/docs/mcp-tools' }, + { title: 'Example Prompts', href: '/docs/mcp-examples' }, + ], + }, + { + title: 'Features', + items: [ + { title: 'Semantic Search', href: '/docs/features/search' }, + { title: 'Dependency Analysis', href: '/docs/features/dependencies' }, + { title: 'Impact Prediction', href: '/docs/features/impact' }, + { title: 'Code Style Analysis', href: '/docs/features/style' }, + ], + }, + { + title: 'Deployment', + items: [ + { title: 'Docker Setup', href: '/docs/deployment/docker' }, + { title: 'Self-Hosting', href: '/docs/deployment/self-host' }, + ], + }, + { + title: 'API Reference', + items: [ + { title: 'Overview', href: '/docs/api' }, + { title: 'Repositories', href: '/docs/api/repositories' }, + { title: 'Search', href: '/docs/api/search' }, + { title: 'Analysis', href: '/docs/api/analysis' }, + ], + }, + { + title: 'Contributing', + items: [ + { title: 'Architecture', href: '/docs/architecture' }, + { title: 'Development Setup', href: '/docs/contributing' }, + ], + }, +] + +function MobileNavigation({ onNavigate }: { onNavigate: () => void }) { + const location = useLocation() + + const isActive = (href: string) => { + return location.pathname === href + } + + return ( + + ) +} diff --git a/frontend/src/components/docs/DocsPageHeader.tsx b/frontend/src/components/docs/DocsPageHeader.tsx new file mode 100644 index 0000000..f2422a0 --- /dev/null +++ b/frontend/src/components/docs/DocsPageHeader.tsx @@ -0,0 +1,38 @@ +import { TimeEstimate, DifficultyBadge } from './DocsBadges' + +interface DocsPageHeaderProps { + title: string + description: string + timeEstimate?: number + difficulty?: 'beginner' | 'intermediate' | 'advanced' + category?: string +} + +export function DocsPageHeader({ + title, + description, + timeEstimate, + difficulty, + category +}: DocsPageHeaderProps) { + return ( +
+ {category && ( +
+ {category} +
+ )} + +

{title}

+ +

{description}

+ + {(timeEstimate != null || difficulty) && ( +
+ {timeEstimate != null && } + {difficulty && } +
+ )} +
+ ) +} diff --git a/frontend/src/components/docs/DocsPrerequisites.tsx b/frontend/src/components/docs/DocsPrerequisites.tsx new file mode 100644 index 0000000..8ffb685 --- /dev/null +++ b/frontend/src/components/docs/DocsPrerequisites.tsx @@ -0,0 +1,101 @@ +import { useState } from 'react' +import { ChevronDown, Check, ExternalLink } from 'lucide-react' +import { cn } from '@/lib/utils' +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible' + +interface Prerequisite { + text: string + href?: string + completed?: boolean +} + +interface DocsPrerequisitesProps { + items: Prerequisite[] + defaultOpen?: boolean +} + +export function DocsPrerequisites({ items, defaultOpen = false }: DocsPrerequisitesProps) { + const [open, setOpen] = useState(defaultOpen) + + return ( + + +
+ 📋 + Prerequisites + ({items.length} items) +
+ +
+ + +
+
    + {items.map((item, index) => ( +
  • + + {item.completed ? ( + + ) : ( + + )} + + + {item.href ? ( + + {item.text} + + + ) : ( + item.text + )} + +
  • + ))} +
+
+
+
+ ) +} + +// What you will learn section +interface LearningObjective { + text: string +} + +interface DocsLearningObjectivesProps { + items: LearningObjective[] +} + +export function DocsLearningObjectives({ items }: DocsLearningObjectivesProps) { + return ( +
+

+ 🎯 + What you will learn +

+
    + {items.map((item, index) => ( +
  • + + {item.text} +
  • + ))} +
+
+ ) +} diff --git a/frontend/src/components/docs/DocsRelated.tsx b/frontend/src/components/docs/DocsRelated.tsx new file mode 100644 index 0000000..fad57bc --- /dev/null +++ b/frontend/src/components/docs/DocsRelated.tsx @@ -0,0 +1,101 @@ +import { Link } from 'react-router-dom' +import { ArrowRight, ChevronLeft, ChevronRight } from 'lucide-react' +import { cn } from '@/lib/utils' + +interface RelatedDoc { + title: string + description: string + href: string + icon?: React.ReactNode +} + +interface DocsRelatedProps { + items: RelatedDoc[] + title?: string +} + +export function DocsRelated({ items, title = 'Related' }: DocsRelatedProps) { + return ( +
+

+ {title} +

+
+ {items.map((item) => ( + + {item.icon && ( + + {item.icon} + + )} +
+

+ {item.title} +

+

+ {item.description} +

+
+ + + ))} +
+
+ ) +} + +// Previous/Next navigation at bottom of page +interface DocsPaginationProps { + prev?: { + title: string + href: string + } + next?: { + title: string + href: string + } +} + +export function DocsPagination({ prev, next }: DocsPaginationProps) { + return ( +
+ {prev ? ( + + +
+

Previous

+

{prev.title}

+
+ + ) : ( +
+ )} + + {next ? ( + +
+

Next

+

{next.title}

+
+ + + ) : ( +
+ )} +
+ ) +} diff --git a/frontend/src/components/docs/DocsSearch.tsx b/frontend/src/components/docs/DocsSearch.tsx new file mode 100644 index 0000000..b8d942e --- /dev/null +++ b/frontend/src/components/docs/DocsSearch.tsx @@ -0,0 +1,239 @@ +import { useEffect, useState, useCallback, useMemo } from 'react' +import { useNavigate } from 'react-router-dom' +import { + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command' +import { + FileText, + Zap, + GitBranch, + Search as SearchIcon, + AlertTriangle, + Palette, + Terminal, + Server, + Code, + BookOpen +} from 'lucide-react' + +interface DocPage { + title: string + href: string + description: string + icon: React.ReactNode + category: string +} + +const docsPages: DocPage[] = [ + // Getting Started + { + title: 'Introduction', + href: '/docs', + description: 'Overview of OpenCodeIntel', + icon: , + category: 'Getting Started' + }, + { + title: 'Quick Start', + href: '/docs/quickstart', + description: 'Get up and running in 5 minutes', + icon: , + category: 'Getting Started' + }, + + // MCP Integration + { + title: 'MCP Setup Guide', + href: '/docs/mcp-setup', + description: 'Connect to Claude Desktop', + icon: , + category: 'MCP Integration' + }, + { + title: 'MCP Tools Reference', + href: '/docs/mcp-tools', + description: 'All available MCP tools', + icon: , + category: 'MCP Integration' + }, + { + title: 'Example Prompts', + href: '/docs/mcp-examples', + description: 'Real prompts that work', + icon: , + category: 'MCP Integration' + }, + + // Features + { + title: 'Semantic Search', + href: '/docs/features/search', + description: 'Find code by meaning', + icon: , + category: 'Features' + }, + { + title: 'Dependency Analysis', + href: '/docs/features/dependencies', + description: 'Visualize architecture', + icon: , + category: 'Features' + }, + { + title: 'Impact Prediction', + href: '/docs/features/impact', + description: 'Know what breaks', + icon: , + category: 'Features' + }, + { + title: 'Code Style Analysis', + href: '/docs/features/style', + description: 'Team conventions', + icon: , + category: 'Features' + }, + + // Deployment + { + title: 'Docker Setup', + href: '/docs/deployment/docker', + description: 'Run with Docker Compose', + icon: , + category: 'Deployment' + }, + { + title: 'Self-Hosting Guide', + href: '/docs/deployment/self-host', + description: 'Full deployment guide', + icon: , + category: 'Deployment' + }, + + // API Reference + { + title: 'API Overview', + href: '/docs/api', + description: 'REST API introduction', + icon: , + category: 'API Reference' + }, + { + title: 'Repositories API', + href: '/docs/api/repositories', + description: 'Manage indexed repositories', + icon: , + category: 'API Reference' + }, + { + title: 'Search API', + href: '/docs/api/search', + description: 'Semantic code search', + icon: , + category: 'API Reference' + }, + { + title: 'Analysis API', + href: '/docs/api/analysis', + description: 'Dependencies, impact, style', + icon: , + category: 'API Reference' + }, + + // Contributing + { + title: 'Architecture', + href: '/docs/architecture', + description: 'System design and tech stack', + icon: , + category: 'Contributing' + }, + { + title: 'Development Setup', + href: '/docs/contributing', + description: 'Local dev environment', + icon: , + category: 'Contributing' + }, +] + +// Pre-compute grouped pages at module level since docsPages is constant +const groupedPages = docsPages.reduce((acc, page) => { + if (!acc[page.category]) { + acc[page.category] = [] + } + acc[page.category].push(page) + return acc +}, {} as Record) + +export function DocsSearch() { + const [open, setOpen] = useState(false) + const navigate = useNavigate() + + const isMac = useMemo(() => { + if (typeof navigator === 'undefined') return true + return navigator.platform.toLowerCase().includes('mac') + }, []) + + useEffect(() => { + const down = (e: KeyboardEvent) => { + if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { + e.preventDefault() + setOpen((open) => !open) + } + } + + document.addEventListener('keydown', down) + return () => document.removeEventListener('keydown', down) + }, []) + + const runCommand = useCallback((command: () => void) => { + setOpen(false) + command() + }, []) + + return ( + <> + + + + + + No results found. + {Object.entries(groupedPages).map(([category, pages]) => ( + + {pages.map((page) => ( + runCommand(() => navigate(page.href))} + className="flex items-center gap-3 cursor-pointer" + > + {page.icon} +
+ {page.title} + {page.description} +
+
+ ))} +
+ ))} +
+
+ + ) +} diff --git a/frontend/src/components/docs/DocsSidebar.tsx b/frontend/src/components/docs/DocsSidebar.tsx index d902619..8cf81c2 100644 --- a/frontend/src/components/docs/DocsSidebar.tsx +++ b/frontend/src/components/docs/DocsSidebar.tsx @@ -1,4 +1,20 @@ import { Link, useLocation } from 'react-router-dom' +import { cn } from '@/lib/utils' +import { ScrollArea } from '@/components/ui/scroll-area' +import { DocsSearch } from './DocsSearch' +import { + BookOpen, + Zap, + Terminal, + Code, + FileText, + Search, + GitBranch, + AlertTriangle, + Palette, + Server, + ChevronLeft +} from 'lucide-react' interface NavItem { title: string @@ -15,32 +31,48 @@ const navigation: NavSection[] = [ { title: 'Getting Started', items: [ - { title: 'Introduction', href: '/docs' }, - { title: 'Quick Start', href: '/docs/quickstart' }, + { title: 'Introduction', href: '/docs', icon: }, + { title: 'Quick Start', href: '/docs/quickstart', icon: }, ], }, { title: 'MCP Integration', items: [ - { title: 'MCP Setup Guide', href: '/docs/mcp-setup' }, - { title: 'Available Tools', href: '/docs/mcp-tools' }, - { title: 'Example Prompts', href: '/docs/mcp-examples' }, + { title: 'MCP Setup Guide', href: '/docs/mcp-setup', icon: }, + { title: 'Tools Reference', href: '/docs/mcp-tools', icon: }, + { title: 'Example Prompts', href: '/docs/mcp-examples', icon: }, ], }, { title: 'Features', items: [ - { title: 'Semantic Search', href: '/docs/features/search' }, - { title: 'Dependency Analysis', href: '/docs/features/dependencies' }, - { title: 'Impact Prediction', href: '/docs/features/impact' }, - { title: 'Code Style Analysis', href: '/docs/features/style' }, + { title: 'Semantic Search', href: '/docs/features/search', icon: }, + { title: 'Dependency Analysis', href: '/docs/features/dependencies', icon: }, + { title: 'Impact Prediction', href: '/docs/features/impact', icon: }, + { title: 'Code Style Analysis', href: '/docs/features/style', icon: }, + ], + }, + { + title: 'API Reference', + items: [ + { title: 'Overview', href: '/docs/api', icon: }, + { title: 'Repositories', href: '/docs/api/repositories', icon: }, + { title: 'Search', href: '/docs/api/search', icon: }, + { title: 'Analysis', href: '/docs/api/analysis', icon: }, ], }, { title: 'Deployment', items: [ - { title: 'Docker Setup', href: '/docs/deployment/docker' }, - { title: 'Self-Hosting', href: '/docs/deployment/self-host' }, + { title: 'Docker Setup', href: '/docs/deployment/docker', icon: }, + { title: 'Self-Hosting', href: '/docs/deployment/self-host', icon: }, + ], + }, + { + title: 'Contributing', + items: [ + { title: 'Architecture', href: '/docs/architecture', icon: }, + { title: 'Development Setup', href: '/docs/contributing', icon: }, ], }, ] @@ -53,43 +85,83 @@ export function DocsSidebar() { } return ( -
) diff --git a/frontend/src/components/docs/DocsStepList.tsx b/frontend/src/components/docs/DocsStepList.tsx new file mode 100644 index 0000000..6bb95df --- /dev/null +++ b/frontend/src/components/docs/DocsStepList.tsx @@ -0,0 +1,83 @@ +import { cn } from '@/lib/utils' + +interface Step { + title: string + children: React.ReactNode +} + +interface DocsStepListProps { + children: React.ReactNode +} + +interface DocsStepProps { + number: number + title: string + children: React.ReactNode +} + +export function DocsStepList({ children }: DocsStepListProps) { + return ( +
+ {children} +
+ ) +} + +export function DocsStep({ number, title, children }: DocsStepProps) { + return ( +
+ {/* Step number */} +
+ {number} +
+ + {/* Connecting line */} +
+ + {/* Content */} +
+

{title}

+
+ {children} +
+
+
+ ) +} + +// Alternative: Card-style steps for more visual separation +interface DocsStepCardProps { + number: number + title: string + description?: string + children: React.ReactNode +} + +export function DocsStepCard({ number, title, description, children }: DocsStepCardProps) { + return ( +
+
+ {/* Step indicator */} +
+
+ {number} +
+
+
+ + {/* Card content */} +
+
+

{title}

+ {description && ( +

{description}

+ )} +
+ {children} +
+
+
+
+
+ ) +} diff --git a/frontend/src/components/docs/DocsSteps.tsx b/frontend/src/components/docs/DocsSteps.tsx new file mode 100644 index 0000000..24defc1 --- /dev/null +++ b/frontend/src/components/docs/DocsSteps.tsx @@ -0,0 +1,99 @@ +import { cn } from '@/lib/utils' + +interface StepProps { + number: number + title: string + children: React.ReactNode +} + +export function Step({ number, title, children }: StepProps) { + return ( +
+ {/* Vertical line connecting steps */} +
+ + {/* Step number */} +
+ {number} +
+ + {/* Content */} +
+

{title}

+
+ {children} +
+
+
+ ) +} + +interface StepListProps { + children: React.ReactNode + className?: string +} + +export function StepList({ children, className }: StepListProps) { + return ( +
+ {children} +
+ ) +} + +// Alternative: Horizontal steps for shorter processes +interface HorizontalStepProps { + number: number + title: string + description?: string + active?: boolean + completed?: boolean +} + +export function HorizontalStep({ number, title, description, active, completed }: HorizontalStepProps) { + return ( +
+ {/* Connector line */} +
+ +
+ {/* Step number */} +
+ {completed ? '✓' : number} +
+ +
+

+ {title} +

+ {description && ( +

{description}

+ )} +
+
+
+ ) +} + +interface HorizontalStepListProps { + children: React.ReactNode + className?: string +} + +export function HorizontalStepList({ children, className }: HorizontalStepListProps) { + return ( +
+ {children} +
+ ) +} diff --git a/frontend/src/components/docs/DocsTableOfContents.tsx b/frontend/src/components/docs/DocsTableOfContents.tsx new file mode 100644 index 0000000..537fe07 --- /dev/null +++ b/frontend/src/components/docs/DocsTableOfContents.tsx @@ -0,0 +1,105 @@ +import { useEffect, useState } from 'react' +import { cn } from '@/lib/utils' + +export interface TOCItem { + id: string + title: string + level: number +} + +interface DocsTableOfContentsProps { + items: TOCItem[] +} + +export function DocsTableOfContents({ items }: DocsTableOfContentsProps) { + const [activeId, setActiveId] = useState('') + + useEffect(() => { + if (items.length === 0) return + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setActiveId(entry.target.id) + } + }) + }, + { + rootMargin: '-80px 0px -80% 0px', + threshold: 0, + } + ) + + items.forEach((item) => { + const element = document.getElementById(item.id) + if (element) { + observer.observe(element) + } + }) + + return () => observer.disconnect() + }, [items]) + + if (items.length === 0) { + return null + } + + return ( + + ) +} + +// Hook to extract TOC items from page content +export function useTableOfContents(): TOCItem[] { + const [items, setItems] = useState([]) + + useEffect(() => { + // Find all headings with IDs in the main content + const headings = document.querySelectorAll('article h2[id], article h3[id], article h4[id]') + + const tocItems: TOCItem[] = Array.from(headings).map((heading) => ({ + id: heading.id, + title: heading.textContent || '', + level: parseInt(heading.tagName.charAt(1)), + })) + + setItems(tocItems) + }, []) + + return items +} diff --git a/frontend/src/components/docs/index.ts b/frontend/src/components/docs/index.ts index a31bba1..15c31a7 100644 --- a/frontend/src/components/docs/index.ts +++ b/frontend/src/components/docs/index.ts @@ -1,2 +1,17 @@ +// Layout export { DocsLayout } from './DocsLayout' export { DocsSidebar } from './DocsSidebar' +export { DocsBreadcrumb } from './DocsBreadcrumb' +export { DocsTableOfContents, useTableOfContents } from './DocsTableOfContents' +export type { TOCItem } from './DocsTableOfContents' + +// Search +export { DocsSearch } from './DocsSearch' + +// Content components +export { DocsCodeBlock, DocsCodeTabs } from './DocsCodeBlock' +export { DocsCallout } from './DocsCallout' +export { DocsPrerequisites, DocsLearningObjectives } from './DocsPrerequisites' +export { DocsRelated, DocsPagination } from './DocsRelated' +export { Step, StepList, HorizontalStep, HorizontalStepList } from './DocsSteps' +export { TimeEstimate, DifficultyBadge } from './DocsBadges' diff --git a/frontend/src/components/ui/collapsible.tsx b/frontend/src/components/ui/collapsible.tsx new file mode 100644 index 0000000..9fa4894 --- /dev/null +++ b/frontend/src/components/ui/collapsible.tsx @@ -0,0 +1,11 @@ +"use client" + +import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" + +const Collapsible = CollapsiblePrimitive.Root + +const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger + +const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent + +export { Collapsible, CollapsibleTrigger, CollapsibleContent } diff --git a/frontend/src/components/ui/command.tsx b/frontend/src/components/ui/command.tsx new file mode 100644 index 0000000..1c1b1aa --- /dev/null +++ b/frontend/src/components/ui/command.tsx @@ -0,0 +1,158 @@ +"use client" + +import * as React from "react" +import { Command as CommandPrimitive } from "cmdk" +import { Search } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Dialog, DialogContent } from "@/components/ui/dialog" + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Command.displayName = CommandPrimitive.displayName + +interface CommandDialogProps { + open?: boolean + onOpenChange?: (open: boolean) => void + children?: React.ReactNode +} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ) +} + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)) + +CommandInput.displayName = CommandPrimitive.Input.displayName + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandList.displayName = CommandPrimitive.List.displayName + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)) + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandGroup.displayName = CommandPrimitive.Group.displayName + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +CommandSeparator.displayName = CommandPrimitive.Separator.displayName + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandItem.displayName = CommandPrimitive.Item.displayName + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/frontend/src/components/ui/scroll-area.tsx b/frontend/src/components/ui/scroll-area.tsx new file mode 100644 index 0000000..0b4a48d --- /dev/null +++ b/frontend/src/components/ui/scroll-area.tsx @@ -0,0 +1,48 @@ +"use client" + +import * as React from "react" +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" + +import { cn } from "@/lib/utils" + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)) +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + +)) +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName + +export { ScrollArea, ScrollBar } diff --git a/frontend/src/pages/ArchitecturePage.tsx b/frontend/src/pages/ArchitecturePage.tsx new file mode 100644 index 0000000..c17c78b --- /dev/null +++ b/frontend/src/pages/ArchitecturePage.tsx @@ -0,0 +1,341 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'overview', title: 'Overview', level: 2 }, + { id: 'tech-stack', title: 'Tech Stack', level: 2 }, + { id: 'data-flow', title: 'Data Flow', level: 2 }, + { id: 'backend-services', title: 'Backend Services', level: 2 }, + { id: 'frontend-structure', title: 'Frontend Structure', level: 2 }, + { id: 'mcp-server', title: 'MCP Server', level: 2 }, + { id: 'database-schema', title: 'Database Schema', level: 2 }, +] + +export function ArchitecturePage() { + return ( + +
+
+ +
+

Architecture

+

+ How OpenCodeIntel is built. Technical deep-dive for contributors. +

+
+ +

Overview

+ +

+ OpenCodeIntel is a monorepo with three main components: a FastAPI backend, + a React frontend, and a standalone MCP server. The backend handles code indexing, + semantic search, and analysis. The frontend provides the dashboard UI. The MCP + server exposes tools to AI assistants like Claude. +

+ + +{`opencodeintel/ +├── backend/ # FastAPI API server (Python 3.11+) +├── frontend/ # React dashboard (TypeScript, Vite, Bun) +├── mcp-server/ # MCP protocol server (Python) +├── supabase/ # Database migrations +├── docs/ # Additional documentation +└── docker-compose.yml`} + + +

Tech Stack

+ +

Backend

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentTechnologyPurpose
FrameworkFastAPIAsync REST API with automatic OpenAPI docs
RuntimePython 3.11+Required for tree-sitter bindings
Code ParsingTree-sitterAST extraction for Python, JS, TS
EmbeddingsOpenAI text-embedding-3-small1536-dim vectors for semantic search
SummariesGPT-4o-miniNatural language code descriptions
Vector DBPineconeServerless vector storage and search
CacheRedisQuery caching, rate limiting
DatabaseSupabase (PostgreSQL)User data, repo metadata, API keys
AuthSupabase AuthJWT-based authentication
RerankingCohere (optional)Improves search result ordering
+
+ +

Frontend

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentTechnologyPurpose
FrameworkReact 18UI framework
LanguageTypeScriptType safety
Build ToolViteFast dev server and bundling
Package ManagerBunFast installs (do NOT use npm)
StylingTailwind CSSUtility-first CSS
Componentsshadcn/ui + RadixAccessible component primitives
Data FetchingTanStack QueryCaching, background refetch
RoutingReact Router v7Client-side navigation
Graph VisualizationReact Flow + DagreDependency graph rendering
+
+ + + The frontend uses Bun exclusively. Never use npm or yarn. + Always run bun install, not npm install. + + +

Data Flow

+ +

Indexing Pipeline

+

When a repository is added:

+ + +{`1. Clone repo to backend/repos/{uuid}/ +2. Walk file tree, filter by language (Python, JS, TS) +3. For each file: + a. Parse with Tree-sitter → Extract functions/classes + b. Generate summary with GPT-4o-mini + c. Create embedding with text-embedding-3-small + d. Store in Pinecone with metadata +4. Build dependency graph from import statements +5. Cache graph in Redis +6. Update repo status in Supabase`} + + +

Search Pipeline

+ +{`1. Receive query from user/MCP +2. Check Redis cache for identical query +3. If miss: + a. Embed query with text-embedding-3-small + b. Query Pinecone for top-k similar chunks + c. (Optional) Rerank with Cohere + d. Cache results in Redis (5 min TTL) +4. Return formatted results`} + + +

Backend Services

+ +

+ Key services in backend/services/: +

+ +
+ + + + + + +
+ +

API Routes

+

+ Routes in backend/routes/. + All prefixed with /api/v1: +

+ + +{`repos.py → /api/v1/repos/* # CRUD for repositories +search.py → /api/v1/search # Semantic search +analysis.py → /api/v1/repos/{id}/* # Dependencies, impact, style, DNA +auth.py → /api/v1/auth/* # Login, signup, session +github.py → /api/v1/github/* # GitHub OAuth, repo import +health.py → /health # Health check (no prefix)`} + + +

Frontend Structure

+ + +{`frontend/src/ +├── components/ +│ ├── ui/ # shadcn/ui primitives +│ ├── docs/ # Documentation components +│ ├── dashboard/ # Dashboard-specific components +│ ├── landing/ # Landing page components +│ └── DependencyGraph/ # React Flow graph components +├── pages/ # Route components +├── contexts/ # React contexts (AuthContext) +├── hooks/ # Custom hooks +├── services/ # API client +├── lib/ # Utilities +└── config/ # App configuration`} + + +

MCP Server

+ +

+ The MCP server (mcp-server/server.py) + is a standalone Python process that exposes 7 tools to AI assistants: +

+ + +{`# Tools exposed via MCP protocol +search_code # Semantic code search +list_repositories # List indexed repos +get_dependency_graph # File dependency graph +analyze_code_style # Team coding conventions +analyze_impact # Change impact analysis +get_repository_insights # High-level repo metrics +get_codebase_dna # Architectural patterns`} + + +

+ The MCP server is a thin proxy - it forwards requests to the FastAPI backend + and formats responses for AI consumption. It uses httpx for + async HTTP calls and the mcp library + for protocol handling. +

+ +

Database Schema

+ +

+ Supabase tables (see supabase/migrations/): +

+ + +{`-- Core tables in codeintel schema +codeintel.repositories # Repo metadata, status, user_id +codeintel.api_keys # User API keys for programmatic access +codeintel.user_limits # Rate limits and quotas per user + +-- Auth handled by Supabase Auth (auth.users) +-- Row Level Security (RLS) enforces user isolation`} + + + + Vector embeddings are stored in Pinecone, not PostgreSQL. + This allows efficient similarity search at scale. + + + +
+ ) +} + +function ServiceCard({ name, description }: { name: string; description: string }) { + return ( +
+

{name}

+

{description}

+
+ ) +} diff --git a/frontend/src/pages/ContributingPage.tsx b/frontend/src/pages/ContributingPage.tsx new file mode 100644 index 0000000..4961068 --- /dev/null +++ b/frontend/src/pages/ContributingPage.tsx @@ -0,0 +1,262 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPrerequisites, + DocsPagination, + TimeEstimate, + Step, + StepList, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'prerequisites', title: 'Prerequisites', level: 2 }, + { id: 'local-setup', title: 'Local Setup', level: 2 }, + { id: 'running-tests', title: 'Running Tests', level: 2 }, + { id: 'code-style', title: 'Code Style', level: 2 }, + { id: 'pull-requests', title: 'Pull Requests', level: 2 }, + { id: 'project-structure', title: 'Project Structure', level: 2 }, +] + +export function ContributingPage() { + return ( + +
+
+ +
+

Contributing

+

+ How to set up your development environment and contribute to OpenCodeIntel. +

+
+ +

Prerequisites

+ + + + + The frontend uses Bun exclusively. Do NOT use npm or yarn. + If you don't have Bun, install it: curl -fsSL https://bun.sh/install | bash + + +

Local Setup

+ + + + +{`git clone https://github.com/YOUR_USERNAME/opencodeintel.git +cd opencodeintel`} + + + + + +{`cd backend +python -m venv venv +source venv/bin/activate # Windows: venv\\Scripts\\activate +pip install -r requirements.txt + +# Configure environment +cp .env.example .env +# Edit .env and add your API keys: +# - OPENAI_API_KEY +# - PINECONE_API_KEY +# - PINECONE_INDEX_NAME`} + + + + + +{`cd ../frontend +bun install # NOT npm install! + +# Configure environment +cp .env.example .env`} + + + + +

Option A: Run services individually

+ +{`# Terminal 1: Backend +cd backend +source venv/bin/activate +uvicorn main:app --reload --port 8000 + +# Terminal 2: Frontend +cd frontend +bun run dev`} + + +

Option B: Use Docker Compose

+ +{`docker compose up -d`} + +
+ + + + +
+ +

Running Tests

+ +

Backend Tests

+ +{`cd backend +source venv/bin/activate + +# Run all tests +pytest tests/ -v + +# Run with coverage +pytest tests/ -v --cov=services --cov-report=term-missing + +# Run specific test file +pytest tests/test_validation.py -v + +# Run tests matching a pattern +pytest tests/ -v -k "test_search"`} + + +

Frontend Type Check

+ +{`cd frontend +bun run build # Build check +bun run typecheck # TypeScript only`} + + + + Tests use mocked external services (OpenAI, Pinecone, Supabase). + See backend/tests/conftest.py for mock setup. + + +

Code Style

+ +

Python (Backend)

+
    +
  • Follow PEP 8
  • +
  • Use type hints for function signatures
  • +
  • Max line length: 120 characters
  • +
  • Use async/await for I/O operations
  • +
  • Docstrings for public functions
  • +
+ + +{`# Lint check +flake8 services/ --max-line-length=120`} + + +

TypeScript (Frontend)

+
    +
  • TypeScript strict mode enabled
  • +
  • Prefer functional components with hooks
  • +
  • Use Tailwind for styling (no CSS files)
  • +
  • Prefer shadcn/ui components over custom UI
  • +
  • No any types without justification
  • +
+ +

Commit Messages

+

Use conventional commits:

+ +{`feat: add new feature +fix: bug fix +docs: documentation changes +refactor: code restructuring +test: adding tests +chore: maintenance tasks`} + + +

Pull Requests

+ +

Before Submitting

+
    +
  • Create an issue first to discuss the approach
  • +
  • Fork the repo and create a feature branch
  • +
  • Write tests for new functionality
  • +
  • Ensure all tests pass: pytest tests/ -v
  • +
  • Update documentation if needed
  • +
+ +

PR Guidelines

+
    +
  • Keep changes focused (one feature/fix per PR)
  • +
  • Write clear commit messages
  • +
  • Reference the issue number in the PR description
  • +
  • Add tests for new code
  • +
  • Respond to review feedback promptly
  • +
+ +

Branch Naming

+ +{`feature/add-rust-support +fix/search-cache-bug +docs/update-api-reference +refactor/simplify-indexer`} + + +

Project Structure

+ + +{`opencodeintel/ +├── backend/ +│ ├── main.py # FastAPI app entry point +│ ├── routes/ # API route handlers +│ ├── services/ # Business logic +│ ├── middleware/ # Auth, rate limiting +│ ├── tests/ # pytest test suite +│ ├── config/ # API versioning config +│ └── requirements.txt # Python dependencies +│ +├── frontend/ +│ ├── src/ +│ │ ├── components/ # React components +│ │ ├── pages/ # Route pages +│ │ ├── hooks/ # Custom hooks +│ │ ├── contexts/ # React contexts +│ │ └── services/ # API client +│ ├── package.json # Dependencies +│ └── bun.lock # Bun lockfile (NOT package-lock.json) +│ +├── mcp-server/ +│ ├── server.py # MCP protocol implementation +│ └── config.py # API URL config +│ +├── supabase/ +│ └── migrations/ # Database schema +│ +├── docker-compose.yml # Local development stack +├── Makefile # Common commands +└── CONTRIBUTING.md # This guide (markdown version)`} + + + + Use make commands for common tasks: + make f - Rebuild frontend + make b - Rebuild backend + make logs - View frontend logs + + + +
+ ) +} diff --git a/frontend/src/pages/DocsHomePage.tsx b/frontend/src/pages/DocsHomePage.tsx index ba25764..a24065e 100644 --- a/frontend/src/pages/DocsHomePage.tsx +++ b/frontend/src/pages/DocsHomePage.tsx @@ -1,153 +1,223 @@ import { Link } from 'react-router-dom' -import { DocsLayout } from '../components/docs/DocsLayout' +import { DocsLayout } from '@/components/docs' +import { + Zap, + Search, + GitBranch, + AlertTriangle, + Palette, + Terminal, + Server, + ArrowRight, + BookOpen, + Code, + ExternalLink +} from 'lucide-react' -function FeatureCard({ title, description, href, icon }: { +interface QuickLinkProps { title: string description: string href: string - icon: React.ReactNode -}) { + icon: React.ReactNode + time?: string +} + +function QuickLink({ title, description, href, icon, time }: QuickLinkProps) { return ( -
-
+
+
{icon}
-
-

{title}

-

{description}

-
+ {time && ( + {time} + )} +
+

+ {title} +

+

{description}

+ + Learn more + + + + ) +} + +interface FeatureCardProps { + title: string + description: string + href: string + icon: React.ReactNode +} + +function FeatureCard({ title, description, href, icon }: FeatureCardProps) { + return ( + +
+ {icon} +
+
+

+ {title} +

+

{description}

+ ) } export function DocsHomePage() { return ( - -
- {/* Header */} + +
+ {/* Hero */}
-

OpenCodeIntel Documentation

-

+

+ OpenCodeIntel Docs +

+

Give your AI assistant deep understanding of your codebase. Semantic search, - dependency analysis, and impact prediction - all through MCP. + dependency analysis, and impact prediction. Set up in 5 minutes.

- {/* Quick Start */} -
-

Get Started

-

- New to OpenCodeIntel? Start here. Most developers are up and running in under 5 minutes. -

- -
- +

+ Get Started +

+
+ } + time="5 min" + /> + - - - } + icon={} + time="5 min" /> - - - - } + } + time="15 min" />
- {/* Core Features */} -
-

Core Features

-

- What OpenCodeIntel actually does for you. -

- -
+ {/* Features */} +
+

+ Core Features +

+
- - - } + icon={} /> - - - } + icon={} /> - - - } + icon={} /> - - - } + icon={} />
{/* Why OpenCodeIntel */} -
-

Why OpenCodeIntel?

-
-

- The problem: AI coding assistants are powerful, - but they're flying blind. They can't search your actual codebase, don't understand - your architecture, and have no clue what breaks when you change something. -

-

- The solution: OpenCodeIntel is an MCP server that - gives AI assistants persistent memory of your codebase. Not just file contents - - semantic understanding, dependency graphs, and impact analysis. -

-

- The result: Claude that actually knows your code. - Ask "where's the auth middleware?" and get real answers, not guesses. -

+
+
+

Why OpenCodeIntel?

+
+

+ The problem: AI coding assistants are powerful, + but they are flying blind. They cannot search your actual codebase, do not understand + your architecture, and have no clue what breaks when you change something. +

+

+ The solution: OpenCodeIntel is an MCP server that + gives AI assistants persistent memory of your codebase. Not just file contents but + semantic understanding, dependency graphs, and impact analysis. +

+

+ The result: Claude that actually knows your code. + Ask "where is the auth middleware?" and get real answers, not guesses. +

+
+
+
+ + {/* API Reference */} +
+

+ API Reference +

+
+ +
+ +
+
+

REST API

+

Full API reference with examples

+
+ + +
+ +
+
+

MCP Tools

+

Tools available to AI assistants

+
+
{/* Resources */}
-

Resources

-
diff --git a/frontend/src/pages/MCPExamplesPage.tsx b/frontend/src/pages/MCPExamplesPage.tsx new file mode 100644 index 0000000..5f21aaf --- /dev/null +++ b/frontend/src/pages/MCPExamplesPage.tsx @@ -0,0 +1,184 @@ +import { + DocsLayout, + DocsCallout, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'onboarding', title: 'Onboarding to New Codebase', level: 2 }, + { id: 'refactoring', title: 'Before Refactoring', level: 2 }, + { id: 'debugging', title: 'Debugging', level: 2 }, + { id: 'code-review', title: 'Code Review', level: 2 }, + { id: 'writing-new-code', title: 'Writing New Code', level: 2 }, + { id: 'documentation', title: 'Documentation', level: 2 }, +] + +export function MCPExamplesPage() { + return ( + +
+
+ +
+

Example Prompts

+

+ Real-world prompts for using OpenCodeIntel with Claude. Copy, paste, adapt. +

+
+ +

+ These are actual prompts that work well with OpenCodeIntel. They show how to + leverage semantic search, dependency analysis, and impact prediction in real workflows. +

+ + {/* Onboarding */} +

+ Onboarding to New Codebase +

+

First day on a new project? Start here.

+ + + + + + {/* Refactoring */} +

+ Before Refactoring +

+

Know what will break before you break it.

+ + + + + + {/* Debugging */} +

+ Debugging +

+

Find the source of bugs faster.

+ + + + + + {/* Code Review */} +

+ Code Review +

+

Review PRs with context.

+ + + + + + {/* Writing New Code */} +

+ Writing New Code +

+

Write code that fits in.

+ + + + + + {/* Documentation */} +

+ Documentation +

+

Understand and document.

+ + + + + + These prompts work best when you have already indexed your repository. + The more code indexed, the better the results. + + + +
+ ) +} + +function PromptCard({ title, prompt, explanation }: { title: string; prompt: string; explanation: string }) { + return ( +
+

{title}

+

"{prompt}"

+

{explanation}

+
+ ) +} diff --git a/frontend/src/pages/MCPSetupPage.tsx b/frontend/src/pages/MCPSetupPage.tsx index 316075f..3644ba9 100644 --- a/frontend/src/pages/MCPSetupPage.tsx +++ b/frontend/src/pages/MCPSetupPage.tsx @@ -1,204 +1,157 @@ -import { DocsLayout } from '../components/docs/DocsLayout' - -// Code block component with copy button -function CodeBlock({ children, language = 'bash' }: { children: string; language?: string }) { - const copyToClipboard = () => { - navigator.clipboard.writeText(children) - } - - return ( -
-
-        
-          {children}
-        
-      
- -
- ) -} - -// Callout component for tips and warnings -function Callout({ type = 'info', children }: { type?: 'info' | 'warning' | 'tip'; children: React.ReactNode }) { - const styles = { - info: 'bg-blue-500/10 border-blue-500/30 text-blue-200', - warning: 'bg-yellow-500/10 border-yellow-500/30 text-yellow-200', - tip: 'bg-green-500/10 border-green-500/30 text-green-200', - } - - const icons = { - info: 'ℹ️', - warning: '⚠️', - tip: '💡', - } - - return ( -
- {icons[type]} - {children} -
- ) -} +import { + DocsLayout, + DocsCodeBlock, + DocsCodeTabs, + DocsCallout, + DocsPrerequisites, + DocsLearningObjectives, + DocsPagination, + TimeEstimate, + Step, + StepList, + TOCItem +} from '@/components/docs' +import { Terminal } from 'lucide-react' + +const tocItems: TOCItem[] = [ + { id: 'what-is-mcp', title: 'What is MCP?', level: 2 }, + { id: 'prerequisites', title: 'Prerequisites', level: 2 }, + { id: 'setup-steps', title: 'Setup Steps', level: 2 }, + { id: 'available-tools', title: 'Available Tools', level: 2 }, + { id: 'example-prompts', title: 'Example Prompts', level: 2 }, + { id: 'troubleshooting', title: 'Troubleshooting', level: 2 }, +] export function MCPSetupPage() { return ( - -
- {/* Header */} -
-
- - - - MCP Integration -
-

MCP Setup Guide

-

- Connect OpenCodeIntel to Claude Desktop in under 5 minutes. Give your AI assistant - persistent memory of your entire codebase. -

+ + {/* Header */} +
+
+
- - {/* The Problem */} -
-

- Most AI coding assistants forget your codebase the moment you close the chat. - You explain your auth flow, close the window, come back tomorrow - and Claude - has no idea what you're talking about. -

-

- OpenCodeIntel fixes this. It's an MCP server that gives Claude (or any MCP-compatible AI) - persistent access to your codebase - semantic search, dependency graphs, impact analysis, - the works. -

-
- - {/* What is MCP */} -
-

What is MCP?

-

- MCP (Model Context Protocol) is Anthropic's open standard for connecting AI assistants - to external tools and data sources. Think of it as USB for AI - a universal way to - plug in capabilities. -

-

- Instead of copy-pasting code into Claude, MCP lets Claude directly search your codebase, - analyze dependencies, and understand impact of changes. The result? Claude that actually knows your code. -

-
- - {/* Prerequisites */} -
-

Prerequisites

-

Before you start, make sure you have:

-
    -
  • - - Claude Desktop installed (download here) -
  • -
  • - - OpenCodeIntel backend running (local or hosted) -
  • -
  • - - Python 3.11+ for the MCP server -
  • -
  • - - 5 minutes of your time -
  • -
-
- - {/* Setup Steps */} -
-

Setup Steps

- - {/* Step 1 */} -
-

- 1 - Clone the MCP Server -

-

If you haven't already, grab the OpenCodeIntel repo:

- {`git clone https://github.com/OpenCodeIntel/opencodeintel.git -cd opencodeintel/mcp-server`} -
- - {/* Step 2 */} -
-

- 2 - Install Dependencies -

- {`pip install -r requirements.txt`} -

That's it. No virtual environment drama for a simple MCP server.

-
- - {/* Step 3 */} -
-

- 3 - Configure Environment -

-

Create your .env file:

- {`cp .env.example .env`} -

Edit .env with your settings:

- {`# Where's your OpenCodeIntel backend running? +

MCP Setup Guide

+

+ Connect OpenCodeIntel to Claude Desktop in under 5 minutes. Give your AI assistant + persistent memory of your entire codebase. +

+
+ + + + {/* Intro */} +

+ Most AI coding assistants forget your codebase the moment you close the chat. + You explain your auth flow, close the window, come back tomorrow, and Claude + has no idea what you were talking about. +

+

+ OpenCodeIntel fixes this. It is an MCP server that gives Claude (or any MCP-compatible AI) + persistent access to your codebase: semantic search, dependency graphs, impact analysis, + the works. +

+ + {/* What is MCP */} +

What is MCP?

+

+ MCP (Model Context Protocol) is Anthropic's open standard for connecting AI assistants + to external tools and data sources. Think of it as USB for AI: a universal way to + plug in capabilities. +

+

+ Instead of copy-pasting code into Claude, MCP lets Claude directly search your codebase, + analyze dependencies, and understand impact of changes. + The result? Claude that actually knows your code. +

+ + {/* Prerequisites */} +

Prerequisites

+ + + {/* Setup Steps */} +

Setup Steps

+ + + +

If you have not already, grab the OpenCodeIntel repo:

+ +{`git clone https://github.com/OpenCodeIntel/opencodeintel.git +cd opencodeintel/mcp-server`} + +
+ + +

Install the required Python packages:

+ +{`pip install -r requirements.txt`} + + + No virtual environment drama needed for a simple MCP server. Just install and go. + +
+ + +

Create your .env file:

+ +{`cp .env.example .env`} + +

Edit .env with your settings:

+ +{`# Where is your CodeIntel backend running? BACKEND_API_URL=http://localhost:8000 -# Your API key (get this from the OpenCodeIntel dashboard) -API_KEY=your-api-key-here`} - - Using hosted OpenCodeIntel? Replace localhost:8000 with your hosted URL. - +# Your API key (get this from the CodeIntel dashboard) +API_KEY=your-api-key-here`} + + + Using hosted OpenCodeIntel? Replace localhost:8000 with your hosted URL. + +
+ + +

This is where the magic happens. You need to tell Claude Desktop about the MCP server.

+

Find your config file:

+ +
+ + + + + + + + + + + + + + + + + + + + + +
OSConfig Location
macOS~/Library/Application Support/Claude/claude_desktop_config.json
Windows%APPDATA%\Claude\claude_desktop_config.json
Linux~/.config/Claude/claude_desktop_config.json
- {/* Step 4 */} -
-

- 4 - Configure Claude Desktop -

-

This is where the magic happens. You need to tell Claude Desktop about the MCP server.

- -

Find your config file:

-
- - - - - - - - - - - - - - - - - - - - - -
OSConfig Location
macOS~/Library/Application Support/Claude/claude_desktop_config.json
Windows%APPDATA%\Claude\claude_desktop_config.json
Linux~/.config/Claude/claude_desktop_config.json
-
- -

Add OpenCodeIntel to your config:

- {`{ +

Add OpenCodeIntel to your config:

+ +{`{ "mcpServers": { "codeintel": { "command": "python", @@ -209,211 +162,197 @@ API_KEY=your-api-key-here`}
} } } -}`} - - Important: Use the absolute path to server.py. Relative paths won't work. - - -

Example for macOS:

- {`{ - "mcpServers": { - "codeintel": { - "command": "python3", - "args": ["/Users/yourname/projects/opencodeintel/mcp-server/server.py"], - "env": { - "BACKEND_API_URL": "http://localhost:8000", - "API_KEY": "dev-secret-key" - } - } - } -}`} -
- - {/* Step 5 */} -
-

- 5 - Restart Claude Desktop -

-

- Completely quit Claude Desktop (not just close the window) and reopen it. -

-

- You should see a 🔧 icon in the chat input - that means MCP tools are available. -

-
-
- - {/* Available Tools */} -
-

Available Tools

-

Once connected, Claude has access to these tools:

- -
- {/* search_code */} -
-

search_code

-

Semantic search across your codebase. Finds code by meaning, not just keywords.

-
-

"Find authentication middleware"

-

"Show me error handling patterns"

-

"Where's the database connection logic?"

-
-
- - {/* list_repositories */} -
-

list_repositories

-

See all indexed repositories.

-
-

"What repos do you have access to?"

-

"List my codebases"

-
-
- - {/* get_dependency_graph */} -
-

get_dependency_graph

-

Understand how files connect. See which files are critical vs isolated.

-
-

"Show me the dependency graph for this repo"

-

"What files does auth.py depend on?"

-
-
- - {/* analyze_code_style */} -
-

analyze_code_style

-

Team patterns: naming conventions, async usage, type hints, common imports.

-
-

"What coding conventions does this repo use?"

-

"Is this team using snake_case or camelCase?"

-
-
- - {/* analyze_impact */} -
-

analyze_impact

-

Before you change a file, know what breaks. Shows dependents, impact, and related tests.

-
-

"What happens if I modify src/auth/middleware.py?"

-

"What's the blast radius of changing this file?"

-
-
- - {/* get_repository_insights */} -
-

get_repository_insights

-

High-level overview: file count, critical files, architecture patterns.

-
-

"Give me an overview of this codebase"

-

"What are the most important files here?"

-
-
-
-
- - {/* Example Prompts */} -
-

Example Prompts

-

Here's how to actually use OpenCodeIntel with Claude:

- -
-
-

Understanding new code:

-

"I just joined this project. Search for the main entry points and explain the architecture."

-
- -
-

Before refactoring:

-

"I want to refactor UserService. What's the impact? What tests cover it?"

-
- -
-

Finding patterns:

-

"How does this codebase handle errors? Find examples of error handling."

-
- -
-

Code review prep:

-

"Search for all usages of the deprecated oldAuth() function."

-
- -
-

Matching team style:

-

"Analyze the code style. I want to write a new module that fits in."

-
-
-
- - {/* Troubleshooting */} -
-

Troubleshooting

- -
-
-

Claude doesn't show the 🔧 icon

-
    -
  • • Check the config path - Make sure you're editing the right config file
  • -
  • • Validate JSON - A single missing comma breaks everything
  • -
  • • Use absolute paths - Relative paths don't work
  • -
  • • Restart fully - Quit Claude Desktop completely, not just close window
  • -
-
- -
-

"Connection refused" errors

-

Your OpenCodeIntel backend isn't running. Start it:

- {`cd opencodeintel/backend -python main.py`} -
- -
-

"Unauthorized" errors

-

- Check your API_KEY in both - the .env file and Claude Desktop config. - They need to match what your backend expects. -

-
- -
-

Tools work but return no results

-

- You probably haven't indexed any repositories yet. Open the OpenCodeIntel dashboard and add a repo first. -

-
+}`} + + + + Use the absolute path to server.py. Relative paths will not work. + + + + +

+ Completely quit Claude Desktop (not just close the window) and reopen it. +

+

+ You should see a 🔧 icon in the chat input. That means MCP tools are available. +

+ + Claude now has access to your codebase through OpenCodeIntel. + +
+ + + {/* Available Tools */} +

Available Tools

+

Once connected, Claude has access to these tools:

+ +
+ + + + + + +
+ + {/* Example Prompts */} +

Example Prompts

+

Here is how to actually use OpenCodeIntel with Claude:

+ +
+ + + + + +
+ + {/* Troubleshooting */} +

Troubleshooting

+ +
+ + + + + +
+ + {/* Pagination */} + + + ) +} -
-

Python command not found

-

On macOS, you might need python3 instead of python:

- {`{ - "command": "python3", - "args": ["/path/to/server.py"] -}`} -
-
-
+// Helper components +function ToolCard({ name, description, examples }: { name: string; description: string; examples: string[] }) { + return ( +
+

{name}

+

{description}

+
+ {examples.map((example, i) => ( +

"{example}"

+ ))} +
+
+ ) +} - {/* What's Next */} -
-

What's Next?

-

Once you're set up:

-
    -
  1. 1. Index a repository through the OpenCodeIntel dashboard
  2. -
  3. 2. Start chatting with Claude about your code
  4. -
  5. 3. Try impact analysis before your next refactor
  6. -
-

- Questions? Issues? Open a GitHub issue or reach out. -

-
+function PromptExample({ title, prompt }: { title: string; prompt: string }) { + return ( +
+

{title}:

+

"{prompt}"

+
+ ) +} - {/* Footer */} -
- Built because AI assistants shouldn't have amnesia about your code. -
-
-
+function TroubleshootItem({ problem, solutions }: { problem: string; solutions: string[] }) { + return ( +
+

{problem}

+
    + {solutions.map((solution, i) => ( +
  • + + {solution} +
  • + ))} +
+
) } diff --git a/frontend/src/pages/MCPToolsPage.tsx b/frontend/src/pages/MCPToolsPage.tsx new file mode 100644 index 0000000..f4e812e --- /dev/null +++ b/frontend/src/pages/MCPToolsPage.tsx @@ -0,0 +1,191 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'search-code', title: 'search_code', level: 2 }, + { id: 'list-repositories', title: 'list_repositories', level: 2 }, + { id: 'get-dependency-graph', title: 'get_dependency_graph', level: 2 }, + { id: 'analyze-impact', title: 'analyze_impact', level: 2 }, + { id: 'analyze-code-style', title: 'analyze_code_style', level: 2 }, + { id: 'get-repository-insights', title: 'get_repository_insights', level: 2 }, + { id: 'get-codebase-dna', title: 'get_codebase_dna', level: 2 }, +] + +export function MCPToolsPage() { + return ( + +
+
+ +
+

MCP Tools Reference

+

+ Complete reference for all MCP tools available to Claude and other AI assistants. +

+
+ +

+ When you connect OpenCodeIntel via MCP, your AI assistant gets access to these tools. + Each tool has specific parameters and returns structured data. +

+ + {/* search_code */} + + + {/* list_repositories */} + + + {/* get_dependency_graph */} + + + {/* analyze_impact */} + + + {/* analyze_code_style */} + + + {/* get_repository_insights */} + + + {/* get_codebase_dna */} + + + +
+ ) +} + +interface Parameter { + name: string + type: string + required: boolean + description: string +} + +interface ToolSectionProps { + id: string + name: string + description: string + parameters: Parameter[] + returns: string + example: string +} + +function ToolSection({ id, name, description, parameters, returns, example }: ToolSectionProps) { + return ( +
+

{name}

+

{description}

+ + {parameters.length > 0 && ( + <> +

Parameters

+
+ + + + + + + + + + + {parameters.map((param) => ( + + + + + + + ))} + +
NameTypeRequiredDescription
{param.name}{param.type}{param.required ? Yes : 'No'}{param.description}
+
+ + )} + + {parameters.length === 0 && ( +

No parameters required.

+ )} + +

Returns

+

{returns}

+ +

Example Prompt

+ {example} +
+ ) +} diff --git a/frontend/src/pages/QuickStartPage.tsx b/frontend/src/pages/QuickStartPage.tsx new file mode 100644 index 0000000..2687f87 --- /dev/null +++ b/frontend/src/pages/QuickStartPage.tsx @@ -0,0 +1,228 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPrerequisites, + DocsLearningObjectives, + DocsPagination, + TimeEstimate, + Step, + StepList, + TOCItem +} from '@/components/docs' +import { Zap } from 'lucide-react' + +const tocItems: TOCItem[] = [ + { id: 'hosted', title: 'Option 1: Hosted (Fastest)', level: 2 }, + { id: 'docker', title: 'Option 2: Docker', level: 2 }, + { id: 'manual', title: 'Option 3: Manual Setup', level: 2 }, + { id: 'next-steps', title: 'Next Steps', level: 2 }, +] + +export function QuickStartPage() { + return ( + + {/* Header */} +
+
+ +
+

Quick Start

+

+ Get OpenCodeIntel running and search your first codebase in under 5 minutes. +

+
+ + + +

+ Pick the setup that works for you. Hosted is fastest if you just want to try it out. + Docker is great for local development. Manual gives you the most control. +

+ + {/* Option 1: Hosted */} +

+ Option 1: Hosted (Fastest) +

+

+ Zero setup. Just sign in and start searching. +

+ + + +

+ Head to opencodeintel.com and + sign in with GitHub. +

+
+ + +

+ Click "Add Repository" and paste a GitHub URL. Public repos work immediately. + For private repos, you will need to connect your GitHub account. +

+
+ + +

+ Indexing takes 30 seconds to a few minutes depending on repo size. + You will see a progress bar. Grab a coffee if it is a big one. +

+
+ + +

+ Once indexed, type a query like "authentication logic" or "error handling" + and see the magic happen. +

+ + That is it. You can now search your codebase by meaning, not keywords. + +
+
+ + {/* Option 2: Docker */} +

+ Option 2: Docker +

+

+ Run everything locally with one command. Great for development or keeping data on your machine. +

+ + + + + + +{`git clone https://github.com/OpenCodeIntel/opencodeintel.git +cd opencodeintel`} + + + + + +{`cp .env.example .env`} + +

Edit .env and add your API keys:

+ +{`OPENAI_API_KEY=sk-... +PINECONE_API_KEY=... +PINECONE_INDEX_NAME=codeintel`} + +
+ + + +{`docker compose up -d`} + +

This starts the backend, frontend, and Redis. First run takes a few minutes to build.

+
+ + +

+ Go to localhost:3000 and + you will see the OpenCodeIntel dashboard. +

+ + Backend API runs on localhost:8000. API docs at localhost:8000/docs. + +
+
+ + {/* Option 3: Manual */} +

+ Option 3: Manual Setup +

+

+ Run backend and frontend separately. Best for active development. +

+ + + + + + +{`# macOS with Homebrew +brew install redis +brew services start redis + +# Or with Docker +docker run -d -p 6379:6379 redis:7-alpine`} + + + + + +{`cd backend +python -m venv venv +source venv/bin/activate # Windows: venv\\Scripts\\activate +pip install -r requirements.txt + +# Copy and configure environment +cp .env.example .env +# Edit .env with your API keys + +# Run the server +uvicorn main:app --reload`} + +

Backend will be running on localhost:8000.

+
+ + + +{`cd frontend +bun install +bun run dev`} + +

Frontend will be running on localhost:5173.

+
+
+ + {/* Next Steps */} +

+ Next Steps +

+

+ Now that you have OpenCodeIntel running, here is what to do next: +

+
    +
  • + + Connect to Claude: Set up MCP to give Claude access to your codebase. MCP Setup Guide +
  • +
  • + + Explore features: Learn about dependency graphs, impact analysis, and code style detection. Features +
  • +
  • + + Use the API: Build integrations with the REST API. API Reference +
  • +
+ + {/* Pagination */} + +
+ ) +} diff --git a/frontend/src/pages/api/APIAnalysisPage.tsx b/frontend/src/pages/api/APIAnalysisPage.tsx new file mode 100644 index 0000000..ecf35ea --- /dev/null +++ b/frontend/src/pages/api/APIAnalysisPage.tsx @@ -0,0 +1,235 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'dependencies', title: 'Dependency Graph', level: 2 }, + { id: 'impact', title: 'Impact Analysis', level: 2 }, + { id: 'style', title: 'Code Style', level: 2 }, + { id: 'insights', title: 'Repository Insights', level: 2 }, +] + +export function APIAnalysisPage() { + return ( + +
+
+ +
+

Analysis API

+

+ Dependency graphs, impact analysis, code style, and repository insights. +

+
+ + {/* Dependency Graph */} +

Dependency Graph

+ +
+ + GET + + /api/v1/repos/{'{repo_id}'}/dependencies +
+ +

+ Get the complete import/dependency graph for a repository. +

+ +

Response

+ +{`{ + "nodes": [ + { + "id": "src/auth/middleware.py", + "label": "middleware.py", + "directory": "src/auth", + "in_degree": 12, + "out_degree": 3, + "is_hub": true + } + ], + "edges": [ + { + "source": "src/api/routes.py", + "target": "src/auth/middleware.py", + "type": "import" + } + ], + "stats": { + "total_files": 47, + "total_edges": 89, + "hub_files": ["src/utils/index.ts"], + "leaf_files": ["src/config.py"], + "circular_dependencies": [] + } +}`} + + +

Example

+ +{`curl -H "Authorization: Bearer " \\ + http://localhost:8000/api/v1/repos/repo_abc123/dependencies`} + + + {/* Impact Analysis */} +

Impact Analysis

+ +
+ + POST + + /api/v1/repos/{'{repo_id}'}/impact +
+ +

+ Analyze the impact of changing a specific file. +

+ +

Request Body

+ +{`{ + "file_path": "src/auth/middleware.py" +}`} + + +

Response

+ +{`{ + "file": "src/auth/middleware.py", + "risk_level": "high", + "risk_score": 0.85, + "direct_dependents": [ + "src/api/routes.py", + "src/api/admin.py" + ], + "indirect_dependents": [ + "src/main.py" + ], + "related_tests": [ + "tests/test_auth.py" + ], + "test_coverage": { + "has_tests": true, + "test_count": 2 + }, + "recommendations": [ + "High-impact file with 5 dependents", + "Run tests before deploying changes" + ] +}`} + + +

Example

+ +{`curl -X POST \\ + -H "Authorization: Bearer " \\ + -H "Content-Type: application/json" \\ + -d '{"file_path": "src/auth/middleware.py"}' \\ + http://localhost:8000/api/v1/repos/repo_abc123/impact`} + + + {/* Code Style */} +

Code Style

+ +
+ + GET + + /api/v1/repos/{'{repo_id}'}/style-analysis +
+ +

+ Analyze coding conventions and patterns in the repository. +

+ +

Response

+ +{`{ + "naming": { + "variables": "snake_case", + "functions": "snake_case", + "classes": "PascalCase", + "confidence": 0.92 + }, + "patterns": { + "async_style": "async/await", + "error_handling": "try/except", + "imports": "absolute" + }, + "type_system": { + "type_hint_usage": 0.78, + "common_types": ["Optional", "List", "Dict"] + }, + "common_imports": [ + { "module": "fastapi", "count": 34 }, + { "module": "pydantic", "count": 28 } + ] +}`} + + +

Example

+ +{`curl -H "Authorization: Bearer " \\ + http://localhost:8000/api/v1/repos/repo_abc123/style-analysis`} + + + {/* Repository Insights */} +

Repository Insights

+ +
+ + GET + + /api/v1/repos/{'{repo_id}'}/insights +
+ +

+ Get high-level insights and metrics about a repository. +

+ +

Response

+ +{`{ + "overview": { + "total_files": 142, + "total_lines": 28500, + "languages": { + "TypeScript": 65, + "Python": 42, + "JavaScript": 35 + } + }, + "architecture": { + "pattern": "monolith with service layer", + "entry_points": ["src/main.py", "src/cli.py"], + "critical_files": [ + "src/core/engine.py", + "src/api/routes.py" + ] + }, + "health": { + "test_coverage_estimate": "partial", + "documentation_coverage": 0.45, + "circular_dependencies": 0 + } +}`} + + +

Example

+ +{`curl -H "Authorization: Bearer " \\ + http://localhost:8000/api/v1/repos/repo_abc123/insights`} + + + +
+ ) +} diff --git a/frontend/src/pages/api/APIOverviewPage.tsx b/frontend/src/pages/api/APIOverviewPage.tsx new file mode 100644 index 0000000..ee94b52 --- /dev/null +++ b/frontend/src/pages/api/APIOverviewPage.tsx @@ -0,0 +1,233 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' +import { Link } from 'react-router-dom' + +const tocItems: TOCItem[] = [ + { id: 'base-url', title: 'Base URL', level: 2 }, + { id: 'authentication', title: 'Authentication', level: 2 }, + { id: 'endpoints', title: 'Endpoints', level: 2 }, + { id: 'errors', title: 'Error Handling', level: 2 }, + { id: 'rate-limits', title: 'Rate Limits', level: 2 }, +] + +export function APIOverviewPage() { + return ( + +
+
+ +
+

API Reference

+

+ REST API for programmatic access to OpenCodeIntel features. +

+
+ +

+ The OpenCodeIntel API lets you integrate code intelligence into your own tools. + Search code, analyze dependencies, and predict impact programmatically. +

+ + + Interactive API docs available at http://localhost:8000/docs when running locally. + + +

Base URL

+ + +{`# Local development +http://localhost:8000/api/v1 + +# Hosted version +https://api.opencodeintel.com/api/v1`} + + +

+ The API is versioned. Current version is v1. +

+ +

Authentication

+ +

+ All API requests require an API key passed in the Authorization header: +

+ + +{`curl -H "Authorization: Bearer " \\ + http://localhost:8000/api/v1/repos`} + + +

+ Get your API key from the dashboard under Settings → API Keys. +

+ +

Endpoints

+ +
+ + + + + + + +
+ +

Error Handling

+ +

+ The API returns standard HTTP status codes. Errors include a JSON body with details: +

+ + +{`{ + "error": "not_found", + "message": "Repository not found", + "status": 404 +}`} + + +

Common Status Codes

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeMeaning
200Success
201Created
400Bad request - check your parameters
401Unauthorized - invalid or missing API key
404Not found - resource doesn't exist
429Rate limited - slow down
500Server error - try again or contact support
+
+ +

Rate Limits

+ +

+ API requests are rate limited to ensure fair usage: +

+ +
    +
  • + + Search: 100 requests/minute +
  • +
  • + + Analysis: 60 requests/minute +
  • +
  • + + Indexing: 10 repositories/hour +
  • +
+ +

+ Rate limit info is included in response headers: +

+ + +{`X-RateLimit-Limit: 100 +X-RateLimit-Remaining: 95 +X-RateLimit-Reset: 1620000000`} + + + +
+ ) +} + +function EndpointLink({ method, path, description, href }: { method: string; path: string; description: string; href: string }) { + const methodColors: Record = { + GET: 'text-green-400 bg-green-500/10', + POST: 'text-blue-400 bg-blue-500/10', + PUT: 'text-amber-400 bg-amber-500/10', + DELETE: 'text-red-400 bg-red-500/10', + } + + return ( + + + {method} + + {path} + {description} + + ) +} diff --git a/frontend/src/pages/api/APIRepositoriesPage.tsx b/frontend/src/pages/api/APIRepositoriesPage.tsx new file mode 100644 index 0000000..af12b17 --- /dev/null +++ b/frontend/src/pages/api/APIRepositoriesPage.tsx @@ -0,0 +1,224 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'list', title: 'List Repositories', level: 2 }, + { id: 'get', title: 'Get Repository', level: 2 }, + { id: 'create', title: 'Add Repository', level: 2 }, + { id: 'delete', title: 'Delete Repository', level: 2 }, + { id: 'reindex', title: 'Reindex Repository', level: 2 }, +] + +export function APIRepositoriesPage() { + return ( + +
+
+ +
+

Repositories API

+

+ Manage repositories: list, add, delete, and reindex. +

+
+ + {/* List Repositories */} +

List Repositories

+ + +

Returns all repositories accessible to your account.

+ +

Response

+ +{`{ + "repositories": [ + { + "id": "repo_abc123", + "name": "my-project", + "url": "https://github.com/user/my-project", + "status": "indexed", + "file_count": 142, + "last_indexed": "2024-01-15T10:30:00Z", + "created_at": "2024-01-10T08:00:00Z" + } + ], + "total": 1 +}`} + + +

Example

+ +{`curl -H "Authorization: Bearer " \\ + http://localhost:8000/api/v1/repos`} + + + {/* Get Repository */} +

Get Repository

+ + +

Get details for a specific repository.

+ +

Parameters

+ + +

Response

+ +{`{ + "id": "repo_abc123", + "name": "my-project", + "url": "https://github.com/user/my-project", + "status": "indexed", + "file_count": 142, + "languages": { + "TypeScript": 65, + "Python": 42, + "JavaScript": 35 + }, + "last_indexed": "2024-01-15T10:30:00Z", + "indexing_duration_ms": 45000, + "created_at": "2024-01-10T08:00:00Z" +}`} + + + {/* Add Repository */} +

Add Repository

+ + +

Add a new repository for indexing.

+ +

Request Body

+ +{`{ + "url": "https://github.com/user/my-project", + "branch": "main" +}`} + +

The branch field is optional and defaults to the repository's default branch.

+ +

Response

+ +{`{ + "id": "repo_abc123", + "name": "my-project", + "status": "indexing", + "message": "Repository added. Indexing in progress." +}`} + + +

Example

+ +{`curl -X POST \\ + -H "Authorization: Bearer " \\ + -H "Content-Type: application/json" \\ + -d '{"url": "https://github.com/user/my-project"}' \\ + http://localhost:8000/api/v1/repos`} + + + {/* Delete Repository */} +

Delete Repository

+ + +

Remove a repository and all its indexed data.

+ +

Parameters

+ + +

Response

+ +{`{ + "message": "Repository deleted successfully" +}`} + + + {/* Reindex Repository */} +

Reindex Repository

+ + +

Trigger a fresh index of the repository. Use after major code changes.

+ +

Parameters

+ + +

Response

+ +{`{ + "id": "repo_abc123", + "status": "indexing", + "message": "Reindexing started" +}`} + + + +
+ ) +} + +function EndpointHeader({ method, path }: { method: string; path: string }) { + const methodColors: Record = { + GET: 'text-green-400 bg-green-500/10', + POST: 'text-blue-400 bg-blue-500/10', + PUT: 'text-amber-400 bg-amber-500/10', + DELETE: 'text-red-400 bg-red-500/10', + } + + return ( +
+ + {method} + + {path} +
+ ) +} + +interface Param { + name: string + location: string + type: string + required: boolean + description: string +} + +function ParamTable({ params }: { params: Param[] }) { + return ( +
+ + + + + + + + + + + + {params.map((param) => ( + + + + + + + + ))} + +
NameLocationTypeRequiredDescription
{param.name}{param.location}{param.type}{param.required ? Yes : 'No'}{param.description}
+
+ ) +} diff --git a/frontend/src/pages/api/APISearchPage.tsx b/frontend/src/pages/api/APISearchPage.tsx new file mode 100644 index 0000000..e720806 --- /dev/null +++ b/frontend/src/pages/api/APISearchPage.tsx @@ -0,0 +1,195 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'search', title: 'Semantic Search', level: 2 }, + { id: 'parameters', title: 'Parameters', level: 2 }, + { id: 'response', title: 'Response', level: 2 }, + { id: 'examples', title: 'Examples', level: 2 }, +] + +export function APISearchPage() { + return ( + +
+
+ +
+

Search API

+

+ Semantic code search across indexed repositories. +

+
+ + + +
+ + POST + + /api/v1/search +
+ +

+ Search code by meaning, not just keywords. The query is converted to an embedding + and matched against indexed code chunks. +

+ +

Parameters

+ +

Request Body

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDescription
querystringYesNatural language search query
repo_idstringYesRepository to search in
max_resultsintegerNoMaximum results to return (default: 10, max: 50)
min_scorefloatNoMinimum relevance score 0-1 (default: 0.5)
file_filterstringNoGlob pattern to filter files (e.g., "*.py", "src/**")
+
+ + +{`{ + "query": "authentication middleware", + "repo_id": "repo_abc123", + "max_results": 10, + "min_score": 0.6, + "file_filter": "src/**/*.ts" +}`} + + +

Response

+ + +{`{ + "results": [ + { + "file_path": "src/middleware/auth.ts", + "content": "export async function verifyToken(req, res, next) {\\n const token = req.headers.authorization;\\n // ... verification logic\\n}", + "start_line": 15, + "end_line": 42, + "score": 0.89, + "summary": "Token verification middleware that validates JWT tokens and attaches user to request", + "language": "typescript" + }, + { + "file_path": "src/utils/jwt.ts", + "content": "export function decodeToken(token: string) { ... }", + "start_line": 1, + "end_line": 20, + "score": 0.75, + "summary": "JWT utility functions for encoding and decoding tokens", + "language": "typescript" + } + ], + "total": 2, + "query": "authentication middleware", + "search_time_ms": 145 +}`} + + +

Result Fields

+ +
    +
  • + file_path + - Path to the file containing the match +
  • +
  • + content + - The matching code chunk +
  • +
  • + start_line / end_line + - Line numbers in the original file +
  • +
  • + score + - Relevance score from 0 to 1 +
  • +
  • + summary + - AI-generated description of what the code does +
  • +
+ +

Examples

+ +

Basic Search

+ +{`curl -X POST \\ + -H "Authorization: Bearer " \\ + -H "Content-Type: application/json" \\ + -d '{"query": "error handling", "repo_id": "repo_abc123"}' \\ + http://localhost:8000/api/v1/search`} + + +

Search with Filters

+ +{`curl -X POST \\ + -H "Authorization: Bearer " \\ + -H "Content-Type: application/json" \\ + -d '{ + "query": "database connection pool", + "repo_id": "repo_abc123", + "max_results": 5, + "min_score": 0.7, + "file_filter": "**/*.py" + }' \\ + http://localhost:8000/api/v1/search`} + + + + For best results, use natural language queries that describe what the code does, + not exact function names. "handles user login" works better than "handleLogin". + + + +
+ ) +} diff --git a/frontend/src/pages/api/index.ts b/frontend/src/pages/api/index.ts new file mode 100644 index 0000000..53b02ea --- /dev/null +++ b/frontend/src/pages/api/index.ts @@ -0,0 +1,4 @@ +export { APIOverviewPage } from './APIOverviewPage' +export { APIRepositoriesPage } from './APIRepositoriesPage' +export { APISearchPage } from './APISearchPage' +export { APIAnalysisPage } from './APIAnalysisPage' diff --git a/frontend/src/pages/deployment/DockerSetupPage.tsx b/frontend/src/pages/deployment/DockerSetupPage.tsx new file mode 100644 index 0000000..0558c3c --- /dev/null +++ b/frontend/src/pages/deployment/DockerSetupPage.tsx @@ -0,0 +1,242 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPrerequisites, + DocsPagination, + TimeEstimate, + Step, + StepList, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'prerequisites', title: 'Prerequisites', level: 2 }, + { id: 'quick-start', title: 'Quick Start', level: 2 }, + { id: 'configuration', title: 'Configuration', level: 2 }, + { id: 'services', title: 'Services', level: 2 }, + { id: 'commands', title: 'Useful Commands', level: 2 }, + { id: 'troubleshooting', title: 'Troubleshooting', level: 2 }, +] + +export function DockerSetupPage() { + return ( + +
+
+ +
+

Docker Setup

+

+ Run the entire OpenCodeIntel stack locally with Docker Compose. +

+
+ +

+ Docker is the fastest way to run OpenCodeIntel locally. One command spins up the + backend, frontend, and Redis. No Python versions to manage, no Node versions to worry about. +

+ +

Prerequisites

+ + + +

Quick Start

+ + + + +{`git clone https://github.com/OpenCodeIntel/opencodeintel.git +cd opencodeintel`} + + + + + +{`cp .env.example .env`} + +

Edit .env with your API keys:

+ +{`# Required +OPENAI_API_KEY=sk-... +PINECONE_API_KEY=... +PINECONE_INDEX_NAME=codeintel + +# Optional - for GitHub OAuth +GITHUB_CLIENT_ID=... +GITHUB_CLIENT_SECRET=... + +# Optional - for Supabase auth +SUPABASE_URL=... +SUPABASE_ANON_KEY=...`} + +
+ + + +{`docker compose up -d`} + +

First run downloads images and builds containers. Takes 2-5 minutes.

+
+ + + +{`docker compose ps`} + +

You should see three containers running:

+
    +
  • codeintel-frontend - Port 3000
  • +
  • codeintel-backend - Port 8000
  • +
  • codeintel-redis - Port 6379
  • +
+
+ + +

Go to localhost:3000

+ + You are running OpenCodeIntel locally! + +
+
+ +

Configuration

+ +

Environment Variables

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VariableRequiredDescription
OPENAI_API_KEYYesFor embeddings and code summaries
PINECONE_API_KEYYesVector database for semantic search
PINECONE_INDEX_NAMEYesName of your Pinecone index
REDIS_URLNoDefaults to redis://redis:6379
GITHUB_CLIENT_IDNoFor GitHub OAuth login
GITHUB_CLIENT_SECRETNoFor GitHub OAuth login
+
+ +

Services

+ +
+ + + +
+ +

Useful Commands

+ + +{`# Start all services +docker compose up -d + +# Stop all services +docker compose down + +# View logs +docker compose logs -f + +# View logs for specific service +docker compose logs -f backend + +# Rebuild after code changes +docker compose up -d --build + +# Reset everything (including volumes) +docker compose down -v`} + + +

Troubleshooting

+ +

Container won't start

+

+ Check logs: docker compose logs backend +

+

+ Common causes: missing .env file, invalid API keys, port already in use. +

+ +

Port already in use

+

+ Something else is using port 3000 or 8000. Either stop that service or change ports in docker-compose.yml. +

+ +

Out of memory

+

+ Increase Docker Desktop memory allocation in Settings → Resources. Recommend 4GB+. +

+ + +
+ ) +} + +function ServiceCard({ name, port, description, healthCheck }: { name: string; port: string; description: string; healthCheck: string }) { + return ( +
+
+

{name}

+ :{port} +
+

{description}

+

Health check: {healthCheck}

+
+ ) +} diff --git a/frontend/src/pages/deployment/SelfHostingPage.tsx b/frontend/src/pages/deployment/SelfHostingPage.tsx new file mode 100644 index 0000000..9a4bdcc --- /dev/null +++ b/frontend/src/pages/deployment/SelfHostingPage.tsx @@ -0,0 +1,223 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'options', title: 'Deployment Options', level: 2 }, + { id: 'railway', title: 'Railway', level: 2 }, + { id: 'fly', title: 'Fly.io', level: 2 }, + { id: 'vps', title: 'VPS / Bare Metal', level: 2 }, + { id: 'production', title: 'Production Checklist', level: 2 }, +] + +export function SelfHostingPage() { + return ( + +
+
+ +
+

Self-Hosting

+

+ Deploy OpenCodeIntel on your own infrastructure for full control and data privacy. +

+
+ +

+ Self-hosting gives you complete control over your data. Your code never leaves your infrastructure. + Choose from several deployment options depending on your needs. +

+ +

Deployment Options

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionBest ForEffortCost
RailwayQuick setup, small teamsLow~$20/mo
Fly.ioGlobal distribution, scalingMedium~$15/mo
VPSFull control, existing infraHigher~$10/mo
+
+ +

Railway

+

+ Railway is the easiest way to deploy. Connect your repo and it handles the rest. +

+ + +{`# 1. Install Railway CLI +npm install -g @railway/cli + +# 2. Login +railway login + +# 3. Initialize project +cd opencodeintel +railway init + +# 4. Add Redis +railway add --plugin redis + +# 5. Set environment variables +railway variables set OPENAI_API_KEY=sk-... +railway variables set PINECONE_API_KEY=... +railway variables set PINECONE_INDEX_NAME=codeintel + +# 6. Deploy +railway up`} + + + + Railway auto-detects Dockerfile and docker-compose.yml. It will deploy all services automatically. + + +

Fly.io

+

+ Fly.io is great for global distribution. Deploy close to your users. +

+ + +{`# 1. Install Fly CLI +curl -L https://fly.io/install.sh | sh + +# 2. Login +fly auth login + +# 3. Create apps +cd backend +fly launch --name codeintel-backend + +cd ../frontend +fly launch --name codeintel-frontend + +# 4. Set secrets +fly secrets set OPENAI_API_KEY=sk-... -a codeintel-backend +fly secrets set PINECONE_API_KEY=... -a codeintel-backend + +# 5. Create Redis (Upstash recommended) +# Or use Fly's built-in Redis: fly redis create + +# 6. Deploy +fly deploy -a codeintel-backend +fly deploy -a codeintel-frontend`} + + +

VPS / Bare Metal

+

+ For full control, deploy on any Linux server with Docker. +

+ + +{`# 1. SSH into your server +ssh user@your-server.com + +# 2. Install Docker +curl -fsSL https://get.docker.com | sh + +# 3. Clone repo +git clone https://github.com/OpenCodeIntel/opencodeintel.git +cd opencodeintel + +# 4. Configure +cp .env.example .env +nano .env # Add your API keys + +# 5. Start with production config +docker compose -f docker-compose.prod.yml up -d + +# 6. Set up reverse proxy (nginx/caddy) +# Point your domain to the server`} + + +

Nginx Config

+ +{`server { + listen 80; + server_name codeintel.yourdomain.com; + + location / { + proxy_pass http://localhost:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + } + + location /api { + proxy_pass http://localhost:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } +}`} + + +

Production Checklist

+ +
    +
  • + + HTTPS: Use SSL/TLS. Let's Encrypt is free. +
  • +
  • + + Backups: Back up your Pinecone index and any persistent data. +
  • +
  • + + Monitoring: Set up health checks and alerts. +
  • +
  • + + Rate Limiting: Protect your API endpoints. +
  • +
  • + + Auth: Configure proper authentication if exposing publicly. +
  • +
  • + + Secrets: Never commit API keys. Use environment variables or secret managers. +
  • +
+ + + If you are exposing OpenCodeIntel to the internet, make sure authentication is properly configured. + The default setup is for local development and does not enforce auth on all endpoints. + + + +
+ ) +} diff --git a/frontend/src/pages/deployment/index.ts b/frontend/src/pages/deployment/index.ts new file mode 100644 index 0000000..7100b0e --- /dev/null +++ b/frontend/src/pages/deployment/index.ts @@ -0,0 +1,2 @@ +export { DockerSetupPage } from './DockerSetupPage' +export { SelfHostingPage } from './SelfHostingPage' diff --git a/frontend/src/pages/features/CodeStyleAnalysisPage.tsx b/frontend/src/pages/features/CodeStyleAnalysisPage.tsx new file mode 100644 index 0000000..e2cccfb --- /dev/null +++ b/frontend/src/pages/features/CodeStyleAnalysisPage.tsx @@ -0,0 +1,174 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'what-it-analyzes', title: 'What It Analyzes', level: 2 }, + { id: 'usage', title: 'Usage', level: 2 }, + { id: 'api-response', title: 'API Response', level: 2 }, + { id: 'use-cases', title: 'Use Cases', level: 2 }, +] + +export function CodeStyleAnalysisPage() { + return ( + +
+
+ +
+

Code Style Analysis

+

+ Understand team conventions. Write code that fits in with existing patterns. +

+
+ +

+ Every codebase has conventions. Some use snake_case, others camelCase. Some teams + love type hints, others hate them. Code style analysis extracts these patterns + so you (or your AI assistant) can write code that fits in. +

+ +

What It Analyzes

+ +
+ + + + +
+ +

Usage

+ +

Via MCP (Claude Desktop)

+ +{`"What coding conventions does this repo use?" +"Is this codebase using snake_case or camelCase?" +"How should I structure a new module to fit this codebase?" +"What testing patterns does this team follow?"`} + + +

Via API

+ +{`curl "http://localhost:8000/api/v1/repos/{repo_id}/style-analysis"`} + + +

API Response

+ + +{`{ + "naming": { + "variables": "snake_case", + "functions": "snake_case", + "classes": "PascalCase", + "files": "snake_case", + "confidence": 0.92 + }, + "patterns": { + "async_style": "async/await", + "error_handling": "try/except with custom exceptions", + "imports": "absolute with __init__.py exports", + "structure": "class-based services with dependency injection" + }, + "type_system": { + "type_hint_usage": 0.78, + "strict_mode": false, + "common_types": ["Optional", "List", "Dict", "Union"] + }, + "common_imports": [ + { "module": "fastapi", "count": 34 }, + { "module": "pydantic", "count": 28 }, + { "module": "sqlalchemy", "count": 22 }, + { "module": "pytest", "count": 18 } + ], + "recommendations": [ + "Use snake_case for variables and functions", + "Add type hints - team uses them 78% of the time", + "Follow existing pattern: services as classes, utilities as functions" + ] +}`} + + +

Use Cases

+ +

Onboarding

+

+ New to a codebase? Run style analysis to instantly learn the conventions. + No more guessing if you should use tabs or spaces. +

+ +

AI Code Generation

+

+ When Claude generates code, it can use style analysis to match your team's patterns. + The generated code looks like your team wrote it. +

+ +

Code Review

+

+ Check if a PR follows team conventions. "This uses camelCase but we use snake_case." +

+ + + Style analysis works best on codebases with consistent patterns. If your codebase + is a mix of styles (happens during migrations), the confidence scores will be lower. + + + +
+ ) +} + +function StyleCategory({ title, items }: { title: string; items: string[] }) { + return ( +
+

{title}

+
    + {items.map((item, i) => ( +
  • + + {item} +
  • + ))} +
+
+ ) +} diff --git a/frontend/src/pages/features/DependencyAnalysisPage.tsx b/frontend/src/pages/features/DependencyAnalysisPage.tsx new file mode 100644 index 0000000..c571a61 --- /dev/null +++ b/frontend/src/pages/features/DependencyAnalysisPage.tsx @@ -0,0 +1,176 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'what-it-shows', title: 'What It Shows', level: 2 }, + { id: 'usage', title: 'Usage', level: 2 }, + { id: 'reading-the-graph', title: 'Reading the Graph', level: 2 }, + { id: 'api-response', title: 'API Response', level: 2 }, +] + +export function DependencyAnalysisPage() { + return ( + +
+
+ +
+

Dependency Analysis

+

+ Visualize your architecture. See what connects to what and identify the critical files. +

+
+ +

+ Every codebase has hidden structure. Some files are imported everywhere (hub files). + Some import everything (aggregator files). Some are isolated. Understanding this + structure helps you make better decisions about where to make changes. +

+ +

What It Shows

+ +

+ The dependency graph gives you: +

+ +
    +
  • + +
    + Import relationships: +

    Which files import which. Follow the arrows to trace data flow.

    +
    +
  • +
  • + +
    + Hub files: +

    Files with many dependents. Change these carefully - lots of things depend on them.

    +
    +
  • +
  • + +
    + Leaf files: +

    Files with no dependents. Safe to modify - nothing else uses them.

    +
    +
  • +
  • + +
    + Circular dependencies: +

    A imports B, B imports A. Usually a sign of tangled architecture.

    +
    +
  • +
  • + +
    + Directory clusters: +

    Files grouped by folder show natural module boundaries.

    +
    +
  • +
+ +

Usage

+ +

Via MCP (Claude Desktop)

+ +{`"Show me the dependency graph for this repo" +"What files depend on auth/middleware.py?" +"Find the most connected files in the codebase"`} + + +

Via API

+ +{`curl "http://localhost:8000/api/v1/repos/{repo_id}/dependencies"`} + + +

Via Dashboard

+

+ Click on any repository and navigate to the "Dependencies" tab. You will see an interactive + graph you can zoom, pan, and click on nodes to explore. +

+ +

Reading the Graph

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VisualMeaning
Large nodeMany files depend on this one (high in-degree)
Many outgoing arrowsThis file imports many others (high out-degree)
Cluster of nodesFiles in same directory or tightly coupled
Isolated nodeStandalone file with no dependencies
Bidirectional arrowCircular dependency (A→B and B→A)
+
+ + + The graph uses force-directed layout. Related files naturally cluster together. + If you see two clusters far apart, they are probably independent modules. + + +

API Response

+ +

The API returns nodes and edges:

+ + +{`{ + "nodes": [ + { + "id": "src/auth/middleware.py", + "label": "middleware.py", + "directory": "src/auth", + "in_degree": 12, + "out_degree": 3 + } + ], + "edges": [ + { + "source": "src/api/routes.py", + "target": "src/auth/middleware.py" + } + ], + "stats": { + "total_files": 47, + "total_edges": 89, + "hub_files": ["src/utils/index.ts", "src/auth/middleware.py"], + "leaf_files": ["src/config.py", "src/constants.ts"] + } +}`} + + + +
+ ) +} diff --git a/frontend/src/pages/features/ImpactPredictionPage.tsx b/frontend/src/pages/features/ImpactPredictionPage.tsx new file mode 100644 index 0000000..d05e698 --- /dev/null +++ b/frontend/src/pages/features/ImpactPredictionPage.tsx @@ -0,0 +1,189 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'why-it-matters', title: 'Why It Matters', level: 2 }, + { id: 'what-you-get', title: 'What You Get', level: 2 }, + { id: 'usage', title: 'Usage', level: 2 }, + { id: 'api-response', title: 'API Response', level: 2 }, +] + +export function ImpactPredictionPage() { + return ( + +
+
+ +
+

Impact Prediction

+

+ Know what breaks before you change it. See the blast radius of any modification. +

+
+ +

+ You are about to refactor UserService.ts. + Will anything break? Impact prediction tells you exactly which files depend on it, + which tests cover it, and how risky the change is. +

+ +

Why It Matters

+ +

+ Without impact analysis, you are guessing. You make a change, run the tests, + and hope nothing breaks in production. With impact analysis: +

+ +
    +
  • + + Know exactly which files will be affected +
  • +
  • + + See if tests exist for the affected code +
  • +
  • + + Get a risk score to help prioritize review +
  • +
  • + + Find blind spots where test coverage is missing +
  • +
+ +

What You Get

+ +
+ + + + +
+ + + If a file has 10+ dependents and no tests, think twice before making big changes. + Consider adding tests first, or breaking the change into smaller pieces. + + +

Usage

+ +

Via MCP (Claude Desktop)

+ +{`"What is the impact of changing src/auth/middleware.py?" +"What happens if I modify the UserService?" +"Show me the blast radius of database.ts"`} + + +

Via API

+ +{`curl -X POST \\ + -H "Content-Type: application/json" \\ + -d '{"file_path": "src/auth/middleware.py"}' \\ + http://localhost:8000/api/v1/repos/{repo_id}/impact`} + + +

Via Dashboard

+

+ Click any file in the dependency graph and select "Analyze Impact" from the context menu. +

+ +

API Response

+ + +{`{ + "file": "src/auth/middleware.py", + "risk_level": "high", + "risk_score": 0.85, + "direct_dependents": [ + "src/api/routes.py", + "src/api/admin.py", + "src/api/webhooks.py" + ], + "indirect_dependents": [ + "src/main.py", + "src/server.py" + ], + "related_tests": [ + "tests/test_auth.py", + "tests/integration/test_api.py" + ], + "test_coverage": { + "has_tests": true, + "test_count": 2, + "coverage_estimate": "partial" + }, + "recommendations": [ + "High-impact file with 5 dependents", + "Consider adding more test coverage before major changes", + "Related tests found - run these after modification" + ] +}`} + + +

Risk Levels

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
LevelScoreMeaning
Low0.0 - 0.3Few or no dependents. Safe to modify.
Medium0.3 - 0.7Some dependents. Test after changes.
High0.7 - 1.0Many dependents or missing tests. Proceed carefully.
+
+ + +
+ ) +} + +function ImpactSection({ title, description }: { title: string; description: string }) { + return ( +
+

{title}

+

{description}

+
+ ) +} diff --git a/frontend/src/pages/features/SemanticSearchPage.tsx b/frontend/src/pages/features/SemanticSearchPage.tsx new file mode 100644 index 0000000..a82abaf --- /dev/null +++ b/frontend/src/pages/features/SemanticSearchPage.tsx @@ -0,0 +1,164 @@ +import { + DocsLayout, + DocsCodeBlock, + DocsCallout, + DocsPagination, + TimeEstimate, + TOCItem +} from '@/components/docs' + +const tocItems: TOCItem[] = [ + { id: 'how-it-works', title: 'How It Works', level: 2 }, + { id: 'usage', title: 'Usage', level: 2 }, + { id: 'examples', title: 'Examples', level: 2 }, + { id: 'tips', title: 'Tips for Better Results', level: 2 }, +] + +export function SemanticSearchPage() { + return ( + +
+
+ +
+

Semantic Search

+

+ Find code by meaning, not keywords. Search for "error handling" and find processFailure(). +

+
+ +

+ Traditional code search is like using Ctrl+F - you find exactly what you type, nothing more. + Semantic search understands what you mean. Ask for "authentication middleware" and it finds + verifyToken(), + checkAuth(), and + requireLogin(). +

+ +

How It Works

+ +

+ When you index a repository, OpenCodeIntel: +

+
    +
  1. + 1. + Parses your code using Tree-sitter to understand structure (functions, classes, imports) +
  2. +
  3. + 2. + Generates summaries using GPT-4o-mini to describe what each chunk does in plain English +
  4. +
  5. + 3. + Creates embeddings using OpenAI's embedding model and stores them in Pinecone +
  6. +
+

+ When you search, your query gets embedded and matched against the stored vectors. + The result? Code that means what you are looking for, even if it does not contain your exact words. +

+ +

Usage

+ +

Via MCP (Claude Desktop)

+

Just ask Claude naturally:

+ +{`"Find the authentication logic in this codebase" +"Where is error handling implemented?" +"Show me the database connection setup"`} + + +

Via API

+ +{`curl -X POST "http://localhost:8000/api/v1/search" \\ + -H "Content-Type: application/json" \\ + -d '{ + "repo_id": "your-repo-id", + "query": "authentication middleware", + "max_results": 10 + }'`} + + +

Via Dashboard

+

+ Use the search bar in the dashboard. Results show file path, code snippet, and relevance score. +

+ +

Examples

+ +
+ + + +
+ +

Tips for Better Results

+ +
    +
  • + + Be descriptive: "user authentication flow" works better than just "auth" +
  • +
  • + + Use domain language: Search in the terms your codebase uses +
  • +
  • + + Ask questions: "How does the app handle failed payments?" works great +
  • +
  • + + Avoid exact syntax: Do not search for "function handleAuth(" - describe what it does instead +
  • +
+ + + If you are not finding what you expect, try rephrasing. "Error handling" and "exception management" + might return different results depending on how your code is written. + + + +
+ ) +} + +function SearchExample({ query, finds }: { query: string; finds: string[] }) { + return ( +
+

Query:

+

"{query}"

+

Finds:

+
    + {finds.map((find, i) => ( +
  • {find}
  • + ))} +
+
+ ) +} diff --git a/frontend/src/pages/features/index.ts b/frontend/src/pages/features/index.ts new file mode 100644 index 0000000..2137d66 --- /dev/null +++ b/frontend/src/pages/features/index.ts @@ -0,0 +1,4 @@ +export { SemanticSearchPage } from './SemanticSearchPage' +export { DependencyAnalysisPage } from './DependencyAnalysisPage' +export { ImpactPredictionPage } from './ImpactPredictionPage' +export { CodeStyleAnalysisPage } from './CodeStyleAnalysisPage'