diff --git a/.env.example b/.env.example index dc00d2e6c3..4077e9cd22 100644 --- a/.env.example +++ b/.env.example @@ -42,6 +42,12 @@ ARCHON_DOCS_PORT=3838 # If not set, defaults to localhost, 127.0.0.1, ::1, and the HOST value above VITE_ALLOWED_HOSTS= +# Development Tools +# VITE_SHOW_DEVTOOLS: Show TanStack Query DevTools (for developers only) +# Set to "true" to enable the DevTools panel in bottom right corner +# Defaults to "false" for end users +VITE_SHOW_DEVTOOLS=false + # When enabled, PROD mode will proxy ARCHON_SERVER_PORT through ARCHON_UI_PORT. This exposes both the # Archon UI and API through a single port. This is useful when deploying Archon behind a reverse # proxy where you want to expose the frontend on a single external domain. diff --git a/.gitignore b/.gitignore index 5145d4dc35..bf0230adf9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ __pycache__ PRPs/local PRPs/completed/ /logs/ +.zed diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..3c0928c168 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,388 @@ +# AGENTS.md + +## Alpha Development Guidelines + +**Local-only deployment** - each user runs their own instance. + +### Core Principles + +- **No backwards compatibility** - remove deprecated code immediately +- **Detailed errors over graceful failures** - we want to identify and fix issues fast +- **Break things to improve them** - alpha is for rapid iteration + +### Error Handling + +**Core Principle**: In alpha, we need to intelligently decide when to fail hard and fast to quickly address issues, and when to allow processes to complete in critical services despite failures. Read below carefully and make intelligent decisions on a case-by-case basis. + +#### When to Fail Fast and Loud (Let it Crash!) + +These errors should stop execution and bubble up immediately: (except for crawling flows) + +- **Service startup failures** - If credentials, database, or any service can't initialize, the system should crash with a clear error +- **Missing configuration** - Missing environment variables or invalid settings should stop the system +- **Database connection failures** - Don't hide connection issues, expose them +- **Authentication/authorization failures** - Security errors must be visible and halt the operation +- **Data corruption or validation errors** - Never silently accept bad data, Pydantic should raise +- **Critical dependencies unavailable** - If a required service is down, fail immediately +- **Invalid data that would corrupt state** - Never store zero embeddings, null foreign keys, or malformed JSON + +#### When to Complete but Log Detailed Errors + +These operations should continue but track and report failures clearly: + +- **Batch processing** - When crawling websites or processing documents, complete what you can and report detailed failures for each item +- **Background tasks** - Embedding generation, async jobs should finish the queue but log failures +- **WebSocket events** - Don't crash on a single event failure, log it and continue serving other clients +- **Optional features** - If projects/tasks are disabled, log and skip rather than crash +- **External API calls** - Retry with exponential backoff, then fail with a clear message about what service failed and why + +#### Critical Nuance: Never Accept Corrupted Data + +When a process should continue despite failures, it must **skip the failed item entirely** rather than storing corrupted data: + +**❌ WRONG - Silent Corruption:** + +```python +try: + embedding = create_embedding(text) +except Exception as e: + embedding = [0.0] * 1536 # NEVER DO THIS - corrupts database + store_document(doc, embedding) +``` + +**✅ CORRECT - Skip Failed Items:** + +```python +try: + embedding = create_embedding(text) + store_document(doc, embedding) # Only store on success +except Exception as e: + failed_items.append({'doc': doc, 'error': str(e)}) + logger.error(f"Skipping document {doc.id}: {e}") + # Continue with next document, don't store anything +``` + +**✅ CORRECT - Batch Processing with Failure Tracking:** + +```python +def process_batch(items): + results = {'succeeded': [], 'failed': []} + + for item in items: + try: + result = process_item(item) + results['succeeded'].append(result) + except Exception as e: + results['failed'].append({ + 'item': item, + 'error': str(e), + 'traceback': traceback.format_exc() + }) + logger.error(f"Failed to process {item.id}: {e}") + + # Always return both successes and failures + return results +``` + +#### Error Message Guidelines + +- Include context about what was being attempted when the error occurred +- Preserve full stack traces with `exc_info=True` in Python logging +- Use specific exception types, not generic Exception catching +- Include relevant IDs, URLs, or data that helps debug the issue +- Never return None/null to indicate failure - raise an exception with details +- For batch operations, always report both success count and detailed failure list + +### Code Quality + +- Remove dead code immediately rather than maintaining it - no backward compatibility or legacy functions +- Prioritize functionality over production-ready patterns +- Focus on user experience and feature completeness +- When updating code, don't reference what is changing (avoid keywords like LEGACY, CHANGED, REMOVED), instead focus on comments that document just the functionality of the code + +## Architecture Overview + +Archon V2 Alpha is a microservices-based knowledge management system with MCP (Model Context Protocol) integration: + +- **Frontend (port 3737)**: React + TypeScript + Vite + TailwindCSS + - **UI Strategy**: Radix UI primitives in `/features`, custom components in legacy `/components` + - **State Management**: TanStack Query for all data fetching in `/features` + - **Styling**: Tron-inspired glassmorphism with Tailwind CSS +- **Main Server (port 8181)**: FastAPI with HTTP polling for updates +- **MCP Server (port 8051)**: Lightweight HTTP-based MCP protocol server +- **Agents Service (port 8052)**: PydanticAI agents for AI/ML operations +- **Database**: Supabase (PostgreSQL + pgvector for embeddings) + +## Development Commands + +### Frontend (archon-ui-main/) + +```bash +npm run dev # Start development server on port 3737 +npm run build # Build for production +npm run lint # Run ESLint +npm run test # Run Vitest tests +npm run test:coverage # Run tests with coverage report +``` + +# Biome Linter Guide for AI Assistants + +## Overview + +This project uses Biome for linting and formatting the `/src/features` directory. Biome provides fast, machine-readable feedback that AI assistants can use to improve code quality. + +## Configuration + +Biome is configured in `biome.json`: + +- **Scope**: Only checks `/src/features/**` directory +- **Formatting**: 2 spaces, 80 char line width +- **Linting**: Recommended rules enabled +- **Import Organization**: Automatically sorts and groups imports + +## AI Assistant Workflow in the new /features directory + +1. **Check Issues**: Run `npm run biome:ai` to get JSON output +2. **Parse Output**: Extract error locations and types +3. **Apply Fixes**: + - Run `npm run biome:ai-fix` for auto-fixable issues + - Manually fix remaining issues based on patterns above +4. **Verify**: Run `npm run biome:ai` again to confirm fixes + +## JSON Output Format + +When using `biome:ai`, the output is structured JSON: + +```json +{ + "diagnostics": [ + { + "file": "path/to/file.tsx", + "line": 10, + "column": 5, + "severity": "error", + "message": "Description of the issue", + "rule": "lint/a11y/useButtonType" + } + ] +} +``` + +### Backend (python/) + +```bash +# Using uv package manager +uv sync # Install/update dependencies +uv run pytest # Run tests +uv run python -m src.server.main # Run server locally + +# With Docker +docker-compose up --build -d # Start all services +docker-compose logs -f # View logs +docker-compose restart # Restart services +``` + +### Testing + +```bash +# Frontend tests (from archon-ui-main/) +npm run test:coverage:stream # Run with streaming output +npm run test:ui # Run with Vitest UI + +# Backend tests (from python/) +uv run pytest tests/test_api_essentials.py -v +uv run pytest tests/test_service_integration.py -v +``` + +## Key API Endpoints + +### Knowledge Base + +- `POST /api/knowledge/crawl` - Crawl a website +- `POST /api/knowledge/upload` - Upload documents (PDF, DOCX, MD) +- `GET /api/knowledge/items` - List knowledge items +- `POST /api/knowledge/search` - RAG search + +### MCP Integration + +- `GET /api/mcp/health` - MCP server status +- `POST /api/mcp/tools/{tool_name}` - Execute MCP tool +- `GET /api/mcp/tools` - List available tools + +### Projects & Tasks (when enabled) + +- `GET /api/projects` - List all projects +- `POST /api/projects` - Create project +- `GET /api/projects/{id}` - Get single project +- `PUT /api/projects/{id}` - Update project +- `DELETE /api/projects/{id}` - Delete project +- `GET /api/projects/{id}/tasks` - Get tasks for project (use this, not getTasks) +- `POST /api/tasks` - Create task +- `PUT /api/tasks/{id}` - Update task +- `DELETE /api/tasks/{id}` - Delete task + +## Polling Architecture + +### HTTP Polling (replaced Socket.IO) + +- **Polling intervals**: 1-2s for active operations, 5-10s for background data +- **ETag caching**: Reduces bandwidth by ~70% via 304 Not Modified responses +- **Smart pausing**: Stops polling when browser tab is inactive +- **Progress endpoints**: `/api/progress/crawl`, `/api/progress/project-creation` + +### Key Polling Hooks + +- `usePolling` - Generic polling with ETag support +- `useDatabaseMutation` - Optimistic updates with rollback +- `useProjectMutation` - Project-specific operations + +## Environment Variables + +Required in `.env`: + +```bash +SUPABASE_URL=https://your-project.supabase.co +SUPABASE_SERVICE_KEY=your-service-key-here +``` + +Optional: + +```bash +OPENAI_API_KEY=your-openai-key # Can be set via UI +LOGFIRE_TOKEN=your-logfire-token # For observability +LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR +``` + +## File Organization + +### Frontend Structure + +- `src/components/` - Legacy UI components (custom-built) +- `src/features/` - Modern vertical slice architecture with Radix UI + - `ui/primitives/` - Radix UI primitives with Tron glassmorphism + - `projects/` - Project management feature + - `tasks/` - Task management sub-feature +- `src/pages/` - Main application pages +- `src/services/` - API communication and business logic +- `src/hooks/` - Custom React hooks +- `src/contexts/` - React context providers + +### UI Libraries + +- **Radix UI** (@radix-ui/react-\*) - Unstyled, accessible primitives for `/features` +- **TanStack Query** - Data fetching and caching for `/features` +- **React DnD** - Drag and drop for Kanban boards +- **Tailwind CSS** - Utility-first styling with Tron-inspired glassmorphism +- **Framer Motion** - Animations (minimal usage) + +### Theme Management + +- **ThemeContext** - Manages light/dark theme state +- **Tailwind dark mode** - Uses `dark:` prefix with selector strategy +- **Automatic switching** - All components respect theme via Tailwind classes +- **Persistent** - Theme choice saved in localStorage +- **Tron aesthetic** - Stronger neon glows in dark mode, subtle in light mode + +We're migrating to a vertical slice architecture where each feature is self-contained. Features are organized by domain hierarchy - main features contain their sub-features. For example, tasks are a sub-feature of projects, so they live at `features/projects/tasks/` rather than as separate siblings. Each feature level has its own components, hooks, types, and services folders. This keeps related code together and makes the codebase more maintainable as it scales. + +### Backend Structure + +- `src/server/` - Main FastAPI application +- `src/server/api_routes/` - API route handlers +- `src/server/services/` - Business logic services +- `src/mcp/` - MCP server implementation +- `src/agents/` - PydanticAI agent implementations + +## Database Schema + +Key tables in Supabase: + +- `sources` - Crawled websites and uploaded documents +- `documents` - Processed document chunks with embeddings +- `projects` - Project management (optional feature) +- `tasks` - Task tracking linked to projects +- `code_examples` - Extracted code snippets + +## API Naming Conventions + +### Task Status Values + +Use database values directly (no UI mapping): + +- `todo`, `doing`, `review`, `done` + +### Service Method Patterns + +- `get[Resource]sByProject(projectId)` - Scoped queries +- `get[Resource](id)` - Single resource +- `create[Resource](data)` - Create operations +- `update[Resource](id, updates)` - Updates +- `delete[Resource](id)` - Soft deletes + +### State Naming + +- `is[Action]ing` - Loading states (e.g., `isSwitchingProject`) +- `[resource]Error` - Error messages +- `selected[Resource]` - Current selection + +## Common Development Tasks + +### Add a new API endpoint + +1. Create route handler in `python/src/server/api_routes/` +2. Add service logic in `python/src/server/services/` +3. Include router in `python/src/server/main.py` +4. Update frontend service in `archon-ui-main/src/services/` + +### Add a new UI component + +For **features** directory (preferred for new components): + +1. Use Radix UI primitives from `src/features/ui/primitives/` +2. Create component in relevant feature folder under `src/features/` +3. Use TanStack Query for data fetching +4. Apply Tron-inspired glassmorphism styling with Tailwind + +For **legacy** components: + +1. Create component in `archon-ui-main/src/components/` +2. Add to page in `archon-ui-main/src/pages/` +3. Include any new API calls in services +4. Add tests in `archon-ui-main/test/` + +### Debug MCP connection issues + +1. Check MCP health: `curl http://localhost:8051/health` +2. View MCP logs: `docker-compose logs archon-mcp` +3. Test tool execution via UI MCP page +4. Verify Supabase connection and credentials + +## Code Quality Standards + +We enforce code quality through automated linting and type checking: + +- **Python 3.12** with 120 character line length +- **Ruff** for linting - checks for errors, warnings, unused imports, and code style +- **Mypy** for type checking - ensures type safety across the codebase +- **Auto-formatting** on save in IDEs to maintain consistent style +- Run `uv run ruff check` and `uv run mypy src/` locally before committing + +## MCP Tools Available + +When connected to Cursor/Windsurf: + +- `archon:perform_rag_query` - Search knowledge base +- `archon:search_code_examples` - Find code snippets +- `archon:manage_project` - Project operations +- `archon:manage_task` - Task management +- `archon:get_available_sources` - List knowledge sources + +## Important Notes + +- Projects feature is optional - toggle in Settings UI +- All services communicate via HTTP, not gRPC +- HTTP polling handles all updates (Socket.IO removed) +- Frontend uses Vite proxy for API calls in development +- Python backend uses `uv` for dependency management +- Docker Compose handles service orchestration +- we use tanstack query NO PROP DRILLING! refacring in progress! diff --git a/CLAUDE.md b/CLAUDE.md index 23808e55b2..b618b16137 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co #### When to Fail Fast and Loud (Let it Crash!) -These errors should stop execution and bubble up immediately: +These errors should stop execution and bubble up immediately: (except for crawling flows) - **Service startup failures** - If credentials, database, or any service can't initialize, the system should crash with a clear error - **Missing configuration** - Missing environment variables or invalid settings should stop the system @@ -102,16 +102,6 @@ def process_batch(items): - Focus on user experience and feature completeness - When updating code, don't reference what is changing (avoid keywords like LEGACY, CHANGED, REMOVED), instead focus on comments that document just the functionality of the code -## Architecture Overview - -Archon V2 Alpha is a microservices-based knowledge management system with MCP (Model Context Protocol) integration: - -- **Frontend (port 3737)**: React + TypeScript + Vite + TailwindCSS -- **Main Server (port 8181)**: FastAPI with HTTP polling for updates -- **MCP Server (port 8051)**: Lightweight HTTP-based MCP protocol server -- **Agents Service (port 8052)**: PydanticAI agents for AI/ML operations -- **Database**: Supabase (PostgreSQL + pgvector for embeddings) - ## Development Commands ### Frontend (archon-ui-main/) @@ -119,129 +109,231 @@ Archon V2 Alpha is a microservices-based knowledge management system with MCP (M ```bash npm run dev # Start development server on port 3737 npm run build # Build for production -npm run lint # Run ESLint -npm run test # Run Vitest tests -npm run test:coverage # Run tests with coverage report +npm run lint # Run ESLint on legacy code (excludes /features) +npm run lint:files path/to/file.tsx # Lint specific files + +# Biome for /src/features directory only +npm run biome # Check features directory +npm run biome:fix # Auto-fix issues +npm run biome:format # Format code (120 char lines) +npm run biome:ai # Machine-readable JSON output for AI +npm run biome:ai-fix # Auto-fix with JSON output + +# Testing +npm run test # Run all tests in watch mode +npm run test:ui # Run with Vitest UI interface +npm run test:coverage:stream # Run once with streaming output +vitest run src/features/projects # Test specific directory + +# TypeScript +npx tsc --noEmit # Check all TypeScript errors +npx tsc --noEmit 2>&1 | grep "src/features" # Check features only ``` ### Backend (python/) ```bash -# Using uv package manager -uv sync # Install/update dependencies -uv run pytest # Run tests -uv run python -m src.server.main # Run server locally - -# With Docker -docker-compose up --build -d # Start all services -docker-compose logs -f # View logs -docker-compose restart # Restart services +# Using uv package manager (preferred) +uv sync --group all # Install all dependencies +uv run python -m src.server.main # Run server locally on 8181 +uv run pytest # Run all tests +uv run pytest tests/test_api_essentials.py -v # Run specific test +uv run ruff check # Run linter +uv run ruff check --fix # Auto-fix linting issues +uv run mypy src/ # Type check + +# Docker operations +docker compose up --build -d # Start all services +docker compose --profile backend up -d # Backend only (for hybrid dev) +docker compose logs -f archon-server # View server logs +docker compose logs -f archon-mcp # View MCP server logs +docker compose restart archon-server # Restart after code changes +docker compose down # Stop all services +docker compose down -v # Stop and remove volumes ``` -### Testing +### Quick Workflows ```bash -# Frontend tests (from archon-ui-main/) -npm run test:coverage:stream # Run with streaming output -npm run test:ui # Run with Vitest UI +# Hybrid development (recommended) - backend in Docker, frontend local +make dev # Or manually: docker compose --profile backend up -d && cd archon-ui-main && npm run dev + +# Full Docker mode +make dev-docker # Or: docker compose up --build -d + +# Run linters before committing +make lint # Runs both frontend and backend linters +make lint-fe # Frontend only (ESLint + Biome) +make lint-be # Backend only (Ruff + MyPy) -# Backend tests (from python/) -uv run pytest tests/test_api_essentials.py -v -uv run pytest tests/test_service_integration.py -v +# Testing +make test # Run all tests +make test-fe # Frontend tests only +make test-be # Backend tests only ``` -## Key API Endpoints +## Architecture Overview -### Knowledge Base +Archon V2 Alpha is a microservices-based knowledge management system with MCP (Model Context Protocol) integration: -- `POST /api/knowledge/crawl` - Crawl a website -- `POST /api/knowledge/upload` - Upload documents (PDF, DOCX, MD) -- `GET /api/knowledge/items` - List knowledge items -- `POST /api/knowledge/search` - RAG search +### Service Architecture -### MCP Integration +- **Frontend (port 3737)**: React + TypeScript + Vite + TailwindCSS + - **Dual UI Strategy**: + - `/features` - Modern vertical slice with Radix UI primitives + TanStack Query + - `/components` - Legacy custom components (being migrated) + - **State Management**: TanStack Query for all data fetching (no prop drilling) + - **Styling**: Tron-inspired glassmorphism with Tailwind CSS + - **Linting**: Biome for `/features`, ESLint for legacy code -- `GET /api/mcp/health` - MCP server status -- `POST /api/mcp/tools/{tool_name}` - Execute MCP tool -- `GET /api/mcp/tools` - List available tools +- **Main Server (port 8181)**: FastAPI with HTTP polling for updates + - Handles all business logic, database operations, and external API calls + - WebSocket support removed in favor of HTTP polling with ETag caching -### Projects & Tasks (when enabled) +- **MCP Server (port 8051)**: Lightweight HTTP-based MCP protocol server + - Provides tools for AI assistants (Claude, Cursor, Windsurf) + - Exposes knowledge search, task management, and project operations -- `GET /api/projects` - List all projects -- `POST /api/projects` - Create project -- `GET /api/projects/{id}` - Get single project -- `PUT /api/projects/{id}` - Update project -- `DELETE /api/projects/{id}` - Delete project -- `GET /api/projects/{id}/tasks` - Get tasks for project (use this, not getTasks) -- `POST /api/tasks` - Create task -- `PUT /api/tasks/{id}` - Update task -- `DELETE /api/tasks/{id}` - Delete task +- **Agents Service (port 8052)**: PydanticAI agents for AI/ML operations + - Handles complex AI workflows and document processing -## Polling Architecture +- **Database**: Supabase (PostgreSQL + pgvector for embeddings) + - Cloud or local Supabase both supported + - pgvector for semantic search capabilities -### HTTP Polling (replaced Socket.IO) -- **Polling intervals**: 1-2s for active operations, 5-10s for background data -- **ETag caching**: Reduces bandwidth by ~70% via 304 Not Modified responses -- **Smart pausing**: Stops polling when browser tab is inactive -- **Progress endpoints**: `/api/progress/crawl`, `/api/progress/project-creation` +### Frontend Architecture Details -### Key Polling Hooks -- `usePolling` - Generic polling with ETag support -- `useDatabaseMutation` - Optimistic updates with rollback -- `useProjectMutation` - Project-specific operations +#### Vertical Slice Architecture (/features) -## Environment Variables +Features are organized by domain hierarchy with self-contained modules: -Required in `.env`: +``` +src/features/ +├── ui/ +│ ├── primitives/ # Radix UI base components +│ ├── hooks/ # Shared UI hooks (useSmartPolling, etc) +│ └── types/ # UI type definitions +├── projects/ +│ ├── components/ # Project UI components +│ ├── hooks/ # Project hooks (useProjectQueries, etc) +│ ├── services/ # Project API services +│ ├── types/ # Project type definitions +│ ├── tasks/ # Tasks sub-feature (nested under projects) +│ │ ├── components/ +│ │ ├── hooks/ # Task-specific hooks +│ │ ├── services/ # Task API services +│ │ └── types/ +│ └── documents/ # Documents sub-feature +│ ├── components/ +│ ├── services/ +│ └── types/ +``` -```bash -SUPABASE_URL=https://your-project.supabase.co -SUPABASE_SERVICE_KEY=your-service-key-here +#### TanStack Query Patterns + +All data fetching uses TanStack Query with consistent patterns: + +```typescript +// Query keys factory pattern +export const projectKeys = { + all: ["projects"] as const, + lists: () => [...projectKeys.all, "list"] as const, + detail: (id: string) => [...projectKeys.all, "detail", id] as const, +}; + +// Smart polling with visibility awareness +const { refetchInterval } = useSmartPolling(10000); // Pauses when tab inactive + +// Optimistic updates with rollback +useMutation({ + onMutate: async (data) => { + await queryClient.cancelQueries(key); + const previous = queryClient.getQueryData(key); + queryClient.setQueryData(key, optimisticData); + return { previous }; + }, + onError: (err, vars, context) => { + if (context?.previous) { + queryClient.setQueryData(key, context.previous); + } + }, +}); ``` -Optional: +### Backend Architecture Details -```bash -OPENAI_API_KEY=your-openai-key # Can be set via UI -LOGFIRE_TOKEN=your-logfire-token # For observability -LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR +#### Service Layer Pattern + +```python +# API Route -> Service -> Database +# src/server/api_routes/projects.py +@router.get("/{project_id}") +async def get_project(project_id: str): + return await project_service.get_project(project_id) + +# src/server/services/project_service.py +async def get_project(project_id: str): + # Business logic here + return await db.fetch_project(project_id) ``` -## File Organization +#### Error Handling Patterns -### Frontend Structure +```python +# Use specific exceptions +class ProjectNotFoundError(Exception): pass +class ValidationError(Exception): pass + +# Rich error responses +@app.exception_handler(ProjectNotFoundError) +async def handle_not_found(request, exc): + return JSONResponse( + status_code=404, + content={"detail": str(exc), "type": "not_found"} + ) +``` -- `src/components/` - Reusable UI components -- `src/pages/` - Main application pages -- `src/services/` - API communication and business logic -- `src/hooks/` - Custom React hooks -- `src/contexts/` - React context providers +## Polling Architecture + +### HTTP Polling (replaced Socket.IO) + +- **Polling intervals**: 1-2s for active operations, 5-10s for background data +- **ETag caching**: Reduces bandwidth by ~70% via 304 Not Modified responses +- **Smart pausing**: Stops polling when browser tab is inactive +- **Progress endpoints**: `/api/progress/{id}` for operation tracking -### Backend Structure +### Key Polling Hooks -- `src/server/` - Main FastAPI application -- `src/server/api_routes/` - API route handlers -- `src/server/services/` - Business logic services -- `src/mcp/` - MCP server implementation -- `src/agents/` - PydanticAI agent implementations +- `useSmartPolling` - Adjusts interval based on page visibility/focus +- `useCrawlProgressPolling` - Specialized for crawl progress with auto-cleanup +- `useProjectTasks` - Smart polling for task lists ## Database Schema Key tables in Supabase: - `sources` - Crawled websites and uploaded documents + - Stores metadata, crawl status, and configuration - `documents` - Processed document chunks with embeddings + - Text chunks with vector embeddings for semantic search - `projects` - Project management (optional feature) + - Contains features array, documents, and metadata - `tasks` - Task tracking linked to projects + - Status: todo, doing, review, done + - Assignee: User, Archon, AI IDE Agent - `code_examples` - Extracted code snippets + - Language, summary, and relevance metadata ## API Naming Conventions ### Task Status Values + Use database values directly (no UI mapping): + - `todo`, `doing`, `review`, `done` ### Service Method Patterns + - `get[Resource]sByProject(projectId)` - Scoped queries - `get[Resource](id)` - Single resource - `create[Resource](data)` - Create operations @@ -249,10 +341,30 @@ Use database values directly (no UI mapping): - `delete[Resource](id)` - Soft deletes ### State Naming + - `is[Action]ing` - Loading states (e.g., `isSwitchingProject`) - `[resource]Error` - Error messages - `selected[Resource]` - Current selection +## Environment Variables + +Required in `.env`: + +```bash +SUPABASE_URL=https://your-project.supabase.co # Or http://host.docker.internal:8000 for local +SUPABASE_SERVICE_KEY=your-service-key-here # Use legacy key format for cloud Supabase +``` + +Optional: + +```bash +LOGFIRE_TOKEN=your-logfire-token # For observability +LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR +ARCHON_SERVER_PORT=8181 # Server port +ARCHON_MCP_PORT=8051 # MCP server port +ARCHON_UI_PORT=3737 # Frontend port +``` + ## Common Development Tasks ### Add a new API endpoint @@ -260,47 +372,72 @@ Use database values directly (no UI mapping): 1. Create route handler in `python/src/server/api_routes/` 2. Add service logic in `python/src/server/services/` 3. Include router in `python/src/server/main.py` -4. Update frontend service in `archon-ui-main/src/services/` +4. Update frontend service in `archon-ui-main/src/features/[feature]/services/` -### Add a new UI component +### Add a new UI component in features directory -1. Create component in `archon-ui-main/src/components/` -2. Add to page in `archon-ui-main/src/pages/` -3. Include any new API calls in services -4. Add tests in `archon-ui-main/test/` +1. Use Radix UI primitives from `src/features/ui/primitives/` +2. Create component in relevant feature folder under `src/features/[feature]/components/` +3. Define types in `src/features/[feature]/types/` +4. Use TanStack Query hook from `src/features/[feature]/hooks/` +5. Apply Tron-inspired glassmorphism styling with Tailwind ### Debug MCP connection issues 1. Check MCP health: `curl http://localhost:8051/health` -2. View MCP logs: `docker-compose logs archon-mcp` +2. View MCP logs: `docker compose logs archon-mcp` 3. Test tool execution via UI MCP page 4. Verify Supabase connection and credentials +### Fix TypeScript/Linting Issues + +```bash +# TypeScript errors in features +npx tsc --noEmit 2>&1 | grep "src/features" + +# Biome auto-fix for features +npm run biome:fix + +# ESLint for legacy code +npm run lint:files src/components/SomeComponent.tsx +``` + ## Code Quality Standards -We enforce code quality through automated linting and type checking: +### Frontend + +- **TypeScript**: Strict mode enabled, no implicit any +- **Biome** for `/src/features/`: 120 char lines, double quotes, trailing commas +- **ESLint** for legacy code: Standard React rules +- **Testing**: Vitest with React Testing Library + +### Backend - **Python 3.12** with 120 character line length -- **Ruff** for linting - checks for errors, warnings, unused imports, and code style -- **Mypy** for type checking - ensures type safety across the codebase -- **Auto-formatting** on save in IDEs to maintain consistent style -- Run `uv run ruff check` and `uv run mypy src/` locally before committing +- **Ruff** for linting - checks for errors, warnings, unused imports +- **Mypy** for type checking - ensures type safety +- **Pytest** for testing with async support ## MCP Tools Available -When connected to Cursor/Windsurf: +When connected to Client/Cursor/Windsurf: - `archon:perform_rag_query` - Search knowledge base - `archon:search_code_examples` - Find code snippets -- `archon:manage_project` - Project operations -- `archon:manage_task` - Task management +- `archon:create_project` - Create new project +- `archon:list_projects` - List all projects +- `archon:create_task` - Create task in project +- `archon:list_tasks` - List and filter tasks +- `archon:update_task` - Update task status/details - `archon:get_available_sources` - List knowledge sources ## Important Notes - Projects feature is optional - toggle in Settings UI - All services communicate via HTTP, not gRPC -- HTTP polling handles all updates (Socket.IO removed) +- HTTP polling handles all updates - Frontend uses Vite proxy for API calls in development - Python backend uses `uv` for dependency management - Docker Compose handles service orchestration +- TanStack Query for all data fetching - NO PROP DRILLING +- Vertical slice architecture in `/features` - features own their sub-features diff --git a/archon-ui-main/.dockerignore b/archon-ui-main/.dockerignore index bbae03653d..9e1e281898 100644 --- a/archon-ui-main/.dockerignore +++ b/archon-ui-main/.dockerignore @@ -43,6 +43,16 @@ docker-compose.yml # Tests coverage test-results +tests/ +**/*.test.ts +**/*.test.tsx +**/*.spec.ts +**/*.spec.tsx +**/__tests__ +**/*.e2e.test.ts +**/*.integration.test.ts +vitest.config.ts +tsconfig.prod.json # Documentation README.md diff --git a/archon-ui-main/.eslintrc.cjs b/archon-ui-main/.eslintrc.cjs index f7de173a9a..62100daacf 100644 --- a/archon-ui-main/.eslintrc.cjs +++ b/archon-ui-main/.eslintrc.cjs @@ -6,28 +6,119 @@ module.exports = { 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', ], - ignorePatterns: ['dist', '.eslintrc.cjs'], + ignorePatterns: [ + 'dist', + '.eslintrc.cjs', + 'public', + '__mocks__', + '*.config.js', + '*.config.ts', + 'coverage', + 'node_modules', + 'src/features/**' // Biome handles this directory + ], parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, plugins: ['react-refresh'], rules: { + /** + * LINTING STRATEGY FOR ALPHA DEVELOPMENT: + * + * Development: Warnings don't block local development, allowing rapid iteration + * CI/PR: Run with --max-warnings 0 to treat warnings as errors before merge + * + * Philosophy: + * - Strict typing where it helps AI assistants (Claude Code, Copilot, etc.) + * - Pragmatic flexibility for alpha-stage rapid development + * - Console.log allowed locally but caught in CI + * - Progressive enhancement: stricter rules in /features (new code) vs /components (legacy) + */ + + // React Refresh 'react-refresh/only-export-components': [ 'warn', { allowConstantExport: true }, ], - '@typescript-eslint/no-unused-vars': ['warn', { + + // TypeScript - Pragmatic strictness for AI-assisted development + '@typescript-eslint/no-explicit-any': 'warn', // Visible but won't block development + '@typescript-eslint/no-non-null-assertion': 'warn', // Allow when developer is certain + '@typescript-eslint/no-empty-function': 'warn', // Sometimes needed for placeholders + '@typescript-eslint/ban-types': 'error', // Keep strict - prevents real issues + + // Help AI assistants understand code intent + '@typescript-eslint/explicit-function-return-type': ['warn', { + allowExpressions: true, + allowTypedFunctionExpressions: true, + allowHigherOrderFunctions: true, + allowDirectConstAssertionInArrowFunctions: true, + }], + + // Better TypeScript patterns + '@typescript-eslint/prefer-as-const': 'error', + + // Variable and import management - strict with escape hatches + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', - ignoreRestSiblings: true + ignoreRestSiblings: true, + destructuredArrayIgnorePattern: '^_' }], - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/ban-types': 'off', - '@typescript-eslint/no-non-null-assertion': 'warn', - '@typescript-eslint/no-inferrable-types': 'off', + + // React hooks - warn to allow intentional omissions during development 'react-hooks/exhaustive-deps': 'warn', - 'no-case-declarations': 'off', - 'no-constant-condition': 'warn', - 'prefer-const': 'warn', - 'no-undef': 'off', + + // Console usage - warn locally, CI treats as error + 'no-console': ['warn', { allow: ['error', 'warn'] }], // console.log caught but not blocking + + // General code quality + 'prefer-const': 'error', + 'no-var': 'error', + 'no-constant-condition': 'error', + 'no-debugger': 'warn', // Warn in dev, error in CI + 'no-alert': 'error', + + // Disable rules that conflict with TypeScript + 'no-undef': 'off', // TypeScript handles this better + 'no-unused-vars': 'off', // Use @typescript-eslint/no-unused-vars instead }, -} + + // Override rules for specific file types and directories + overrides: [ + { + // Stricter rules for new vertical slice architecture + files: ['src/features/**/*.{ts,tsx}'], + rules: { + '@typescript-eslint/no-explicit-any': 'error', // No any in new code + '@typescript-eslint/explicit-function-return-type': ['error', { + allowExpressions: true, + allowTypedFunctionExpressions: true, + }], + 'no-console': ['error', { allow: ['error', 'warn'] }], // Stricter console usage + } + }, + { + // More lenient for legacy components being migrated + files: ['src/components/**/*.{ts,tsx}', 'src/services/**/*.{ts,tsx}'], + rules: { + '@typescript-eslint/no-explicit-any': 'warn', // Still visible during migration + '@typescript-eslint/explicit-function-return-type': 'off', // Not required for legacy + 'no-console': 'warn', // Warn during migration + } + }, + { + // Test files - most lenient but still helpful + files: ['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}', 'test/**/*'], + rules: { + '@typescript-eslint/no-explicit-any': 'warn', // OK in tests but still visible + '@typescript-eslint/no-non-null-assertion': 'off', // Fine in tests + '@typescript-eslint/no-empty-function': 'off', // Mock functions need this + '@typescript-eslint/explicit-function-return-type': 'off', + 'no-console': 'off', // Debugging in tests is fine + } + } + ] +}; \ No newline at end of file diff --git a/archon-ui-main/__mocks__/lucide-react.tsx b/archon-ui-main/__mocks__/lucide-react.tsx deleted file mode 100644 index a3553fe118..0000000000 --- a/archon-ui-main/__mocks__/lucide-react.tsx +++ /dev/null @@ -1,296 +0,0 @@ -import React from 'react' -import { vi } from 'vitest' - -const createMockIcon = (name: string) => { - const MockIcon = React.forwardRef(({ className, ...props }: any, ref: any) => ( - - {name} - - )) - MockIcon.displayName = name - return MockIcon -} - -// Export all icons used in the app -export const Settings = createMockIcon('Settings') -export const Check = createMockIcon('Check') -export const CheckCircle = createMockIcon('CheckCircle') -export const X = createMockIcon('X') -export const XCircle = createMockIcon('XCircle') -export const Eye = createMockIcon('Eye') -export const EyeOff = createMockIcon('EyeOff') -export const Save = createMockIcon('Save') -export const Loader = createMockIcon('Loader') -export const Loader2 = createMockIcon('Loader2') -export const RefreshCw = createMockIcon('RefreshCw') -export const Play = createMockIcon('Play') -export const Pause = createMockIcon('Pause') -export const Square = createMockIcon('Square') -export const FileText = createMockIcon('FileText') -export const Download = createMockIcon('Download') -export const Upload = createMockIcon('Upload') -export const ChevronDown = createMockIcon('ChevronDown') -export const ChevronUp = createMockIcon('ChevronUp') -export const ChevronLeft = createMockIcon('ChevronLeft') -export const ChevronRight = createMockIcon('ChevronRight') -export const Plus = createMockIcon('Plus') -export const Minus = createMockIcon('Minus') -export const Edit = createMockIcon('Edit') -export const Edit2 = createMockIcon('Edit2') -export const Edit3 = createMockIcon('Edit3') -export const Trash = createMockIcon('Trash') -export const Trash2 = createMockIcon('Trash2') -export const User = createMockIcon('User') -export const Users = createMockIcon('Users') -export const Bot = createMockIcon('Bot') -export const Database = createMockIcon('Database') -export const Server = createMockIcon('Server') -export const Globe = createMockIcon('Globe') -export const Search = createMockIcon('Search') -export const Filter = createMockIcon('Filter') -export const Copy = createMockIcon('Copy') -export const ExternalLink = createMockIcon('ExternalLink') -export const Info = createMockIcon('Info') -export const AlertCircle = createMockIcon('AlertCircle') -export const AlertTriangle = createMockIcon('AlertTriangle') -export const Zap = createMockIcon('Zap') -export const Code = createMockIcon('Code') -export const Terminal = createMockIcon('Terminal') -export const Book = createMockIcon('Book') -export const BookOpen = createMockIcon('BookOpen') -export const Folder = createMockIcon('Folder') -export const FolderOpen = createMockIcon('FolderOpen') -export const File = createMockIcon('File') -export const Hash = createMockIcon('Hash') -export const Tag = createMockIcon('Tag') -export const Clock = createMockIcon('Clock') -export const Calendar = createMockIcon('Calendar') -export const MapPin = createMockIcon('MapPin') -export const Link = createMockIcon('Link') -export const Mail = createMockIcon('Mail') -export const Phone = createMockIcon('Phone') -export const Home = createMockIcon('Home') -export const Menu = createMockIcon('Menu') -export const MoreHorizontal = createMockIcon('MoreHorizontal') -export const MoreVertical = createMockIcon('MoreVertical') -export const Refresh = createMockIcon('Refresh') -export const RotateCcw = createMockIcon('RotateCcw') -export const RotateCw = createMockIcon('RotateCw') -export const Sun = createMockIcon('Sun') -export const Moon = createMockIcon('Moon') -export const Monitor = createMockIcon('Monitor') -export const Wifi = createMockIcon('Wifi') -export const WifiOff = createMockIcon('WifiOff') -export const Volume2 = createMockIcon('Volume2') -export const VolumeX = createMockIcon('VolumeX') -export const BarChart = createMockIcon('BarChart') -export const PieChart = createMockIcon('PieChart') -export const TrendingUp = createMockIcon('TrendingUp') -export const TrendingDown = createMockIcon('TrendingDown') -export const ArrowUp = createMockIcon('ArrowUp') -export const ArrowDown = createMockIcon('ArrowDown') -export const ArrowLeft = createMockIcon('ArrowLeft') -export const ArrowRight = createMockIcon('ArrowRight') -export const Send = createMockIcon('Send') -export const MessageSquare = createMockIcon('MessageSquare') -export const MessageCircle = createMockIcon('MessageCircle') -export const Heart = createMockIcon('Heart') -export const Star = createMockIcon('Star') -export const Bookmark = createMockIcon('Bookmark') -export const Share = createMockIcon('Share') -export const Share2 = createMockIcon('Share2') -export const Maximize = createMockIcon('Maximize') -export const Minimize = createMockIcon('Minimize') -export const Expand = createMockIcon('Expand') -export const Shrink = createMockIcon('Shrink') -export const Move = createMockIcon('Move') -export const Shuffle = createMockIcon('Shuffle') -export const Repeat = createMockIcon('Repeat') -export const StopCircle = createMockIcon('StopCircle') -export const SkipBack = createMockIcon('SkipBack') -export const SkipForward = createMockIcon('SkipForward') -export const FastForward = createMockIcon('FastForward') -export const Rewind = createMockIcon('Rewind') -export const Camera = createMockIcon('Camera') -export const Image = createMockIcon('Image') -export const Video = createMockIcon('Video') -export const Mic = createMockIcon('Mic') -export const MicOff = createMockIcon('MicOff') -export const Headphones = createMockIcon('Headphones') -export const Speaker = createMockIcon('Speaker') -export const Bell = createMockIcon('Bell') -export const BellOff = createMockIcon('BellOff') -export const Shield = createMockIcon('Shield') -export const ShieldCheck = createMockIcon('ShieldCheck') -export const ShieldAlert = createMockIcon('ShieldAlert') -export const Key = createMockIcon('Key') -export const Lock = createMockIcon('Lock') -export const Unlock = createMockIcon('Unlock') -export const LogIn = createMockIcon('LogIn') -export const LogOut = createMockIcon('LogOut') -export const UserPlus = createMockIcon('UserPlus') -export const UserMinus = createMockIcon('UserMinus') -export const UserCheck = createMockIcon('UserCheck') -export const UserX = createMockIcon('UserX') -export const Package = createMockIcon('Package') -export const Package2 = createMockIcon('Package2') -export const ShoppingCart = createMockIcon('ShoppingCart') -export const ShoppingBag = createMockIcon('ShoppingBag') -export const CreditCard = createMockIcon('CreditCard') -export const DollarSign = createMockIcon('DollarSign') -export const Percent = createMockIcon('Percent') -export const Activity = createMockIcon('Activity') -export const Cpu = createMockIcon('Cpu') -export const HardDrive = createMockIcon('HardDrive') -export const MemoryStick = createMockIcon('MemoryStick') -export const Smartphone = createMockIcon('Smartphone') -export const Tablet = createMockIcon('Tablet') -export const Laptop = createMockIcon('Laptop') -export const Monitor2 = createMockIcon('Monitor2') -export const Tv = createMockIcon('Tv') -export const Watch = createMockIcon('Watch') -export const Gamepad2 = createMockIcon('Gamepad2') -export const Mouse = createMockIcon('Mouse') -export const Keyboard = createMockIcon('Keyboard') -export const Printer = createMockIcon('Printer') -export const Scanner = createMockIcon('Scanner') -export const Webcam = createMockIcon('Webcam') -export const Bluetooth = createMockIcon('Bluetooth') -export const Usb = createMockIcon('Usb') -export const Zap2 = createMockIcon('Zap2') -export const Battery = createMockIcon('Battery') -export const BatteryCharging = createMockIcon('BatteryCharging') -export const Plug = createMockIcon('Plug') -export const Power = createMockIcon('Power') -export const PowerOff = createMockIcon('PowerOff') -export const BarChart2 = createMockIcon('BarChart2') -export const BarChart3 = createMockIcon('BarChart3') -export const BarChart4 = createMockIcon('BarChart4') -export const LineChart = createMockIcon('LineChart') -export const PieChart2 = createMockIcon('PieChart2') -export const Layers = createMockIcon('Layers') -export const Layers2 = createMockIcon('Layers2') -export const Layers3 = createMockIcon('Layers3') -export const Grid = createMockIcon('Grid') -export const Grid2x2 = createMockIcon('Grid2x2') -export const Grid3x3 = createMockIcon('Grid3x3') -export const List = createMockIcon('List') -export const ListChecks = createMockIcon('ListChecks') -export const ListTodo = createMockIcon('ListTodo') -export const CheckSquare = createMockIcon('CheckSquare') -export const Square2 = createMockIcon('Square2') -export const Circle = createMockIcon('Circle') -export const CircleCheck = createMockIcon('CircleCheck') -export const CircleX = createMockIcon('CircleX') -export const CircleDot = createMockIcon('CircleDot') -export const Target = createMockIcon('Target') -export const Focus = createMockIcon('Focus') -export const Crosshair = createMockIcon('Crosshair') -export const Locate = createMockIcon('Locate') -export const LocateFixed = createMockIcon('LocateFixed') -export const Navigation = createMockIcon('Navigation') -export const Navigation2 = createMockIcon('Navigation2') -export const Compass = createMockIcon('Compass') -export const Map = createMockIcon('Map') -export const TestTube = createMockIcon('TestTube') -export const FlaskConical = createMockIcon('FlaskConical') -export const Bug = createMockIcon('Bug') -export const GitBranch = createMockIcon('GitBranch') -export const GitCommit = createMockIcon('GitCommit') -export const GitMerge = createMockIcon('GitMerge') -export const GitPullRequest = createMockIcon('GitPullRequest') -export const Github = createMockIcon('Github') -export const Gitlab = createMockIcon('Gitlab') -export const Bitbucket = createMockIcon('Bitbucket') -export const Network = createMockIcon('Network') -export const GitGraph = createMockIcon('GitGraph') -export const ListFilter = createMockIcon('ListFilter') -export const CheckSquare2 = createMockIcon('CheckSquare2') -export const CircleSlash2 = createMockIcon('CircleSlash2') -export const Clock3 = createMockIcon('Clock3') -export const GitCommitHorizontal = createMockIcon('GitCommitHorizontal') -export const CalendarDays = createMockIcon('CalendarDays') -export const Sparkles = createMockIcon('Sparkles') -export const Layout = createMockIcon('Layout') -export const Table = createMockIcon('Table') -export const Columns = createMockIcon('Columns') -export const GitPullRequestDraft = createMockIcon('GitPullRequestDraft') -export const BrainCircuit = createMockIcon('BrainCircuit') -export const Wrench = createMockIcon('Wrench') -export const PlugZap = createMockIcon('PlugZap') -export const BoxIcon = createMockIcon('BoxIcon') -export const Box = createMockIcon('Box') -export const Brain = createMockIcon('Brain') -export const LinkIcon = createMockIcon('LinkIcon') -export const Sparkle = createMockIcon('Sparkle') -export const FolderTree = createMockIcon('FolderTree') -export const Lightbulb = createMockIcon('Lightbulb') -export const Rocket = createMockIcon('Rocket') -export const Building = createMockIcon('Building') -export const FileCode = createMockIcon('FileCode') -export const FileJson = createMockIcon('FileJson') -export const Braces = createMockIcon('Braces') -export const Binary = createMockIcon('Binary') -export const Palette = createMockIcon('Palette') -export const Paintbrush = createMockIcon('Paintbrush') -export const Type = createMockIcon('Type') -export const Heading = createMockIcon('Heading') -export const AlignLeft = createMockIcon('AlignLeft') -export const AlignCenter = createMockIcon('AlignCenter') -export const AlignRight = createMockIcon('AlignRight') -export const Bold = createMockIcon('Bold') -export const Italic = createMockIcon('Italic') -export const Underline = createMockIcon('Underline') -export const Strikethrough = createMockIcon('Strikethrough') -export const FileCheck = createMockIcon('FileCheck') -export const FileX = createMockIcon('FileX') -export const FilePlus = createMockIcon('FilePlus') -export const FileMinus = createMockIcon('FileMinus') -export const FolderPlus = createMockIcon('FolderPlus') -export const FolderMinus = createMockIcon('FolderMinus') -export const FolderCheck = createMockIcon('FolderCheck') -export const FolderX = createMockIcon('FolderX') -export const startMCPServer = createMockIcon('startMCPServer') -export const Pin = createMockIcon('Pin') -export const CheckCircle2 = createMockIcon('CheckCircle2') -export const Clipboard = createMockIcon('Clipboard') -export const LayoutGrid = createMockIcon('LayoutGrid') -export const Pencil = createMockIcon('Pencil') -export const MousePointer = createMockIcon('MousePointer') -export const GripVertical = createMockIcon('GripVertical') -export const History = createMockIcon('History') -export const PlusCircle = createMockIcon('PlusCircle') -export const MinusCircle = createMockIcon('MinusCircle') -export const ChevronDownIcon = createMockIcon('ChevronDownIcon') -export const FileIcon = createMockIcon('FileIcon') -export const AlertCircleIcon = createMockIcon('AlertCircleIcon') -export const Clock4 = createMockIcon('Clock4') -export const XIcon = createMockIcon('XIcon') -export const CheckIcon = createMockIcon('CheckIcon') -export const TrashIcon = createMockIcon('TrashIcon') -export const EyeIcon = createMockIcon('EyeIcon') -export const EditIcon = createMockIcon('EditIcon') -export const DownloadIcon = createMockIcon('DownloadIcon') -export const RefreshIcon = createMockIcon('RefreshIcon') -export const SearchIcon = createMockIcon('SearchIcon') -export const FilterIcon = createMockIcon('FilterIcon') -export const PlusIcon = createMockIcon('PlusIcon') -export const FolderIcon = createMockIcon('FolderIcon') -export const FileTextIcon = createMockIcon('FileTextIcon') -export const BookOpenIcon = createMockIcon('BookOpenIcon') -export const DatabaseIcon = createMockIcon('DatabaseIcon') -export const GlobeIcon = createMockIcon('GlobeIcon') -export const TagIcon = createMockIcon('TagIcon') -export const CalendarIcon = createMockIcon('CalendarIcon') -export const ClockIcon = createMockIcon('ClockIcon') -export const UserIcon = createMockIcon('UserIcon') -export const SettingsIcon = createMockIcon('SettingsIcon') -export const InfoIcon = createMockIcon('InfoIcon') -export const WarningIcon = createMockIcon('WarningIcon') -export const ErrorIcon = createMockIcon('ErrorIcon') \ No newline at end of file diff --git a/archon-ui-main/biome.json b/archon-ui-main/biome.json new file mode 100644 index 0000000000..2461476d86 --- /dev/null +++ b/archon-ui-main/biome.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.2/schema.json", + "files": { + "includes": ["src/features/**", "src/components/layout/**"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 120, + "bracketSpacing": true, + "attributePosition": "auto" + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "trailingCommas": "all", + "semicolons": "always", + "arrowParentheses": "always", + "bracketSameLine": false + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": { + "level": "on" + } + } + } + } +} diff --git a/archon-ui-main/package-lock.json b/archon-ui-main/package-lock.json index c5a0773e96..cb8e8f3687 100644 --- a/archon-ui-main/package-lock.json +++ b/archon-ui-main/package-lock.json @@ -8,11 +8,17 @@ "name": "archon-ui", "version": "0.1.0", "dependencies": { - "@milkdown/crepe": "^7.5.0", - "@milkdown/kit": "^7.5.0", - "@milkdown/plugin-history": "^7.5.0", - "@milkdown/preset-commonmark": "^7.5.0", - "@xyflow/react": "^12.3.0", + "@mdxeditor/editor": "^3.42.0", + "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-toast": "^1.2.15", + "@radix-ui/react-tooltip": "^1.2.8", + "@tanstack/react-query": "^5.85.8", + "@tanstack/react-query-devtools": "^5.85.8", "clsx": "latest", "date-fns": "^4.1.0", "fractional-indexing": "^3.2.0", @@ -23,24 +29,26 @@ "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.3.1", + "react-markdown": "^10.1.0", "react-router-dom": "^6.26.2", "tailwind-merge": "latest", "zod": "^3.25.46" }, "devDependencies": { + "@biomejs/biome": "2.2.2", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^14.3.1", "@testing-library/user-event": "^14.5.2", "@types/node": "^20.19.0", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^5.54.0", - "@typescript-eslint/parser": "^5.54.0", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react": "^4.2.1", "@vitest/coverage-v8": "^1.6.0", "@vitest/ui": "^1.6.0", "autoprefixer": "latest", - "eslint": "^8.50.0", + "eslint": "^8.57.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.1", "jsdom": "^24.1.0", @@ -263,6 +271,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -272,6 +281,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -305,6 +315,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.28.0" @@ -395,6 +406,7 @@ "version": "7.28.1", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -411,6 +423,169 @@ "dev": true, "license": "MIT" }, + "node_modules/@biomejs/biome": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.2.tgz", + "integrity": "sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.2.2", + "@biomejs/cli-darwin-x64": "2.2.2", + "@biomejs/cli-linux-arm64": "2.2.2", + "@biomejs/cli-linux-arm64-musl": "2.2.2", + "@biomejs/cli-linux-x64": "2.2.2", + "@biomejs/cli-linux-x64-musl": "2.2.2", + "@biomejs/cli-win32-arm64": "2.2.2", + "@biomejs/cli-win32-x64": "2.2.2" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.2.tgz", + "integrity": "sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.2.tgz", + "integrity": "sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.2.tgz", + "integrity": "sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.2.tgz", + "integrity": "sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.2.tgz", + "integrity": "sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.2.tgz", + "integrity": "sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.2.tgz", + "integrity": "sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.2.tgz", + "integrity": "sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@codemirror/autocomplete": { "version": "6.18.6", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", @@ -763,6 +938,19 @@ "crelt": "^1.0.5" } }, + "node_modules/@codemirror/merge": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/@codemirror/merge/-/merge-6.10.2.tgz", + "integrity": "sha512-rmHzVkt5FnCtsi0IgvDIDjh/J4LmbfOboB7FMvVl21IHO0p1QM6jSwjkBjBD3D+c+T79OabEqoduCqvJCBV8Yg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/highlight": "^1.0.0", + "style-mod": "^4.1.0" + } + }, "node_modules/@codemirror/search": { "version": "6.5.11", "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", @@ -783,18 +971,6 @@ "@marijn/find-cluster-break": "^1.0.0" } }, - "node_modules/@codemirror/theme-one-dark": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", - "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/highlight": "^1.0.0" - } - }, "node_modules/@codemirror/view": { "version": "6.38.1", "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz", @@ -807,6 +983,61 @@ "w3c-keyname": "^2.2.4" } }, + "node_modules/@codesandbox/nodebox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@codesandbox/nodebox/-/nodebox-0.1.8.tgz", + "integrity": "sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg==", + "license": "SEE LICENSE IN ./LICENSE", + "dependencies": { + "outvariant": "^1.4.0", + "strict-event-emitter": "^0.4.3" + } + }, + "node_modules/@codesandbox/sandpack-client": { + "version": "2.19.8", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-client/-/sandpack-client-2.19.8.tgz", + "integrity": "sha512-CMV4nr1zgKzVpx4I3FYvGRM5YT0VaQhALMW9vy4wZRhEyWAtJITQIqZzrTGWqB1JvV7V72dVEUCUPLfYz5hgJQ==", + "license": "Apache-2.0", + "dependencies": { + "@codesandbox/nodebox": "0.1.8", + "buffer": "^6.0.3", + "dequal": "^2.0.2", + "mime-db": "^1.52.0", + "outvariant": "1.4.0", + "static-browser-server": "1.0.3" + } + }, + "node_modules/@codesandbox/sandpack-react": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-react/-/sandpack-react-2.20.0.tgz", + "integrity": "sha512-takd1YpW/PMQ6KPQfvseWLHWklJovGY8QYj8MtWnskGKbjOGJ6uZfyZbcJ6aCFLQMpNyjTqz9AKNbvhCOZ1TUQ==", + "license": "Apache-2.0", + "dependencies": { + "@codemirror/autocomplete": "^6.4.0", + "@codemirror/commands": "^6.1.3", + "@codemirror/lang-css": "^6.0.1", + "@codemirror/lang-html": "^6.4.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.3.2", + "@codemirror/state": "^6.2.0", + "@codemirror/view": "^6.7.1", + "@codesandbox/sandpack-client": "^2.19.8", + "@lezer/highlight": "^1.1.3", + "@react-hook/intersection-observer": "^3.1.1", + "@stitches/core": "^1.2.6", + "anser": "^2.1.1", + "clean-set": "^1.1.2", + "dequal": "^2.0.2", + "escape-carriage": "^1.3.1", + "lz-string": "^1.4.4", + "react-devtools-inline": "4.4.0", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19", + "react-dom": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -1417,28 +1648,56 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz", - "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.9" + "@floating-ui/utils": "^0.2.10" } }, "node_modules/@floating-ui/dom": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz", - "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.16", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz", + "integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.6", + "@floating-ui/utils": "^0.2.10", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.7.1", - "@floating-ui/utils": "^0.2.9" + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, "node_modules/@floating-ui/utils": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", - "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", "license": "MIT" }, "node_modules/@humanwhocodes/config-array": { @@ -1588,6 +1847,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -1601,6 +1861,261 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lexical/clipboard": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/clipboard/-/clipboard-0.33.1.tgz", + "integrity": "sha512-Qd3/Cm3TW2DFQv58kMtLi86u5YOgpBdf+o7ySbXz55C613SLACsYQBB3X5Vu5hTx/t/ugYOpII4HkiatW6d9zA==", + "license": "MIT", + "dependencies": { + "@lexical/html": "0.33.1", + "@lexical/list": "0.33.1", + "@lexical/selection": "0.33.1", + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/code": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/code/-/code-0.33.1.tgz", + "integrity": "sha512-E0Y/+1znkqVpP52Y6blXGAduoZek9SSehJN+vbH+4iQKyFwTA7JB+jd5C5/K0ik55du9X7SN/oTynByg7lbcAA==", + "license": "MIT", + "dependencies": { + "@lexical/utils": "0.33.1", + "lexical": "0.33.1", + "prismjs": "^1.30.0" + } + }, + "node_modules/@lexical/devtools-core": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/devtools-core/-/devtools-core-0.33.1.tgz", + "integrity": "sha512-3yHu5diNtjwhoe2q/x9as6n6rIfA+QO2CfaVjFRkam8rkAW6zUzQT1D0fQdE8nOfWvXBgY1mH/ZLP4dDXBdG5Q==", + "license": "MIT", + "dependencies": { + "@lexical/html": "0.33.1", + "@lexical/link": "0.33.1", + "@lexical/mark": "0.33.1", + "@lexical/table": "0.33.1", + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + }, + "peerDependencies": { + "react": ">=17.x", + "react-dom": ">=17.x" + } + }, + "node_modules/@lexical/dragon": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/dragon/-/dragon-0.33.1.tgz", + "integrity": "sha512-UQ6DLkcDAr83wA1vz3sUgtcpYcMifC4sF0MieZAoMzFrna6Ekqj7OJ7g8Lo7m7AeuT4NETRVDsjIEDdrQMKLLA==", + "license": "MIT", + "dependencies": { + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/hashtag": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/hashtag/-/hashtag-0.33.1.tgz", + "integrity": "sha512-M3IsDe4cifggMBZgYAVT7hCLWcwQ3dIcUPdr9Xc6wDQQQdEqOQYB0PO//9bSYUVq+BNiiTgysc+TtlM7PiJfiw==", + "license": "MIT", + "dependencies": { + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/history": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/history/-/history-0.33.1.tgz", + "integrity": "sha512-Bk0h3D6cFkJ7w3HKvqQua7n6Xfz7nR7L3gLDBH9L0nsS4MM9+LteSEZPUe0kj4VuEjnxufYstTc9HA2aNLKxnQ==", + "license": "MIT", + "dependencies": { + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/html": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/html/-/html-0.33.1.tgz", + "integrity": "sha512-t14vu4eKa6BWz1N7/rwXgXif1k4dj73dRvllWJgfXum+a36vn1aySNYOlOfqWXF7k1b3uJmoqsWK7n/1ASnimw==", + "license": "MIT", + "dependencies": { + "@lexical/selection": "0.33.1", + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/link": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/link/-/link-0.33.1.tgz", + "integrity": "sha512-JCTu7Fft2J2kgfqJiWnGei+UMIXVKiZKaXzuHCuGQTFu92DeCyd02azBaFazZHEkSqCIFZ0DqVV2SpIJmd0Ygw==", + "license": "MIT", + "dependencies": { + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/list": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/list/-/list-0.33.1.tgz", + "integrity": "sha512-PXp56dWADSThc9WhwWV4vXhUc3sdtCqsfPD3UQNGUZ9rsAY1479rqYLtfYgEmYPc8JWXikQCAKEejahCJIm8OQ==", + "license": "MIT", + "dependencies": { + "@lexical/selection": "0.33.1", + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/mark": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/mark/-/mark-0.33.1.tgz", + "integrity": "sha512-tGdOf1e694lnm/HyWUKEkEWjDyfhCBFG7u8iRKNpsYTpB3M1FsJUXbphE2bb8MyWfhHbaNxnklupSSaSPzO88A==", + "license": "MIT", + "dependencies": { + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/markdown": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/markdown/-/markdown-0.33.1.tgz", + "integrity": "sha512-p5zwWNF70pELRx60wxE8YOFVNiNDkw7gjKoYqkED23q5hj4mcqco9fQf6qeeZChjxLKjfyT6F1PpWgxmlBlxBw==", + "license": "MIT", + "dependencies": { + "@lexical/code": "0.33.1", + "@lexical/link": "0.33.1", + "@lexical/list": "0.33.1", + "@lexical/rich-text": "0.33.1", + "@lexical/text": "0.33.1", + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/offset": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/offset/-/offset-0.33.1.tgz", + "integrity": "sha512-3YIlUs43QdKSBLEfOkuciE2tn9loxVmkSs/HgaIiLYl0Edf1W00FP4ItSmYU4De5GopXsHq6+Y3ry4pU/ciUiQ==", + "license": "MIT", + "dependencies": { + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/overflow": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/overflow/-/overflow-0.33.1.tgz", + "integrity": "sha512-3BDq1lOw567FeCk4rN2ellKwoXTM9zGkGuKnSGlXS1JmtGGGSvT+uTANX3KOOfqTNSrOkrwoM+3hlFv7p6VpiQ==", + "license": "MIT", + "dependencies": { + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/plain-text": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/plain-text/-/plain-text-0.33.1.tgz", + "integrity": "sha512-2HxdhAx6bwF8y5A9P0q3YHsYbhUo4XXm+GyKJO87an8JClL2W+GYLTSDbfNWTh4TtH95eG+UYLOjNEgyU6tsWA==", + "license": "MIT", + "dependencies": { + "@lexical/clipboard": "0.33.1", + "@lexical/selection": "0.33.1", + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/react": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/react/-/react-0.33.1.tgz", + "integrity": "sha512-ylnUmom5h8PY+Z14uDmKLQEoikTPN77GRM0NRCIdtbWmOQqOq/5BhuCzMZE1WvpL5C6n3GtK6IFnsMcsKmVOcw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.27.8", + "@lexical/devtools-core": "0.33.1", + "@lexical/dragon": "0.33.1", + "@lexical/hashtag": "0.33.1", + "@lexical/history": "0.33.1", + "@lexical/link": "0.33.1", + "@lexical/list": "0.33.1", + "@lexical/mark": "0.33.1", + "@lexical/markdown": "0.33.1", + "@lexical/overflow": "0.33.1", + "@lexical/plain-text": "0.33.1", + "@lexical/rich-text": "0.33.1", + "@lexical/table": "0.33.1", + "@lexical/text": "0.33.1", + "@lexical/utils": "0.33.1", + "@lexical/yjs": "0.33.1", + "lexical": "0.33.1", + "react-error-boundary": "^3.1.4" + }, + "peerDependencies": { + "react": ">=17.x", + "react-dom": ">=17.x" + } + }, + "node_modules/@lexical/rich-text": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/rich-text/-/rich-text-0.33.1.tgz", + "integrity": "sha512-ZBIsj4LwmamRBCGjJiPSLj7N/XkUDv/pnYn5Rp0BL42WpOiQLvOoGLrZxgUJZEmRPQnx42ZgLKVgrWHsyjuoAA==", + "license": "MIT", + "dependencies": { + "@lexical/clipboard": "0.33.1", + "@lexical/selection": "0.33.1", + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/selection": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/selection/-/selection-0.33.1.tgz", + "integrity": "sha512-KXPkdCDdVfIUXmkwePu9DAd3kLjL0aAqL5G9CMCFsj7RG9lLvvKk7kpivrAIbRbcsDzO44QwsFPisZHbX4ioXA==", + "license": "MIT", + "dependencies": { + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/table": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/table/-/table-0.33.1.tgz", + "integrity": "sha512-pzB11i1Y6fzmy0IPUKJyCdhVBgXaNOxJUxrQJWdKNYCh1eMwwMEQvj+8inItd/11aUkjcdHjwDTht8gL2UHKiQ==", + "license": "MIT", + "dependencies": { + "@lexical/clipboard": "0.33.1", + "@lexical/utils": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/text": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/text/-/text-0.33.1.tgz", + "integrity": "sha512-CnyU3q3RytXXWVSvC5StOKISzFAPGK9MuesNDDGyZk7yDK+J98gV6df4RBKfqwcokFMThpkUlvMeKe1+S2y25A==", + "license": "MIT", + "dependencies": { + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/utils": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/utils/-/utils-0.33.1.tgz", + "integrity": "sha512-eKysPjzEE9zD+2af3WRX5U3XbeNk0z4uv1nXGH3RG15uJ4Huzjht82hzsQpCFUobKmzYlQaQs5y2IYKE2puipQ==", + "license": "MIT", + "dependencies": { + "@lexical/list": "0.33.1", + "@lexical/selection": "0.33.1", + "@lexical/table": "0.33.1", + "lexical": "0.33.1" + } + }, + "node_modules/@lexical/yjs": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@lexical/yjs/-/yjs-0.33.1.tgz", + "integrity": "sha512-Zx1rabMm/Zjk7n7YQMIQLUN+tqzcg1xqcgNpEHSfK1GA8QMPXCPvXWFT3ZDC4tfZOSy/YIqpVUyWZAomFqRa+g==", + "license": "MIT", + "dependencies": { + "@lexical/offset": "0.33.1", + "@lexical/selection": "0.33.1", + "lexical": "0.33.1" + }, + "peerDependencies": { + "yjs": ">=13.5.22" + } + }, "node_modules/@lezer/common": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", @@ -1784,470 +2299,1090 @@ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "license": "MIT" }, - "node_modules/@milkdown/components": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/components/-/components-7.15.1.tgz", - "integrity": "sha512-yGfjSi7VaRtiyoJA/KGVQtXpig2GYFe7uFAC6kRwskJZF/LQH/+hjjTgpPIcGn+AhKghtJ049ZXnJkgFU45YYA==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.5.1", - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/exception": "7.15.1", - "@milkdown/plugin-tooltip": "7.15.1", - "@milkdown/preset-commonmark": "7.15.1", - "@milkdown/preset-gfm": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/transformer": "7.15.1", - "@milkdown/utils": "7.15.1", - "@types/lodash.debounce": "^4.0.7", - "@types/lodash.throttle": "^4.1.9", - "clsx": "^2.0.0", - "dompurify": "^3.2.5", - "lodash.debounce": "^4.0.8", - "lodash.throttle": "^4.1.1", - "nanoid": "^5.0.9", - "tslib": "^2.8.1", - "unist-util-visit": "^5.0.0", - "vue": "^3.5.13" + "node_modules/@mdxeditor/editor": { + "version": "3.42.0", + "resolved": "https://registry.npmjs.org/@mdxeditor/editor/-/editor-3.42.0.tgz", + "integrity": "sha512-nQN07RkTm842T477IjPqp1FhWCQMpmbLToOVrc6EjSI60aHifwzva+eqYmElHFKE2jyGiD5FsaQXri1SSORJNg==", + "license": "MIT", + "dependencies": { + "@codemirror/commands": "^6.2.4", + "@codemirror/lang-markdown": "^6.2.3", + "@codemirror/language-data": "^6.5.1", + "@codemirror/merge": "^6.4.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.23.0", + "@codesandbox/sandpack-react": "^2.20.0", + "@lexical/clipboard": "^0.33.1", + "@lexical/link": "^0.33.1", + "@lexical/list": "^0.33.1", + "@lexical/markdown": "^0.33.1", + "@lexical/plain-text": "^0.33.1", + "@lexical/react": "^0.33.1", + "@lexical/rich-text": "^0.33.1", + "@lexical/selection": "^0.33.1", + "@lexical/utils": "^0.33.1", + "@mdxeditor/gurx": "^1.1.4", + "@radix-ui/colors": "^3.0.0", + "@radix-ui/react-dialog": "^1.1.11", + "@radix-ui/react-icons": "^1.3.2", + "@radix-ui/react-popover": "^1.1.11", + "@radix-ui/react-popper": "^1.2.4", + "@radix-ui/react-select": "^2.2.2", + "@radix-ui/react-toggle-group": "^1.1.7", + "@radix-ui/react-toolbar": "^1.1.7", + "@radix-ui/react-tooltip": "^1.2.4", + "classnames": "^2.3.2", + "cm6-theme-basic-light": "^0.2.0", + "codemirror": "^6.0.1", + "downshift": "^7.6.0", + "js-yaml": "4.1.0", + "lexical": "^0.33.1", + "mdast-util-directive": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-frontmatter": "^2.0.1", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-highlight-mark": "^1.2.2", + "mdast-util-mdx": "^3.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-to-markdown": "^2.1.0", + "micromark-extension-directive": "^3.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.1", + "micromark-extension-highlight-mark": "^1.2.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs": "^3.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.1", + "micromark-util-symbol": "^2.0.0", + "react-hook-form": "^7.56.1", + "unidiff": "^1.0.2" + }, + "engines": { + "node": ">=16" }, "peerDependencies": { - "@codemirror/language": "^6", - "@codemirror/state": "^6", - "@codemirror/view": "^6" + "react": ">= 18 || >= 19", + "react-dom": ">= 18 || >= 19" } }, - "node_modules/@milkdown/components/node_modules/nanoid": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", - "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/@mdxeditor/gurx": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@mdxeditor/gurx/-/gurx-1.2.3.tgz", + "integrity": "sha512-5DQOlEx46oN9spggrC8husAGAhVoEFBGIYKN48es08XhRUbSU6l5bcIQYwRrQaY8clU1tExIcXzw8/fNnoxjpg==", "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "react": ">= 18 || >= 19", + "react-dom": ">= 18 || >= 19" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": "^18 || >=20" + "node": ">= 8" } }, - "node_modules/@milkdown/core": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/core/-/core-7.15.1.tgz", - "integrity": "sha512-jcuKZnZ9rrffwpAFq+0pMIwfxnchZOCFSIQT7NQnsOhzFXnCNXu69cxzlcK3CZExDgkmivHM62xFsjN9l7vTdg==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "license": "MIT", "dependencies": { - "@milkdown/ctx": "7.15.1", - "@milkdown/exception": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/transformer": "7.15.1", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "tslib": "^2.8.1", - "unified": "^11.0.3" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" } }, - "node_modules/@milkdown/crepe": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/crepe/-/crepe-7.15.1.tgz", - "integrity": "sha512-2n63s4vBzTO0IBTO/nIsS/XKyPIpOXjiIwXgkIXkbzNt/sPA6hEwtvEJDCz7uzRo/MCxZu0jLjPW/OpVMakLNg==", + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/colors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-3.0.0.tgz", + "integrity": "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==", + "license": "MIT" + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", + "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", "license": "MIT", "dependencies": { - "@codemirror/commands": "^6.2.4", - "@codemirror/language": "^6.10.1", - "@codemirror/language-data": "^6.3.1", - "@codemirror/state": "^6.4.1", - "@codemirror/theme-one-dark": "^6.1.2", - "@codemirror/view": "^6.16.0", - "@floating-ui/dom": "^1.5.1", - "@milkdown/kit": "7.15.1", - "@types/lodash-es": "^4.17.12", - "clsx": "^2.0.0", - "codemirror": "^6.0.1", - "katex": "^0.16.0", - "lodash-es": "^4.17.21", - "nanoid": "^5.0.9", - "prosemirror-virtual-cursor": "^0.4.2", - "remark-math": "^6.0.0", - "tslib": "^2.8.1", - "unist-util-visit": "^5.0.0", - "vue": "^3.5.13" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@milkdown/crepe/node_modules/nanoid": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", - "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true } - ], + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, - "engines": { - "node": "^18 || >=20" + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@milkdown/ctx": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/ctx/-/ctx-7.15.1.tgz", - "integrity": "sha512-MO2EymuAmcT9TVcbZVr0TriFMAPP1d1p/cWVbyqZXKsxK1sRzNxJCpdPm20LD2e2qJt6pRziIf/ugGww1Tvf7A==", + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", "dependencies": { - "@milkdown/exception": "7.15.1", - "tslib": "^2.8.1" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@milkdown/exception": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/exception/-/exception-7.15.1.tgz", - "integrity": "sha512-QMpT/8SYM1CIuptHrOKzaelZd4ZU1j9mz3m2EwF4Ql0PNOXoWW50/P7gtr71foyTu3fPyXA9f8/GaTkihD/b/Q==", + "node_modules/@radix-ui/react-icons": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz", + "integrity": "sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==", + "license": "MIT", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "license": "MIT", "dependencies": { - "tslib": "^2.8.1" + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@milkdown/kit": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/kit/-/kit-7.15.1.tgz", - "integrity": "sha512-wmUt9mN+rfJgCz11c3z2E8ExIKhd2QLdFPCPb8OHamebsf+td5nS0HX2vpvkaumgD4AQA0KCbMs9WmqChG/K7w==", + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "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-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", "license": "MIT", "dependencies": { - "@milkdown/components": "7.15.1", - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/plugin-block": "7.15.1", - "@milkdown/plugin-clipboard": "7.15.1", - "@milkdown/plugin-cursor": "7.15.1", - "@milkdown/plugin-history": "7.15.1", - "@milkdown/plugin-indent": "7.15.1", - "@milkdown/plugin-listener": "7.15.1", - "@milkdown/plugin-slash": "7.15.1", - "@milkdown/plugin-tooltip": "7.15.1", - "@milkdown/plugin-trailing": "7.15.1", - "@milkdown/plugin-upload": "7.15.1", - "@milkdown/preset-commonmark": "7.15.1", - "@milkdown/preset-gfm": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/transformer": "7.15.1", - "@milkdown/utils": "7.15.1", - "tslib": "^2.8.1" + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@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", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-block": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-block/-/plugin-block-7.15.1.tgz", - "integrity": "sha512-ltftyP6brSs5N3q9mJhcauqfuDuGIGm2dsXwpibsRsO8WbCptVpQPjHCNGJp+/Y+bLrRas5DAz+cqFvfDvCDYA==", + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.5.1", - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/exception": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "@types/lodash.throttle": "^4.1.9", - "lodash.throttle": "^4.1.1", - "tslib": "^2.8.1" + "@radix-ui/react-primitive": "2.1.3", + "@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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-clipboard": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-clipboard/-/plugin-clipboard-7.15.1.tgz", - "integrity": "sha512-mtq+CQhuCR/bVKHep588OsrIHxQAve85VHIPHPaU768c6jkQhGlr82a0bp90hhzTMREWDqxsFrJAxPM+PDtugA==", + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", "license": "MIT", "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "tslib": "^2.8.1" + "@radix-ui/react-compose-refs": "1.1.2", + "@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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-cursor": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-cursor/-/plugin-cursor-7.15.1.tgz", - "integrity": "sha512-HZloO+PpoXvdf854aflIA1pq5cmoRHNvaiC3QCeywAz6y0EHFr0NSJRQQZwXIefdbi5l/CP/lkc9dJJotzgEng==", + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "tslib": "^2.8.1" + "@radix-ui/react-slot": "1.2.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-history": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-history/-/plugin-history-7.15.1.tgz", - "integrity": "sha512-2LkYbZYuix7LUI/sR1NQO5oZOjcT9E6wJhDHcMmeO8XoIO6r0q8STdH7jvITkB/Rr9wNRXfI+V86hvsfB0aMbw==", + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", "license": "MIT", "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "tslib": "^2.8.1" + "@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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-indent": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-indent/-/plugin-indent-7.15.1.tgz", - "integrity": "sha512-D3asSTw6Jvyn3TRVOGNNwhslL0OgnU0Fi9G1JOt9nsaqDIuTMQQhapJzr3VCZn1ko9hdlYUKBQnPkAXngNZKjg==", + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@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-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", "license": "MIT", "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "tslib": "^2.8.1" + "@radix-ui/react-primitive": "2.1.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-listener": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-listener/-/plugin-listener-7.15.1.tgz", - "integrity": "sha512-lnpFzAmhJK0+No0R4utWNx31cDunBqkdBGMBbV6571SHgfVIHw/T8z64t8Fo7xNt9OjcgKee877tk6TaL98HiQ==", + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "@types/lodash.debounce": "^4.0.7", - "lodash.debounce": "^4.0.8", - "tslib": "^2.8.1" + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-slash": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-slash/-/plugin-slash-7.15.1.tgz", - "integrity": "sha512-b/wvpr7+hRgNsftu3XbUMHEKOKUSdNN+HXMhTqTIheB/m/Y7zSdPL3kXMQC1ZRwHyDu1oL6lUuMCMCJ8cDiMvg==", + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.5.1", - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/exception": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "@types/lodash.debounce": "^4.0.7", - "lodash.debounce": "^4.0.8", - "tslib": "^2.8.1" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "license": "MIT", + "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-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@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-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", + "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz", + "integrity": "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-toggle": "1.1.10", + "@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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toolbar": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.11.tgz", + "integrity": "sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-separator": "1.1.7", + "@radix-ui/react-toggle-group": "1.1.11" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-tooltip": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-tooltip/-/plugin-tooltip-7.15.1.tgz", - "integrity": "sha512-Q/TwzqM4CRSTmz0+E/amtNTgk7DJpAOjjCR4am02N2HbYP7GcL92mC1pEx//tus1AW9+LOdF+cmWyObHGkC7Vg==", + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.5.1", - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/exception": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "@types/lodash.throttle": "^4.1.9", - "lodash.throttle": "^4.1.1", - "tslib": "^2.8.1" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-trailing": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-trailing/-/plugin-trailing-7.15.1.tgz", - "integrity": "sha512-zsECiNOMta4bIy+4a+BplmMwWfrhy3SYcm1kH6DjvkpoBG6LtZC4fblEnlW4feHzDwXVLHkbac5urR4rsZxLcA==", + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "tslib": "^2.8.1" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@milkdown/plugin-upload": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/plugin-upload/-/plugin-upload-7.15.1.tgz", - "integrity": "sha512-9sU2GRERc7lhQ8mSANZ3v1531pmVRImJx4Pr73oB7VPRDS8GbnhtuVI6VewoVh3zJkM8MSE4G4L/TfWXfa2UGQ==", + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", "license": "MIT", "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/exception": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/utils": "7.15.1", - "tslib": "^2.8.1" + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@milkdown/preset-commonmark": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/preset-commonmark/-/preset-commonmark-7.15.1.tgz", - "integrity": "sha512-P1dewR9TGe8VFIE5F+W9g/2QQzf47EZ+Uq4CF5mYAFbjzPHwJDgfN4vA/o43feXFNxu5cm4UQFWzSCtDNZFTWg==", + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "license": "MIT", "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/exception": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/transformer": "7.15.1", - "@milkdown/utils": "7.15.1", - "remark-inline-links": "^7.0.0", - "tslib": "^2.8.1", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.1" - } - }, - "node_modules/@milkdown/preset-gfm": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/preset-gfm/-/preset-gfm-7.15.1.tgz", - "integrity": "sha512-cmQsx1lwWGi7vv/8Kx92dToWpqKWLDp5OZSWE0eiLCtAV87v+vL/bT6xDrjqmlpFjA5WEM7ah+Ki3EpqLsRfng==", - "license": "MIT", - "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/exception": "7.15.1", - "@milkdown/preset-commonmark": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/transformer": "7.15.1", - "@milkdown/utils": "7.15.1", - "prosemirror-safari-ime-span": "^1.0.1", - "remark-gfm": "^4.0.1", - "tslib": "^2.8.1" - } - }, - "node_modules/@milkdown/prose": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/prose/-/prose-7.15.1.tgz", - "integrity": "sha512-8xSgiC6qk8j9zDbRZiWcdclr4vIxU6tnzMBg/Kr4pIEOsir0KA+c6kPNFj7T91BeaV9ksCsWOmYfBwd2SptCXQ==", - "license": "MIT", - "dependencies": { - "@milkdown/exception": "7.15.1", - "prosemirror-changeset": "^2.2.1", - "prosemirror-commands": "^1.6.2", - "prosemirror-dropcursor": "^1.8.1", - "prosemirror-gapcursor": "^1.3.2", - "prosemirror-history": "^1.4.1", - "prosemirror-inputrules": "^1.4.0", - "prosemirror-keymap": "^1.2.2", - "prosemirror-model": "^1.24.1", - "prosemirror-schema-list": "^1.5.0", - "prosemirror-state": "^1.4.3", - "prosemirror-tables": "^1.7.0", - "prosemirror-transform": "^1.10.2", - "prosemirror-view": "^1.37.1", - "tslib": "^2.8.1" - } - }, - "node_modules/@milkdown/transformer": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/transformer/-/transformer-7.15.1.tgz", - "integrity": "sha512-Hwp0swHmvN2D6iM67mnoP7wPeiDipz/GDdyO7CfnYjUsUQcRHzMouoqJ91dp9bO+f4EJ0Vr+8C1qIYRAn4ZDhA==", - "license": "MIT", - "dependencies": { - "@milkdown/exception": "7.15.1", - "@milkdown/prose": "7.15.1", - "remark": "^15.0.1", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "tslib": "^2.8.1", - "unified": "^11.0.3" + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@milkdown/utils": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@milkdown/utils/-/utils-7.15.1.tgz", - "integrity": "sha512-CvApKO84xdIGOUqvVeyDKRyN+PYqi8WNC9im7YWis2EojaSEleX7GMVOoWUHcB8xSdyuY+yJzPguMsx3QKuHIg==", + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "license": "MIT", - "dependencies": { - "@milkdown/core": "7.15.1", - "@milkdown/ctx": "7.15.1", - "@milkdown/exception": "7.15.1", - "@milkdown/prose": "7.15.1", - "@milkdown/transformer": "7.15.1", - "nanoid": "^5.0.9", - "tslib": "^2.8.1" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@milkdown/utils/node_modules/nanoid": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", - "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "engines": { - "node": "^18 || >=20" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@radix-ui/rect": "1.1.1" }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "dev": true, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, "node_modules/@react-dnd/asap": { @@ -2268,6 +3403,28 @@ "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", "license": "MIT" }, + "node_modules/@react-hook/intersection-observer": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@react-hook/intersection-observer/-/intersection-observer-3.1.2.tgz", + "integrity": "sha512-mWU3BMkmmzyYMSuhO9wu3eJVP21N8TcgYm9bZnTrMwuM818bEk+0NRM3hP+c/TqA9Ln5C7qE53p1H0QMtzYdvQ==", + "license": "MIT", + "dependencies": { + "@react-hook/passive-layout-effect": "^1.2.0", + "intersection-observer": "^0.10.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@react-hook/passive-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz", + "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8" + } + }, "node_modules/@remix-run/router": { "version": "1.23.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", @@ -2571,6 +3728,65 @@ "dev": true, "license": "MIT" }, + "node_modules/@stitches/core": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", + "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==", + "license": "MIT" + }, + "node_modules/@tanstack/query-core": { + "version": "5.85.7", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.85.7.tgz", + "integrity": "sha512-FLT3EtuTbXBmOrDku4bI80Eivmjn/o/Zc1lVEd/6yzR8UAUSnDwYiwghCZvLqHyGSN5mO35ux1yPGMFYBFRSwA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.84.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.84.0.tgz", + "integrity": "sha512-fbF3n+z1rqhvd9EoGp5knHkv3p5B2Zml1yNRjh7sNXklngYI5RVIWUrUjZ1RIcEoscarUb0+bOvIs5x9dwzOXQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.85.8", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.85.8.tgz", + "integrity": "sha512-r3rW55STAO03EJg5mrCVIJvaEK3oeHme5u7QovuRFIKRbEgTzTv2DPdenX46X+x56LsU3ree1N4rzI/+gJ7KEA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.85.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.85.8", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.85.8.tgz", + "integrity": "sha512-83SXqRpmVlRMpaj32veez/8ohjY7O4VQIYDqW91b4i9AQjiYgE24FbBfR/SOL8b5MfKhHMZkD+BQSpCh9jY06w==", + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.84.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.85.8", + "react": "^18 || ^19" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -2777,55 +3993,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-drag": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", - "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-selection": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", - "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", - "license": "MIT" - }, - "node_modules/@types/d3-transition": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", - "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-zoom": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", - "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", - "license": "MIT", - "dependencies": { - "@types/d3-interpolate": "*", - "@types/d3-selection": "*" - } - }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -2839,9 +4006,17 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -2858,45 +4033,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/katex": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", - "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", - "license": "MIT" - }, - "node_modules/@types/lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", - "license": "MIT" - }, - "node_modules/@types/lodash-es": { - "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", - "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/lodash.debounce": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz", - "integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==", - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/lodash.throttle": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.9.tgz", - "integrity": "sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==", - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -2926,14 +4062,12 @@ "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.23", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -2944,26 +4078,19 @@ "version": "18.3.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^18.0.0" } }, "node_modules/@types/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", "dev": true, "license": "MIT" }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "license": "MIT", - "optional": true - }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -2971,33 +4098,34 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -3006,26 +4134,27 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -3034,17 +4163,17 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3052,26 +4181,26 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -3080,13 +4209,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3094,72 +4223,98 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=16 || 14 >=14.17" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3170,7 +4325,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, "license": "ISC" }, "node_modules/@vitejs/plugin-react": { @@ -3417,165 +4571,10 @@ "dev": true, "license": "MIT" }, - "node_modules/@vue/compiler-core": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", - "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.0", - "@vue/shared": "3.5.18", - "entities": "^4.5.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-core/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@vue/compiler-core/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", - "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.5.18", - "@vue/shared": "3.5.18" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", - "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.0", - "@vue/compiler-core": "3.5.18", - "@vue/compiler-dom": "3.5.18", - "@vue/compiler-ssr": "3.5.18", - "@vue/shared": "3.5.18", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.17", - "postcss": "^8.5.6", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", - "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.18", - "@vue/shared": "3.5.18" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz", - "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", - "license": "MIT", - "dependencies": { - "@vue/shared": "3.5.18" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz", - "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.18", - "@vue/shared": "3.5.18" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", - "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.18", - "@vue/runtime-core": "3.5.18", - "@vue/shared": "3.5.18", - "csstype": "^3.1.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz", - "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-ssr": "3.5.18", - "@vue/shared": "3.5.18" - }, - "peerDependencies": { - "vue": "3.5.18" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", - "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", - "license": "MIT" - }, - "node_modules/@xyflow/react": { - "version": "12.6.4", - "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.6.4.tgz", - "integrity": "sha512-/dOQ43Nu217cwHzy7f8kNUrFMeJJENzftVgT2VdFFHi6fHlG83pF+gLmvkRW9Be7alCsR6G+LFxxCdsQQbazHg==", - "license": "MIT", - "dependencies": { - "@xyflow/system": "0.0.61", - "classcat": "^5.0.3", - "zustand": "^4.4.0" - }, - "peerDependencies": { - "react": ">=17", - "react-dom": ">=17" - } - }, - "node_modules/@xyflow/system": { - "version": "0.0.61", - "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.61.tgz", - "integrity": "sha512-TsZG/Ez8dzxX6/Ol44LvFqVZsYvyz6dpDlAQZZk6hTL7JLGO5vN3dboRJqMwU8/Qtr5IEv5YBzojjAwIqW1HCA==", - "license": "MIT", - "dependencies": { - "@types/d3-drag": "^3.0.7", - "@types/d3-selection": "^3.0.10", - "@types/d3-transition": "^3.0.8", - "@types/d3-zoom": "^3.0.8", - "d3-drag": "^3.0.0", - "d3-selection": "^3.0.0", - "d3-zoom": "^3.0.0" - } - }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3588,7 +4587,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -3634,6 +4632,12 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/anser": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/anser/-/anser-2.3.2.tgz", + "integrity": "sha512-PMqBCBvrOVDRqLGooQb+z+t1Q0PiPyurUQeZRR5uHBOVZcW8B04KMmnT12USnhpNX2wCPagWzLVppQMUG3u0Dw==", + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -3692,9 +4696,20 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -3820,6 +4835,26 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3890,6 +4925,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -4047,6 +5106,36 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/check-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", @@ -4098,10 +5187,16 @@ "node": ">= 6" } }, - "node_modules/classcat": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", - "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/clean-set": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/clean-set/-/clean-set-1.1.2.tgz", + "integrity": "sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==", "license": "MIT" }, "node_modules/clsx": { @@ -4113,6 +5208,18 @@ "node": ">=6" } }, + "node_modules/cm6-theme-basic-light": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/cm6-theme-basic-light/-/cm6-theme-basic-light-0.2.0.tgz", + "integrity": "sha512-1prg2gv44sYfpHscP26uLT/ePrh0mlmVwMSoSd3zYKQ92Ab3jPRLzyCnpyOCQLJbK+YdNs4HvMRqMNYdy4pMhA==", + "license": "MIT", + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, "node_modules/codemirror": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", @@ -4161,6 +5268,16 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -4171,6 +5288,12 @@ "node": ">= 6" } }, + "node_modules/compute-scroll-into-view": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz", + "integrity": "sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g==", + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4217,159 +5340,67 @@ "which": "^2.0.1" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssstyle": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.1.tgz", - "integrity": "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/css-color": "^3.1.2", - "rrweb-cssom": "^0.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cssstyle/node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" + "node": ">= 8" } }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "license": "ISC", + "node_modules/cssstyle": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.1.tgz", + "integrity": "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==", + "dev": true, + "license": "MIT", "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" + "@asamuzakjp/css-color": "^3.1.2", + "rrweb-cssom": "^0.8.0" }, "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" + "node": ">=18" } }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", "license": "ISC", "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" + "es5-ext": "^0.10.64", + "type": "^2.7.2" }, "engines": { - "node": ">=12" + "node": ">=0.12" } }, "node_modules/data-urls": { @@ -4541,6 +5572,12 @@ "node": ">=6" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -4632,13 +5669,32 @@ "dev": true, "license": "MIT" }, - "node_modules/dompurify": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", - "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", - "license": "(MPL-2.0 OR Apache-2.0)", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/downshift": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-7.6.2.tgz", + "integrity": "sha512-iOv+E1Hyt3JDdL9yYcOgW7nZ7GQ2Uz6YbggwXvKUSleetYhU2nXD482Rz6CzvM4lvI1At34BYruKAL4swRGxaA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.14.8", + "compute-scroll-into-view": "^2.0.4", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "react": ">=16.12.0" } }, "node_modules/dunder-proto": { @@ -4760,6 +5816,46 @@ "node": ">= 0.4" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -4809,6 +5905,12 @@ "node": ">=6" } }, + "node_modules/escape-carriage": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/escape-carriage/-/escape-carriage-1.3.1.tgz", + "integrity": "sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4903,17 +6005,20 @@ } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { @@ -4929,33 +6034,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "node_modules/eslint/node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -4972,6 +6050,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -5003,16 +6096,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -5026,7 +6109,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -5036,14 +6119,28 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, "node_modules/estree-walker": { @@ -5066,6 +6163,16 @@ "node": ">=0.10.0" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", @@ -5090,6 +6197,15 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -5156,6 +6272,19 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -5277,6 +6406,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -5414,6 +6551,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -5605,6 +6751,46 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -5640,6 +6826,16 @@ "dev": true, "license": "MIT" }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -5691,6 +6887,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5757,6 +6973,12 @@ "dev": true, "license": "ISC" }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -5768,8 +6990,38 @@ "hasown": "^2.0.2", "side-channel": "^1.1.0" }, - "engines": { - "node": ">= 0.4" + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/intersection-observer": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.10.0.tgz", + "integrity": "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==", + "license": "W3C-20150513" + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, "node_modules/is-arguments": { @@ -5899,6 +7151,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -5932,6 +7194,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -6141,6 +7413,17 @@ "dev": true, "license": "ISC" }, + "node_modules/isomorphic.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", + "license": "MIT", + "peer": true, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -6231,7 +7514,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -6328,31 +7610,6 @@ "node": ">=6" } }, - "node_modules/katex": { - "version": "0.16.22", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", - "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "license": "MIT", - "dependencies": { - "commander": "^8.3.0" - }, - "bin": { - "katex": "cli.js" - } - }, - "node_modules/katex/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6363,6 +7620,15 @@ "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6377,6 +7643,34 @@ "node": ">= 0.8.0" } }, + "node_modules/lexical": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/lexical/-/lexical-0.33.1.tgz", + "integrity": "sha512-+kiCS/GshQmCs/meMb8MQT4AMvw3S3Ef0lSCv2Xi6Itvs59OD+NjQWNfYkDteIbKtVE/w0Yiqh56VyGwIb8UcA==", + "license": "MIT" + }, + "node_modules/lib0": { + "version": "0.2.114", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", + "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -6437,18 +7731,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6456,12 +7738,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", - "license": "MIT" - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -6517,7 +7793,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, "license": "MIT", "bin": { "lz-string": "bin/bin.js" @@ -6527,6 +7802,7 @@ "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -6587,30 +7863,20 @@ "node": ">= 0.4" } }, - "node_modules/mdast-util-definitions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", - "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "node_modules/mdast-util-directive": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", + "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", "unist-util-visit-parents": "^6.0.0" }, "funding": { @@ -6618,18 +7884,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mdast-util-from-markdown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", @@ -6654,57 +7908,34 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { + "node_modules/mdast-util-frontmatter": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" + "engines": { + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mdast-util-gfm-strikethrough": { @@ -6755,19 +7986,86 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-math": { + "node_modules/mdast-util-highlight-mark": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/mdast-util-highlight-mark/-/mdast-util-highlight-mark-1.2.2.tgz", + "integrity": "sha512-OYumVoytj+B9YgwzBhBcYUCLYHIPvJtAvwnMyKhUXbfUFuER5S+FDZyu9fadUxm2TCT5fRYK3jQXh2ioWAxrMw==", + "license": "MIT", + "dependencies": { + "micromark-extension-highlight-mark": "1.2.0" + } + }, + "node_modules/mdast-util-mdx": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", - "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "license": "MIT", "dependencies": { + "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", - "longest-streak": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.1.0", - "unist-util-remove-position": "^5.0.0" + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { "type": "opencollective", @@ -6788,6 +8086,27 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-to-markdown": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", @@ -6908,19 +8227,34 @@ "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", "license": "MIT", "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "license": "MIT", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" }, "funding": { @@ -6928,14 +8262,33 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-autolink-literal": { + "node_modules/micromark-extension-gfm-strikethrough": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", "license": "MIT", "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" }, @@ -6944,18 +8297,15 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-footnote": { + "node_modules/micromark-extension-gfm-task-list-item": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" }, @@ -6964,45 +8314,72 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "node_modules/micromark-extension-highlight-mark": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-extension-highlight-mark/-/micromark-extension-highlight-mark-1.2.0.tgz", + "integrity": "sha512-huGtbd/9kQsMk8u7nrVMaS5qH/47yDG6ZADggo5Owz5JoY8wdfQjfuy118/QiYNCvdFuFDbzT0A7K7Hp2cBsXA==", + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "uvu": "^0.5.6" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { + "@types/estree": "^1.0.0", "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", "license": "MIT", "dependencies": { + "@types/estree": "^1.0.0", "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-tagfilter": { + "node_modules/micromark-extension-mdx-md": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", "license": "MIT", "dependencies": { "micromark-util-types": "^2.0.0" @@ -7012,16 +8389,19 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", "license": "MIT", "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" }, "funding": { @@ -7029,19 +8409,21 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-math": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", - "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", "license": "MIT", "dependencies": { - "@types/katex": "^0.16.0", + "@types/estree": "^1.0.0", "devlop": "^1.0.0", - "katex": "^0.16.0", - "micromark-factory-space": "^2.0.0", + "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" }, "funding": { "type": "opencollective", @@ -7091,6 +8473,33 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, "node_modules/micromark-factory-space": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", @@ -7292,6 +8701,31 @@ ], "license": "MIT" }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, "node_modules/micromark-util-html-tag-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", @@ -7439,7 +8873,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -7539,6 +8972,15 @@ "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", "license": "MIT" }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -7571,6 +9013,7 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, "funding": [ { "type": "github", @@ -7592,12 +9035,11 @@ "dev": true, "license": "MIT" }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true, - "license": "MIT" + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" }, "node_modules/node-releases": { "version": "2.0.19", @@ -7666,7 +9108,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7787,10 +9228,10 @@ "node": ">= 0.8.0" } }, - "node_modules/orderedmap": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", - "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "node_modules/outvariant": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz", + "integrity": "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==", "license": "MIT" }, "node_modules/p-limit": { @@ -7845,6 +9286,31 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/parse5": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", @@ -7950,6 +9416,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -8018,6 +9485,7 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -8210,181 +9678,31 @@ "node": ">=6" } }, - "node_modules/prosemirror-changeset": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz", - "integrity": "sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==", - "license": "MIT", - "dependencies": { - "prosemirror-transform": "^1.0.0" - } - }, - "node_modules/prosemirror-commands": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", - "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", - "license": "MIT", - "dependencies": { - "prosemirror-model": "^1.0.0", - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.10.2" - } - }, - "node_modules/prosemirror-dropcursor": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", - "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", - "license": "MIT", - "dependencies": { - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.1.0", - "prosemirror-view": "^1.1.0" - } - }, - "node_modules/prosemirror-gapcursor": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz", - "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==", - "license": "MIT", - "dependencies": { - "prosemirror-keymap": "^1.0.0", - "prosemirror-model": "^1.0.0", - "prosemirror-state": "^1.0.0", - "prosemirror-view": "^1.0.0" - } - }, - "node_modules/prosemirror-history": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz", - "integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==", - "license": "MIT", - "dependencies": { - "prosemirror-state": "^1.2.2", - "prosemirror-transform": "^1.0.0", - "prosemirror-view": "^1.31.0", - "rope-sequence": "^1.3.0" - } - }, - "node_modules/prosemirror-inputrules": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.0.tgz", - "integrity": "sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==", - "license": "MIT", - "dependencies": { - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.0.0" - } - }, - "node_modules/prosemirror-keymap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", - "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", - "license": "MIT", - "dependencies": { - "prosemirror-state": "^1.0.0", - "w3c-keyname": "^2.2.0" - } - }, - "node_modules/prosemirror-model": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.1.tgz", - "integrity": "sha512-AUvbm7qqmpZa5d9fPKMvH1Q5bqYQvAZWOGRvxsB6iFLyycvC9MwNemNVjHVrWgjaoxAfY8XVg7DbvQ/qxvI9Eg==", - "license": "MIT", - "dependencies": { - "orderedmap": "^2.0.0" - } - }, - "node_modules/prosemirror-safari-ime-span": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/prosemirror-safari-ime-span/-/prosemirror-safari-ime-span-1.0.2.tgz", - "integrity": "sha512-QJqD8s1zE/CuK56kDsUhndh5hiHh/gFnAuPOA9ytva2s85/ZEt2tNWeALTJN48DtWghSKOmiBsvVn2OlnJ5H2w==", - "license": "MIT", - "dependencies": { - "prosemirror-state": "^1.4.3", - "prosemirror-view": "^1.33.8" - }, - "funding": { - "url": "https://github.com/sponsors/ocavue" - } - }, - "node_modules/prosemirror-schema-list": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz", - "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==", - "license": "MIT", - "dependencies": { - "prosemirror-model": "^1.0.0", - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.7.3" - } - }, - "node_modules/prosemirror-state": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz", - "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==", - "license": "MIT", - "dependencies": { - "prosemirror-model": "^1.0.0", - "prosemirror-transform": "^1.0.0", - "prosemirror-view": "^1.27.0" - } - }, - "node_modules/prosemirror-tables": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.7.1.tgz", - "integrity": "sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q==", - "license": "MIT", - "dependencies": { - "prosemirror-keymap": "^1.2.2", - "prosemirror-model": "^1.25.0", - "prosemirror-state": "^1.4.3", - "prosemirror-transform": "^1.10.3", - "prosemirror-view": "^1.39.1" - } - }, - "node_modules/prosemirror-transform": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.4.tgz", - "integrity": "sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==", + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", "dependencies": { - "prosemirror-model": "^1.21.0" + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" } }, - "node_modules/prosemirror-view": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.40.0.tgz", - "integrity": "sha512-2G3svX0Cr1sJjkD/DYWSe3cfV5VPVTBOxI9XQEGWJDFEpsZb/gh4MV29ctv+OJx2RFX4BLt09i+6zaGM/ldkCw==", - "license": "MIT", - "dependencies": { - "prosemirror-model": "^1.20.0", - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.1.0" - } + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" }, - "node_modules/prosemirror-virtual-cursor": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/prosemirror-virtual-cursor/-/prosemirror-virtual-cursor-0.4.2.tgz", - "integrity": "sha512-pUMKnIuOhhnMcgIJUjhIQTVJruBEGxfMBVQSrK0g2qhGPDm1i12KdsVaFw15dYk+29tZcxjMeR7P5VDKwmbwJg==", + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", "license": "MIT", "funding": { - "url": "https://github.com/sponsors/ocavue" - }, - "peerDependencies": { - "prosemirror-model": "^1.0.0", - "prosemirror-state": "^1.0.0", - "prosemirror-view": "^1.0.0" - }, - "peerDependenciesMeta": { - "prosemirror-model": { - "optional": true - }, - "prosemirror-state": { - "optional": true - }, - "prosemirror-view": { - "optional": true - } + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, "node_modules/psl": { @@ -8450,6 +9768,15 @@ "node": ">=0.10.0" } }, + "node_modules/react-devtools-inline": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/react-devtools-inline/-/react-devtools-inline-4.4.0.tgz", + "integrity": "sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==", + "license": "MIT", + "dependencies": { + "es6-symbol": "^3" + } + }, "node_modules/react-dnd": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", @@ -8502,13 +9829,71 @@ "react": "^18.3.1" } }, + "node_modules/react-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.62.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", + "integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, "license": "MIT" }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -8516,7 +9901,54 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/react-router": { @@ -8551,6 +9983,28 @@ "react-dom": ">=16.8" } }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -8618,71 +10072,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/remark": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", - "integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-inline-links": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/remark-inline-links/-/remark-inline-links-7.0.0.tgz", - "integrity": "sha512-4uj1pPM+F495ySZhTIB6ay2oSkTsKgmYaKk/q5HIdhX2fuyLEegpjWa0VdJRJ01sgOqAFo7MBKdDUejIYBMVMQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-definitions": "^6.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-math": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", - "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-math": "^3.0.0", - "micromark-extension-math": "^3.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -8699,15 +10088,17 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", "license": "MIT", "dependencies": { + "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "opencollective", @@ -8820,12 +10211,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/rope-sequence": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", - "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", - "license": "MIT" - }, "node_modules/rrweb-cssom": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", @@ -8857,6 +10242,18 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", @@ -9099,11 +10496,22 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -9111,6 +10519,18 @@ "dev": true, "license": "MIT" }, + "node_modules/static-browser-server": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/static-browser-server/-/static-browser-server-1.0.3.tgz", + "integrity": "sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==", + "license": "Apache-2.0", + "dependencies": { + "@open-draft/deferred-promise": "^2.1.0", + "dotenv": "^16.0.3", + "mime-db": "^1.52.0", + "outvariant": "^1.3.0" + } + }, "node_modules/std-env": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", @@ -9132,6 +10552,12 @@ "node": ">= 0.4" } }, + "node_modules/strict-event-emitter": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", + "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==", + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -9202,6 +10628,20 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -9294,6 +10734,24 @@ "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", "license": "MIT" }, + "node_modules/style-to-js": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz", + "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.9" + } + }, + "node_modules/style-to-object": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", + "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -9397,6 +10855,12 @@ "dev": true, "license": "MIT" }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, "node_modules/tailwind-merge": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.0.tgz", @@ -9569,6 +11033,16 @@ "node": ">=18" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/trough": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", @@ -9579,6 +11053,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -9643,28 +11130,11 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" }, "node_modules/type-check": { "version": "0.4.0", @@ -9706,7 +11176,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -9730,6 +11200,24 @@ "devOptional": true, "license": "MIT" }, + "node_modules/unidiff": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unidiff/-/unidiff-1.0.4.tgz", + "integrity": "sha512-ynU0vsAXw0ir8roa+xPCUHmnJ5goc5BTM2Kuc3IJd8UwgaeRs7VSD5+eeaQL+xp1JtB92hu/Zy/Lgy7RZcr1pQ==", + "license": "MIT", + "dependencies": { + "diff": "^5.1.0" + } + }, + "node_modules/unidiff/node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", @@ -9762,14 +11250,26 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-remove-position": { + "node_modules/unist-util-position": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", @@ -9880,13 +11380,47 @@ "requires-port": "^1.0.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/util-deprecate": { @@ -9896,6 +11430,33 @@ "dev": true, "license": "MIT" }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uvu/node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -10080,27 +11641,6 @@ } } }, - "node_modules/vue": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz", - "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.18", - "@vue/compiler-sfc": "3.5.18", - "@vue/runtime-dom": "3.5.18", - "@vue/server-renderer": "3.5.18", - "@vue/shared": "3.5.18" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", @@ -10438,6 +11978,24 @@ "node": ">= 14.6" } }, + "node_modules/yjs": { + "version": "13.6.27", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz", + "integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==", + "license": "MIT", + "peer": true, + "dependencies": { + "lib0": "^0.2.99" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -10470,34 +12028,6 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zustand": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", - "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", - "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.2.2" - }, - "engines": { - "node": ">=12.7.0" - }, - "peerDependencies": { - "@types/react": ">=16.8", - "immer": ">=9.0.6", - "react": ">=16.8" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - } - } - }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/archon-ui-main/package.json b/archon-ui-main/package.json index 1f5a91c8a6..336132fee2 100644 --- a/archon-ui-main/package.json +++ b/archon-ui-main/package.json @@ -7,6 +7,14 @@ "dev": "npx vite", "build": "npx vite build", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", + "lint:files": "eslint --ext .js,.jsx,.ts,.tsx", + "biome": "biome check", + "biome:fix": "biome check --write", + "biome:format": "biome format --write", + "biome:lint": "biome lint", + "biome:ai": "biome check --reporter=json", + "biome:ai-fix": "biome check --write --reporter=json", + "biome:ci": "biome ci", "preview": "npx vite preview", "test": "vitest", "test:ui": "vitest --ui", @@ -18,11 +26,17 @@ "seed:projects": "node --loader ts-node/esm ../scripts/seed-project-data.ts" }, "dependencies": { - "@milkdown/crepe": "^7.5.0", - "@milkdown/kit": "^7.5.0", - "@milkdown/plugin-history": "^7.5.0", - "@milkdown/preset-commonmark": "^7.5.0", - "@xyflow/react": "^12.3.0", + "@mdxeditor/editor": "^3.42.0", + "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-toast": "^1.2.15", + "@radix-ui/react-tooltip": "^1.2.8", + "@tanstack/react-query": "^5.85.8", + "@tanstack/react-query-devtools": "^5.85.8", "clsx": "latest", "date-fns": "^4.1.0", "fractional-indexing": "^3.2.0", @@ -33,24 +47,26 @@ "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.3.1", + "react-markdown": "^10.1.0", "react-router-dom": "^6.26.2", "tailwind-merge": "latest", "zod": "^3.25.46" }, "devDependencies": { + "@biomejs/biome": "2.2.2", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^14.3.1", "@testing-library/user-event": "^14.5.2", "@types/node": "^20.19.0", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^5.54.0", - "@typescript-eslint/parser": "^5.54.0", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react": "^4.2.1", "@vitest/coverage-v8": "^1.6.0", "@vitest/ui": "^1.6.0", "autoprefixer": "latest", - "eslint": "^8.50.0", + "eslint": "^8.57.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.1", "jsdom": "^24.1.0", diff --git a/archon-ui-main/src/App.tsx b/archon-ui-main/src/App.tsx index 427347cbcc..2a0cdc22f1 100644 --- a/archon-ui-main/src/App.tsx +++ b/archon-ui-main/src/App.tsx @@ -1,13 +1,17 @@ import { useState, useEffect } from 'react'; import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { KnowledgeBasePage } from './pages/KnowledgeBasePage'; import { SettingsPage } from './pages/SettingsPage'; import { MCPPage } from './pages/MCPPage'; import { OnboardingPage } from './pages/OnboardingPage'; -import { MainLayout } from './components/layouts/MainLayout'; +import { MainLayout } from './components/layout/MainLayout'; import { ThemeProvider } from './contexts/ThemeContext'; import { ToastProvider } from './contexts/ToastContext'; +import { ToastProvider as FeaturesToastProvider } from './features/ui/components/ToastProvider'; import { SettingsProvider, useSettings } from './contexts/SettingsContext'; +import { TooltipProvider } from './features/ui/primitives/tooltip'; import { ProjectPage } from './pages/ProjectPage'; import { DisconnectScreenOverlay } from './components/DisconnectScreenOverlay'; import { ErrorBoundaryWithBugReport } from './components/bug-report/ErrorBoundaryWithBugReport'; @@ -15,6 +19,28 @@ import { MigrationBanner } from './components/ui/MigrationBanner'; import { serverHealthService } from './services/serverHealthService'; import { useMigrationStatus } from './hooks/useMigrationStatus'; +// Create a client with optimized settings for our polling use case +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // Keep data fresh for 2 seconds by default + staleTime: 2000, + // Cache data for 5 minutes + gcTime: 5 * 60 * 1000, + // Retry failed requests 3 times + retry: 3, + // Refetch on window focus + refetchOnWindowFocus: true, + // Don't refetch on reconnect by default (we handle this manually) + refetchOnReconnect: false, + }, + mutations: { + // Retry mutations once on failure + retry: 1, + }, + }, +}); + const AppRoutes = () => { const { projectsEnabled } = useSettings(); @@ -105,12 +131,21 @@ const AppContent = () => { export function App() { return ( - - - - - - - + + + + + + + + + + + + + {import.meta.env.VITE_SHOW_DEVTOOLS === 'true' && ( + + )} + ); } \ No newline at end of file diff --git a/archon-ui-main/src/components/layouts/ArchonChatPanel.tsx b/archon-ui-main/src/components/agent-chat/ArchonChatPanel.tsx similarity index 100% rename from archon-ui-main/src/components/layouts/ArchonChatPanel.tsx rename to archon-ui-main/src/components/agent-chat/ArchonChatPanel.tsx diff --git a/archon-ui-main/src/components/knowledge-base/CrawlingProgressCard.tsx b/archon-ui-main/src/components/knowledge-base/CrawlingProgressCard.tsx index 0f39158101..f5eeb5aa71 100644 --- a/archon-ui-main/src/components/knowledge-base/CrawlingProgressCard.tsx +++ b/archon-ui-main/src/components/knowledge-base/CrawlingProgressCard.tsx @@ -26,7 +26,7 @@ import { Card } from '../ui/Card'; import { Button } from '../ui/Button'; import { Badge } from '../ui/Badge'; import { CrawlProgressData } from '../../types/crawl'; -import { useCrawlProgressPolling } from '../../hooks/usePolling'; +import { useCrawlProgressPolling } from '../../hooks/useCrawlQueries'; import { useTerminalScroll } from '../../hooks/useTerminalScroll'; interface CrawlingProgressCardProps { diff --git a/archon-ui-main/src/components/layout/MainLayout.tsx b/archon-ui-main/src/components/layout/MainLayout.tsx new file mode 100644 index 0000000000..da0b26964b --- /dev/null +++ b/archon-ui-main/src/components/layout/MainLayout.tsx @@ -0,0 +1,193 @@ +import { AlertCircle, WifiOff } from "lucide-react"; +import type React from "react"; +import { useEffect } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import { useToast } from "../../features/ui/hooks/useToast"; +import { cn } from "../../lib/utils"; +import { credentialsService } from "../../services/credentialsService"; +import { isLmConfigured } from "../../utils/onboarding"; + +// TEMPORARY: Import from old components until they're migrated to features +import { BackendStartupError } from "../BackendStartupError"; +import { useBackendHealth } from "./hooks/useBackendHealth"; +import { Navigation } from "./Navigation"; + +interface MainLayoutProps { + children: React.ReactNode; + className?: string; +} + +interface BackendStatusProps { + isHealthLoading: boolean; + isBackendError: boolean; + healthData: { ready: boolean } | undefined; +} + +/** + * Backend health indicator component + */ +function BackendStatus({ isHealthLoading, isBackendError, healthData }: BackendStatusProps) { + if (isHealthLoading) { + return ( +
+
+ Connecting... +
+ ); + } + + if (isBackendError) { + return ( +
+ + Backend Offline +
+ ); + } + + if (healthData?.ready === false) { + return ( +
+ + Backend Starting... +
+ ); + } + + return null; +} + +/** + * Modern main layout using TanStack Query and Radix UI patterns + * Uses CSS Grid for layout instead of fixed positioning + */ +export function MainLayout({ children, className }: MainLayoutProps) { + const navigate = useNavigate(); + const location = useLocation(); + const { showToast } = useToast(); + + // Backend health monitoring with TanStack Query + const { + data: healthData, + isError: isBackendError, + error: backendError, + isLoading: isHealthLoading, + failureCount, + } = useBackendHealth(); + + // Track if backend has completely failed (for showing BackendStartupError) + const backendStartupFailed = isBackendError && failureCount >= 5; + + // TEMPORARY: Handle onboarding redirect using old logic until migrated + useEffect(() => { + const checkOnboarding = async () => { + // Skip if backend failed to start + if (backendStartupFailed) { + return; + } + + // Skip if not ready, already on onboarding, or already dismissed + if (!healthData?.ready || location.pathname === "/onboarding") { + return; + } + + // Check if onboarding was already dismissed + if (localStorage.getItem("onboardingDismissed") === "true") { + return; + } + + try { + // Fetch credentials in parallel (using old service temporarily) + const [ragCreds, apiKeyCreds] = await Promise.all([ + credentialsService.getCredentialsByCategory("rag_strategy"), + credentialsService.getCredentialsByCategory("api_keys"), + ]); + + // Check if LM is configured (using old utility temporarily) + const configured = isLmConfigured(ragCreds, apiKeyCreds); + + if (!configured) { + // Redirect to onboarding + navigate("/onboarding", { replace: true }); + } + } catch (error) { + // Log error but don't block app + console.error("ONBOARDING_CHECK_FAILED:", error); + showToast(`Configuration check failed. You can manually configure in Settings.`, "warning"); + } + }; + + checkOnboarding(); + }, [healthData?.ready, backendStartupFailed, location.pathname, navigate, showToast]); + + // Show backend error toast (once) + useEffect(() => { + if (isBackendError && backendError) { + const errorMessage = backendError instanceof Error ? backendError.message : "Backend connection failed"; + showToast(`Backend unavailable: ${errorMessage}. Some features may not work.`, "error"); + } + }, [isBackendError, backendError, showToast]); + + return ( +
+ {/* TEMPORARY: Show backend startup error using old component */} + {backendStartupFailed && } + + {/* Fixed full-page background grid that doesn't scroll */} +
+ + {/* Floating Navigation */} +
+ + +
+ + {/* Main Content Area - matches old layout exactly */} +
+
+
{children}
+
+
+ + {/* TEMPORARY: Floating Chat Button (disabled) - from old layout */} +
+ + {/* Tooltip */} +
+
Coming Soon
+
Knowledge Assistant is under development
+
+
+
+
+ ); +} + +/** + * Layout variant without navigation for special pages + */ +export function MinimalLayout({ children, className }: MainLayoutProps) { + return ( +
+ {/* Background Grid Effect */} +
+ + {/* Centered Content */} +
{children}
+
+ ); +} diff --git a/archon-ui-main/src/components/layout/Navigation.tsx b/archon-ui-main/src/components/layout/Navigation.tsx new file mode 100644 index 0000000000..e2f1e80676 --- /dev/null +++ b/archon-ui-main/src/components/layout/Navigation.tsx @@ -0,0 +1,178 @@ +import { BookOpen, Settings } from "lucide-react"; +import type React from "react"; +import { Link, useLocation } from "react-router-dom"; +// TEMPORARY: Use old SettingsContext until settings are migrated +import { useSettings } from "../../contexts/SettingsContext"; +import { glassmorphism } from "../../features/ui/primitives/styles"; +import { Tooltip, TooltipContent, TooltipTrigger } from "../../features/ui/primitives/tooltip"; +import { cn } from "../../lib/utils"; + +interface NavigationItem { + path: string; + icon: React.ReactNode; + label: string; + enabled?: boolean; +} + +interface NavigationProps { + className?: string; +} + +/** + * Modern navigation component using Radix UI patterns + * No fixed positioning - parent controls layout + */ +export function Navigation({ className }: NavigationProps) { + const location = useLocation(); + const { projectsEnabled } = useSettings(); + + // Navigation items configuration + const navigationItems: NavigationItem[] = [ + { + path: "/", + icon: , + label: "Knowledge Base", + enabled: true, + }, + { + path: "/mcp", + icon: ( + + + + + ), + label: "MCP Server", + enabled: true, + }, + { + path: "/settings", + icon: , + label: "Settings", + enabled: true, + }, + ]; + + const isProjectsActive = location.pathname.startsWith("/projects"); + + return ( + + ); +} diff --git a/archon-ui-main/src/components/layout/hooks/useBackendHealth.ts b/archon-ui-main/src/components/layout/hooks/useBackendHealth.ts new file mode 100644 index 0000000000..de0b8aa049 --- /dev/null +++ b/archon-ui-main/src/components/layout/hooks/useBackendHealth.ts @@ -0,0 +1,64 @@ +import { useQuery } from "@tanstack/react-query"; +import { getApiUrl } from "../../../config/api"; + +interface HealthResponse { + ready: boolean; + message?: string; + server_status?: string; + credentials_status?: string; + database_status?: string; + uptime?: number; +} + +/** + * Hook to monitor backend health status using TanStack Query + * Replaces the direct fetch polling in old MainLayout + */ +export function useBackendHealth() { + return useQuery({ + queryKey: ["backend", "health"], + queryFn: async () => { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 5000); + + try { + const response = await fetch(`${getApiUrl()}/api/health`, { + method: "GET", + signal: controller.signal, + }); + + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`Health check failed: ${response.status}`); + } + + return response.json(); + } catch (error) { + clearTimeout(timeoutId); + if (error instanceof Error && error.name === "AbortError") { + throw new Error("Health check timeout (5s)"); + } + throw error; + } + }, + // Retry configuration for startup scenarios + retry: (failureCount) => { + // Keep retrying during startup, up to 5 times + if (failureCount < 5) { + return true; + } + return false; + }, + retryDelay: (attemptIndex) => { + // Exponential backoff: 1.5s, 2.25s, 3.375s, etc. + return Math.min(1500 * 1.5 ** attemptIndex, 10000); + }, + // Refetch every 30 seconds when healthy + refetchInterval: 30000, + // Keep trying to connect on window focus + refetchOnWindowFocus: true, + // Consider data fresh for 20 seconds + staleTime: 20000, + }); +} diff --git a/archon-ui-main/src/components/layout/index.ts b/archon-ui-main/src/components/layout/index.ts new file mode 100644 index 0000000000..10736d9b0a --- /dev/null +++ b/archon-ui-main/src/components/layout/index.ts @@ -0,0 +1,3 @@ +export { useBackendHealth } from "./hooks/useBackendHealth"; +export { MainLayout, MinimalLayout } from "./MainLayout"; +export { Navigation } from "./Navigation"; diff --git a/archon-ui-main/src/components/layout/types.ts b/archon-ui-main/src/components/layout/types.ts new file mode 100644 index 0000000000..38874bba18 --- /dev/null +++ b/archon-ui-main/src/components/layout/types.ts @@ -0,0 +1,26 @@ +export interface NavigationItem { + path: string; + icon: React.ReactNode; + label: string; + enabled?: boolean; +} + +export interface HealthResponse { + ready: boolean; + message?: string; + server_status?: string; + credentials_status?: string; + database_status?: string; + uptime?: number; +} + +export interface AppSettings { + projectsEnabled: boolean; + theme?: "light" | "dark" | "system"; + // Add other settings as needed +} + +export interface OnboardingCheckResult { + shouldShowOnboarding: boolean; + reason: "dismissed" | "missing_rag" | "missing_api_key" | null; +} diff --git a/archon-ui-main/src/components/layouts/MainLayout.tsx b/archon-ui-main/src/components/layouts/MainLayout.tsx deleted file mode 100644 index acc9188a0e..0000000000 --- a/archon-ui-main/src/components/layouts/MainLayout.tsx +++ /dev/null @@ -1,215 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useNavigate, useLocation } from 'react-router-dom'; -import { SideNavigation } from './SideNavigation'; -import { ArchonChatPanel } from './ArchonChatPanel'; -import { X } from 'lucide-react'; -import { useToast } from '../../contexts/ToastContext'; -import { credentialsService } from '../../services/credentialsService'; -import { isLmConfigured } from '../../utils/onboarding'; -import { BackendStartupError } from '../BackendStartupError'; -/** - * Props for the MainLayout component - */ -interface MainLayoutProps { - children: React.ReactNode; -} -/** - * MainLayout - The main layout component for the application - * - * This component provides the overall layout structure including: - * - Side navigation - * - Main content area - * - Knowledge chat panel (slidable) - */ -export const MainLayout: React.FC = ({ - children -}) => { - // State to track if chat panel is open - const [isChatOpen, setIsChatOpen] = useState(false); - const { showToast } = useToast(); - const navigate = useNavigate(); - const location = useLocation(); - const [backendReady, setBackendReady] = useState(false); - const [backendStartupFailed, setBackendStartupFailed] = useState(false); - - // Check backend readiness - useEffect(() => { - - const checkBackendHealth = async (retryCount = 0) => { - const maxRetries = 3; // 3 retries total - const retryDelay = 1500; // 1.5 seconds between retries - - try { - // Create AbortController for proper timeout handling - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 5000); - - // Check if backend is responding with a simple health check - const response = await fetch(`${credentialsService['baseUrl']}/api/health`, { - method: 'GET', - signal: controller.signal - }); - - clearTimeout(timeoutId); - - if (response.ok) { - const healthData = await response.json(); - console.log('📋 Backend health check:', healthData); - - // Check if backend is truly ready (not just started) - if (healthData.ready === true) { - console.log('✅ Backend is fully initialized'); - setBackendReady(true); - setBackendStartupFailed(false); - } else { - // Backend is starting up but not ready yet - console.log(`🔄 Backend initializing... (attempt ${retryCount + 1}/${maxRetries}):`, healthData.message || 'Loading credentials...'); - - // Retry with shorter interval during initialization - if (retryCount < maxRetries) { - setTimeout(() => { - checkBackendHealth(retryCount + 1); - }, retryDelay); // Constant 1.5s retry during initialization - } else { - console.warn('Backend initialization taking too long - proceeding anyway'); - // Don't mark as failed yet, just not fully ready - setBackendReady(false); - } - } - } else { - throw new Error(`Backend health check failed: ${response.status}`); - } - } catch (error) { - // Handle AbortError separately for timeout - const errorMessage = error instanceof Error - ? (error.name === 'AbortError' ? 'Request timeout (5s)' : error.message) - : 'Unknown error'; - // Only log after first attempt to reduce noise during normal startup - if (retryCount > 0) { - console.log(`Backend not ready yet (attempt ${retryCount + 1}/${maxRetries}):`, errorMessage); - } - - // Retry if we haven't exceeded max retries - if (retryCount < maxRetries) { - setTimeout(() => { - checkBackendHealth(retryCount + 1); - }, retryDelay * Math.pow(1.5, retryCount)); // Exponential backoff for connection errors - } else { - console.error('Backend startup failed after maximum retries - showing error message'); - setBackendReady(false); - setBackendStartupFailed(true); - } - } - }; - - - // Start the health check process - setTimeout(() => { - checkBackendHealth(); - }, 1000); // Wait 1 second for initial app startup - }, []); // Empty deps - only run once on mount - - // Check for onboarding redirect after backend is ready - useEffect(() => { - const checkOnboarding = async () => { - // Skip if backend failed to start - if (backendStartupFailed) { - return; - } - - // Skip if not ready, already on onboarding, or already dismissed - if (!backendReady || location.pathname === '/onboarding') { - return; - } - - // Check if onboarding was already dismissed - if (localStorage.getItem('onboardingDismissed') === 'true') { - return; - } - - try { - // Fetch credentials in parallel - const [ragCreds, apiKeyCreds] = await Promise.all([ - credentialsService.getCredentialsByCategory('rag_strategy'), - credentialsService.getCredentialsByCategory('api_keys') - ]); - - // Check if LM is configured - const configured = isLmConfigured(ragCreds, apiKeyCreds); - - if (!configured) { - // Redirect to onboarding - navigate('/onboarding', { replace: true }); - } - } catch (error) { - // Detailed error handling per alpha principles - fail loud but don't block - const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - const errorDetails = { - context: 'Onboarding configuration check', - pathname: location.pathname, - error: errorMessage, - timestamp: new Date().toISOString() - }; - - // Log with full context and stack trace - console.error('ONBOARDING_CHECK_FAILED:', errorDetails, error); - - // Make error visible to user but don't block app functionality - showToast( - `Configuration check failed: ${errorMessage}. You can manually configure in Settings.`, - 'warning' - ); - - // Let user continue - onboarding is optional, they can configure manually - } - }; - - checkOnboarding(); - }, [backendReady, backendStartupFailed, location.pathname, navigate, showToast]); - - return
- {/* Show backend startup error if backend failed to start */} - {backendStartupFailed && } - - {/* Fixed full-page background grid that doesn't scroll */} -
- {/* Floating Navigation */} -
- -
- {/* Main Content Area - no left margin to allow grid to extend full width */} -
-
-
{children}
-
-
- {/* Floating Chat Button - Only visible when chat is closed */} - {!isChatOpen && ( -
- - {/* Tooltip */} -
-
Coming Soon
-
Knowledge Assistant is under development
-
-
-
- )} - {/* Chat Sidebar - Slides in/out from right */} -
- {/* Close button - Only visible when chat is open */} - {isChatOpen && } - {/* Knowledge Chat Panel */} - -
-
; -}; diff --git a/archon-ui-main/src/components/layouts/SideNavigation.tsx b/archon-ui-main/src/components/layouts/SideNavigation.tsx deleted file mode 100644 index f4165032bf..0000000000 --- a/archon-ui-main/src/components/layouts/SideNavigation.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useState } from 'react'; -import { Link, useLocation } from 'react-router-dom'; -import { BookOpen, HardDrive, Settings } from 'lucide-react'; -import { useSettings } from '../../contexts/SettingsContext'; -/** - * Interface for navigation items - */ -export interface NavigationItem { - path: string; - icon: React.ReactNode; - label: string; -} -/** - * Props for the SideNavigation component - */ -interface SideNavigationProps { - className?: string; - 'data-id'?: string; -} -/** - * Tooltip component for navigation items - */ -const NavTooltip: React.FC<{ - show: boolean; - label: string; - position?: 'left' | 'right'; -}> = ({ - show, - label, - position = 'right' -}) => { - if (!show) return null; - return
- {label} -
-
; -}; -/** - * SideNavigation - A vertical navigation component - * - * This component renders a navigation sidebar with icons and the application logo. - * It highlights the active route and provides hover effects. - */ -export const SideNavigation: React.FC = ({ - className = '', - 'data-id': dataId -}) => { - // State to track which tooltip is currently visible - const [activeTooltip, setActiveTooltip] = useState(null); - const { projectsEnabled } = useSettings(); - - // Default navigation items - const navigationItems: NavigationItem[] = [{ - path: '/', - icon: , - label: 'Knowledge Base' - }, { - path: '/mcp', - icon: , - label: 'MCP Server' - }, { - path: '/settings', - icon: , - label: 'Settings' - }]; - // Logo configuration - const logoSrc = "/logo-neon.png"; - const logoAlt = 'Knowledge Base Logo'; - // Get current location to determine active route - const location = useLocation(); - const isProjectsActive = location.pathname === '/projects' && projectsEnabled; - - const logoClassName = ` - logo-container p-2 relative rounded-lg transition-all duration-300 - ${isProjectsActive ? 'bg-gradient-to-b from-white/20 to-white/5 dark:from-white/10 dark:to-black/20 shadow-[0_5px_15px_-5px_rgba(59,130,246,0.3)] dark:shadow-[0_5px_15px_-5px_rgba(59,130,246,0.5)] transform scale-110' : ''} - ${projectsEnabled ? 'hover:bg-white/10 dark:hover:bg-white/5 cursor-pointer' : 'opacity-50 cursor-not-allowed'} - `; - - return
- {/* Logo - Conditionally clickable based on Projects enabled */} - {projectsEnabled ? ( - setActiveTooltip('logo')} - onMouseLeave={() => setActiveTooltip(null)} - > - {logoAlt} - {/* Active state decorations */} - {isProjectsActive && <> - - - } - - - ) : ( -
setActiveTooltip('logo')} - onMouseLeave={() => setActiveTooltip(null)} - > - {logoAlt} - -
- )} - {/* Navigation links */} - -
; -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/mcp/ClientCard.tsx b/archon-ui-main/src/components/mcp/ClientCard.tsx deleted file mode 100644 index 6ddbd059bb..0000000000 --- a/archon-ui-main/src/components/mcp/ClientCard.tsx +++ /dev/null @@ -1,508 +0,0 @@ -import React, { useEffect, useState, useRef } from 'react'; -import { Server, Activity, Clock, ChevronRight, Hammer, Settings, Trash2, Plug, PlugZap } from 'lucide-react'; -import { Client } from './MCPClients'; -import { mcpClientService } from '../../services/mcpClientService'; -import { useToast } from '../../contexts/ToastContext'; - -interface ClientCardProps { - client: Client; - onSelect: () => void; - onEdit?: (client: Client) => void; - onDelete?: (client: Client) => void; - onConnectionChange?: () => void; -} - -export const ClientCard = ({ - client, - onSelect, - onEdit, - onDelete, - onConnectionChange -}: ClientCardProps) => { - const [isFlipped, setIsFlipped] = useState(false); - const [isHovered, setIsHovered] = useState(false); - const [isConnecting, setIsConnecting] = useState(false); - const particlesRef = useRef(null); - const { showToast } = useToast(); - - // Special styling for Archon client - const isArchonClient = client.name.includes('Archon') || client.name.includes('archon'); - - // Status-based styling - const statusConfig = { - online: { - color: isArchonClient ? 'archon' : 'cyan', - glow: isArchonClient ? 'shadow-[0_0_25px_rgba(59,130,246,0.7),0_0_15px_rgba(168,85,247,0.5)] dark:shadow-[0_0_35px_rgba(59,130,246,0.8),0_0_20px_rgba(168,85,247,0.7)]' : 'shadow-[0_0_15px_rgba(34,211,238,0.5)] dark:shadow-[0_0_20px_rgba(34,211,238,0.7)]', - border: isArchonClient ? 'border-blue-400/60 dark:border-blue-500/60' : 'border-cyan-400/50 dark:border-cyan-500/40', - badge: isArchonClient ? 'bg-blue-500/30 text-blue-400 border-blue-500/40' : 'bg-cyan-500/20 text-cyan-400 border-cyan-500/30', - pulse: isArchonClient ? 'bg-blue-400' : 'bg-cyan-400' - }, - offline: { - color: 'gray', - glow: 'shadow-[0_0_15px_rgba(156,163,175,0.3)] dark:shadow-[0_0_15px_rgba(156,163,175,0.4)]', - border: 'border-gray-400/30 dark:border-gray-600/30', - badge: 'bg-gray-500/20 text-gray-400 border-gray-500/30', - pulse: 'bg-gray-400' - }, - error: { - color: 'pink', - glow: 'shadow-[0_0_15px_rgba(236,72,153,0.5)] dark:shadow-[0_0_20px_rgba(236,72,153,0.7)]', - border: 'border-pink-400/50 dark:border-pink-500/40', - badge: 'bg-pink-500/20 text-pink-400 border-pink-500/30', - pulse: 'bg-pink-400' - } - }; - - // Handle mouse movement for bioluminescent effect - useEffect(() => { - if (!isArchonClient || !particlesRef.current) return; - - const currentMousePos = { x: 0, y: 0 }; - const glowOrganisms: HTMLDivElement[] = []; - let isMousePresent = false; - - const createBioluminescentOrganism = (targetX: number, targetY: number, delay = 0) => { - const organism = document.createElement('div'); - organism.className = 'absolute rounded-full pointer-events-none'; - - const startX = targetX + (Math.random() - 0.5) * 100; - const startY = targetY + (Math.random() - 0.5) * 100; - const size = 8 + Math.random() * 12; - - organism.style.left = `${startX}px`; - organism.style.top = `${startY}px`; - organism.style.width = `${size}px`; - organism.style.height = `${size}px`; - organism.style.transform = 'translate(-50%, -50%)'; - organism.style.opacity = '0'; - - const hues = [180, 200, 220, 240, 260, 280]; - const hue = hues[Math.floor(Math.random() * hues.length)]; - - organism.style.background = 'transparent'; - - organism.style.boxShadow = ` - 0 0 ${size * 2}px hsla(${hue}, 90%, 60%, 0.4), - 0 0 ${size * 4}px hsla(${hue}, 80%, 50%, 0.25), - 0 0 ${size * 6}px hsla(${hue}, 70%, 40%, 0.15), - 0 0 ${size * 8}px hsla(${hue}, 60%, 30%, 0.08) - `; - - organism.style.filter = `blur(${2 + Math.random() * 3}px) opacity(0.6)`; - - particlesRef.current?.appendChild(organism); - - setTimeout(() => { - const duration = 1200 + Math.random() * 800; - - organism.style.transition = `all ${duration}ms cubic-bezier(0.2, 0.0, 0.1, 1)`; - organism.style.left = `${targetX + (Math.random() - 0.5) * 50}px`; - organism.style.top = `${targetY + (Math.random() - 0.5) * 50}px`; - organism.style.opacity = '0.8'; - organism.style.transform = 'translate(-50%, -50%) scale(1.2)'; - - setTimeout(() => { - if (!isMousePresent) { - organism.style.transition = `all 2500ms cubic-bezier(0.6, 0.0, 0.9, 1)`; - organism.style.left = `${startX + (Math.random() - 0.5) * 300}px`; - organism.style.top = `${startY + (Math.random() - 0.5) * 300}px`; - organism.style.opacity = '0'; - organism.style.transform = 'translate(-50%, -50%) scale(0.2)'; - organism.style.filter = `blur(${8 + Math.random() * 5}px) opacity(0.2)`; - } - }, duration + 800); - - setTimeout(() => { - if (particlesRef.current?.contains(organism)) { - particlesRef.current.removeChild(organism); - const index = glowOrganisms.indexOf(organism); - if (index > -1) glowOrganisms.splice(index, 1); - } - }, duration + 2000); - - }, delay); - - return organism; - }; - - const spawnOrganismsTowardMouse = () => { - if (!isMousePresent) return; - - const count = 3 + Math.random() * 4; - for (let i = 0; i < count; i++) { - const organism = createBioluminescentOrganism( - currentMousePos.x, - currentMousePos.y, - i * 100 - ); - glowOrganisms.push(organism); - } - }; - - const handleMouseEnter = () => { - isMousePresent = true; - clearInterval(ambientInterval); - ambientInterval = setInterval(createAmbientGlow, 1500); - }; - - const handleMouseMove = (e: MouseEvent) => { - if (!particlesRef.current) return; - - const rect = particlesRef.current.getBoundingClientRect(); - currentMousePos.x = e.clientX - rect.left; - currentMousePos.y = e.clientY - rect.top; - - isMousePresent = true; - - if (Math.random() < 0.4) { - spawnOrganismsTowardMouse(); - } - }; - - const handleMouseLeave = () => { - setTimeout(() => { - isMousePresent = false; - clearInterval(ambientInterval); - }, 800); - }; - - const createAmbientGlow = () => { - if (!particlesRef.current || isMousePresent) return; - - const x = Math.random() * particlesRef.current.clientWidth; - const y = Math.random() * particlesRef.current.clientHeight; - const organism = createBioluminescentOrganism(x, y); - - organism.style.opacity = '0.3'; - organism.style.filter = `blur(${4 + Math.random() * 4}px) opacity(0.4)`; - organism.style.animation = 'pulse 4s ease-in-out infinite'; - organism.style.transform = 'translate(-50%, -50%) scale(0.8)'; - - glowOrganisms.push(organism); - }; - - let ambientInterval = setInterval(createAmbientGlow, 1500); - - const cardElement = particlesRef.current; - cardElement.addEventListener('mouseenter', handleMouseEnter); - cardElement.addEventListener('mousemove', handleMouseMove); - cardElement.addEventListener('mouseleave', handleMouseLeave); - - return () => { - cardElement.removeEventListener('mouseenter', handleMouseEnter); - cardElement.removeEventListener('mousemove', handleMouseMove); - cardElement.removeEventListener('mouseleave', handleMouseLeave); - clearInterval(ambientInterval); - }; - }, [isArchonClient]); - - const currentStatus = statusConfig[client.status]; - - // Handle card flip - const toggleFlip = (e: React.MouseEvent) => { - e.stopPropagation(); - setIsFlipped(!isFlipped); - }; - - // Handle edit - const handleEdit = (e: React.MouseEvent) => { - e.stopPropagation(); - onEdit?.(client); - }; - - // Handle connect/disconnect - const handleConnect = async (e: React.MouseEvent) => { - e.stopPropagation(); - setIsConnecting(true); - - try { - if (client.status === 'offline') { - await mcpClientService.connectClient(client.id); - showToast(`Connected to ${client.name}`, 'success'); - } else { - await mcpClientService.disconnectClient(client.id); - showToast(`Disconnected from ${client.name}`, 'success'); - } - - // The parent component should handle refreshing the client list - // No need to reload the entire page - onConnectionChange?.(); - } catch (error) { - showToast(error instanceof Error ? error.message : 'Connection operation failed', 'error'); - } finally { - setIsConnecting(false); - } - }; - - // Special background for Archon client - const archonBackground = isArchonClient ? 'bg-gradient-to-b from-white/80 via-blue-50/30 to-white/60 dark:from-white/10 dark:via-blue-900/10 dark:to-black/30' : 'bg-gradient-to-b from-white/80 to-white/60 dark:from-white/10 dark:to-black/30'; - - return ( -
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > -
- {/* Front Side */} -
- {/* Particle container for Archon client */} - {isArchonClient && ( -
-
-
- )} - - {/* Subtle aurora glow effect for Archon client */} - {isArchonClient && ( -
-
-
- )} - - {/* Connect/Disconnect button */} - - - {/* Edit button - moved to be second from right */} - {onEdit && ( - - )} - - {/* Delete button - only for non-Archon clients */} - {!isArchonClient && onDelete && ( - - )} - - {/* Client info */} -
- {isArchonClient ? ( -
- Archon -
-
- ) : ( -
- -
- )} - -
-

- {client.name} -

-

- {client.ip} -

-
-
- -
-
- - Last seen: - - {client.lastSeen} - -
-
- - Version: - - {client.version} - -
-
- - Tools: - - {client.tools.length} available - -
- - {/* Error message display */} - {client.status === 'error' && client.lastError && ( -
-
-
-
-

Last Error:

-

- {client.lastError} -

-
-
-
- )} -
- - {/* Status badge - moved to bottom left */} -
-
-
- - -
- {client.status.charAt(0).toUpperCase() + client.status.slice(1)} -
-
- - {/* Tools button - with Hammer icon */} - -
- - {/* Back Side */} -
- {/* Subtle aurora glow effect for Archon client */} - {isArchonClient && ( -
-
-
- )} - - {/* Connect/Disconnect button - also on back side */} - - - {/* Edit button - also on back side */} - {onEdit && ( - - )} - - {/* Delete button on back side - only for non-Archon clients */} - {!isArchonClient && onDelete && ( - - )} - -

- - Available Tools ({client.tools.length}) -

- -
- {client.tools.length === 0 ? ( -
-

- {client.status === 'offline' - ? 'Client offline - tools unavailable' - : 'No tools discovered'} -

-
- ) : ( - client.tools.map(tool => ( -
-
- - {tool.name} - - -
-

- {tool.description} -

- {tool.parameters.length > 0 && ( -

- {tool.parameters.length} parameter{tool.parameters.length !== 1 ? 's' : ''} -

- )} -
- )) - )} -
- - {/* Status badge - also at bottom left on back side */} -
-
-
- - -
- {client.status.charAt(0).toUpperCase() + client.status.slice(1)} -
-
- - {/* Flip button - back to front */} - -
-
-
- ); -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/mcp/MCPClients.tsx b/archon-ui-main/src/components/mcp/MCPClients.tsx deleted file mode 100644 index d780ce62bb..0000000000 --- a/archon-ui-main/src/components/mcp/MCPClients.tsx +++ /dev/null @@ -1,858 +0,0 @@ -import React, { useState, memo, useEffect } from 'react'; -import { Plus, Settings, Trash2, X } from 'lucide-react'; -import { ClientCard } from './ClientCard'; -import { ToolTestingPanel } from './ToolTestingPanel'; -import { Button } from '../ui/Button'; -import { mcpClientService, MCPClient, MCPClientConfig } from '../../services/mcpClientService'; -import { useToast } from '../../contexts/ToastContext'; -import { DeleteConfirmModal } from '../common/DeleteConfirmModal'; - -// Client interface (keeping for backward compatibility) -export interface Client { - id: string; - name: string; - status: 'online' | 'offline' | 'error'; - ip: string; - lastSeen: string; - version: string; - tools: Tool[]; - region?: string; - lastError?: string; -} - -// Tool interface -export interface Tool { - id: string; - name: string; - description: string; - parameters: ToolParameter[]; -} - -// Tool parameter interface -export interface ToolParameter { - name: string; - type: 'string' | 'number' | 'boolean' | 'array'; - required: boolean; - description?: string; -} - -export const MCPClients = memo(() => { - const [clients, setClients] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - // State for selected client and panel visibility - const [selectedClient, setSelectedClient] = useState(null); - const [isPanelOpen, setIsPanelOpen] = useState(false); - const [isAddClientModalOpen, setIsAddClientModalOpen] = useState(false); - - // State for edit drawer - const [editClient, setEditClient] = useState(null); - const [isEditDrawerOpen, setIsEditDrawerOpen] = useState(false); - - const { showToast } = useToast(); - - // State for delete confirmation modal - const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); - const [clientToDelete, setClientToDelete] = useState(null); - - // Load clients when component mounts - useEffect(() => { - loadAllClients(); - - // Set up periodic status checks every 10 seconds - const statusInterval = setInterval(() => { - // Silently refresh client statuses without loading state - refreshClientStatuses(); - }, 10000); - - return () => clearInterval(statusInterval); - }, []); - - /** - * Refresh client statuses without showing loading state - */ - const refreshClientStatuses = async () => { - try { - const dbClients = await mcpClientService.getClients(); - - setClients(prevClients => - prevClients.map(client => { - const dbClient = dbClients.find(db => db.id === client.id); - if (dbClient) { - return { - ...client, - status: dbClient.status === 'connected' ? 'online' : - dbClient.status === 'error' ? 'error' : 'offline', - lastSeen: dbClient.last_seen ? new Date(dbClient.last_seen).toLocaleString() : 'Never', - lastError: dbClient.last_error || undefined - }; - } - return client; - }) - ); - } catch (error) { - console.warn('Failed to refresh client statuses:', error); - } - }; - - /** - * Load all clients: Archon (hardcoded) + real database clients - */ - const loadAllClients = async () => { - try { - setIsLoading(true); - setError(null); - - // Load ALL clients from database (including Archon) - let dbClients: MCPClient[] = []; - try { - dbClients = await mcpClientService.getClients(); - } catch (clientError) { - console.warn('Failed to load database clients:', clientError); - dbClients = []; - } - - // Convert database clients to our Client interface and load their tools - const convertedClients: Client[] = await Promise.all( - dbClients.map(async (dbClient) => { - const client = convertDbClientToClient(dbClient); - // Load tools for connected clients using universal method - if (client.status === 'online') { - await loadTools(client); - } - return client; - }) - ); - - // Set all clients (Archon will be included as a regular client) - setClients(convertedClients); - } catch (error) { - console.error('Failed to load MCP clients:', error); - setError(error instanceof Error ? error.message : 'Failed to load clients'); - setClients([]); - } finally { - setIsLoading(false); - } - }; - - /** - * Convert database MCP client to our Client interface - */ - const convertDbClientToClient = (dbClient: MCPClient): Client => { - // Map database status to our status types - const statusMap: Record = { - 'connected': 'online', - 'disconnected': 'offline', - 'connecting': 'offline', - 'error': 'error' - }; - - // Extract connection info (Streamable HTTP-only) - const config = dbClient.connection_config; - const ip = config.url || 'N/A'; - - return { - id: dbClient.id, - name: dbClient.name, - status: statusMap[dbClient.status] || 'offline', - ip, - lastSeen: dbClient.last_seen ? new Date(dbClient.last_seen).toLocaleString() : 'Never', - version: config.version || 'Unknown', - region: config.region || 'Unknown', - tools: [], // Will be loaded separately - lastError: dbClient.last_error || undefined - }; - }; - - /** - * Load tools from any MCP client using universal client service - */ - const loadTools = async (client: Client) => { - try { - const toolsResponse = await mcpClientService.getClientTools(client.id); - - // Convert client tools to our Tool interface format - const convertedTools: Tool[] = toolsResponse.tools.map((clientTool: any, index: number) => { - const parameters: ToolParameter[] = []; - - // Extract parameters from tool schema - if (clientTool.tool_schema?.inputSchema?.properties) { - const required = clientTool.tool_schema.inputSchema.required || []; - Object.entries(clientTool.tool_schema.inputSchema.properties).forEach(([name, schema]: [string, any]) => { - parameters.push({ - name, - type: schema.type === 'integer' ? 'number' : - schema.type === 'array' ? 'array' : - schema.type === 'boolean' ? 'boolean' : 'string', - required: required.includes(name), - description: schema.description || `${name} parameter` - }); - }); - } - - return { - id: `${client.id}-${index}`, - name: clientTool.tool_name, - description: clientTool.tool_description || 'No description available', - parameters - }; - }); - - client.tools = convertedTools; - console.log(`Loaded ${convertedTools.length} tools for client ${client.name}`); - } catch (error) { - console.error(`Failed to load tools for client ${client.name}:`, error); - client.tools = []; - } - }; - - /** - * Handle adding a new client - */ - const handleAddClient = async (clientConfig: MCPClientConfig) => { - try { - // Create client in database - const newClient = await mcpClientService.createClient(clientConfig); - - // Convert and add to local state - const convertedClient = convertDbClientToClient(newClient); - - // Try to load tools if client is connected - if (convertedClient.status === 'online') { - await loadTools(convertedClient); - } - - setClients(prev => [...prev, convertedClient]); - - // Close modal - setIsAddClientModalOpen(false); - - console.log('Client added successfully:', newClient.name); - } catch (error) { - console.error('Failed to add client:', error); - setError(error instanceof Error ? error.message : 'Failed to add client'); - throw error; // Re-throw so modal can handle it - } - }; - - // Handle client selection - const handleSelectClient = async (client: Client) => { - setSelectedClient(client); - setIsPanelOpen(true); - - // Refresh tools for the selected client if needed - if (client.tools.length === 0 && client.status === 'online') { - await loadTools(client); - - // Update the client in the list - setClients(prev => prev.map(c => c.id === client.id ? client : c)); - } - }; - - // Handle client editing - const handleEditClient = (client: Client) => { - setEditClient(client); - setIsEditDrawerOpen(true); - }; - - // Handle client deletion (triggers confirmation modal) - const handleDeleteClient = (client: Client) => { - setClientToDelete(client); - setShowDeleteConfirm(true); - }; - - // Refresh clients list (for after connection state changes) - const refreshClients = async () => { - try { - const dbClients = await mcpClientService.getClients(); - const convertedClients = await Promise.all( - dbClients.map(async (dbClient) => { - const client = convertDbClientToClient(dbClient); - if (client.status === 'online') { - await loadTools(client); - } - return client; - }) - ); - setClients(convertedClients); - } catch (error) { - console.error('Failed to refresh clients:', error); - setError(error instanceof Error ? error.message : 'Failed to refresh clients'); - } - }; - - // Confirm deletion and execute - const confirmDeleteClient = async () => { - if (!clientToDelete) return; - - try { - await mcpClientService.deleteClient(clientToDelete.id); - setClients(prev => prev.filter(c => c.id !== clientToDelete.id)); - showToast(`MCP Client "${clientToDelete.name}" deleted successfully`, 'success'); - } catch (error) { - console.error('Failed to delete MCP client:', error); - showToast(error instanceof Error ? error.message : 'Failed to delete MCP client', 'error'); - } finally { - setShowDeleteConfirm(false); - setClientToDelete(null); - } - }; - - // Cancel deletion - const cancelDeleteClient = () => { - setShowDeleteConfirm(false); - setClientToDelete(null); - }; - - if (isLoading) { - return ( -
-
-
-

