diff --git a/apps/interactive-journal/.gitignore b/apps/interactive-journal/.gitignore
new file mode 100644
index 0000000..6e4100c
--- /dev/null
+++ b/apps/interactive-journal/.gitignore
@@ -0,0 +1,42 @@
+# Environment files
+.env
+.env.local
+.env.*.local
+
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+venv/
+.venv/
+env/
+.eggs/
+*.egg-info/
+.pytest_cache/
+
+# Node.js
+node_modules/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Build outputs
+dist/
+build/
+*.local
+
+# IDE
+.idea/
+.vscode/
+*.swp
+*.swo
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+logs/
diff --git a/apps/interactive-journal/README.md b/apps/interactive-journal/README.md
new file mode 100644
index 0000000..83f3fd0
--- /dev/null
+++ b/apps/interactive-journal/README.md
@@ -0,0 +1,40 @@
+## Setup
+
+### Backend
+
+```bash
+cd backend
+
+# Create virtual environment
+python -m venv venv
+source venv/bin/activate
+
+# Install dependencies
+pip install -r requirements.txt
+
+# Create .env file
+cp .env.example .env
+# Edit .env with your values:
+# MONGODB_URI=your-mongodb-connection-string
+# ANTHROPIC_API_KEY=your-anthropic-api-key
+# VOYAGE_API_KEY=your-voyage-api-key
+
+# Run the server
+uvicorn app.main:app --reload
+```
+
+Backend runs at http://localhost:8000
+
+### Frontend
+
+```bash
+cd frontend
+
+# Install dependencies
+npm install
+
+# Run the dev server
+npm run dev
+```
+
+Frontend runs at http://localhost:5173
diff --git a/apps/interactive-journal/backend/.env.example b/apps/interactive-journal/backend/.env.example
new file mode 100644
index 0000000..f210bd9
--- /dev/null
+++ b/apps/interactive-journal/backend/.env.example
@@ -0,0 +1,14 @@
+# MongoDB Atlas connection string
+MONGODB_URI=your-mongodb-connection-string-here
+
+# Database name
+DATABASE_NAME=memoir
+
+# Anthropic API key
+ANTHROPIC_API_KEY=your-anthropic-api-key-here
+
+# Anthropic model to use
+ANTHROPIC_MODEL=claude-sonnet-4-5
+
+# Voyage AI API key (for V2 semantic search)
+VOYAGE_API_KEY=your-voyage-api-key-here
diff --git a/apps/interactive-journal/backend/app/__init__.py b/apps/interactive-journal/backend/app/__init__.py
new file mode 100644
index 0000000..3db73c2
--- /dev/null
+++ b/apps/interactive-journal/backend/app/__init__.py
@@ -0,0 +1 @@
+# Memoir - Interactive Journaling App
diff --git a/apps/interactive-journal/backend/app/config.py b/apps/interactive-journal/backend/app/config.py
new file mode 100644
index 0000000..72c40ab
--- /dev/null
+++ b/apps/interactive-journal/backend/app/config.py
@@ -0,0 +1,25 @@
+import os
+
+# User config
+USER_ID = "Apoorva"
+
+# MongoDB config
+MONGODB_URI = os.getenv("MONGODB_URI", "mongodb://localhost:27017")
+DATABASE_NAME = os.getenv("DATABASE_NAME", "memoir")
+
+# Anthropic config
+ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
+ANTHROPIC_MODEL = os.getenv("ANTHROPIC_MODEL", "claude-sonnet-4-5")
+
+# Voyage AI config
+VOYAGE_API_KEY = os.getenv("VOYAGE_API_KEY")
+VOYAGE_MULTIMODAL_MODEL = "voyage-multimodal-3.5"
+VOYAGE_TEXT_MODEL = "voyage-3-large"
+
+# Vector search config
+VECTOR_INDEX_NAME = "vector_index"
+VECTOR_DIMENSIONS = 1024
+VECTOR_NUM_CANDIDATES = 100
+
+# Image config
+IMAGE_SIZE = (1024, 1024)
diff --git a/apps/interactive-journal/backend/app/main.py b/apps/interactive-journal/backend/app/main.py
new file mode 100644
index 0000000..ddd17b5
--- /dev/null
+++ b/apps/interactive-journal/backend/app/main.py
@@ -0,0 +1,59 @@
+from dotenv import load_dotenv
+
+load_dotenv(override=True)
+
+import logging
+from contextlib import asynccontextmanager
+
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+
+from app.routers import routes
+from app.services.mongodb import close_db, connect_db
+
+logging.basicConfig(
+ level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+)
+logger = logging.getLogger(__name__)
+
+
+@asynccontextmanager
+async def lifespan(app: FastAPI):
+ # Startup
+ logger.info("Starting Memoir API...")
+ connect_db()
+ logger.info("Memoir API started successfully")
+ yield
+ # Shutdown
+ logger.info("Shutting down Memoir API...")
+ close_db()
+
+
+app = FastAPI(
+ title="Memoir",
+ description="AI-powered interactive journaling application",
+ version="1.0.0",
+ lifespan=lifespan,
+)
+
+# CORS middleware for frontend
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["http://localhost:5173"], # Vite default port
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+# Include routers
+app.include_router(routes.router, prefix="/api/entries", tags=["entries"])
+
+
+@app.get("/")
+def root():
+ return {"message": "Welcome to Memoir API"}
+
+
+@app.get("/health")
+def health_check():
+ return {"status": "healthy"}
diff --git a/apps/interactive-journal/backend/app/routers/__init__.py b/apps/interactive-journal/backend/app/routers/__init__.py
new file mode 100644
index 0000000..22da696
--- /dev/null
+++ b/apps/interactive-journal/backend/app/routers/__init__.py
@@ -0,0 +1 @@
+# Routers module
diff --git a/apps/interactive-journal/backend/app/routers/helpers.py b/apps/interactive-journal/backend/app/routers/helpers.py
new file mode 100644
index 0000000..f4acd06
--- /dev/null
+++ b/apps/interactive-journal/backend/app/routers/helpers.py
@@ -0,0 +1,229 @@
+import base64
+import logging
+import uuid
+from datetime import datetime, timedelta
+from io import BytesIO
+from pathlib import Path
+
+from fastapi import UploadFile
+from PIL import Image
+
+from app.config import IMAGE_SIZE, USER_ID, VECTOR_INDEX_NAME, VECTOR_NUM_CANDIDATES
+from app.services.anthropic import extract_memories
+from app.services.voyage import get_multimodal_embedding, get_text_embedding
+
+logger = logging.getLogger(__name__)
+
+# Store images in frontend's public folder for direct access
+UPLOADS_DIR = (
+ Path(__file__).parent.parent.parent.parent / "frontend" / "public" / "uploads"
+)
+UPLOADS_DIR.mkdir(parents=True, exist_ok=True)
+
+
+def save_user_message(
+ db, entry_id: str, content: str | Path, version: int, msg_date: datetime
+) -> None:
+ """Save a user message (text or image) with its embedding."""
+ message = {
+ "entry_id": entry_id,
+ "user_id": USER_ID,
+ "role": "user",
+ "version": version,
+ "created_at": msg_date,
+ }
+
+ if version == 2:
+ is_image = isinstance(content, Path)
+ mode = "image" if is_image else "text"
+ message["embedding"] = get_multimodal_embedding(
+ content, mode=mode, input_type="document"
+ )
+ if is_image:
+ message["image"] = content.name
+ else:
+ message["content"] = content
+ else:
+ message["embedding"] = get_text_embedding(content, input_type="document")
+ message["content"] = content
+
+ db.messages.insert_one(message)
+ logger.info(f"Saved message for entry {entry_id}")
+
+
+def extract_and_save_memories(
+ db, entry_id: str, conversation: list[dict], entry_date: datetime
+) -> None:
+ """Extract memories from conversation and save them."""
+ context = "\n".join(f"{msg['role']}: {msg['content']}" for msg in conversation)
+ memories = extract_memories(context)
+
+ if memories:
+ memory_docs = [
+ {
+ "user_id": USER_ID,
+ "entry_id": entry_id,
+ "content": memory_content,
+ "embedding": get_text_embedding(memory_content, input_type="document"),
+ "created_at": entry_date,
+ }
+ for memory_content in memories
+ ]
+ db.memories.insert_many(memory_docs)
+ logger.info(f"Extracted and saved {len(memories)} memories: {memories}")
+
+
+def retrieve_relevant_memories(db, query: str) -> list[str]:
+ """Retrieve relevant memories via vector search."""
+ query_embedding = get_text_embedding(query, input_type="query")
+ pipeline = [
+ {
+ "$vectorSearch": {
+ "index": VECTOR_INDEX_NAME,
+ "path": "embedding",
+ "queryVector": query_embedding,
+ "numCandidates": VECTOR_NUM_CANDIDATES,
+ "limit": 10,
+ "filter": {"user_id": USER_ID},
+ }
+ },
+ {"$project": {"content": 1, "score": {"$meta": "vectorSearchScore"}}},
+ ]
+ results = list(db.memories.aggregate(pipeline))
+ memories = [r["content"] for r in results]
+ logger.info(f"Retrieved {len(memories)} memories for context")
+ return memories
+
+
+def get_conversation_history(
+ db, entry_id: str, include_images: bool = True
+) -> list[dict]:
+ """Get conversation history for an entry."""
+ history = list(
+ db.messages.find(
+ {"entry_id": entry_id}, {"role": 1, "content": 1, "image": 1, "_id": 0}
+ ).sort("created_at", 1)
+ )
+
+ messages = []
+ for msg in history:
+ if msg.get("content"):
+ messages.append({"role": msg["role"], "content": msg["content"]})
+ elif msg.get("image") and include_images:
+ image_path = UPLOADS_DIR / msg["image"]
+ if image_path.exists():
+ messages.append(
+ {
+ "role": msg["role"],
+ "content": [image_to_base64(image_path)],
+ }
+ )
+ return messages
+
+
+def image_to_base64(image_path: Path) -> dict:
+ """Convert an image file to Claude's base64 format, resizing to fit limits."""
+ with Image.open(image_path) as img:
+ img = img.resize(IMAGE_SIZE, Image.Resampling.LANCZOS)
+ buffer = BytesIO()
+ img.save(buffer, format="JPEG", quality=85)
+ data = base64.standard_b64encode(buffer.getvalue()).decode("utf-8")
+
+ return {
+ "type": "image",
+ "source": {"type": "base64", "media_type": "image/jpeg", "data": data},
+ }
+
+
+def save_assistant_message(db, entry_id: str, content: str, msg_date: datetime) -> None:
+ """Save an assistant response message."""
+ db.messages.insert_one(
+ {
+ "entry_id": entry_id,
+ "role": "assistant",
+ "content": content,
+ "created_at": msg_date,
+ }
+ )
+ logger.info(f"Saved AI response for entry {entry_id}")
+
+
+def save_image_file(image_file: UploadFile) -> Path:
+ """Save uploaded image file."""
+ filename = f"{uuid.uuid4()}{Path(image_file.filename).suffix or '.jpg'}"
+ image_path = UPLOADS_DIR / filename
+ with open(image_path, "wb") as f:
+ f.write(image_file.file.read())
+ return image_path
+
+
+def get_monthly_filter(user_id: str) -> dict:
+ """Get common filter for monthly v2 entries."""
+ thirty_days_ago = datetime.now() - timedelta(days=30)
+ return {
+ "user_id": user_id,
+ "version": 2,
+ "created_at": {"$gte": thirty_days_ago},
+ }
+
+
+def get_total_entries(db, user_id: str) -> int:
+ """Get total entries count for past 30 days."""
+ return db.entries.count_documents(get_monthly_filter(user_id))
+
+
+def get_longest_streak(db, user_id: str) -> int:
+ """Get longest consecutive days streak in past 30 days."""
+ pipeline = [
+ {"$match": get_monthly_filter(user_id)},
+ {"$project": {"date": {"$dateTrunc": {"date": "$created_at", "unit": "day"}}}},
+ {"$group": {"_id": "$date"}},
+ {"$sort": {"_id": 1}},
+ ]
+ dates = [doc["_id"] for doc in db.entries.aggregate(pipeline)]
+
+ if not dates:
+ return 0
+
+ longest = current = 1
+ for i in range(1, len(dates)):
+ if (dates[i] - dates[i - 1]).days == 1:
+ current += 1
+ longest = max(longest, current)
+ else:
+ current = 1
+
+ return longest
+
+
+def get_mood_distribution(db, user_id: str) -> dict:
+ """Get sentiment distribution for past 30 days."""
+ filter = get_monthly_filter(user_id)
+ filter["sentiment"] = {"$exists": True}
+ pipeline = [
+ {"$match": filter},
+ {"$group": {"_id": "$sentiment", "count": {"$sum": 1}}},
+ ]
+ results = list(db.entries.aggregate(pipeline))
+ counts = {r["_id"]: r["count"] for r in results}
+ total = sum(counts.values()) or 1
+ return {
+ "positive": round(counts.get("positive", 0) / total * 100),
+ "neutral": round(counts.get("neutral", 0) / total * 100),
+ "mixed": round(counts.get("mixed", 0) / total * 100),
+ "negative": round(counts.get("negative", 0) / total * 100),
+ }
+
+
+def get_themes(db, user_id: str) -> list[dict]:
+ """Get all themes with counts for past 30 days."""
+ filter = get_monthly_filter(user_id)
+ filter["themes"] = {"$exists": True}
+ pipeline = [
+ {"$match": filter},
+ {"$unwind": "$themes"},
+ {"$group": {"_id": "$themes", "count": {"$sum": 1}}},
+ {"$sort": {"count": -1}},
+ ]
+ results = list(db.entries.aggregate(pipeline))
+ return [{"theme": r["_id"], "count": r["count"]} for r in results]
diff --git a/apps/interactive-journal/backend/app/routers/routes.py b/apps/interactive-journal/backend/app/routers/routes.py
new file mode 100644
index 0000000..9aaa427
--- /dev/null
+++ b/apps/interactive-journal/backend/app/routers/routes.py
@@ -0,0 +1,249 @@
+import logging
+from datetime import datetime, timedelta
+from typing import Optional
+
+from bson import ObjectId
+from fastapi import APIRouter, File, Form, UploadFile
+from fastapi.responses import StreamingResponse
+
+from app.config import USER_ID, VECTOR_INDEX_NAME, VECTOR_NUM_CANDIDATES
+from app.routers.helpers import (
+ extract_and_save_memories,
+ get_conversation_history,
+ get_longest_streak,
+ get_mood_distribution,
+ get_themes,
+ get_total_entries,
+ image_to_base64,
+ retrieve_relevant_memories,
+ save_assistant_message,
+ save_image_file,
+ save_user_message,
+)
+from app.services.anthropic import (
+ analyze_entry,
+ generate_journal_prompt,
+ generate_response,
+)
+from app.services.mongodb import get_database
+from app.services.voyage import get_multimodal_embedding, get_text_embedding
+
+logger = logging.getLogger(__name__)
+
+router = APIRouter()
+
+
+@router.post("/")
+def create_entry(version: int = Form(1), entry_date: str = Form(...)):
+ db = get_database()
+ entry_dt = datetime.fromisoformat(entry_date)
+ entry_data = {
+ "user_id": USER_ID,
+ "title": entry_dt.strftime("%d/%m/%Y"),
+ "version": version,
+ "created_at": entry_dt,
+ }
+ result = db.entries.insert_one(entry_data)
+ logger.info(f"Created entry {result.inserted_id} for user {USER_ID}")
+ return {"_id": str(result.inserted_id)}
+
+
+@router.post("/{entry_id}/messages")
+def send_message(
+ entry_id: str,
+ content: Optional[str] = Form(None),
+ images: list[UploadFile] = File([]),
+ version: int = Form(1),
+ entry_date: Optional[str] = Form(None),
+):
+ db = get_database()
+ is_v2 = version == 2
+ msg_date = datetime.fromisoformat(entry_date)
+
+ # Save image files to disk before streaming (file handles close after)
+ image_paths = [save_image_file(image) for image in images]
+
+ # Build current message (text, images, or both)
+ messages = []
+ if content:
+ messages.append({"type": "text", "text": content})
+ for path in image_paths:
+ messages.append(image_to_base64(path))
+
+ # Get conversation history and add current message
+ conversation = get_conversation_history(db, entry_id)
+ if messages:
+ conversation.append({"role": "user", "content": messages})
+
+ # Retrieve relevant memories for context (V2 only)
+ memories = retrieve_relevant_memories(db, content) if is_v2 and content else []
+
+ def respond_and_save():
+ # Stream response to user
+ response_text = []
+ for chunk in generate_response(conversation, memories=memories):
+ response_text.append(chunk)
+ yield chunk
+
+ # Save messages to DB after response completes
+ if content:
+ save_user_message(db, entry_id, content, version, msg_date)
+ for path in image_paths:
+ save_user_message(db, entry_id, path, version, msg_date)
+ save_assistant_message(db, entry_id, "".join(response_text), msg_date)
+
+ return StreamingResponse(respond_and_save(), media_type="text/plain")
+
+
+@router.get("/search")
+def search_entries(q: str, version: int = 1):
+ """Search entries using vector search, grouped by entry."""
+ db = get_database()
+ logger.info(f"Searching entries with query: {q[:50]}... (version={version})")
+
+ # Use appropriate embedding based on version
+ if version == 2:
+ query_embedding = get_multimodal_embedding(q, mode="text", input_type="query")
+ else:
+ query_embedding = get_text_embedding(q, input_type="query")
+
+ pipeline = [
+ {
+ "$vectorSearch": {
+ "index": VECTOR_INDEX_NAME,
+ "path": "embedding",
+ "queryVector": query_embedding,
+ "numCandidates": VECTOR_NUM_CANDIDATES,
+ "limit": 20,
+ "filter": {"user_id": USER_ID, "version": version},
+ }
+ },
+ {
+ "$project": {
+ "entry_id": 1,
+ "content": 1,
+ "image": 1,
+ "created_at": 1,
+ "score": {"$meta": "vectorSearchScore"},
+ }
+ },
+ {
+ "$group": {
+ "_id": "$entry_id",
+ "content": {"$first": "$content"},
+ "image": {"$first": "$image"},
+ "created_at": {"$first": "$created_at"},
+ "score": {"$max": "$score"},
+ }
+ },
+ {"$sort": {"score": -1}},
+ {"$limit": 5},
+ ]
+
+ results = list(db.messages.aggregate(pipeline))
+ for result in results:
+ result["_id"] = str(result["_id"])
+
+ logger.info(f"Search returned {len(results)} entries")
+ return results
+
+
+@router.post("/{entry_id}/analyze")
+def save_entry(entry_id: str, entry_date: str = Form(...)):
+ """Analyze entry for sentiment/themes and extract memories."""
+ db = get_database()
+ conversation = get_conversation_history(db, entry_id, include_images=False)
+
+ if not conversation:
+ return {"error": "No messages in entry"}
+
+ # Analyze sentiment and themes
+ analysis = analyze_entry(conversation)
+ db.entries.update_one(
+ {"_id": ObjectId(entry_id)},
+ {"$set": {"sentiment": analysis["sentiment"], "themes": analysis["themes"]}},
+ )
+
+ # Extract memories from full conversation
+ extract_and_save_memories(
+ db, entry_id, conversation, datetime.fromisoformat(entry_date)
+ )
+
+
+@router.get("/")
+def get_entries(version: int = 1):
+ db = get_database()
+ query = {"user_id": USER_ID, "version": version}
+ entries = list(db.entries.find(query).sort("created_at", -1))
+ for entry in entries:
+ entry["_id"] = str(entry["_id"])
+ return entries
+
+
+@router.post("/generate-prompt")
+def generate_prompt(entry_id: str = Form(...), entry_date: str = Form(...)):
+ """Generate a journal prompt based on the last month's memories."""
+ db = get_database()
+ one_month_ago = datetime.now() - timedelta(days=30)
+
+ memories = list(
+ db.memories.find(
+ {"user_id": USER_ID, "created_at": {"$gte": one_month_ago}},
+ {"content": 1, "created_at": 1, "_id": 0},
+ )
+ )
+ memory_contents = [
+ f"Date: {m['created_at'].strftime('%Y-%m-%d')}, Memory: {m['content']}"
+ for m in memories
+ ]
+ logger.info(f"Found {len(memory_contents)} memories from the last month")
+
+ prompt = generate_journal_prompt(memory_contents)
+
+ # Save the prompt as an assistant message
+ msg_date = datetime.fromisoformat(entry_date)
+ prompt_msg = {
+ "entry_id": entry_id,
+ "role": "assistant",
+ "content": prompt,
+ "created_at": msg_date,
+ }
+ db.messages.insert_one(prompt_msg)
+ logger.info(f"Saved generated prompt for entry {entry_id}")
+
+ return {"prompt": prompt}
+
+
+@router.get("/{entry_id}/messages")
+def get_messages(entry_id: str):
+ db = get_database()
+ messages = list(db.messages.find({"entry_id": entry_id}).sort("created_at", 1))
+ for msg in messages:
+ msg["_id"] = str(msg["_id"])
+ msg.pop("embedding", None)
+ return messages
+
+
+@router.get("/insights")
+def get_insights():
+ """Get user insights: stats, mood distribution, and themes."""
+ db = get_database()
+ return {
+ "total_entries": get_total_entries(db, USER_ID),
+ "longest_streak": get_longest_streak(db, USER_ID),
+ "mood": get_mood_distribution(db, USER_ID),
+ "themes": get_themes(db, USER_ID),
+ }
+
+
+@router.delete("/{entry_id}")
+def delete_entry(entry_id: str):
+ db = get_database()
+ db.entries.delete_one({"_id": ObjectId(entry_id)})
+ messages = db.messages.delete_many({"entry_id": entry_id})
+ memories = db.memories.delete_many({"entry_id": entry_id})
+ logger.info(
+ f"Deleted entry {entry_id}: "
+ f"{messages.deleted_count} messages, {memories.deleted_count} memories"
+ )
+ return {"deleted": True}
diff --git a/apps/interactive-journal/backend/app/services/__init__.py b/apps/interactive-journal/backend/app/services/__init__.py
new file mode 100644
index 0000000..0557eb6
--- /dev/null
+++ b/apps/interactive-journal/backend/app/services/__init__.py
@@ -0,0 +1 @@
+# Services module
diff --git a/apps/interactive-journal/backend/app/services/anthropic.py b/apps/interactive-journal/backend/app/services/anthropic.py
new file mode 100644
index 0000000..3215f5d
--- /dev/null
+++ b/apps/interactive-journal/backend/app/services/anthropic.py
@@ -0,0 +1,119 @@
+import logging
+from datetime import datetime
+from typing import Literal, Optional
+
+import anthropic
+from pydantic import BaseModel
+
+from app.config import ANTHROPIC_API_KEY, ANTHROPIC_MODEL
+from app.services.prompts import (
+ INSIGHTS_PROMPT,
+ JOURNAL_SYSTEM_PROMPT,
+ MEMORY_EXTRACTION_PROMPT,
+ PROMPT_GENERATOR,
+)
+
+logger = logging.getLogger(__name__)
+
+client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
+
+
+class MemoriesOutput(BaseModel):
+ memories: list[str]
+
+
+class EntryAnalysis(BaseModel):
+ sentiment: Literal["positive", "negative", "neutral", "mixed"]
+ themes: list[str]
+
+
+def extract_memories(user_message: str) -> list[str]:
+ """Extract memories/insights from a user's journal entry."""
+ logger.info(f"Extracting memories using {ANTHROPIC_MODEL}")
+
+ try:
+ response = client.beta.messages.parse(
+ model=ANTHROPIC_MODEL,
+ max_tokens=500,
+ temperature=0.8,
+ betas=["structured-outputs-2025-11-13"],
+ system=MEMORY_EXTRACTION_PROMPT,
+ messages=[{"role": "user", "content": user_message}],
+ output_format=MemoriesOutput,
+ )
+ memories = response.parsed_output.memories
+ logger.info(f"Extracted {len(memories)} memories")
+ return memories
+ except Exception as e:
+ logger.error(f"Failed to extract memories: {e}")
+ return []
+
+
+def analyze_entry(conversation: list[dict]) -> dict:
+ """Analyze a journal entry for sentiment and themes."""
+ logger.info(f"Analyzing entry with {len(conversation)} messages")
+
+ content = "\n".join(f"{msg['role']}: {msg['content']}" for msg in conversation)
+
+ try:
+ response = client.beta.messages.parse(
+ model=ANTHROPIC_MODEL,
+ max_tokens=200,
+ temperature=0.8,
+ betas=["structured-outputs-2025-11-13"],
+ system=INSIGHTS_PROMPT,
+ messages=[{"role": "user", "content": content}],
+ output_format=EntryAnalysis,
+ )
+ result = {
+ "sentiment": response.parsed_output.sentiment,
+ "themes": response.parsed_output.themes,
+ }
+ logger.info(f"Entry analysis: {result}")
+ return result
+ except Exception as e:
+ logger.error(f"Failed to analyze entry: {e}")
+ return {"sentiment": "neutral", "themes": []}
+
+
+def generate_response(messages: list[dict], memories: Optional[list[str]] = None):
+ """Generate a streaming response using Anthropic's Claude."""
+ logger.info(
+ f"Generating response using {ANTHROPIC_MODEL} with {len(memories) if memories else 0} memories"
+ )
+
+ system_prompt = JOURNAL_SYSTEM_PROMPT
+ if memories:
+ memory_context = "\n".join(f"- {m}" for m in memories)
+ system_prompt += f"\n\nRelevant memories about this user:\n{memory_context}\n\nUse these memories to provide more personalized and contextual responses when relevant."
+
+ with client.messages.stream(
+ model=ANTHROPIC_MODEL,
+ max_tokens=500,
+ temperature=0.8,
+ system=system_prompt,
+ messages=messages,
+ ) as stream:
+ yield from stream.text_stream
+
+
+def generate_journal_prompt(memories: list[str]) -> str:
+ """Generate a reflective journal prompt based on past memories."""
+ logger.info(f"Generating journal prompt from {len(memories)} memories")
+
+ if not memories:
+ return "What's on your mind today?"
+
+ today = datetime.now().strftime("%Y-%m-%d")
+ memory_context = "\n".join(f"- {m}" for m in memories)
+ user_content = f"Today's date: {today}\n\nMemories:\n{memory_context}"
+
+ response = client.messages.create(
+ model=ANTHROPIC_MODEL,
+ max_tokens=150,
+ temperature=0.8,
+ system=PROMPT_GENERATOR,
+ messages=[{"role": "user", "content": user_content}],
+ )
+
+ return response.content[0].text
diff --git a/apps/interactive-journal/backend/app/services/mongodb.py b/apps/interactive-journal/backend/app/services/mongodb.py
new file mode 100644
index 0000000..feee948
--- /dev/null
+++ b/apps/interactive-journal/backend/app/services/mongodb.py
@@ -0,0 +1,81 @@
+import logging
+
+from pymongo import MongoClient
+from pymongo.errors import CollectionInvalid, OperationFailure
+
+from app.config import (
+ DATABASE_NAME,
+ MONGODB_URI,
+ VECTOR_DIMENSIONS,
+ VECTOR_INDEX_NAME,
+)
+
+logger = logging.getLogger(__name__)
+
+client: MongoClient = None
+db = None
+
+
+def connect_db():
+ global client, db
+ client = MongoClient(MONGODB_URI)
+ db = client[DATABASE_NAME]
+ client.admin.command("ping")
+ logger.info(f"Connected to MongoDB: {DATABASE_NAME}")
+ setup_collections()
+ setup_indexes()
+
+
+def setup_collections():
+ for name in ["entries", "messages", "memories"]:
+ try:
+ db.create_collection(name)
+ logger.info(f"Created collection: {name}")
+ except CollectionInvalid:
+ logger.info(f"Collection already exists: {name}")
+
+
+def setup_indexes():
+ create_vector_index("messages", filter_paths=["user_id", "version"])
+ create_vector_index("memories", filter_paths=["user_id"])
+
+
+def create_vector_index(collection_name: str, filter_paths: list[str]):
+ collection = db[collection_name]
+
+ existing = list(collection.list_search_indexes())
+ if any(idx.get("name") == VECTOR_INDEX_NAME for idx in existing):
+ logger.info(f"Vector index already exists on {collection_name}")
+ return
+
+ fields = [
+ {
+ "type": "vector",
+ "numDimensions": VECTOR_DIMENSIONS,
+ "path": "embedding",
+ "similarity": "cosine",
+ },
+ ] + [{"type": "filter", "path": p} for p in filter_paths]
+
+ try:
+ collection.create_search_index(
+ model={
+ "name": VECTOR_INDEX_NAME,
+ "type": "vectorSearch",
+ "definition": {"fields": fields},
+ }
+ )
+ logger.info(f"Created vector index on {collection_name}")
+ except OperationFailure as e:
+ logger.error(f"Failed to create index on {collection_name}: {e}")
+
+
+def close_db():
+ global client
+ if client:
+ client.close()
+ logger.info("MongoDB connection closed")
+
+
+def get_database():
+ return db
diff --git a/apps/interactive-journal/backend/app/services/prompts.py b/apps/interactive-journal/backend/app/services/prompts.py
new file mode 100644
index 0000000..459aadf
--- /dev/null
+++ b/apps/interactive-journal/backend/app/services/prompts.py
@@ -0,0 +1,47 @@
+JOURNAL_SYSTEM_PROMPT = """You are a thoughtful and empathetic AI journaling companion called Memoir.
+Your role is to help users reflect on their thoughts, feelings, and experiences through conversation.
+
+Guidelines:
+- Ask thoughtful follow-up questions to help users explore their thoughts deeper
+- Be supportive and non-judgmental
+- Help users identify patterns and insights in their reflections
+- Keep responses concise but meaningful
+- Encourage self-reflection without being preachy
+- If users share something difficult, acknowledge their feelings first
+
+Remember: You're a journaling companion, not a therapist. Focus on reflection and exploration."""
+
+MEMORY_EXTRACTION_PROMPT = """You are a memory extraction system. Analyze the user's journal entry and extract meaningful memories, insights, and facts about the user.
+
+Extract information such as:
+- Personal facts (relationships, work, hobbies, preferences)
+- Emotional patterns and feelings
+- Goals, aspirations, and plans
+- Significant events or experiences
+- Insights and realizations
+
+Return a JSON array of memory strings. Each memory should be a concise, standalone statement ending in a period.
+If no meaningful memories can be extracted, return an empty array.
+
+Example output:
+["User has a sister named Sarah.", "User feels anxious about their job interview next week.", "User enjoys morning walks."]"""
+
+PROMPT_GENERATOR = """Based on the user's past memories, generate a thoughtful journaling prompt that encourages deeper reflection.
+
+Each memory includes its date. Use this to frame your prompt appropriately (e.g., "Last week you mentioned..." or "A few weeks ago you wrote about..."). Today's date is provided below.
+
+Pick one memory that seems meaningful and ask an open-ended question about it. Keep the prompt concise (1-2 sentences).
+
+Return only the prompt, nothing else."""
+
+INSIGHTS_PROMPT = """Analyze this journal entry conversation and extract:
+1. Overall sentiment (positive, negative, neutral, or mixed)
+2. Key themes discussed (2-4 short themes)
+
+Return a JSON object with this structure:
+{
+ "sentiment": "positive" | "negative" | "neutral" | "mixed",
+ "themes": ["theme1", "theme2", ...]
+}
+
+Keep themes concise (1-3 words each). Examples: "work stress", "family", "self-improvement", "gratitude"."""
diff --git a/apps/interactive-journal/backend/app/services/voyage.py b/apps/interactive-journal/backend/app/services/voyage.py
new file mode 100644
index 0000000..bf2fdcc
--- /dev/null
+++ b/apps/interactive-journal/backend/app/services/voyage.py
@@ -0,0 +1,67 @@
+import logging
+from pathlib import Path
+
+import voyageai
+from PIL import Image
+
+from app.config import (
+ IMAGE_SIZE,
+ VOYAGE_API_KEY,
+ VOYAGE_MULTIMODAL_MODEL,
+ VOYAGE_TEXT_MODEL,
+)
+
+logger = logging.getLogger(__name__)
+
+vo = voyageai.Client(api_key=VOYAGE_API_KEY)
+
+
+def get_multimodal_embedding(
+ content: str | Path, mode: str, input_type: str
+) -> list[float]:
+ """
+ Generate embeddings using Voyage AI's voyage-multimodal-3.5 model.
+
+ Args:
+ content: Text string or path to image file
+ mode (str): Content mode ("image" or "text")
+ input_type (str): Type of input ("document" or "query")
+
+ Returns:
+ list[float]: Embedding of the content as a list.
+ """
+ logger.info(
+ f"Generating multimodal embedding: mode={mode}, input_type={input_type}"
+ )
+
+ if mode == "image":
+ img = Image.open(content)
+ content = img.resize(IMAGE_SIZE, Image.Resampling.LANCZOS)
+
+ result = vo.multimodal_embed(
+ inputs=[[content]], model=VOYAGE_MULTIMODAL_MODEL, input_type=input_type
+ ).embeddings[0]
+
+ logger.debug(f"Generated {len(result)}-dim embedding")
+ return result
+
+
+def get_text_embedding(text: str, input_type: str = "document") -> list[float]:
+ """
+ Generate text embeddings using Voyage AI's voyage-3-large model.
+
+ Args:
+ text: Text string to embed
+ input_type: Type of input ("document" or "query")
+
+ Returns:
+ list[float]: Embedding of the text as a list.
+ """
+ logger.info(f"Generating text embedding: input_type={input_type}")
+
+ result = vo.embed(
+ texts=[text], model=VOYAGE_TEXT_MODEL, input_type=input_type
+ ).embeddings[0]
+
+ logger.debug(f"Generated {len(result)}-dim embedding")
+ return result
diff --git a/apps/interactive-journal/backend/requirements.txt b/apps/interactive-journal/backend/requirements.txt
new file mode 100644
index 0000000..4ae2815
--- /dev/null
+++ b/apps/interactive-journal/backend/requirements.txt
@@ -0,0 +1,7 @@
+fastapi==0.115.6
+uvicorn[standard]==0.34.0
+pymongo==4.10.1
+anthropic==0.75.0
+python-dotenv==1.0.1
+voyageai==0.3.7
+Pillow==11.0.0
diff --git a/apps/interactive-journal/frontend/.gitignore b/apps/interactive-journal/frontend/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/apps/interactive-journal/frontend/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/apps/interactive-journal/frontend/eslint.config.js b/apps/interactive-journal/frontend/eslint.config.js
new file mode 100644
index 0000000..4fa125d
--- /dev/null
+++ b/apps/interactive-journal/frontend/eslint.config.js
@@ -0,0 +1,29 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{js,jsx}'],
+ extends: [
+ js.configs.recommended,
+ reactHooks.configs.flat.recommended,
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ parserOptions: {
+ ecmaVersion: 'latest',
+ ecmaFeatures: { jsx: true },
+ sourceType: 'module',
+ },
+ },
+ rules: {
+ 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
+ },
+ },
+])
diff --git a/apps/interactive-journal/frontend/index.html b/apps/interactive-journal/frontend/index.html
new file mode 100644
index 0000000..c20fbd3
--- /dev/null
+++ b/apps/interactive-journal/frontend/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ frontend
+
+
+
+
+
+
diff --git a/apps/interactive-journal/frontend/package-lock.json b/apps/interactive-journal/frontend/package-lock.json
new file mode 100644
index 0000000..bdafea8
--- /dev/null
+++ b/apps/interactive-journal/frontend/package-lock.json
@@ -0,0 +1,2872 @@
+{
+ "name": "frontend",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "frontend",
+ "version": "0.0.0",
+ "dependencies": {
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.39.1",
+ "@types/react": "^19.2.5",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^5.1.1",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "vite": "^7.2.4"
+ }
+ },
+ "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.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
+ "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "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/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.53",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
+ "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
+ "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
+ "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
+ "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
+ "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
+ "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
+ "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
+ "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
+ "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
+ "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
+ "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
+ "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
+ "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
+ "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
+ "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
+ "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
+ "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
+ "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
+ "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
+ "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+ "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+ "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/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz",
+ "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.5",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.53",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.18.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.4",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.4.tgz",
+ "integrity": "sha512-ZCQ9GEWl73BVm8bu5Fts8nt7MHdbt5vY9bP6WGnUh+r3l8M7CgfyTlwsgCbMC66BNxPr6Xoce3j66Ms5YUQTNA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "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/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001759",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz",
+ "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==",
+ "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/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.266",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.266.tgz",
+ "integrity": "sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "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/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
+ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.1",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz",
+ "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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/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/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/globals": {
+ "version": "16.5.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+ "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "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/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "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/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "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/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/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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": "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/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/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
+ "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
+ "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.1"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
+ "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+ "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.53.3",
+ "@rollup/rollup-android-arm64": "4.53.3",
+ "@rollup/rollup-darwin-arm64": "4.53.3",
+ "@rollup/rollup-darwin-x64": "4.53.3",
+ "@rollup/rollup-freebsd-arm64": "4.53.3",
+ "@rollup/rollup-freebsd-x64": "4.53.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.53.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.53.3",
+ "@rollup/rollup-linux-arm64-musl": "4.53.3",
+ "@rollup/rollup-linux-loong64-gnu": "4.53.3",
+ "@rollup/rollup-linux-ppc64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-musl": "4.53.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-musl": "4.53.3",
+ "@rollup/rollup-openharmony-arm64": "4.53.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.53.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.53.3",
+ "@rollup/rollup-win32-x64-gnu": "4.53.3",
+ "@rollup/rollup-win32-x64-msvc": "4.53.3",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "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/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
+ "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
+ "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/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.2.7",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz",
+ "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
+ "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ }
+ }
+}
diff --git a/apps/interactive-journal/frontend/package.json b/apps/interactive-journal/frontend/package.json
new file mode 100644
index 0000000..1d89f06
--- /dev/null
+++ b/apps/interactive-journal/frontend/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "frontend",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.39.1",
+ "@types/react": "^19.2.5",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^5.1.1",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "vite": "^7.2.4"
+ }
+}
diff --git a/apps/interactive-journal/frontend/public/vite.svg b/apps/interactive-journal/frontend/public/vite.svg
new file mode 100644
index 0000000..ee9fada
--- /dev/null
+++ b/apps/interactive-journal/frontend/public/vite.svg
@@ -0,0 +1 @@
+
diff --git a/apps/interactive-journal/frontend/src/App.css b/apps/interactive-journal/frontend/src/App.css
new file mode 100644
index 0000000..24e63de
--- /dev/null
+++ b/apps/interactive-journal/frontend/src/App.css
@@ -0,0 +1,1030 @@
+.app {
+ display: flex;
+ height: 100vh;
+ position: relative;
+ overflow: hidden;
+ background: #fafafa;
+}
+
+/* Mesh gradient background */
+.app::before {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background:
+ radial-gradient(at 0% 0%, rgba(6, 182, 212, 0.4) 0%, transparent 50%),
+ radial-gradient(at 50% 0%, rgba(168, 85, 247, 0.35) 0%, transparent 45%),
+ radial-gradient(at 100% 0%, rgba(251, 146, 60, 0.5) 0%, transparent 50%),
+ radial-gradient(at 0% 50%, rgba(34, 197, 94, 0.3) 0%, transparent 40%),
+ radial-gradient(at 50% 50%, rgba(236, 72, 153, 0.25) 0%, transparent 50%),
+ radial-gradient(at 100% 50%, rgba(234, 179, 8, 0.4) 0%, transparent 45%),
+ radial-gradient(at 0% 100%, rgba(59, 130, 246, 0.35) 0%, transparent 50%),
+ radial-gradient(at 50% 100%, rgba(20, 184, 166, 0.3) 0%, transparent 45%),
+ radial-gradient(at 100% 100%, rgba(239, 68, 68, 0.35) 0%, transparent 50%);
+ animation: meshMove 30s ease-in-out infinite alternate;
+ z-index: 0;
+ pointer-events: none;
+}
+
+@keyframes meshMove {
+ 0% {
+ filter: blur(60px) saturate(150%);
+ transform: scale(1) rotate(0deg);
+ }
+ 50% {
+ filter: blur(80px) saturate(130%);
+ transform: scale(1.1) rotate(2deg);
+ }
+ 100% {
+ filter: blur(60px) saturate(150%);
+ transform: scale(1) rotate(-2deg);
+ }
+}
+
+/* Sidebar */
+.sidebar {
+ width: 280px;
+ background: transparent;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ z-index: 10;
+ padding: 24px 20px;
+}
+
+.sidebar-header {
+ padding: 8px 4px 24px;
+}
+
+.user-info {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 12px 4px;
+ margin-top: auto;
+}
+
+.user-avatar {
+ width: 32px;
+ height: 32px;
+ background: linear-gradient(135deg, #8b5cf6, #ec4899);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-weight: 600;
+ font-size: 14px;
+}
+
+.user-name {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1a1a1a;
+}
+
+.logo {
+ font-family: 'Sacramento', cursive;
+ font-size: 2.5rem;
+ font-weight: 400;
+ color: #1a1a1a;
+ letter-spacing: 0.08em;
+ -webkit-text-stroke: 0.5px #1a1a1a;
+}
+
+.sidebar-section {
+ margin-bottom: 24px;
+}
+
+.sidebar-section:last-child {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: visible;
+}
+
+.section-header {
+ font-size: 11px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: #6b7280;
+ padding: 0 4px 12px;
+}
+
+.new-entry-btn {
+ width: 100%;
+ padding: 12px 16px;
+ background: transparent;
+ color: #374151;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 10px;
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.new-entry-icon {
+ font-size: 18px;
+ font-weight: 400;
+ color: #6b7280;
+}
+
+.new-entry-btn:hover {
+ background: rgba(0, 0, 0, 0.04);
+ border-color: rgba(0, 0, 0, 0.25);
+}
+
+.empty-state {
+ padding: 16px 4px;
+ color: #9ca3af;
+ font-size: 13px;
+}
+
+.entry-list {
+ flex: 1;
+ overflow-y: auto;
+}
+
+.entry-item {
+ padding: 10px 12px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 400;
+ color: #374151;
+ transition: all 0.15s ease;
+ margin-bottom: 2px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ position: relative;
+}
+
+.entry-item:hover {
+ background: rgba(0, 0, 0, 0.05);
+}
+
+.entry-item.active {
+ background: rgba(0, 0, 0, 0.08);
+ font-weight: 500;
+ color: #1a1a1a;
+}
+
+.entry-item.menu-open {
+ background: rgba(0, 0, 0, 0.06);
+}
+
+.entry-title {
+ flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.menu-btn {
+ opacity: 0;
+ background: none;
+ border: none;
+ padding: 4px 6px;
+ cursor: pointer;
+ font-size: 14px;
+ color: #9ca3af;
+ border-radius: 4px;
+ transition: all 0.15s ease;
+ line-height: 1;
+}
+
+.entry-item:hover .menu-btn,
+.menu-btn.open {
+ opacity: 1;
+}
+
+.menu-btn:hover,
+.menu-btn.open {
+ color: #374151;
+ background: rgba(0, 0, 0, 0.08);
+}
+
+.dropdown-menu {
+ position: fixed;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+ min-width: 120px;
+ z-index: 9999;
+ overflow: hidden;
+ border: 1px solid rgba(0, 0, 0, 0.08);
+}
+
+.dropdown-item {
+ width: 100%;
+ padding: 10px 14px;
+ border: none;
+ background: none;
+ font-family: inherit;
+ font-size: 13px;
+ text-align: left;
+ cursor: pointer;
+ transition: background 0.15s ease;
+ display: block;
+}
+
+.dropdown-item:hover {
+ background: #f3f4f6;
+}
+
+.dropdown-item.delete {
+ color: #dc2626;
+}
+
+.dropdown-item.delete:hover {
+ background: #fef2f2;
+}
+
+/* Entry area */
+.entry {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ z-index: 1;
+ margin: 24px 24px 24px 0;
+ background: rgba(255, 255, 255, 0.6);
+ backdrop-filter: blur(30px);
+ border-radius: 24px;
+ border: 1px solid rgba(255, 255, 255, 0.7);
+ box-shadow: 0 8px 40px rgba(0, 0, 0, 0.06);
+}
+
+.entry-empty {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #6b7280;
+ font-size: 17px;
+ font-weight: 500;
+}
+
+.greeting {
+ font-size: 28px;
+ font-weight: 400;
+ color: #374151;
+ text-align: center;
+ margin-bottom: 32px;
+}
+
+.messages {
+ flex: 1;
+ overflow-y: auto;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+}
+
+.message {
+ width: 100%;
+ padding: 24px 48px;
+ font-size: 15px;
+ line-height: 1.75;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.04);
+}
+
+.message.user {
+ background: transparent;
+ color: #1f2937;
+}
+
+.message.assistant {
+ background: rgba(139, 92, 246, 0.04);
+ color: #1f2937;
+}
+
+.message-content {
+ max-width: 720px;
+ margin: 0 auto;
+}
+
+.message-label {
+ font-size: 12px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin-bottom: 8px;
+ color: #6b7280;
+}
+
+.message.user .message-label {
+ color: #8b5cf6;
+}
+
+.message.assistant .message-label {
+ color: #ec4899;
+}
+
+/* Entry input */
+.entry-input {
+ padding: 24px 48px 32px;
+ max-width: 816px;
+ margin: 0 auto;
+ width: 100%;
+}
+
+.entry-input-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 8px 8px 8px 20px;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-radius: 24px;
+ background: rgba(255, 255, 255, 0.9);
+ transition: all 0.3s ease;
+}
+
+.entry-input-wrapper:focus-within {
+ border-color: rgba(139, 92, 246, 0.5);
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.08);
+}
+
+.entry-input input {
+ flex: 1;
+ padding: 12px 0;
+ border: none;
+ font-family: inherit;
+ font-size: 15px;
+ outline: none;
+ background: transparent;
+}
+
+.entry-input input::placeholder {
+ color: #9ca3af;
+}
+
+.send-btn {
+ padding: 10px;
+ background: transparent;
+ color: #374151;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 10px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.send-btn:hover {
+ background: rgba(0, 0, 0, 0.04);
+ border-color: rgba(0, 0, 0, 0.25);
+}
+
+.send-btn svg {
+ display: block;
+}
+
+/* Version toggle */
+.version-toggle {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-top: 12px;
+}
+
+.version-label {
+ font-size: 12px;
+ font-weight: 500;
+ color: #9ca3af;
+ transition: color 0.2s ease;
+}
+
+.version-label.active {
+ color: #1a1a1a;
+}
+
+.toggle-switch {
+ width: 44px;
+ height: 24px;
+ background: rgba(0, 0, 0, 0.1);
+ border: none;
+ border-radius: 12px;
+ cursor: pointer;
+ position: relative;
+ transition: background 0.2s ease;
+ padding: 0;
+}
+
+.toggle-switch.on {
+ background: #8b5cf6;
+}
+
+.toggle-knob {
+ position: absolute;
+ top: 2px;
+ left: 2px;
+ width: 20px;
+ height: 20px;
+ background: white;
+ border-radius: 50%;
+ transition: transform 0.2s ease;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+}
+
+.toggle-switch.on .toggle-knob {
+ transform: translateX(20px);
+}
+
+/* Sidebar nav */
+.sidebar-nav {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ padding: 0 4px;
+ margin-bottom: 16px;
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 12px;
+ background: transparent;
+ border: none;
+ border-radius: 8px;
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 400;
+ color: #374151;
+ cursor: pointer;
+ transition: all 0.15s ease;
+ text-align: left;
+}
+
+.nav-item:hover {
+ background: rgba(0, 0, 0, 0.05);
+}
+
+.nav-item.active {
+ background: rgba(0, 0, 0, 0.08);
+ font-weight: 500;
+}
+
+.nav-item svg {
+ flex-shrink: 0;
+ color: #6b7280;
+}
+
+/* Search form */
+.search-form {
+ position: relative;
+ margin-bottom: 12px;
+}
+
+.search-input {
+ width: 100%;
+ padding: 10px 32px 10px 12px;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-radius: 8px;
+ font-family: inherit;
+ font-size: 13px;
+ background: rgba(255, 255, 255, 0.6);
+ outline: none;
+ transition: all 0.2s ease;
+}
+
+.search-input:focus {
+ border-color: rgba(139, 92, 246, 0.5);
+ box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.1);
+}
+
+.search-input::placeholder {
+ color: #9ca3af;
+}
+
+.search-clear {
+ position: absolute;
+ right: 8px;
+ top: 50%;
+ transform: translateY(-50%);
+ background: none;
+ border: none;
+ font-size: 18px;
+ color: #9ca3af;
+ cursor: pointer;
+ padding: 0 4px;
+ line-height: 1;
+}
+
+.search-clear:hover {
+ color: #6b7280;
+}
+
+.search-results {
+ margin-bottom: 8px;
+}
+
+/* Photo upload button */
+.photo-btn {
+ padding: 10px;
+ background: transparent;
+ color: #6b7280;
+ border: none;
+ border-radius: 10px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.photo-btn:hover {
+ background: rgba(0, 0, 0, 0.04);
+ color: #374151;
+}
+
+/* Main area search */
+.entry-search {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 48px;
+ max-width: 600px;
+ margin: 0 auto;
+ width: 100%;
+ overflow-y: auto;
+}
+
+.search-title {
+ font-size: 24px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 24px;
+}
+
+.search-form-main {
+ position: relative;
+ width: 100%;
+ margin-bottom: 24px;
+}
+
+.search-input-main {
+ width: 100%;
+ padding: 16px 48px 16px 20px;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-radius: 16px;
+ font-family: inherit;
+ font-size: 16px;
+ background: rgba(255, 255, 255, 0.9);
+ outline: none;
+ transition: all 0.2s ease;
+}
+
+.search-input-main:focus {
+ border-color: rgba(139, 92, 246, 0.5);
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
+}
+
+.search-input-main::placeholder {
+ color: #9ca3af;
+}
+
+.search-clear-main {
+ position: absolute;
+ right: 16px;
+ top: 50%;
+ transform: translateY(-50%);
+ background: none;
+ border: none;
+ font-size: 24px;
+ color: #9ca3af;
+ cursor: pointer;
+ padding: 0 4px;
+ line-height: 1;
+}
+
+.search-clear-main:hover {
+ color: #6b7280;
+}
+
+.search-results-main {
+ width: 100%;
+ flex: 1;
+ overflow-y: auto;
+}
+
+.no-results {
+ text-align: center;
+ color: #9ca3af;
+ font-size: 15px;
+}
+
+.search-result-item {
+ padding: 16px 20px;
+ background: rgba(255, 255, 255, 0.6);
+ border: 1px solid rgba(0, 0, 0, 0.06);
+ border-radius: 12px;
+ margin-bottom: 12px;
+ cursor: pointer;
+ transition: all 0.15s ease;
+}
+
+.search-result-item:hover {
+ background: rgba(255, 255, 255, 0.9);
+ border-color: rgba(139, 92, 246, 0.3);
+}
+
+.result-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+}
+
+.result-date {
+ font-size: 12px;
+ font-weight: 500;
+ color: #8b5cf6;
+}
+
+.result-score {
+ font-size: 11px;
+ color: #6b7280;
+ background: #f3f4f6;
+ padding: 2px 8px;
+ border-radius: 12px;
+}
+
+.result-content {
+ font-size: 14px;
+ color: #374151;
+ line-height: 1.6;
+ margin: 0;
+}
+
+.result-image {
+ max-width: 100%;
+ max-height: 150px;
+ border-radius: 8px;
+ object-fit: contain;
+}
+
+/* Image previews in input */
+.image-preview-container {
+ display: flex;
+ gap: 12px;
+ padding: 12px 48px;
+ flex-wrap: wrap;
+}
+
+.image-preview {
+ position: relative;
+ width: 80px;
+ height: 80px;
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.image-preview img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.remove-image-btn {
+ position: absolute;
+ top: 4px;
+ right: 4px;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ background: rgba(0, 0, 0, 0.6);
+ color: white;
+ border: none;
+ font-size: 14px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ line-height: 1;
+}
+
+.remove-image-btn:hover {
+ background: rgba(0, 0, 0, 0.8);
+}
+
+/* Message text and images */
+.message-text {
+ margin: 0;
+}
+
+.message-image {
+ max-width: 400px;
+ max-height: 300px;
+ border-radius: 12px;
+ object-fit: contain;
+ display: block;
+}
+
+/* Date picker modal */
+.date-picker-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.4);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+}
+
+.date-picker-modal {
+ background: white;
+ border-radius: 16px;
+ padding: 24px;
+ min-width: 300px;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
+}
+
+.date-picker-modal h3 {
+ margin: 0 0 20px;
+ font-size: 18px;
+ font-weight: 600;
+ color: #1a1a1a;
+}
+
+.date-input {
+ width: 100%;
+ padding: 12px 16px;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 10px;
+ font-family: inherit;
+ font-size: 15px;
+ outline: none;
+ transition: all 0.2s ease;
+}
+
+.date-input:focus {
+ border-color: rgba(139, 92, 246, 0.5);
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
+}
+
+.date-picker-actions {
+ display: flex;
+ gap: 12px;
+ margin-top: 20px;
+ justify-content: flex-end;
+}
+
+.date-cancel-btn,
+.date-confirm-btn {
+ padding: 10px 20px;
+ border-radius: 10px;
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.date-cancel-btn {
+ background: transparent;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ color: #374151;
+}
+
+.date-cancel-btn:hover {
+ background: rgba(0, 0, 0, 0.04);
+}
+
+.date-confirm-btn {
+ background: #8b5cf6;
+ border: none;
+ color: white;
+}
+
+.date-confirm-btn:hover {
+ background: #7c3aed;
+}
+
+/* Prompt generator */
+.prompt-generator {
+ display: flex;
+ justify-content: flex-end;
+ padding: 0 48px 12px;
+ max-width: 816px;
+ margin: 0 auto;
+ width: 100%;
+}
+
+.generate-prompt-btn {
+ padding: 12px 16px;
+ background: transparent;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 10px;
+ color: #374151;
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.generate-prompt-btn:hover:not(:disabled) {
+ background: rgba(0, 0, 0, 0.04);
+ border-color: rgba(0, 0, 0, 0.25);
+}
+
+.generate-prompt-btn:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+/* Insights */
+.insights-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 24px;
+ width: 100%;
+}
+
+.insight-card {
+ background: rgba(255, 255, 255, 0.7);
+ backdrop-filter: blur(20px);
+ border: 1px solid rgba(0, 0, 0, 0.08);
+ border-radius: 16px;
+ padding: 32px 24px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+}
+
+.insight-value {
+ font-size: 48px;
+ font-weight: 600;
+ color: #1a1a1a;
+ line-height: 1;
+ margin-bottom: 8px;
+}
+
+.insight-label {
+ font-size: 14px;
+ color: #6b7280;
+ font-weight: 500;
+}
+
+/* Save Entry */
+.save-entry {
+ display: flex;
+ justify-content: flex-end;
+ padding: 16px 48px;
+ max-width: 816px;
+ margin: 0 auto;
+ width: 100%;
+}
+
+.save-btn {
+ padding: 12px 24px;
+ background: #8b5cf6;
+ border: none;
+ border-radius: 10px;
+ color: white;
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.save-btn:hover {
+ background: #7c3aed;
+}
+
+.save-btn:active {
+ transform: scale(0.95);
+}
+
+.save-btn.saved {
+ background: #bbf7d0;
+ color: #166534;
+}
+
+/* Insights Sections */
+.section-title {
+ font-size: 16px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 16px;
+}
+
+.mood-section,
+.themes-section {
+ width: 100%;
+ margin-top: 32px;
+}
+
+/* Mood Bars */
+.mood-bars {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.mood-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.mood-emoji {
+ font-size: 20px;
+ width: 28px;
+}
+
+.mood-bar-track {
+ flex: 1;
+ height: 16px;
+ background: rgba(0, 0, 0, 0.05);
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.mood-bar-fill {
+ height: 100%;
+ border-radius: 8px;
+ transition: width 0.3s ease;
+}
+
+.mood-bar-fill.positive {
+ background: #22c55e;
+}
+
+.mood-bar-fill.neutral {
+ background: #94a3b8;
+}
+
+.mood-bar-fill.mixed {
+ background: #f59e0b;
+}
+
+.mood-bar-fill.negative {
+ background: #ef4444;
+}
+
+.mood-percent {
+ font-size: 14px;
+ color: #6b7280;
+ width: 40px;
+ text-align: right;
+}
+
+/* Word Cloud */
+.word-cloud {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px 16px;
+ justify-content: center;
+ align-items: baseline;
+ max-height: 300px;
+ overflow-y: auto;
+}
+
+.theme-word {
+ color: #6b7280;
+ font-weight: 500;
+ transition: color 0.2s ease;
+ cursor: default;
+}
+
+.theme-word:hover {
+ color: #7c3aed;
+}
+
+/* Hidden Scrollbar */
+.entry-list,
+.messages,
+.entry-search,
+.search-results-main,
+.word-cloud {
+ scrollbar-width: none;
+}
+
+.entry-list::-webkit-scrollbar,
+.messages::-webkit-scrollbar,
+.entry-search::-webkit-scrollbar,
+.search-results-main::-webkit-scrollbar,
+.word-cloud::-webkit-scrollbar {
+ display: none;
+}
diff --git a/apps/interactive-journal/frontend/src/App.jsx b/apps/interactive-journal/frontend/src/App.jsx
new file mode 100644
index 0000000..9f53848
--- /dev/null
+++ b/apps/interactive-journal/frontend/src/App.jsx
@@ -0,0 +1,172 @@
+import { useState, useEffect } from 'react'
+import Sidebar from './components/Sidebar'
+import Entry from './components/Entry'
+import './App.css'
+
+const API_URL = 'http://localhost:8000/api'
+
+function App() {
+ const [entries, setEntries] = useState([])
+ const [activeEntry, setActiveEntry] = useState(null)
+ const [messages, setMessages] = useState([])
+ const [isV2, setIsV2] = useState(false)
+ const [activeSection, setActiveSection] = useState(null)
+
+ useEffect(() => {
+ fetchEntries()
+ }, [isV2])
+
+ useEffect(() => {
+ if (activeEntry) {
+ fetchMessages(activeEntry)
+ }
+ }, [activeEntry])
+
+ const fetchEntries = async () => {
+ const version = isV2 ? 2 : 1
+ const res = await fetch(`${API_URL}/entries/?version=${version}`)
+ const data = await res.json()
+ setEntries(data)
+ }
+
+ const fetchMessages = async (entryId) => {
+ const res = await fetch(`${API_URL}/entries/${entryId}/messages`)
+ const data = await res.json()
+ setMessages(data)
+ }
+
+ const createEntry = async (entryDate) => {
+ const version = isV2 ? 2 : 1
+ const formData = new FormData()
+ formData.append('version', version)
+ formData.append('entry_date', entryDate)
+ const res = await fetch(`${API_URL}/entries/`, {
+ method: 'POST',
+ body: formData
+ })
+ const data = await res.json()
+ await fetchEntries()
+ setActiveEntry(data._id)
+ setMessages([])
+ setActiveSection(null)
+ }
+
+ const deleteEntry = async (entryId) => {
+ await fetch(`${API_URL}/entries/${entryId}`, { method: 'DELETE' })
+ await fetchEntries()
+ if (activeEntry === entryId) {
+ setActiveEntry(null)
+ setMessages([])
+ }
+ }
+
+ const toggleVersion = () => {
+ setIsV2(!isV2)
+ setActiveEntry(null)
+ setMessages([])
+ }
+
+ const sendMessage = async (content, images = []) => {
+ // Show user messages immediately (text and images separately)
+ const newMessages = []
+
+ if (content.trim()) {
+ newMessages.push({
+ _id: Date.now().toString(),
+ role: 'user',
+ content
+ })
+ }
+
+ images.forEach((img, index) => {
+ newMessages.push({
+ _id: Date.now().toString() + '-img-' + index,
+ role: 'user',
+ image: img.preview
+ })
+ })
+
+ // Show user messages immediately
+ setMessages(prev => [...prev, ...newMessages])
+
+ // Send to backend using FormData
+ const formData = new FormData()
+ if (content) {
+ formData.append('content', content)
+ }
+ images.forEach(img => {
+ formData.append('images', img.file)
+ })
+ formData.append('version', isV2 ? 2 : 1)
+ const activeEntryObj = entries.find(e => e._id === activeEntry)
+ if (activeEntryObj?.created_at) {
+ formData.append('entry_date', activeEntryObj.created_at)
+ }
+
+ const res = await fetch(`${API_URL}/entries/${activeEntry}/messages`, {
+ method: 'POST',
+ body: formData
+ })
+
+ // Read the streaming response
+ const reader = res.body.getReader()
+ const decoder = new TextDecoder()
+ const aiMessageId = Date.now().toString() + '-ai'
+ let fullResponse = ''
+ let messageAdded = false
+
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+
+ const chunk = decoder.decode(value, { stream: true })
+ fullResponse += chunk
+
+ // Add AI message on first chunk, then update
+ if (!messageAdded) {
+ setMessages(prev => [...prev, { _id: aiMessageId, role: 'assistant', content: fullResponse }])
+ messageAdded = true
+ } else {
+ setMessages(prev => prev.map(msg =>
+ msg._id === aiMessageId ? { ...msg, content: fullResponse } : msg
+ ))
+ }
+ }
+ }
+
+ return (
+
+ {
+ setActiveEntry(entryId)
+ if (isV2) setActiveSection('entries')
+ }}
+ onNewEntry={createEntry}
+ onDeleteEntry={deleteEntry}
+ isV2={isV2}
+ onToggleVersion={toggleVersion}
+ activeSection={activeSection}
+ onSectionChange={(section) => {
+ setActiveSection(section)
+ setActiveEntry(null)
+ setMessages([])
+ }}
+ />
+ activeEntry && fetchMessages(activeEntry)}
+ isV2={isV2}
+ activeSection={activeSection}
+ onSelectEntry={setActiveEntry}
+ />
+
+ )
+}
+
+export default App
diff --git a/apps/interactive-journal/frontend/src/assets/react.svg b/apps/interactive-journal/frontend/src/assets/react.svg
new file mode 100644
index 0000000..8e0e0f1
--- /dev/null
+++ b/apps/interactive-journal/frontend/src/assets/react.svg
@@ -0,0 +1 @@
+
diff --git a/apps/interactive-journal/frontend/src/components/Entry.jsx b/apps/interactive-journal/frontend/src/components/Entry.jsx
new file mode 100644
index 0000000..fbe61e0
--- /dev/null
+++ b/apps/interactive-journal/frontend/src/components/Entry.jsx
@@ -0,0 +1,380 @@
+import { useState, useRef, useEffect } from 'react'
+
+function Entry({ messages, onSendMessage, hasActiveEntry, activeEntry, entries, onRefreshMessages, isV2, activeSection, onSelectEntry }) {
+ const [input, setInput] = useState('')
+ const [selectedImages, setSelectedImages] = useState([])
+ const [searchQuery, setSearchQuery] = useState('')
+ const [searchResults, setSearchResults] = useState(null)
+ const [isSearching, setIsSearching] = useState(false)
+ const [isGeneratingPrompt, setIsGeneratingPrompt] = useState(false)
+ const [insights, setInsights] = useState(null)
+ const [saveStatus, setSaveStatus] = useState(null)
+ const messagesEndRef = useRef(null)
+ const fileInputRef = useRef(null)
+
+ const handleGeneratePrompt = async () => {
+ setIsGeneratingPrompt(true)
+ try {
+ const activeEntryObj = entries.find(e => e._id === activeEntry)
+ const formData = new FormData()
+ formData.append('entry_id', activeEntry)
+ formData.append('entry_date', activeEntryObj?.created_at || new Date().toISOString())
+
+ await fetch('http://localhost:8000/api/entries/generate-prompt', {
+ method: 'POST',
+ body: formData
+ })
+ onRefreshMessages()
+ } catch (error) {
+ console.error('Failed to generate prompt:', error)
+ }
+ setIsGeneratingPrompt(false)
+ }
+
+ const handleSaveEntry = async () => {
+ setSaveStatus('saving')
+ try {
+ const activeEntryObj = entries.find(e => e._id === activeEntry)
+ const formData = new FormData()
+ formData.append('entry_date', activeEntryObj?.created_at || new Date().toISOString())
+
+ await fetch(`http://localhost:8000/api/entries/${activeEntry}/analyze`, {
+ method: 'POST',
+ body: formData
+ })
+ setSaveStatus('saved')
+ setTimeout(() => setSaveStatus(null), 2000)
+ } catch (error) {
+ console.error('Failed to save entry:', error)
+ setSaveStatus(null)
+ }
+ }
+
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
+ }
+
+ useEffect(() => {
+ scrollToBottom()
+ }, [messages])
+
+ useEffect(() => {
+ if (isV2 && activeSection === 'insights') {
+ fetch('http://localhost:8000/api/entries/insights')
+ .then(res => res.json())
+ .then(data => setInsights(data))
+ .catch(err => console.error('Failed to fetch insights:', err))
+ }
+ }, [isV2, activeSection])
+
+ useEffect(() => {
+ setSearchQuery('')
+ setSearchResults(null)
+ }, [isV2])
+
+
+ const handleSubmit = (e) => {
+ e.preventDefault()
+ if ((input.trim() || selectedImages.length > 0) && hasActiveEntry) {
+ onSendMessage(input, selectedImages)
+ setInput('')
+ setSelectedImages([])
+ }
+ }
+
+ const handlePhotoClick = () => {
+ fileInputRef.current?.click()
+ }
+
+ const handleFileChange = (e) => {
+ const files = Array.from(e.target.files || [])
+
+ files.forEach(file => {
+ setSelectedImages(prev => [...prev, {
+ file,
+ preview: URL.createObjectURL(file),
+ name: file.name
+ }])
+ })
+
+ // Reset input so same file can be selected again
+ e.target.value = ''
+ }
+
+ const removeImage = (index) => {
+ setSelectedImages(prev => prev.filter((_, i) => i !== index))
+ }
+
+ const handleSearch = async (e) => {
+ e.preventDefault()
+ if (!searchQuery.trim()) return
+
+ setIsSearching(true)
+ try {
+ const version = isV2 ? 2 : 1
+ const res = await fetch(`http://localhost:8000/api/entries/search?q=${encodeURIComponent(searchQuery)}&version=${version}`)
+ const data = await res.json()
+ setSearchResults(data)
+ } catch (error) {
+ console.error('Search failed:', error)
+ }
+ setIsSearching(false)
+ }
+
+ const clearSearch = () => {
+ setSearchQuery('')
+ setSearchResults(null)
+ }
+
+ // Show search interface when Entries tab is clicked and no entry selected
+ if (activeSection === 'entries' && !hasActiveEntry) {
+ return (
+
+
+
Search your entries
+
+ {searchResults && (
+
+ {searchResults.length === 0 ? (
+
No matches found
+ ) : (
+ searchResults.map((result) => (
+
onSelectEntry(result._id)}
+ >
+
+
+ {new Date(result.created_at).toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric'
+ })}
+
+
+ {(result.score * 100).toFixed(0)}% match
+
+
+ {result.image ? (
+
+ ) : (
+
{result.content}
+ )}
+
+ ))
+ )}
+
+ )}
+
+
+ )
+ }
+
+ // Show insights when V2 Insights tab is active and no entry selected
+ if (isV2 && activeSection === 'insights' && !hasActiveEntry) {
+ return (
+
+
+
Your month in review
+ {insights ? (
+ <>
+
+
+ {insights.total_entries}
+ Entries
+
+
+ {insights.longest_streak}
+ Longest streak
+
+
+
+ {(insights.mood.positive > 0 || insights.mood.neutral > 0 || insights.mood.mixed > 0 || insights.mood.negative > 0) && (
+
+
Mood
+
+
+
😊
+
+
{insights.mood.positive}%
+
+
+
😐
+
+
{insights.mood.neutral}%
+
+
+
🤔
+
+
{insights.mood.mixed}%
+
+
+
😔
+
+
{insights.mood.negative}%
+
+
+
+ )}
+
+ {insights.themes.length > 0 && (
+
+
Themes
+
+ {insights.themes.map((item, i) => (
+
+ {item.theme}
+
+ ))}
+
+
+ )}
+ >
+ ) : (
+
Loading...
+ )}
+
+
+ )
+ }
+
+ if (!hasActiveEntry) {
+ return (
+
+
+
How was your day, Apoorva?
+
+
+ )
+ }
+
+ return (
+
+
+ {messages.map((msg) => (
+
+
+
+ {msg.role === 'user' ? 'You' : 'Memoir'}
+
+ {msg.content &&
{msg.content}
}
+ {msg.image && (
+
+ )}
+
+
+ ))}
+ {isV2 && messages.length > 0 && (
+
+
+ {saveStatus === 'saving' ? 'Saving...' : saveStatus === 'saved' ? 'Saved ✓' : 'Save'}
+
+
+ )}
+
+
+
+ {/* Image previews */}
+ {selectedImages.length > 0 && (
+
+ {selectedImages.map((img, index) => (
+
+
+
removeImage(index)}
+ >
+ ×
+
+
+ ))}
+
+ )}
+
+ {isV2 && messages.length === 0 && (
+
+
+ {isGeneratingPrompt ? 'Generating...' : "Need inspiration? Generate a prompt"}
+
+
+ )}
+
+
+
+ )
+}
+
+export default Entry
diff --git a/apps/interactive-journal/frontend/src/components/Sidebar.jsx b/apps/interactive-journal/frontend/src/components/Sidebar.jsx
new file mode 100644
index 0000000..1a6273d
--- /dev/null
+++ b/apps/interactive-journal/frontend/src/components/Sidebar.jsx
@@ -0,0 +1,192 @@
+import { useState, useEffect } from 'react'
+
+function Sidebar({ entries, activeEntry, onSelectEntry, onNewEntry, onDeleteEntry, isV2, onToggleVersion, activeSection, onSectionChange }) {
+ const [openMenu, setOpenMenu] = useState(null)
+ const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 })
+ const [showDatePicker, setShowDatePicker] = useState(false)
+ const [selectedDate, setSelectedDate] = useState('')
+
+ const handleNewEntryClick = () => {
+ setSelectedDate(new Date().toISOString().split('T')[0])
+ setShowDatePicker(true)
+ }
+
+ const handleDateConfirm = () => {
+ if (selectedDate) {
+ onNewEntry(selectedDate)
+ setShowDatePicker(false)
+ }
+ }
+
+ // Filter entries to only show recent ones (last 7 days) in V2
+ const recentEntries = entries.filter(entry => {
+ const entryDate = new Date(entry.created_at)
+ const oneWeekAgo = new Date()
+ oneWeekAgo.setDate(oneWeekAgo.getDate() - 7)
+ return entryDate >= oneWeekAgo
+ })
+
+ const handleMenuClick = (e, entryId) => {
+ e.stopPropagation()
+ if (openMenu === entryId) {
+ setOpenMenu(null)
+ } else {
+ const rect = e.currentTarget.getBoundingClientRect()
+ setMenuPosition({
+ top: rect.bottom + 4,
+ left: rect.left
+ })
+ setOpenMenu(entryId)
+ }
+ }
+
+ const handleDelete = (e, entryId) => {
+ e.stopPropagation()
+ onDeleteEntry(entryId)
+ setOpenMenu(null)
+ }
+
+ // Close menu when clicking outside
+ useEffect(() => {
+ const handleClickOutside = () => setOpenMenu(null)
+ if (openMenu) {
+ document.addEventListener('click', handleClickOutside)
+ return () => document.removeEventListener('click', handleClickOutside)
+ }
+ }, [openMenu])
+
+ // Ensure active entry is always included, sorted chronologically
+ const getDisplayEntries = () => {
+ if (!isV2) return entries
+ const activeEntryObj = entries.find(e => e._id === activeEntry)
+ if (!activeEntryObj || recentEntries.find(e => e._id === activeEntry)) {
+ return recentEntries
+ }
+ return [...recentEntries, activeEntryObj].sort(
+ (a, b) => new Date(b.created_at) - new Date(a.created_at)
+ )
+ }
+ const displayEntries = getDisplayEntries()
+
+ const formatDate = (dateString) => {
+ const date = new Date(dateString)
+ return date.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric'
+ })
+ }
+
+ return (
+
+
+
Memoir
+
+ V1
+
+
+
+ V2
+
+
+
+
+
+ +
+ New Entry
+
+
+
+ {showDatePicker && (
+
setShowDatePicker(false)}>
+
e.stopPropagation()}>
+
Select entry date
+
setSelectedDate(e.target.value)}
+ className="date-input"
+ />
+
+ setShowDatePicker(false)}>
+ Cancel
+
+
+ Create Entry
+
+
+
+
+ )}
+
+
+ onSectionChange('entries')}
+ >
+ Entries
+
+ {isV2 && (
+ onSectionChange('insights')}
+ >
+ Insights
+
+ )}
+
+
+
+
Recent
+
+ {displayEntries.length === 0 ? (
+
No recent entries
+ ) : (
+ displayEntries.map((entry) => (
+
onSelectEntry(entry._id)}
+ >
+ {formatDate(entry.created_at)}
+ handleMenuClick(e, entry._id)}
+ >
+ ⋯
+
+
+ ))
+ )}
+
+
+
+ {/* Fixed position dropdown */}
+ {openMenu && (
+
e.stopPropagation()}
+ >
+ handleDelete(e, openMenu)}
+ >
+ Delete
+
+
+ )}
+
+
+ A
+ Apoorva
+
+
+ )
+}
+
+export default Sidebar
diff --git a/apps/interactive-journal/frontend/src/index.css b/apps/interactive-journal/frontend/src/index.css
new file mode 100644
index 0000000..363ca51
--- /dev/null
+++ b/apps/interactive-journal/frontend/src/index.css
@@ -0,0 +1,24 @@
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Sacramento&display=swap');
+
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+:root {
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ line-height: 1.6;
+ font-weight: 400;
+ color: #1a1a1a;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ min-height: 100vh;
+}
+
+#root {
+ height: 100vh;
+}
diff --git a/apps/interactive-journal/frontend/src/main.jsx b/apps/interactive-journal/frontend/src/main.jsx
new file mode 100644
index 0000000..b9a1a6d
--- /dev/null
+++ b/apps/interactive-journal/frontend/src/main.jsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.jsx'
+
+createRoot(document.getElementById('root')).render(
+
+
+ ,
+)
diff --git a/apps/interactive-journal/frontend/vite.config.js b/apps/interactive-journal/frontend/vite.config.js
new file mode 100644
index 0000000..8b0f57b
--- /dev/null
+++ b/apps/interactive-journal/frontend/vite.config.js
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+})
diff --git a/ruff.toml b/ruff.toml
index 411c7bd..e73ef5a 100644
--- a/ruff.toml
+++ b/ruff.toml
@@ -17,6 +17,7 @@ extend-select = [
"YTT", # flake8-2020
]
ignore = [
+ "E402", # Module level import not at top of file,
"F811", # Redefinition of unused,
"F402", # Import shadowing,
"B006", # Do not use mutable data structures for argument defaults,