",
+ "confidence": <0.0 to 1.0>,
+ "reasoning": "<2-3 key text patterns you found>"
+}}
+
+Return ONLY valid JSON, no other text."""
+
+ # Call VL model with extended timeout for slower inference servers
+ response = self.client.chat.completions.create(
+ model=config.DETECTION_MODEL,
+ messages=[{"role": "user", "content": message_content}],
+ max_tokens=300, # Increased for better reasoning
+ temperature=0.2, # Slightly higher for flexibility
+ timeout=120.0 # 2 minutes for classification on slower servers
+ )
+
+ result = self._parse_json_response(response.choices[0].message.content)
+
+ if not result or not result.get('document_type'):
+ logger.warning("VL model returned empty result")
+ return {"document_type": "undetected", "confidence": 0.0, "reasoning": "Could not determine document type"}
+
+ logger.info(f"VL classification: {result.get('document_type')} ({result.get('confidence', 0):.0%})")
+ return result
+
+ except TimeoutError:
+ logger.error("Document classification timed out after 2 minutes")
+ return {
+ "document_type": "undetected",
+ "confidence": 0.0,
+ "reasoning": "Classification timed out. Please select a template manually."
+ }
+ except Exception as e:
+ logger.error(f"Document type detection failed: {str(e)}")
+ return {
+ "document_type": "undetected",
+ "confidence": 0.0,
+ "reasoning": f"Classification failed: {str(e)}"
+ }
+
+ def _mock_extraction(self, schema: Dict[str, str]) -> Dict[str, Any]:
+ """Generate mock data based on schema"""
+ mock_data = {}
+ for field, field_type in schema.items():
+ if "invoice" in field.lower() or "number" in field.lower():
+ mock_data[field] = "INV-2024-001"
+ elif "date" in field.lower():
+ mock_data[field] = "2024-12-29"
+ elif "vendor" in field.lower() or "company" in field.lower():
+ mock_data[field] = "Acme Corporation"
+ elif "total" in field.lower() or "amount" in field.lower():
+ mock_data[field] = 1250.00
+ elif field_type == "array":
+ mock_data[field] = [
+ {"description": "Consulting Services", "amount": 1000.00},
+ {"description": "Software License", "amount": 250.00}
+ ]
+ else:
+ mock_data[field] = f"Mock {field}"
+
+ return mock_data
+
+ def process_chat_message(self, message: str, current_schema: Dict[str, str]) -> tuple[str, Dict[str, str]]:
+ """Process chat message to build extraction schema"""
+
+ if not self.client:
+ return self._mock_chat_response(message, current_schema)
+
+ try:
+ schema_str = "\n".join([f"- {k}: {v}" for k, v in current_schema.items()]) if current_schema else "None yet"
+
+ system_prompt = """You are a helpful assistant that helps users build document extraction schemas.
+
+When users greet you or ask questions: Respond conversationally and ask what fields they want to extract.
+When users request fields: Update the schema and confirm.
+
+FIELD NAME RULES:
+- Use snake_case
+- Use EXACT names user specifies
+- Plural words (deliverables, medications, items) = array type
+- NO typos, NO extra letters, NO suffix additions like "_list"
+
+DATA TYPES:
+- string: text, names, emails
+- number: integers, floats
+- date: dates
+- array: lists, multiple items, plural nouns
+- object: nested structures
+
+USER REQUEST PATTERNS:
+"extract X" or "add X" → ADD field X to schema (determine type from name)
+"extract X as array" → ADD field X with type=array
+"remove X" → DELETE field X from schema
+"replace X with Y" → DELETE X, ADD Y
+"X and Y as well" → ADD both X and Y to schema
+"X, Y, Z" → ADD all three to schema
+"hi", "hello", "help" → Greet and ask what to extract
+
+RESPONSE FORMAT (JSON only):
+{
+ "reply": "Your conversational response",
+ "schema": {"field1": "type1", "field2": "type2"}
+}
+
+EXAMPLES:
+User: "hi"
+Response: {"reply": "Hello! I'll help you configure document extraction. What fields would you like to extract from your documents?", "schema": {}}
+
+User: "extract deliverables"
+Response: {"reply": "Added deliverables as an array field. What else would you like to extract?", "schema": {"deliverables": "array"}}
+
+User: "add client_email and contractor_email"
+Response: {"reply": "Added client_email and contractor_email fields.", "schema": {"deliverables": "array", "client_email": "string", "contractor_email": "string"}}"""
+
+ user_prompt = f"""Current schema:
+{schema_str}
+
+User request: {message}
+
+Process this request and update the schema accordingly."""
+
+ response = self.client.chat.completions.create(
+ model=config.VISION_MODEL,
+ messages=[
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": user_prompt}
+ ],
+ max_tokens=800,
+ temperature=0.3,
+ timeout=120.0 # 2 minutes for chat processing on slower servers
+ )
+
+ result = self._parse_json_response(response.choices[0].message.content)
+
+ reply = result.get("reply", "I'm here to help! What would you like to extract?")
+ updated_schema = result.get("schema", current_schema)
+
+ logger.info(f"Chat processed: {len(updated_schema)} fields in schema")
+
+ return reply, updated_schema
+
+ except Exception as e:
+ logger.error(f"Chat processing error: {str(e)}")
+ return self._mock_chat_response(message, current_schema)
+
+ def _mock_chat_response(self, message: str, current_schema: Dict[str, str]) -> tuple[str, Dict[str, str]]:
+ """Mock chat response when vision client is not available"""
+ message_lower = message.lower()
+
+ if not current_schema:
+ if "invoice" in message_lower:
+ reply = "I'll help you extract invoice data. What fields do you need? Common invoice fields include: invoice number, date, vendor, line items, and total amount."
+ return reply, {}
+ else:
+ reply = "I can help you define what data to extract from your documents. What type of document are you working with?"
+ return reply, {}
+
+ new_schema = dict(current_schema)
+
+ if "invoice number" in message_lower or "number" in message_lower:
+ new_schema["invoice_number"] = "string"
+ if "date" in message_lower:
+ new_schema["date"] = "date"
+ if "vendor" in message_lower or "company" in message_lower:
+ new_schema["vendor"] = "string"
+ if "total" in message_lower or "amount" in message_lower:
+ new_schema["total"] = "number"
+ if "line item" in message_lower or "items" in message_lower:
+ new_schema["line_items"] = "array"
+
+ if new_schema != current_schema:
+ field_list = ", ".join(new_schema.keys())
+ reply = f"Got it! I'll extract these fields: {field_list}. You can test this extraction or add more fields."
+ else:
+ reply = "What other fields would you like to extract?"
+
+ return reply, new_schema
+
+ def _extract_schema_from_response(self, message: str, current_schema: Dict[str, str]) -> Dict[str, str]:
+ """Extract schema updates from user message"""
+ new_schema = dict(current_schema)
+ message_lower = message.lower()
+
+ field_mappings = {
+ "invoice number": ("invoice_number", "string"),
+ "invoice_number": ("invoice_number", "string"),
+ "date": ("date", "date"),
+ "vendor": ("vendor", "string"),
+ "company": ("vendor", "string"),
+ "total": ("total", "number"),
+ "amount": ("total", "number"),
+ "line items": ("line_items", "array"),
+ "items": ("line_items", "array"),
+ }
+
+ for keyword, (field, field_type) in field_mappings.items():
+ if keyword in message_lower:
+ new_schema[field] = field_type
+
+ return new_schema
diff --git a/sample_solutions/DocQvision/api/utils/__init__.py b/sample_solutions/DocQvision/api/utils/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/sample_solutions/DocQvision/api/utils/validators.py b/sample_solutions/DocQvision/api/utils/validators.py
new file mode 100644
index 00000000..5a7ae58a
--- /dev/null
+++ b/sample_solutions/DocQvision/api/utils/validators.py
@@ -0,0 +1,66 @@
+"""Validation utilities for file uploads and data"""
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+def validate_pdf_file(content: bytes, filename: str) -> tuple[bool, str]:
+ """
+ Validate that uploaded file is a legitimate PDF
+
+ Args:
+ content: File bytes
+ filename: Original filename
+
+ Returns:
+ (is_valid, error_message)
+ """
+ if not content:
+ return False, "Empty file"
+
+ if not filename.lower().endswith('.pdf'):
+ return False, "Only PDF files are supported"
+
+ if not content.startswith(b'%PDF-'):
+ return False, "Invalid PDF file format (magic bytes mismatch)"
+
+ if len(content) > 10 * 1024 * 1024:
+ return False, "File size exceeds 10MB limit"
+
+ return True, ""
+
+
+def sanitize_chat_message(message: str) -> str:
+ """
+ Sanitize user chat input to prevent prompt injection
+
+ Args:
+ message: User chat message
+
+ Returns:
+ Sanitized message
+ """
+ if not message:
+ return ""
+
+ message = message.strip()
+
+ if len(message) > 2000:
+ message = message[:2000]
+
+ suspicious_patterns = [
+ "ignore previous",
+ "ignore all previous",
+ "disregard previous",
+ "forget previous",
+ "new instructions:",
+ "system:",
+ "assistant:",
+ ]
+
+ message_lower = message.lower()
+ for pattern in suspicious_patterns:
+ if pattern in message_lower:
+ logger.warning(f"Potential prompt injection detected: {pattern}")
+
+ return message
diff --git a/sample_solutions/DocQvision/assets/configure-chat.png b/sample_solutions/DocQvision/assets/configure-chat.png
new file mode 100644
index 00000000..0691ed17
Binary files /dev/null and b/sample_solutions/DocQvision/assets/configure-chat.png differ
diff --git a/sample_solutions/DocQvision/assets/configure-initial.png b/sample_solutions/DocQvision/assets/configure-initial.png
new file mode 100644
index 00000000..4098cb0c
Binary files /dev/null and b/sample_solutions/DocQvision/assets/configure-initial.png differ
diff --git a/sample_solutions/DocQvision/assets/configure-test-results.png b/sample_solutions/DocQvision/assets/configure-test-results.png
new file mode 100644
index 00000000..5eb690ab
Binary files /dev/null and b/sample_solutions/DocQvision/assets/configure-test-results.png differ
diff --git a/sample_solutions/DocQvision/assets/history-page.png b/sample_solutions/DocQvision/assets/history-page.png
new file mode 100644
index 00000000..074f9101
Binary files /dev/null and b/sample_solutions/DocQvision/assets/history-page.png differ
diff --git a/sample_solutions/DocQvision/assets/results-view.png b/sample_solutions/DocQvision/assets/results-view.png
new file mode 100644
index 00000000..5eb690ab
Binary files /dev/null and b/sample_solutions/DocQvision/assets/results-view.png differ
diff --git a/sample_solutions/DocQvision/assets/upload-page.png b/sample_solutions/DocQvision/assets/upload-page.png
new file mode 100644
index 00000000..4a23c34c
Binary files /dev/null and b/sample_solutions/DocQvision/assets/upload-page.png differ
diff --git a/sample_solutions/DocQvision/docker-compose.yml b/sample_solutions/DocQvision/docker-compose.yml
new file mode 100644
index 00000000..98656677
--- /dev/null
+++ b/sample_solutions/DocQvision/docker-compose.yml
@@ -0,0 +1,109 @@
+# =============================================================================
+# DocQvision - Docker Compose Configuration
+# =============================================================================
+# This file orchestrates the DocQvision application stack consisting of:
+# - Backend API (FastAPI)
+# - Frontend UI (React + Nginx)
+# =============================================================================
+
+services:
+ # ===========================================================================
+ # Backend API Service
+ # ===========================================================================
+ backend:
+ build:
+ context: ./api
+ dockerfile: Dockerfile
+ args:
+ - BUILDKIT_INLINE_CACHE=1
+ image: DocQvision-backend:latest
+ container_name: DocQvision-backend
+ ports:
+ - "${BACKEND_PORT:-5001}:5001"
+ env_file:
+ - ./api/.env
+ environment:
+ - DATABASE_URL=sqlite:////app/data/DocQvision.db
+ - PYTHONUNBUFFERED=1
+ volumes:
+ # Persistent database storage
+ - db_data:/app/data
+ extra_hosts:
+ - "${LOCAL_URL_ENDPOINT}:host-gateway"
+ networks:
+ - DocQvision_network
+ restart: unless-stopped
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:5001/health"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 40s
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 2G
+ reservations:
+ cpus: '0.5'
+ memory: 512M
+ labels:
+ - "com.DocQvision.service=backend"
+ - "com.DocQvision.version=1.0"
+
+ # ===========================================================================
+ # Frontend UI Service
+ # ===========================================================================
+ frontend:
+ build:
+ context: ./ui
+ dockerfile: Dockerfile
+ args:
+ - BUILDKIT_INLINE_CACHE=1
+ image: DocQvision-frontend:latest
+ container_name: DocQvision-frontend
+ ports:
+ - "${FRONTEND_PORT:-3000}:80"
+ depends_on:
+ backend:
+ condition: service_healthy
+ networks:
+ - DocQvision_network
+ restart: unless-stopped
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost/"]
+ interval: 30s
+ timeout: 3s
+ retries: 3
+ start_period: 10s
+ deploy:
+ resources:
+ limits:
+ cpus: '1'
+ memory: 512M
+ reservations:
+ cpus: '0.25'
+ memory: 128M
+ labels:
+ - "com.DocQvision.service=frontend"
+ - "com.DocQvision.version=1.0"
+
+# =============================================================================
+# Networks
+# =============================================================================
+networks:
+ DocQvision_network:
+ driver: bridge
+ name: DocQvision_network
+ labels:
+ - "com.DocQvision.network=main"
+
+# =============================================================================
+# Volumes
+# =============================================================================
+volumes:
+ db_data:
+ driver: local
+ name: DocQvision_db_data
+ labels:
+ - "com.DocQvision.volume=database"
diff --git a/sample_solutions/DocQvision/sample-documents/invoice1.pdf b/sample_solutions/DocQvision/sample-documents/invoice1.pdf
new file mode 100644
index 00000000..80ef2fa7
Binary files /dev/null and b/sample_solutions/DocQvision/sample-documents/invoice1.pdf differ
diff --git a/sample_solutions/DocQvision/sample-documents/invoice2.pdf b/sample_solutions/DocQvision/sample-documents/invoice2.pdf
new file mode 100644
index 00000000..79d63e65
Binary files /dev/null and b/sample_solutions/DocQvision/sample-documents/invoice2.pdf differ
diff --git a/sample_solutions/DocQvision/sample-documents/prescription.pdf b/sample_solutions/DocQvision/sample-documents/prescription.pdf
new file mode 100644
index 00000000..d8a936d3
Binary files /dev/null and b/sample_solutions/DocQvision/sample-documents/prescription.pdf differ
diff --git a/sample_solutions/DocQvision/sample-documents/service-contract.pdf b/sample_solutions/DocQvision/sample-documents/service-contract.pdf
new file mode 100644
index 00000000..066001db
Binary files /dev/null and b/sample_solutions/DocQvision/sample-documents/service-contract.pdf differ
diff --git a/sample_solutions/DocQvision/ui/.dockerignore b/sample_solutions/DocQvision/ui/.dockerignore
new file mode 100644
index 00000000..1fa4d037
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/.dockerignore
@@ -0,0 +1,56 @@
+# Dependencies
+node_modules/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Production build
+dist/
+build/
+.cache/
+
+# Environment variables
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+.DS_Store
+
+# Testing
+coverage/
+.nyc_output/
+
+# Logs
+logs/
+*.log
+
+# Documentation
+*.md
+docs/
+
+# Git
+.git/
+.gitignore
+.gitattributes
+
+# Docker
+Dockerfile*
+docker-compose*.yml
+.dockerignore
+
+# Temporary files
+*.tmp
+*.bak
+.cache/
+
+# OS
+Thumbs.db
diff --git a/sample_solutions/DocQvision/ui/Dockerfile b/sample_solutions/DocQvision/ui/Dockerfile
new file mode 100644
index 00000000..230b0b98
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/Dockerfile
@@ -0,0 +1,64 @@
+# =============================================================================
+# DocQvision UI - Production Dockerfile
+# =============================================================================
+
+# =============================================================================
+# Builder stage - Build React application
+# =============================================================================
+FROM node:20-alpine as builder
+
+WORKDIR /app
+
+# Copy package files for dependency installation
+COPY package.json ./
+
+# Install dependencies
+RUN npm install
+
+# Copy source code
+COPY . .
+
+# Build the application
+RUN npm run build
+
+# =============================================================================
+# Runtime stage - Serve with nginx
+# =============================================================================
+FROM nginx:1.25-alpine as runtime
+
+# Install curl for healthcheck
+RUN apk add --no-cache curl
+
+# Remove default nginx config
+RUN rm -rf /etc/nginx/conf.d/*
+
+# Copy custom nginx configuration
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+
+# Copy built application from builder
+COPY --from=builder /app/dist /usr/share/nginx/html
+
+# Create non-root user for security
+RUN addgroup -g 1001 -S appuser && \
+ adduser -S -D -H -u 1001 -h /var/cache/nginx -s /sbin/nologin -G appuser -g appuser appuser
+
+# Change ownership of nginx directories
+RUN chown -R appuser:appuser /var/cache/nginx && \
+ chown -R appuser:appuser /var/log/nginx && \
+ chown -R appuser:appuser /etc/nginx/conf.d && \
+ touch /var/run/nginx.pid && \
+ chown -R appuser:appuser /var/run/nginx.pid && \
+ chown -R appuser:appuser /usr/share/nginx/html
+
+# Switch to non-root user
+USER appuser
+
+# Expose HTTP port
+EXPOSE 80
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+ CMD curl -f http://localhost/ || exit 1
+
+# Start nginx
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/sample_solutions/DocQvision/ui/index.html b/sample_solutions/DocQvision/ui/index.html
new file mode 100644
index 00000000..9da79c86
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ DocQvision - Document Intelligence Platform
+
+
+
+
+
+
diff --git a/sample_solutions/DocQvision/ui/nginx.conf b/sample_solutions/DocQvision/ui/nginx.conf
new file mode 100644
index 00000000..0194eb93
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/nginx.conf
@@ -0,0 +1,26 @@
+server {
+ listen 80;
+ server_name localhost;
+ root /usr/share/nginx/html;
+ index index.html;
+
+ # Frontend routes - React Router
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+
+ # Backend API proxy
+ location /api {
+ proxy_pass http://DocQvision-backend:5001;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection 'upgrade';
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_cache_bypass $http_upgrade;
+ proxy_read_timeout 300s;
+ proxy_connect_timeout 75s;
+ }
+}
diff --git a/sample_solutions/DocQvision/ui/package-lock.json b/sample_solutions/DocQvision/ui/package-lock.json
new file mode 100644
index 00000000..06c2b01b
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/package-lock.json
@@ -0,0 +1,3529 @@
+{
+ "name": "docqvision-ui",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "docqvision-ui",
+ "version": "1.0.0",
+ "dependencies": {
+ "axios": "^1.13.5",
+ "lucide-react": "^0.454.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-dropzone": "^14.2.10",
+ "react-pdf": "^9.1.1",
+ "react-router-dom": "^6.29.0"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.3.3",
+ "autoprefixer": "^10.4.20",
+ "postcss": "^8.4.47",
+ "tailwindcss": "^3.4.14",
+ "vite": "^5.4.10"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "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"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "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": ">= 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"
+ }
+ },
+ "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": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.23.2",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
+ "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.27",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz",
+ "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz",
+ "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz",
+ "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz",
+ "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz",
+ "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz",
+ "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz",
+ "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz",
+ "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz",
+ "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz",
+ "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz",
+ "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz",
+ "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz",
+ "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz",
+ "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz",
+ "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz",
+ "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz",
+ "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz",
+ "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz",
+ "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz",
+ "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz",
+ "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz",
+ "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/attr-accept": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz",
+ "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.23",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
+ "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001760",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.13.5",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
+ "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "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",
+ "optional": true
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.11",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
+ "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "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",
+ "optional": true,
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001761",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz",
+ "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/canvas": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.2.0.tgz",
+ "integrity": "sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "node-addon-api": "^7.0.0",
+ "prebuild-install": "^7.1.3"
+ },
+ "engines": {
+ "node": "^18.12.0 || >= 20.9.0"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "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/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "license": "(MIT OR WTFPL)",
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-selector": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz",
+ "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.7.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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",
+ "optional": true
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.7",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.454.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.454.0.tgz",
+ "integrity": "sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/make-cancellable-promise": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-1.3.2.tgz",
+ "integrity": "sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1"
+ }
+ },
+ "node_modules/make-event-props": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-1.6.2.tgz",
+ "integrity": "sha512-iDwf7mA03WPiR8QxvcVHmVWEPfMY1RZXerDVNCRYW7dUr2ppH3J58Rwb39/WG39yTZdRSxr3x+2v22tvI0VEvA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge-refs": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-1.3.0.tgz",
+ "integrity": "sha512-nqXPXbso+1dcKDpPCXvwZyJILz+vSLqGGOnDrYHQYE+B8n9JTCekVLC65AfCpR4ggVyA/45Y0iR9LDyS2iI+zA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "optional": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "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",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-build-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
+ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/node-abi": {
+ "version": "3.85.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz",
+ "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-abi/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "optional": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path2d": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.2.tgz",
+ "integrity": "sha512-+vnG6S4dYcYxZd+CZxzXCNKdELYZSKfohrk98yajCo1PtRoDgCTrrwOvK1GT0UoAdVszagDVllQc0U1vaX4NUQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pdfjs-dist": {
+ "version": "4.8.69",
+ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.8.69.tgz",
+ "integrity": "sha512-IHZsA4T7YElCKNNXtiLgqScw4zPd3pG9do8UrznC757gMd7UPeHSL2qwNNMJo4r79fl8oj1Xx+1nh2YkzdMpLQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "canvas": "^3.0.0-rc2",
+ "path2d": "^0.2.1"
+ }
+ },
+ "node_modules/picocolors": {
+ "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": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postcss": {
+ "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",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+ "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
+ "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
+ "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.1.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "jiti": ">=1.21.0",
+ "postcss": ">=8.0.9",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prebuild-install": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
+ "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "detect-libc": "^2.0.0",
+ "expand-template": "^2.0.3",
+ "github-from-package": "0.0.0",
+ "minimist": "^1.2.3",
+ "mkdirp-classic": "^0.5.3",
+ "napi-build-utils": "^2.0.0",
+ "node-abi": "^3.3.0",
+ "pump": "^3.0.0",
+ "rc": "^1.2.7",
+ "simple-get": "^4.0.0",
+ "tar-fs": "^2.0.0",
+ "tunnel-agent": "^0.6.0"
+ },
+ "bin": {
+ "prebuild-install": "bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
+ "node_modules/pump": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "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/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+ "optional": true,
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-dropzone": {
+ "version": "14.3.8",
+ "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz",
+ "integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==",
+ "license": "MIT",
+ "dependencies": {
+ "attr-accept": "^2.2.4",
+ "file-selector": "^2.1.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">= 10.13"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8 || 18.0.0"
+ }
+ },
+ "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/react-pdf": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-9.2.1.tgz",
+ "integrity": "sha512-AJt0lAIkItWEZRA5d/mO+Om4nPCuTiQ0saA+qItO967DTjmGjnhmF+Bi2tL286mOTfBlF5CyLzJ35KTMaDoH+A==",
+ "license": "MIT",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "dequal": "^2.0.3",
+ "make-cancellable-promise": "^1.3.1",
+ "make-event-props": "^1.6.0",
+ "merge-refs": "^1.3.0",
+ "pdfjs-dist": "4.8.69",
+ "tiny-invariant": "^1.0.0",
+ "warning": "^4.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "6.30.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
+ "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.23.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.30.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
+ "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.23.2",
+ "react-router": "6.30.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
+ "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.54.0",
+ "@rollup/rollup-android-arm64": "4.54.0",
+ "@rollup/rollup-darwin-arm64": "4.54.0",
+ "@rollup/rollup-darwin-x64": "4.54.0",
+ "@rollup/rollup-freebsd-arm64": "4.54.0",
+ "@rollup/rollup-freebsd-x64": "4.54.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.54.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.54.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.54.0",
+ "@rollup/rollup-linux-arm64-musl": "4.54.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.54.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.54.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.54.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.54.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.54.0",
+ "@rollup/rollup-linux-x64-gnu": "4.54.0",
+ "@rollup/rollup-linux-x64-musl": "4.54.0",
+ "@rollup/rollup-openharmony-arm64": "4.54.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.54.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.54.0",
+ "@rollup/rollup-win32-x64-gnu": "4.54.0",
+ "@rollup/rollup-win32-x64-msvc": "4.54.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "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": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "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",
+ "optional": true
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "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",
+ "optional": true
+ },
+ "node_modules/simple-get": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+ "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",
+ "optional": true,
+ "dependencies": {
+ "decompress-response": "^6.0.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "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/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sucrase": {
+ "version": "3.35.1",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
+ "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "tinyglobby": "^0.2.11",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.4.19",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
+ "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.6.0",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.2",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.21.7",
+ "lilconfig": "^3.1.3",
+ "micromatch": "^4.0.8",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.1.1",
+ "postcss": "^8.4.47",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
+ "postcss-nested": "^6.2.0",
+ "postcss-selector-parser": "^6.1.2",
+ "resolve": "^1.22.8",
+ "sucrase": "^3.35.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
+ "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.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",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "5.4.21",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ }
+ }
+}
diff --git a/sample_solutions/DocQvision/ui/package.json b/sample_solutions/DocQvision/ui/package.json
new file mode 100644
index 00000000..d95b0544
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "DocQvision-ui",
+ "private": true,
+ "version": "1.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^6.29.0",
+ "axios": "^1.13.5",
+ "react-pdf": "^9.1.1",
+ "react-dropzone": "^14.2.10",
+ "lucide-react": "^0.454.0"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.3.3",
+ "vite": "^5.4.10",
+ "autoprefixer": "^10.4.20",
+ "postcss": "^8.4.47",
+ "tailwindcss": "^3.4.14"
+ }
+}
diff --git a/sample_solutions/DocQvision/ui/postcss.config.js b/sample_solutions/DocQvision/ui/postcss.config.js
new file mode 100644
index 00000000..2e7af2b7
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/sample_solutions/DocQvision/ui/src/App.jsx b/sample_solutions/DocQvision/ui/src/App.jsx
new file mode 100644
index 00000000..d53c819a
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/App.jsx
@@ -0,0 +1,23 @@
+import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
+import Layout from './components/Layout'
+import ConfigurePage from './pages/ConfigurePage'
+import UploadPage from './pages/UploadPage'
+import ResultsPage from './pages/ResultsPage'
+import HistoryPage from './pages/HistoryPage'
+
+function App() {
+ return (
+
+
+
+ } />
+ } />
+ } />
+ } />
+
+
+
+ )
+}
+
+export default App
diff --git a/sample_solutions/DocQvision/ui/src/components/ChatInterface.jsx b/sample_solutions/DocQvision/ui/src/components/ChatInterface.jsx
new file mode 100644
index 00000000..b6f693b0
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/components/ChatInterface.jsx
@@ -0,0 +1,74 @@
+import { useState } from 'react'
+import { Send } from 'lucide-react'
+
+const ChatInterface = ({ onSendMessage, chatHistory, isLoading }) => {
+ const [message, setMessage] = useState('')
+
+ const handleSubmit = (e) => {
+ e.preventDefault()
+ if (message.trim() && !isLoading) {
+ onSendMessage(message)
+ setMessage('')
+ }
+ }
+
+ return (
+
+
+ {chatHistory.length === 0 ? (
+
+
Chat with AI to define extraction fields
+
+
Examples:
+
• "I want to extract invoice data"
+
• "Extract patient name, medication, and dosage from prescriptions"
+
• "Get company name, date, and terms from contracts"
+
+
+ AI will ask follow-up questions to build your extraction template
+
+
+ ) : (
+ chatHistory.map((msg, idx) => (
+
+ ))
+ )}
+
+
+
+
+ )
+}
+
+export default ChatInterface
diff --git a/sample_solutions/DocQvision/ui/src/components/ErrorMessage.jsx b/sample_solutions/DocQvision/ui/src/components/ErrorMessage.jsx
new file mode 100644
index 00000000..3368ce6b
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/components/ErrorMessage.jsx
@@ -0,0 +1,39 @@
+import { AlertCircle, X } from 'lucide-react'
+
+const ErrorMessage = ({ message, onClose, type = 'error' }) => {
+ if (!message) return null
+
+ const styles = {
+ error: 'bg-red-50 border-red-200 text-red-800',
+ warning: 'bg-amber-50 border-amber-200 text-amber-800',
+ info: 'bg-blue-50 border-blue-200 text-blue-800',
+ success: 'bg-green-50 border-green-200 text-green-800'
+ }
+
+ const iconColors = {
+ error: 'text-red-600',
+ warning: 'text-amber-600',
+ info: 'text-blue-600',
+ success: 'text-green-600'
+ }
+
+ return (
+
+
+
+ {onClose && (
+
+
+
+ )}
+
+ )
+}
+
+export default ErrorMessage
diff --git a/sample_solutions/DocQvision/ui/src/components/ExtractionStatus.jsx b/sample_solutions/DocQvision/ui/src/components/ExtractionStatus.jsx
new file mode 100644
index 00000000..2643e655
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/components/ExtractionStatus.jsx
@@ -0,0 +1,202 @@
+import { useState, useEffect } from 'react'
+import { Loader, CheckCircle, XCircle, Clock } from 'lucide-react'
+import { getExtractionResult } from '../services/api'
+
+const ExtractionStatus = ({ jobId, onComplete, onError, compact = false }) => {
+ const [status, setStatus] = useState('pending')
+ const [result, setResult] = useState(null)
+ const [progress, setProgress] = useState(0)
+ const [startTime] = useState(Date.now())
+ const [, setTick] = useState(0) // Force re-render for elapsed time display
+
+ useEffect(() => {
+ if (!jobId) return
+
+ const pollInterval = setInterval(async () => {
+ try {
+ const data = await getExtractionResult(jobId)
+ setStatus(data.status)
+
+ if (data.status === 'success') {
+ setProgress(100)
+ setResult(data)
+ clearInterval(pollInterval)
+ onComplete?.(data)
+ } else if (data.status === 'failed') {
+ clearInterval(pollInterval)
+ onError?.(data.error_message || 'Extraction failed')
+ } else if (data.status === 'running') {
+ // Slow, realistic progress for Xeon machine (30-75 seconds per page)
+ // Expected total time: 60-180 seconds for typical 2-3 page documents
+ const elapsedSeconds = (Date.now() - startTime) / 1000
+
+ // Logarithmic progress curve - slower as it approaches completion
+ // Designed for 60-180 second processing time
+ let calculatedProgress = 0
+ if (elapsedSeconds < 30) {
+ // First 30 seconds: 0% -> 20% (fast initial progress)
+ calculatedProgress = (elapsedSeconds / 30) * 20
+ } else if (elapsedSeconds < 60) {
+ // 30-60 seconds: 20% -> 40%
+ calculatedProgress = 20 + ((elapsedSeconds - 30) / 30) * 20
+ } else if (elapsedSeconds < 120) {
+ // 60-120 seconds: 40% -> 65%
+ calculatedProgress = 40 + ((elapsedSeconds - 60) / 60) * 25
+ } else if (elapsedSeconds < 180) {
+ // 120-180 seconds: 65% -> 80%
+ calculatedProgress = 65 + ((elapsedSeconds - 120) / 60) * 15
+ } else {
+ // After 180 seconds: cap at 85% (never reach 100% until backend confirms)
+ calculatedProgress = 80 + Math.min((elapsedSeconds - 180) / 60 * 5, 5)
+ }
+
+ setProgress(Math.min(Math.floor(calculatedProgress), 85))
+ }
+ } catch (error) {
+ console.error('Polling error:', error)
+ clearInterval(pollInterval)
+ onError?.(error.message)
+ }
+ }, 2000)
+
+ return () => clearInterval(pollInterval)
+ }, [jobId, onComplete, onError, startTime])
+
+ // Update elapsed time display every second when processing
+ useEffect(() => {
+ if (status !== 'running') return
+
+ const tickInterval = setInterval(() => {
+ setTick(t => t + 1) // Force re-render to update elapsed time
+ }, 1000)
+
+ return () => clearInterval(tickInterval)
+ }, [status])
+
+ const getStatusIcon = () => {
+ const iconClass = "w-6 h-6"
+ switch (status) {
+ case 'success':
+ return
+ case 'failed':
+ return
+ case 'running':
+ return
+ default:
+ return
+ }
+ }
+
+ const getStatusColor = () => {
+ switch (status) {
+ case 'success': return 'text-green-600'
+ case 'failed': return 'text-red-600'
+ case 'running': return 'text-blue-600'
+ default: return 'text-gray-600'
+ }
+ }
+
+ if (compact) {
+ const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000)
+ const minutes = Math.floor(elapsedSeconds / 60)
+ const seconds = elapsedSeconds % 60
+ const timeDisplay = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`
+
+ return (
+
+
+ {getStatusIcon()}
+ {status}
+ {status === 'running' && (
+ ({timeDisplay} elapsed)
+ )}
+
+ {status === 'running' && (
+
+
+
+ Processing on inference server... {progress}%
+
+
+ )}
+
+ )
+ }
+
+ return (
+
+
+ {getStatusIcon()}
+
+
{status}
+
Job ID: {jobId}
+
+
+
+ {status === 'running' && (
+
+
+
+
+ Processing on inference server... {progress}%
+
+
+ {(() => {
+ const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000)
+ const minutes = Math.floor(elapsedSeconds / 60)
+ const seconds = elapsedSeconds % 60
+ return minutes > 0 ? `${minutes}m ${seconds}s elapsed` : `${seconds}s elapsed`
+ })()}
+
+
+
+ Note: Processing may take 1-3 minutes depending on document complexity
+
+
+ )}
+
+ {result && result.status === 'success' && (
+
+
+
+ Stage Used:
+ {result.stage_used}
+
+
+ Coverage:
+
+ {result.field_coverage_percent
+ ? `${(result.field_coverage_percent * 100).toFixed(1)}%`
+ : 'N/A'}
+
+
+
+ Processing Time:
+
+ {result.processing_time_ms ? `${result.processing_time_ms}ms` : 'N/A'}
+
+
+ {result.model_used && (
+
+ Model:
+ {result.model_used}
+
+ )}
+
+
+ )}
+
+ )
+}
+
+export default ExtractionStatus
diff --git a/sample_solutions/DocQvision/ui/src/components/FileUpload.jsx b/sample_solutions/DocQvision/ui/src/components/FileUpload.jsx
new file mode 100644
index 00000000..788b9b5c
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/components/FileUpload.jsx
@@ -0,0 +1,113 @@
+import { useState } from 'react'
+import { useDropzone } from 'react-dropzone'
+import { Upload, FileText, X } from 'lucide-react'
+
+const FileUpload = ({ onFileSelect, multiple = false, maxFiles = 5 }) => {
+ const [files, setFiles] = useState([])
+
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
+ accept: { 'application/pdf': ['.pdf'] },
+ maxSize: 10 * 1024 * 1024,
+ multiple: multiple,
+ maxFiles: multiple ? maxFiles : 1,
+ onDrop: (acceptedFiles) => {
+ if (acceptedFiles.length > 0) {
+ if (multiple) {
+ const newFiles = [...files, ...acceptedFiles].slice(0, maxFiles)
+ setFiles(newFiles)
+ onFileSelect(newFiles)
+ } else {
+ const selectedFile = acceptedFiles[0]
+ setFiles([selectedFile])
+ onFileSelect(selectedFile)
+ }
+ }
+ },
+ onDropRejected: (rejections) => {
+ const rejection = rejections[0]
+ if (rejection.errors[0].code === 'file-too-large') {
+ alert('File size must be less than 10MB')
+ } else if (rejection.errors[0].code === 'too-many-files') {
+ alert(`Maximum ${maxFiles} files allowed`)
+ } else {
+ alert('Please upload PDF files only')
+ }
+ },
+ })
+
+ const handleRemove = (indexToRemove) => {
+ if (multiple) {
+ const newFiles = files.filter((_, index) => index !== indexToRemove)
+ setFiles(newFiles)
+ onFileSelect(newFiles)
+ } else {
+ setFiles([])
+ onFileSelect(null)
+ }
+ }
+
+ return (
+
+
+
+ {files.length === 0 ? (
+ <>
+
+
+ {multiple ? `Drop your files here or click to browse` : `Drop your file here or click to browse`}
+
+
Supported formats: PDF
+
+ Maximum file size: 10MB{multiple ? ` • Maximum ${maxFiles} files` : ''}
+
+ >
+ ) : (
+
+
+ {files.length} file{files.length > 1 ? 's' : ''} selected
+ {multiple && ` (max ${maxFiles})`}
+
+
+ )}
+
+
+ {files.length > 0 && (
+
+ {files.map((file, index) => (
+
+
+
+
+
{file.name}
+
+ {(file.size / 1024 / 1024).toFixed(2)} MB
+
+
+
+
{
+ e.stopPropagation()
+ handleRemove(index)
+ }}
+ className="text-gray-400 hover:text-red-500 transition-colors ml-2 flex-shrink-0"
+ >
+
+
+
+ ))}
+
+ )}
+
+ )
+}
+
+export default FileUpload
diff --git a/sample_solutions/DocQvision/ui/src/components/Layout.jsx b/sample_solutions/DocQvision/ui/src/components/Layout.jsx
new file mode 100644
index 00000000..ce71ff8b
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/components/Layout.jsx
@@ -0,0 +1,59 @@
+import { Link, useLocation } from 'react-router-dom'
+import { FileSearch } from 'lucide-react'
+
+const Layout = ({ children }) => {
+ const location = useLocation()
+
+ const navigation = [
+ { name: 'Configure', href: '/' },
+ { name: 'Upload', href: '/upload' },
+ { name: 'History', href: '/history' },
+ ]
+
+ return (
+
+
+
+
+
+
+
+ DocQvision
+
+
+
+
+ {navigation.map((item) => (
+
+ {item.name}
+
+ ))}
+
+
+
+
+
+
+ {location.pathname === '/' && (
+
+
+ DocQvision uses AI vision models to extract structured data from PDF documents.
+ Configure extraction templates once, then reuse them for processing multiple documents.
+
+
+ )}
+ {children}
+
+
+ )
+}
+
+export default Layout
diff --git a/sample_solutions/DocQvision/ui/src/components/PDFPreview.jsx b/sample_solutions/DocQvision/ui/src/components/PDFPreview.jsx
new file mode 100644
index 00000000..43b8fc62
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/components/PDFPreview.jsx
@@ -0,0 +1,183 @@
+import { useState, useRef, useEffect } from 'react'
+import { FileText, ZoomIn, ZoomOut, RotateCw, AlertCircle } from 'lucide-react'
+import { Document, Page, pdfjs } from 'react-pdf'
+import 'react-pdf/dist/Page/AnnotationLayer.css'
+import 'react-pdf/dist/Page/TextLayer.css'
+
+pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`
+
+const PDFPreview = ({ file }) => {
+ const [numPages, setNumPages] = useState(null)
+ const [pageNumber, setPageNumber] = useState(1)
+ const [fileUrl, setFileUrl] = useState(null)
+ const [scale, setScale] = useState(1.0)
+ const [rotation, setRotation] = useState(0)
+ const [error, setError] = useState(null)
+ const containerRef = useRef(null)
+ const [containerWidth, setContainerWidth] = useState(0)
+
+ useEffect(() => {
+ if (containerRef.current) {
+ const updateWidth = () => {
+ setContainerWidth(containerRef.current.offsetWidth - 32)
+ }
+ updateWidth()
+ window.addEventListener('resize', updateWidth)
+ return () => window.removeEventListener('resize', updateWidth)
+ }
+ }, [])
+
+ const onDocumentLoadSuccess = ({ numPages }) => {
+ setNumPages(numPages)
+ setPageNumber(1)
+ setError(null)
+ }
+
+ const onDocumentLoadError = (error) => {
+ console.error('PDF load error:', error)
+ setError('Failed to load PDF file. Please try again.')
+ setNumPages(null)
+ }
+
+ useEffect(() => {
+ if (file) {
+ setError(null)
+ const url = URL.createObjectURL(file)
+ setFileUrl(url)
+
+ return () => {
+ if (url) {
+ URL.revokeObjectURL(url)
+ }
+ }
+ } else {
+ setFileUrl(null)
+ setNumPages(null)
+ setPageNumber(1)
+ setScale(1.0)
+ setRotation(0)
+ setError(null)
+ }
+ }, [file])
+
+ const handleZoomIn = () => {
+ setScale((prev) => {
+ const newScale = Math.min(prev + 0.2, 3.0)
+ return newScale
+ })
+ }
+
+ const handleZoomOut = () => {
+ setScale((prev) => {
+ const newScale = Math.max(prev - 0.2, 0.5)
+ return newScale
+ })
+ }
+
+ const handleRotate = () => {
+ setRotation((prev) => (prev + 90) % 360)
+ }
+
+ if (!file || !fileUrl) {
+ return (
+
+
+
+
+
+
Upload a PDF to preview
+
+
+
+ )
+ }
+
+ return (
+
+
+
{file.name}
+
+
+
+
+ {Math.round(scale * 100)}%
+
+
+
+
+
+
+ {numPages && (
+
+ Page {pageNumber} of {numPages}
+
+ )}
+
+
+
+ {error ? (
+
+
+
{error}
+
+ The PDF file may be corrupted or incompatible. Please try uploading a different file.
+
+
+ ) : (
+
+
+
+ }
+ >
+
+
+ )}
+
+ {numPages && numPages > 1 && (
+
+ setPageNumber((prev) => Math.max(1, prev - 1))}
+ disabled={pageNumber <= 1}
+ className="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded text-sm disabled:opacity-50 disabled:cursor-not-allowed"
+ >
+ Previous
+
+ setPageNumber((prev) => Math.min(numPages, prev + 1))}
+ disabled={pageNumber >= numPages}
+ className="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded text-sm disabled:opacity-50 disabled:cursor-not-allowed"
+ >
+ Next
+
+
+ )}
+
+ )
+}
+
+export default PDFPreview
diff --git a/sample_solutions/DocQvision/ui/src/index.css b/sample_solutions/DocQvision/ui/src/index.css
new file mode 100644
index 00000000..1393534e
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/index.css
@@ -0,0 +1,77 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ background-color: #f9fafb;
+}
+
+#root {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: #f1f1f1;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #888;
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #555;
+}
+
+@layer components {
+ .card {
+ @apply bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition-shadow;
+ }
+
+ .btn-primary {
+ @apply bg-primary-600 hover:bg-primary-700 text-white font-medium px-6 py-2.5 rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed;
+ }
+
+ .btn-secondary {
+ @apply bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium px-6 py-2.5 rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed;
+ }
+
+ .file-drop-zone {
+ @apply border-2 border-dashed rounded-lg p-12 text-center transition-colors duration-200;
+ }
+
+ .file-drop-zone-active {
+ @apply border-primary-500 bg-primary-50;
+ }
+
+ .file-drop-zone-inactive {
+ @apply border-gray-300 bg-gray-50;
+ }
+}
diff --git a/sample_solutions/DocQvision/ui/src/main.jsx b/sample_solutions/DocQvision/ui/src/main.jsx
new file mode 100644
index 00000000..54b39dd1
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/main.jsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App.jsx'
+import './index.css'
+
+ReactDOM.createRoot(document.getElementById('root')).render(
+
+
+ ,
+)
diff --git a/sample_solutions/DocQvision/ui/src/pages/ConfigurePage.jsx b/sample_solutions/DocQvision/ui/src/pages/ConfigurePage.jsx
new file mode 100644
index 00000000..50ae768d
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/pages/ConfigurePage.jsx
@@ -0,0 +1,486 @@
+import { useState, useEffect } from 'react'
+import { Upload, Save, PlayCircle, RefreshCw, X, AlertCircle } from 'lucide-react'
+import PDFPreview from '../components/PDFPreview'
+import ChatInterface from '../components/ChatInterface'
+import ErrorMessage from '../components/ErrorMessage'
+import { configureSchema, saveTemplate, uploadDocument, createTemplate, extractData, getExtractionResult, deleteTemplate } from '../services/api'
+
+const ConfigurePage = () => {
+ const [file, setFile] = useState(null)
+ const [chatHistory, setChatHistory] = useState([])
+ const [schema, setSchema] = useState({})
+ const [isLoading, setIsLoading] = useState(false)
+ const [sessionId, setSessionId] = useState(null)
+ const [testResults, setTestResults] = useState(null)
+ const [testingStatus, setTestingStatus] = useState('')
+ const [error, setError] = useState(null)
+ const [successMessage, setSuccessMessage] = useState(null)
+
+ useEffect(() => {
+ // Load saved session from localStorage
+ const savedSession = localStorage.getItem('configure_session')
+ if (savedSession) {
+ try {
+ const { sessionId: savedSessionId, schema: savedSchema, chatHistory: savedHistory } = JSON.parse(savedSession)
+
+ // If there's a saved session with fields, ask user if they want to continue or start fresh
+ if (savedSchema && Object.keys(savedSchema).length > 0) {
+ const continueSession = window.confirm(
+ `📋 Previous session found with ${Object.keys(savedSchema).length} configured fields.\n\n` +
+ `Do you want to continue where you left off?\n\n` +
+ `Click "OK" to continue, or "Cancel" to start a new template.`
+ )
+
+ if (!continueSession) {
+ localStorage.removeItem('configure_session')
+ const newSessionId = crypto.randomUUID()
+ setSessionId(newSessionId)
+ return
+ }
+ }
+
+ setSessionId(savedSessionId)
+ setSchema(savedSchema || {})
+ setChatHistory(savedHistory || [])
+ } catch (e) {
+ console.error('Failed to load saved session:', e)
+ localStorage.removeItem('configure_session')
+ const newSessionId = crypto.randomUUID()
+ setSessionId(newSessionId)
+ }
+ } else {
+ const newSessionId = crypto.randomUUID()
+ setSessionId(newSessionId)
+ }
+ }, [])
+
+ useEffect(() => {
+ // Save session to localStorage whenever it changes
+ if (sessionId) {
+ localStorage.setItem('configure_session', JSON.stringify({
+ sessionId,
+ schema,
+ chatHistory
+ }))
+ }
+ }, [sessionId, schema, chatHistory])
+
+ const handleFileUpload = (e) => {
+ // Clear previous errors
+ setError(null)
+ setSuccessMessage(null)
+
+ // Prevent changing PDF if session has schema configured
+ if (Object.keys(schema).length > 0 && file) {
+ const confirm = window.confirm(
+ '⚠️ Uploading a new PDF will require starting a new session.\n\n' +
+ 'Your current schema configuration will be cleared.\n\n' +
+ 'Click "New Template" to start fresh, or cancel to keep current session.'
+ )
+ if (!confirm) {
+ e.target.value = '' // Reset file input
+ return
+ }
+ // User confirmed - clear session
+ handleNewTemplate()
+ return
+ }
+
+ const selectedFile = e.target.files[0]
+ if (selectedFile && selectedFile.type === 'application/pdf') {
+ if (selectedFile.size <= 10 * 1024 * 1024) {
+ setFile(selectedFile)
+ } else {
+ setError('File size must be less than 10MB')
+ e.target.value = ''
+ }
+ } else {
+ setError('Please upload a PDF file')
+ e.target.value = ''
+ }
+ }
+
+ const handleSendMessage = async (message) => {
+ // Add user message immediately to chat history
+ const userMessage = { role: 'user', content: message }
+ setChatHistory(prev => [...prev, userMessage])
+
+ setIsLoading(true)
+ try {
+ const response = await configureSchema(message, sessionId)
+ setChatHistory(response.chat_history || [])
+ setSchema(response.schema || {})
+ if (response.session_id && !sessionId) {
+ setSessionId(response.session_id)
+ }
+ } catch (error) {
+ console.error('Error sending message:', error)
+ setError('Failed to process message')
+ // Remove the user message if API call failed
+ setChatHistory(prev => prev.filter(msg => msg !== userMessage))
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ const handleNewTemplate = () => {
+ if (Object.keys(schema).length > 0) {
+ const confirm = window.confirm('This will clear your current configuration. Continue?')
+ if (!confirm) return
+ }
+ const newSessionId = crypto.randomUUID()
+ setSessionId(newSessionId)
+ setSchema({})
+ setChatHistory([])
+ setFile(null)
+ setTestResults(null)
+ localStorage.removeItem('configure_session')
+ }
+
+ const handleTestExtraction = async () => {
+ if (!file) {
+ setError('Please upload a document first')
+ return
+ }
+ if (Object.keys(schema).length === 0) {
+ setError('Please configure extraction fields first')
+ return
+ }
+
+ setIsLoading(true)
+ setTestResults(null)
+ let tempTemplateId = null
+
+ try {
+ // 1. Upload the document
+ setTestingStatus('Uploading document...')
+ const uploadResponse = await uploadDocument(file)
+ const documentId = uploadResponse.document_id
+
+ // 2. Create a temporary template
+ setTestingStatus('Creating temporary template...')
+
+ // Convert schema to proper FieldSchema format
+ const convertToFieldSchema = (fieldValue) => {
+ // If it's already a proper object with 'type' field
+ if (typeof fieldValue === 'object' && fieldValue !== null && 'type' in fieldValue) {
+ const result = {
+ type: fieldValue.type,
+ required: fieldValue.required !== undefined ? fieldValue.required : true
+ }
+ // Only add description if it exists and is not empty
+ if (fieldValue.description && fieldValue.description.trim()) {
+ result.description = fieldValue.description
+ }
+ return result
+ }
+ // If it's a simple string type
+ if (typeof fieldValue === 'string') {
+ return { type: fieldValue, required: true }
+ }
+ // Fallback to string
+ return { type: 'string', required: true }
+ }
+
+ const tempTemplate = await createTemplate({
+ name: `Test_${Date.now()}`,
+ doc_type: 'test',
+ schema_json: Object.fromEntries(
+ Object.entries(schema).map(([field, fieldValue]) => [
+ field,
+ convertToFieldSchema(fieldValue)
+ ])
+ )
+ })
+ tempTemplateId = tempTemplate.id
+
+ // 3. Run extraction
+ setTestingStatus('Running extraction...')
+ const extractionJob = await extractData(documentId, tempTemplateId)
+
+ // 4. Poll for results
+ let attempts = 0
+ const maxAttempts = 30
+ while (attempts < maxAttempts) {
+ const progress = Math.round(((attempts + 1) / maxAttempts) * 100)
+ setTestingStatus(`Processing extraction... ${progress}%`)
+ await new Promise(resolve => setTimeout(resolve, 2000))
+ const result = await getExtractionResult(extractionJob.id)
+
+ if (result.status === 'success') {
+ setTestResults(result)
+ break
+ } else if (result.status === 'failed') {
+ setError(`Extraction Failed: ${result.error_message || 'Unknown error'}`)
+ break
+ }
+
+ attempts++
+ }
+
+ if (attempts >= maxAttempts) {
+ setError('Test extraction timed out. Please try again.')
+ }
+
+ } catch (error) {
+ console.error('Error testing extraction:', error)
+ setError(`Failed to test extraction: ${error.message}`)
+ } finally {
+ // Clean up: Delete the temporary template
+ if (tempTemplateId) {
+ try {
+ setTestingStatus('Cleaning up...')
+ await deleteTemplate(tempTemplateId)
+ } catch (err) {
+ console.error('Failed to delete temporary template:', err)
+ }
+ }
+ setTestingStatus('')
+ setIsLoading(false)
+ }
+ }
+
+ const handleSaveTemplate = async () => {
+ if (Object.keys(schema).length === 0) {
+ setError('Please configure extraction fields first')
+ return
+ }
+
+ const templateName = prompt('Enter template name:')
+ if (!templateName || !templateName.trim()) {
+ setError('Template name is required. Please provide a name for your template.')
+ return
+ }
+
+ const templateType = prompt('Enter document type (e.g., invoice, prescription, contract):')
+ if (!templateType || !templateType.trim()) {
+ setError('Document type is required. This helps validate documents during extraction.')
+ return
+ }
+
+ setIsLoading(true)
+ try {
+ // Check for duplicate template names
+ const { getTemplates } = await import('../services/api')
+ const existingTemplates = await getTemplates()
+ const templateList = Array.isArray(existingTemplates) ? existingTemplates : (existingTemplates.templates || [])
+
+ const duplicate = templateList.find(t => t.name.toLowerCase() === templateName.trim().toLowerCase())
+ if (duplicate) {
+ const overwrite = window.confirm(
+ `⚠️ Template "${templateName}" already exists!\n\n` +
+ `Do you want to overwrite it?`
+ )
+ if (!overwrite) {
+ setIsLoading(false)
+ return
+ }
+ }
+
+ await saveTemplate(templateName.trim(), templateType.trim(), schema)
+ setSuccessMessage('Template saved successfully!')
+ localStorage.removeItem('configure_session') // Clear session after successful save
+ } catch (error) {
+ console.error('Error saving template:', error)
+ if (error.response?.data?.detail) {
+ setError(`Failed to save template: ${error.response.data.detail}`)
+ } else {
+ setError('Failed to save template. Please try again.')
+ }
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ return (
+
+
+
+
Configure Document Type
+
+ Upload a sample document and chat with AI to define extraction fields
+
+ {Object.keys(schema).length > 0 && (
+
+
+ Active session with {Object.keys(schema).length} configured fields
+
+ )}
+
+
+
+ New Template
+
+
+
+ {/* Error Messages */}
+
setError(null)} />
+ setSuccessMessage(null)} />
+
+
+
+
+
+
Document Preview
+
+
+ Upload PDF
+
+
+
+
Supports PDF up to 10MB
+
+
+
+
+
+
+
+ {Object.keys(schema).length > 0 && (
+
+
+ Configured Fields ({Object.keys(schema).length})
+
+
+
+ )}
+
+ {/* Loading modal during test extraction */}
+ {isLoading && testingStatus && (
+
+
+
+
+
Testing Extraction
+
{testingStatus}
+
+
+
+ )}
+
+ {/* Test results section */}
+ {testResults && (
+
+
+
+
Test Extraction Results
+
+ Coverage: {testResults.field_coverage_percent ? (testResults.field_coverage_percent * 100).toFixed(0) : 0}% |
+ Stage: {testResults.stage_used || 'N/A'} |
+ Time: {testResults.processing_time_ms || 0}ms
+
+
+
setTestResults(null)}
+ className="text-gray-400 hover:text-gray-600"
+ title="Clear test results"
+ >
+
+
+
+
+ {Object.entries(testResults.extracted_data || {}).map(([field, value]) => (
+
+
{field}
+
+ {value !== null && value !== undefined && value !== '' ? (
+ Array.isArray(value) ? (
+
+ {value.map((item, idx) => (
+
+ •
+
+ {typeof item === 'object' && item !== null ? (
+
+ {Object.entries(item).map(([key, val]) => (
+
+
+ {key.replace(/_/g, ' ')}:
+
+
+ {val !== null && val !== undefined ? String(val) : '-'}
+
+
+ ))}
+
+ ) : (
+
{String(item)}
+ )}
+
+
+ ))}
+
+ ) : (
+
{String(value)}
+ )
+ ) : (
+
+
+ Not found
+
+ )}
+
+
+ ))}
+
+
+ )}
+
+
+
+
+ Test Extraction
+
+
+
+ Save Template
+
+
+
+ )
+}
+
+export default ConfigurePage
diff --git a/sample_solutions/DocQvision/ui/src/pages/HistoryPage.jsx b/sample_solutions/DocQvision/ui/src/pages/HistoryPage.jsx
new file mode 100644
index 00000000..dc3edc07
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/pages/HistoryPage.jsx
@@ -0,0 +1,573 @@
+import { useState, useEffect } from 'react'
+import { FileText, Eye, Download, RefreshCw, Trash2, FileJson, FileSpreadsheet, CheckSquare, Square, Archive, X, AlertCircle, Clock, CheckCircle, XCircle } from 'lucide-react'
+import { getExtractionHistory, getTemplates, deleteExtraction, reExtract, getExtractionResult } from '../services/api'
+
+const HistoryPage = () => {
+ const [history, setHistory] = useState([])
+ const [templates, setTemplates] = useState([])
+ const [loading, setLoading] = useState(true)
+ const [selectedItems, setSelectedItems] = useState([])
+ const [showResultsModal, setShowResultsModal] = useState(false)
+ const [selectedResult, setSelectedResult] = useState(null)
+ const [pollingJobs, setPollingJobs] = useState(new Set())
+ const [filters, setFilters] = useState({
+ template_id: '',
+ status: '',
+ limit: 50
+ })
+
+ useEffect(() => {
+ loadTemplates()
+ loadHistory()
+
+ // Auto-refresh every 3 seconds to poll for running jobs
+ const interval = setInterval(() => {
+ if (history.some(item => item.status === 'running' || item.status === 'pending')) {
+ loadHistory()
+ }
+ }, 3000)
+
+ return () => clearInterval(interval)
+ }, [filters])
+
+ const loadTemplates = async () => {
+ try {
+ const data = await getTemplates()
+ setTemplates(data)
+ } catch (error) {
+ console.error('Failed to load templates:', error)
+ }
+ }
+
+ const loadHistory = async () => {
+ setLoading(true)
+ try {
+ const data = await getExtractionHistory(filters)
+ setHistory(data)
+ } catch (error) {
+ console.error('Failed to load history:', error)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const handleSelectItem = (id) => {
+ setSelectedItems(prev =>
+ prev.includes(id)
+ ? prev.filter(item => item !== id)
+ : [...prev, id]
+ )
+ }
+
+ const handleSelectAll = () => {
+ if (selectedItems.length === history.length) {
+ setSelectedItems([])
+ } else {
+ setSelectedItems(history.map(item => item.id))
+ }
+ }
+
+ const handleViewResults = (item) => {
+ setSelectedResult(item)
+ setShowResultsModal(true)
+ }
+
+ const handleDownloadJSON = (item) => {
+ const dataStr = JSON.stringify(item.extracted_data, null, 2)
+ const dataBlob = new Blob([dataStr], { type: 'application/json' })
+ const url = URL.createObjectURL(dataBlob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `${item.document_filename.replace('.pdf', '')}_extraction.json`
+ link.click()
+ URL.revokeObjectURL(url)
+ }
+
+ const handleDownloadCSV = (item) => {
+ if (!item.extracted_data) return
+
+ const headers = Object.keys(item.extracted_data)
+ const values = Object.values(item.extracted_data).map(v => {
+ if (v === null || v === undefined) return ''
+ if (Array.isArray(v) || typeof v === 'object') {
+ // Serialize arrays/objects as JSON
+ return JSON.stringify(v)
+ }
+ return String(v)
+ })
+ const csv = `${headers.join(',')}\n${values.map(v => `"${String(v).replace(/"/g, '""')}"`).join(',')}`
+
+ const dataBlob = new Blob([csv], { type: 'text/csv' })
+ const url = URL.createObjectURL(dataBlob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `${item.document_filename.replace('.pdf', '')}_extraction.csv`
+ link.click()
+ URL.revokeObjectURL(url)
+ }
+
+ const handleReExtract = async (item) => {
+ try {
+ const newJob = await reExtract(item.id)
+ alert(`✅ Re-extraction started! Job ID: ${newJob.id.substring(0, 8)}...`)
+ loadHistory()
+ } catch (error) {
+ console.error('Failed to re-extract:', error)
+ alert(`❌ Re-extraction failed: ${error.response?.data?.detail || error.message}`)
+ }
+ }
+
+ const handleDelete = async (id) => {
+ if (!window.confirm('Are you sure you want to delete this extraction result?')) return
+
+ try {
+ await deleteExtraction(id)
+ loadHistory()
+ setSelectedItems(prev => prev.filter(item => item !== id))
+ } catch (error) {
+ console.error('Failed to delete:', error)
+ alert(`Failed to delete: ${error.response?.data?.detail || error.message}`)
+ }
+ }
+
+ const handleBulkDelete = async () => {
+ if (selectedItems.length === 0) return
+ if (!window.confirm(`Delete ${selectedItems.length} selected items?`)) return
+
+ try {
+ await Promise.all(selectedItems.map(id => deleteExtraction(id)))
+ loadHistory()
+ setSelectedItems([])
+ } catch (error) {
+ console.error('Failed to bulk delete:', error)
+ alert('Failed to delete some items')
+ }
+ }
+
+ const handleBulkExportJSON = () => {
+ if (selectedItems.length === 0) return
+
+ const selectedData = history.filter(item => selectedItems.includes(item.id))
+ const exportData = selectedData.map(item => ({
+ filename: item.document_filename,
+ template: item.template_name,
+ extracted_data: item.extracted_data,
+ metadata: {
+ status: item.status,
+ coverage: item.field_coverage_percent,
+ processing_time_ms: item.processing_time_ms,
+ created_at: item.created_at
+ }
+ }))
+
+ const dataStr = JSON.stringify(exportData, null, 2)
+ const dataBlob = new Blob([dataStr], { type: 'application/json' })
+ const url = URL.createObjectURL(dataBlob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `bulk_export_${Date.now()}.json`
+ link.click()
+ URL.revokeObjectURL(url)
+ }
+
+ const getStatusIcon = (status) => {
+ switch (status) {
+ case 'success':
+ return
+ case 'failed':
+ return
+ case 'running':
+ return
+ case 'pending':
+ return
+ default:
+ return
+ }
+ }
+
+ const getStatusBadge = (status) => {
+ const colors = {
+ success: 'bg-green-100 text-green-800',
+ failed: 'bg-red-100 text-red-800',
+ running: 'bg-blue-100 text-blue-800',
+ pending: 'bg-gray-100 text-gray-800'
+ }
+ return (
+
+ {status}
+
+ )
+ }
+
+ const getStageBadge = (stage) => {
+ const colors = {
+ traditional: 'bg-purple-100 text-purple-800',
+ vision: 'bg-indigo-100 text-indigo-800',
+ mock: 'bg-gray-100 text-gray-800'
+ }
+ return stage ? (
+
+ {stage}
+
+ ) : null
+ }
+
+ return (
+
+
+
Extraction History
+
View, manage, and export document extraction results
+
+
+ {/* Filters */}
+
+
+
+
+ Template
+
+ setFilters({ ...filters, template_id: e.target.value })}
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+ >
+ All Templates
+ {templates.map((template) => (
+
+ {template.name}
+
+ ))}
+
+
+
+
+
+ Status
+
+ setFilters({ ...filters, status: e.target.value })}
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+ >
+ All Status
+ Success
+ Failed
+ Running
+ Pending
+
+
+
+
+
+ Results Per Page
+
+ setFilters({ ...filters, limit: parseInt(e.target.value) })}
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+ >
+ 25
+ 50
+ 100
+
+
+
+
+
+ {/* Bulk Actions */}
+ {selectedItems.length > 0 && (
+
+
+
+ {selectedItems.length} items selected
+
+
+
+
+ Export Selected (JSON)
+
+
+
+ Delete Selected
+
+
+
+ )}
+
+ {loading ? (
+
+ ) : history.length === 0 ? (
+
+
+
No extraction history found
+
Run your first extraction to see results here
+
+ ) : (
+
+ {/* Table Header */}
+
+
+
+
+ {selectedItems.length === history.length ? (
+
+ ) : (
+
+ )}
+
+
+
Document
+
Template
+
Status
+
Coverage
+
Created
+
Actions
+
+
+
+ {/* Table Body */}
+
+ {history.map((item) => (
+
+
+ {/* Checkbox */}
+
+ handleSelectItem(item.id)}
+ className="text-gray-600 hover:text-gray-900"
+ >
+ {selectedItems.includes(item.id) ? (
+
+ ) : (
+
+ )}
+
+
+
+ {/* Document */}
+
+
+
+
+
+ {item.document_filename || 'Unknown'}
+
+ {item.document_page_count && (
+
+ {item.document_page_count} pages
+
+ )}
+
+
+
+
+ {/* Template */}
+
+
{item.template_name || 'N/A'}
+ {item.stage_used && (
+
{getStageBadge(item.stage_used)}
+ )}
+
+
+ {/* Status */}
+
+
+ {getStatusIcon(item.status)}
+
+
+
+ {/* Coverage */}
+
+ {item.field_coverage_percent !== null && item.field_coverage_percent !== undefined ? (
+
+
+ {(item.field_coverage_percent * 100).toFixed(0)}%
+
+ {item.processing_time_ms && (
+
+ {item.processing_time_ms}ms
+
+ )}
+
+ ) : (
+
-
+ )}
+
+
+ {/* Created */}
+
+
+ {new Date(item.created_at).toLocaleDateString()}
+
+
+ {new Date(item.created_at).toLocaleTimeString()}
+
+
+
+ {/* Actions */}
+
+
+ {item.status === 'success' && item.extracted_data && (
+ <>
+ handleViewResults(item)}
+ className="p-1.5 text-blue-600 hover:bg-blue-50 rounded transition-colors"
+ title="View Results"
+ >
+
+
+ handleDownloadJSON(item)}
+ className="p-1.5 text-green-600 hover:bg-green-50 rounded transition-colors"
+ title="Download JSON"
+ >
+
+
+ handleDownloadCSV(item)}
+ className="p-1.5 text-green-600 hover:bg-green-50 rounded transition-colors"
+ title="Download CSV"
+ >
+
+
+ >
+ )}
+ {(item.status === 'success' || item.status === 'failed') && (
+ handleReExtract(item)}
+ className="p-1.5 text-purple-600 hover:bg-purple-50 rounded transition-colors"
+ title="Re-extract"
+ >
+
+
+ )}
+ handleDelete(item.id)}
+ className="p-1.5 text-red-600 hover:bg-red-50 rounded transition-colors"
+ title="Delete"
+ >
+
+
+
+
+
+
+ {/* Error message row */}
+ {item.status === 'failed' && item.error_message && (
+
+
+
+
{item.error_message}
+
+
+ )}
+
+ ))}
+
+
+ )}
+
+ {/* View Results Modal */}
+ {showResultsModal && selectedResult && (
+
+
+
+
+
Extraction Results
+
+ {selectedResult.document_filename} • {selectedResult.template_name}
+
+
+ {getStatusBadge(selectedResult.status)}
+ {selectedResult.stage_used && getStageBadge(selectedResult.stage_used)}
+ {selectedResult.field_coverage_percent !== null && (
+
+ Coverage: {(selectedResult.field_coverage_percent * 100).toFixed(0)}%
+
+ )}
+ {selectedResult.processing_time_ms && (
+
+ Time: {selectedResult.processing_time_ms}ms
+
+ )}
+
+
+
setShowResultsModal(false)}
+ className="text-gray-400 hover:text-gray-600"
+ >
+
+
+
+
+
+ {Object.entries(selectedResult.extracted_data || {}).map(([field, value]) => (
+
+
{field}
+
+ {value !== null && value !== undefined && value !== '' ? (
+ Array.isArray(value) ? (
+
+ {value.map((item, idx) => (
+
+ •
+
+ {typeof item === 'object' && item !== null ? (
+
+ {Object.entries(item).map(([key, val]) => (
+
+
+ {key.replace(/_/g, ' ')}:
+
+
+ {val !== null && val !== undefined ? String(val) : '-'}
+
+
+ ))}
+
+ ) : (
+
{String(item)}
+ )}
+
+
+ ))}
+
+ ) : (
+
{String(value)}
+ )
+ ) : (
+
+
+ Not found
+
+ )}
+
+
+ ))}
+
+
+
+
+ )}
+
+ )
+}
+
+export default HistoryPage
diff --git a/sample_solutions/DocQvision/ui/src/pages/ResultsPage.jsx b/sample_solutions/DocQvision/ui/src/pages/ResultsPage.jsx
new file mode 100644
index 00000000..1f46106f
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/pages/ResultsPage.jsx
@@ -0,0 +1,141 @@
+import { useLocation, useNavigate } from 'react-router-dom'
+import { Download, FileJson, FileSpreadsheet, Upload } from 'lucide-react'
+
+const ResultsPage = () => {
+ const location = useLocation()
+ const navigate = useNavigate()
+ const result = location.state?.result
+
+ if (!result) {
+ return (
+
+
No extraction results available
+
navigate('/upload')} className="btn-primary">
+ Upload Document
+
+
+ )
+ }
+
+ const { extracted_data, processing_time_ms } = result
+
+ const handleExportJSON = () => {
+ const dataStr = JSON.stringify(extracted_data, null, 2)
+ const dataBlob = new Blob([dataStr], { type: 'application/json' })
+ const url = URL.createObjectURL(dataBlob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = 'extracted_data.json'
+ link.click()
+ }
+
+ const handleExportCSV = () => {
+ const headers = Object.keys(extracted_data).filter(
+ (key) => typeof extracted_data[key] !== 'object'
+ )
+ const values = headers.map((key) => extracted_data[key])
+
+ const csvContent = [
+ headers.join(','),
+ values.map((v) => `"${v}"`).join(','),
+ ].join('\n')
+
+ const dataBlob = new Blob([csvContent], { type: 'text/csv' })
+ const url = URL.createObjectURL(dataBlob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = 'extracted_data.csv'
+ link.click()
+ }
+
+ const renderValue = (value) => {
+ if (Array.isArray(value)) {
+ return (
+
+ {value.map((item, idx) => (
+
+ {JSON.stringify(item, null, 2)}
+
+ ))}
+
+ )
+ }
+ if (typeof value === 'object') {
+ return {JSON.stringify(value, null, 2)}
+ }
+ return value
+ }
+
+ return (
+
+
+
+
Extraction Complete
+
+ Processed in {processing_time_ms}ms
+
+
+
+
+
+
+ Extracted Data
+
+
+
+
+
+
+ Field Name
+
+
+ Extracted Value
+
+
+
+
+ {Object.entries(extracted_data).map(([field, value]) => (
+
+
+ {field}
+
+
+ {renderValue(value)}
+
+
+ ))}
+
+
+
+
+
+
+
navigate('/upload')}
+ className="btn-secondary flex items-center gap-2"
+ >
+
+ New Document
+
+
+
+
+ Export JSON
+
+
+
+ Export CSV
+
+
+
+
+ )
+}
+
+export default ResultsPage
diff --git a/sample_solutions/DocQvision/ui/src/pages/UploadPage.jsx b/sample_solutions/DocQvision/ui/src/pages/UploadPage.jsx
new file mode 100644
index 00000000..d6cc1653
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/pages/UploadPage.jsx
@@ -0,0 +1,587 @@
+import { useState, useEffect } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { Upload as UploadIcon, XCircle, AlertCircle, CheckCircle, Loader, FileText, Eye, Download, FileJson, FileSpreadsheet } from 'lucide-react'
+import FileUpload from '../components/FileUpload'
+import ExtractionStatus from '../components/ExtractionStatus'
+import ErrorMessage from '../components/ErrorMessage'
+import { uploadDocument, batchUploadDocuments, extractData, getTemplates } from '../services/api'
+
+const UploadPage = () => {
+ const navigate = useNavigate()
+ const [files, setFiles] = useState([])
+ const [templates, setTemplates] = useState([])
+ const [selectedTemplate, setSelectedTemplate] = useState('')
+ const [isLoading, setIsLoading] = useState(false)
+ const [batchJobs, setBatchJobs] = useState([])
+ const [errorMessage, setErrorMessage] = useState(null)
+ const [error, setError] = useState(null)
+ const [viewModalResult, setViewModalResult] = useState(null)
+
+ useEffect(() => {
+ loadTemplates()
+ }, [])
+
+ const loadTemplates = async () => {
+ try {
+ const response = await getTemplates()
+ console.log('Templates response:', response)
+
+ // Handle both array and object responses
+ const templateList = Array.isArray(response) ? response : (response.templates || [])
+
+ setTemplates(templateList)
+ if (templateList.length > 0) {
+ setSelectedTemplate(templateList[0].id)
+ }
+ } catch (error) {
+ console.error('Error loading templates:', error)
+ setError('Failed to load templates. Please try refreshing the page.')
+ }
+ }
+
+ const handleFileSelect = (selectedFiles) => {
+ setError(null)
+ setFiles(Array.isArray(selectedFiles) ? selectedFiles : [selectedFiles])
+ }
+
+ const handleUploadAndExtract = async () => {
+ if (!files || files.length === 0) {
+ setError('Please select at least one file')
+ return
+ }
+ if (!selectedTemplate) {
+ setError('Please select a document type')
+ return
+ }
+
+ setIsLoading(true)
+ setErrorMessage(null)
+ setBatchJobs([])
+
+ try {
+ // Single file - use single upload
+ if (files.length === 1) {
+ const uploadResponse = await uploadDocument(files[0])
+ const extractResponse = await extractData(uploadResponse.document_id, selectedTemplate)
+
+ // Track as single job
+ setBatchJobs([{
+ filename: files[0].name,
+ success: true,
+ documentId: uploadResponse.document_id,
+ error: null,
+ jobId: extractResponse.id,
+ status: 'processing',
+ extractionResult: null
+ }])
+ } else {
+ // Multiple files - use batch upload
+ const uploadResponse = await batchUploadDocuments(files)
+
+ // Initialize batch jobs tracking
+ const jobs = uploadResponse.results.map(result => ({
+ filename: result.filename,
+ success: result.success,
+ documentId: result.document_id,
+ error: result.error,
+ jobId: null,
+ status: result.success ? 'pending' : 'failed',
+ extractionResult: null
+ }))
+
+ setBatchJobs(jobs)
+
+ // Start extraction for successful uploads
+ const successfulUploads = jobs.filter(job => job.success)
+
+ for (let i = 0; i < successfulUploads.length; i++) {
+ const job = successfulUploads[i]
+ try {
+ const extractResponse = await extractData(job.documentId, selectedTemplate)
+
+ // Update job with extraction ID
+ setBatchJobs(prev => prev.map(j =>
+ j.filename === job.filename
+ ? { ...j, jobId: extractResponse.id, status: 'processing' }
+ : j
+ ))
+ } catch (error) {
+ console.error(`Error starting extraction for ${job.filename}:`, error)
+ setBatchJobs(prev => prev.map(j =>
+ j.filename === job.filename
+ ? { ...j, status: 'failed', error: error.message }
+ : j
+ ))
+ }
+ }
+ }
+ } catch (error) {
+ console.error('Error processing documents:', error)
+ setErrorMessage(error.response?.data?.detail || error.message || 'Failed to process documents')
+ setIsLoading(false)
+ }
+ }
+
+ const handleBatchJobComplete = (jobId, result) => {
+ setBatchJobs(prev => prev.map(j =>
+ j.jobId === jobId
+ ? { ...j, status: 'completed', extractionResult: result }
+ : j
+ ))
+
+ // Check if all jobs are complete
+ const updatedJobs = batchJobs.map(j =>
+ j.jobId === jobId ? { ...j, status: 'completed' } : j
+ )
+
+ const allComplete = updatedJobs.every(j =>
+ j.status === 'completed' || j.status === 'failed'
+ )
+
+ if (allComplete) {
+ setIsLoading(false)
+ }
+ }
+
+ const handleBatchJobError = (jobId, error) => {
+ setBatchJobs(prev => prev.map(j =>
+ j.jobId === jobId
+ ? { ...j, status: 'failed', error }
+ : j
+ ))
+ }
+
+ const handleViewResults = (job) => {
+ console.log('handleViewResults called with job:', job)
+ if (job && job.extractionResult) {
+ console.log('Setting viewModalResult:', job.extractionResult)
+ setViewModalResult(job.extractionResult)
+ } else {
+ console.error('No extraction result found in job')
+ setError('Unable to view results - extraction data not available')
+ }
+ }
+
+ const handleDownloadJSON = (job) => {
+ if (!job.extractionResult) return
+
+ const dataStr = JSON.stringify(job.extractionResult.extracted_data, null, 2)
+ const dataBlob = new Blob([dataStr], { type: 'application/json' })
+ const url = URL.createObjectURL(dataBlob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `${job.filename.replace('.pdf', '')}_extraction.json`
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ URL.revokeObjectURL(url)
+ }
+
+ const handleDownloadCSV = (job) => {
+ if (!job.extractionResult || !job.extractionResult.extracted_data) return
+
+ const data = job.extractionResult.extracted_data
+ const headers = Object.keys(data)
+ const values = Object.values(data).map(v => {
+ if (v === null || v === undefined) return ''
+ if (Array.isArray(v) || typeof v === 'object') {
+ return JSON.stringify(v)
+ }
+ return String(v)
+ })
+
+ let csvContent = headers.join(',') + '\n'
+ csvContent += values.map(v => `"${String(v).replace(/"/g, '""')}"`).join(',')
+
+ const dataBlob = new Blob([csvContent], { type: 'text/csv' })
+ const url = URL.createObjectURL(dataBlob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `${job.filename.replace('.pdf', '')}_extraction.csv`
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ URL.revokeObjectURL(url)
+ }
+
+ const handleBatchExportJSON = () => {
+ const completedJobs = batchJobs.filter(j => j.status === 'completed' && j.extractionResult)
+
+ if (completedJobs.length === 0) {
+ setError('No completed extractions to export')
+ return
+ }
+
+ const batchData = completedJobs.map(job => ({
+ filename: job.filename,
+ extracted_data: job.extractionResult.extracted_data,
+ metadata: {
+ coverage: job.extractionResult.field_coverage_percent,
+ stage_used: job.extractionResult.stage_used,
+ processing_time_ms: job.extractionResult.processing_time_ms
+ }
+ }))
+
+ const dataStr = JSON.stringify(batchData, null, 2)
+ const dataBlob = new Blob([dataStr], { type: 'application/json' })
+ const url = URL.createObjectURL(dataBlob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `batch_extraction_${Date.now()}.json`
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ URL.revokeObjectURL(url)
+ }
+
+ const handleBatchExportCSV = () => {
+ const completedJobs = batchJobs.filter(j => j.status === 'completed' && j.extractionResult)
+
+ if (completedJobs.length === 0) {
+ setError('No completed extractions to export')
+ return
+ }
+
+ // Get all unique field names across all extractions
+ const allFields = new Set()
+ completedJobs.forEach(job => {
+ Object.keys(job.extractionResult.extracted_data).forEach(field => allFields.add(field))
+ })
+
+ const fieldNames = ['filename', ...Array.from(allFields)]
+
+ // Build CSV
+ let csvContent = fieldNames.join(',') + '\n'
+
+ completedJobs.forEach(job => {
+ const data = job.extractionResult.extracted_data
+ const row = [
+ `"${job.filename}"`,
+ ...Array.from(allFields).map(field => {
+ const value = data[field]
+ if (value === undefined || value === null) return '""'
+
+ // Serialize arrays/objects as JSON
+ if (Array.isArray(value) || typeof value === 'object') {
+ return `"${JSON.stringify(value).replace(/"/g, '""')}"`
+ }
+
+ return `"${String(value).replace(/"/g, '""')}"`
+ })
+ ]
+ csvContent += row.join(',') + '\n'
+ })
+
+ const dataBlob = new Blob([csvContent], { type: 'text/csv' })
+ const url = URL.createObjectURL(dataBlob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `batch_extraction_${Date.now()}.csv`
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ URL.revokeObjectURL(url)
+ }
+
+ return (
+
+
+
+ Extract Data from Documents
+
+
+ Upload up to 5 documents at once to extract structured data
+
+
+
+ {/* Error Messages */}
+
setError(null)} />
+
+
+
+
+
+
+ Select Document Type:
+
+ {
+ setError(null)
+ setSelectedTemplate(e.target.value)
+ }}
+ className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
+ disabled={isLoading}
+ >
+ {templates.length === 0 ? (
+ No templates available - Create one in Configure tab
+ ) : (
+ templates.map((template) => (
+
+ {template.name}
+
+ ))
+ )}
+
+
+
+
+
+
+
+ Document Requirements:
+
+
+ • PDF only
+ • Maximum file size: 10MB per file
+ • Upload 1-5 documents at once
+
+
+
+
+
+ {isLoading ? 'Processing...' : `Upload & Extract${files.length > 0 ? ` (${files.length})` : ''}`}
+
+
+ {errorMessage && (
+
+
+
+
+
Extraction Failed
+
+ {errorMessage.split('\n\n').map((section, idx) => {
+ const lines = section.split('\n').filter(line => line.trim())
+ if (lines.length === 0) return null
+
+ const header = lines[0]
+ const items = lines.slice(1)
+
+ return (
+
+
{header}
+ {items.length > 0 && (
+
+ {items.map((item, i) => (
+ {item}
+ ))}
+
+ )}
+
+ )
+ })}
+
+
+
setErrorMessage(null)}
+ className="text-red-400 hover:text-red-600 flex-shrink-0"
+ >
+
+
+
+
+ )}
+
+ {batchJobs.length > 0 && (
+
+
+
+ Processing Documents ({batchJobs.filter(j => j.status === 'completed').length}/{batchJobs.length} completed)
+
+ {batchJobs.filter(j => j.status === 'completed').length > 1 && (
+
+
+
+ Export All (JSON)
+
+
+
+ Export All (CSV)
+
+
+ )}
+
+
+ {batchJobs.map((job, index) => (
+
+
+
+
+ {job.filename}
+
+
+ {job.status === 'completed' && (
+
+ )}
+ {job.status === 'processing' && (
+
+ )}
+ {job.status === 'pending' && (
+
+ )}
+ {job.status === 'failed' && (
+
+ )}
+
+
+
+ {job.status === 'processing' && job.jobId && (
+
+ handleBatchJobComplete(job.jobId, result)}
+ onError={(error) => handleBatchJobError(job.jobId, error)}
+ compact={true}
+ />
+
+ )}
+
+ {job.status === 'failed' && job.error && (
+
+ Error: {job.error}
+
+ )}
+
+ {job.status === 'completed' && job.extractionResult && (
+
+
+
+ Coverage: {job.extractionResult.field_coverage_percent ? (job.extractionResult.field_coverage_percent * 100).toFixed(0) : 'N/A'}% •
+ Stage: {job.extractionResult.stage_used || 'N/A'}
+
+
+ handleViewResults(job)}
+ className="flex items-center gap-1 px-3 py-1.5 text-sm text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
+ title="View Results"
+ >
+
+ View
+
+ handleDownloadJSON(job)}
+ className="flex items-center gap-1 px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
+ title="Download JSON"
+ >
+
+ JSON
+
+ handleDownloadCSV(job)}
+ className="flex items-center gap-1 px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
+ title="Download CSV"
+ >
+
+ CSV
+
+
+
+
+ )}
+
+ ))}
+
+
+ )}
+
+
+
+
+ {/* View Results Modal */}
+ {viewModalResult && (
+
+
+
+
+
Extraction Results
+
+ Coverage: {viewModalResult.field_coverage_percent ? (viewModalResult.field_coverage_percent * 100).toFixed(0) : 'N/A'}% •
+ Stage: {viewModalResult.stage_used || 'N/A'} •
+ Time: {viewModalResult.processing_time_ms || 'N/A'}ms
+
+
+
setViewModalResult(null)}
+ className="text-gray-400 hover:text-gray-600 transition-colors"
+ title="Close"
+ >
+
+
+
+
+ {viewModalResult.extracted_data && Object.keys(viewModalResult.extracted_data).length > 0 ? (
+
+ {Object.entries(viewModalResult.extracted_data).map(([field, value]) => (
+
+
{field}
+
+ {value !== null && value !== undefined && value !== '' ? (
+ Array.isArray(value) ? (
+
+ {value.map((item, idx) => (
+
+ •
+
+ {typeof item === 'object' && item !== null ? (
+
+ {Object.entries(item).map(([key, val]) => (
+
+
+ {key.replace(/_/g, ' ')}:
+
+
+ {val !== null && val !== undefined ? String(val) : '-'}
+
+
+ ))}
+
+ ) : (
+
{String(item)}
+ )}
+
+
+ ))}
+
+ ) : (
+
{String(value)}
+ )
+ ) : (
+
+
+ Not found
+
+ )}
+
+
+ ))}
+
+ ) : (
+
+
No extraction data available
+
+ )}
+
+
+
+ )}
+
+ )
+}
+
+export default UploadPage
diff --git a/sample_solutions/DocQvision/ui/src/services/api.js b/sample_solutions/DocQvision/ui/src/services/api.js
new file mode 100644
index 00000000..b6f7ce9f
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/src/services/api.js
@@ -0,0 +1,117 @@
+import axios from 'axios'
+
+// In production (Docker), nginx proxies /api to backend, so use relative path
+// In development (npm run dev), directly call backend on port 5001
+const API_BASE_URL = import.meta.env.VITE_API_URL ||
+ (import.meta.env.DEV ? 'http://localhost:5001' : '')
+
+const api = axios.create({
+ baseURL: API_BASE_URL,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+})
+
+export const configureSchema = async (message, sessionId = null) => {
+ const formData = new FormData()
+ formData.append('message', message)
+ if (sessionId) {
+ formData.append('session_id', sessionId)
+ }
+ const response = await api.post('/api/configure', formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ })
+ return response.data
+}
+
+export const uploadDocument = async (file) => {
+ const formData = new FormData()
+ formData.append('file', file)
+ const response = await api.post('/api/documents/upload', formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ })
+ return response.data
+}
+
+export const batchUploadDocuments = async (files) => {
+ const formData = new FormData()
+ files.forEach((file) => {
+ formData.append('files', file)
+ })
+ const response = await api.post('/api/documents/batch-upload', formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ })
+ return response.data
+}
+
+export const extractData = async (documentId, templateId) => {
+ const response = await api.post('/api/extract', {
+ document_id: documentId,
+ template_id: templateId
+ })
+ return response.data
+}
+
+export const getExtractionResult = async (jobId) => {
+ const response = await api.get(`/api/extract/${jobId}`)
+ return response.data
+}
+
+export const saveTemplate = async (name, templateType, schema) => {
+ const formData = new FormData()
+ formData.append('name', name)
+ formData.append('template_type', templateType)
+ formData.append('schema', JSON.stringify(schema))
+ const response = await api.post('/api/templates/save', formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ })
+ return response.data
+}
+
+export const createTemplate = async (templateData) => {
+ const response = await api.post('/api/templates', templateData)
+ return response.data
+}
+
+export const getTemplate = async (templateId) => {
+ const response = await api.get(`/api/templates/${templateId}`)
+ return response.data
+}
+
+export const updateTemplate = async (templateId, updateData) => {
+ const response = await api.put(`/api/templates/${templateId}`, updateData)
+ return response.data
+}
+
+export const deleteTemplate = async (templateId) => {
+ const response = await api.delete(`/api/templates/${templateId}`)
+ return response.data
+}
+
+export const getExtractionHistory = async (filters = {}) => {
+ const params = new URLSearchParams()
+ if (filters.template_id) params.append('template_id', filters.template_id)
+ if (filters.status) params.append('status', filters.status)
+ if (filters.skip) params.append('skip', filters.skip)
+ if (filters.limit) params.append('limit', filters.limit)
+
+ const response = await api.get(`/api/history?${params.toString()}`)
+ return response.data
+}
+
+export const getTemplates = async () => {
+ const response = await api.get('/api/templates')
+ return response.data
+}
+
+export const deleteExtraction = async (jobId) => {
+ const response = await api.delete(`/api/extract/${jobId}`)
+ return response.data
+}
+
+export const reExtract = async (jobId) => {
+ const response = await api.post(`/api/extract/${jobId}/re-extract`)
+ return response.data
+}
+
+export default api
diff --git a/sample_solutions/DocQvision/ui/tailwind.config.js b/sample_solutions/DocQvision/ui/tailwind.config.js
new file mode 100644
index 00000000..e0f5aa6a
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/tailwind.config.js
@@ -0,0 +1,37 @@
+export default {
+ content: [
+ "./index.html",
+ "./src/**/*.{js,ts,jsx,tsx}",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ primary: {
+ 50: '#f0f9ff',
+ 100: '#e0f2fe',
+ 200: '#bae6fd',
+ 300: '#7dd3fc',
+ 400: '#38bdf8',
+ 500: '#0ea5e9',
+ 600: '#0284c7',
+ 700: '#0369a1',
+ 800: '#075985',
+ 900: '#0c4a6e',
+ },
+ secondary: {
+ 50: '#fdf4ff',
+ 100: '#fae8ff',
+ 200: '#f5d0fe',
+ 300: '#f0abfc',
+ 400: '#e879f9',
+ 500: '#d946ef',
+ 600: '#c026d3',
+ 700: '#a21caf',
+ 800: '#86198f',
+ 900: '#701a75',
+ },
+ },
+ },
+ },
+ plugins: [],
+}
diff --git a/sample_solutions/DocQvision/ui/vite.config.js b/sample_solutions/DocQvision/ui/vite.config.js
new file mode 100644
index 00000000..80d84674
--- /dev/null
+++ b/sample_solutions/DocQvision/ui/vite.config.js
@@ -0,0 +1,10 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 3000,
+ host: true
+ }
+})