Loading MCP clients...

-
-
- ); - } - - return ( -
- {/* Error display */} - {error && ( -
-

{error}

- -
- )} - - {/* Add Client Button */} -
-
-

MCP Clients

-

- Connect and manage your MCP-enabled applications -

-
- -
- - {/* Client Grid */} -
-
- {clients.map(client => ( - handleSelectClient(client)} - onEdit={() => handleEditClient(client)} - onDelete={() => handleDeleteClient(client)} - onConnectionChange={refreshClients} - /> - ))} -
-
- - {/* Tool Testing Panel */} - setIsPanelOpen(false)} - /> - - {/* Add Client Modal */} - {isAddClientModalOpen && ( - setIsAddClientModalOpen(false)} - onSubmit={handleAddClient} - /> - )} - - {/* Edit Client Drawer */} - {isEditDrawerOpen && editClient && ( - { - setIsEditDrawerOpen(false); - setEditClient(null); - }} - onUpdate={(updatedClient) => { - // Update the client in state or remove if deleted - setClients(prev => { - if (!updatedClient) { // If updatedClient is null, it means deletion - return prev.filter(c => c.id !== editClient?.id); // Remove the client that was being edited - } - return prev.map(c => c.id === updatedClient.id ? updatedClient : c); - }); - setIsEditDrawerOpen(false); - setEditClient(null); - }} - /> - )} - - {/* Delete Confirmation Modal for Clients */} - {showDeleteConfirm && clientToDelete && ( - - )} -
- ); -}); - -// Add Client Modal Component -interface AddClientModalProps { - isOpen: boolean; - onClose: () => void; - onSubmit: (config: MCPClientConfig) => Promise; -} - -const AddClientModal: React.FC = ({ isOpen, onClose, onSubmit }) => { - const [formData, setFormData] = useState({ - name: '', - url: '', - auto_connect: true - }); - const [isSubmitting, setIsSubmitting] = useState(false); - const [error, setError] = useState(null); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (!formData.name.trim()) { - setError('Client name is required'); - return; - } - - setIsSubmitting(true); - setError(null); - - try { - // Validate URL - if (!formData.url.trim()) { - setError('MCP server URL is required'); - setIsSubmitting(false); - return; - } - - // Ensure URL is valid - try { - const url = new URL(formData.url); - if (!url.protocol.startsWith('http')) { - setError('URL must start with http:// or https://'); - setIsSubmitting(false); - return; - } - } catch (e) { - setError('Invalid URL format'); - setIsSubmitting(false); - return; - } - - const connection_config = { - url: formData.url.trim() - }; - - const clientConfig: MCPClientConfig = { - name: formData.name.trim(), - transport_type: 'http', - connection_config, - auto_connect: formData.auto_connect - }; - - await onSubmit(clientConfig); - - // Reset form on success - setFormData({ - name: '', - url: '', - auto_connect: true - }); - setError(null); - } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to add client'); - } finally { - setIsSubmitting(false); - } - }; - - if (!isOpen) return null; - - return ( -
-
-
- -

- Add New MCP Client -

- -
- {/* Client Name */} -
- - setFormData(prev => ({ ...prev, name: e.target.value }))} - className="w-full px-3 py-2 bg-white/50 dark:bg-black/50 border border-gray-300 dark:border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-cyan-500" - placeholder="Enter client name" - required - /> -
- - {/* MCP Server URL */} -
- - setFormData(prev => ({ ...prev, url: e.target.value }))} - className="w-full px-3 py-2 bg-white/50 dark:bg-black/50 border border-gray-300 dark:border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-cyan-500" - placeholder="http://host.docker.internal:8051/mcp" - required - /> -

- The HTTP endpoint URL of the MCP server -

-

- Docker Note: Use host.docker.internal instead of localhost - to access services running on your host machine -

-
- - {/* Auto Connect */} -
- setFormData(prev => ({ ...prev, auto_connect: e.target.checked }))} - className="mr-2" - /> - -
- - {/* Error message */} - {error && ( -
- {error} -
- )} - - {/* Buttons */} -
- - -
-
-
-
- ); -}; - -// Edit Client Drawer Component -interface EditClientDrawerProps { - client: Client; - isOpen: boolean; - onClose: () => void; - onUpdate: (client: Client | null) => void; // Allow null to indicate deletion -} - -const EditClientDrawer: React.FC = ({ client, isOpen, onClose, onUpdate }) => { - const [editFormData, setEditFormData] = useState({ - name: client.name, - url: '', - auto_connect: true - }); - const [isSubmitting, setIsSubmitting] = useState(false); - const [error, setError] = useState(null); - const [isConnecting, setIsConnecting] = useState(false); - - // State for delete confirmation modal (moved here) - const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); - const [clientToDelete, setClientToDelete] = useState(null); - - const { showToast } = useToast(); // Initialize useToast here - - // Load current client config when drawer opens - useEffect(() => { - if (isOpen && client) { - // Get client config from the API and populate form - loadClientConfig(); - } - }, [isOpen, client.id]); - - const loadClientConfig = async () => { - try { - const dbClient = await mcpClientService.getClient(client.id); - const config = dbClient.connection_config; - - setEditFormData({ - name: dbClient.name, - url: config.url || '', - auto_connect: dbClient.auto_connect - }); - } catch (error) { - console.error('Failed to load client config:', error); - setError('Failed to load client configuration'); - } - }; - - const handleUpdateSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setIsSubmitting(true); - setError(null); - - try { - // Validate URL - if (!editFormData.url.trim()) { - setError('MCP server URL is required'); - setIsSubmitting(false); - return; - } - - // Ensure URL is valid - try { - const url = new URL(editFormData.url); - if (!url.protocol.startsWith('http')) { - setError('URL must start with http:// or https://'); - setIsSubmitting(false); - return; - } - } catch (e) { - setError('Invalid URL format'); - setIsSubmitting(false); - return; - } - - const connection_config = { - url: editFormData.url.trim() - }; - - // Update client via API - const updatedClient = await mcpClientService.updateClient(client.id, { - name: editFormData.name, - transport_type: 'http', - connection_config, - auto_connect: editFormData.auto_connect - }); - - // Update local state - const convertedClient = { - ...client, - name: updatedClient.name, - ip: editFormData.url - }; - - onUpdate(convertedClient); - onClose(); - } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to update client'); - } finally { - setIsSubmitting(false); - } - }; - - const handleConnect = async () => { - setIsConnecting(true); - try { - await mcpClientService.connectClient(client.id); - // Reload the client to get updated status - loadClientConfig(); - } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to connect'); - } finally { - setIsConnecting(false); - } - }; - - const handleDisconnect = async () => { - try { - await mcpClientService.disconnectClient(client.id); - // Reload the client to get updated status - loadClientConfig(); - } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to disconnect'); - } - }; - - const handleDelete = async () => { - if (confirm(`Are you sure you want to delete "${client.name}"?`)) { - try { - await mcpClientService.deleteClient(client.id); - onClose(); - // Trigger a reload of the clients list - window.location.reload(); - } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to delete client'); - } - } - }; - - if (!isOpen) return null; - - return ( -
-
e.stopPropagation()} - > -
- -

- - Edit Client Configuration -

- -
- {/* Client Name */} -
- - setEditFormData(prev => ({ ...prev, name: e.target.value }))} - className="w-full px-3 py-2 bg-white/50 dark:bg-black/50 border border-gray-300 dark:border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-cyan-500" - required - /> -
- - {/* MCP Server URL */} -
- - setEditFormData(prev => ({ ...prev, url: e.target.value }))} - className="w-full px-3 py-2 bg-white/50 dark:bg-black/50 border border-gray-300 dark:border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-cyan-500" - placeholder="http://host.docker.internal:8051/mcp" - required - /> -

- The HTTP endpoint URL of the MCP server -

-

- Docker Note: Use host.docker.internal instead of localhost - to access services running on your host machine -

-
- - {/* Auto Connect */} -
- setEditFormData(prev => ({ ...prev, auto_connect: e.target.checked }))} - className="mr-2" - /> - -
- - {/* Error message */} - {error && ( -
- {error} -
- )} - - {/* Action Buttons */} -
-

Quick Actions

-
- - - - -
-
- - {/* Form Buttons */} -
- - -
-
-
-
- ); -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/mcp/ToolTestingPanel.tsx b/archon-ui-main/src/components/mcp/ToolTestingPanel.tsx deleted file mode 100644 index f1866c8440..0000000000 --- a/archon-ui-main/src/components/mcp/ToolTestingPanel.tsx +++ /dev/null @@ -1,568 +0,0 @@ -import React, { useEffect, useState, useRef } from 'react'; -import { X, Play, ChevronDown, TerminalSquare, Copy, Check, MinusCircle, Maximize2, Minimize2, Hammer, GripHorizontal } from 'lucide-react'; -import { Client, Tool } from './MCPClients'; -import { Button } from '../ui/Button'; -import { mcpClientService } from '../../services/mcpClientService'; - -interface ToolTestingPanelProps { - client: Client | null; - isOpen: boolean; - onClose: () => void; -} - -interface TerminalLine { - id: string; - content: string; - isTyping: boolean; - isCommand: boolean; - isError?: boolean; - isWarning?: boolean; -} - -export const ToolTestingPanel = ({ - client, - isOpen, - onClose -}: ToolTestingPanelProps) => { - const [selectedTool, setSelectedTool] = useState(null); - const [terminalOutput, setTerminalOutput] = useState([{ - id: '1', - content: '> Tool testing terminal ready', - isTyping: false, - isCommand: true - }]); - const [paramValues, setParamValues] = useState>({}); - const [isCopied, setIsCopied] = useState(false); - const [panelHeight, setPanelHeight] = useState(400); - const [isResizing, setIsResizing] = useState(false); - const [isMaximized, setIsMaximized] = useState(false); - const [isExecuting, setIsExecuting] = useState(false); - const terminalRef = useRef(null); - const resizeHandleRef = useRef(null); - const panelRef = useRef(null); - const previousHeightRef = useRef(400); - - // Reset selected tool when client changes - useEffect(() => { - if (client && client.tools.length > 0) { - setSelectedTool(client.tools[0]); - setParamValues({}); - } else { - setSelectedTool(null); - setParamValues({}); - } - }, [client]); - - // Auto-scroll terminal to bottom when output changes - useEffect(() => { - if (terminalRef.current) { - terminalRef.current.scrollTop = terminalRef.current.scrollHeight; - } - }, [terminalOutput]); - - // Handle resizing functionality - useEffect(() => { - const handleMouseMove = (e: MouseEvent) => { - if (isResizing && panelRef.current) { - const containerHeight = window.innerHeight; - const mouseY = e.clientY; - const newHeight = containerHeight - mouseY; - if (newHeight >= 200 && newHeight <= containerHeight * 0.8) { - setPanelHeight(newHeight); - } - } - }; - - const handleMouseUp = () => { - setIsResizing(false); - document.body.style.cursor = 'default'; - document.body.style.userSelect = 'auto'; - }; - - if (isResizing) { - document.addEventListener('mousemove', handleMouseMove); - document.addEventListener('mouseup', handleMouseUp); - document.body.style.cursor = 'ns-resize'; - document.body.style.userSelect = 'none'; - } - - return () => { - document.removeEventListener('mousemove', handleMouseMove); - document.removeEventListener('mouseup', handleMouseUp); - }; - }, [isResizing]); - - // Handle tool selection - const handleToolSelect = (tool: Tool) => { - setSelectedTool(tool); - setParamValues({}); - }; - - // Handle parameter value change - const handleParamChange = (paramName: string, value: string) => { - setParamValues(prev => ({ - ...prev, - [paramName]: value - })); - }; - - // Simulate typing animation for terminal output - const addTypingLine = (content: string, isCommand: boolean = false, isError: boolean = false, isWarning: boolean = false) => { - const newLineId = Date.now().toString() + Math.random().toString(36).substring(2); - - setTerminalOutput(prev => [...prev, { - id: newLineId, - content: '', - isTyping: true, - isCommand, - isError, - isWarning - }]); - - // Simulate typing animation - let currentText = ''; - const textArray = content.split(''); - const typeInterval = setInterval(() => { - if (textArray.length > 0) { - currentText += textArray.shift(); - setTerminalOutput(prev => prev.map(line => - line.id === newLineId ? { - ...line, - content: currentText - } : line - )); - } else { - clearInterval(typeInterval); - setTerminalOutput(prev => prev.map(line => - line.id === newLineId ? { - ...line, - isTyping: false - } : line - )); - } - }, 15); // Faster typing - - return newLineId; - }; - - // Add instant line (no typing effect) - const addInstantLine = (content: string, isCommand: boolean = false, isError: boolean = false, isWarning: boolean = false) => { - const newLineId = Date.now().toString() + Math.random().toString(36).substring(2); - - setTerminalOutput(prev => [...prev, { - id: newLineId, - content, - isTyping: false, - isCommand, - isError, - isWarning - }]); - - return newLineId; - }; - - // Convert parameter values to proper types - const convertParameterValues = (): Record => { - if (!selectedTool) return {}; - - const convertedParams: Record = {}; - - selectedTool.parameters.forEach(param => { - const value = paramValues[param.name]; - - if (value !== undefined && value !== '') { - try { - switch (param.type) { - case 'number': - convertedParams[param.name] = Number(value); - if (isNaN(convertedParams[param.name])) { - throw new Error(`Invalid number: ${value}`); - } - break; - case 'boolean': - convertedParams[param.name] = value.toLowerCase() === 'true' || value === '1'; - break; - case 'array': - // Try to parse as JSON array first, fallback to comma-separated - try { - convertedParams[param.name] = JSON.parse(value); - if (!Array.isArray(convertedParams[param.name])) { - throw new Error('Not an array'); - } - } catch { - convertedParams[param.name] = value.split(',').map(v => v.trim()).filter(v => v); - } - break; - default: - convertedParams[param.name] = value; - } - } catch (error) { - console.warn(`Parameter conversion error for ${param.name}:`, error); - convertedParams[param.name] = value; // Fallback to string - } - } - }); - - return convertedParams; - }; - - - - // Execute tool using universal MCP client service (works for ALL clients) - const executeTool = async () => { - if (!selectedTool || !client) return; - - try { - const convertedParams = convertParameterValues(); - - addTypingLine(`> Connecting to ${client.name} via MCP protocol...`); - - // Call the client tool via MCP service - const result = await mcpClientService.callClientTool({ - client_id: client.id, - tool_name: selectedTool.name, - arguments: convertedParams - }); - - setTimeout(() => addTypingLine('> Tool executed successfully'), 300); - - // Display the result - setTimeout(() => { - if (result) { - let resultText = ''; - - if (typeof result === 'object') { - if (result.content) { - // Handle MCP content response - if (Array.isArray(result.content)) { - resultText = result.content.map((item: any) => - item.text || JSON.stringify(item, null, 2) - ).join('\n'); - } else { - resultText = result.content.text || JSON.stringify(result.content, null, 2); - } - } else { - resultText = JSON.stringify(result, null, 2); - } - } else { - resultText = String(result); - } - - addInstantLine('> Result:'); - addInstantLine(resultText); - } else { - addTypingLine('> No result returned'); - } - - addTypingLine('> Completed successfully'); - setIsExecuting(false); - }, 600); - - } catch (error: any) { - console.error('MCP tool execution failed:', error); - setTimeout(() => { - addTypingLine(`> ERROR: Failed to execute tool on ${client.name}`, false, true); - addTypingLine(`> ${error.message || 'Unknown error occurred'}`, false, true); - addTypingLine('> Execution failed'); - setIsExecuting(false); - }, 300); - } - }; - - // Validate required parameters - const validateParameters = (): string | null => { - if (!selectedTool) return 'No tool selected'; - - for (const param of selectedTool.parameters) { - if (param.required && !paramValues[param.name]) { - return `Required parameter '${param.name}' is missing`; - } - } - - return null; - }; - - // Handle tool execution - const executeSelectedTool = () => { - if (!selectedTool || !client || isExecuting) return; - - // Validate required parameters - const validationError = validateParameters(); - if (validationError) { - addTypingLine(`> ERROR: ${validationError}`, false, true); - return; - } - - setIsExecuting(true); - - // Add command to terminal - const params = selectedTool.parameters.map(p => { - const value = paramValues[p.name]; - return value ? `${p.name}=${value}` : undefined; - }).filter(Boolean).join(' '); - - const command = `> execute ${selectedTool.name} ${params}`; - addTypingLine(command, true); - - // Execute using universal client service for ALL clients - setTimeout(() => { - executeTool(); - }, 200); - }; - - // Handle copy terminal output - const copyTerminalOutput = () => { - const textContent = terminalOutput.map(line => line.content).join('\n'); - navigator.clipboard.writeText(textContent); - setIsCopied(true); - setTimeout(() => setIsCopied(false), 2000); - }; - - // Handle resize start - const handleResizeStart = (e: React.MouseEvent) => { - e.preventDefault(); - setIsResizing(true); - }; - - // Handle maximize/minimize - const toggleMaximize = () => { - if (isMaximized) { - setPanelHeight(previousHeightRef.current); - } else { - previousHeightRef.current = panelHeight; - setPanelHeight(window.innerHeight * 0.8); - } - setIsMaximized(!isMaximized); - }; - - // Clear terminal - const clearTerminal = () => { - setTerminalOutput([{ - id: Date.now().toString(), - content: '> Terminal cleared', - isTyping: false, - isCommand: true - }]); - }; - - if (!isOpen || !client) return null; - - return ( -
- {/* Resize handle at the top */} -
-
-
- - {/* Panel with neon effect */} -
-
- - {/* Header */} -
-

- - {client.name} - - {client.ip} - - - {client.tools.length} tools available - -

-
- - - -
-
- - {/* Content */} -
- {client.tools.length === 0 ? ( -
-
- -

No Tools Available

-

- {client.status === 'offline' - ? 'Client is offline. Tools will be available when connected.' - : 'No tools discovered for this client.'} -

-
-
- ) : ( -
- {/* Left column: Tool selection and parameters */} -
- {/* Tool selection and execute button row */} -
-
- -
- -
- -
-
-
-
- -
-
- - {/* Tool description */} - {selectedTool && ( -

- {selectedTool.description} -

- )} - - {/* Parameters */} - {selectedTool && selectedTool.parameters.length > 0 && ( -
-

- Parameters -

-
- {selectedTool.parameters.map(param => ( -
- - handleParamChange(param.name, e.target.value)} - className="w-full px-3 py-2 text-sm bg-white/50 dark:bg-black/50 border border-gray-300 dark:border-gray-700 rounded-md focus:outline-none focus:ring-1 focus:ring-cyan-500 focus:border-cyan-500 transition-all duration-200" - placeholder={param.description || `Enter ${param.name}`} - /> - {param.description && ( -

- {param.description} -

- )} -
- ))} -
-
- )} -
- - {/* Right column: Terminal output */} -
-
-
-
- - - Terminal Output - -
- -
-
- {terminalOutput.map(line => ( -
- {line.content} - {line.isTyping && } -
- ))} -
-
-
-
- )} -
-
-
- ); -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/DataTab.tsx b/archon-ui-main/src/components/project-tasks/DataTab.tsx deleted file mode 100644 index 6ba8dac2d6..0000000000 --- a/archon-ui-main/src/components/project-tasks/DataTab.tsx +++ /dev/null @@ -1,956 +0,0 @@ -import React, { useCallback, useState, useEffect, useMemo } from 'react'; -import '@xyflow/react/dist/style.css'; -import { ReactFlow, Node, Edge, Background, Controls, MarkerType, NodeChange, applyNodeChanges, EdgeChange, applyEdgeChanges, ConnectionLineType, addEdge, Connection, Handle, Position } from '@xyflow/react'; -import { Database, Info, Calendar, TrendingUp, Edit, Plus, X, Save, Trash2 } from 'lucide-react'; -import { projectService } from '../../services/projectService'; -import { useToast } from '../../contexts/ToastContext'; - -// Custom node types - will be defined inside the component to access state - -const createTableNode = (id: string, label: string, columns: string[], x: number, y: number): Node => ({ - id, - type: 'table', - data: { - label, - columns - }, - position: { - x, - y - } -}); - -// Default fallback nodes for basic database structure -const defaultNodes: Node[] = [ - createTableNode('users', 'Users', ['id (PK) - UUID', 'email - VARCHAR(255)', 'password - VARCHAR(255)', 'firstName - VARCHAR(100)', 'lastName - VARCHAR(100)', 'createdAt - TIMESTAMP', 'updatedAt - TIMESTAMP'], 150, 100), - createTableNode('projects', 'Projects', ['id (PK) - UUID', 'title - VARCHAR(255)', 'description - TEXT', 'status - VARCHAR(50)', 'userId (FK) - UUID', 'createdAt - TIMESTAMP', 'updatedAt - TIMESTAMP'], 500, 100) -]; - -const defaultEdges: Edge[] = [{ - id: 'projects-users', - source: 'users', - target: 'projects', - sourceHandle: 'Users-id', - targetHandle: 'Projects-userId', - animated: true, - style: { - stroke: '#d946ef' - }, - markerEnd: { - type: MarkerType.Arrow, - color: '#d946ef' - } -}]; - -// Data metadata card component for the new data structure -const DataCard = ({ data }: { data: any }) => { - const iconMap: { [key: string]: any } = { - 'ShoppingCart': Database, - 'Database': Database, - 'Info': Info, - 'Calendar': Calendar, - 'TrendingUp': TrendingUp - }; - - const IconComponent = iconMap[data.icon] || Database; - - const colorClasses = { - cyan: 'from-cyan-900/40 to-cyan-800/30 border-cyan-500/50 text-cyan-400', - blue: 'from-blue-900/40 to-blue-800/30 border-blue-500/50 text-blue-400', - purple: 'from-purple-900/40 to-purple-800/30 border-purple-500/50 text-purple-400', - pink: 'from-pink-900/40 to-pink-800/30 border-pink-500/50 text-pink-400' - }; - - const colorClass = colorClasses[data.color as keyof typeof colorClasses] || colorClasses.cyan; - - return ( -
-
- -
Project Data Overview
-
-
-
-
Description:
-
{data.description}
-
-
- Progress: -
-
-
-
- {data.progress}% -
-
-
- Last updated: {data.updated} -
-
-
- ); -}; - -interface DataTabProps { - project?: { - id: string; - title: string; - data?: any[]; - } | null; -} - -export const DataTab = ({ project }: DataTabProps) => { - const [nodes, setNodes] = useState([]); - const [edges, setEdges] = useState([]); - const [loading, setLoading] = useState(true); - const [viewMode, setViewMode] = useState<'metadata' | 'erd'>('metadata'); - const [editingNode, setEditingNode] = useState(null); - const [showEditModal, setShowEditModal] = useState(false); - const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); - const [isSaving, setIsSaving] = useState(false); - const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); - const [nodeToDelete, setNodeToDelete] = useState(null); - - const { showToast } = useToast(); - - - // Helper function to normalize nodes to ensure required properties - const normalizeNode = (node: any): Node => { - return { - id: node.id || `node-${Date.now()}-${Math.random()}`, - type: node.type || 'table', - position: { - x: node.position?.x || 100, - y: node.position?.y || 100 - }, - data: { - label: node.data?.label || 'Untitled', - columns: node.data?.columns || ['id (PK) - UUID'] - } - }; - }; - - useEffect(() => { - console.log('DataTab project data:', project?.data); - - // Determine view mode based on data structure - if (project?.data) { - if (Array.isArray(project.data) && project.data.length > 0) { - // Handle array format: [{"type": "erd", ...}] or [{"description": "...", "progress": 65}] - const firstItem = project.data[0]; - console.log('First data item (array):', firstItem); - - if (firstItem.description && typeof firstItem.progress === 'number') { - console.log('Setting metadata view'); - setViewMode('metadata'); - } else if (firstItem.type === 'erd' && firstItem.nodes && firstItem.edges) { - console.log('Setting ERD view with structured array data'); - setViewMode('erd'); - // Normalize nodes to ensure required properties - const normalizedNodes = firstItem.nodes.map(normalizeNode); - setNodes(normalizedNodes); - // Fix any ArrowClosed marker types in loaded edges - const sanitizedEdges = firstItem.edges.map((edge: any) => ({ - ...edge, - markerEnd: edge.markerEnd ? { - ...edge.markerEnd, - type: edge.markerEnd.type === 'ArrowClosed' ? MarkerType.Arrow : edge.markerEnd.type - } : undefined - })); - setEdges(sanitizedEdges); - } else { - console.log('Setting ERD view for array data'); - setViewMode('erd'); - // Normalize nodes to ensure required properties - const normalizedNodes = project.data.map(normalizeNode); - setNodes(normalizedNodes); - setEdges([]); - } - } else if (typeof project.data === 'object' && !Array.isArray(project.data) && - (project.data as any).type === 'erd' && - (project.data as any).nodes && - (project.data as any).edges) { - // Handle direct object format: {"type": "erd", "nodes": [...], "edges": [...]} - console.log('Setting ERD view with direct object data'); - setViewMode('erd'); - // Normalize nodes to ensure required properties - const normalizedNodes = (project.data as any).nodes.map(normalizeNode); - setNodes(normalizedNodes); - // Fix any ArrowClosed marker types in loaded edges - const sanitizedEdges = (project.data as any).edges.map((edge: any) => ({ - ...edge, - markerEnd: edge.markerEnd ? { - ...edge.markerEnd, - type: edge.markerEnd.type === 'ArrowClosed' ? MarkerType.Arrow : edge.markerEnd.type - } : undefined - })); - setEdges(sanitizedEdges); - } else { - console.log('Unknown data format, showing empty state'); - setViewMode('erd'); - setNodes([]); - setEdges([]); - } - } else { - console.log('No data, using empty state'); - setViewMode('erd'); - setNodes([]); - setEdges([]); - } - setLoading(false); - }, [project]); - - const onNodesChange = useCallback((changes: NodeChange[]) => { - setNodes(nds => applyNodeChanges(changes, nds)); - setHasUnsavedChanges(true); - }, []); - - const onEdgesChange = useCallback((changes: EdgeChange[]) => { - setEdges(eds => applyEdgeChanges(changes, eds)); - setHasUnsavedChanges(true); - }, []); - const onConnect = useCallback(async (connection: Connection) => { - const newEdgeProps = { - animated: true, - style: { - stroke: '#22d3ee' - }, - markerEnd: { - type: MarkerType.Arrow, - color: '#22d3ee' - }, - label: 'relates to', - labelStyle: { - fill: '#e94560', - fontWeight: 500 - }, - labelBgStyle: { - fill: 'rgba(0, 0, 0, 0.7)' - } - }; - - const newEdges = addEdge({ ...connection, ...newEdgeProps }, edges); - setEdges(newEdges); - - // Auto-save to database - await saveToDatabase(nodes, newEdges); - }, [nodes, edges, project?.id]); - - const handleNodeClick = useCallback((event: React.MouseEvent, node: Node) => { - setEditingNode(node); - setShowEditModal(true); - }, []); - - - - const addTableNode = async () => { - if (!project?.id) { - console.error('❌ No project ID available for adding table'); - return; - } - - console.log('🔄 Adding new table...'); - const newNodeId = `table-${Date.now()}`; - const newNode = createTableNode(newNodeId, `New Table ${nodes.length + 1}`, ['id (PK) - UUID', 'name - VARCHAR(255)', 'description - TEXT', 'createdAt - TIMESTAMP', 'updatedAt - TIMESTAMP'], 400, 300); - const newNodes = [...nodes, newNode]; - setNodes(newNodes); - - // Auto-save to database - try { - console.log('💾 Saving new table to database...'); - await saveToDatabase(newNodes, edges); - console.log('✅ New table saved successfully'); - } catch (error) { - console.error('❌ Failed to save new table:', error); - // Optionally revert the UI change if save failed - setNodes(nodes); - } - }; - - const saveToDatabase = async (nodesToSave = nodes, edgesToSave = edges) => { - if (!project?.id) { - console.error('No project ID available for saving'); - return; - } - - console.log('💾 saveToDatabase called with:', { - projectId: project.id, - nodeCount: nodesToSave.length, - edgeCount: edgesToSave.length - }); - - setIsSaving(true); - try { - const updatedData = { - type: 'erd', - nodes: nodesToSave, - edges: edgesToSave - }; - - console.log('🔄 Calling projectService.updateProject with data:', updatedData); - - const result = await projectService.updateProject(project.id, { - data: [updatedData] // Wrap in array to match UpdateProjectRequest type - }); - - console.log('✅ ERD data saved successfully, result:', result); - setHasUnsavedChanges(false); - } catch (error) { - console.error('❌ Failed to save ERD data:', error); - console.error('Error details:', error); - throw error; // Re-throw so calling function can handle it - } finally { - setIsSaving(false); - } - }; - - const saveNodeChanges = async (updatedNode: Node) => { - // Update local state first - const newNodes = nodes.map(node => - node.id === updatedNode.id ? updatedNode : node - ); - setNodes(newNodes); - - // Save to database - await saveToDatabase(newNodes, edges); - - setShowEditModal(false); - setEditingNode(null); - }; - - const handleManualSave = async () => { - await saveToDatabase(); - }; - - const handleDeleteNode = useCallback(async (event: React.MouseEvent, nodeId: string) => { - event.stopPropagation(); // Prevent triggering the edit modal - - if (!project?.id) { - console.error('❌ No project ID available for deleting table'); - return; - } - - // Show custom confirmation dialog - setNodeToDelete(nodeId); - setShowDeleteConfirm(true); - }, [project?.id]); - - const confirmDelete = useCallback(async () => { - if (!nodeToDelete) return; - - console.log('🗑️ Deleting table:', nodeToDelete); - - try { - // Remove node from UI - const newNodes = nodes.filter(node => node.id !== nodeToDelete); - - // Remove any edges connected to this node - const newEdges = edges.filter(edge => - edge.source !== nodeToDelete && edge.target !== nodeToDelete - ); - - setNodes(newNodes); - setEdges(newEdges); - - // Save to database - console.log('💾 Saving after table deletion...'); - await saveToDatabase(newNodes, newEdges); - console.log('✅ Table deleted successfully'); - showToast('Table deleted successfully', 'success'); - - // Close confirmation dialog - setShowDeleteConfirm(false); - setNodeToDelete(null); - } catch (error) { - console.error('❌ Failed to delete table:', error); - // Revert UI changes on error - setNodes(nodes); - setEdges(edges); - showToast('Failed to delete table', 'error'); - } - }, [nodeToDelete, nodes, edges, saveToDatabase]); - - const cancelDelete = useCallback(() => { - setShowDeleteConfirm(false); - setNodeToDelete(null); - }, []); - - // Memoize nodeTypes to prevent recreation on every render - const nodeTypes = useMemo(() => ({ - table: ({ data, id }: any) => ( -
-
-
- -
- {data.label} -
-
-
- - -
-
-
- {data.columns.map((col: string, i: number) => { - const isPK = col.includes('PK'); - const isFK = col.includes('FK'); - return ( -
- {col} - {isPK && ( - - )} - {isFK && ( - - )} -
- ); - })} -
-
- ) - }), [handleNodeClick, handleDeleteNode, nodes]); - - if (loading) { - return ( -
-
Loading data...
-
- ); - } - - return ( -
-
-
-
-
- - {viewMode === 'metadata' ? 'Data Overview' : 'Data Relationships'} - {viewMode === 'erd' && nodes.length > 0 && ` (${nodes.length} tables)`} - {viewMode === 'metadata' && Array.isArray(project?.data) && ` (${project.data.length} items)`} -
- {viewMode === 'metadata' && ( - - )} - {viewMode === 'erd' && ( -
- - {hasUnsavedChanges && ( - - )} - -
- )} -
- - {viewMode === 'metadata' ? ( -
- {Array.isArray(project?.data) && project.data.length > 0 ? ( -
- {project.data.map((item, index) => ( - - ))} -
- ) : ( -
- -

No metadata available

-

Switch to ERD view to see database schema

-
- )} -
- ) : ( -
- {/* Subtle neon glow at the top */} -
- {nodes.length === 0 ? ( -
- -

No data schema defined

-

Add tables to design your database

-
- ) : ( - - - - )} -
- )} - - {/* Delete Confirmation Modal */} - {showDeleteConfirm && ( - n.id === nodeToDelete)?.data.label as string || 'table'} - /> - )} - - {/* Edit Modal */} - {showEditModal && editingNode && ( - { - setShowEditModal(false); - setEditingNode(null); - }} - /> - )} -
-
- ); -}; - -// Delete Confirmation Modal Component -const DeleteConfirmModal = ({ - onConfirm, - onCancel, - tableName -}: { - onConfirm: () => void; - onCancel: () => void; - tableName: string; -}) => { - return ( -
-
- -
-
-
- -
-
-

- Delete Table -

-

- This action cannot be undone -

-
-
- -

- Are you sure you want to delete the "{tableName}" table? - This will also remove all related connections. -

- -
- - -
-
-
-
- ); -}; - -// Column interface for better type management -interface ColumnDefinition { - name: string; - dataType: string; - columnType: 'regular' | 'pk' | 'fk'; - referencedTable?: string; - referencedColumn?: string; -} - -// Edit Table Modal Component -const EditTableModal = ({ - node, - nodes, - edges, - onSave, - onUpdateEdges, - onClose -}: { - node: Node; - nodes: Node[]; - edges: Edge[]; - onSave: (node: Node) => void; - onUpdateEdges: (edges: Edge[]) => void; - onClose: () => void; -}) => { - const [tableName, setTableName] = useState(node.data.label as string); - const [columns, setColumns] = useState([]); - - // Parse existing columns into structured format - useEffect(() => { - const parsedColumns = (node.data.columns as string[]).map((colStr: string) => { - const parts = colStr.split(' - '); - const nameAndType = parts[0]; - const dataType = parts[1] || 'VARCHAR(255)'; - - let columnType: 'regular' | 'pk' | 'fk' = 'regular'; - let name = nameAndType; - const referencedTable = ''; - const referencedColumn = ''; - - if (nameAndType.includes('(PK)')) { - columnType = 'pk'; - name = nameAndType.replace(' (PK)', ''); - } else if (nameAndType.includes('(FK)')) { - columnType = 'fk'; - name = nameAndType.replace(' (FK)', ''); - } - - return { - name, - dataType, - columnType, - referencedTable, - referencedColumn - }; - }); - setColumns(parsedColumns); - }, [node.data.columns]); - - const addColumn = () => { - setColumns([...columns, { - name: 'newColumn', - dataType: 'VARCHAR(255)', - columnType: 'regular', - referencedTable: '', - referencedColumn: '' - }]); - }; - - const updateColumn = (index: number, field: keyof ColumnDefinition, value: string) => { - const newColumns = [...columns]; - newColumns[index] = { ...newColumns[index], [field]: value }; - setColumns(newColumns); - }; - - const removeColumn = (index: number) => { - setColumns(columns.filter((_, i) => i !== index)); - }; - - // Get available tables for FK references (exclude current table) - const getAvailableTables = () => { - return nodes.filter(n => n.id !== node.id).map(n => ({ - id: n.id, - label: n.data.label as string - })); - }; - - // Get available columns for a specific table - const getAvailableColumns = (tableId: string) => { - const targetNode = nodes.find(n => n.id === tableId); - if (!targetNode) return []; - - return (targetNode.data.columns as string[]) - .filter(col => col.includes('(PK)')) // Only allow referencing primary keys - .map(col => { - const name = col.split(' - ')[0].replace(' (PK)', ''); - return { name, label: name }; - }); - }; - - const handleSave = () => { - // Convert columns back to string format - const columnStrings = columns.map(col => { - let name = col.name; - if (col.columnType === 'pk') { - name += ' (PK)'; - } else if (col.columnType === 'fk') { - name += ' (FK)'; - } - return `${name} - ${col.dataType}`; - }); - - const updatedNode = { - ...node, - data: { - ...node.data, - label: tableName, - columns: columnStrings - } - }; - - // Create edges for FK relationships - const newEdges = [...edges]; - - // Remove existing edges from this table - const filteredEdges = newEdges.filter(edge => edge.source !== node.id); - - // Add new edges for FK columns - columns.forEach(col => { - if (col.columnType === 'fk' && col.referencedTable && col.referencedColumn) { - const edgeId = `${col.referencedTable}-${node.id}`; - const newEdge = { - id: edgeId, - source: col.referencedTable, - target: node.id, - sourceHandle: `${nodes.find(n => n.id === col.referencedTable)?.data.label}-${col.referencedColumn}`, - targetHandle: `${tableName}-${col.name}`, - animated: true, - style: { - stroke: '#d946ef' - }, - markerEnd: { - type: MarkerType.Arrow, - color: '#d946ef' - } - }; - filteredEdges.push(newEdge); - } - }); - - // Update edges state - onUpdateEdges(filteredEdges); - - onSave(updatedNode); - }; - - return ( -
-
-
-

- - Edit Table -

- -
- -
- {/* Table Name */} -
- - setTableName(e.target.value)} - className="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:border-cyan-500 focus:outline-none" - /> -
- - {/* Columns */} -
-
- - -
- - {/* Column Headers */} -
-
Column Name
-
Data Type
-
Type
-
References (FK only)
-
-
- -
- {columns.map((column, index) => ( -
- {/* Column Name */} - updateColumn(index, 'name', e.target.value)} - className="col-span-3 px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:border-cyan-500 focus:outline-none text-sm" - /> - - {/* Data Type */} - updateColumn(index, 'dataType', e.target.value)} - className="col-span-2 px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:border-cyan-500 focus:outline-none text-sm" - /> - - {/* Column Type */} - - - {/* FK Reference (only show for FK columns) */} - {column.columnType === 'fk' && ( - <> - - - - - )} - - {/* Spacer for non-FK columns */} - {column.columnType !== 'fk' &&
} - - {/* Remove Button */} - -
- ))} -
-
-
- - {/* Actions */} -
- - -
-
-
- ); -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/DocsTab.tsx b/archon-ui-main/src/components/project-tasks/DocsTab.tsx deleted file mode 100644 index e2e0862425..0000000000 --- a/archon-ui-main/src/components/project-tasks/DocsTab.tsx +++ /dev/null @@ -1,1494 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Plus, X, Search, Upload, Link as LinkIcon, Check, Brain, Save, History, Eye, Edit3, Sparkles } from 'lucide-react'; -import { Button } from '../ui/Button'; -import { knowledgeBaseService, KnowledgeItem } from '../../services/knowledgeBaseService'; -import { projectService } from '../../services/projectService'; -import { useToast } from '../../contexts/ToastContext'; -import { Input } from '../ui/Input'; -import { Card } from '../ui/Card'; -import { Badge } from '../ui/Badge'; -import { Select } from '../ui/Select'; -import { useCrawlProgressPolling } from '../../hooks/usePolling'; -import { MilkdownEditor } from './MilkdownEditor'; -import { VersionHistoryModal } from './VersionHistoryModal'; -import { PRPViewer } from '../prp'; -import { DocumentCard, NewDocumentCard } from './DocumentCard'; - - - - -interface ProjectDoc { - id: string; - title: string; - created_at: string; - updated_at: string; - // Content field stores markdown or structured data - content?: any; - document_type?: string; -} - -interface Task { - id: string; - title: string; - feature: string; - status: 'backlog' | 'in-progress' | 'review' | 'complete'; -} - -// Document Templates - Updated for proper MCP database storage -const DOCUMENT_TEMPLATES = { - 'prp_base': { - name: 'Feature PRP Template', - icon: '🚀', - document_type: 'prp', - content: { - document_type: 'prp', - title: 'New Feature Implementation', - version: '1.0', - author: 'User', - date: new Date().toISOString().split('T')[0], - status: 'draft', - - goal: 'Build a specific feature - replace with your goal', - - why: [ - 'Business value this feature provides', - 'User problem this solves', - 'How it integrates with existing functionality' - ], - - what: { - description: 'Detailed description of what users will see and experience', - success_criteria: [ - 'Measurable outcome 1 (e.g., response time < 200ms)', - 'User behavior outcome 2 (e.g., 90% task completion rate)', - 'Technical outcome 3 (e.g., zero data loss during operations)' - ], - user_stories: [ - 'As a [user type], I want to [action] so that [benefit]', - 'As a [user type], I need to [requirement] in order to [goal]' - ] - }, - - context: { - documentation: [ - { - source: 'https://docs.example.com/api', - why: 'API endpoints and data models needed' - }, - { - source: 'src/components/Example.tsx', - why: 'Existing pattern to follow for UI components' - } - ], - existing_code: [ - { - file: 'src/services/baseService.ts', - purpose: 'Service layer pattern to extend' - } - ], - gotchas: [ - 'Critical requirement or constraint to remember', - 'Common mistake to avoid during implementation' - ], - dependencies: [ - 'Package or service that must be available', - 'Another feature that must be completed first' - ] - }, - - implementation_blueprint: { - phase_1_foundation: { - description: 'Set up core infrastructure', - duration: '2-3 days', - tasks: [ - { - title: 'Create TypeScript interfaces', - details: 'Define all data types and API contracts', - files: ['src/types/newFeature.ts'] - }, - { - title: 'Set up database schema', - details: 'Create tables and relationships if needed', - files: ['migrations/add_feature_tables.sql'] - } - ] - }, - phase_2_implementation: { - description: 'Build core functionality', - duration: '1 week', - tasks: [ - { - title: 'Implement service layer', - details: 'Business logic and data access', - files: ['src/services/newFeatureService.ts'] - }, - { - title: 'Create API endpoints', - details: 'RESTful endpoints with proper validation', - files: ['src/api/newFeatureApi.ts'] - }, - { - title: 'Build UI components', - details: 'React components with TypeScript', - files: ['src/components/NewFeature.tsx'] - } - ] - }, - phase_3_integration: { - description: 'Connect everything and test', - duration: '2-3 days', - tasks: [ - { - title: 'Integrate frontend with backend', - details: 'Connect UI to API endpoints', - files: ['src/hooks/useNewFeature.ts'] - }, - { - title: 'Add comprehensive tests', - details: 'Unit, integration, and E2E tests', - files: ['tests/newFeature.test.ts'] - } - ] - } - }, - - validation: { - level_1_syntax: [ - 'npm run lint -- --fix', - 'npm run typecheck', - 'Ensure no TypeScript errors' - ], - level_2_unit_tests: [ - 'npm run test -- newFeature', - 'Verify all unit tests pass with >80% coverage' - ], - level_3_integration: [ - 'npm run test:integration', - 'Test API endpoints with proper data flow' - ], - level_4_end_to_end: [ - 'Start development server and test user flows', - 'Verify feature works as expected in browser', - 'Test error scenarios and edge cases' - ] - } - } - }, - 'prp_task': { - name: 'Task/Bug Fix PRP', - icon: '✅', - document_type: 'prp', - content: { - document_type: 'prp', - title: 'Task or Bug Fix', - version: '1.0', - author: 'User', - date: new Date().toISOString().split('T')[0], - status: 'draft', - - goal: 'Fix specific bug or complete targeted task', - - why: [ - 'Impact on users or system if not fixed', - 'How this fits into larger project goals', - 'Priority level and urgency' - ], - - what: { - description: 'Specific problem to solve and expected outcome', - current_behavior: 'What happens now (the problem)', - expected_behavior: 'What should happen instead', - acceptance_criteria: [ - 'Specific testable condition 1', - 'Specific testable condition 2', - 'No regressions in existing functionality' - ] - }, - - context: { - affected_files: [ - { - path: 'src/component.tsx', - reason: 'Contains the bug or needs the change' - }, - { - path: 'src/service.ts', - reason: 'Related logic that may need updates' - } - ], - root_cause: 'Analysis of why this issue exists', - related_issues: [ - 'Link to GitHub issue or ticket', - 'Related bugs or enhancement requests' - ], - dependencies: [ - 'Other tasks that must be completed first', - 'External services or APIs involved' - ] - }, - - implementation_steps: [ - { - step: 1, - action: 'Reproduce the issue', - details: 'Create test case that demonstrates the problem' - }, - { - step: 2, - action: 'Identify root cause', - details: 'Debug and trace the issue to its source' - }, - { - step: 3, - action: 'Implement fix', - details: 'Apply minimal change that resolves the issue' - }, - { - step: 4, - action: 'Test solution', - details: 'Verify fix works and doesn\'t break other functionality' - }, - { - step: 5, - action: 'Update documentation', - details: 'Update any relevant docs or comments' - } - ], - - validation: { - reproduction_test: [ - 'Steps to reproduce the original issue', - 'Verify the issue no longer occurs' - ], - regression_tests: [ - 'Run existing test suite to ensure no regressions', - 'Test related functionality manually' - ], - edge_cases: [ - 'Test boundary conditions', - 'Test error scenarios' - ] - } - } - }, - 'prp_planning': { - name: 'Architecture/Planning PRP', - icon: '📐', - document_type: 'prp', - content: { - document_type: 'prp', - title: 'System Architecture and Planning', - version: '1.0', - author: 'User', - date: new Date().toISOString().split('T')[0], - status: 'draft', - - goal: 'Design and plan system architecture for [specific system/feature]', - - why: [ - 'Strategic business objective driving this architecture', - 'Technical debt or scalability issues to address', - 'Future growth and maintainability requirements' - ], - - what: { - scope: 'System boundaries, affected components, and integration points', - deliverables: [ - 'Comprehensive architecture documentation', - 'Component specifications and interfaces', - 'Implementation roadmap and timeline', - 'Migration/deployment strategy' - ], - constraints: [ - 'Budget and timeline limitations', - 'Technical constraints and dependencies', - 'Regulatory or compliance requirements' - ] - }, - - current_state_analysis: { - strengths: [ - 'What works well in the current system', - 'Stable components that should be preserved', - 'Existing patterns worth maintaining' - ], - weaknesses: [ - 'Performance bottlenecks and limitations', - 'Maintenance and scaling challenges', - 'Security or reliability concerns' - ], - opportunities: [ - 'Modern technologies to leverage', - 'Process improvements to implement', - 'Business capabilities to enable' - ], - threats: [ - 'Risks during transition period', - 'Dependencies on legacy systems', - 'Resource and timeline constraints' - ] - }, - - proposed_architecture: { - overview: 'High-level description of the new architecture', - components: { - frontend: { - technology: 'React 18 with TypeScript', - patterns: 'Component composition with ShadCN UI', - state_management: 'React hooks with context for global state' - }, - backend: { - technology: 'FastAPI with async Python', - patterns: 'Service layer with repository pattern', - database: 'Supabase PostgreSQL with proper indexing' - }, - realtime: { - technology: 'HTTP polling for live updates', - patterns: 'ETag-based polling with smart pausing' - }, - infrastructure: { - deployment: 'Docker containers with orchestration', - monitoring: 'Comprehensive logging and metrics', - security: 'OAuth2 with proper encryption' - } - }, - data_flow: [ - 'User interaction → Frontend validation → API call', - 'Backend processing → Database operations → Response', - 'Real-time events → HTTP polling → UI updates' - ], - integration_points: [ - 'External APIs and their usage patterns', - 'Third-party services and data sources', - 'Legacy system interfaces' - ] - }, - - implementation_phases: { - phase_1_foundation: { - duration: '2-3 weeks', - objective: 'Core infrastructure and basic functionality', - deliverables: [ - 'Database schema and basic API endpoints', - 'Authentication and authorization system', - 'Core UI components and routing' - ], - success_criteria: [ - 'Basic user flows working end-to-end', - 'Core API responses under 200ms', - 'Authentication working with test users' - ] - }, - phase_2_features: { - duration: '3-4 weeks', - objective: 'Primary feature implementation', - deliverables: [ - 'Complete feature set with UI', - 'Real-time updates and notifications', - 'Data validation and error handling' - ], - success_criteria: [ - 'All major user stories implemented', - 'Real-time features working reliably', - 'Comprehensive error handling' - ] - }, - phase_3_optimization: { - duration: '1-2 weeks', - objective: 'Testing, optimization, and deployment', - deliverables: [ - 'Comprehensive test suite', - 'Performance optimization', - 'Production deployment' - ], - success_criteria: [ - 'Test coverage >80%', - 'Performance targets met', - 'Successful production deployment' - ] - } - }, - - success_metrics: { - performance: [ - 'API response time <200ms for 95% of requests', - 'UI load time <2 seconds', - 'Support 1000+ concurrent users' - ], - quality: [ - 'Test coverage >80%', - 'Zero critical security vulnerabilities', - 'Mean time to recovery <15 minutes' - ], - business: [ - 'User task completion rate >90%', - 'Feature adoption >60% within first month', - 'User satisfaction score >4.5/5' - ] - }, - - risks_and_mitigation: { - technical_risks: [ - { - risk: 'Integration complexity with legacy systems', - mitigation: 'Phased approach with fallback options' - }, - { - risk: 'Performance issues at scale', - mitigation: 'Load testing and optimization in early phases' - } - ], - business_risks: [ - { - risk: 'Timeline delays due to scope creep', - mitigation: 'Clear requirements and change control process' - } - ] - } - } - }, - - // Simple markdown templates for non-PRP documents - 'markdown_doc': { - name: 'Markdown Document', - icon: '📝', - document_type: 'markdown', - content: { - markdown: `# Document Title - -## Overview - -Provide a brief overview of this document... - -## Content - -Add your content here... - -## Next Steps - -- [ ] Action item 1 -- [ ] Action item 2` - } - }, - - 'meeting_notes': { - name: 'Meeting Notes', - icon: '📋', - document_type: 'meeting_notes', - content: { - meeting_date: new Date().toISOString().split('T')[0], - attendees: ['Person 1', 'Person 2'], - agenda: [ - 'Agenda item 1', - 'Agenda item 2' - ], - notes: 'Meeting discussion notes...', - action_items: [ - { - item: 'Action item 1', - owner: 'Person Name', - due_date: 'YYYY-MM-DD' - } - ], - next_meeting: 'YYYY-MM-DD' - } - } -}; - -/* ——————————————————————————————————————————— */ -/* Main component */ -/* ——————————————————————————————————————————— */ -export const DocsTab = ({ - tasks, - project -}: { - tasks: Task[]; - project?: { - id: string; - title: string; - created_at?: string; - updated_at?: string; - } | null; -}) => { - // Document state - const [documents, setDocuments] = useState([]); - const [selectedDocument, setSelectedDocument] = useState(null); - const [isEditing, setIsEditing] = useState(false); - const [isSaving, setIsSaving] = useState(false); - const [loading, setLoading] = useState(false); - const [showTemplateModal, setShowTemplateModal] = useState(false); - const [showVersionHistory, setShowVersionHistory] = useState(false); - const [viewMode, setViewMode] = useState<'beautiful' | 'markdown'>('beautiful'); - - // Dark mode detection - const [isDarkMode, setIsDarkMode] = useState(false); - - useEffect(() => { - const checkDarkMode = () => { - const htmlElement = document.documentElement; - const hasDarkClass = htmlElement.classList.contains('dark'); - const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - setIsDarkMode(hasDarkClass || prefersDark); - }; - - checkDarkMode(); - - // Listen for changes - const observer = new MutationObserver(checkDarkMode); - observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }); - - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - mediaQuery.addEventListener('change', checkDarkMode); - - return () => { - observer.disconnect(); - mediaQuery.removeEventListener('change', checkDarkMode); - }; - }, []); - - // Knowledge management state - const [showTechnicalModal, setShowTechnicalModal] = useState(false); - const [showBusinessModal, setShowBusinessModal] = useState(false); - const [selectedTechnicalSources, setSelectedTechnicalSources] = useState([]); - const [selectedBusinessSources, setSelectedBusinessSources] = useState([]); - const [showAddSourceModal, setShowAddSourceModal] = useState(false); - const [sourceType, setSourceType] = useState<'technical' | 'business'>('technical'); - const [knowledgeItems, setKnowledgeItems] = useState([]); - const [activeProgressId, setActiveProgressId] = useState(null); - const { showToast } = useToast(); - - // Poll for crawl progress - const crawlProgress = useCrawlProgressPolling(activeProgressId); - - // Load project documents from the project data - const loadProjectDocuments = async () => { - if (!project?.id || !project.docs) return; - - try { - setLoading(true); - - // Use the docs directly from the project data - const projectDocuments: ProjectDoc[] = project.docs.map((doc: any) => ({ - id: doc.id, - title: doc.title || 'Untitled Document', - created_at: doc.created_at, - updated_at: doc.updated_at, - content: doc.content, - document_type: doc.document_type || 'document' - })); - - setDocuments(projectDocuments); - - // Auto-select first document if available and no document is currently selected - if (projectDocuments.length > 0 && !selectedDocument) { - setSelectedDocument(projectDocuments[0]); - } - - console.log(`Loaded ${projectDocuments.length} documents from project data`); - } catch (error) { - console.error('Failed to load documents:', error); - showToast('Failed to load documents', 'error'); - } finally { - setLoading(false); - } - }; - - // Create new document from template - const createDocumentFromTemplate = async (templateKey: string) => { - if (!project?.id) return; - - const template = DOCUMENT_TEMPLATES[templateKey as keyof typeof DOCUMENT_TEMPLATES]; - if (!template) return; - - try { - setIsSaving(true); - - // Create the document in the database first - const newDocument = await projectService.createDocument(project.id, { - title: template.name, - content: template.content, - document_type: template.document_type, - tags: [] - }); - - // Add to documents list with the real document from the database - setDocuments(prev => [...prev, newDocument]); - setSelectedDocument(newDocument); - - console.log('Document created successfully:', newDocument); - showToast('Document created successfully', 'success'); - setShowTemplateModal(false); - } catch (error) { - console.error('Failed to create document:', error); - showToast( - error instanceof Error ? error.message : 'Failed to create document', - 'error' - ); - } finally { - setIsSaving(false); - } - }; - - // Save document changes - const saveDocument = async () => { - if (!selectedDocument || !project?.id) return; - - try { - setIsSaving(true); - - // Call backend API to persist changes - const updatedDocument = await projectService.updateDocument( - project.id, - selectedDocument.id, - { - title: selectedDocument.title, - content: selectedDocument.content, - tags: selectedDocument.tags, - author: selectedDocument.author - } - ); - - // Update local state with backend response - setDocuments(prev => prev.map(doc => - doc.id === selectedDocument.id ? updatedDocument : doc - )); - setSelectedDocument(updatedDocument); - - console.log('Document saved successfully:', updatedDocument); - showToast('Document saved successfully', 'success'); - setIsEditing(false); - } catch (error) { - console.error('Failed to save document:', error); - showToast( - error instanceof Error ? error.message : 'Failed to save document', - 'error' - ); - } finally { - setIsSaving(false); - } - }; - - // Note: Block editing functions removed - now handled by BlockNoteEditor internally - - // Load project data including linked sources - const loadProjectData = async () => { - if (!project?.id) return; - - try { - const response = await fetch(`/api/projects/${project.id}`); - if (!response.ok) throw new Error('Failed to load project data'); - - const projectData = await response.json(); - - // Initialize selected sources from saved project data - const technicalSourceIds = (projectData.technical_sources || []).map((source: any) => source.source_id); - const businessSourceIds = (projectData.business_sources || []).map((source: any) => source.source_id); - - setSelectedTechnicalSources(technicalSourceIds); - setSelectedBusinessSources(businessSourceIds); - - console.log('Loaded project sources:', { - technical: technicalSourceIds, - business: businessSourceIds - }); - } catch (error) { - console.error('Failed to load project data:', error); - showToast('Failed to load project sources', 'error'); - } - }; - - // Load knowledge items and documents on mount - useEffect(() => { - loadKnowledgeItems(); - loadProjectDocuments(); - loadProjectData(); // Load saved sources - - // Cleanup function - return () => { - console.log('🧹 DocsTab: Cleanup'); - // Polling cleanup happens automatically in hooks - }; - }, [project?.id]); - - // Clear selected document when project changes - useEffect(() => { - setSelectedDocument(null); - }, [project?.id]); - - // Handle crawl progress updates - useEffect(() => { - if (crawlProgress.data) { - const status = crawlProgress.data.status; - console.log('📊 Crawl progress update:', crawlProgress.data); - - if (status === 'completed') { - showToast('Crawling completed successfully', 'success'); - loadKnowledgeItems(); // Reload knowledge items - setActiveProgressId(null); // Clear active progress - } else if (status === 'failed' || status === 'error') { - const errorMsg = crawlProgress.data.error || 'Crawling failed'; - showToast(`Crawling failed: ${errorMsg}`, 'error'); - setActiveProgressId(null); // Clear active progress - } - } - }, [crawlProgress.data, showToast]); - - // Existing knowledge loading function - const loadKnowledgeItems = async (knowledgeType?: 'technical' | 'business') => { - try { - setLoading(true); - const response = await knowledgeBaseService.getKnowledgeItems({ - knowledge_type: knowledgeType, - page: 1, - per_page: 50 - }); - setKnowledgeItems(response.items); - } catch (error) { - console.error('Failed to load knowledge items:', error); - showToast('Failed to load knowledge items', 'error'); - setKnowledgeItems([]); - } finally { - setLoading(false); - } - }; - - // Knowledge management helper functions (simplified for brevity) - const transformToLegacyFormat = (items: KnowledgeItem[]) => { - return items.map(item => ({ - id: item.id, - title: item.title, - type: item.metadata.source_type || 'url', - lastUpdated: new Date(item.updated_at).toLocaleDateString() - })); - }; - - const technicalSources = transformToLegacyFormat( - knowledgeItems.filter(item => item.metadata.knowledge_type === 'technical') - ); - - const businessSources = transformToLegacyFormat( - knowledgeItems.filter(item => item.metadata.knowledge_type === 'business') - ); - - const toggleTechnicalSource = (id: string) => { - setSelectedTechnicalSources(prev => prev.includes(id) ? prev.filter(item => item !== id) : [...prev, id]); - }; - const toggleBusinessSource = (id: string) => { - setSelectedBusinessSources(prev => prev.includes(id) ? prev.filter(item => item !== id) : [...prev, id]); - }; - const saveTechnicalSources = async () => { - if (!project?.id) return; - - try { - await projectService.updateProject(project.id, { - technical_sources: selectedTechnicalSources - }); - showToast('Technical sources updated successfully', 'success'); - setShowTechnicalModal(false); - // Reload project data to reflect the changes - await loadProjectData(); - } catch (error) { - console.error('Failed to save technical sources:', error); - showToast('Failed to update technical sources', 'error'); - } - }; - - const saveBusinessSources = async () => { - if (!project?.id) return; - - try { - await projectService.updateProject(project.id, { - business_sources: selectedBusinessSources - }); - showToast('Business sources updated successfully', 'success'); - setShowBusinessModal(false); - // Reload project data to reflect the changes - await loadProjectData(); - } catch (error) { - console.error('Failed to save business sources:', error); - showToast('Failed to update business sources', 'error'); - } - }; - - const handleStartCrawl = async (progressId: string, initialData: any) => { - console.log(`🚀 Starting crawl tracking for: ${progressId}`); - setActiveProgressId(progressId); - showToast('Crawling started - tracking progress', 'success'); - }; - - const openAddSourceModal = (type: 'technical' | 'business') => { - setSourceType(type); - setShowAddSourceModal(true); - }; - - return ( -
-
- {/* Document Header */} -
-
-
-

- Project Docs -

-

{project?.title || 'No project selected'}

-
- - {/* View mode and action buttons */} -
- {selectedDocument && ( -
- {/* View mode toggle for all documents */} -
- - -
- - {isEditing && ( - - )} -
- )} - - -
-
-
- - {/* Document Cards Container */} -
-
- {documents.map(doc => ( - { - try { - // Call API to delete from database first - await projectService.deleteDocument(project.id, docId); - - // Then remove from local state - setDocuments(prev => prev.filter(d => d.id !== docId)); - if (selectedDocument?.id === docId) { - setSelectedDocument(documents.find(d => d.id !== docId) || null); - } - showToast('Document deleted', 'success'); - } catch (error) { - console.error('Failed to delete document:', error); - showToast('Failed to delete document', 'error'); - } - }} - isDarkMode={isDarkMode} - /> - ))} - - {/* Add New Document Card */} - setShowTemplateModal(true)} /> -
-
- - {/* Document Content */} - {loading ? ( -
-
Loading documents...
-
- ) : selectedDocument ? ( - // Show PRPViewer in beautiful mode for all documents - viewMode === 'beautiful' ? ( -
- -
- ) : ( - { - try { - setIsSaving(true); - - // Call backend API to persist changes - const savedDocument = await projectService.updateDocument( - project.id, - updatedDocument.id, - { - title: updatedDocument.title, - content: updatedDocument.content, - tags: updatedDocument.tags, - author: updatedDocument.author - } - ); - - // Update local state with backend response - setSelectedDocument(savedDocument); - setDocuments(prev => prev.map(doc => - doc.id === updatedDocument.id ? savedDocument : doc - )); - - console.log('Document saved via MilkdownEditor'); - showToast('Document saved successfully', 'success'); - } catch (error) { - console.error('Failed to save document:', error); - showToast( - error instanceof Error ? error.message : 'Failed to save document', - 'error' - ); - } finally { - setIsSaving(false); - } - }} - className="mb-8" - /> - ) - ) : ( -
- -

No documents found

-

Create a new document to get started

-
- )} - - {/* Knowledge Sections */} -
- technicalSources.find(source => source.id === id))} - onAddClick={() => setShowTechnicalModal(true)} - /> - businessSources.find(source => source.id === id))} - onAddClick={() => setShowBusinessModal(true)} - /> -
-
- - {/* Template Selection Modal */} - {showTemplateModal && ( - setShowTemplateModal(false)} - onSelectTemplate={createDocumentFromTemplate} - isCreating={isSaving} - /> - )} - - {/* Existing Modals (simplified for brevity) */} - {showTechnicalModal && ( - setShowTechnicalModal(false)} - onAddSource={() => openAddSourceModal('technical')} - /> - )} - - {showBusinessModal && ( - setShowBusinessModal(false)} - onAddSource={() => openAddSourceModal('business')} - /> - )} - - {showAddSourceModal && ( - setShowAddSourceModal(false)} - onSuccess={() => { - loadKnowledgeItems(); - setShowAddSourceModal(false); - }} - onStartCrawl={handleStartCrawl} - /> - )} - - {/* Version History Modal */} - {showVersionHistory && project && ( - setShowVersionHistory(false)} - onRestore={() => { - // Reload documents after restore - loadProjectDocuments(); - setShowVersionHistory(false); - }} - /> - )} -
- ); -}; - - -/* ——————————————————————————————————————————— */ -/* Helper components */ -/* ——————————————————————————————————————————— */ - -// ArchonEditor component removed - replaced with BlockNoteEditor - -// Template Modal Component -const TemplateModal: React.FC<{ - onClose: () => void; - onSelectTemplate: (templateKey: string) => void; - isCreating: boolean; -}> = ({ onClose, onSelectTemplate, isCreating }) => { - const templates = Object.entries(DOCUMENT_TEMPLATES); - - const getTemplateDescription = (key: string, template: any) => { - const descriptions: Record = { - 'prp_base': 'Comprehensive template for implementing new features with full context, validation loops, and structured implementation blueprint.', - 'prp_task': 'Focused template for specific tasks or bug fixes with clear steps and validation criteria.', - 'prp_planning': 'Strategic template for architecture planning and system design with risk analysis and success metrics.', - 'markdown_doc': 'Simple markdown document for general documentation and notes.', - 'meeting_notes': 'Structured template for meeting notes with attendees, agenda, and action items.' - }; - return descriptions[key] || 'Document template'; - }; - - return ( -
-
- -
-
-

- Choose a Template -

- -
- -
- {templates.map(([key, template]) => ( - - ))} -
- - {isCreating && ( -
-
- Creating document... -
- )} -
-
-
- ); -}; - -const KnowledgeSection: React.FC<{ - title: string; - color: 'blue' | 'purple' | 'pink' | 'orange'; - sources: any[]; - onAddClick: () => void; -}> = ({ - title, - color, - sources = [], - onAddClick -}) => { - const colorMap = { - blue: { - bg: 'bg-blue-500/10', - border: 'border-blue-500/30', - text: 'text-blue-600 dark:text-blue-400', - buttonBg: 'bg-blue-500/20', - buttonHover: 'hover:bg-blue-500/30', - buttonBorder: 'border-blue-500/40', - buttonShadow: 'hover:shadow-[0_0_15px_rgba(59,130,246,0.3)]' - }, - purple: { - bg: 'bg-purple-500/10', - border: 'border-purple-500/30', - text: 'text-purple-600 dark:text-purple-400', - buttonBg: 'bg-purple-500/20', - buttonHover: 'hover:bg-purple-500/30', - buttonBorder: 'border-purple-500/40', - buttonShadow: 'hover:shadow-[0_0_15px_rgba(168,85,247,0.3)]' - }, - pink: { - bg: 'bg-pink-500/10', - border: 'border-pink-500/30', - text: 'text-pink-600 dark:text-pink-400', - buttonBg: 'bg-pink-500/20', - buttonHover: 'hover:bg-pink-500/30', - buttonBorder: 'border-pink-500/40', - buttonShadow: 'hover:shadow-[0_0_15px_rgba(236,72,153,0.3)]' - }, - orange: { - bg: 'bg-orange-500/10', - border: 'border-orange-500/30', - text: 'text-orange-600 dark:text-orange-400', - buttonBg: 'bg-orange-500/20', - buttonHover: 'hover:bg-orange-500/30', - buttonBorder: 'border-orange-500/40', - buttonShadow: 'hover:shadow-[0_0_15px_rgba(249,115,22,0.3)]' - } - }; - return
-
-

- - {title} -

- -
-
-
- {sources && sources.length > 0 ?
- {sources.map(source => source &&
- {source.type === 'url' ? : } -
-
- {source.title} -
-
- Updated {source.lastUpdated} -
-
-
)} -
:
-

No knowledge sources added yet

-

- Click "Add Sources" to select relevant documents -

-
} -
-
; -}; - -const SourceSelectionModal: React.FC<{ - title: string; - sources: any[]; - selectedSources: string[]; - onToggleSource: (id: string) => void; - onSave: () => void; - onClose: () => void; - onAddSource: () => void; -}> = ({ - title, - sources, - selectedSources, - onToggleSource, - onSave, - onClose, - onAddSource -}) => { - const [searchQuery, setSearchQuery] = useState(''); - // Filter sources based on search query - const filteredSources = sources.filter(source => source.title.toLowerCase().includes(searchQuery.toLowerCase())); - return
-
-
-
-

- {title} -

- -
- {/* Search and Add Source */} -
-
- - setSearchQuery(e.target.value)} placeholder="Search sources..." className="w-full bg-white/50 dark:bg-black/70 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white rounded-md py-2 pl-10 pr-3 focus:outline-none focus:border-blue-400 focus:shadow-[0_0_10px_rgba(59,130,246,0.2)] transition-all duration-300" /> -
- -
- {/* Sources List */} -
- {filteredSources.length > 0 ?
- {filteredSources.map(source =>
onToggleSource(source.id)} className={`flex items-center gap-3 p-3 rounded-md cursor-pointer transition-all duration-200 - ${selectedSources.includes(source.id) ? 'bg-blue-100/80 dark:bg-blue-900/30 border border-blue-300 dark:border-blue-500/50' : 'bg-white/50 dark:bg-black/30 border border-gray-200 dark:border-gray-800 hover:border-gray-300 dark:hover:border-gray-700'}`}> -
- {selectedSources.includes(source.id) && } -
- {source.type === 'url' ? : } -
-
- {source.title} -
-
- Updated {source.lastUpdated} -
-
-
)} -
:
- No sources found matching your search -
} -
- {/* Action Buttons */} -
- - -
-
-
-
; -}; - -interface AddKnowledgeModalProps { - sourceType: 'technical' | 'business'; - onClose: () => void; - onSuccess: () => void; - onStartCrawl: (progressId: string, initialData: any) => void; -} - -const AddKnowledgeModal = ({ - sourceType, - onClose, - onSuccess, - onStartCrawl -}: AddKnowledgeModalProps) => { - const [method, setMethod] = useState<'url' | 'file'>('url'); - const [url, setUrl] = useState(''); - const [updateFrequency, setUpdateFrequency] = useState('7'); - const [tags, setTags] = useState([]); - const [newTag, setNewTag] = useState(''); - const [selectedFile, setSelectedFile] = useState(null); - const [loading, setLoading] = useState(false); - const { showToast } = useToast(); - - const handleSubmit = async () => { - try { - setLoading(true); - - if (method === 'url') { - if (!url.trim()) { - showToast('Please enter a URL', 'error'); - return; - } - - const result = await knowledgeBaseService.crawlUrl({ - url: url.trim(), - knowledge_type: sourceType, - tags, - update_frequency: parseInt(updateFrequency) - }); - - // Check if result contains a progressId for progress tracking - if ((result as any).progressId) { - // Start progress tracking via polling - onStartCrawl((result as any).progressId, { - currentUrl: url.trim(), - totalPages: 0, - processedPages: 0 - }); - - showToast('Crawling started - tracking progress', 'success'); - onClose(); // Close modal immediately - } else { - // Fallback for immediate response - showToast((result as any).message || 'Crawling started', 'success'); - onSuccess(); - } - } else { - if (!selectedFile) { - showToast('Please select a file', 'error'); - return; - } - - const result = await knowledgeBaseService.uploadDocument(selectedFile, { - knowledge_type: sourceType, - tags - }); - - showToast((result as any).message || 'Document uploaded successfully', 'success'); - onSuccess(); - } - } catch (error) { - console.error('Failed to add knowledge:', error); - showToast('Failed to add knowledge source', 'error'); - } finally { - setLoading(false); - } - }; - - return
- -

- Add {sourceType === 'technical' ? 'Technical' : 'Business'} Knowledge Source -

- - {/* Source Type Selection */} -
- - -
- - {/* URL Input */} - {method === 'url' &&
- setUrl(e.target.value)} placeholder="https://..." accentColor="blue" /> -
} - - {/* File Upload */} - {method === 'file' &&
- - setSelectedFile(e.target.files?.[0] || null)} - className="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-purple-50 file:text-purple-700 hover:file:bg-purple-100" - /> -

- Supports PDF, MD, DOC up to 10MB -

-
} - - {/* Update Frequency */} - {method === 'url' &&
- setNewTag(e.target.value)} onKeyDown={e => { - if (e.key === 'Enter' && newTag.trim()) { - setTags([...tags, newTag.trim()]); - setNewTag(''); - } - }} placeholder="Add tags..." accentColor="purple" /> -
- - {/* Action Buttons */} -
- - -
-
-
; -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/DocumentCard.tsx b/archon-ui-main/src/components/project-tasks/DocumentCard.tsx deleted file mode 100644 index e6ec5b9b70..0000000000 --- a/archon-ui-main/src/components/project-tasks/DocumentCard.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import React, { useState } from 'react'; -import { Rocket, Code, Briefcase, Users, FileText, X, Plus, Clipboard } from 'lucide-react'; -import { useToast } from '../../contexts/ToastContext'; - -export interface ProjectDoc { - id: string; - title: string; - content: any; - document_type?: string; - updated_at: string; - created_at?: string; -} - -interface DocumentCardProps { - document: ProjectDoc; - isActive: boolean; - onSelect: (doc: ProjectDoc) => void; - onDelete: (docId: string) => void; - isDarkMode: boolean; -} - -export const DocumentCard: React.FC = ({ - document, - isActive, - onSelect, - onDelete, - isDarkMode -}) => { - const [showDelete, setShowDelete] = useState(false); - const { showToast } = useToast(); - - const getDocumentIcon = (type?: string) => { - switch (type) { - case 'prp': return ; - case 'technical': return ; - case 'business': return ; - case 'meeting_notes': return ; - default: return ; - } - }; - - const getTypeColor = (type?: string) => { - switch (type) { - case 'prp': return 'bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/30'; - case 'technical': return 'bg-green-500/10 text-green-600 dark:text-green-400 border-green-500/30'; - case 'business': return 'bg-purple-500/10 text-purple-600 dark:text-purple-400 border-purple-500/30'; - case 'meeting_notes': return 'bg-orange-500/10 text-orange-600 dark:text-orange-400 border-orange-500/30'; - default: return 'bg-gray-500/10 text-gray-600 dark:text-gray-400 border-gray-500/30'; - } - }; - - const handleCopyId = (e: React.MouseEvent) => { - e.stopPropagation(); - navigator.clipboard.writeText(document.id); - showToast('Document ID copied to clipboard', 'success'); - - // Visual feedback - const button = e.currentTarget; - const originalHTML = button.innerHTML; - button.innerHTML = '
Copied
'; - setTimeout(() => { - button.innerHTML = originalHTML; - }, 2000); - }; - - return ( -
onSelect(document)} - onMouseEnter={() => setShowDelete(true)} - onMouseLeave={() => setShowDelete(false)} - > - {/* Document Type Badge */} -
- {getDocumentIcon(document.document_type)} - {document.document_type || 'document'} -
- - {/* Title */} -

- {document.title} -

- - {/* Metadata */} -

- {new Date(document.updated_at || document.created_at || Date.now()).toLocaleDateString()} -

- - {/* ID Display Section - Always visible for active, hover for others */} -
- - {document.id.slice(0, 8)}... - - -
- - {/* Delete Button */} - {showDelete && !isActive && ( - - )} -
- ); -}; - -// New Document Card Component -interface NewDocumentCardProps { - onClick: () => void; -} - -export const NewDocumentCard: React.FC = ({ onClick }) => { - return ( -
- - New Document -
- ); -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/DraggableTaskCard.tsx b/archon-ui-main/src/components/project-tasks/DraggableTaskCard.tsx deleted file mode 100644 index 62435e3bec..0000000000 --- a/archon-ui-main/src/components/project-tasks/DraggableTaskCard.tsx +++ /dev/null @@ -1,275 +0,0 @@ -import React, { useRef, useState } from 'react'; -import { useDrag, useDrop } from 'react-dnd'; -import { Edit, Trash2, RefreshCw, Tag, Clipboard } from 'lucide-react'; -import { Task } from './TaskTableView'; -import { ItemTypes, getAssigneeIcon, getAssigneeGlow, getOrderColor, getOrderGlow } from '../../lib/task-utils'; -import { useToast } from '../../contexts/ToastContext'; - -export interface DraggableTaskCardProps { - task: Task; - index: number; - onView: () => void; - onComplete: () => void; - onDelete: (task: Task) => void; - onTaskReorder: (taskId: string, targetIndex: number, status: Task['status']) => void; - hoveredTaskId?: string | null; - onTaskHover?: (taskId: string | null) => void; - selectedTasks?: Set; - onTaskSelect?: (taskId: string) => void; -} - -export const DraggableTaskCard = ({ - task, - index, - onView, - onComplete, - onDelete, - onTaskReorder, - hoveredTaskId, - onTaskHover, - selectedTasks, - onTaskSelect, -}: DraggableTaskCardProps) => { - const { showToast } = useToast(); - - const [{ isDragging }, drag] = useDrag({ - type: ItemTypes.TASK, - item: { id: task.id, status: task.status, index }, - collect: (monitor) => ({ - isDragging: !!monitor.isDragging() - }) - }); - - const [, drop] = useDrop({ - accept: ItemTypes.TASK, - hover: (draggedItem: { id: string; status: Task['status']; index: number }, monitor) => { - if (!monitor.isOver({ shallow: true })) return; - if (draggedItem.id === task.id) return; - if (draggedItem.status !== task.status) return; - - const draggedIndex = draggedItem.index; - const hoveredIndex = index; - - if (draggedIndex === hoveredIndex) return; - - - // Move the task immediately for visual feedback (same pattern as table view) - onTaskReorder(draggedItem.id, hoveredIndex, task.status); - - // Update the dragged item's index to prevent re-triggering - draggedItem.index = hoveredIndex; - } - }); - - const [isFlipped, setIsFlipped] = useState(false); - - const toggleFlip = (e: React.MouseEvent) => { - e.stopPropagation(); - setIsFlipped(!isFlipped); - }; - - const isHighlighted = hoveredTaskId === task.id; - const isSelected = selectedTasks?.has(task.id) || false; - - const handleMouseEnter = () => { - onTaskHover?.(task.id); - }; - - const handleMouseLeave = () => { - onTaskHover?.(null); - }; - - const handleTaskClick = (e: React.MouseEvent) => { - if (e.ctrlKey || e.metaKey) { - e.stopPropagation(); - onTaskSelect?.(task.id); - } - }; - - - // Card styling - using CSS-based height animation for better scrolling - - // Card styling constants - const cardScale = 'scale-100'; - const cardOpacity = 'opacity-100'; - - // Subtle highlight effect for related tasks - applied to the card, not parent - const highlightGlow = isHighlighted - ? 'border-cyan-400/50 shadow-[0_0_8px_rgba(34,211,238,0.2)]' - : ''; - - // Selection styling - const selectionGlow = isSelected - ? 'border-blue-500 shadow-[0_0_12px_rgba(59,130,246,0.4)] bg-blue-50/30 dark:bg-blue-900/20' - : ''; - - // Simplified hover effect - just a glowing border - const hoverEffectClasses = 'group-hover:border-cyan-400/70 dark:group-hover:border-cyan-500/50 group-hover:shadow-[0_0_15px_rgba(34,211,238,0.4)] dark:group-hover:shadow-[0_0_15px_rgba(34,211,238,0.6)]'; - - // Base card styles with proper rounded corners - const cardBaseStyles = 'bg-gradient-to-b from-white/80 to-white/60 dark:from-white/10 dark:to-black/30 border border-gray-200 dark:border-gray-700 rounded-lg'; - - // Transition settings - const transitionStyles = 'transition-all duration-200 ease-in-out'; - - return ( -
drag(drop(node))} - style={{ - perspective: '1000px', - transformStyle: 'preserve-3d' - }} - className={`flip-card w-full min-h-[140px] cursor-move relative ${cardScale} ${cardOpacity} ${isDragging ? 'opacity-50 scale-90' : ''} ${transitionStyles} group`} - onMouseEnter={handleMouseEnter} - onMouseLeave={handleMouseLeave} - onClick={handleTaskClick} - > -
- {/* Front side with subtle hover effect */} -
- {/* Priority indicator */} -
- - {/* Content container with fixed padding - exactly matching back side structure */} -
-
-
- - {task.feature} -
- - {/* Task order display */} -
- {task.task_order} -
- - {/* Action buttons group */} -
- - - -
-
- -

- {task.title} -

- - {/* Spacer to push assignee section to bottom */} -
- -
-
-
- {getAssigneeIcon(task.assignee?.name || 'User')} -
- {task.assignee?.name || 'User'} -
- -
-
-
- - {/* Back side */} - {/* Back side with same hover effect */} -
- {/* Priority indicator */} -
- - {/* Content container with fixed padding */} -
-
-

- {task.title} -

- -
- - {/* Description container with absolute positioning inside parent bounds */} -
-
-

{task.description}

-
-
-
-
-
-
- ); -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/EditTaskModal.tsx b/archon-ui-main/src/components/project-tasks/EditTaskModal.tsx deleted file mode 100644 index fc00312c37..0000000000 --- a/archon-ui-main/src/components/project-tasks/EditTaskModal.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import React, { memo, useCallback, useMemo, useState, useEffect, useRef } from 'react'; -import { X } from 'lucide-react'; -import { Button } from '../ui/Button'; -import { ArchonLoadingSpinner } from '../animations/Animations'; -import { DebouncedInput, FeatureInput } from './TaskInputComponents'; -import type { Task } from './TaskTableView'; - -interface EditTaskModalProps { - isModalOpen: boolean; - editingTask: Task | null; - projectFeatures: any[]; - isLoadingFeatures: boolean; - isSavingTask: boolean; - onClose: () => void; - onSave: (task: Task) => Promise; - getTasksForPrioritySelection: (status: Task['status']) => Array<{value: number, label: string}>; -} - -const ASSIGNEE_OPTIONS = ['User', 'Archon', 'AI IDE Agent'] as const; - -// Removed debounce utility - now using DebouncedInput component - -export const EditTaskModal = memo(({ - isModalOpen, - editingTask, - projectFeatures, - isLoadingFeatures, - isSavingTask, - onClose, - onSave, - getTasksForPrioritySelection -}: EditTaskModalProps) => { - const [localTask, setLocalTask] = useState(null); - - // Diagnostic: Track render count - const renderCount = useRef(0); - - useEffect(() => { - renderCount.current++; - console.log(`[EditTaskModal] Render #${renderCount.current}`, { - localTask: localTask?.title, - isModalOpen, - timestamp: Date.now() - }); - }); - - // Sync local state with editingTask when it changes - useEffect(() => { - if (editingTask) { - setLocalTask(editingTask); - } - }, [editingTask]); - - const priorityOptions = useMemo(() => { - console.log(`[EditTaskModal] Recalculating priorityOptions for status: ${localTask?.status || 'todo'}`); - return getTasksForPrioritySelection(localTask?.status || 'todo'); - }, [localTask?.status, getTasksForPrioritySelection]); - - // Memoized handlers for input changes - const handleTitleChange = useCallback((value: string) => { - console.log('[EditTaskModal] Title changed via DebouncedInput:', value); - setLocalTask(prev => prev ? { ...prev, title: value } : null); - }, []); - - const handleDescriptionChange = useCallback((value: string) => { - console.log('[EditTaskModal] Description changed via DebouncedInput:', value); - setLocalTask(prev => prev ? { ...prev, description: value } : null); - }, []); - - const handleFeatureChange = useCallback((value: string) => { - console.log('[EditTaskModal] Feature changed via FeatureInput:', value); - setLocalTask(prev => prev ? { ...prev, feature: value } : null); - }, []); - - const handleStatusChange = useCallback((e: React.ChangeEvent) => { - const newStatus = e.target.value as Task['status']; - const newOrder = getTasksForPrioritySelection(newStatus)[0]?.value || 1; - setLocalTask(prev => prev ? { ...prev, status: newStatus, task_order: newOrder } : null); - }, [getTasksForPrioritySelection]); - - const handlePriorityChange = useCallback((e: React.ChangeEvent) => { - setLocalTask(prev => prev ? { ...prev, task_order: parseInt(e.target.value) } : null); - }, []); - - const handleAssigneeChange = useCallback((e: React.ChangeEvent) => { - setLocalTask(prev => prev ? { - ...prev, - assignee: { name: e.target.value as 'User' | 'Archon' | 'AI IDE Agent', avatar: '' } - } : null); - }, []); - - const handleSave = useCallback(() => { - if (localTask) { - onSave(localTask); - } - }, [localTask, onSave]); - - const handleClose = useCallback(() => { - onClose(); - }, [onClose]); - - if (!isModalOpen) return null; - - return ( -
-
-
-
-

- {editingTask?.id ? 'Edit Task' : 'New Task'} -

- -
- -
-
- - -
- -
- - -
- -
-
- - -
- -
- - -
-
- -
-
- - -
- -
- - -
-
-
- - -
- - -
-
-
-
- ); -}, (prevProps, nextProps) => { - // Custom comparison function to prevent unnecessary re-renders - // Only re-render if these specific props change - const isEqual = ( - prevProps.isModalOpen === nextProps.isModalOpen && - prevProps.editingTask?.id === nextProps.editingTask?.id && - prevProps.editingTask?.title === nextProps.editingTask?.title && - prevProps.editingTask?.description === nextProps.editingTask?.description && - prevProps.editingTask?.status === nextProps.editingTask?.status && - prevProps.editingTask?.assignee?.name === nextProps.editingTask?.assignee?.name && - prevProps.editingTask?.feature === nextProps.editingTask?.feature && - prevProps.editingTask?.task_order === nextProps.editingTask?.task_order && - prevProps.isSavingTask === nextProps.isSavingTask && - prevProps.isLoadingFeatures === nextProps.isLoadingFeatures && - prevProps.projectFeatures === nextProps.projectFeatures // Reference equality check - ); - - if (!isEqual) { - console.log('[EditTaskModal] Props changed, re-rendering'); - } - - return isEqual; -}); - -EditTaskModal.displayName = 'EditTaskModal'; \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/FeaturesTab.tsx b/archon-ui-main/src/components/project-tasks/FeaturesTab.tsx deleted file mode 100644 index 10add1b4dc..0000000000 --- a/archon-ui-main/src/components/project-tasks/FeaturesTab.tsx +++ /dev/null @@ -1,814 +0,0 @@ -import { useCallback, useState, useEffect, useMemo } from 'react' -import '@xyflow/react/dist/style.css' -import { - ReactFlow, - Node, - Edge, - Controls, - MarkerType, - NodeProps, - Handle, - Position, - NodeChange, - applyNodeChanges, - EdgeChange, - applyEdgeChanges, - Connection, - addEdge, -} from '@xyflow/react' -import { Layout, Component as ComponentIcon, X, Trash2, Edit, Save } from 'lucide-react' -import { projectService } from '../../services/projectService' -import { useToast } from '../../contexts/ToastContext' - -// Define custom node types following React Flow v12 pattern -type PageNodeData = { - label: string; - type: string; - route: string; - components: number; -}; - -type ServiceNodeData = { - label: string; - type: string; -}; - -// Define union type for all custom nodes -type CustomNodeTypes = Node | Node; - -// Custom node components -const PageNode = ({ data }: NodeProps) => { - const pageData = data as PageNodeData; - return ( -
- -
-
- -
{pageData.label}
-
-
{pageData.type}
-
-
Route: {pageData.route}
-
Components: {pageData.components}
-
-
- -
- ); -}; - -const ServiceNode = ({ data }: NodeProps) => { - const serviceData = data as ServiceNodeData; - return ( -
- -
-
- -
{serviceData.label}
-
-
{serviceData.type}
-
- -
- ); -}; - -const nodeTypes = { - page: PageNode, - service: ServiceNode, -} - -// Default/fallback nodes for when project has no features data -const defaultNodes: Node[] = [ - { - id: 'start', - type: 'page', - data: { - label: 'Start App', - type: 'Entry Point', - route: '/', - components: 3, - }, - position: { - x: 400, - y: 0, - }, - }, - { - id: 'home', - type: 'page', - data: { - label: 'Homepage', - type: 'Main View', - route: '/home', - components: 6, - }, - position: { - x: 400, - y: 150, - }, - }, -]; - -// Default/fallback edges -const defaultEdges: Edge[] = [ - { - id: 'start-home', - source: 'start', - target: 'home', - animated: true, - style: { - stroke: '#22d3ee', - }, - markerEnd: { - type: MarkerType.ArrowClosed, - color: '#22d3ee', - }, - }, -]; - -interface FeaturesTabProps { - project?: { - id: string; - title: string; - features?: any[]; - } | null; -} - -export const FeaturesTab = ({ project }: FeaturesTabProps) => { - const [nodes, setNodes] = useState([]) - const [edges, setEdges] = useState([]) - const [loading, setLoading] = useState(true) - const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) - const [nodeToDelete, setNodeToDelete] = useState(null) - const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false) - const [editingNode, setEditingNode] = useState(null) - const [showEditModal, setShowEditModal] = useState(false) - const [isSaving, setIsSaving] = useState(false) - - const { showToast } = useToast() - - // Load features from project or show empty state - useEffect(() => { - if (project?.features && Array.isArray(project.features) && project.features.length > 0) { - // Ensure all nodes have required properties with defaults - const normalizedNodes = project.features.map((node: any, index: number) => ({ - ...node, - // Ensure position exists with sensible defaults - position: node.position || { - x: 250 + (index % 3) * 250, // Spread horizontally - y: 200 + Math.floor(index / 3) * 150 // Stack vertically - }, - // Ensure type exists (fallback based on data structure) - type: node.type || (node.data?.route ? 'page' : 'service'), - // Ensure data exists - data: node.data || { label: 'Unknown', type: 'Unknown Component' } - })); - - setNodes(normalizedNodes) - // Generate edges based on the flow (simplified logic) - const generatedEdges = generateEdgesFromNodes(normalizedNodes) - setEdges(generatedEdges) - } else { - // Show empty state - no nodes or edges - setNodes([]) - setEdges([]) - } - setLoading(false) - }, [project]) - - // Helper function to generate edges based on node positioning and types - const generateEdgesFromNodes = (nodes: Node[]): Edge[] => { - const edges: Edge[] = [] - - // Sort nodes by y position to create a logical flow (with safety check for position) - const sortedNodes = [...nodes].sort((a, b) => { - const aY = a.position?.y || 0; - const bY = b.position?.y || 0; - return aY - bY; - }) - - for (let i = 0; i < sortedNodes.length - 1; i++) { - const currentNode = sortedNodes[i] - const nextNode = sortedNodes[i + 1] - - // Connect sequential nodes with appropriate styling - const edgeStyle = currentNode.type === 'service' ? '#d946ef' : '#22d3ee' - - edges.push({ - id: `${currentNode.id}-${nextNode.id}`, - source: currentNode.id, - target: nextNode.id, - animated: true, - style: { - stroke: edgeStyle, - }, - markerEnd: { - type: MarkerType.ArrowClosed, - color: edgeStyle, - }, - }) - } - - return edges - } - - const onNodesChange = useCallback( - (changes: NodeChange[]) => { - setNodes((nds) => applyNodeChanges(changes, nds)) - setHasUnsavedChanges(true) - }, - [], - ) - - const onEdgesChange = useCallback( - (changes: EdgeChange[]) => { - setEdges((eds) => applyEdgeChanges(changes, eds)) - setHasUnsavedChanges(true) - }, - [], - ) - - const onConnect = useCallback( - (connection: Connection) => { - const sourceNode = nodes.find((node) => node.id === connection.source) - // Set edge color based on source node type - const edgeStyle = - sourceNode?.type === 'service' - ? { - stroke: '#d946ef', - } - : // Fuchsia for service nodes - { - stroke: '#22d3ee', - } // Cyan for page nodes - setEdges((eds) => - addEdge( - { - ...connection, - animated: true, - style: edgeStyle, - markerEnd: { - type: MarkerType.ArrowClosed, - color: edgeStyle.stroke, - }, - }, - eds, - ), - ) - setHasUnsavedChanges(true) - }, - [nodes], - ) - - const saveToDatabase = async (nodesToSave = nodes, edgesToSave = edges) => { - if (!project?.id) { - console.error('❌ No project ID available for saving features'); - return; - } - - setIsSaving(true); - try { - console.log('💾 Saving features to database...'); - await projectService.updateProject(project.id, { - features: nodesToSave - }); - console.log('✅ Features saved successfully'); - setHasUnsavedChanges(false); - } catch (error) { - console.error('❌ Failed to save features:', error); - throw error; - } finally { - setIsSaving(false); - } - }; - - const handleManualSave = async () => { - await saveToDatabase(); - }; - - const addPageNode = async () => { - const newNode: Node = { - id: `page-${Date.now()}`, - type: 'page', - data: { - label: `New Page`, - type: 'Page Component', - route: '/new-page', - components: 0, - }, - position: { - x: 250, - y: 200, - }, - } - const newNodes = [...nodes, newNode]; - setNodes(newNodes); - setHasUnsavedChanges(true); - - // Auto-save when adding - try { - await saveToDatabase(newNodes, edges); - } catch (error) { - // Revert on error - setNodes(nodes); - } - } - - const addServiceNode = async () => { - const newNode: Node = { - id: `service-${Date.now()}`, - type: 'service', - data: { - label: 'New Service', - type: 'Service Component', - }, - position: { - x: 250, - y: 200, - }, - } - const newNodes = [...nodes, newNode]; - setNodes(newNodes); - setHasUnsavedChanges(true); - - // Auto-save when adding - try { - await saveToDatabase(newNodes, edges); - } catch (error) { - // Revert on error - setNodes(nodes); - } - } - - const handleDeleteNode = useCallback(async (event: React.MouseEvent, nodeId: string) => { - event.stopPropagation(); - - if (!project?.id) { - console.error('❌ No project ID available for deleting node'); - return; - } - - // Show custom confirmation dialog - setNodeToDelete(nodeId); - setShowDeleteConfirm(true); - }, [project?.id]); - - const confirmDelete = useCallback(async () => { - if (!nodeToDelete) return; - - console.log('🗑️ Deleting node:', nodeToDelete); - - try { - // Remove node from UI - const newNodes = nodes.filter(node => node.id !== nodeToDelete); - - // Remove any edges connected to this node - const newEdges = edges.filter(edge => - edge.source !== nodeToDelete && edge.target !== nodeToDelete - ); - - setNodes(newNodes); - setEdges(newEdges); - - // Save to database - await saveToDatabase(newNodes, newEdges); - showToast('Node deleted successfully', 'success'); - - // Close confirmation dialog - setShowDeleteConfirm(false); - setNodeToDelete(null); - } catch (error) { - console.error('❌ Failed to delete node:', error); - // Revert UI changes on error - setNodes(nodes); - setEdges(edges); - showToast('Failed to delete node', 'error'); - } - }, [nodeToDelete, nodes, edges]); - - const cancelDelete = useCallback(() => { - setShowDeleteConfirm(false); - setNodeToDelete(null); - }, []); - - const handleNodeClick = useCallback((event: React.MouseEvent, node: Node) => { - setEditingNode(node); - setShowEditModal(true); - }, []); - - const saveNodeChanges = async (updatedNode: Node) => { - // Update local state first - const newNodes = nodes.map(node => - node.id === updatedNode.id ? updatedNode : node - ); - setNodes(newNodes); - - // Save to database - await saveToDatabase(newNodes, edges); - - setShowEditModal(false); - setEditingNode(null); - }; - - // Memoize node types with delete and edit functionality - const nodeTypes = useMemo(() => ({ - page: ({ data, id }: NodeProps) => { - const pageData = data as any; - return ( -
- -
{ - const actualNode = nodes.find(node => node.id === id); - if (actualNode) { - handleNodeClick(e, actualNode); - } - }} - > -
-
- -
{pageData.label}
-
-
- - -
-
-
{pageData.type}
-
-
Route: {pageData.route}
-
Components: {pageData.components}
-
-
- -
- ); - }, - service: ({ data, id }: NodeProps) => { - const serviceData = data as any; - return ( -
- -
{ - const actualNode = nodes.find(node => node.id === id); - if (actualNode) { - handleNodeClick(e, actualNode); - } - }} - > -
-
- -
{serviceData.label}
-
-
- - -
-
-
{serviceData.type}
-
- -
- ); - } - }), [handleNodeClick, handleDeleteNode, nodes]); - - if (loading) { - return ( -
-
Loading features...
-
- ) - } - - return ( -
-
-
-
-
- - Feature Planner {project?.features ? `(${project.features.length} features)` : '(Default)'} -
-
- {hasUnsavedChanges && ( - - )} - - -
-
-
- {/* Subtle neon glow at the top */} -
- {nodes.length === 0 ? ( -
- -

No features defined

-

Add pages and services to get started

-
- ) : ( - - - - )} -
- - {/* Delete Confirmation Modal */} - {showDeleteConfirm && ( - n.id === nodeToDelete)?.data.label as string || 'node'} - /> - )} - - {/* Edit Modal */} - {showEditModal && editingNode && ( - { - setShowEditModal(false); - setEditingNode(null); - }} - /> - )} -
-
- ) -} - -// Delete Confirmation Modal Component -const DeleteConfirmModal = ({ - onConfirm, - onCancel, - nodeName -}: { - onConfirm: () => void; - onCancel: () => void; - nodeName: string; -}) => { - return ( -
-
- -
-
-
- -
-
-

- Delete Node -

-

- This action cannot be undone -

-
-
- -

- Are you sure you want to delete "{nodeName}"? - This will also remove all related connections. -

- -
- - -
-
-
-
- ); -}; - -// Edit Feature Modal Component -const EditFeatureModal = ({ - node, - onSave, - onClose -}: { - node: Node; - onSave: (node: Node) => void; - onClose: () => void; -}) => { - const [name, setName] = useState(node.data.label as string); - const [route, setRoute] = useState((node.data as any).route || ''); - const [components, setComponents] = useState((node.data as any).components || 0); - - const isPageNode = node.type === 'page'; - - const handleSave = () => { - const updatedNode = { - ...node, - data: { - ...node.data, - label: name, - ...(isPageNode && { route, components }) - } - }; - onSave(updatedNode); - }; - - return ( -
-
-
-

- {isPageNode ? : } - Edit {isPageNode ? 'Page' : 'Service'} -

- -
- -
-
- - setName(e.target.value)} - className="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:border-cyan-500 focus:outline-none" - /> -
- - {isPageNode && ( - <> -
- - setRoute(e.target.value)} - className="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:border-cyan-500 focus:outline-none" - placeholder="/example-page" - /> -
- -
- - setComponents(parseInt(e.target.value) || 0)} - className="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:border-cyan-500 focus:outline-none" - min="0" - /> -
- - )} -
- -
- - -
-
-
- ); -}; diff --git a/archon-ui-main/src/components/project-tasks/MilkdownEditor.css b/archon-ui-main/src/components/project-tasks/MilkdownEditor.css deleted file mode 100644 index c9c272d7af..0000000000 --- a/archon-ui-main/src/components/project-tasks/MilkdownEditor.css +++ /dev/null @@ -1,277 +0,0 @@ -/* Milkdown Editor Custom Styles - Archon Theme */ - -/* Main editor container */ -.milkdown-crepe-editor { - background: rgba(255, 255, 255, 0.5); - backdrop-filter: blur(10px); - border: 1px solid rgba(59, 130, 246, 0.3); - border-radius: 12px; - padding: 1.5rem; - position: relative; - overflow: hidden; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); -} - -/* Gradient border effect */ -.milkdown-crepe-editor::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(90deg, #3b82f6, #a855f7); - opacity: 0.8; -} - -/* Dark mode container */ -.dark .milkdown-crepe-editor { - background: rgba(0, 0, 0, 0.3); - backdrop-filter: blur(20px); - border-color: rgba(59, 130, 246, 0.5); - box-shadow: 0 0 20px rgba(59, 130, 246, 0.1); -} - -/* Remove default Crepe theme styling */ -.milkdown-crepe-editor .milkdown { - background: transparent !important; - border: none !important; - box-shadow: none !important; -} - -/* Editor content area */ -.milkdown-crepe-editor .ProseMirror { - font-family: Inter, system-ui, -apple-system, sans-serif; - min-height: 400px; - max-width: 100%; - padding: 1rem; - background: transparent; - color: #1f2937; - line-height: 1.6; -} - -.dark .milkdown-crepe-editor .ProseMirror { - color: #f9fafb; -} - -/* Remove dark mode filter - use proper theming instead */ -.milkdown-theme-dark { - filter: none; -} - -/* Typography */ -.milkdown-crepe-editor h1 { - font-size: 2rem; - font-weight: 700; - margin-bottom: 1rem; - color: #111827; -} - -.dark .milkdown-crepe-editor h1 { - color: #f9fafb; -} - -.milkdown-crepe-editor h2 { - font-size: 1.5rem; - font-weight: 600; - margin-top: 1.5rem; - margin-bottom: 0.75rem; - color: #374151; -} - -.dark .milkdown-crepe-editor h2 { - color: #e5e7eb; -} - -.milkdown-crepe-editor h3 { - font-size: 1.25rem; - font-weight: 600; - margin-top: 1rem; - margin-bottom: 0.5rem; - color: #4b5563; -} - -.dark .milkdown-crepe-editor h3 { - color: #d1d5db; -} - -/* Links */ -.milkdown-crepe-editor a { - color: #3b82f6; - text-decoration: none; - transition: color 0.2s; -} - -.milkdown-crepe-editor a:hover { - color: #2563eb; - text-decoration: underline; -} - -.dark .milkdown-crepe-editor a { - color: #60a5fa; -} - -.dark .milkdown-crepe-editor a:hover { - color: #93bbfc; -} - -/* Code blocks */ -.milkdown-crepe-editor pre { - background: rgba(0, 0, 0, 0.05); - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 8px; - padding: 1rem; - overflow-x: auto; - font-family: 'JetBrains Mono', 'Fira Code', monospace; -} - -.dark .milkdown-crepe-editor pre { - background: rgba(255, 255, 255, 0.05); - border-color: rgba(255, 255, 255, 0.1); -} - -.milkdown-crepe-editor code { - background: rgba(59, 130, 246, 0.1); - padding: 0.125rem 0.375rem; - border-radius: 4px; - font-size: 0.875rem; - font-family: 'JetBrains Mono', 'Fira Code', monospace; -} - -.dark .milkdown-crepe-editor code { - background: rgba(59, 130, 246, 0.2); -} - -/* Lists */ -.milkdown-crepe-editor ul, -.milkdown-crepe-editor ol { - padding-left: 1.5rem; - margin: 0.5rem 0; -} - -.milkdown-crepe-editor li { - margin: 0.25rem 0; -} - -/* Blockquotes */ -.milkdown-crepe-editor blockquote { - border-left: 4px solid #3b82f6; - padding-left: 1rem; - margin: 1rem 0; - color: #6b7280; - font-style: italic; -} - -.dark .milkdown-crepe-editor blockquote { - color: #9ca3af; - border-left-color: #60a5fa; -} - -/* Tables */ -.milkdown-crepe-editor table { - border-collapse: collapse; - width: 100%; - margin: 1rem 0; -} - -.milkdown-crepe-editor th, -.milkdown-crepe-editor td { - border: 1px solid rgba(0, 0, 0, 0.1); - padding: 0.5rem; - text-align: left; -} - -.dark .milkdown-crepe-editor th, -.dark .milkdown-crepe-editor td { - border-color: rgba(255, 255, 255, 0.1); -} - -.milkdown-crepe-editor th { - background: rgba(59, 130, 246, 0.05); - font-weight: 600; -} - -.dark .milkdown-crepe-editor th { - background: rgba(59, 130, 246, 0.1); -} - -/* Toolbar styling */ -.milkdown-crepe-editor .milkdown-toolbar { - background: transparent; - border: none; - padding: 0; - margin-bottom: 1rem; -} - -/* Toolbar buttons */ -.milkdown-crepe-editor .toolbar-item { - background: rgba(255, 255, 255, 0.8); - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 6px; - padding: 0.375rem 0.75rem; - margin: 0 0.25rem; - cursor: pointer; - transition: all 0.2s; - color: #374151; -} - -.dark .milkdown-crepe-editor .toolbar-item { - background: rgba(255, 255, 255, 0.1); - border-color: rgba(255, 255, 255, 0.2); - color: #e5e7eb; -} - -.milkdown-crepe-editor .toolbar-item:hover { - background: #3b82f6; - border-color: #3b82f6; - color: white; - transform: translateY(-1px); - box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.3); -} - -/* Selection */ -.milkdown-crepe-editor .ProseMirror ::selection { - background: rgba(59, 130, 246, 0.3); -} - -.dark .milkdown-crepe-editor .ProseMirror ::selection { - background: rgba(96, 165, 250, 0.3); -} - -/* Focus state */ -.milkdown-crepe-editor .ProseMirror:focus { - outline: none; -} - -/* Placeholder */ -.milkdown-crepe-editor .ProseMirror.is-empty::before { - content: 'Start writing...'; - color: #9ca3af; - position: absolute; - pointer-events: none; -} - -/* Horizontal rule */ -.milkdown-crepe-editor hr { - border: none; - height: 1px; - background: linear-gradient(90deg, transparent, rgba(59, 130, 246, 0.5), transparent); - margin: 2rem 0; -} - -/* Save button animation */ -@keyframes pulse-glow { - 0% { - box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); - } - 70% { - box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); - } - 100% { - box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); - } -} - -.save-button-pulse { - animation: pulse-glow 2s infinite; -} \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/MilkdownEditor.tsx b/archon-ui-main/src/components/project-tasks/MilkdownEditor.tsx deleted file mode 100644 index 6f945ccb54..0000000000 --- a/archon-ui-main/src/components/project-tasks/MilkdownEditor.tsx +++ /dev/null @@ -1,555 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { Crepe, CrepeFeature } from '@milkdown/crepe'; -import '@milkdown/crepe/theme/common/style.css'; -import '@milkdown/crepe/theme/frame.css'; -import '@milkdown/crepe/theme/frame-dark.css'; -import './MilkdownEditor.css'; -import { Save, Undo } from 'lucide-react'; - -interface MilkdownEditorProps { - document: { - id: string; - title: string; - content?: any; - created_at: string; - updated_at: string; - }; - onSave: (document: any) => void; - className?: string; - isDarkMode?: boolean; -} - -export const MilkdownEditor: React.FC = ({ - document: doc, - onSave, - className = '', - isDarkMode = false, -}) => { - const editorRef = useRef(null); - const crepeRef = useRef(null); - const [isLoading, setIsLoading] = useState(false); - const [hasChanges, setHasChanges] = useState(false); - const [isReverted, setIsReverted] = useState(false); - const [originalContent, setOriginalContent] = useState(''); - const [currentContent, setCurrentContent] = useState(''); - - // Convert document content to markdown string - const getMarkdownContent = () => { - if (typeof doc.content === 'string') { - return doc.content; - } - - if (doc.content && typeof doc.content === 'object') { - // If content has a markdown field, use it - if (doc.content.markdown) { - return doc.content.markdown; - } - - // Check if this is a PRP document - if (doc.content.document_type === 'prp' || doc.document_type === 'prp') { - return convertPRPToMarkdown(doc.content); - } - - // Otherwise, convert the content object to a readable markdown format - let markdown = `# ${doc.title}\n\n`; - - Object.entries(doc.content).forEach(([key, value]) => { - const sectionTitle = key.replace(/_/g, ' ').charAt(0).toUpperCase() + key.replace(/_/g, ' ').slice(1); - markdown += `## ${sectionTitle}\n\n`; - - if (Array.isArray(value)) { - value.forEach(item => { - markdown += `- ${item}\n`; - }); - markdown += '\n'; - } else if (typeof value === 'object' && value !== null) { - if (value.description) { - markdown += `${value.description}\n\n`; - } else { - Object.entries(value).forEach(([subKey, subValue]) => { - markdown += `**${subKey}:** ${subValue}\n\n`; - }); - } - } else { - markdown += `${value}\n\n`; - } - }); - - return markdown; - } - - return `# ${doc.title}\n\nStart writing...`; - }; - - // Helper function to format values for markdown - // Enhanced formatValue to handle complex nested structures - const formatValue = (value: any, indent = '', depth = 0): string => { - if (value === null || value === undefined) { - return ''; - } - - if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { - return String(value); - } - - if (Array.isArray(value)) { - if (value.length === 0) return ''; - - // Check if it's a simple array (strings/numbers) - const isSimple = value.every(item => - typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean' - ); - - if (isSimple) { - return value.map(item => `${indent}- ${item}`).join('\n') + '\n'; - } - - // Complex array with objects - return value.map((item, index) => { - if (typeof item === 'object' && item !== null) { - const itemLines = formatValue(item, indent + ' ', depth + 1).split('\n'); - const firstLine = itemLines[0]; - const restLines = itemLines.slice(1).join('\n'); - - if (itemLines.length === 1 || (itemLines.length === 2 && !itemLines[1])) { - // Single line item - return `${indent}- ${firstLine}`; - } else { - // Multi-line item - return `${indent}-\n${indent} ${firstLine}${restLines ? '\n' + restLines : ''}`; - } - } - return `${indent}- ${formatValue(item, indent + ' ', depth + 1)}`; - }).join('\n') + '\n'; - } - - if (typeof value === 'object' && value !== null) { - const entries = Object.entries(value); - if (entries.length === 0) return ''; - - // Check if it's a simple object (all values are primitives) - const isSimple = entries.every(([_, val]) => - typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean' - ); - - if (isSimple && entries.length <= 3 && depth > 0) { - // Inline simple objects - const pairs = entries.map(([k, v]) => `${formatKey(k)}: ${v}`); - return pairs.join(', '); - } - - let result = ''; - entries.forEach(([key, val], index) => { - const formattedKey = formatKey(key); - - if (val === null || val === undefined) { - return; // Skip null/undefined - } - - if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') { - result += `${indent}**${formattedKey}:** ${val}\n`; - } else if (Array.isArray(val)) { - result += `${indent}**${formattedKey}:**\n${formatValue(val, indent, depth + 1)}`; - } else if (typeof val === 'object') { - // Use appropriate heading level based on depth - const headingLevel = Math.min(depth + 3, 6); - const heading = '#'.repeat(headingLevel); - result += `${indent}${heading} ${formattedKey}\n\n${formatValue(val, indent, depth + 1)}`; - } - - // Add spacing between top-level sections - if (depth === 0 && index < entries.length - 1) { - result += '\n'; - } - }); - - return result; - } - - return String(value); - }; - - // Helper to format keys nicely - const formatKey = (key: string): string => { - return key - .replace(/_/g, ' ') - .replace(/([a-z])([A-Z])/g, '$1 $2') - .split(' ') - .filter(word => word.length > 0) - .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) - .join(' '); - }; - - // Convert PRP document structure to readable markdown - fully dynamic - const convertPRPToMarkdown = (content: any): string => { - // Handle raw string content - if (typeof content === 'string') { - return content; - } - - // Handle null/undefined - if (!content || typeof content !== 'object') { - return `# ${doc.title}\n\nNo content available.`; - } - - // Start with title - let markdown = `# ${content.title || doc.title || 'Untitled Document'}\n\n`; - - // Group metadata fields - const metadataFields = ['version', 'author', 'date', 'status', 'document_type', 'created_at', 'updated_at']; - const metadata = metadataFields.filter(field => content[field]); - - if (metadata.length > 0) { - markdown += `## Metadata\n\n`; - metadata.forEach(field => { - const value = content[field]; - const label = formatKey(field); - markdown += `- **${label}:** ${value}\n`; - }); - markdown += '\n'; - } - - // Process all other fields dynamically - const skipFields = ['title', ...metadataFields, 'id', '_id', 'project_id']; - - // Sort fields by priority (known important fields first) - const priorityFields = [ - 'goal', 'goals', 'objective', 'objectives', - 'why', 'rationale', 'background', - 'what', 'description', 'overview', - 'context', 'background_context', - 'user_personas', 'personas', 'users', 'stakeholders', - 'user_flows', 'flows', 'journeys', 'workflows', - 'requirements', 'functional_requirements', 'non_functional_requirements', - 'success_metrics', 'metrics', 'kpis', 'success_criteria', - 'timeline', 'roadmap', 'milestones', 'phases', - 'implementation_plan', 'implementation_roadmap', 'plan', - 'technical_requirements', 'technical_implementation', 'architecture', - 'validation_gates', 'testing_strategy', 'quality_gates', - 'risks', 'risk_assessment', 'mitigation_strategies' - ]; - - // Create ordered list of fields - const orderedFields = []; - const remainingFields = []; - - Object.keys(content).forEach(key => { - if (skipFields.includes(key)) return; - - const lowerKey = key.toLowerCase(); - const priorityIndex = priorityFields.findIndex(pf => - lowerKey === pf || lowerKey.includes(pf) || pf.includes(lowerKey) - ); - - if (priorityIndex !== -1) { - orderedFields.push({ key, priority: priorityIndex }); - } else { - remainingFields.push(key); - } - }); - - // Sort by priority - orderedFields.sort((a, b) => a.priority - b.priority); - - // Process fields in order - const allFields = [...orderedFields.map(f => f.key), ...remainingFields]; - - allFields.forEach(key => { - const value = content[key]; - if (value === null || value === undefined) return; - - const sectionTitle = formatKey(key); - markdown += `## ${sectionTitle}\n\n`; - - // Handle different value types - if (typeof value === 'string') { - markdown += `${value}\n\n`; - } else if (typeof value === 'number' || typeof value === 'boolean') { - markdown += `${value}\n\n`; - } else if (Array.isArray(value)) { - markdown += formatValue(value) + '\n'; - } else if (typeof value === 'object') { - markdown += formatValue(value) + '\n'; - } - }); - - return markdown.trim(); - }; - - // Initialize editor - useEffect(() => { - if (!editorRef.current || crepeRef.current) return; - - const initialContent = getMarkdownContent(); - setOriginalContent(initialContent); - setCurrentContent(initialContent); - - // Add theme class to root element - if (isDarkMode) { - editorRef.current.classList.add('milkdown-theme-dark'); - } - - const crepe = new Crepe({ - root: editorRef.current, - defaultValue: initialContent, - features: { - [CrepeFeature.HeaderMeta]: true, - [CrepeFeature.LinkTooltip]: true, - [CrepeFeature.ImageBlock]: true, - [CrepeFeature.BlockEdit]: true, - [CrepeFeature.ListItem]: true, - [CrepeFeature.CodeBlock]: true, - [CrepeFeature.Table]: true, - [CrepeFeature.Toolbar]: true, - }, - }); - - crepe.create().then(() => { - console.log('Milkdown editor created'); - - // Set up content change tracking - const editorElement = editorRef.current?.querySelector('.ProseMirror'); - if (editorElement) { - // Listen for input events on the editor - const handleInput = () => { - // Get current markdown content - const markdown = crepe.getMarkdown(); - console.log('Editor content changed via input:', markdown.substring(0, 50) + '...'); - setCurrentContent(markdown); - - // Compare trimmed content to avoid whitespace issues - const hasUnsavedChanges = markdown.trim() !== originalContent.trim(); - setHasChanges(hasUnsavedChanges); - setIsReverted(false); - }; - - // Listen to multiple events to catch all changes - editorElement.addEventListener('input', handleInput); - editorElement.addEventListener('keyup', handleInput); - editorElement.addEventListener('paste', handleInput); - editorElement.addEventListener('cut', handleInput); - - // Store the handlers for cleanup - (editorElement as any)._milkdownHandlers = { - input: handleInput, - keyup: handleInput, - paste: handleInput, - cut: handleInput - }; - } - }).catch((error) => { - console.error('Failed to create Milkdown editor:', error); - }); - - crepeRef.current = crepe; - - return () => { - // Clean up event listeners - const editorElement = editorRef.current?.querySelector('.ProseMirror'); - if (editorElement && (editorElement as any)._milkdownHandlers) { - const handlers = (editorElement as any)._milkdownHandlers; - editorElement.removeEventListener('input', handlers.input); - editorElement.removeEventListener('keyup', handlers.keyup); - editorElement.removeEventListener('paste', handlers.paste); - editorElement.removeEventListener('cut', handlers.cut); - delete (editorElement as any)._milkdownHandlers; - } - - if (crepeRef.current) { - crepeRef.current.destroy(); - crepeRef.current = null; - } - }; - }, [doc.id, originalContent]); - - // Update theme class when isDarkMode changes - useEffect(() => { - if (editorRef.current) { - if (isDarkMode) { - editorRef.current.classList.add('milkdown-theme-dark'); - } else { - editorRef.current.classList.remove('milkdown-theme-dark'); - } - } - }, [isDarkMode]); - - // Add keyboard shortcut for saving - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if ((e.metaKey || e.ctrlKey) && e.key === 's') { - e.preventDefault(); - if (hasChanges && !isLoading) { - handleSave(); - } - } - }; - - window.addEventListener('keydown', handleKeyDown); - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [hasChanges, isLoading, currentContent]); - - // Handle manual save - const handleSave = async () => { - if (!hasChanges || isLoading) return; - - try { - setIsLoading(true); - console.log('Saving document with content:', currentContent.substring(0, 100) + '...'); - - // Create updated document with markdown content stored in content field - const updatedDocument = { - ...doc, - content: { - markdown: currentContent, - // Preserve any other content fields - ...(typeof doc.content === 'object' && doc.content !== null ? doc.content : {}) - }, - updated_at: new Date().toISOString(), - }; - - await onSave(updatedDocument); - - // Update state after successful save - setHasChanges(false); - setIsReverted(false); - setOriginalContent(currentContent); - console.log('Document saved successfully'); - } catch (error) { - console.error('Error saving document:', error); - // You might want to show an error toast here - } finally { - setIsLoading(false); - } - }; - - // Handle undo changes - const handleUndo = () => { - if (crepeRef.current && editorRef.current) { - // Destroy and recreate editor with original content - crepeRef.current.destroy(); - - const crepe = new Crepe({ - root: editorRef.current, - defaultValue: originalContent, - features: { - [CrepeFeature.HeaderMeta]: true, - [CrepeFeature.LinkTooltip]: true, - [CrepeFeature.ImageBlock]: true, - [CrepeFeature.BlockEdit]: true, - [CrepeFeature.ListItem]: true, - [CrepeFeature.CodeBlock]: true, - [CrepeFeature.Table]: true, - [CrepeFeature.Toolbar]: true, - }, - }); - - crepe.create().then(() => { - console.log('Milkdown editor reverted to original content'); - - // Set up content change tracking for the new editor instance - const editorElement = editorRef.current?.querySelector('.ProseMirror'); - if (editorElement) { - const handleInput = () => { - const markdown = crepe.getMarkdown(); - console.log('Editor content changed after undo:', markdown.substring(0, 50) + '...'); - setCurrentContent(markdown); - const hasUnsavedChanges = markdown.trim() !== originalContent.trim(); - setHasChanges(hasUnsavedChanges); - setIsReverted(false); - }; - - editorElement.addEventListener('input', handleInput); - editorElement.addEventListener('keyup', handleInput); - editorElement.addEventListener('paste', handleInput); - editorElement.addEventListener('cut', handleInput); - - (editorElement as any)._milkdownHandlers = { - input: handleInput, - keyup: handleInput, - paste: handleInput, - cut: handleInput - }; - } - - setCurrentContent(originalContent); - setHasChanges(false); - setIsReverted(true); - }).catch((error) => { - console.error('Failed to revert Milkdown editor:', error); - }); - - crepeRef.current = crepe; - } - }; - - return ( -
-
-
-

- {doc.title} -

-
- {isLoading ? ( - -
- Saving... -
- ) : isReverted ? ( - -
- Reverted -
- ) : hasChanges ? ( - -
- Unsaved changes -
- ) : ( - -
- All changes saved -
- )} -
-
-
- {hasChanges && ( - - )} - -
-
- -
-
- ); -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/Tabs.tsx b/archon-ui-main/src/components/project-tasks/Tabs.tsx deleted file mode 100644 index fd66d55ce5..0000000000 --- a/archon-ui-main/src/components/project-tasks/Tabs.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React, { useMemo, useState, createContext, useContext } from 'react'; -interface TabsProps { - defaultValue: string; - value?: string; - onValueChange?: (value: string) => void; - children: React.ReactNode; - className?: string; -} -const TabsContext = createContext<{ - value: string; - onValueChange: (value: string) => void; -}>({ - value: '', - onValueChange: () => {} -}); -export const Tabs = ({ - defaultValue, - value, - onValueChange, - children, - className = '' -}: TabsProps) => { - const [internalValue, setInternalValue] = useState(defaultValue); - const activeValue = value !== undefined ? value : internalValue; - const contextValue = useMemo(() => ({ - value: activeValue, - onValueChange: (newValue: string) => { - setInternalValue(newValue); - onValueChange?.(newValue); - } - }), [activeValue, onValueChange]); - return -
{children}
-
; -}; -interface TabsListProps { - children: React.ReactNode; - className?: string; -} -export const TabsList = ({ - children, - className = '' -}: TabsListProps) => { - return
- {/* Subtle neon glow effect */} -
- {children} -
; -}; -interface TabsTriggerProps { - value: string; - children: React.ReactNode; - className?: string; - onClick?: () => void; - color?: 'blue' | 'purple' | 'pink' | 'orange' | 'cyan' | 'green'; -} -export const TabsTrigger = ({ - value, - children, - className = '', - onClick, - color = 'blue' -}: TabsTriggerProps) => { - const { - value: activeValue, - onValueChange - } = useContext(TabsContext); - const isActive = activeValue === value; - const handleClick = () => { - onValueChange(value); - onClick?.(); - }; - const colorMap = { - blue: { - text: 'text-blue-600 dark:text-blue-400', - glow: 'bg-blue-500 shadow-[0_0_10px_2px_rgba(59,130,246,0.4)] dark:shadow-[0_0_20px_5px_rgba(59,130,246,0.7)]', - hover: 'hover:text-blue-500 dark:hover:text-blue-400/70' - }, - purple: { - text: 'text-purple-600 dark:text-purple-400', - glow: 'bg-purple-500 shadow-[0_0_10px_2px_rgba(168,85,247,0.4)] dark:shadow-[0_0_20px_5px_rgba(168,85,247,0.7)]', - hover: 'hover:text-purple-500 dark:hover:text-purple-400/70' - }, - pink: { - text: 'text-pink-600 dark:text-pink-400', - glow: 'bg-pink-500 shadow-[0_0_10px_2px_rgba(236,72,153,0.4)] dark:shadow-[0_0_20px_5px_rgba(236,72,153,0.7)]', - hover: 'hover:text-pink-500 dark:hover:text-pink-400/70' - }, - orange: { - text: 'text-orange-600 dark:text-orange-400', - glow: 'bg-orange-500 shadow-[0_0_10px_2px_rgba(249,115,22,0.4)] dark:shadow-[0_0_20px_5px_rgba(249,115,22,0.7)]', - hover: 'hover:text-orange-500 dark:hover:text-orange-400/70' - }, - cyan: { - text: 'text-cyan-600 dark:text-cyan-400', - glow: 'bg-cyan-500 shadow-[0_0_10px_2px_rgba(34,211,238,0.4)] dark:shadow-[0_0_20px_5px_rgba(34,211,238,0.7)]', - hover: 'hover:text-cyan-500 dark:hover:text-cyan-400/70' - }, - green: { - text: 'text-emerald-600 dark:text-emerald-400', - glow: 'bg-emerald-500 shadow-[0_0_10px_2px_rgba(16,185,129,0.4)] dark:shadow-[0_0_20px_5px_rgba(16,185,129,0.7)]', - hover: 'hover:text-emerald-500 dark:hover:text-emerald-400/70' - } - }; - return ; -}; -interface TabsContentProps { - value: string; - children: React.ReactNode; - className?: string; -} -export const TabsContent = ({ - value, - children, - className = '' -}: TabsContentProps) => { - const { - value: activeValue - } = useContext(TabsContext); - // Simplified TabsContent - we're handling animations in the parent component now - if (activeValue !== value) return null; - return
- {children} -
; -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/TaskBoardView.tsx b/archon-ui-main/src/components/project-tasks/TaskBoardView.tsx deleted file mode 100644 index 58761486bc..0000000000 --- a/archon-ui-main/src/components/project-tasks/TaskBoardView.tsx +++ /dev/null @@ -1,371 +0,0 @@ -import React, { useRef, useState, useCallback } from 'react'; -import { useDrop } from 'react-dnd'; -import { useToast } from '../../contexts/ToastContext'; -import { DeleteConfirmModal } from '../common/DeleteConfirmModal'; -import { Trash2 } from 'lucide-react'; -import { Task } from './TaskTableView'; // Import Task interface -import { ItemTypes, getAssigneeIcon, getAssigneeGlow, getOrderColor, getOrderGlow } from '../../lib/task-utils'; -import { DraggableTaskCard } from './DraggableTaskCard'; - -interface TaskBoardViewProps { - tasks: Task[]; - onTaskView: (task: Task) => void; - onTaskComplete: (taskId: string) => void; - onTaskDelete: (task: Task) => void; - onTaskMove: (taskId: string, newStatus: Task['status']) => void; - onTaskReorder: (taskId: string, targetIndex: number, status: Task['status']) => void; -} - -interface ColumnDropZoneProps { - status: Task['status']; - title: string; - tasks: Task[]; - onTaskMove: (taskId: string, newStatus: Task['status']) => void; - onTaskView: (task: Task) => void; - onTaskDelete: (task: Task) => void; - onTaskReorder: (taskId: string, targetIndex: number, status: Task['status']) => void; - allTasks: Task[]; - hoveredTaskId: string | null; - onTaskHover: (taskId: string | null) => void; - selectedTasks: Set; - onTaskSelect: (taskId: string) => void; -} - -const ColumnDropZone = ({ - status, - title, - tasks, - onTaskMove, - onTaskView, - onTaskDelete, - onTaskReorder, - allTasks, - hoveredTaskId, - onTaskHover, - selectedTasks, - onTaskSelect -}: ColumnDropZoneProps) => { - const ref = useRef(null); - - const [{ isOver }, drop] = useDrop({ - accept: ItemTypes.TASK, - drop: (item: { id: string; status: Task['status'] }) => { - if (item.status !== status) { - // Moving to different status - use length of current column as new order - onTaskMove(item.id, status); - } - }, - collect: (monitor) => ({ - isOver: !!monitor.isOver() - }) - }); - - drop(ref); - - // Get column header color based on status - const getColumnColor = () => { - switch (status) { - case 'todo': - return 'text-gray-600 dark:text-gray-400'; - case 'doing': - return 'text-blue-600 dark:text-blue-400'; - case 'review': - return 'text-purple-600 dark:text-purple-400'; - case 'done': - return 'text-green-600 dark:text-green-400'; - } - }; - - // Get column header glow based on status - const getColumnGlow = () => { - switch (status) { - case 'todo': - return 'bg-gray-500/30'; - case 'doing': - return 'bg-blue-500/30 shadow-[0_0_10px_2px_rgba(59,130,246,0.2)]'; - case 'review': - return 'bg-purple-500/30 shadow-[0_0_10px_2px_rgba(168,85,247,0.2)]'; - case 'done': - return 'bg-green-500/30 shadow-[0_0_10px_2px_rgba(16,185,129,0.2)]'; - } - }; - - // Just use the tasks as-is since they're already parent tasks only - const organizedTasks = tasks; - - return ( -
-
-

{title}

- {/* Column header divider with glow */} -
-
- -
- {organizedTasks.map((task, index) => ( - onTaskView(task)} - onComplete={() => onTaskComplete(task.id)} - onDelete={onTaskDelete} - onTaskReorder={onTaskReorder} - hoveredTaskId={hoveredTaskId} - onTaskHover={onTaskHover} - selectedTasks={selectedTasks} - onTaskSelect={onTaskSelect} - /> - ))} -
-
- ); -}; - -export const TaskBoardView = ({ - tasks, - onTaskView, - onTaskComplete, - onTaskDelete, - onTaskMove, - onTaskReorder -}: TaskBoardViewProps) => { - const [hoveredTaskId, setHoveredTaskId] = useState(null); - const [selectedTasks, setSelectedTasks] = useState>(new Set()); - - // State for delete confirmation modal - const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); - const [taskToDelete, setTaskToDelete] = useState(null); - - const { showToast } = useToast(); - - // Multi-select handlers - const toggleTaskSelection = useCallback((taskId: string) => { - setSelectedTasks(prev => { - const newSelection = new Set(prev); - if (newSelection.has(taskId)) { - newSelection.delete(taskId); - } else { - newSelection.add(taskId); - } - return newSelection; - }); - }, []); - - const selectAllTasks = useCallback(() => { - setSelectedTasks(new Set(tasks.map(task => task.id))); - }, [tasks]); - - const clearSelection = useCallback(() => { - setSelectedTasks(new Set()); - }, []); - - // Mass delete handler - const handleMassDelete = useCallback(async () => { - if (selectedTasks.size === 0) return; - - const tasksToDelete = tasks.filter(task => selectedTasks.has(task.id)); - - try { - const results = await Promise.allSettled( - tasksToDelete.map(task => Promise.resolve(onTaskDelete(task))) - ); - const rejected = results.filter(r => r.status === 'rejected'); - if (rejected.length) { - console.error('Some deletions failed:', rejected.length); - } - clearSelection(); - } catch (error) { - console.error('Failed to delete tasks:', error); - } - }, [selectedTasks, tasks, onTaskDelete, clearSelection]); - - // Mass status change handler - const handleMassStatusChange = useCallback(async (newStatus: Task['status']) => { - if (selectedTasks.size === 0) return; - - const tasksToUpdate = tasks.filter(task => selectedTasks.has(task.id)); - - try { - // Call onTaskMove so optimistic UI and counts refresh immediately; parent persists - tasksToUpdate.forEach(task => onTaskMove(task.id, newStatus)); - clearSelection(); - showToast(`Moved ${tasksToUpdate.length} task${tasksToUpdate.length !== 1 ? 's' : ''} to ${newStatus}`, 'success'); - } catch (error) { - console.error('Failed to update tasks:', error); - showToast('Failed to move some tasks', 'error'); - } - }, [selectedTasks, tasks, onTaskMove, clearSelection, showToast]); - - // No status mapping needed - using database values directly - - // Handle task deletion (opens confirmation modal) - const handleDeleteTask = useCallback((task: Task) => { - setTaskToDelete(task); - setShowDeleteConfirm(true); - }, [setTaskToDelete, setShowDeleteConfirm]); - - // Confirm deletion and execute - const confirmDeleteTask = useCallback(async () => { - if (!taskToDelete) return; - - try { - // Delegate deletion to parent to avoid duplicate API calls - await onTaskDelete(taskToDelete); - } catch (error) { - console.error('Failed to delete task:', error); - } finally { - setShowDeleteConfirm(false); - setTaskToDelete(null); - } - }, [taskToDelete, onTaskDelete, setShowDeleteConfirm, setTaskToDelete]); - - // Cancel deletion - const cancelDeleteTask = useCallback(() => { - setShowDeleteConfirm(false); - setTaskToDelete(null); - }, [setShowDeleteConfirm, setTaskToDelete]); - - // Simple task filtering for board view - const getTasksByStatus = (status: Task['status']) => { - return tasks - .filter(task => task.status === status) - .sort((a, b) => a.task_order - b.task_order); - }; - - // Note: With optimistic updates, we no longer show loading overlays for successful moves - // Tasks update instantly and only rollback on actual failures - - return ( -
- - {/* Multi-select toolbar */} - {selectedTasks.size > 0 && ( -
-
- - {selectedTasks.size} task{selectedTasks.size !== 1 ? 's' : ''} selected - -
- -
- {/* Status change dropdown */} - - - {/* Mass delete button */} - - - {/* Clear selection */} - -
-
- )} - - {/* Board Columns */} -
- {/* Todo Column */} - - - {/* Doing Column */} - - - {/* Review Column */} - - - {/* Done Column */} - -
- - {/* Delete Confirmation Modal for Tasks */} - {showDeleteConfirm && taskToDelete && ( - - )} -
- ); -}; \ No newline at end of file diff --git a/archon-ui-main/src/components/project-tasks/TaskInputComponents.tsx b/archon-ui-main/src/components/project-tasks/TaskInputComponents.tsx deleted file mode 100644 index 75924c33f9..0000000000 --- a/archon-ui-main/src/components/project-tasks/TaskInputComponents.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import React, { memo, useState, useEffect, useCallback, useRef } from 'react'; - -interface DebouncedInputProps { - value: string; - onChange: (value: string) => void; - placeholder?: string; - className?: string; - type?: 'text' | 'textarea'; - rows?: number; -} - -// Memoized input component that manages its own state -export const DebouncedInput = memo(({ - value, - onChange, - placeholder, - className, - type = 'text', - rows = 5 -}: DebouncedInputProps) => { - const [localValue, setLocalValue] = useState(value); - const timeoutRef = useRef(); - const isFirstRender = useRef(true); - - // Sync with external value only on mount or when externally changed - useEffect(() => { - if (isFirstRender.current) { - isFirstRender.current = false; - return; - } - // Only update if the external value is different from local - if (value !== localValue) { - setLocalValue(value); - } - }, [value]); - - const handleChange = useCallback((e: React.ChangeEvent) => { - const newValue = e.target.value; - setLocalValue(newValue); - - // Clear existing timeout - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - - // Set new timeout for debounced update - timeoutRef.current = setTimeout(() => { - onChange(newValue); - }, 300); - }, [onChange]); - - // Cleanup timeout on unmount - useEffect(() => { - return () => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - }; - }, []); - - if (type === 'textarea') { - return ( -